From fa5622671aeaf0c0db054826c9a374243e870ccd Mon Sep 17 00:00:00 2001 From: CNduka001 Date: Tue, 30 Jun 2026 09:41:23 +0100 Subject: [PATCH] docs: correct replayFromLedger idempotency claims in JSDoc and swagger summary Updates the replayFromLedger JSDoc to clarify that the @@unique([transactionHash, eventType]) constraint guarantees StreamEvent row dedup, but Stream.withdrawnAmount increments in handleTokensWithdrawn are NOT idempotent on replay. Updates the /v1/admin/indexer/replay swagger summary to match. Refs #808 --- backend/src/routes/v1/admin.routes.ts | 2 +- backend/src/services/indexerService.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/src/routes/v1/admin.routes.ts b/backend/src/routes/v1/admin.routes.ts index 253f112..f6427c7 100644 --- a/backend/src/routes/v1/admin.routes.ts +++ b/backend/src/routes/v1/admin.routes.ts @@ -222,7 +222,7 @@ router.post('/indexer/reset', async (req: Request, res: Response) => { * /v1/admin/indexer/replay: * post: * tags: [Admin] - * summary: Replay events from a given ledger (idempotent) + * summary: Replay events from a given ledger (StreamEvent rows deduplicated; stream mutations not idempotent — see indexerService.ts JSDoc) * security: [{ adminAuth: [] }] * parameters: * - in: query diff --git a/backend/src/services/indexerService.ts b/backend/src/services/indexerService.ts index 4dc6a2d..d386be4 100644 --- a/backend/src/services/indexerService.ts +++ b/backend/src/services/indexerService.ts @@ -38,8 +38,13 @@ export async function resetIndexer(toLedger: number): Promise { /** * Replay events from a given ledger by resetting state and triggering a poll. - * Deduplication in the worker (transactionHash + eventType + ledger) ensures - * no duplicate StreamEvent rows are created. + * The @@unique([transactionHash, eventType]) constraint on StreamEvent + * guarantees no duplicate StreamEvent rows are created on replay. + * + * CAVEAT: This dedup does NOT apply to stream state mutations. + * Stream.withdrawnAmount (handleTokensWithdrawn, soroban-event-worker.ts:635) + * is incremented unconditionally on every replay, so replay is NOT fully + * idempotent. See issue #808 for the withdrawnAmount idempotency fix. */ export async function replayFromLedger(fromLedger: number): Promise { await resetIndexer(fromLedger);