فهرست منبع

Merge pull request #4076 from overleaf/jpa-events-split-test

[misc] submit events and prepare roll-out for pdf caching w/ split test

GitOrigin-RevId: a7b7af65e1adf5bf003b65d96f1641a343b4b09c
Jakob Ackermann 5 سال پیش
والد
کامیت
f0b3d8a26a

+ 19 - 3
services/web/app/src/Features/Project/ProjectController.js

@@ -37,6 +37,7 @@ const BrandVariationsHandler = require('../BrandVariations/BrandVariationsHandle
 const UserController = require('../User/UserController')
 const UserController = require('../User/UserController')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const Modules = require('../../infrastructure/Modules')
 const Modules = require('../../infrastructure/Modules')
+const { getTestSegmentation } = require('../SplitTests/SplitTestHandler')
 const { getNewLogsUIVariantForUser } = require('../Helpers/NewLogsUI')
 const { getNewLogsUIVariantForUser } = require('../Helpers/NewLogsUI')
 
 
 const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
 const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
@@ -801,6 +802,22 @@ const ProjectController = {
               }
               }
             }
             }
 
 
+            const trackPdfDownload =
+              Settings.enablePdfCaching &&
+              (user.alphaProgram ||
+                (user.betaProgram &&
+                  getTestSegmentation(userId, 'track_pdf_download').variant ===
+                    'enabled'))
+            const enablePdfCaching =
+              Settings.enablePdfCaching &&
+              shouldDisplayFeature(
+                'enable_pdf_caching',
+                user.alphaProgram ||
+                  (user.betaProgram &&
+                    getTestSegmentation(userId, 'enable_pdf_caching')
+                      .variant === 'enabled')
+              )
+
             res.render('project/editor', {
             res.render('project/editor', {
               title: project.name,
               title: project.name,
               priority_title: true,
               priority_title: true,
@@ -878,9 +895,8 @@ const ProjectController = {
               ),
               ),
               showNewBinaryFileUI: shouldDisplayFeature('new_binary_file'),
               showNewBinaryFileUI: shouldDisplayFeature('new_binary_file'),
               showSymbolPalette: shouldDisplayFeature('symbol_palette'),
               showSymbolPalette: shouldDisplayFeature('symbol_palette'),
-              enablePdfCaching:
-                Settings.enablePdfCaching &&
-                shouldDisplayFeature('enable_pdf_caching', user.alphaProgram),
+              trackPdfDownload,
+              enablePdfCaching,
             })
             })
             timer.done()
             timer.done()
           }
           }

+ 1 - 0
services/web/app/views/project/editor.pug

@@ -188,6 +188,7 @@ block append meta
 	meta(name="ol-logsUISubvariant" content=logsUISubvariant)
 	meta(name="ol-logsUISubvariant" content=logsUISubvariant)
 	meta(name="ol-showSymbolPalette" data-type="boolean" content=showSymbolPalette)
 	meta(name="ol-showSymbolPalette" data-type="boolean" content=showSymbolPalette)
 	meta(name="ol-enablePdfCaching" data-type="boolean" content=enablePdfCaching)
 	meta(name="ol-enablePdfCaching" data-type="boolean" content=enablePdfCaching)
+	meta(name="ol-trackPdfDownload" data-type="boolean" content=trackPdfDownload)
 
 
 	- var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {})
 	- var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {})
 	meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n)
 	meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n)

+ 7 - 45
services/web/frontend/js/ide/pdf/controllers/PdfController.js

@@ -7,6 +7,7 @@ import { rootContext } from '../../../shared/context/root-context'
 import 'ace/ace'
 import 'ace/ace'
 import getMeta from '../../../utils/meta'
 import getMeta from '../../../utils/meta'
 import { waitForServiceWorker } from '../../pdfng/directives/serviceWorkerManager'
 import { waitForServiceWorker } from '../../pdfng/directives/serviceWorkerManager'
+import { trackPdfDownload } from './PdfJsMetrics'
 
 
 const AUTO_COMPILE_MAX_WAIT = 5000
 const AUTO_COMPILE_MAX_WAIT = 5000
 // We add a 1 second debounce to sending user changes to server if they aren't
 // We add a 1 second debounce to sending user changes to server if they aren't
@@ -405,25 +406,13 @@ App.controller(
         // convert the qs hash into a query string and append it
         // convert the qs hash into a query string and append it
         $scope.pdf.url += createQueryString(qs)
         $scope.pdf.url += createQueryString(qs)
 
 
-        const {
-          onFirstRenderDone,
-          firstRenderDone,
-          updateConsumedBandwidth,
-        } = trackPDFDownload()
-        $scope.pdf.firstRenderDone = firstRenderDone
-        $scope.pdf.updateConsumedBandwidth = updateConsumedBandwidth
-        const { serviceWorkerMetrics } = response
-
-        onFirstRenderDone.then(({ latencyFetch, latencyRender }) => {
-          sl_console.log(
-            JSON.stringify({
-              latencyFetch,
-              latencyRender,
-              totalBandwidthReportedByPdfJs,
-              serviceWorkerMetrics,
-            })
+        if (getMeta('ol-trackPdfDownload')) {
+          const { firstRenderDone, updateConsumedBandwidth } = trackPdfDownload(
+            response
           )
           )
-        })
+          $scope.pdf.firstRenderDone = firstRenderDone
+          $scope.pdf.updateConsumedBandwidth = updateConsumedBandwidth
+        }
 
 
         // Save all downloads as files
         // Save all downloads as files
         qs.popupDownload = true
         qs.popupDownload = true
@@ -572,33 +561,6 @@ App.controller(
       )
       )
     }
     }
 
 
-    let totalBandwidthReportedByPdfJs = 0
-    function trackPDFDownload() {
-      const t0 = performance.now()
-      let bandwidth = 0
-      function firstRenderDone({ timePDFFetched, timePDFRendered }) {
-        const latencyFetch = timePDFFetched - t0
-        // The renderer does not yield in case the browser tab is hidden.
-        // It will yield when the browser tab is visible again.
-        // This will skew our performance metrics for rendering!
-        const latencyRender = timePDFRendered - t0
-        done({ latencyFetch, latencyRender })
-      }
-      function updateConsumedBandwidth(bytes) {
-        totalBandwidthReportedByPdfJs += bytes - bandwidth
-        bandwidth = bytes
-      }
-      let done
-      const onFirstRenderDone = new Promise(resolve => {
-        done = resolve
-      })
-      return {
-        onFirstRenderDone,
-        firstRenderDone,
-        updateConsumedBandwidth,
-      }
-    }
-
     function fetchLogs(fileByPath, options) {
     function fetchLogs(fileByPath, options) {
       let blgFile, chktexFile, logFile
       let blgFile, chktexFile, logFile
 
 

+ 60 - 0
services/web/frontend/js/ide/pdf/controllers/PdfJsMetrics.js

@@ -0,0 +1,60 @@
+import { v4 as uuid } from 'uuid'
+import { sendMBSampled } from '../../../infrastructure/event-tracking'
+
+const pdfJsMetrics = {
+  id: uuid(),
+  epoch: Date.now(),
+  totalBandwidth: 0,
+}
+
+const SAMPLING_RATE = 0.01
+
+export function trackPdfDownload(response) {
+  const { serviceWorkerMetrics, stats, timings } = response
+
+  const t0 = performance.now()
+  let bandwidth = 0
+  function firstRenderDone({ timePDFFetched, timePDFRendered }) {
+    const latencyFetch = timePDFFetched - t0
+    // The renderer does not yield in case the browser tab is hidden.
+    // It will yield when the browser tab is visible again.
+    // This will skew our performance metrics for rendering!
+    const latencyRender = timePDFRendered - timePDFFetched
+    done({ latencyFetch, latencyRender })
+  }
+  function updateConsumedBandwidth(bytes) {
+    pdfJsMetrics.totalBandwidth += bytes - bandwidth
+    bandwidth = bytes
+  }
+  let done
+  const onFirstRenderDone = new Promise(resolve => {
+    done = resolve
+  })
+
+  // Submit latency along with compile context.
+  onFirstRenderDone.then(({ latencyFetch, latencyRender }) => {
+    submitCompileMetrics({
+      latencyFetch,
+      latencyRender,
+      stats,
+      timings,
+    })
+  })
+  // Submit bandwidth counter separate from compile context.
+  submitPDFBandwidth({ pdfJsMetrics, serviceWorkerMetrics })
+
+  return {
+    firstRenderDone,
+    updateConsumedBandwidth,
+  }
+}
+
+function submitCompileMetrics(metrics) {
+  sl_console.log('/event/compile-metrics', JSON.stringify(metrics))
+  sendMBSampled('compile-metrics', metrics, SAMPLING_RATE)
+}
+
+function submitPDFBandwidth(metrics) {
+  sl_console.log('/event/pdf-bandwidth', JSON.stringify(metrics))
+  sendMBSampled('pdf-bandwidth', metrics, SAMPLING_RATE)
+}

+ 6 - 0
services/web/frontend/js/infrastructure/event-tracking.js

@@ -11,3 +11,9 @@ export function sendMB(key, body = {}) {
     // ignore errors
     // ignore errors
   })
   })
 }
 }
+
+export function sendMBSampled(key, body = {}, rate = 0.01) {
+  if (Math.random() < rate) {
+    sendMB(key, body)
+  }
+}

+ 2 - 0
services/web/frontend/js/serviceWorker.js

@@ -1,9 +1,11 @@
+import { v4 as uuid } from 'uuid'
 const COMPILE_REQUEST_MATCHER = /^\/project\/[0-9a-f]{24}\/compile$/
 const COMPILE_REQUEST_MATCHER = /^\/project\/[0-9a-f]{24}\/compile$/
 const MIN_CHUNK_SIZE = 128 * 1024
 const MIN_CHUNK_SIZE = 128 * 1024
 
 
 const PDF_FILES = new Map()
 const PDF_FILES = new Map()
 
 
 const METRICS = {
 const METRICS = {
+  id: uuid(),
   epoch: Date.now(),
   epoch: Date.now(),
   cachedBytes: 0,
   cachedBytes: 0,
   fetchedBytes: 0,
   fetchedBytes: 0,

+ 4 - 0
services/web/test/unit/src/Project/ProjectControllerTests.js

@@ -124,12 +124,16 @@ describe('ProjectController', function () {
         .stub()
         .stub()
         .returns({ newLogsUI: false, subvariant: null }),
         .returns({ newLogsUI: false, subvariant: null }),
     }
     }
+    this.SplitTestHandler = {
+      getTestSegmentation: sinon.stub().returns({ enabled: false }),
+    }
 
 
     this.ProjectController = SandboxedModule.require(MODULE_PATH, {
     this.ProjectController = SandboxedModule.require(MODULE_PATH, {
       requires: {
       requires: {
         mongodb: { ObjectId },
         mongodb: { ObjectId },
         'settings-sharelatex': this.settings,
         'settings-sharelatex': this.settings,
         '@overleaf/metrics': this.Metrics,
         '@overleaf/metrics': this.Metrics,
+        '../SplitTests/SplitTestHandler': this.SplitTestHandler,
         './ProjectDeleter': this.ProjectDeleter,
         './ProjectDeleter': this.ProjectDeleter,
         './ProjectDuplicator': this.ProjectDuplicator,
         './ProjectDuplicator': this.ProjectDuplicator,
         './ProjectCreationHandler': this.ProjectCreationHandler,
         './ProjectCreationHandler': this.ProjectCreationHandler,