@@ -30,14 +30,20 @@ export class IndexerProcessor extends WorkerHost {
3030 private readonly logger = new Logger ( IndexerProcessor . name ) ;
3131
3232 /**
33- * Soroban RPC only retains contract events for a limited window of recent
34- * ledgers. If our stored cursor falls further behind the network head than
35- * this, the RPC rejects `getEvents` with
36- * "startLedger must be within the ledger range". We keep this safety margin
37- * below the head and fast-forward the cursor to it when needed.
33+ * How far behind the network head a cursor may fall before we treat it as
34+ * stale. Soroban RPC only retains contract events for a limited window of
35+ * recent ledgers; once the cursor drops more than this below the head, the RPC
36+ * rejects `getEvents` with "startLedger must be within the ledger range".
3837 */
3938 private static readonly LEDGER_RETENTION_BUFFER = 100_000 ;
4039
40+ /**
41+ * When a stale cursor is healed, how far below the network head we jump to.
42+ * A small buffer keeps the catch-up point comfortably inside the retention
43+ * window so recovery completes in a single cycle.
44+ */
45+ private static readonly CATCH_UP_BUFFER = 1_000 ;
46+
4147 private readonly loanContractId : string ;
4248 private readonly reputationContractId : string ;
4349
@@ -431,8 +437,9 @@ export class IndexerProcessor extends WorkerHost {
431437 * Proactively fast-forwards a stale cursor.
432438 *
433439 * A checkpoint that has fallen too far behind the network head would be
434- * rejected by the RPC, so we advance both the start ledger we return and the
435- * persisted cursor to a safe point just inside the retention window.
440+ * rejected by the RPC. When that happens we jump close to the network head
441+ * (leaving a small buffer inside the retention window) so the indexer catches
442+ * up in a single cycle rather than crawling forward.
436443 *
437444 * @returns The ledger the caller should start fetching from.
438445 */
@@ -463,19 +470,23 @@ export class IndexerProcessor extends WorkerHost {
463470 return startLedger ;
464471 }
465472
473+ // Too far behind to recover full history from the RPC, so jump close to the
474+ // network head and catch up in a single cycle.
475+ const catchUpLedger = latestLedger - IndexerProcessor . CATCH_UP_BUFFER ;
476+
466477 this . logger . warn ( {
467478 context : 'IndexerProcessor' ,
468479 action : 'healStaleCursor' ,
469480 label,
470481 startLedger,
471- minValidLedger ,
482+ catchUpLedger ,
472483 latestLedger,
473- } , `Stale ledger checkpoint for ${ label } : ${ startLedger } is below minimum valid ${ minValidLedger } . Fast-forwarding. ` ) ;
484+ } , `Stale cursor detected for ${ label } . Jumping from ${ startLedger } directly to ${ catchUpLedger } (latest: ${ latestLedger } ) ` ) ;
474485
475- // Persist (minValidLedger - 1) so the next resume starts exactly at
476- // minValidLedger , matching the cursor+1 semantics used elsewhere.
477- await this . updateCursor ( contractId , minValidLedger - 1 ) ;
478- return minValidLedger ;
486+ // Persist (catchUpLedger - 1) so the next resume starts exactly at
487+ // catchUpLedger , matching the cursor+1 semantics used elsewhere.
488+ await this . updateCursor ( contractId , catchUpLedger - 1 ) ;
489+ return catchUpLedger ;
479490 }
480491
481492 /**
0 commit comments