From c3a0379074412c83a710c5619a6dc29930fde397 Mon Sep 17 00:00:00 2001 From: Suchita Doshi Date: Wed, 7 Oct 2020 11:19:47 -0700 Subject: [PATCH] Add instrumentation for measuring metrics related to visit --- src/ember-app.js | 24 ++++++++++++++---- test/fastboot-test.js | 57 +++++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/ember-app.js b/src/ember-app.js index ce1e2f3..145d55b 100644 --- a/src/ember-app.js +++ b/src/ember-app.js @@ -188,8 +188,11 @@ class EmberApp { * * @returns {Object} */ - getAppInstanceInfo(appInstance, isAppInstancePreBuilt = true) { - return { app: appInstance, isSandboxPreBuilt: isAppInstancePreBuilt }; + getAppInstanceInfo( + appInstance, + appAnalytics = { isAppInstancePreBuilt: true, appInstanceRetrievalTime: 0 } + ) { + return { app: appInstance, appAnalytics }; } /** @@ -205,10 +208,14 @@ class EmberApp { * only */ async getNewApplicationInstance() { + const appInstanceRetrievalStartTime = Date.now(); const queueObject = this._sandboxApplicationInstanceQueue.dequeue(); const app = await queueObject.item; - return this.getAppInstanceInfo(app, queueObject.isItemPreBuilt); + return this.getAppInstanceInfo(app, { + isAppInstancePreBuilt: queueObject.isItemPreBuilt, + appInstanceRetrievalTime: Date.now() - appInstanceRetrievalStartTime, + }); } /** @@ -236,10 +243,11 @@ class EmberApp { async _visit(path, fastbootInfo, bootOptions, result, buildSandboxPerVisit) { let shouldBuildApp = buildSandboxPerVisit || this._applicationInstance === undefined; - let { app, isSandboxPreBuilt } = shouldBuildApp + let { app, appAnalytics } = shouldBuildApp ? await this.getNewApplicationInstance() : this.getAppInstanceInfo(this._applicationInstance); + result.analytics.appInstanceRetrievalTime = appAnalytics.appInstanceRetrievalTime; if (buildSandboxPerVisit) { // entangle the specific application instance to the result, so it can be // destroyed when result._destroy() is called (after the visit is @@ -248,7 +256,7 @@ class EmberApp { // we add analytics information about the current request to know // whether it used sandbox from the pre-built queue or built on demand. - result.analytics.usedPrebuiltSandbox = isSandboxPreBuilt; + result.analytics.usedPrebuiltSandbox = appAnalytics.isAppInstancePreBuilt; } else { // save the created application instance so that we can clean it up when // this instance of `src/ember-app.js` is destroyed (e.g. reload) @@ -262,7 +270,11 @@ class EmberApp { registerFastBootInfo(fastbootInfo, instance); await instance.boot(bootOptions); + + const instanceVisitStartTime = Date.now(); await instance.visit(path, bootOptions); + result.analytics.instanceVisitTime = Date.now() - instanceVisitStartTime; + await fastbootInfo.deferredPromise; } @@ -290,6 +302,7 @@ class EmberApp { * @returns {Promise} result */ async visit(path, options) { + let fastbootVisitStartTime = Date.now(); let req = options.request; let res = options.response; let html = options.html || this.html; @@ -323,6 +336,7 @@ class EmberApp { try { await this._visit(path, fastbootInfo, bootOptions, result, buildSandboxPerVisit); + result.analytics.fastbootVisitTime = Date.now() - fastbootVisitStartTime; if (!disableShoebox) { // if shoebox is not disabled, then create the shoebox and send API data diff --git a/test/fastboot-test.js b/test/fastboot-test.js index 6f77f25..9e22403 100644 --- a/test/fastboot-test.js +++ b/test/fastboot-test.js @@ -441,27 +441,21 @@ describe('FastBoot', function() { let html = await result.html(); expect(html).to.match(/Items: 1/); - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await fastboot.visit('/', { buildSandboxPerVisit: true }); analytics = result.analytics; html = await result.html(); expect(html).to.match(/Items: 1/); - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await fastboot.visit('/', { buildSandboxPerVisit: true }); analytics = result.analytics; html = await result.html(); expect(html).to.match(/Items: 1/); - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); }); it('errors can be properly attributed with buildSandboxPerVisit=true', async function() { @@ -524,21 +518,15 @@ describe('FastBoot', function() { let result = await first; let analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await second; analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await third; analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: false, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(false); }); it('it leverages sandbox from queue when present', async function() { @@ -566,21 +554,38 @@ describe('FastBoot', function() { let result = await first; let analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await second; analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, - }); + expect(analytics.usedPrebuiltSandbox).to.equal(true); result = await third; analytics = result.analytics; - expect(analytics).to.be.deep.equals({ - usedPrebuiltSandbox: true, + expect(analytics.usedPrebuiltSandbox).to.equal(true); + }); + + it('should contain the analytics data for the visit', async function() { + this.timeout(3000); + + var fastboot = new FastBoot({ + distPath: fixture('app-with-prototype-mutations'), }); + + let resultWithSandboxPerVisit = await fastboot.visit('/', { buildSandboxPerVisit: true }); + expect(Object.keys(resultWithSandboxPerVisit.analytics)).to.deep.equals([ + 'appInstanceRetrievalTime', + 'usedPrebuiltSandbox', + 'instanceVisitTime', + 'fastbootVisitTime', + ]); + + let resultWithoutSandboxPerVisit = await fastboot.visit('/'); + expect(Object.keys(resultWithoutSandboxPerVisit.analytics)).to.deep.equals([ + 'appInstanceRetrievalTime', + 'instanceVisitTime', + 'fastbootVisitTime', + ]); }); it('htmlEntrypoint works', function() {