Przeglądaj źródła

Merge pull request #10007 from overleaf/jpa-pdf-caching-latency-compute

[web] collect latencyCompute metric from pdf-caching

GitOrigin-RevId: f20fa2b54078a7106b2413432c4f47375878a5d6
Jakob Ackermann 3 lat temu
rodzic
commit
abaecac268

+ 1 - 1
services/web/frontend/js/features/pdf-preview/util/metrics.js

@@ -4,7 +4,7 @@ import { trackPdfDownloadEnabled } from './pdf-caching-flags'
 
 // VERSION should get incremented when making changes to caching behavior or
 //  adjusting metrics collection.
-const VERSION = 6
+const VERSION = 7
 
 // editing session id
 const EDITOR_SESSION_ID = uuid()

+ 5 - 0
services/web/frontend/js/features/pdf-preview/util/pdf-caching-transport.js

@@ -26,6 +26,8 @@ export function generatePdfCachingTransportFactory(PDFJS) {
     cachedBytes: 0,
     fetchedCount: 0,
     fetchedBytes: 0,
+    latencyComputeMax: 0,
+    latencyComputeTotal: 0,
     requestedCount: 0,
     requestedBytes: 0,
     oldUrlHitCount: 0,
@@ -118,6 +120,9 @@ export function generatePdfCachingTransportFactory(PDFJS) {
       // Be trigger-happy here until we reached a stable state of the feature.
       return null
     }
+    // Latency is collected per preview cycle.
+    metrics.latencyComputeMax = 0
+    metrics.latencyComputeTotal = 0
     return new PDFDataRangeTransport({
       url,
       pdfFile,

+ 49 - 1
services/web/frontend/js/features/pdf-preview/util/pdf-caching.js

@@ -583,6 +583,37 @@ function addPrefetchingChunks({
   dynamicChunks.sort(sortByStartASC)
 }
 
+class Timer {
+  constructor() {
+    this.max = 0
+    this.total = 0
+    this.lastStart = 0
+  }
+
+  startBlockingCompute() {
+    this.lastStart = performance.now()
+  }
+
+  finishBlockingCompute() {
+    if (this.lastStart === 0) return
+    const last = performance.now() - this.lastStart
+    if (last > this.max) {
+      this.max = last
+    }
+    this.total += last
+    this.lastStart = 0
+  }
+
+  reportInto(metrics) {
+    const max = Math.ceil(this.max)
+    const total = Math.ceil(this.total)
+    if (max > metrics.latencyComputeMax) {
+      metrics.latencyComputeMax = max
+    }
+    metrics.latencyComputeTotal += total
+  }
+}
+
 /**
  *
  * @param {string} url
@@ -612,6 +643,8 @@ export async function fetchRange({
   cachedUrlLookupEnabled,
   abortSignal,
 }) {
+  const timer = new Timer()
+  timer.startBlockingCompute()
   preprocessFileOnce({ file, usageScore, cachedUrls })
   const startXRefTableRange =
     Math.floor(file.startXRefTable / PDF_JS_CHUNK_SIZE) * PDF_JS_CHUNK_SIZE
@@ -650,6 +683,8 @@ export async function fetchRange({
   ) {
     // fall back to the original range request when no chunks are cached.
     // Exception: The first range should fetch the xref table as well.
+    timer.finishBlockingCompute()
+    timer.reportInto(metrics)
     trackDownloadStats(metrics, {
       size,
       cachedCount: 0,
@@ -728,6 +763,9 @@ export async function fetchRange({
   let fetchedBytes = 0
   const reassembledBlob = new Uint8Array(size)
 
+  // Pause while performing network IO
+  timer.finishBlockingCompute()
+
   const rawResponses = await Promise.all(
     requests.map(async ({ chunk, url, init }) => {
       try {
@@ -739,6 +777,7 @@ export async function fetchRange({
           metrics,
           cachedUrlLookupEnabled,
         })
+        timer.startBlockingCompute()
         const boundary = getMultipartBoundary(response, chunk)
         const blobFetchDate = getServerTime(response)
         const blobSize = getResponseSize(response)
@@ -761,7 +800,10 @@ export async function fetchRange({
             fetchedBytes += blobSize
           }
         }
-        const data = backFillObjectContext(chunk, await response.arrayBuffer())
+        timer.finishBlockingCompute()
+        const buf = await response.arrayBuffer()
+        timer.startBlockingCompute()
+        const data = backFillObjectContext(chunk, buf)
         if (!Array.isArray(chunk)) {
           return [{ chunk, data }]
         }
@@ -774,10 +816,14 @@ export async function fetchRange({
         })
       } catch (err) {
         throw OError.tag(err, 'cannot fetch chunk', { chunk, url, init })
+      } finally {
+        timer.finishBlockingCompute()
       }
     })
   )
 
+  timer.startBlockingCompute()
+
   rawResponses
     .flat() // flatten after splitting multipart responses
     .concat(prefetched.map(chunk => ({ chunk, data: chunk.buffer })))
@@ -805,6 +851,8 @@ export async function fetchRange({
       reassembledBlob.set(data, insertPosition)
     })
 
+  timer.finishBlockingCompute()
+  timer.reportInto(metrics)
   trackDownloadStats(metrics, {
     size,
     cachedCount,