@secondlayer/subgraphs 3.25.1 → 3.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +31 -18
- package/dist/src/index.js +32 -12
- package/dist/src/index.js.map +4 -4
- package/dist/src/runtime/block-processor.js +29 -9
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.js +29 -9
- package/dist/src/runtime/catchup.js.map +3 -3
- package/dist/src/runtime/context.d.ts +20 -0
- package/dist/src/runtime/context.js +29 -9
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +33 -14
- package/dist/src/runtime/processor.js.map +5 -5
- package/dist/src/runtime/reindex.d.ts +59 -3
- package/dist/src/runtime/reindex.js +33 -12
- package/dist/src/runtime/reindex.js.map +4 -4
- package/dist/src/runtime/reorg.js +29 -9
- package/dist/src/runtime/reorg.js.map +3 -3
- package/dist/src/runtime/runner.d.ts +20 -0
- package/dist/src/service.js +33 -14
- package/dist/src/service.js.map +5 -5
- package/dist/src/testing/index.js +29 -9
- package/dist/src/testing/index.js.map +3 -3
- package/package.json +1 -1
|
@@ -345,6 +345,29 @@ interface SubgraphDefinition {
|
|
|
345
345
|
/** Handler functions — keys must match source names (or "*" for catch-all) */
|
|
346
346
|
handlers: Record<string, SubgraphHandler>;
|
|
347
347
|
}
|
|
348
|
+
import { Block, Event, Transaction } from "@secondlayer/shared/db";
|
|
349
|
+
interface BlockData {
|
|
350
|
+
block: Block;
|
|
351
|
+
txs: Transaction[];
|
|
352
|
+
events: Event[];
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Where the subgraph runtime reads canonical chain data. Today it taps the
|
|
356
|
+
* indexer Postgres directly (`PostgresBlockSource`); the re-point adds a
|
|
357
|
+
* `PublicApiBlockSource` that consumes the Streams clock + Index data over
|
|
358
|
+
* HTTP. `matchSources` / handlers / flush / outbox are unchanged — only the
|
|
359
|
+
* loader + tip swap behind this seam.
|
|
360
|
+
*/
|
|
361
|
+
interface BlockSource {
|
|
362
|
+
/** Highest canonical block height available to process. */
|
|
363
|
+
getTip(): Promise<number>;
|
|
364
|
+
/** Canonical block data for [fromHeight, toHeight], keyed by height. */
|
|
365
|
+
loadBlockRange(fromHeight: number, toHeight: number): Promise<Map<number, BlockData>>;
|
|
366
|
+
/** Sparse-scan probe: lowest height in (afterHeight, untilHeight] holding
|
|
367
|
+
* an event this source's subgraph could match, or null when the rest of
|
|
368
|
+
* the range is empty. Optional — only event-scoped sources support it. */
|
|
369
|
+
nextDataHeight?(afterHeight: number, untilHeight: number): Promise<number | null>;
|
|
370
|
+
}
|
|
348
371
|
type ReindexBatchConfig = {
|
|
349
372
|
defaultBatchSize: number
|
|
350
373
|
minBatchSize: number
|
|
@@ -364,16 +387,49 @@ declare function initialReindexProgressBlock(fromBlock: number): number;
|
|
|
364
387
|
declare function resolveReindexResumeBlock(row: ReindexResumeRow): number | null;
|
|
365
388
|
declare function resolveReindexBatchConfig(env?: ReindexBatchEnv): ReindexBatchConfig;
|
|
366
389
|
interface ReindexOptions {
|
|
367
|
-
|
|
368
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Policy floor on the walk's first block — NOT a caller-supplied range.
|
|
392
|
+
* Free-tier accounts may not reindex below their registered `start_block`
|
|
393
|
+
* (see `resolveGenesisPolicy` on the API side); this raises the walk start
|
|
394
|
+
* and can never lower it. There is deliberately no `toBlock` counterpart:
|
|
395
|
+
* a reindex always runs to chain tip. See {@link resolveBlockRange}.
|
|
396
|
+
*/
|
|
397
|
+
startBlockFloor?: number;
|
|
369
398
|
schemaName?: string;
|
|
370
399
|
/** Op row to receive processed_events on each progress flush. */
|
|
371
400
|
operationId?: string;
|
|
372
401
|
signal?: AbortSignal;
|
|
373
402
|
}
|
|
374
403
|
/**
|
|
404
|
+
* Resolve the reindex walk range. Always the whole subgraph:
|
|
405
|
+
* `[def.startBlock, chain tip]`. Chain tip reads from `index_progress` —
|
|
406
|
+
* lives in the source DB.
|
|
407
|
+
*
|
|
408
|
+
* Reindex drops the schema unconditionally, so the walk range is also the
|
|
409
|
+
* ONLY data that survives. A narrower walk therefore does not "reindex a
|
|
410
|
+
* range" — it silently destroys every row outside it while leaving the
|
|
411
|
+
* subgraph reporting `active` at chain tip (f079: a single-block
|
|
412
|
+
* `--from-block N --to-block N` emptied sbtc-flows, and the first recovery
|
|
413
|
+
* attempt rebuilt only 21% of its history). Ranged, additive work is
|
|
414
|
+
* `backfillSubgraph`'s job.
|
|
415
|
+
*
|
|
416
|
+
* `startBlockFloor` is a plan-policy floor, not a range: it can only raise
|
|
417
|
+
* the start block, never lower it, and never bounds the end.
|
|
418
|
+
*/
|
|
419
|
+
declare function resolveBlockRange(source: BlockSource, def: SubgraphDefinition, opts?: ReindexOptions): Promise<{
|
|
420
|
+
fromBlock: number
|
|
421
|
+
toBlock: number
|
|
422
|
+
}>;
|
|
423
|
+
/**
|
|
375
424
|
* Reindex a subgraph by dropping its tables, recreating them, and reprocessing
|
|
376
425
|
* all historical blocks through the handler pipeline.
|
|
426
|
+
*
|
|
427
|
+
* Always whole-subgraph: `[start_block, chain tip]`. There is no ranged
|
|
428
|
+
* reindex — the drop cannot be scoped to a range without re-running delta
|
|
429
|
+
* handlers (`ctx.increment` / `ctx.update` / `ctx.patchOrInsert`) over rows
|
|
430
|
+
* that were never removed, which is the exact double-count hazard `backfill`
|
|
431
|
+
* already refuses. Use `backfillSubgraph` for ranged, additive work.
|
|
432
|
+
*
|
|
377
433
|
* Supports cancellation via AbortSignal — on shutdown abort, status stays
|
|
378
434
|
* "reindexing" for auto-resume; on user cancel, status resets to "active".
|
|
379
435
|
*/
|
|
@@ -405,4 +461,4 @@ declare function backfillSubgraph(def: SubgraphDefinition, opts: {
|
|
|
405
461
|
}): Promise<{
|
|
406
462
|
processed: number
|
|
407
463
|
}>;
|
|
408
|
-
export { resumeReindex, resolveReindexResumeBlock, resolveReindexBatchConfig, reindexSubgraph, initialReindexProgressBlock, backfillSubgraph, ReindexOptions };
|
|
464
|
+
export { resumeReindex, resolveReindexResumeBlock, resolveReindexBatchConfig, resolveBlockRange, reindexSubgraph, initialReindexProgressBlock, backfillSubgraph, ReindexOptions };
|
|
@@ -605,14 +605,10 @@ class SubgraphContext {
|
|
|
605
605
|
this.ops.length = 0;
|
|
606
606
|
const statements = this.buildStatements(opsToFlush);
|
|
607
607
|
if ("isTransaction" in this.db) {
|
|
608
|
-
|
|
609
|
-
await sql.raw(stmt).execute(this.db);
|
|
610
|
-
}
|
|
608
|
+
await this.execStatements(this.db, statements);
|
|
611
609
|
} else {
|
|
612
610
|
await this.db.transaction().execute(async (tx) => {
|
|
613
|
-
|
|
614
|
-
await sql.raw(stmt).execute(tx);
|
|
615
|
-
}
|
|
611
|
+
await this.execStatements(tx, statements);
|
|
616
612
|
});
|
|
617
613
|
}
|
|
618
614
|
const writes = opsToFlush.map((op, rowIndex) => {
|
|
@@ -631,6 +627,25 @@ class SubgraphContext {
|
|
|
631
627
|
});
|
|
632
628
|
return { count: opsToFlush.length, writes };
|
|
633
629
|
}
|
|
630
|
+
async execStatements(executor, statements) {
|
|
631
|
+
const affected = (r) => r.numAffectedRows ?? 0n;
|
|
632
|
+
for (const stmt of statements) {
|
|
633
|
+
if (typeof stmt === "string") {
|
|
634
|
+
await sql.raw(stmt).execute(executor);
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
const updated = await sql.raw(stmt.update).execute(executor);
|
|
638
|
+
if (affected(updated) > 0n)
|
|
639
|
+
continue;
|
|
640
|
+
const inserted = await sql.raw(stmt.insert).execute(executor);
|
|
641
|
+
if (affected(inserted) > 0n)
|
|
642
|
+
continue;
|
|
643
|
+
const retried = await sql.raw(stmt.update).execute(executor);
|
|
644
|
+
if (affected(retried) === 0n) {
|
|
645
|
+
throw new Error(`increment could not be applied to ${this.pgSchemaName}.${stmt.table} where ${stmt.keyPred}: row neither updated nor inserted`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
634
649
|
prepareInsert(op) {
|
|
635
650
|
const upsertKeys = op.data._upsert_keys;
|
|
636
651
|
const data = { ...op.data };
|
|
@@ -710,7 +725,6 @@ class SubgraphContext {
|
|
|
710
725
|
for (const r of batch.rows.values()) {
|
|
711
726
|
const keyPred = batch.keyCols.map((k) => `"${k}" = ${escapeLiteral(r.keys[k])}`).join(" AND ");
|
|
712
727
|
const updSet = batch.deltaCols.map((c) => `"${c}" = COALESCE("${c}", 0) + (${String(r.deltas[c] ?? 0n)})`).join(", ");
|
|
713
|
-
statements.push(`UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`);
|
|
714
728
|
const insertVals = [
|
|
715
729
|
...batch.keyCols.map((k) => escapeLiteral(r.keys[k])),
|
|
716
730
|
...batch.deltaCols.map((c) => String(r.deltas[c] ?? 0n)),
|
|
@@ -718,7 +732,13 @@ class SubgraphContext {
|
|
|
718
732
|
escapeLiteral(r.meta.txId),
|
|
719
733
|
"NOW()"
|
|
720
734
|
];
|
|
721
|
-
statements.push(
|
|
735
|
+
statements.push({
|
|
736
|
+
kind: "increment",
|
|
737
|
+
table: batch.table,
|
|
738
|
+
keyPred,
|
|
739
|
+
update: `UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`,
|
|
740
|
+
insert: `INSERT INTO ${qualifiedTable} (${cols.map((c) => `"${c}"`).join(", ")}) ` + `SELECT ${insertVals.join(", ")} ` + `WHERE NOT EXISTS (SELECT 1 FROM ${qualifiedTable} WHERE ${keyPred})`
|
|
741
|
+
});
|
|
722
742
|
}
|
|
723
743
|
incBatch = null;
|
|
724
744
|
incBatchKey = "";
|
|
@@ -3353,9 +3373,9 @@ async function processBlockRange(def, opts) {
|
|
|
3353
3373
|
return { blocksProcessed, totalEventsProcessed, totalErrors, aborted };
|
|
3354
3374
|
}
|
|
3355
3375
|
async function resolveBlockRange(source, def, opts) {
|
|
3356
|
-
const
|
|
3357
|
-
const
|
|
3358
|
-
return { fromBlock, toBlock };
|
|
3376
|
+
const definitionStart = def.startBlock ?? 1;
|
|
3377
|
+
const fromBlock = opts?.startBlockFloor != null ? Math.max(definitionStart, opts.startBlockFloor) : definitionStart;
|
|
3378
|
+
return { fromBlock, toBlock: await source.getTip() };
|
|
3359
3379
|
}
|
|
3360
3380
|
async function clearReindexMetadata(db, subgraphName) {
|
|
3361
3381
|
await db.updateTable("subgraphs").set({ reindex_from_block: null, reindex_to_block: null }).where("name", "=", subgraphName).execute();
|
|
@@ -3576,10 +3596,11 @@ export {
|
|
|
3576
3596
|
resumeReindex,
|
|
3577
3597
|
resolveReindexResumeBlock,
|
|
3578
3598
|
resolveReindexBatchConfig,
|
|
3599
|
+
resolveBlockRange,
|
|
3579
3600
|
reindexSubgraph,
|
|
3580
3601
|
initialReindexProgressBlock,
|
|
3581
3602
|
backfillSubgraph
|
|
3582
3603
|
};
|
|
3583
3604
|
|
|
3584
|
-
//# debugId=
|
|
3605
|
+
//# debugId=E95B0AF0DF971D5864756E2164756E21
|
|
3585
3606
|
//# sourceMappingURL=reindex.js.map
|