From 24c93c4269aed29a1e24e8cf152baa6801d0ba3c Mon Sep 17 00:00:00 2001 From: James Date: Thu, 10 Sep 2026 11:17:57 +0100 Subject: [PATCH] perf(cache): remove eager response store tag index --- .../workers-response-store/src/metadata-do.ts | 109 ++---------------- .../workers-response-store/tests/e2e.test.mjs | 17 +-- 2 files changed, 14 insertions(+), 112 deletions(-) diff --git a/packages/workers-response-store/src/metadata-do.ts b/packages/workers-response-store/src/metadata-do.ts index 8dd6c3c34..9a21da165 100644 --- a/packages/workers-response-store/src/metadata-do.ts +++ b/packages/workers-response-store/src/metadata-do.ts @@ -26,11 +26,6 @@ type WriteReservation = { revision: number; }; -type TagIndexEntry = { - keyHash: string; - tag: string; -}; - export type CacheMetadataStub = DurableObjectStub & { beginWrite(keyHash: string, cacheKey: string): Promise; reserveWrite( @@ -63,7 +58,6 @@ export type CacheMetadataStub = DurableObjectStub & { getEntriesMatching(options: ResponseStoreRefreshOptions): Promise; purgeMatching(options: ResponseStorePurgeOptions): Promise; inspect(): Promise; - inspectTagIndex(): Promise; }; type EntryRow = Record & { @@ -85,11 +79,6 @@ type EntryRow = Record & { tombstoned: number; }; -type TagRow = Record & { - key_hash: string; - tag: string; -}; - const MAX_SQL_PARAMETERS = 100; const ORPHAN_RETENTION_MS = 60 * 60 * 1000; const ORPHAN_CLEANUP_LIMIT = 100; @@ -212,38 +201,6 @@ export class CacheMetadata extends DurableObject { ); CREATE INDEX IF NOT EXISTS pending_objects_created_at ON pending_objects(created_at); `); - - const hasBackfilledTagIndex = - ctx.storage.sql - .exec<{ version: number }>( - "SELECT version FROM metadata_schema_migrations WHERE version = 1", - ) - .toArray().length > 0; - - if (!hasBackfilledTagIndex) { - ctx.storage.transactionSync(() => { - const rows = ctx.storage.sql - .exec<{ key_hash: string; cache_tags: string | null }>( - `SELECT key_hash, cache_tags FROM entries - WHERE tombstoned = 0 AND active_revision IS NOT NULL`, - ) - .toArray(); - - for (const row of rows) { - const tags = JSON.parse(row.cache_tags ?? "[]") as string[]; - - for (const tag of normalizeTags(tags)) { - ctx.storage.sql.exec( - "INSERT OR IGNORE INTO entry_tags (tag, key_hash) VALUES (?, ?)", - tag, - row.key_hash, - ); - } - } - - ctx.storage.sql.exec("INSERT INTO metadata_schema_migrations (version) VALUES (1)"); - }); - } }); } @@ -266,43 +223,18 @@ export class CacheMetadata extends DurableObject { .toArray(); } - const matches = new Map(); - const tags = normalizeTags(options.tags ?? []); - - for (let offset = 0; offset < tags.length; offset += MAX_SQL_PARAMETERS) { - const batch = tags.slice(offset, offset + MAX_SQL_PARAMETERS); - const placeholders = batch.map(() => "?").join(", "); - const rows = this.ctx.storage.sql - .exec( - `SELECT DISTINCT entries.* FROM entries - INNER JOIN entry_tags ON entry_tags.key_hash = entries.key_hash - WHERE entries.tombstoned = 0 AND entries.active_revision IS NOT NULL - AND entry_tags.tag IN (${placeholders})`, - ...batch, - ) - .toArray(); - - for (const row of rows) { - matches.set(row.key_hash, row); - } - } - + const tags = new Set(normalizeTags(options.tags ?? [])); const prefixes = options.pathPrefixes ?? []; - if (prefixes.length) { - const rows = this.ctx.storage.sql - .exec( - "SELECT * FROM entries WHERE tombstoned = 0 AND active_revision IS NOT NULL", - ) - .toArray(); - - for (const row of rows) { - if (prefixes.some((prefix) => row.cache_key.startsWith(prefix))) { - matches.set(row.key_hash, row); - } - } - } - - return [...matches.values()]; + return this.ctx.storage.sql + .exec("SELECT * FROM entries WHERE tombstoned = 0 AND active_revision IS NOT NULL") + .toArray() + .filter( + (row) => + prefixes.some((prefix) => row.cache_key.startsWith(prefix)) || + normalizeTags(JSON.parse(row.cache_tags ?? "[]") as string[]).some((tag) => + tags.has(tag), + ), + ); } async trackPendingObjects(objectKeys: string[], createdAt: number): Promise { @@ -541,17 +473,6 @@ export class CacheMetadata extends DurableObject { ); const published = update.rowsWritten === 1; - if (published) { - this.ctx.storage.sql.exec("DELETE FROM entry_tags WHERE key_hash = ?", keyHash); - - for (const tag of normalizeTags(metadata.cacheTags)) { - this.ctx.storage.sql.exec( - "INSERT INTO entry_tags (tag, key_hash) VALUES (?, ?)", - tag, - keyHash, - ); - } - } const entry: StoredEntry = { keyHash, @@ -654,7 +575,6 @@ export class CacheMetadata extends DurableObject { "DELETE FROM revalidation_claims WHERE key_hash = ?", row.key_hash, ); - this.ctx.storage.sql.exec("DELETE FROM entry_tags WHERE key_hash = ?", row.key_hash); } return matches.map((row) => ({ @@ -671,11 +591,4 @@ export class CacheMetadata extends DurableObject { .toArray(); return storedEntriesFromRows(rows); } - - inspectTagIndex(): TagIndexEntry[] { - return this.ctx.storage.sql - .exec("SELECT key_hash, tag FROM entry_tags ORDER BY tag, key_hash") - .toArray() - .map((row) => ({ keyHash: row.key_hash, tag: row.tag })); - } } diff --git a/packages/workers-response-store/tests/e2e.test.mjs b/packages/workers-response-store/tests/e2e.test.mjs index 7a339d019..4189901f2 100644 --- a/packages/workers-response-store/tests/e2e.test.mjs +++ b/packages/workers-response-store/tests/e2e.test.mjs @@ -113,10 +113,6 @@ async function metadata() { return (await metadataStub()).inspect(); } -async function tagIndex() { - return (await metadataStub()).inspectTagIndex(); -} - async function r2Objects() { const bucket = await mf.getR2Bucket("CACHE_BODIES", "user-worker"); return bucket.list(); @@ -422,7 +418,7 @@ test("refresh accepts more tag selectors than one SQLite parameter batch", async assert.equal(await (await read("/refresh-many-tags")).text(), "refreshed"); }); -test("refresh and purge use a reverse tag-to-entry index that follows publication", async () => { +test("refresh and purge select entries from their stored tags", async () => { await put("/tag-index", "seed", { tags: ["Original", "Shared"], revalidator: { @@ -432,25 +428,18 @@ test("refresh and purge use a reverse tag-to-entry index that follows publicatio }, }); - assert.deepEqual( - (await tagIndex()).map(({ tag }) => tag), - ["original", "shared"], - ); + assert.deepEqual((await metadata())[0].cacheTags, ["Original", "Shared"]); assert.deepEqual((await refreshSelectors({ tags: ["ORIGINAL"] })).json, { backingStoreUpdated: true, edgePurgeAccepted: false, }); - assert.deepEqual( - (await tagIndex()).map(({ tag }) => tag), - ["replacement"], - ); + assert.deepEqual((await metadata())[0].cacheTags, ["Replacement"]); assert.deepEqual((await refreshSelectors({ tags: ["original"] })).json, { backingStoreUpdated: false, edgePurgeAccepted: false, }); await purge({ tags: ["REPLACEMENT"] }); - assert.deepEqual(await tagIndex(), []); assert.equal((await read("/tag-index")).status, 404); assert.ok((await (await metadataStub()).getTagExpiration(["replacement"])) > 0); });