Telegram (ask questions / claim the issue here first): https://t.me/+DOylgFv1jyJlNzM0
Labels: bug, backend, Stellar Wave
An admin replay can run the indexer concurrently with a scheduled poll, and the slower one can rewind the cursor.
At soroban-event-worker.ts:132-140, triggerPoll() calls fetchAndProcessEvents() directly, while the scheduled poll() wraps it in this.activeBatch. There's no mutex between them, so a replay and a scheduled poll can be in flight at once. Both write a cursor when they finish, and whichever writes last wins, so a slower run can regress lastCursor. On top of that, waitForDrain() only awaits activeBatch, so a replay batch is invisible to graceful shutdown and can get cut off mid-batch.
What the fix has to hold to
- only one fetchAndProcessEvents runs at a time, whether it came from the scheduler or triggerPoll
- shutdown waits for a replay batch to finish, not just scheduled ones
- the cursor can't be moved backwards by an overlapping run
Done when
Where to start
backend/src/workers/soroban-event-worker.ts, with a look at backend/src/services/indexerService.ts. Removing the legacy SorobanIndexerService is tracked separately, leave it. The guard is small; the shutdown-drain part is where it's easy to miss a case.
Labels:
bug,backend,Stellar WaveAn admin replay can run the indexer concurrently with a scheduled poll, and the slower one can rewind the cursor.
At
soroban-event-worker.ts:132-140,triggerPoll()callsfetchAndProcessEvents()directly, while the scheduledpoll()wraps it inthis.activeBatch. There's no mutex between them, so a replay and a scheduled poll can be in flight at once. Both write a cursor when they finish, and whichever writes last wins, so a slower run can regresslastCursor. On top of that,waitForDrain()only awaitsactiveBatch, so a replay batch is invisible to graceful shutdown and can get cut off mid-batch.What the fix has to hold to
Done when
Where to start
backend/src/workers/soroban-event-worker.ts, with a look atbackend/src/services/indexerService.ts. Removing the legacy SorobanIndexerService is tracked separately, leave it. The guard is small; the shutdown-drain part is where it's easy to miss a case.