Merge pull request #4060 from overleaf/jpa-service-worker-metrics

[misc] serviceWorker: collect metrics for cached vs fetched performance

GitOrigin-RevId: db29bc77801d71008ba61ffb7e335a67cd5bf16d
This commit is contained in:
Jakob Ackermann 2021-05-21 13:31:37 +02:00 committed by Copybot
parent e0616096e2
commit 9ad248af41

View file

@ -3,6 +3,25 @@ const MIN_CHUNK_SIZE = 128 * 1024
const PDF_FILES = new Map()
const METRICS = {
epoch: Date.now(),
cachedBytes: 0,
fetchedBytes: 0,
requestedBytes: 0,
}
/**
*
* @param {number} size
* @param {number} cachedBytes
* @param {number} fetchedBytes
*/
function trackStats({ size, cachedBytes, fetchedBytes }) {
METRICS.cachedBytes += cachedBytes
METRICS.fetchedBytes += fetchedBytes
METRICS.requestedBytes += size
}
/**
* @param {FetchEvent} event
*/
@ -18,6 +37,7 @@ function onFetch(event) {
if (ctx) {
return processPdfRequest(event, ctx)
}
// other request, ignore
}
@ -27,8 +47,9 @@ function processCompileRequest(event) {
if (response.status !== 200) return response
return response.json().then(body => {
handleCompileResponse(body)
// The response body is consumed, serialize it again.
handleCompileResponse(response, body)
// Send the service workers metrics to the frontend.
body.serviceWorkerMetrics = METRICS
return new Response(JSON.stringify(body), response)
})
})
@ -41,8 +62,12 @@ function processCompileRequest(event) {
* @param {Object} file
* @param {string} clsiServerId
* @param {string} compileGroup
* @param {Date} pdfCreatedAt
*/
function processPdfRequest(event, { file, clsiServerId, compileGroup }) {
function processPdfRequest(
event,
{ file, clsiServerId, compileGroup, pdfCreatedAt }
) {
if (!event.request.headers.has('Range') && file.size > MIN_CHUNK_SIZE) {
// skip probe request
const headers = new Headers()
@ -96,6 +121,8 @@ function processPdfRequest(event, { file, clsiServerId, compileGroup }) {
})
)
const size = end - start
let cachedBytes = 0
let fetchedBytes = 0
const reAssembledBlob = new Uint8Array(size)
event.respondWith(
Promise.all(
@ -109,6 +136,22 @@ function processPdfRequest(event, { file, clsiServerId, compileGroup }) {
}`
)
}
const blobFetchDate = getServerTime(response)
const blobSize = getResponseSize(response)
if (blobFetchDate && blobSize) {
const chunkSize =
Math.min(end, chunk.end) - Math.max(start, chunk.start)
// Example: 2MB PDF, 1MB image, 128KB PDF.js chunk.
// | pdf.js chunk |
// | A BIG IMAGE BLOB |
// | THE FULL PDF |
if (blobFetchDate < pdfCreatedAt) {
cachedBytes += chunkSize
} else {
// Blobs are fetched in bulk.
fetchedBytes += blobSize
}
}
return response.arrayBuffer()
})
.then(arrayBuffer => {
@ -134,6 +177,8 @@ function processPdfRequest(event, { file, clsiServerId, compileGroup }) {
const insertPosition = Math.max(chunk.start - start, 0)
reAssembledBlob.set(new Uint8Array(arrayBuffer), insertPosition)
})
trackStats({ size, cachedBytes, fetchedBytes })
return new Response(reAssembledBlob, {
status: 206,
headers: {
@ -152,16 +197,40 @@ function processPdfRequest(event, { file, clsiServerId, compileGroup }) {
}
/**
*
* @param {Response} response
*/
function getServerTime(response) {
const raw = response.headers.get('Date')
if (!raw) return undefined
return new Date(raw)
}
/**
*
* @param {Response} response
*/
function getResponseSize(response) {
const raw = response.headers.get('Content-Length')
if (!raw) return 0
return parseInt(raw, 10)
}
/**
* @param {Response} response
* @param {Object} body
*/
function handleCompileResponse(body) {
function handleCompileResponse(response, body) {
if (!body || body.status !== 'success') return
const pdfCreatedAt = getServerTime(response)
for (const file of body.outputFiles) {
if (file.path !== 'output.pdf') continue // not the pdf used for rendering
if (file.ranges) {
const { clsiServerId, compileGroup } = body
PDF_FILES.set(file.url, {
pdfCreatedAt,
file,
clsiServerId,
compileGroup,