Skip to content

Commit 0b8525d

Browse files
authored
Merge pull request #1967 from Automattic/fix/1966-publication-snapshot-reuse
Reuse canonical publication snapshots
2 parents 527d98d + 7f9c827 commit 0b8525d

3 files changed

Lines changed: 29 additions & 12 deletions

File tree

packages/runtime-cloudflare/src/worker.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -585,14 +585,20 @@ async function drainNextPublicationJob(env: RuntimeEnv, coordinator: RevisionCoo
585585
if (!plan) throw new Error("Publication progress plan is unavailable.")
586586
if (progress.next < plan.upsert.length) {
587587
const route = plan.upsert[progress.next]
588-
runtime = await bootRuntime(env.WORDPRESS_STATE_BUCKET, job.canonical, SITE_URL, await canonicalWordPressAuthConstants(env))
589-
const compiled = await compilePublicationRoutes(runtime, [route], SITE_URL)
590-
const page = compiled[0]
591-
const objectKey = await publishedPageObjectKey(job.canonical.revision, page.route)
592-
const snapshot: WordPressPageSnapshot = { schema: PUBLISHED_PAGE_SCHEMA, canonicalRevision: job.canonical.revision, route: page.route, status: page.status, statusText: page.statusText, headers: page.headers, body: page.body }
593-
const serialized = JSON.stringify(snapshot)
594-
if (new TextEncoder().encode(serialized).byteLength > MAX_PUBLISHED_PAGE_BYTES) throw new Error(`Affected publication artifact exceeds its size budget: ${page.route}.`)
595-
await putImmutableJson(env.WORDPRESS_STATE_BUCKET, objectKey, serialized)
588+
const objectKey = await publishedPageObjectKey(job.canonical.revision, route)
589+
let snapshot = await readWordPressPageSnapshot(env.WORDPRESS_STATE_BUCKET, objectKey, job.canonical.revision, route)
590+
if (!snapshot) {
591+
runtime = await bootRuntime(env.WORDPRESS_STATE_BUCKET, job.canonical, SITE_URL, await canonicalWordPressAuthConstants(env))
592+
const page = (await compilePublicationRoutes(runtime, [route], SITE_URL))[0]
593+
snapshot = { schema: PUBLISHED_PAGE_SCHEMA, canonicalRevision: job.canonical.revision, route: page.route, status: page.status, statusText: page.statusText, headers: page.headers, body: page.body }
594+
const serialized = JSON.stringify(snapshot)
595+
if (new TextEncoder().encode(serialized).byteLength > MAX_PUBLISHED_PAGE_BYTES) throw new Error(`Affected publication artifact exceeds its size budget: ${page.route}.`)
596+
try {
597+
await putImmutableJson(env.WORDPRESS_STATE_BUCKET, objectKey, serialized)
598+
} catch (error) {
599+
if (!await readWordPressPageSnapshot(env.WORDPRESS_STATE_BUCKET, objectKey, job.canonical.revision, route)) throw error
600+
}
601+
}
596602
await writePublicationProgress(env.WORDPRESS_STATE_BUCKET, job, { ...progress, next: progress.next + 1, rendered: [...progress.rendered, route] })
597603
return { schema: "wp-codebox/cloudflare-publication/v1", status: "rendered", jobKey: job.key, route }
598604
}
@@ -765,10 +771,9 @@ async function matchWordPressPageCache(request: Request, pointer: MarkdownPointe
765771
try {
766772
const cached = cache ? await cache.match(wordPressPageCacheKey(request, pointer)) : null
767773
if (cached) return pageCacheResponse(cached, request.method === "HEAD", "hit", "edge")
768-
const object = await bucket.get(await wordPressPageSnapshotKey(request, pointer))
769-
if (!object) return null
770-
const snapshot = JSON.parse(await object.text()) as WordPressPageSnapshot
771-
validateWordPressPageSnapshot(snapshot, pointer.revision, canonicalPublicRoute(request))
774+
const route = canonicalPublicRoute(request)
775+
const snapshot = await readWordPressPageSnapshot(bucket, await wordPressPageSnapshotKey(request, pointer), pointer.revision, route)
776+
if (!snapshot) return null
772777
if (snapshot.status !== 200) return null
773778
const response = new Response(snapshot.body, { status: snapshot.status, statusText: snapshot.statusText, headers: snapshot.headers })
774779
if (cache) await cache.put(wordPressPageCacheKey(request, pointer), response.clone())
@@ -778,6 +783,15 @@ async function matchWordPressPageCache(request: Request, pointer: MarkdownPointe
778783
}
779784
}
780785

786+
async function readWordPressPageSnapshot(bucket: R2Bucket, objectKey: string, canonicalRevision: string, route: string): Promise<WordPressPageSnapshot | null> {
787+
const object = await bucket.get(objectKey)
788+
if (!object) return null
789+
if (object.size > MAX_PUBLISHED_PAGE_BYTES) throw new Error(`Published page artifact exceeds its size budget: ${objectKey}.`)
790+
const snapshot = JSON.parse(await object.text()) as WordPressPageSnapshot
791+
validateWordPressPageSnapshot(snapshot, canonicalRevision, route)
792+
return snapshot
793+
}
794+
781795
async function cacheWordPressPage(request: Request, pointer: MarkdownPointer, response: Response, bucket: R2Bucket): Promise<Response> {
782796
if (!isCacheableWordPressPageRequest(request) || response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || response.headers.has("set-cookie")) return response
783797
if (request.method === "HEAD") return pageCacheResponse(response, true, "miss", "render")

scripts/cloudflare-local-gate.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ try {
4646
await assertAnonymousWordPressPage(origin, "homepage publication candidate")
4747
const initialPublication = await publishRoutes(["/", post.route])
4848
const updatedPost = await updatePost(adminHtml, post, initialPublication.revision)
49+
await assertAnonymousWordPressPage(new URL(post.route, origin), "updated post canonical snapshot before publication")
4950
// Restart with an explicit pending job to prove progress is durable rather than isolate-local.
5051
await stopWorker()
5152
await startWorker()

tests/cloudflare-runtime.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ test("Cloudflare runtime injects composable coordinators without moving PHP out
402402
assert.match(worker, /coordinator\.committed\(candidate\.coordinatorVersion\)/)
403403
assert.match(worker, /\.list\(\{ prefix: `\$\{R2_PUBLICATION_JOB_PREFIX\}\/`, limit: 16 \}\)/)
404404
assert.match(worker, /canonicalVersion: job\.coordinatorVersion/)
405+
assert.match(worker, /readWordPressPageSnapshot\(env\.WORDPRESS_STATE_BUCKET, objectKey, job\.canonical\.revision, route\)/)
406+
assert.match(worker, /if \(!await readWordPressPageSnapshot\(env\.WORDPRESS_STATE_BUCKET, objectKey, job\.canonical\.revision, route\)\) throw error/)
405407
const publicationDrain = worker.slice(worker.indexOf("async function drainNextPublicationJob"), worker.indexOf("async function compilePublicationRoutes"))
406408
assert.match(publicationDrain, /bootRuntime\(env\.WORDPRESS_STATE_BUCKET, job\.canonical, SITE_URL/)
407409
assert.match(publicationDrain, /compilePublicationRoutes\(runtime, \[route\], SITE_URL\)/)

0 commit comments

Comments
 (0)