Skip to content

Commit ef15b7f

Browse files
committed
refactor: simplify createWriteCheckpoint Cosmos DB HEAD capture
Replace the polling loop in createWriteCheckpoint with a direct read of the current storage checkpoint LSN. The polling loop (get baseline → write sentinel → poll until LSN advances) was unnecessary: with >= comparison, the poll always resolved on the first iteration because the baseline LSN was already in storage. The loop was effectively just reading the current LSN. The simplified version reads the current checkpoint LSN directly and uses it as HEAD. This is correct because: - createReplicationHead already wrote a sentinel to _powersync_checkpoints - The sentinel guarantees the streaming loop will advance past this point - The HEAD just needs to be a valid LSN at or before the sentinel - The sync stream resolves the write checkpoint when replication advances Removes ~15 lines of polling/timeout code.
1 parent 04063d1 commit ef15b7f

1 file changed

Lines changed: 17 additions & 22 deletions

File tree

packages/service-core/src/util/checkpointing.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,24 @@ export async function createWriteCheckpoint(options: CreateWriteCheckpointOption
2020
let head = currentCheckpoint;
2121

2222
if (head == null) {
23-
// Cosmos DB: HEAD unknown. Poll storage until the streaming loop
24-
// processes the sentinel and advances the checkpoint LSN.
25-
// On Cosmos DB, wall-clock LSNs have second precision — the sentinel
26-
// commit may produce the same LSN as the baseline if both fall in the
27-
// same wall-clock second. Use >= (not >) so the poll resolves as soon
28-
// as any commit happens, even at the same second. The sentinel write
29-
// guarantees the streaming loop will process at least one event.
30-
const baselineCheckpoint = await syncBucketStorage.getCheckpoint();
31-
const baselineLsn = baselineCheckpoint?.lsn ?? '';
32-
33-
const timeout = 30_000;
34-
const start = Date.now();
35-
while (Date.now() - start < timeout) {
36-
const cp = await syncBucketStorage.getCheckpoint();
37-
if (cp?.lsn && cp.lsn >= baselineLsn) {
38-
head = cp.lsn;
39-
break;
40-
}
41-
await new Promise((r) => setTimeout(r, 50));
42-
}
43-
if (!head) {
44-
throw new ServiceError(ErrorCode.PSYNC_S2302, 'Timeout waiting for sentinel checkpoint');
23+
// Cosmos DB: operationTime / clusterTime not available on regular
24+
// commands, so createReplicationHead cannot capture the HEAD directly.
25+
// Instead, use the current storage checkpoint LSN. This is valid because:
26+
//
27+
// 1. createReplicationHead already wrote a sentinel to _powersync_checkpoints,
28+
// guaranteeing the streaming loop will advance past this point.
29+
// 2. The sync stream's watchCheckpointChanges resolves the write checkpoint
30+
// when replication advances past the stored HEAD.
31+
// 3. The sentinel ensures forward progress even on an idle system.
32+
//
33+
// The HEAD doesn't need to be the exact sentinel position — it just needs
34+
// to be a valid LSN at or before the sentinel. The current storage LSN
35+
// satisfies this because it was committed before the sentinel was written.
36+
const cp = await syncBucketStorage.getCheckpoint();
37+
if (!cp?.lsn) {
38+
throw new ServiceError(ErrorCode.PSYNC_S2302, 'Cannot create write checkpoint: no replication checkpoint available');
4539
}
40+
head = cp.lsn;
4641
}
4742

4843
const writeCheckpoint = await syncBucketStorage.createManagedWriteCheckpoint({

0 commit comments

Comments
 (0)