mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
perf(desktop): collect orphaned draft blobs after the window is up (#49797)
This commit is contained in:
@@ -17,17 +17,29 @@ describe("draft store", () => {
|
||||
expect(database.db.all(sql`SELECT key FROM document`)).toEqual([])
|
||||
})
|
||||
|
||||
test("stores blobs by content hash and collects unreferenced ones on open", () => {
|
||||
test("stores blobs by content hash and collects unreferenced ones shortly after open", async () => {
|
||||
const database = openDatabase(":memory:")
|
||||
const first = createDraftStore(database.db, { delay: 1_000 })
|
||||
let clock = 0
|
||||
const first = createDraftStore(database.db, { delay: 1_000, now: () => clock })
|
||||
const used = first.putBlob(new Uint8Array([1, 2, 3]))
|
||||
const unused = first.putBlob(new Uint8Array([4, 5, 6]))
|
||||
expect(first.putBlob(new Uint8Array([1, 2, 3]))).toBe(used)
|
||||
first.set("doc", JSON.stringify({ parts: [{ blob: { id: used } }] }))
|
||||
first.flush()
|
||||
const second = createDraftStore(database.db, { delay: 1_000 })
|
||||
expect(second.getBlob(used)).toEqual(new Uint8Array([1, 2, 3]))
|
||||
expect(second.getBlob(unused)).toBeNull()
|
||||
first.close()
|
||||
// The startup collection runs off the window's critical path and keeps the usual grace, so a
|
||||
// blob from a session that ended within the grace period is still there...
|
||||
const second = createDraftStore(database.db, { delay: 1_000, now: () => clock, collectDelay: 0 })
|
||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
||||
expect(second.getBlob(unused)).toEqual(new Uint8Array([4, 5, 6]))
|
||||
second.close()
|
||||
// ...and gone once the grace has passed.
|
||||
clock = blobGrace + 1
|
||||
const third = createDraftStore(database.db, { delay: 1_000, now: () => clock, collectDelay: 0 })
|
||||
await new Promise((resolve) => setTimeout(resolve, 5))
|
||||
expect(third.getBlob(used)).toEqual(new Uint8Array([1, 2, 3]))
|
||||
expect(third.getBlob(unused)).toBeNull()
|
||||
third.close()
|
||||
})
|
||||
|
||||
test("collects a retired chunk only once it is unreferenced and past the grace period", () => {
|
||||
|
||||
@@ -27,11 +27,14 @@ const referenced = (value: unknown) => sql`
|
||||
|
||||
export function createDraftStore(
|
||||
db: Database,
|
||||
input: { delay?: number; onError?: (error: unknown) => void; now?: () => number } = {},
|
||||
input: { delay?: number; collectDelay?: number; onError?: (error: unknown) => void; now?: () => number } = {},
|
||||
) {
|
||||
const now = input.now ?? Date.now
|
||||
// Nothing outside this process can hold a blob id at startup, so no grace applies.
|
||||
collectBlobs(db, Infinity)
|
||||
// Orphans left by an earlier session are collected once the window is up rather than before it:
|
||||
// the scan walks every stored document, and the usual grace keeps anything a renderer has
|
||||
// uploaded in the meantime.
|
||||
const startup = setTimeout(() => collectBlobs(db, now() - blobGrace), input.collectDelay ?? 10_000)
|
||||
startup.unref()
|
||||
let collected = now()
|
||||
let orphans = false
|
||||
const byKey = eq(document.key, sql.placeholder("key"))
|
||||
@@ -104,7 +107,10 @@ export function createDraftStore(
|
||||
return data ? (data as Uint8Array<ArrayBuffer>) : null
|
||||
},
|
||||
flush: writer.flush,
|
||||
close: writer.close,
|
||||
close() {
|
||||
clearTimeout(startup)
|
||||
writer.close()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user