Skip reparsing zero-owner backup databases on live db change (#8043)

Backups that own no sessions have nothing to reclaim, so reparsing
them on every live-db update was wasted work; only reparse siblings
that still hold claimable session ownership.
This commit is contained in:
Jinjing 2026-07-10 00:14:01 -07:00 committed by GitHub
parent e75343b1c5
commit bcd16e56fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 37 additions and 4 deletions

View File

@ -476,6 +476,34 @@ describe('scanOpenCodeUsageDatabases', () => {
expect(sessionOne?.eventCount).toBe(1)
})
it('reuses a fully-duplicate backup instead of reparsing it when the live db changes', async () => {
// The backup only holds stale copies of sessions the canonical db already
// owns, so it owns nothing. When the live db changes it must not be
// demoted back into the parse set — there is no claim to reclaim.
const canonicalPath = writeSessionTotalsDb('opencode.db', [['session-1', 1000]])
writeSessionTotalsDb('opencode-backup.db', [['session-1', 400]])
const first = await scanOpenCodeUsageDatabases([], [])
const firstBackup = first.processedDatabases.find((database) =>
database.path.endsWith('opencode-backup.db')
)
expect(firstBackup?.ownedSessionIds).toEqual([])
const db = new Database(canonicalPath)
insertSessionTotalsRow(db, 'session-2', 200)
db.close()
const second = await scanOpenCodeUsageDatabases([], first.processedDatabases)
const secondBackup = second.processedDatabases.find((database) =>
database.path.endsWith('opencode-backup.db')
)
// A reused cache entry is the same object; a reparse would produce a new one.
expect(secondBackup).toBe(firstBackup)
expect(
second.dailyAggregates.reduce((total, aggregate) => total + aggregate.inputTokens, 0)
).toBe(1200)
})
it('lets the live database reclaim sessions after a sticky backup-only claim', async () => {
// First scan only has the backup (e.g. live db temporarily missing), so it
// owns session-1 at the stale snapshot. When opencode.db reappears with

View File

@ -935,11 +935,16 @@ export async function scanOpenCodeUsageDatabases(
// Why: a sticky backup claim from a scan where opencode.db was missing would
// otherwise freeze a still-growing session at the backup snapshot when the
// live db reappears. Reparse lower-priority siblings whenever a higher-
// priority path is being re-evaluated so the live db can reclaim sessions
// without double-counting cached backup aggregates.
// live db reappears. Reparse a lower-priority sibling only when it still owns
// sessions a higher-priority path could reclaim; a sibling that owns nothing
// (the common case once the live db has claimed every shared session) has no
// claim to give back, so reparsing it every time the live db changes is pure
// work.
const demotedReusePaths: string[] = []
for (const dbPath of reusedByPath.keys()) {
for (const [dbPath, reused] of reusedByPath) {
if ((reused.ownedSessionIds?.length ?? 0) === 0) {
continue
}
const higherPriorityParsing = pathsToParse.some(
(candidate) => compareOpenCodeClaimPriority(candidate, dbPath) < 0
)