Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/src/routes/v1/admin.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions backend/src/services/indexerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ export async function resetIndexer(toLedger: number): Promise<void> {

/**
* 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<void> {
await resetIndexer(fromLedger);
Expand Down
Loading