@secondlayer/subgraphs 3.23.1 → 3.24.0
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 +11 -7
- package/dist/src/index.js +108 -64
- package/dist/src/index.js.map +4 -4
- package/dist/src/runtime/block-processor.d.ts +75 -9
- package/dist/src/runtime/block-processor.js +79 -34
- package/dist/src/runtime/block-processor.js.map +4 -4
- package/dist/src/runtime/catchup.d.ts +11 -7
- package/dist/src/runtime/catchup.js +120 -42
- package/dist/src/runtime/catchup.js.map +7 -6
- package/dist/src/runtime/context.js +12 -3
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +275 -214
- package/dist/src/runtime/processor.js.map +8 -8
- package/dist/src/runtime/reindex.d.ts +11 -7
- package/dist/src/runtime/reindex.js +108 -64
- package/dist/src/runtime/reindex.js.map +4 -4
- package/dist/src/runtime/reorg.d.ts +11 -7
- package/dist/src/runtime/reorg.js +221 -135
- package/dist/src/runtime/reorg.js.map +8 -7
- package/dist/src/runtime/runner.d.ts +36 -32
- package/dist/src/runtime/source-matcher.d.ts +11 -7
- package/dist/src/runtime/stats.d.ts +3 -3
- package/dist/src/schema/index.d.ts +11 -7
- package/dist/src/service.js +280 -219
- package/dist/src/service.js.map +8 -8
- package/dist/src/testing/index.d.ts +32 -28
- package/dist/src/testing/index.js +17 -8
- package/dist/src/testing/index.js.map +3 -3
- package/dist/src/triggers/index.d.ts +11 -7
- package/dist/src/types.d.ts +11 -7
- package/dist/src/validate.d.ts +11 -7
- package/package.json +3 -5
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
|
|
2
|
+
import { AssetIdentifier } from "@secondlayer/stacks/filters";
|
|
2
3
|
import { UnwrapResponse } from "@secondlayer/stacks/actions";
|
|
3
4
|
import { AbiContract, ExtractFunctionArgs, ExtractFunctionOutput, ExtractReadOnlyFunctions, ToCamelCase } from "@secondlayer/stacks/clarity";
|
|
4
5
|
/**
|
|
@@ -206,38 +207,38 @@ interface FactoryScope {
|
|
|
206
207
|
/** FT event filters */
|
|
207
208
|
interface FtTransferFilter extends TraitScope {
|
|
208
209
|
type: "ft_transfer";
|
|
209
|
-
assetIdentifier?:
|
|
210
|
+
assetIdentifier?: AssetIdentifier;
|
|
210
211
|
sender?: string;
|
|
211
212
|
recipient?: string;
|
|
212
213
|
minAmount?: bigint;
|
|
213
214
|
}
|
|
214
215
|
interface FtMintFilter extends TraitScope {
|
|
215
216
|
type: "ft_mint";
|
|
216
|
-
assetIdentifier?:
|
|
217
|
+
assetIdentifier?: AssetIdentifier;
|
|
217
218
|
recipient?: string;
|
|
218
219
|
minAmount?: bigint;
|
|
219
220
|
}
|
|
220
221
|
interface FtBurnFilter extends TraitScope {
|
|
221
222
|
type: "ft_burn";
|
|
222
|
-
assetIdentifier?:
|
|
223
|
+
assetIdentifier?: AssetIdentifier;
|
|
223
224
|
sender?: string;
|
|
224
225
|
minAmount?: bigint;
|
|
225
226
|
}
|
|
226
227
|
/** NFT event filters */
|
|
227
228
|
interface NftTransferFilter extends TraitScope {
|
|
228
229
|
type: "nft_transfer";
|
|
229
|
-
assetIdentifier?:
|
|
230
|
+
assetIdentifier?: AssetIdentifier;
|
|
230
231
|
sender?: string;
|
|
231
232
|
recipient?: string;
|
|
232
233
|
}
|
|
233
234
|
interface NftMintFilter extends TraitScope {
|
|
234
235
|
type: "nft_mint";
|
|
235
|
-
assetIdentifier?:
|
|
236
|
+
assetIdentifier?: AssetIdentifier;
|
|
236
237
|
recipient?: string;
|
|
237
238
|
}
|
|
238
239
|
interface NftBurnFilter extends TraitScope {
|
|
239
240
|
type: "nft_burn";
|
|
240
|
-
assetIdentifier?:
|
|
241
|
+
assetIdentifier?: AssetIdentifier;
|
|
241
242
|
sender?: string;
|
|
242
243
|
}
|
|
243
244
|
/**
|
|
@@ -335,7 +336,10 @@ type PrintField = ColumnType | {
|
|
|
335
336
|
interface PrintEventFilter extends TraitScope, FactoryScope {
|
|
336
337
|
type: "print_event";
|
|
337
338
|
/** One contract id, or a set of them (max 20). Supports `*` wildcards.
|
|
338
|
-
*
|
|
339
|
+
* COMPOSES with `trait` — the matcher ANDs them, so the pair means
|
|
340
|
+
* "contracts conforming to this trait, narrowed to these ids". (The Index
|
|
341
|
+
* read API refuses the combination, but that is its query planner's
|
|
342
|
+
* constraint, not a semantic one.) */
|
|
339
343
|
contractId?: string | readonly string[];
|
|
340
344
|
topic?: string;
|
|
341
345
|
/**
|
package/dist/src/index.js
CHANGED
|
@@ -217,7 +217,7 @@ function filterFieldsByType() {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
// src/runtime/context.ts
|
|
220
|
-
import { logger } from "@secondlayer/shared/logger";
|
|
220
|
+
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
221
221
|
import { formatUnits } from "@secondlayer/stacks/utils";
|
|
222
222
|
import { sql } from "kysely";
|
|
223
223
|
|
|
@@ -362,6 +362,7 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
362
362
|
|
|
363
363
|
// src/runtime/chain-read.ts
|
|
364
364
|
import { getSourceDb } from "@secondlayer/shared/db";
|
|
365
|
+
import { logger } from "@secondlayer/shared/logger";
|
|
365
366
|
import { http, createPublicClient } from "@secondlayer/stacks";
|
|
366
367
|
import {
|
|
367
368
|
ContractResponseError,
|
|
@@ -447,6 +448,14 @@ function createChainReadClient(ctx) {
|
|
|
447
448
|
const pinnedTo = constant ? null : ctx.indexBlockHash;
|
|
448
449
|
const cached = await db.selectFrom("chain_read_cache").select("result_hex").where("contract_id", "=", contractId).where("function_name", "=", fn.name).where("args_hash", "=", argsHash).where((eb) => pinnedTo === null ? eb("index_block_hash", "is", null) : eb("index_block_hash", "=", pinnedTo)).executeTakeFirst();
|
|
449
450
|
const resultHex = cached?.result_hex ?? await gate.run(async () => {
|
|
451
|
+
if (!ctx.indexBlockHash) {
|
|
452
|
+
logger.warn("Chain read is not pinned to a block (no index_block_hash)", {
|
|
453
|
+
contractId,
|
|
454
|
+
functionName: fn.name,
|
|
455
|
+
blockHeight: ctx.blockHeight,
|
|
456
|
+
note: 'cache: "contract-constant" asserts this value cannot change; the node answered at its own tip'
|
|
457
|
+
});
|
|
458
|
+
}
|
|
450
459
|
const value2 = await readContract(nodeClient(rpcUrl), {
|
|
451
460
|
contract: contractId,
|
|
452
461
|
functionName: fn.name,
|
|
@@ -561,7 +570,7 @@ class SubgraphContext {
|
|
|
561
570
|
data: { ...key, ...row, ...meta, _upsert_keys: keyColumns }
|
|
562
571
|
});
|
|
563
572
|
} else {
|
|
564
|
-
|
|
573
|
+
logger2.warn("upsert called without matching uniqueKeys constraint, using fallback", {
|
|
565
574
|
table,
|
|
566
575
|
keys: keyColumns
|
|
567
576
|
});
|
|
@@ -1121,7 +1130,7 @@ function decodeFunctionArgs(args) {
|
|
|
1121
1130
|
|
|
1122
1131
|
// src/runtime/runner.ts
|
|
1123
1132
|
import { getErrorMessage } from "@secondlayer/shared";
|
|
1124
|
-
import { logger as
|
|
1133
|
+
import { logger as logger3 } from "@secondlayer/shared/logger";
|
|
1125
1134
|
import {
|
|
1126
1135
|
clarityValueToJS,
|
|
1127
1136
|
deserializeCV as deserializeCV3,
|
|
@@ -1472,7 +1481,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1472
1481
|
units.sort((a, b) => (a.tx.tx_index ?? 0) - (b.tx.tx_index ?? 0) || (a.event?.event_index ?? -1) - (b.event?.event_index ?? -1));
|
|
1473
1482
|
for (const { tx, event, sourceName } of units) {
|
|
1474
1483
|
if (errors >= threshold) {
|
|
1475
|
-
|
|
1484
|
+
logger3.error("Subgraph error threshold reached, skipping remaining events", {
|
|
1476
1485
|
subgraph: subgraph.name,
|
|
1477
1486
|
errors,
|
|
1478
1487
|
threshold
|
|
@@ -1481,7 +1490,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1481
1490
|
}
|
|
1482
1491
|
const handler = subgraph.handlers[sourceName] ?? subgraph.handlers["*"] ?? null;
|
|
1483
1492
|
if (!handler) {
|
|
1484
|
-
|
|
1493
|
+
logger3.warn("No handler found for source", {
|
|
1485
1494
|
subgraph: subgraph.name,
|
|
1486
1495
|
sourceName,
|
|
1487
1496
|
txId: tx.tx_id
|
|
@@ -1538,7 +1547,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1538
1547
|
const verdict = validatePrintPayload(filter.prints, printPayload.topic ?? "", printPayload.data ?? {});
|
|
1539
1548
|
if (!verdict.ok) {
|
|
1540
1549
|
skipped++;
|
|
1541
|
-
|
|
1550
|
+
logger3.warn("Print payload does not match declared prints schema", {
|
|
1542
1551
|
subgraph: subgraph.name,
|
|
1543
1552
|
sourceName,
|
|
1544
1553
|
topic: printPayload.topic,
|
|
@@ -1553,7 +1562,7 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
1553
1562
|
} catch (err) {
|
|
1554
1563
|
ctx.rollbackTo(checkpoint);
|
|
1555
1564
|
errors++;
|
|
1556
|
-
|
|
1565
|
+
logger3.error("Subgraph handler error", {
|
|
1557
1566
|
subgraph: subgraph.name,
|
|
1558
1567
|
sourceName,
|
|
1559
1568
|
txId: tx.tx_id,
|
|
@@ -1833,12 +1842,14 @@ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contract
|
|
|
1833
1842
|
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
1834
1843
|
import {
|
|
1835
1844
|
isByoSubgraph,
|
|
1845
|
+
recordLiveError,
|
|
1836
1846
|
recordLiveProgress,
|
|
1837
1847
|
recordSubgraphProcessed,
|
|
1838
1848
|
resolveSubgraphDb,
|
|
1849
|
+
rewindLiveProgress,
|
|
1839
1850
|
updateSubgraphStatus
|
|
1840
1851
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1841
|
-
import { logger as
|
|
1852
|
+
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
1842
1853
|
import { sql as sql3 } from "kysely";
|
|
1843
1854
|
|
|
1844
1855
|
// src/runtime/block-source.ts
|
|
@@ -1846,7 +1857,7 @@ import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
|
|
|
1846
1857
|
import {
|
|
1847
1858
|
IndexHttpClient
|
|
1848
1859
|
} from "@secondlayer/shared/index-http";
|
|
1849
|
-
import { logger as
|
|
1860
|
+
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
1850
1861
|
|
|
1851
1862
|
// src/runtime/batch-loader.ts
|
|
1852
1863
|
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
@@ -2170,7 +2181,7 @@ class FallbackBlockSource {
|
|
|
2170
2181
|
try {
|
|
2171
2182
|
return await this.primary.getTip();
|
|
2172
2183
|
} catch (err) {
|
|
2173
|
-
|
|
2184
|
+
logger4.warn("block source primary getTip failed — using DB tap", {
|
|
2174
2185
|
error: err instanceof Error ? err.message : String(err)
|
|
2175
2186
|
});
|
|
2176
2187
|
return this.fallback.getTip();
|
|
@@ -2180,7 +2191,7 @@ class FallbackBlockSource {
|
|
|
2180
2191
|
try {
|
|
2181
2192
|
return await this.primary.loadBlockRange(fromHeight, toHeight);
|
|
2182
2193
|
} catch (err) {
|
|
2183
|
-
|
|
2194
|
+
logger4.warn("block source primary loadBlockRange failed — using DB tap", {
|
|
2184
2195
|
from: fromHeight,
|
|
2185
2196
|
to: toHeight,
|
|
2186
2197
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -2212,7 +2223,7 @@ function resolveBlockSource(subgraph) {
|
|
|
2212
2223
|
return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph), canSparseScan(subgraph) ? sparseProbeTargets(subgraph) : undefined, needsTransactionData(subgraph)), postgresBlockSource);
|
|
2213
2224
|
}
|
|
2214
2225
|
if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
|
|
2215
|
-
|
|
2226
|
+
logger4.debug("Subgraph not streams-index eligible, using DB tap", {
|
|
2216
2227
|
subgraph: subgraph.name
|
|
2217
2228
|
});
|
|
2218
2229
|
}
|
|
@@ -2221,7 +2232,7 @@ function resolveBlockSource(subgraph) {
|
|
|
2221
2232
|
|
|
2222
2233
|
// src/runtime/outbox-emit.ts
|
|
2223
2234
|
import { createHash as createHash2 } from "node:crypto";
|
|
2224
|
-
import { logger as
|
|
2235
|
+
import { logger as logger5 } from "@secondlayer/shared/logger";
|
|
2225
2236
|
var loggedKillSwitch = false;
|
|
2226
2237
|
var OP_VERB = {
|
|
2227
2238
|
insert: "created",
|
|
@@ -2245,7 +2256,7 @@ function stableStringify(obj) {
|
|
|
2245
2256
|
async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, blockHeight) {
|
|
2246
2257
|
if (!isEmitOutboxEnabled()) {
|
|
2247
2258
|
if (!loggedKillSwitch) {
|
|
2248
|
-
|
|
2259
|
+
logger5.warn("SECONDLAYER_EMIT_OUTBOX=false — outbox emission bypassed");
|
|
2249
2260
|
loggedKillSwitch = true;
|
|
2250
2261
|
}
|
|
2251
2262
|
return 0;
|
|
@@ -2543,10 +2554,20 @@ class CursorRaceLostError extends Error {
|
|
|
2543
2554
|
this.name = "CursorRaceLostError";
|
|
2544
2555
|
}
|
|
2545
2556
|
}
|
|
2557
|
+
|
|
2558
|
+
class LiveCursorRaceLostError extends Error {
|
|
2559
|
+
constructor(subgraphName, height) {
|
|
2560
|
+
super(`live cursor race lost for ${subgraphName} at block ${height}`);
|
|
2561
|
+
this.name = "LiveCursorRaceLostError";
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2546
2564
|
function opCursorMode(opts) {
|
|
2547
2565
|
const ap = opts?.atomicProgress;
|
|
2548
2566
|
return ap && "operationId" in ap ? ap : undefined;
|
|
2549
2567
|
}
|
|
2568
|
+
function isReorgRewind(opts) {
|
|
2569
|
+
return opts?.reorgRewind === true;
|
|
2570
|
+
}
|
|
2550
2571
|
function statusMode(opts) {
|
|
2551
2572
|
const ap = opts?.atomicProgress;
|
|
2552
2573
|
return ap && "status" in ap ? ap : undefined;
|
|
@@ -2565,7 +2586,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
|
|
|
2565
2586
|
const delay = retryDelaysMs[attempt];
|
|
2566
2587
|
if (delay === undefined)
|
|
2567
2588
|
break;
|
|
2568
|
-
|
|
2589
|
+
logger6.warn("Block processing failed, retrying", {
|
|
2569
2590
|
subgraph: subgraphName,
|
|
2570
2591
|
blockHeight,
|
|
2571
2592
|
attempt: attempt + 1,
|
|
@@ -2597,7 +2618,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2597
2618
|
} else {
|
|
2598
2619
|
const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
|
|
2599
2620
|
if (!data) {
|
|
2600
|
-
|
|
2621
|
+
logger6.debug("Block not found or non-canonical for subgraph processing", {
|
|
2601
2622
|
subgraph: subgraphName,
|
|
2602
2623
|
blockHeight
|
|
2603
2624
|
});
|
|
@@ -2616,7 +2637,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2616
2637
|
result.matched = matched.length;
|
|
2617
2638
|
if (matched.length === 0) {
|
|
2618
2639
|
if (!opts?.skipProgressUpdate) {
|
|
2619
|
-
|
|
2640
|
+
if (isReorgRewind(opts)) {
|
|
2641
|
+
await rewindLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2642
|
+
} else {
|
|
2643
|
+
await recordLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2644
|
+
}
|
|
2620
2645
|
}
|
|
2621
2646
|
return result;
|
|
2622
2647
|
}
|
|
@@ -2635,13 +2660,23 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2635
2660
|
};
|
|
2636
2661
|
let handlerMs = 0;
|
|
2637
2662
|
let flushMs = 0;
|
|
2638
|
-
const applyProgress = async (tx, rr) => {
|
|
2663
|
+
const applyProgress = async (tx, rr, flushedWrites) => {
|
|
2639
2664
|
if (opts?.skipProgressUpdate)
|
|
2640
2665
|
return;
|
|
2666
|
+
const rewind = isReorgRewind(opts);
|
|
2641
2667
|
if (rr.errors > 0 && rr.processed === 0) {
|
|
2642
|
-
|
|
2668
|
+
if (rewind) {
|
|
2669
|
+
await updateSubgraphStatus(tx, subgraphName, "error", blockHeight);
|
|
2670
|
+
} else {
|
|
2671
|
+
await recordLiveError(tx, subgraphName, blockHeight);
|
|
2672
|
+
}
|
|
2673
|
+
} else if (rewind) {
|
|
2674
|
+
await rewindLiveProgress(tx, subgraphName, blockHeight);
|
|
2643
2675
|
} else {
|
|
2644
|
-
await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2676
|
+
const advanced = await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2677
|
+
if (!advanced && flushedWrites && !route.byo) {
|
|
2678
|
+
throw new LiveCursorRaceLostError(subgraphName, blockHeight);
|
|
2679
|
+
}
|
|
2645
2680
|
}
|
|
2646
2681
|
if (rr.processed > 0 || rr.errors > 0) {
|
|
2647
2682
|
const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
|
|
@@ -2690,13 +2725,15 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2690
2725
|
} else if (byoOm && manifest && manifest.count > 0) {
|
|
2691
2726
|
await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
|
|
2692
2727
|
}
|
|
2693
|
-
await applyProgress(tx, runResult);
|
|
2728
|
+
await applyProgress(tx, runResult, manifest ? manifest.count > 0 : false);
|
|
2694
2729
|
});
|
|
2695
2730
|
} else {
|
|
2696
2731
|
try {
|
|
2697
2732
|
await targetDb.transaction().execute(async (tx) => {
|
|
2698
2733
|
const opMode = opCursorMode(opts);
|
|
2699
|
-
|
|
2734
|
+
const sm = statusMode(opts);
|
|
2735
|
+
const rewind = isReorgRewind(opts);
|
|
2736
|
+
if (sm) {
|
|
2700
2737
|
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2701
2738
|
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2702
2739
|
result.skipped = true;
|
|
@@ -2708,6 +2745,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2708
2745
|
result.skipped = true;
|
|
2709
2746
|
return;
|
|
2710
2747
|
}
|
|
2748
|
+
} else if (!rewind) {
|
|
2749
|
+
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2750
|
+
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2751
|
+
result.skipped = true;
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2711
2754
|
}
|
|
2712
2755
|
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2713
2756
|
const handlerStart = performance.now();
|
|
@@ -2725,7 +2768,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2725
2768
|
}
|
|
2726
2769
|
flushMs = performance.now() - flushStart;
|
|
2727
2770
|
}
|
|
2728
|
-
const sm = statusMode(opts);
|
|
2729
2771
|
if (sm && flushedWrites) {
|
|
2730
2772
|
await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
|
|
2731
2773
|
} else if (opMode && flushedWrites) {
|
|
@@ -2734,11 +2776,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2734
2776
|
throw new CursorRaceLostError(opMode.operationId, blockHeight);
|
|
2735
2777
|
}
|
|
2736
2778
|
}
|
|
2737
|
-
await applyProgress(tx, runResult);
|
|
2779
|
+
await applyProgress(tx, runResult, flushedWrites);
|
|
2738
2780
|
});
|
|
2739
2781
|
} catch (err) {
|
|
2740
|
-
if (err instanceof CursorRaceLostError) {
|
|
2741
|
-
|
|
2782
|
+
if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
|
|
2783
|
+
logger6.warn("cursor race lost — block already covered", {
|
|
2742
2784
|
subgraph: subgraphName,
|
|
2743
2785
|
blockHeight,
|
|
2744
2786
|
error: err.message
|
|
@@ -2750,11 +2792,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2750
2792
|
}
|
|
2751
2793
|
}
|
|
2752
2794
|
const totalMs = performance.now() - blockStart;
|
|
2753
|
-
result.
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2795
|
+
if (!result.skipped) {
|
|
2796
|
+
result.timing = {
|
|
2797
|
+
totalMs: Math.round(totalMs),
|
|
2798
|
+
handlerMs: Math.round(handlerMs),
|
|
2799
|
+
flushMs: Math.round(flushMs)
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2758
2802
|
if (blockHeight % 1000 === 0) {
|
|
2759
2803
|
try {
|
|
2760
2804
|
const tables = Object.keys(subgraph.schema);
|
|
@@ -2762,7 +2806,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2762
2806
|
const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
|
|
2763
2807
|
const count = Number(rows[0]?.count ?? 0);
|
|
2764
2808
|
if (count >= 1e7) {
|
|
2765
|
-
|
|
2809
|
+
logger6.warn("Subgraph table exceeds 10M rows (estimate)", {
|
|
2766
2810
|
subgraph: subgraphName,
|
|
2767
2811
|
table,
|
|
2768
2812
|
count
|
|
@@ -2770,7 +2814,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2770
2814
|
}
|
|
2771
2815
|
}
|
|
2772
2816
|
} catch (err) {
|
|
2773
|
-
|
|
2817
|
+
logger6.debug("Row count sample failed", {
|
|
2774
2818
|
subgraph: subgraphName,
|
|
2775
2819
|
error: err instanceof Error ? err.message : String(err)
|
|
2776
2820
|
});
|
|
@@ -2860,11 +2904,11 @@ import {
|
|
|
2860
2904
|
recordSubgraphProcessed as recordSubgraphProcessed2,
|
|
2861
2905
|
updateSubgraphStatus as updateSubgraphStatus2
|
|
2862
2906
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
2863
|
-
import { logger as
|
|
2907
|
+
import { logger as logger8 } from "@secondlayer/shared/logger";
|
|
2864
2908
|
|
|
2865
2909
|
// src/runtime/reindex-notify.ts
|
|
2866
2910
|
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
2867
|
-
import { logger as
|
|
2911
|
+
import { logger as logger7 } from "@secondlayer/shared/logger";
|
|
2868
2912
|
async function notifyReindexComplete(db, subgraphName, stats) {
|
|
2869
2913
|
try {
|
|
2870
2914
|
const subgraph = await db.selectFrom("subgraphs").select(["account_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
@@ -2875,7 +2919,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
2875
2919
|
return;
|
|
2876
2920
|
const resendKey = process.env.RESEND_API_KEY;
|
|
2877
2921
|
if (!resendKey) {
|
|
2878
|
-
|
|
2922
|
+
logger7.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
|
|
2879
2923
|
subgraph: subgraphName
|
|
2880
2924
|
});
|
|
2881
2925
|
return;
|
|
@@ -2897,14 +2941,14 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
2897
2941
|
});
|
|
2898
2942
|
if (!res.ok) {
|
|
2899
2943
|
const text = await res.text().catch(() => "");
|
|
2900
|
-
|
|
2944
|
+
logger7.warn("reindex-complete email failed", {
|
|
2901
2945
|
subgraph: subgraphName,
|
|
2902
2946
|
status: res.status,
|
|
2903
2947
|
body: text.slice(0, 200)
|
|
2904
2948
|
});
|
|
2905
2949
|
}
|
|
2906
2950
|
} catch (err) {
|
|
2907
|
-
|
|
2951
|
+
logger7.warn("reindex-complete email threw", {
|
|
2908
2952
|
subgraph: subgraphName,
|
|
2909
2953
|
error: getErrorMessage2(err)
|
|
2910
2954
|
});
|
|
@@ -3016,7 +3060,7 @@ async function processBlockRange(def, opts) {
|
|
|
3016
3060
|
while (currentHeight <= toBlock) {
|
|
3017
3061
|
if (opts.signal?.aborted) {
|
|
3018
3062
|
aborted = true;
|
|
3019
|
-
|
|
3063
|
+
logger8.info("Block processing aborted", {
|
|
3020
3064
|
subgraph: subgraphName,
|
|
3021
3065
|
currentBlock: currentHeight,
|
|
3022
3066
|
reason: String(opts.signal.reason ?? "unknown")
|
|
@@ -3065,7 +3109,7 @@ async function processBlockRange(def, opts) {
|
|
|
3065
3109
|
});
|
|
3066
3110
|
} catch (err) {
|
|
3067
3111
|
const errorMsg = getErrorMessage3(err);
|
|
3068
|
-
|
|
3112
|
+
logger8.error("Block processing failed persistently", {
|
|
3069
3113
|
subgraph: subgraphName,
|
|
3070
3114
|
blockHeight: height,
|
|
3071
3115
|
error: errorMsg
|
|
@@ -3111,7 +3155,7 @@ async function processBlockRange(def, opts) {
|
|
|
3111
3155
|
lastProgressFlushAt = now;
|
|
3112
3156
|
}
|
|
3113
3157
|
if (blocksProcessed % LOG_INTERVAL === 0) {
|
|
3114
|
-
|
|
3158
|
+
logger8.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
|
|
3115
3159
|
subgraph: subgraphName,
|
|
3116
3160
|
processed: blocksProcessed,
|
|
3117
3161
|
total: totalBlocks,
|
|
@@ -3131,7 +3175,7 @@ async function processBlockRange(def, opts) {
|
|
|
3131
3175
|
if (batchFailedBlocks.length > 0 && opts.subgraphId) {
|
|
3132
3176
|
const gaps = coalesceFailedBlocks(batchFailedBlocks);
|
|
3133
3177
|
await recordGapBatch(targetDb, opts.subgraphId, subgraphName, gaps).catch((err) => {
|
|
3134
|
-
|
|
3178
|
+
logger8.warn("Failed to record subgraph gaps", {
|
|
3135
3179
|
subgraph: subgraphName,
|
|
3136
3180
|
error: err instanceof Error ? err.message : String(err)
|
|
3137
3181
|
});
|
|
@@ -3148,7 +3192,7 @@ async function processBlockRange(def, opts) {
|
|
|
3148
3192
|
} else {
|
|
3149
3193
|
await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
|
|
3150
3194
|
}
|
|
3151
|
-
|
|
3195
|
+
logger8.info("Sparse skip", {
|
|
3152
3196
|
subgraph: subgraphName,
|
|
3153
3197
|
from: batchEnd + 1,
|
|
3154
3198
|
to: jumpTo - 1,
|
|
@@ -3188,11 +3232,11 @@ async function reindexSubgraph(def, opts) {
|
|
|
3188
3232
|
const subgraphName = def.name;
|
|
3189
3233
|
const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
|
|
3190
3234
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing");
|
|
3191
|
-
|
|
3235
|
+
logger8.info("Reindex starting", { subgraph: subgraphName });
|
|
3192
3236
|
try {
|
|
3193
3237
|
const { fromBlock, toBlock } = await resolveBlockRange(source, def, opts);
|
|
3194
3238
|
if (fromBlock > toBlock) {
|
|
3195
|
-
|
|
3239
|
+
logger8.info("No blocks to reindex", {
|
|
3196
3240
|
subgraph: subgraphName,
|
|
3197
3241
|
fromBlock,
|
|
3198
3242
|
toBlock
|
|
@@ -3206,7 +3250,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3206
3250
|
await client.unsafe(stmt);
|
|
3207
3251
|
}
|
|
3208
3252
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
|
|
3209
|
-
|
|
3253
|
+
logger8.info("Schema recreated for reindex", {
|
|
3210
3254
|
subgraph: subgraphName,
|
|
3211
3255
|
cursorResetTo: Math.max(0, fromBlock - 1)
|
|
3212
3256
|
});
|
|
@@ -3220,7 +3264,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3220
3264
|
last_error_at: null,
|
|
3221
3265
|
updated_at: new Date
|
|
3222
3266
|
}).where("name", "=", subgraphName).execute();
|
|
3223
|
-
|
|
3267
|
+
logger8.info("Reindexing blocks", {
|
|
3224
3268
|
subgraph: subgraphName,
|
|
3225
3269
|
fromBlock,
|
|
3226
3270
|
toBlock,
|
|
@@ -3242,9 +3286,9 @@ async function reindexSubgraph(def, opts) {
|
|
|
3242
3286
|
if (reason === "user-cancelled") {
|
|
3243
3287
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3244
3288
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3245
|
-
|
|
3289
|
+
logger8.info("Reindex cancelled by user", { subgraph: subgraphName });
|
|
3246
3290
|
} else {
|
|
3247
|
-
|
|
3291
|
+
logger8.info("Reindex interrupted by shutdown, will resume", {
|
|
3248
3292
|
subgraph: subgraphName
|
|
3249
3293
|
});
|
|
3250
3294
|
}
|
|
@@ -3252,7 +3296,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3252
3296
|
}
|
|
3253
3297
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3254
3298
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3255
|
-
|
|
3299
|
+
logger8.info("Reindex complete", {
|
|
3256
3300
|
subgraph: subgraphName,
|
|
3257
3301
|
blocks: result.blocksProcessed,
|
|
3258
3302
|
events: result.totalEventsProcessed,
|
|
@@ -3265,7 +3309,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3265
3309
|
});
|
|
3266
3310
|
return { processed: result.blocksProcessed };
|
|
3267
3311
|
} catch (err) {
|
|
3268
|
-
|
|
3312
|
+
logger8.error("Reindex failed", {
|
|
3269
3313
|
subgraph: subgraphName,
|
|
3270
3314
|
error: getErrorMessage3(err)
|
|
3271
3315
|
});
|
|
@@ -3287,7 +3331,7 @@ async function resumeReindex(def, opts) {
|
|
|
3287
3331
|
const fromBlock = resolveReindexResumeBlock(row);
|
|
3288
3332
|
const toBlock = Number(row.reindex_to_block);
|
|
3289
3333
|
if (fromBlock == null) {
|
|
3290
|
-
|
|
3334
|
+
logger8.info("No reindex metadata, starting fresh reindex", {
|
|
3291
3335
|
subgraph: subgraphName
|
|
3292
3336
|
});
|
|
3293
3337
|
return reindexSubgraph(def, {
|
|
@@ -3296,12 +3340,12 @@ async function resumeReindex(def, opts) {
|
|
|
3296
3340
|
});
|
|
3297
3341
|
}
|
|
3298
3342
|
if (fromBlock > toBlock) {
|
|
3299
|
-
|
|
3343
|
+
logger8.info("Resume: no remaining blocks", { subgraph: subgraphName });
|
|
3300
3344
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3301
3345
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3302
3346
|
return { processed: 0 };
|
|
3303
3347
|
}
|
|
3304
|
-
|
|
3348
|
+
logger8.info("Resuming reindex", {
|
|
3305
3349
|
subgraph: subgraphName,
|
|
3306
3350
|
fromBlock,
|
|
3307
3351
|
toBlock,
|
|
@@ -3323,9 +3367,9 @@ async function resumeReindex(def, opts) {
|
|
|
3323
3367
|
if (reason === "user-cancelled") {
|
|
3324
3368
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3325
3369
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3326
|
-
|
|
3370
|
+
logger8.info("Resume cancelled by user", { subgraph: subgraphName });
|
|
3327
3371
|
} else {
|
|
3328
|
-
|
|
3372
|
+
logger8.info("Resume interrupted by shutdown, will resume again", {
|
|
3329
3373
|
subgraph: subgraphName
|
|
3330
3374
|
});
|
|
3331
3375
|
}
|
|
@@ -3333,13 +3377,13 @@ async function resumeReindex(def, opts) {
|
|
|
3333
3377
|
}
|
|
3334
3378
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3335
3379
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3336
|
-
|
|
3380
|
+
logger8.info("Resumed reindex complete", {
|
|
3337
3381
|
subgraph: subgraphName,
|
|
3338
3382
|
blocks: result.blocksProcessed
|
|
3339
3383
|
});
|
|
3340
3384
|
return { processed: result.blocksProcessed };
|
|
3341
3385
|
} catch (err) {
|
|
3342
|
-
|
|
3386
|
+
logger8.error("Resumed reindex failed", {
|
|
3343
3387
|
subgraph: subgraphName,
|
|
3344
3388
|
error: getErrorMessage3(err)
|
|
3345
3389
|
});
|
|
@@ -3350,7 +3394,7 @@ async function resumeReindex(def, opts) {
|
|
|
3350
3394
|
async function backfillSubgraph(def, opts) {
|
|
3351
3395
|
const targetDb = getTargetDb2();
|
|
3352
3396
|
const subgraphName = def.name;
|
|
3353
|
-
|
|
3397
|
+
logger8.info("Backfill starting", {
|
|
3354
3398
|
subgraph: subgraphName,
|
|
3355
3399
|
from: opts.fromBlock,
|
|
3356
3400
|
to: opts.toBlock
|
|
@@ -3368,17 +3412,17 @@ async function backfillSubgraph(def, opts) {
|
|
|
3368
3412
|
signal: opts.signal
|
|
3369
3413
|
});
|
|
3370
3414
|
if (result.aborted) {
|
|
3371
|
-
|
|
3415
|
+
logger8.info("Backfill aborted", { subgraph: subgraphName });
|
|
3372
3416
|
return { processed: result.blocksProcessed };
|
|
3373
3417
|
}
|
|
3374
3418
|
const resolved = await resolveGaps(targetDb, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
|
|
3375
3419
|
if (resolved > 0) {
|
|
3376
|
-
|
|
3420
|
+
logger8.info("Resolved subgraph gaps via backfill", {
|
|
3377
3421
|
subgraph: subgraphName,
|
|
3378
3422
|
resolved
|
|
3379
3423
|
});
|
|
3380
3424
|
}
|
|
3381
|
-
|
|
3425
|
+
logger8.info("Backfill complete", {
|
|
3382
3426
|
subgraph: subgraphName,
|
|
3383
3427
|
blocks: result.blocksProcessed,
|
|
3384
3428
|
events: result.totalEventsProcessed,
|
|
@@ -3386,7 +3430,7 @@ async function backfillSubgraph(def, opts) {
|
|
|
3386
3430
|
});
|
|
3387
3431
|
return { processed: result.blocksProcessed };
|
|
3388
3432
|
} catch (err) {
|
|
3389
|
-
|
|
3433
|
+
logger8.error("Backfill failed", {
|
|
3390
3434
|
subgraph: subgraphName,
|
|
3391
3435
|
error: getErrorMessage3(err)
|
|
3392
3436
|
});
|
|
@@ -4692,5 +4736,5 @@ export {
|
|
|
4692
4736
|
ByoBreakingChangeError
|
|
4693
4737
|
};
|
|
4694
4738
|
|
|
4695
|
-
//# debugId=
|
|
4739
|
+
//# debugId=0DAD1DFA856B057164756E2164756E21
|
|
4696
4740
|
//# sourceMappingURL=index.js.map
|