Merge pull request #4128 from overleaf/jpa-expire-cache

[misc] serviceWorker: expire cache entries eventually

GitOrigin-RevId: d672ee5ac714d3c6f689ba635ec8e7397f02364c
This commit is contained in:
Eric Mc Sween 2021-06-08 09:05:25 -04:00 committed by Copybot
parent d56d4c3b4a
commit d6b80789af

View file

@ -1,9 +1,54 @@
import { v4 as uuid } from 'uuid'
const COMPILE_REQUEST_MATCHER = /^\/project\/[0-9a-f]{24}\/compile$/
const PDF_REQUEST_MATCHER = /^\/project\/[0-9a-f]{24}\/.*\/output.pdf$/
const PDF_JS_CHUNK_SIZE = 128 * 1024
const MAX_SUBREQUEST_COUNT = 8
const MAX_SUBREQUEST_BYTES = 4 * PDF_JS_CHUNK_SIZE
const PDF_FILES = new Map()
// Each compile request defines a context (essentially the specific pdf file for
// 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()
function registerPdfContext(clientId, path, context) {
let clientMap = pdfContext.get(clientId)
if (!clientMap) {
clientMap = new Map()
pdfContext.set(clientId, clientMap)
// 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)
}
function getPdfContext(clientId, path) {
const clientMap = pdfContext.get(clientId)
const context = clientMap && clientMap.get(path)
return context
}
function expirePdfContexts() {
// discard client maps for clients that are no longer connected
const currentClientSet = new Set()
self.clients.matchAll().then(function (clientList) {
clientList.forEach(client => {
currentClientSet.add(client.id)
})
pdfContext.forEach((map, clientId) => {
if (!currentClientSet.has(clientId)) {
pdfContext.delete(clientId)
}
})
})
}
const METRICS = {
id: uuid(),
@ -65,9 +110,11 @@ function onFetch(event) {
return processCompileRequest(event)
}
const ctx = PDF_FILES.get(path)
if (ctx) {
return processPdfRequest(event, ctx)
if (path.match(PDF_REQUEST_MATCHER)) {
const ctx = getPdfContext(event.clientId, path)
if (ctx) {
return processPdfRequest(event, ctx)
}
}
// other request, ignore
@ -79,7 +126,7 @@ function processCompileRequest(event) {
if (response.status !== 200) return response
return response.json().then(body => {
handleCompileResponse(response, body)
handleCompileResponse(event, response, body)
// Send the service workers metrics to the frontend.
body.serviceWorkerMetrics = METRICS
return new Response(JSON.stringify(body), response)
@ -308,7 +355,7 @@ function processPdfRequest(
*/
function getServerTime(response) {
const raw = response.headers.get('Date')
if (!raw) return undefined
if (!raw) return new Date()
return new Date(raw)
}
@ -323,10 +370,11 @@ function getResponseSize(response) {
}
/**
* @param {FetchEvent} event
* @param {Response} response
* @param {Object} body
*/
function handleCompileResponse(response, body) {
function handleCompileResponse(event, response, body) {
if (!body || body.status !== 'success') return
const pdfCreatedAt = getServerTime(response)
@ -336,7 +384,7 @@ function handleCompileResponse(response, body) {
if (file.ranges) {
file.ranges.forEach(backFillEdgeBounds)
const { clsiServerId, compileGroup } = body
PDF_FILES.set(file.url, {
registerPdfContext(event.clientId, file.url, {
pdfCreatedAt,
file,
clsiServerId,