Composite: don't undo writes made while a full sync is scanning
A write made during a full sync could be scanned in the replica but not in the (read-ahead) source scan, so the diff removed it, and echo tracking then consumed the server's echo. Keys written during a sync are now left out of the diff. Deterministic regression test with a read-ahead source. Also exclude -0 from the Extended JSON property test (JSON has no -0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qxbkp4tA7G5SCWjzWuxmE
This commit is contained in:
@@ -3,7 +3,7 @@ import { KVStorageComposite } from '../../src/kv-storage/kv-storage-composite.js
|
||||
import { KVStorageInMemory } from '../../src/kv-storage/kv-storage-in-memory.js'
|
||||
import type { BaseKVStorage } from '../../src/kv-storage/base-kv-storage.js'
|
||||
import { DocumentStorageKV } from '../../src/document-storage/document-storage-kv.js'
|
||||
import { collect } from '../helpers.js'
|
||||
import { collect, KVStorageCounting } from '../helpers.js'
|
||||
import { runKVStorageConformance } from './kv-storage.conformance.js'
|
||||
import { TestNetwork } from './sync-helpers.js'
|
||||
|
||||
@@ -141,4 +141,39 @@ describe('KVStorageComposite sync', () => {
|
||||
network.online = false
|
||||
expect((await collect(users.find({ age: { $gt: 35 } }))).map((r) => r.key)).toEqual(['2'])
|
||||
})
|
||||
|
||||
it('does not undo writes made while a full sync is scanning', async () => {
|
||||
// A source whose scan reads ahead (like the HTTP stream) and pauses after its first entry.
|
||||
let release!: () => void
|
||||
let reached!: () => void
|
||||
const gate = new Promise<void>((r) => (release = r))
|
||||
const paused = new Promise<void>((r) => (reached = r))
|
||||
const inner = new KVStorageInMemory()
|
||||
await inner.writeMany([
|
||||
['a', 1],
|
||||
['c', 3],
|
||||
])
|
||||
const source = new KVStorageCounting(inner)
|
||||
const scan = source.scan.bind(source)
|
||||
source.scan = async function* (options) {
|
||||
let first = true
|
||||
for (const entry of await collect(scan(options))) {
|
||||
yield entry
|
||||
if (first && options?.values) ((first = false), reached(), await gate)
|
||||
}
|
||||
}
|
||||
const persistent = new KVStorageInMemory()
|
||||
await persistent.writeMany([['a0', 'stale']]) // a replica copy from before, without sync metadata
|
||||
const db = new KVStorageComposite({ source, replicas: [persistent] })
|
||||
void db.start()
|
||||
await paused // the diff has passed 'b' in the source but not in the replica
|
||||
await db.writeMany([['b', 2]])
|
||||
release()
|
||||
await db.synced()
|
||||
expect(await collect(db.scan({ values: true }))).toEqual([
|
||||
['a', 1],
|
||||
['b', 2],
|
||||
['c', 3],
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('ExtendedJson', () => {
|
||||
fc.bigInt(),
|
||||
fc.uint8Array(),
|
||||
fc.string(),
|
||||
fc.double({ noNaN: true, noDefaultInfinity: true }),
|
||||
fc.double({ noNaN: true, noDefaultInfinity: true }).map((x) => (x === 0 ? 0 : x)) /* JSON has no -0 */,
|
||||
fc.boolean(),
|
||||
fc.constant(null),
|
||||
fc.array(tie('value')),
|
||||
|
||||
Reference in New Issue
Block a user