@secondlayer/subgraphs 0.6.0 → 0.7.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 +3 -1
- package/dist/src/index.js +233 -119
- package/dist/src/index.js.map +16 -16
- package/dist/src/runtime/block-processor.d.ts +2 -0
- package/dist/src/runtime/block-processor.js +130 -100
- package/dist/src/runtime/block-processor.js.map +9 -9
- package/dist/src/runtime/catchup.d.ts +2 -0
- package/dist/src/runtime/catchup.js +180 -104
- package/dist/src/runtime/catchup.js.map +12 -12
- package/dist/src/runtime/clarity.js.map +2 -2
- package/dist/src/runtime/context.d.ts +1 -1
- package/dist/src/runtime/context.js +17 -4
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +198 -110
- package/dist/src/runtime/processor.js.map +14 -14
- package/dist/src/runtime/reindex.d.ts +2 -0
- package/dist/src/runtime/reindex.js +223 -117
- package/dist/src/runtime/reindex.js.map +13 -13
- package/dist/src/runtime/reorg.d.ts +2 -0
- package/dist/src/runtime/reorg.js +143 -104
- package/dist/src/runtime/reorg.js.map +10 -10
- package/dist/src/runtime/runner.d.ts +3 -1
- package/dist/src/runtime/runner.js +7 -3
- package/dist/src/runtime/runner.js.map +4 -4
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/runtime/stats.d.ts +1 -1
- package/dist/src/runtime/stats.js +2 -2
- package/dist/src/runtime/stats.js.map +3 -3
- package/dist/src/schema/index.d.ts +3 -1
- package/dist/src/schema/index.js +11 -3
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +199 -111
- package/dist/src/service.js.map +15 -15
- package/dist/src/templates.js.map +2 -2
- package/dist/src/types.d.ts +2 -0
- package/dist/src/types.js.map +2 -2
- package/dist/src/validate.d.ts +2 -0
- package/dist/src/validate.js.map +2 -2
- package/package.json +51 -51
|
@@ -16,8 +16,8 @@ function sourceKey(source) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// src/runtime/context.ts
|
|
19
|
-
import { sql } from "kysely";
|
|
20
19
|
import { logger } from "@secondlayer/shared/logger";
|
|
20
|
+
import { sql } from "kysely";
|
|
21
21
|
function validateColumnName(name) {
|
|
22
22
|
if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
|
|
23
23
|
throw new Error(`Invalid column name: ${name}`);
|
|
@@ -58,13 +58,26 @@ class SubgraphContext {
|
|
|
58
58
|
const keyColumns = Object.keys(key);
|
|
59
59
|
const hasUniqueConstraint = tableDef.uniqueKeys?.some((uk) => uk.length === keyColumns.length && uk.every((c) => keyColumns.includes(c)));
|
|
60
60
|
if (hasUniqueConstraint) {
|
|
61
|
-
this.ops.push({
|
|
61
|
+
this.ops.push({
|
|
62
|
+
kind: "insert",
|
|
63
|
+
table,
|
|
64
|
+
data: { ...key, ...row, _upsert_keys: keyColumns }
|
|
65
|
+
});
|
|
62
66
|
} else {
|
|
63
67
|
logger.warn("upsert called without matching uniqueKeys constraint, using fallback", {
|
|
64
68
|
table,
|
|
65
69
|
keys: keyColumns
|
|
66
70
|
});
|
|
67
|
-
this.ops.push({
|
|
71
|
+
this.ops.push({
|
|
72
|
+
kind: "insert",
|
|
73
|
+
table,
|
|
74
|
+
data: {
|
|
75
|
+
...key,
|
|
76
|
+
...row,
|
|
77
|
+
_upsert_fallback_keys: keyColumns,
|
|
78
|
+
_upsert_fallback_set: row
|
|
79
|
+
}
|
|
80
|
+
});
|
|
68
81
|
}
|
|
69
82
|
}
|
|
70
83
|
delete(table, where) {
|
|
@@ -227,95 +240,6 @@ function buildWhereClause(where) {
|
|
|
227
240
|
return { clause: parts.join(" AND "), values: [] };
|
|
228
241
|
}
|
|
229
242
|
|
|
230
|
-
// src/runtime/source-matcher.ts
|
|
231
|
-
function matchPattern(value, pattern) {
|
|
232
|
-
if (!pattern.includes("*"))
|
|
233
|
-
return value === pattern;
|
|
234
|
-
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
235
|
-
return new RegExp(`^${regex}$`).test(value);
|
|
236
|
-
}
|
|
237
|
-
function matchSource(source, transactions, eventsByTx) {
|
|
238
|
-
const results = [];
|
|
239
|
-
const key = sourceKey(source);
|
|
240
|
-
for (const tx of transactions) {
|
|
241
|
-
if (source.type) {
|
|
242
|
-
if (!matchPattern(tx.type, source.type))
|
|
243
|
-
continue;
|
|
244
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
245
|
-
let matchedEvents = txEvents;
|
|
246
|
-
if (source.minAmount !== undefined) {
|
|
247
|
-
const amountEvents = matchedEvents.filter((e) => {
|
|
248
|
-
const data = e.data;
|
|
249
|
-
const rawAmount = data?.amount;
|
|
250
|
-
if (rawAmount === undefined)
|
|
251
|
-
return false;
|
|
252
|
-
const amount = BigInt(rawAmount);
|
|
253
|
-
return amount >= source.minAmount;
|
|
254
|
-
});
|
|
255
|
-
if (amountEvents.length === 0)
|
|
256
|
-
continue;
|
|
257
|
-
matchedEvents = amountEvents;
|
|
258
|
-
}
|
|
259
|
-
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
if (source.contract) {
|
|
263
|
-
const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
|
|
264
|
-
if (source.function && tx.function_name) {
|
|
265
|
-
if (!matchPattern(tx.function_name, source.function))
|
|
266
|
-
continue;
|
|
267
|
-
} else if (source.function && !tx.function_name) {
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
271
|
-
let matchedEvents = txEvents;
|
|
272
|
-
if (!txContractMatch) {
|
|
273
|
-
matchedEvents = txEvents.filter((e) => {
|
|
274
|
-
const data = e.data;
|
|
275
|
-
const contractIdentifier = data?.contract_identifier;
|
|
276
|
-
return contractIdentifier && matchPattern(contractIdentifier, source.contract);
|
|
277
|
-
});
|
|
278
|
-
if (matchedEvents.length === 0)
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (source.event) {
|
|
282
|
-
matchedEvents = matchedEvents.filter((e) => {
|
|
283
|
-
if (matchPattern(e.type, source.event))
|
|
284
|
-
return true;
|
|
285
|
-
const data = e.data;
|
|
286
|
-
const topic = data?.topic;
|
|
287
|
-
return topic ? matchPattern(topic, source.event) : false;
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
if (txContractMatch || matchedEvents.length > 0) {
|
|
291
|
-
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return results;
|
|
296
|
-
}
|
|
297
|
-
function matchSources(sources, transactions, events) {
|
|
298
|
-
const eventsByTx = new Map;
|
|
299
|
-
for (const event of events) {
|
|
300
|
-
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
301
|
-
list.push(event);
|
|
302
|
-
eventsByTx.set(event.tx_id, list);
|
|
303
|
-
}
|
|
304
|
-
const seen = new Set;
|
|
305
|
-
const results = [];
|
|
306
|
-
for (const source of sources) {
|
|
307
|
-
const matches = matchSource(source, transactions, eventsByTx);
|
|
308
|
-
for (const match of matches) {
|
|
309
|
-
const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
|
|
310
|
-
if (!seen.has(dedupeKey)) {
|
|
311
|
-
seen.add(dedupeKey);
|
|
312
|
-
results.push(match);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
return results;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
243
|
// src/runtime/clarity.ts
|
|
320
244
|
import { cvToJSON, deserializeCV } from "@secondlayer/stacks/clarity";
|
|
321
245
|
function decodeClarityValue(hex) {
|
|
@@ -348,8 +272,8 @@ function decodeFunctionArgs(args) {
|
|
|
348
272
|
}
|
|
349
273
|
|
|
350
274
|
// src/runtime/runner.ts
|
|
351
|
-
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
352
275
|
import { getErrorMessage } from "@secondlayer/shared";
|
|
276
|
+
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
353
277
|
var DEFAULT_ERROR_THRESHOLD = 50;
|
|
354
278
|
function resolveHandler(handlers, key) {
|
|
355
279
|
return handlers[key] ?? handlers["*"] ?? null;
|
|
@@ -361,7 +285,11 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
361
285
|
for (const { tx, events, sourceKey: sourceKey2 } of matched) {
|
|
362
286
|
const handler = resolveHandler(subgraph.handlers, sourceKey2);
|
|
363
287
|
if (!handler) {
|
|
364
|
-
logger2.warn("No handler found for source key", {
|
|
288
|
+
logger2.warn("No handler found for source key", {
|
|
289
|
+
subgraph: subgraph.name,
|
|
290
|
+
sourceKey: sourceKey2,
|
|
291
|
+
txId: tx.tx_id
|
|
292
|
+
});
|
|
365
293
|
continue;
|
|
366
294
|
}
|
|
367
295
|
ctx.setTx({
|
|
@@ -438,11 +366,103 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
438
366
|
return { processed, errors };
|
|
439
367
|
}
|
|
440
368
|
|
|
369
|
+
// src/runtime/source-matcher.ts
|
|
370
|
+
function matchPattern(value, pattern) {
|
|
371
|
+
if (!pattern.includes("*"))
|
|
372
|
+
return value === pattern;
|
|
373
|
+
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
374
|
+
return new RegExp(`^${regex}$`).test(value);
|
|
375
|
+
}
|
|
376
|
+
function matchSource(source, transactions, eventsByTx) {
|
|
377
|
+
const results = [];
|
|
378
|
+
const key = sourceKey(source);
|
|
379
|
+
for (const tx of transactions) {
|
|
380
|
+
if (source.type) {
|
|
381
|
+
if (!matchPattern(tx.type, source.type))
|
|
382
|
+
continue;
|
|
383
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
384
|
+
let matchedEvents = txEvents;
|
|
385
|
+
if (source.minAmount !== undefined) {
|
|
386
|
+
const amountEvents = matchedEvents.filter((e) => {
|
|
387
|
+
const data = e.data;
|
|
388
|
+
const rawAmount = data?.amount;
|
|
389
|
+
if (rawAmount === undefined)
|
|
390
|
+
return false;
|
|
391
|
+
const amount = BigInt(rawAmount);
|
|
392
|
+
return amount >= source.minAmount;
|
|
393
|
+
});
|
|
394
|
+
if (amountEvents.length === 0)
|
|
395
|
+
continue;
|
|
396
|
+
matchedEvents = amountEvents;
|
|
397
|
+
}
|
|
398
|
+
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (source.contract) {
|
|
402
|
+
const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
|
|
403
|
+
if (source.function && tx.function_name) {
|
|
404
|
+
if (!matchPattern(tx.function_name, source.function))
|
|
405
|
+
continue;
|
|
406
|
+
} else if (source.function && !tx.function_name) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
410
|
+
let matchedEvents = txEvents;
|
|
411
|
+
if (!txContractMatch) {
|
|
412
|
+
matchedEvents = txEvents.filter((e) => {
|
|
413
|
+
const data = e.data;
|
|
414
|
+
const contractIdentifier = data?.contract_identifier;
|
|
415
|
+
return contractIdentifier && matchPattern(contractIdentifier, source.contract);
|
|
416
|
+
});
|
|
417
|
+
if (matchedEvents.length === 0)
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (source.event) {
|
|
421
|
+
matchedEvents = matchedEvents.filter((e) => {
|
|
422
|
+
if (matchPattern(e.type, source.event))
|
|
423
|
+
return true;
|
|
424
|
+
const data = e.data;
|
|
425
|
+
const topic = data?.topic;
|
|
426
|
+
return topic ? matchPattern(topic, source.event) : false;
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (txContractMatch || matchedEvents.length > 0) {
|
|
430
|
+
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return results;
|
|
435
|
+
}
|
|
436
|
+
function matchSources(sources, transactions, events) {
|
|
437
|
+
const eventsByTx = new Map;
|
|
438
|
+
for (const event of events) {
|
|
439
|
+
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
440
|
+
list.push(event);
|
|
441
|
+
eventsByTx.set(event.tx_id, list);
|
|
442
|
+
}
|
|
443
|
+
const seen = new Set;
|
|
444
|
+
const results = [];
|
|
445
|
+
for (const source of sources) {
|
|
446
|
+
const matches = matchSource(source, transactions, eventsByTx);
|
|
447
|
+
for (const match of matches) {
|
|
448
|
+
const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
|
|
449
|
+
if (!seen.has(dedupeKey)) {
|
|
450
|
+
seen.add(dedupeKey);
|
|
451
|
+
results.push(match);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return results;
|
|
456
|
+
}
|
|
457
|
+
|
|
441
458
|
// src/runtime/block-processor.ts
|
|
442
|
-
import { sql as sql2 } from "kysely";
|
|
443
459
|
import { getDb } from "@secondlayer/shared/db";
|
|
460
|
+
import {
|
|
461
|
+
recordSubgraphProcessed,
|
|
462
|
+
updateSubgraphStatus
|
|
463
|
+
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
444
464
|
import { logger as logger3 } from "@secondlayer/shared/logger";
|
|
445
|
-
import {
|
|
465
|
+
import { sql as sql2 } from "kysely";
|
|
446
466
|
|
|
447
467
|
// src/schema/utils.ts
|
|
448
468
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
@@ -467,12 +487,18 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
467
487
|
} else {
|
|
468
488
|
block = await db.selectFrom("blocks").selectAll().where("height", "=", blockHeight).executeTakeFirst();
|
|
469
489
|
if (!block) {
|
|
470
|
-
logger3.warn("Block not found for subgraph processing", {
|
|
490
|
+
logger3.warn("Block not found for subgraph processing", {
|
|
491
|
+
subgraph: subgraphName,
|
|
492
|
+
blockHeight
|
|
493
|
+
});
|
|
471
494
|
result.skipped = true;
|
|
472
495
|
return result;
|
|
473
496
|
}
|
|
474
497
|
if (!block.canonical) {
|
|
475
|
-
logger3.debug("Skipping non-canonical block", {
|
|
498
|
+
logger3.debug("Skipping non-canonical block", {
|
|
499
|
+
subgraph: subgraphName,
|
|
500
|
+
blockHeight
|
|
501
|
+
});
|
|
476
502
|
result.skipped = true;
|
|
477
503
|
return result;
|
|
478
504
|
}
|
|
@@ -541,7 +567,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
541
567
|
const { rows } = await sql2.raw(`SELECT COUNT(*) as count FROM "${schemaName}"."${table}"`).execute(db);
|
|
542
568
|
const count = Number(rows[0].count);
|
|
543
569
|
if (count >= 1e7) {
|
|
544
|
-
logger3.warn("Subgraph table exceeds 10M rows", {
|
|
570
|
+
logger3.warn("Subgraph table exceeds 10M rows", {
|
|
571
|
+
subgraph: subgraphName,
|
|
572
|
+
table,
|
|
573
|
+
count
|
|
574
|
+
});
|
|
545
575
|
}
|
|
546
576
|
}
|
|
547
577
|
} catch {}
|
|
@@ -595,7 +625,7 @@ class StatsAccumulator {
|
|
|
595
625
|
flush_time_ms: Math.round(entry.flushTimeMs),
|
|
596
626
|
max_block_time_ms: Math.round(entry.maxBlockTimeMs),
|
|
597
627
|
max_handler_time_ms: Math.round(entry.maxHandlerTimeMs),
|
|
598
|
-
avg_ops_per_block: parseFloat(avgOpsPerBlock.toFixed(2)),
|
|
628
|
+
avg_ops_per_block: Number.parseFloat(avgOpsPerBlock.toFixed(2)),
|
|
599
629
|
is_catchup: entry.isCatchup
|
|
600
630
|
}).execute();
|
|
601
631
|
}
|
|
@@ -618,8 +648,11 @@ class StatsAccumulator {
|
|
|
618
648
|
|
|
619
649
|
// src/runtime/catchup.ts
|
|
620
650
|
import { getDb as getDb2 } from "@secondlayer/shared/db";
|
|
621
|
-
import {
|
|
651
|
+
import {
|
|
652
|
+
recordGapBatch
|
|
653
|
+
} from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
622
654
|
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
655
|
+
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
623
656
|
|
|
624
657
|
// src/runtime/batch-loader.ts
|
|
625
658
|
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
@@ -669,6 +702,28 @@ var DEFAULT_BATCH_SIZE = 500;
|
|
|
669
702
|
var MIN_BATCH_SIZE = 100;
|
|
670
703
|
var MAX_BATCH_SIZE = 1000;
|
|
671
704
|
var catchingUp = new Set;
|
|
705
|
+
function coalesceGaps(blocks) {
|
|
706
|
+
if (blocks.length === 0)
|
|
707
|
+
return [];
|
|
708
|
+
blocks.sort((a, b) => a.height - b.height);
|
|
709
|
+
const ranges = [];
|
|
710
|
+
let start = blocks[0].height;
|
|
711
|
+
let end = blocks[0].height;
|
|
712
|
+
let reason = blocks[0].reason;
|
|
713
|
+
for (let i = 1;i < blocks.length; i++) {
|
|
714
|
+
const b = blocks[i];
|
|
715
|
+
if (b.height === end + 1 && b.reason === reason) {
|
|
716
|
+
end = b.height;
|
|
717
|
+
} else {
|
|
718
|
+
ranges.push({ start, end, reason });
|
|
719
|
+
start = b.height;
|
|
720
|
+
end = b.height;
|
|
721
|
+
reason = b.reason;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
ranges.push({ start, end, reason });
|
|
725
|
+
return ranges;
|
|
726
|
+
}
|
|
672
727
|
function adjustBatchSize(current, avgEvents) {
|
|
673
728
|
if (avgEvents > 50)
|
|
674
729
|
return Math.max(Math.round(current * 0.5), MIN_BATCH_SIZE);
|
|
@@ -689,10 +744,11 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
689
744
|
const progress = await db.selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
|
|
690
745
|
if (!progress)
|
|
691
746
|
return 0;
|
|
692
|
-
const chainTip = Number(progress.
|
|
747
|
+
const chainTip = Number(progress.highest_seen_block);
|
|
693
748
|
if (lastProcessedBlock >= chainTip)
|
|
694
749
|
return 0;
|
|
695
|
-
const
|
|
750
|
+
const subgraphStart = Number(subgraphRow.start_block) || 1;
|
|
751
|
+
const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
|
|
696
752
|
const totalBlocks = chainTip - lastProcessedBlock;
|
|
697
753
|
logger4.info("Subgraph catch-up starting", {
|
|
698
754
|
subgraph: subgraphName,
|
|
@@ -706,6 +762,14 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
706
762
|
let currentHeight = startBlock;
|
|
707
763
|
let nextBatchPromise = loadBlockRange(db, currentHeight, Math.min(currentHeight + batchSize - 1, chainTip));
|
|
708
764
|
while (currentHeight <= chainTip) {
|
|
765
|
+
const currentRow = await getSubgraph(db, subgraphName);
|
|
766
|
+
if (!currentRow || currentRow.status !== "active") {
|
|
767
|
+
logger4.info("Subgraph status changed, stopping catch-up", {
|
|
768
|
+
subgraph: subgraphName,
|
|
769
|
+
status: currentRow?.status ?? "deleted"
|
|
770
|
+
});
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
709
773
|
const batch = await nextBatchPromise;
|
|
710
774
|
const batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
711
775
|
const nextStart = batchEnd + 1;
|
|
@@ -713,9 +777,11 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
713
777
|
const nextEnd = Math.min(nextStart + batchSize - 1, chainTip);
|
|
714
778
|
nextBatchPromise = loadBlockRange(db, nextStart, nextEnd);
|
|
715
779
|
}
|
|
780
|
+
const batchFailedBlocks = [];
|
|
716
781
|
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
717
782
|
const blockData = batch.get(height);
|
|
718
783
|
if (!blockData) {
|
|
784
|
+
batchFailedBlocks.push({ height, reason: "block_missing" });
|
|
719
785
|
processed++;
|
|
720
786
|
continue;
|
|
721
787
|
}
|
|
@@ -730,6 +796,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
730
796
|
blockHeight: height,
|
|
731
797
|
error: err instanceof Error ? err.message : String(err)
|
|
732
798
|
});
|
|
799
|
+
batchFailedBlocks.push({ height, reason: "processing_error" });
|
|
733
800
|
const { updateSubgraphStatus: updateSubgraphStatus2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
734
801
|
await updateSubgraphStatus2(db, subgraphName, "active", height).catch(() => {});
|
|
735
802
|
processed++;
|
|
@@ -752,6 +819,15 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
752
819
|
});
|
|
753
820
|
}
|
|
754
821
|
}
|
|
822
|
+
if (batchFailedBlocks.length > 0) {
|
|
823
|
+
const gaps = coalesceGaps(batchFailedBlocks);
|
|
824
|
+
await recordGapBatch(db, subgraphRow.id, subgraphName, gaps).catch((err) => {
|
|
825
|
+
logger4.warn("Failed to record subgraph gaps", {
|
|
826
|
+
subgraph: subgraphName,
|
|
827
|
+
error: err instanceof Error ? err.message : String(err)
|
|
828
|
+
});
|
|
829
|
+
});
|
|
830
|
+
}
|
|
755
831
|
const avg = avgEventsPerBlock(batch);
|
|
756
832
|
batchSize = adjustBatchSize(batchSize, avg);
|
|
757
833
|
currentHeight = batchEnd + 1;
|
|
@@ -770,5 +846,5 @@ export {
|
|
|
770
846
|
catchUpSubgraph
|
|
771
847
|
};
|
|
772
848
|
|
|
773
|
-
//# debugId=
|
|
849
|
+
//# debugId=953B5DBF1473343764756E2164756E21
|
|
774
850
|
//# sourceMappingURL=catchup.js.map
|