@secondlayer/subgraphs 0.6.0 → 0.7.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 +1 -1
- package/dist/src/index.js +226 -114
- package/dist/src/index.js.map +16 -16
- package/dist/src/runtime/block-processor.js +130 -100
- package/dist/src/runtime/block-processor.js.map +9 -9
- package/dist/src/runtime/catchup.js +177 -102
- 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 +195 -108
- package/dist/src/runtime/processor.js.map +14 -14
- package/dist/src/runtime/reindex.js +219 -113
- package/dist/src/runtime/reindex.js.map +13 -13
- package/dist/src/runtime/reorg.js +143 -104
- package/dist/src/runtime/reorg.js.map +10 -10
- package/dist/src/runtime/runner.d.ts +1 -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 +1 -1
- package/dist/src/schema/index.js +8 -2
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +196 -109
- package/dist/src/service.js.map +15 -15
- package/dist/src/templates.js.map +2 -2
- package/dist/src/types.js.map +2 -2
- 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
|
}
|
|
@@ -617,10 +647,14 @@ class StatsAccumulator {
|
|
|
617
647
|
}
|
|
618
648
|
|
|
619
649
|
// src/runtime/reindex.ts
|
|
650
|
+
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
620
651
|
import { getDb as getDb2, getRawClient } from "@secondlayer/shared/db";
|
|
652
|
+
import {
|
|
653
|
+
recordGapBatch,
|
|
654
|
+
resolveGaps
|
|
655
|
+
} from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
621
656
|
import { updateSubgraphStatus as updateSubgraphStatus2 } from "@secondlayer/shared/db/queries/subgraphs";
|
|
622
657
|
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
623
|
-
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
624
658
|
|
|
625
659
|
// src/schema/generator.ts
|
|
626
660
|
var TYPE_MAP = {
|
|
@@ -754,6 +788,28 @@ var LOG_INTERVAL = 1000;
|
|
|
754
788
|
var DEFAULT_BATCH_SIZE = 500;
|
|
755
789
|
var MIN_BATCH_SIZE = 100;
|
|
756
790
|
var MAX_BATCH_SIZE = 1000;
|
|
791
|
+
function coalesceFailedBlocks(blocks) {
|
|
792
|
+
if (blocks.length === 0)
|
|
793
|
+
return [];
|
|
794
|
+
blocks.sort((a, b) => a.height - b.height);
|
|
795
|
+
const ranges = [];
|
|
796
|
+
let start = blocks[0].height;
|
|
797
|
+
let end = blocks[0].height;
|
|
798
|
+
let reason = blocks[0].reason;
|
|
799
|
+
for (let i = 1;i < blocks.length; i++) {
|
|
800
|
+
const b = blocks[i];
|
|
801
|
+
if (b.height === end + 1 && b.reason === reason) {
|
|
802
|
+
end = b.height;
|
|
803
|
+
} else {
|
|
804
|
+
ranges.push({ start, end, reason });
|
|
805
|
+
start = b.height;
|
|
806
|
+
end = b.height;
|
|
807
|
+
reason = b.reason;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
ranges.push({ start, end, reason });
|
|
811
|
+
return ranges;
|
|
812
|
+
}
|
|
757
813
|
async function processBlockRange(def, opts) {
|
|
758
814
|
const db = getDb2();
|
|
759
815
|
const subgraphName = def.name;
|
|
@@ -774,9 +830,11 @@ async function processBlockRange(def, opts) {
|
|
|
774
830
|
const nextEnd = Math.min(nextStart + batchSize - 1, toBlock);
|
|
775
831
|
nextBatchPromise = loadBlockRange(db, nextStart, nextEnd);
|
|
776
832
|
}
|
|
833
|
+
const batchFailedBlocks = [];
|
|
777
834
|
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
778
835
|
const blockData = batch.get(height);
|
|
779
836
|
if (!blockData) {
|
|
837
|
+
batchFailedBlocks.push({ height, reason: "block_missing" });
|
|
780
838
|
blocksProcessed++;
|
|
781
839
|
continue;
|
|
782
840
|
}
|
|
@@ -792,6 +850,7 @@ async function processBlockRange(def, opts) {
|
|
|
792
850
|
blockHeight: height,
|
|
793
851
|
error: err instanceof Error ? err.message : String(err)
|
|
794
852
|
});
|
|
853
|
+
batchFailedBlocks.push({ height, reason: "processing_error" });
|
|
795
854
|
await updateSubgraphStatus2(db, subgraphName, status, height).catch(() => {});
|
|
796
855
|
blocksProcessed++;
|
|
797
856
|
totalErrors++;
|
|
@@ -819,6 +878,15 @@ async function processBlockRange(def, opts) {
|
|
|
819
878
|
});
|
|
820
879
|
}
|
|
821
880
|
}
|
|
881
|
+
if (batchFailedBlocks.length > 0 && opts.subgraphId) {
|
|
882
|
+
const gaps = coalesceFailedBlocks(batchFailedBlocks);
|
|
883
|
+
await recordGapBatch(db, opts.subgraphId, subgraphName, gaps).catch((err) => {
|
|
884
|
+
logger4.warn("Failed to record subgraph gaps", {
|
|
885
|
+
subgraph: subgraphName,
|
|
886
|
+
error: err instanceof Error ? err.message : String(err)
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
}
|
|
822
890
|
const avg = avgEventsPerBlock(batch);
|
|
823
891
|
if (avg > 50)
|
|
824
892
|
batchSize = Math.max(Math.round(batchSize * 0.5), MIN_BATCH_SIZE);
|
|
@@ -856,28 +924,46 @@ async function reindexSubgraph(def, opts) {
|
|
|
856
924
|
logger4.info("Schema recreated for reindex", { subgraph: subgraphName });
|
|
857
925
|
const { fromBlock, toBlock } = await resolveBlockRange(db, opts);
|
|
858
926
|
if (fromBlock > toBlock) {
|
|
859
|
-
logger4.info("No blocks to reindex", {
|
|
927
|
+
logger4.info("No blocks to reindex", {
|
|
928
|
+
subgraph: subgraphName,
|
|
929
|
+
fromBlock,
|
|
930
|
+
toBlock
|
|
931
|
+
});
|
|
860
932
|
await updateSubgraphStatus2(db, subgraphName, "active", 0);
|
|
861
933
|
return { processed: 0 };
|
|
862
934
|
}
|
|
863
|
-
logger4.info("Reindexing blocks", {
|
|
864
|
-
|
|
935
|
+
logger4.info("Reindexing blocks", {
|
|
936
|
+
subgraph: subgraphName,
|
|
937
|
+
fromBlock,
|
|
938
|
+
toBlock,
|
|
939
|
+
totalBlocks: toBlock - fromBlock + 1
|
|
940
|
+
});
|
|
941
|
+
const subgraphRow = await db.selectFrom("subgraphs").select(["id", "api_key_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
865
942
|
const result = await processBlockRange(def, {
|
|
866
943
|
fromBlock,
|
|
867
944
|
toBlock,
|
|
868
945
|
status: "reindexing",
|
|
869
946
|
isCatchup: false,
|
|
870
|
-
apiKeyId: subgraphRow?.api_key_id ?? null
|
|
947
|
+
apiKeyId: subgraphRow?.api_key_id ?? null,
|
|
948
|
+
subgraphId: subgraphRow?.id
|
|
871
949
|
});
|
|
872
950
|
const { recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
873
951
|
if (result.totalEventsProcessed > 0 || result.totalErrors > 0) {
|
|
874
952
|
await recordSubgraphProcessed2(db, subgraphName, result.totalEventsProcessed, result.totalErrors, result.totalErrors > 0 ? `${result.totalErrors} error(s) during reindex` : undefined);
|
|
875
953
|
}
|
|
876
954
|
await updateSubgraphStatus2(db, subgraphName, "active", toBlock);
|
|
877
|
-
logger4.info("Reindex complete", {
|
|
955
|
+
logger4.info("Reindex complete", {
|
|
956
|
+
subgraph: subgraphName,
|
|
957
|
+
blocks: result.blocksProcessed,
|
|
958
|
+
events: result.totalEventsProcessed,
|
|
959
|
+
errors: result.totalErrors
|
|
960
|
+
});
|
|
878
961
|
return { processed: result.blocksProcessed };
|
|
879
962
|
} catch (err) {
|
|
880
|
-
logger4.error("Reindex failed", {
|
|
963
|
+
logger4.error("Reindex failed", {
|
|
964
|
+
subgraph: subgraphName,
|
|
965
|
+
error: getErrorMessage2(err)
|
|
966
|
+
});
|
|
881
967
|
await updateSubgraphStatus2(db, subgraphName, "error");
|
|
882
968
|
throw err;
|
|
883
969
|
}
|
|
@@ -885,24 +971,44 @@ async function reindexSubgraph(def, opts) {
|
|
|
885
971
|
async function backfillSubgraph(def, opts) {
|
|
886
972
|
const db = getDb2();
|
|
887
973
|
const subgraphName = def.name;
|
|
888
|
-
logger4.info("Backfill starting", {
|
|
974
|
+
logger4.info("Backfill starting", {
|
|
975
|
+
subgraph: subgraphName,
|
|
976
|
+
from: opts.fromBlock,
|
|
977
|
+
to: opts.toBlock
|
|
978
|
+
});
|
|
889
979
|
try {
|
|
890
|
-
const subgraphRow = await db.selectFrom("subgraphs").select("api_key_id").where("name", "=", subgraphName).executeTakeFirst();
|
|
980
|
+
const subgraphRow = await db.selectFrom("subgraphs").select(["id", "api_key_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
891
981
|
const result = await processBlockRange(def, {
|
|
892
982
|
fromBlock: opts.fromBlock,
|
|
893
983
|
toBlock: opts.toBlock,
|
|
894
984
|
status: "active",
|
|
895
985
|
isCatchup: false,
|
|
896
|
-
apiKeyId: subgraphRow?.api_key_id ?? null
|
|
986
|
+
apiKeyId: subgraphRow?.api_key_id ?? null,
|
|
987
|
+
subgraphId: subgraphRow?.id
|
|
897
988
|
});
|
|
989
|
+
const resolved = await resolveGaps(db, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
|
|
990
|
+
if (resolved > 0) {
|
|
991
|
+
logger4.info("Resolved subgraph gaps via backfill", {
|
|
992
|
+
subgraph: subgraphName,
|
|
993
|
+
resolved
|
|
994
|
+
});
|
|
995
|
+
}
|
|
898
996
|
const { recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
899
997
|
if (result.totalEventsProcessed > 0 || result.totalErrors > 0) {
|
|
900
998
|
await recordSubgraphProcessed2(db, subgraphName, result.totalEventsProcessed, result.totalErrors, result.totalErrors > 0 ? `${result.totalErrors} error(s) during backfill` : undefined);
|
|
901
999
|
}
|
|
902
|
-
logger4.info("Backfill complete", {
|
|
1000
|
+
logger4.info("Backfill complete", {
|
|
1001
|
+
subgraph: subgraphName,
|
|
1002
|
+
blocks: result.blocksProcessed,
|
|
1003
|
+
events: result.totalEventsProcessed,
|
|
1004
|
+
errors: result.totalErrors
|
|
1005
|
+
});
|
|
903
1006
|
return { processed: result.blocksProcessed };
|
|
904
1007
|
} catch (err) {
|
|
905
|
-
logger4.error("Backfill failed", {
|
|
1008
|
+
logger4.error("Backfill failed", {
|
|
1009
|
+
subgraph: subgraphName,
|
|
1010
|
+
error: getErrorMessage2(err)
|
|
1011
|
+
});
|
|
906
1012
|
throw err;
|
|
907
1013
|
}
|
|
908
1014
|
}
|
|
@@ -911,5 +1017,5 @@ export {
|
|
|
911
1017
|
backfillSubgraph
|
|
912
1018
|
};
|
|
913
1019
|
|
|
914
|
-
//# debugId=
|
|
1020
|
+
//# debugId=114013EAB31F34E164756E2164756E21
|
|
915
1021
|
//# sourceMappingURL=reindex.js.map
|