mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #4164 from overleaf/jpa-per-client-metrics
[misc] serviceWorker: per client metrics GitOrigin-RevId: 80ec838a8f96837184c60f4c4d7c3a162ebd8634
This commit is contained in:
parent
4c6933b5b8
commit
af631dc783
1 changed files with 74 additions and 47 deletions
|
@ -11,30 +11,56 @@ const MAX_SUBREQUEST_BYTES = 4 * PDF_JS_CHUNK_SIZE
|
|||
// that compile), requests for that pdf file can use the hashes in the compile
|
||||
// response, which are stored in the context.
|
||||
|
||||
const pdfContext = new Map()
|
||||
const CLIENT_CONTEXT = new Map()
|
||||
|
||||
function registerPdfContext(clientId, path, context) {
|
||||
let clientMap = pdfContext.get(clientId)
|
||||
if (!clientMap) {
|
||||
clientMap = new Map()
|
||||
pdfContext.set(clientId, clientMap)
|
||||
/**
|
||||
* @param {string} clientId
|
||||
*/
|
||||
function getClientContext(clientId) {
|
||||
let clientContext = CLIENT_CONTEXT.get(clientId)
|
||||
if (!clientContext) {
|
||||
const pdfs = new Map()
|
||||
const metrics = {
|
||||
id: uuid(),
|
||||
epoch: Date.now(),
|
||||
cachedBytes: 0,
|
||||
fetchedBytes: 0,
|
||||
requestedBytes: 0,
|
||||
}
|
||||
clientContext = { pdfs, metrics }
|
||||
CLIENT_CONTEXT.set(clientId, clientContext)
|
||||
// clean up old client maps
|
||||
expirePdfContexts()
|
||||
}
|
||||
// we only need to keep the last 3 contexts
|
||||
for (const key of clientMap.keys()) {
|
||||
if (clientMap.size < 3) {
|
||||
break
|
||||
}
|
||||
clientMap.delete(key) // the map keys are returned in insertion order, so we are deleting the oldest entry here
|
||||
}
|
||||
clientMap.set(path, context)
|
||||
return clientContext
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} clientId
|
||||
* @param {string} path
|
||||
* @param {Object} pdfContext
|
||||
*/
|
||||
function registerPdfContext(clientId, path, pdfContext) {
|
||||
const clientContext = getClientContext(clientId)
|
||||
const { pdfs, metrics } = clientContext
|
||||
pdfContext.metrics = metrics
|
||||
// we only need to keep the last 3 contexts
|
||||
for (const key of pdfs.keys()) {
|
||||
if (pdfs.size < 3) {
|
||||
break
|
||||
}
|
||||
pdfs.delete(key) // the map keys are returned in insertion order, so we are deleting the oldest entry here
|
||||
}
|
||||
pdfs.set(path, pdfContext)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} clientId
|
||||
* @param {string} path
|
||||
*/
|
||||
function getPdfContext(clientId, path) {
|
||||
const clientMap = pdfContext.get(clientId)
|
||||
const context = clientMap && clientMap.get(path)
|
||||
return context
|
||||
const { pdfs } = getClientContext(clientId)
|
||||
return pdfs.get(path)
|
||||
}
|
||||
|
||||
function expirePdfContexts() {
|
||||
|
@ -44,51 +70,45 @@ function expirePdfContexts() {
|
|||
clientList.forEach(client => {
|
||||
currentClientSet.add(client.id)
|
||||
})
|
||||
pdfContext.forEach((map, clientId) => {
|
||||
CLIENT_CONTEXT.forEach((map, clientId) => {
|
||||
if (!currentClientSet.has(clientId)) {
|
||||
pdfContext.delete(clientId)
|
||||
CLIENT_CONTEXT.delete(clientId)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const METRICS = {
|
||||
id: uuid(),
|
||||
epoch: Date.now(),
|
||||
cachedBytes: 0,
|
||||
fetchedBytes: 0,
|
||||
requestedBytes: 0,
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} metrics
|
||||
* @param {number} size
|
||||
* @param {number} cachedBytes
|
||||
* @param {number} fetchedBytes
|
||||
*/
|
||||
function trackStats({ size, cachedBytes, fetchedBytes }) {
|
||||
METRICS.cachedBytes += cachedBytes
|
||||
METRICS.fetchedBytes += fetchedBytes
|
||||
METRICS.requestedBytes += size
|
||||
function trackDownloadStats(metrics, { size, cachedBytes, fetchedBytes }) {
|
||||
metrics.cachedBytes += cachedBytes
|
||||
metrics.fetchedBytes += fetchedBytes
|
||||
metrics.requestedBytes += size
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} metrics
|
||||
* @param {boolean} sizeDiffers
|
||||
* @param {boolean} mismatch
|
||||
* @param {boolean} success
|
||||
*/
|
||||
function trackChunkVerify({ sizeDiffers, mismatch, success }) {
|
||||
function trackChunkVerify(metrics, { sizeDiffers, mismatch, success }) {
|
||||
if (sizeDiffers) {
|
||||
METRICS.chunkVerifySizeDiffers |= 0
|
||||
METRICS.chunkVerifySizeDiffers += 1
|
||||
metrics.chunkVerifySizeDiffers |= 0
|
||||
metrics.chunkVerifySizeDiffers += 1
|
||||
}
|
||||
if (mismatch) {
|
||||
METRICS.chunkVerifyMismatch |= 0
|
||||
METRICS.chunkVerifyMismatch += 1
|
||||
metrics.chunkVerifyMismatch |= 0
|
||||
metrics.chunkVerifyMismatch += 1
|
||||
}
|
||||
if (success) {
|
||||
METRICS.chunkVerifySuccess |= 0
|
||||
METRICS.chunkVerifySuccess += 1
|
||||
metrics.chunkVerifySuccess |= 0
|
||||
metrics.chunkVerifySuccess += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +142,9 @@ function onFetch(event) {
|
|||
// other request, ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FetchEvent} event
|
||||
*/
|
||||
function processCompileRequest(event) {
|
||||
event.respondWith(
|
||||
fetch(event.request).then(response => {
|
||||
|
@ -129,8 +152,11 @@ function processCompileRequest(event) {
|
|||
|
||||
return response.json().then(body => {
|
||||
handleCompileResponse(event, response, body)
|
||||
|
||||
// Send the service workers metrics to the frontend.
|
||||
body.serviceWorkerMetrics = METRICS
|
||||
const { metrics } = getClientContext(event.clientId)
|
||||
body.serviceWorkerMetrics = metrics
|
||||
|
||||
return new Response(JSON.stringify(body), response)
|
||||
})
|
||||
})
|
||||
|
@ -180,10 +206,11 @@ function handleProbeRequest(request, file) {
|
|||
* @param {string} clsiServerId
|
||||
* @param {string} compileGroup
|
||||
* @param {Date} pdfCreatedAt
|
||||
* @param {Object} metrics
|
||||
*/
|
||||
function processPdfRequest(
|
||||
event,
|
||||
{ file, clsiServerId, compileGroup, pdfCreatedAt }
|
||||
{ file, clsiServerId, compileGroup, pdfCreatedAt, metrics }
|
||||
) {
|
||||
const response = handleProbeRequest(event.request, file)
|
||||
if (response) {
|
||||
|
@ -313,18 +340,18 @@ function processPdfRequest(
|
|||
.then(response => response.arrayBuffer())
|
||||
.then(arrayBuffer => {
|
||||
const fullBlob = new Uint8Array(arrayBuffer)
|
||||
const metrics = {}
|
||||
const stats = {}
|
||||
if (reAssembledBlob.byteLength !== fullBlob.byteLength) {
|
||||
metrics.sizeDiffers = true
|
||||
stats.sizeDiffers = true
|
||||
} else if (
|
||||
!reAssembledBlob.every((v, idx) => v === fullBlob[idx])
|
||||
) {
|
||||
metrics.mismatch = true
|
||||
stats.mismatch = true
|
||||
} else {
|
||||
metrics.success = true
|
||||
stats.success = true
|
||||
}
|
||||
trackChunkVerify(metrics)
|
||||
if (metrics.success === true) {
|
||||
trackChunkVerify(metrics, stats)
|
||||
if (stats.success === true) {
|
||||
return reAssembledBlob
|
||||
} else {
|
||||
return fullBlob
|
||||
|
@ -333,7 +360,7 @@ function processPdfRequest(
|
|||
}
|
||||
|
||||
return verifyProcess.then(blob => {
|
||||
trackStats({ size, cachedBytes, fetchedBytes })
|
||||
trackDownloadStats(metrics, { size, cachedBytes, fetchedBytes })
|
||||
return new Response(blob, {
|
||||
status: 206,
|
||||
headers: {
|
||||
|
|
Loading…
Reference in a new issue