@secondlayer/subgraphs 3.23.2 → 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.js +50 -15
- package/dist/src/index.js.map +3 -3
- package/dist/src/runtime/block-processor.d.ts +13 -0
- package/dist/src/runtime/block-processor.js +50 -15
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.js +84 -15
- package/dist/src/runtime/catchup.js.map +6 -5
- package/dist/src/runtime/processor.js +182 -130
- package/dist/src/runtime/processor.js.map +7 -7
- package/dist/src/runtime/reindex.js +50 -15
- package/dist/src/runtime/reindex.js.map +3 -3
- package/dist/src/runtime/reorg.js +182 -105
- package/dist/src/runtime/reorg.js.map +7 -6
- package/dist/src/service.js +184 -132
- package/dist/src/service.js.map +7 -7
- package/package.json +3 -5
package/dist/src/service.js
CHANGED
|
@@ -1627,9 +1627,11 @@ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contract
|
|
|
1627
1627
|
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
1628
1628
|
import {
|
|
1629
1629
|
isByoSubgraph,
|
|
1630
|
+
recordLiveError,
|
|
1630
1631
|
recordLiveProgress,
|
|
1631
1632
|
recordSubgraphProcessed,
|
|
1632
1633
|
resolveSubgraphDb,
|
|
1634
|
+
rewindLiveProgress,
|
|
1633
1635
|
updateSubgraphStatus
|
|
1634
1636
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1635
1637
|
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
@@ -2337,10 +2339,20 @@ class CursorRaceLostError extends Error {
|
|
|
2337
2339
|
this.name = "CursorRaceLostError";
|
|
2338
2340
|
}
|
|
2339
2341
|
}
|
|
2342
|
+
|
|
2343
|
+
class LiveCursorRaceLostError extends Error {
|
|
2344
|
+
constructor(subgraphName, height) {
|
|
2345
|
+
super(`live cursor race lost for ${subgraphName} at block ${height}`);
|
|
2346
|
+
this.name = "LiveCursorRaceLostError";
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2340
2349
|
function opCursorMode(opts) {
|
|
2341
2350
|
const ap = opts?.atomicProgress;
|
|
2342
2351
|
return ap && "operationId" in ap ? ap : undefined;
|
|
2343
2352
|
}
|
|
2353
|
+
function isReorgRewind(opts) {
|
|
2354
|
+
return opts?.reorgRewind === true;
|
|
2355
|
+
}
|
|
2344
2356
|
function statusMode(opts) {
|
|
2345
2357
|
const ap = opts?.atomicProgress;
|
|
2346
2358
|
return ap && "status" in ap ? ap : undefined;
|
|
@@ -2410,7 +2422,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2410
2422
|
result.matched = matched.length;
|
|
2411
2423
|
if (matched.length === 0) {
|
|
2412
2424
|
if (!opts?.skipProgressUpdate) {
|
|
2413
|
-
|
|
2425
|
+
if (isReorgRewind(opts)) {
|
|
2426
|
+
await rewindLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2427
|
+
} else {
|
|
2428
|
+
await recordLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2429
|
+
}
|
|
2414
2430
|
}
|
|
2415
2431
|
return result;
|
|
2416
2432
|
}
|
|
@@ -2429,13 +2445,23 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2429
2445
|
};
|
|
2430
2446
|
let handlerMs = 0;
|
|
2431
2447
|
let flushMs = 0;
|
|
2432
|
-
const applyProgress = async (tx, rr) => {
|
|
2448
|
+
const applyProgress = async (tx, rr, flushedWrites) => {
|
|
2433
2449
|
if (opts?.skipProgressUpdate)
|
|
2434
2450
|
return;
|
|
2451
|
+
const rewind = isReorgRewind(opts);
|
|
2435
2452
|
if (rr.errors > 0 && rr.processed === 0) {
|
|
2436
|
-
|
|
2453
|
+
if (rewind) {
|
|
2454
|
+
await updateSubgraphStatus(tx, subgraphName, "error", blockHeight);
|
|
2455
|
+
} else {
|
|
2456
|
+
await recordLiveError(tx, subgraphName, blockHeight);
|
|
2457
|
+
}
|
|
2458
|
+
} else if (rewind) {
|
|
2459
|
+
await rewindLiveProgress(tx, subgraphName, blockHeight);
|
|
2437
2460
|
} else {
|
|
2438
|
-
await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2461
|
+
const advanced = await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2462
|
+
if (!advanced && flushedWrites && !route.byo) {
|
|
2463
|
+
throw new LiveCursorRaceLostError(subgraphName, blockHeight);
|
|
2464
|
+
}
|
|
2439
2465
|
}
|
|
2440
2466
|
if (rr.processed > 0 || rr.errors > 0) {
|
|
2441
2467
|
const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
|
|
@@ -2484,13 +2510,15 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2484
2510
|
} else if (byoOm && manifest && manifest.count > 0) {
|
|
2485
2511
|
await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
|
|
2486
2512
|
}
|
|
2487
|
-
await applyProgress(tx, runResult);
|
|
2513
|
+
await applyProgress(tx, runResult, manifest ? manifest.count > 0 : false);
|
|
2488
2514
|
});
|
|
2489
2515
|
} else {
|
|
2490
2516
|
try {
|
|
2491
2517
|
await targetDb.transaction().execute(async (tx) => {
|
|
2492
2518
|
const opMode = opCursorMode(opts);
|
|
2493
|
-
|
|
2519
|
+
const sm = statusMode(opts);
|
|
2520
|
+
const rewind = isReorgRewind(opts);
|
|
2521
|
+
if (sm) {
|
|
2494
2522
|
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2495
2523
|
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2496
2524
|
result.skipped = true;
|
|
@@ -2502,6 +2530,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2502
2530
|
result.skipped = true;
|
|
2503
2531
|
return;
|
|
2504
2532
|
}
|
|
2533
|
+
} else if (!rewind) {
|
|
2534
|
+
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2535
|
+
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2536
|
+
result.skipped = true;
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2505
2539
|
}
|
|
2506
2540
|
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2507
2541
|
const handlerStart = performance.now();
|
|
@@ -2519,7 +2553,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2519
2553
|
}
|
|
2520
2554
|
flushMs = performance.now() - flushStart;
|
|
2521
2555
|
}
|
|
2522
|
-
const sm = statusMode(opts);
|
|
2523
2556
|
if (sm && flushedWrites) {
|
|
2524
2557
|
await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
|
|
2525
2558
|
} else if (opMode && flushedWrites) {
|
|
@@ -2528,10 +2561,10 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2528
2561
|
throw new CursorRaceLostError(opMode.operationId, blockHeight);
|
|
2529
2562
|
}
|
|
2530
2563
|
}
|
|
2531
|
-
await applyProgress(tx, runResult);
|
|
2564
|
+
await applyProgress(tx, runResult, flushedWrites);
|
|
2532
2565
|
});
|
|
2533
2566
|
} catch (err) {
|
|
2534
|
-
if (err instanceof CursorRaceLostError) {
|
|
2567
|
+
if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
|
|
2535
2568
|
logger6.warn("cursor race lost — block already covered", {
|
|
2536
2569
|
subgraph: subgraphName,
|
|
2537
2570
|
blockHeight,
|
|
@@ -2544,11 +2577,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2544
2577
|
}
|
|
2545
2578
|
}
|
|
2546
2579
|
const totalMs = performance.now() - blockStart;
|
|
2547
|
-
result.
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2580
|
+
if (!result.skipped) {
|
|
2581
|
+
result.timing = {
|
|
2582
|
+
totalMs: Math.round(totalMs),
|
|
2583
|
+
handlerMs: Math.round(handlerMs),
|
|
2584
|
+
flushMs: Math.round(flushMs)
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2552
2587
|
if (blockHeight % 1000 === 0) {
|
|
2553
2588
|
try {
|
|
2554
2589
|
const tables = Object.keys(subgraph.schema);
|
|
@@ -3192,6 +3227,33 @@ async function backfillSubgraph(def, opts) {
|
|
|
3192
3227
|
import { getTargetDb as getTargetDb3 } from "@secondlayer/shared/db";
|
|
3193
3228
|
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
3194
3229
|
import { logger as logger9 } from "@secondlayer/shared/logger";
|
|
3230
|
+
|
|
3231
|
+
// src/runtime/catchup-leader.ts
|
|
3232
|
+
import {
|
|
3233
|
+
SUBGRAPH_CATCHUP_LOCK_KEY,
|
|
3234
|
+
createPostgresLeaderBackend,
|
|
3235
|
+
withLeaderLock
|
|
3236
|
+
} from "@secondlayer/shared/leader";
|
|
3237
|
+
import { targetListenerUrl } from "@secondlayer/shared/queue/listener";
|
|
3238
|
+
var catchUpLeader = false;
|
|
3239
|
+
function isCatchUpLeader() {
|
|
3240
|
+
return catchUpLeader;
|
|
3241
|
+
}
|
|
3242
|
+
function startCatchUpLeader(opts = {}) {
|
|
3243
|
+
return withLeaderLock(SUBGRAPH_CATCHUP_LOCK_KEY, async () => {
|
|
3244
|
+
catchUpLeader = true;
|
|
3245
|
+
await opts.onAcquire?.();
|
|
3246
|
+
return () => {
|
|
3247
|
+
catchUpLeader = false;
|
|
3248
|
+
};
|
|
3249
|
+
}, {
|
|
3250
|
+
pollMs: opts.pollMs,
|
|
3251
|
+
heartbeatMs: opts.heartbeatMs,
|
|
3252
|
+
createBackend: opts.createBackend ?? (() => createPostgresLeaderBackend(targetListenerUrl()))
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
// src/runtime/catchup.ts
|
|
3195
3257
|
var LOG_INTERVAL2 = 1000;
|
|
3196
3258
|
var STANDARD_CATCHUP_BATCH_CONFIG = {
|
|
3197
3259
|
defaultBatchSize: 500,
|
|
@@ -3313,6 +3375,13 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
3313
3375
|
let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
3314
3376
|
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
3315
3377
|
while (currentHeight <= chainTip) {
|
|
3378
|
+
if (!isCatchUpLeader()) {
|
|
3379
|
+
logger9.info("Lost catch-up leadership, stopping catch-up", {
|
|
3380
|
+
subgraph: subgraphName,
|
|
3381
|
+
processed
|
|
3382
|
+
});
|
|
3383
|
+
break;
|
|
3384
|
+
}
|
|
3316
3385
|
const currentRow = await getSubgraph(targetDb, subgraphName);
|
|
3317
3386
|
if (!currentRow || currentRow.status !== "active") {
|
|
3318
3387
|
logger9.info("Subgraph status changed, stopping catch-up", {
|
|
@@ -3422,6 +3491,7 @@ import {
|
|
|
3422
3491
|
resolveSubgraphRawClient
|
|
3423
3492
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
3424
3493
|
import { logger as logger10 } from "@secondlayer/shared/logger";
|
|
3494
|
+
import { sql as sql4 } from "kysely";
|
|
3425
3495
|
async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
3426
3496
|
const targetDb = getTargetDb4();
|
|
3427
3497
|
const activeSubgraphs = (await listSubgraphs(targetDb)).filter((v) => v.status === "active");
|
|
@@ -3438,96 +3508,9 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3438
3508
|
continue;
|
|
3439
3509
|
bumpReorgEpoch(sg.name);
|
|
3440
3510
|
await withSubgraphBlockLock(sg.name, async () => {
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
const revertedByTable = {};
|
|
3445
|
-
for (const tableName of tableNames) {
|
|
3446
|
-
const rows = await client.unsafe(`SELECT * FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1 LIMIT 1000`, [blockHeight]);
|
|
3447
|
-
if (rows.length > 0)
|
|
3448
|
-
revertedByTable[tableName] = rows;
|
|
3449
|
-
}
|
|
3450
|
-
const hasJournal = (await client.unsafe(`SELECT to_regclass('"${schemaName}"."_journal"') AS r`))[0]?.r;
|
|
3451
|
-
if (hasJournal) {
|
|
3452
|
-
await client.begin(async (tx) => {
|
|
3453
|
-
for (const tableName of tableNames) {
|
|
3454
|
-
const earliest = `
|
|
3455
|
-
SELECT DISTINCT ON (row_key) row_key, prev_row
|
|
3456
|
-
FROM "${schemaName}"."_journal"
|
|
3457
|
-
WHERE block_height >= $1 AND table_name = $2
|
|
3458
|
-
ORDER BY row_key, _jid ASC`;
|
|
3459
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" t USING (${earliest}) e WHERE to_jsonb(t.*) @> e.row_key`, [blockHeight, tableName]);
|
|
3460
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3461
|
-
await tx.unsafe(`INSERT INTO "${schemaName}"."${tableName}"
|
|
3462
|
-
SELECT r.* FROM (${earliest}) e
|
|
3463
|
-
CROSS JOIN LATERAL jsonb_populate_record(NULL::"${schemaName}"."${tableName}", e.prev_row) r
|
|
3464
|
-
WHERE e.prev_row IS NOT NULL`, [blockHeight, tableName]);
|
|
3465
|
-
}
|
|
3466
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
|
|
3467
|
-
});
|
|
3468
|
-
} else {
|
|
3469
|
-
logger10.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
|
|
3470
|
-
for (const tableName of tableNames) {
|
|
3471
|
-
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
await client.unsafe(`DO $$ BEGIN
|
|
3475
|
-
IF to_regclass('"${schemaName}"."_factory_addresses"') IS NOT NULL THEN
|
|
3476
|
-
DELETE FROM "${schemaName}"."_factory_addresses" WHERE block_height >= ${Number(blockHeight)};
|
|
3477
|
-
END IF;
|
|
3478
|
-
END $$`);
|
|
3479
|
-
for (const [tableName, rows] of Object.entries(revertedByTable)) {
|
|
3480
|
-
if (rows.length === 0)
|
|
3481
|
-
continue;
|
|
3482
|
-
await targetDb.insertInto("subscription_outbox").columns([
|
|
3483
|
-
"subscription_id",
|
|
3484
|
-
"subgraph_name",
|
|
3485
|
-
"table_name",
|
|
3486
|
-
"block_height",
|
|
3487
|
-
"event_type",
|
|
3488
|
-
"payload",
|
|
3489
|
-
"dedup_key",
|
|
3490
|
-
"row_pk",
|
|
3491
|
-
"status"
|
|
3492
|
-
]).expression((eb) => eb.selectFrom("subscriptions").select((eb2) => [
|
|
3493
|
-
"id as subscription_id",
|
|
3494
|
-
eb2.val(sg.name).as("subgraph_name"),
|
|
3495
|
-
eb2.val(tableName).as("table_name"),
|
|
3496
|
-
eb2.val(blockHeight).as("block_height"),
|
|
3497
|
-
eb2.val(`${sg.name}.${tableName}.reverted`).as("event_type"),
|
|
3498
|
-
eb2.val(JSON.stringify({
|
|
3499
|
-
subgraph: sg.name,
|
|
3500
|
-
table: tableName,
|
|
3501
|
-
reorgFromHeight: blockHeight,
|
|
3502
|
-
rows
|
|
3503
|
-
})).as("payload"),
|
|
3504
|
-
eb2.val(`reorg:${sg.name}:${tableName}:${blockHeight}`).as("dedup_key"),
|
|
3505
|
-
eb2.val({
|
|
3506
|
-
subgraph: sg.name,
|
|
3507
|
-
table: tableName,
|
|
3508
|
-
reorgRoot: blockHeight
|
|
3509
|
-
}).as("row_pk"),
|
|
3510
|
-
eb2.val("pending").as("status")
|
|
3511
|
-
]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute().catch((err) => {
|
|
3512
|
-
logger10.error("Failed to emit revert event for subscriptions", {
|
|
3513
|
-
event: "reorg_revert_emit_dropped",
|
|
3514
|
-
subgraph: sg.name,
|
|
3515
|
-
table: tableName,
|
|
3516
|
-
blockHeight,
|
|
3517
|
-
error: getErrorMessage4(err)
|
|
3518
|
-
});
|
|
3519
|
-
});
|
|
3520
|
-
}
|
|
3521
|
-
logger10.info("Subgraph reorg cleanup done", {
|
|
3522
|
-
subgraph: sg.name,
|
|
3523
|
-
blockHeight,
|
|
3524
|
-
tablesAffected: Object.keys(revertedByTable).length
|
|
3525
|
-
});
|
|
3526
|
-
const def = await loadSubgraphDef(sg);
|
|
3527
|
-
await processBlock(def, sg.name, blockHeight);
|
|
3528
|
-
logger10.info("Subgraph reorg reprocessed", {
|
|
3529
|
-
subgraph: sg.name,
|
|
3530
|
-
blockHeight
|
|
3511
|
+
await getTargetDb4().transaction().execute(async (lockTx) => {
|
|
3512
|
+
await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
|
|
3513
|
+
await reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef);
|
|
3531
3514
|
});
|
|
3532
3515
|
});
|
|
3533
3516
|
} catch (err) {
|
|
@@ -3539,6 +3522,100 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3539
3522
|
}
|
|
3540
3523
|
}
|
|
3541
3524
|
}
|
|
3525
|
+
async function reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef) {
|
|
3526
|
+
const targetDb = getTargetDb4();
|
|
3527
|
+
const schemaName = sg.schema_name ?? pgSchemaName(sg.name);
|
|
3528
|
+
const client = resolveSubgraphRawClient(sg);
|
|
3529
|
+
const tableNames = Object.keys(schema);
|
|
3530
|
+
const revertedByTable = {};
|
|
3531
|
+
for (const tableName of tableNames) {
|
|
3532
|
+
const rows = await client.unsafe(`SELECT * FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1 LIMIT 1000`, [blockHeight]);
|
|
3533
|
+
if (rows.length > 0)
|
|
3534
|
+
revertedByTable[tableName] = rows;
|
|
3535
|
+
}
|
|
3536
|
+
const hasJournal = (await client.unsafe(`SELECT to_regclass('"${schemaName}"."_journal"') AS r`))[0]?.r;
|
|
3537
|
+
if (hasJournal) {
|
|
3538
|
+
await client.begin(async (tx) => {
|
|
3539
|
+
for (const tableName of tableNames) {
|
|
3540
|
+
const earliest = `
|
|
3541
|
+
SELECT DISTINCT ON (row_key) row_key, prev_row
|
|
3542
|
+
FROM "${schemaName}"."_journal"
|
|
3543
|
+
WHERE block_height >= $1 AND table_name = $2
|
|
3544
|
+
ORDER BY row_key, _jid ASC`;
|
|
3545
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" t USING (${earliest}) e WHERE to_jsonb(t.*) @> e.row_key`, [blockHeight, tableName]);
|
|
3546
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3547
|
+
await tx.unsafe(`INSERT INTO "${schemaName}"."${tableName}"
|
|
3548
|
+
SELECT r.* FROM (${earliest}) e
|
|
3549
|
+
CROSS JOIN LATERAL jsonb_populate_record(NULL::"${schemaName}"."${tableName}", e.prev_row) r
|
|
3550
|
+
WHERE e.prev_row IS NOT NULL`, [blockHeight, tableName]);
|
|
3551
|
+
}
|
|
3552
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
|
|
3553
|
+
});
|
|
3554
|
+
} else {
|
|
3555
|
+
logger10.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
|
|
3556
|
+
for (const tableName of tableNames) {
|
|
3557
|
+
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
await client.unsafe(`DO $$ BEGIN
|
|
3561
|
+
IF to_regclass('"${schemaName}"."_factory_addresses"') IS NOT NULL THEN
|
|
3562
|
+
DELETE FROM "${schemaName}"."_factory_addresses" WHERE block_height >= ${Number(blockHeight)};
|
|
3563
|
+
END IF;
|
|
3564
|
+
END $$`);
|
|
3565
|
+
for (const [tableName, rows] of Object.entries(revertedByTable)) {
|
|
3566
|
+
if (rows.length === 0)
|
|
3567
|
+
continue;
|
|
3568
|
+
await targetDb.insertInto("subscription_outbox").columns([
|
|
3569
|
+
"subscription_id",
|
|
3570
|
+
"subgraph_name",
|
|
3571
|
+
"table_name",
|
|
3572
|
+
"block_height",
|
|
3573
|
+
"event_type",
|
|
3574
|
+
"payload",
|
|
3575
|
+
"dedup_key",
|
|
3576
|
+
"row_pk",
|
|
3577
|
+
"status"
|
|
3578
|
+
]).expression((eb) => eb.selectFrom("subscriptions").select((eb2) => [
|
|
3579
|
+
"id as subscription_id",
|
|
3580
|
+
eb2.val(sg.name).as("subgraph_name"),
|
|
3581
|
+
eb2.val(tableName).as("table_name"),
|
|
3582
|
+
eb2.val(blockHeight).as("block_height"),
|
|
3583
|
+
eb2.val(`${sg.name}.${tableName}.reverted`).as("event_type"),
|
|
3584
|
+
eb2.val(JSON.stringify({
|
|
3585
|
+
subgraph: sg.name,
|
|
3586
|
+
table: tableName,
|
|
3587
|
+
reorgFromHeight: blockHeight,
|
|
3588
|
+
rows
|
|
3589
|
+
})).as("payload"),
|
|
3590
|
+
eb2.val(`reorg:${sg.name}:${tableName}:${blockHeight}`).as("dedup_key"),
|
|
3591
|
+
eb2.val({
|
|
3592
|
+
subgraph: sg.name,
|
|
3593
|
+
table: tableName,
|
|
3594
|
+
reorgRoot: blockHeight
|
|
3595
|
+
}).as("row_pk"),
|
|
3596
|
+
eb2.val("pending").as("status")
|
|
3597
|
+
]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.columns(["subscription_id", "dedup_key"]).doNothing()).execute().catch((err) => {
|
|
3598
|
+
logger10.error("Failed to emit revert event for subscriptions", {
|
|
3599
|
+
event: "reorg_revert_emit_dropped",
|
|
3600
|
+
subgraph: sg.name,
|
|
3601
|
+
table: tableName,
|
|
3602
|
+
blockHeight,
|
|
3603
|
+
error: getErrorMessage4(err)
|
|
3604
|
+
});
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3607
|
+
logger10.info("Subgraph reorg cleanup done", {
|
|
3608
|
+
subgraph: sg.name,
|
|
3609
|
+
blockHeight,
|
|
3610
|
+
tablesAffected: Object.keys(revertedByTable).length
|
|
3611
|
+
});
|
|
3612
|
+
const def = await loadSubgraphDef(sg);
|
|
3613
|
+
await processBlock(def, sg.name, blockHeight, { reorgRewind: true });
|
|
3614
|
+
logger10.info("Subgraph reorg reprocessed", {
|
|
3615
|
+
subgraph: sg.name,
|
|
3616
|
+
blockHeight
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3542
3619
|
|
|
3543
3620
|
// src/runtime/processor.ts
|
|
3544
3621
|
import { randomUUID } from "node:crypto";
|
|
@@ -3571,31 +3648,6 @@ import {
|
|
|
3571
3648
|
targetListenerUrl as targetListenerUrl2
|
|
3572
3649
|
} from "@secondlayer/shared/queue/listener";
|
|
3573
3650
|
|
|
3574
|
-
// src/runtime/catchup-leader.ts
|
|
3575
|
-
import {
|
|
3576
|
-
SUBGRAPH_CATCHUP_LOCK_KEY,
|
|
3577
|
-
createPostgresLeaderBackend,
|
|
3578
|
-
withLeaderLock
|
|
3579
|
-
} from "@secondlayer/shared/leader";
|
|
3580
|
-
import { targetListenerUrl } from "@secondlayer/shared/queue/listener";
|
|
3581
|
-
var catchUpLeader = false;
|
|
3582
|
-
function isCatchUpLeader() {
|
|
3583
|
-
return catchUpLeader;
|
|
3584
|
-
}
|
|
3585
|
-
function startCatchUpLeader(opts = {}) {
|
|
3586
|
-
return withLeaderLock(SUBGRAPH_CATCHUP_LOCK_KEY, async () => {
|
|
3587
|
-
catchUpLeader = true;
|
|
3588
|
-
await opts.onAcquire?.();
|
|
3589
|
-
return () => {
|
|
3590
|
-
catchUpLeader = false;
|
|
3591
|
-
};
|
|
3592
|
-
}, {
|
|
3593
|
-
pollMs: opts.pollMs,
|
|
3594
|
-
heartbeatMs: opts.heartbeatMs,
|
|
3595
|
-
createBackend: opts.createBackend ?? (() => createPostgresLeaderBackend(targetListenerUrl()))
|
|
3596
|
-
});
|
|
3597
|
-
}
|
|
3598
|
-
|
|
3599
3651
|
// src/runtime/streams-reorg-poll.ts
|
|
3600
3652
|
import { getErrorMessage as getErrorMessage5 } from "@secondlayer/shared";
|
|
3601
3653
|
import { IndexHttpClient as IndexHttpClient2 } from "@secondlayer/shared/index-http";
|
|
@@ -4013,12 +4065,12 @@ async function startSubgraphProcessor(opts) {
|
|
|
4013
4065
|
// src/service.ts
|
|
4014
4066
|
import { assertDbSplit, getDb } from "@secondlayer/shared/db";
|
|
4015
4067
|
import { logger as logger13 } from "@secondlayer/shared/logger";
|
|
4016
|
-
import { sql as
|
|
4068
|
+
import { sql as sql5 } from "kysely";
|
|
4017
4069
|
var HEARTBEAT_INTERVAL_MS2 = 30000;
|
|
4018
4070
|
var SERVICE_NAME = "subgraph-processor";
|
|
4019
4071
|
async function writeHeartbeat() {
|
|
4020
4072
|
try {
|
|
4021
|
-
await getDb().insertInto("service_heartbeats").values({ name: SERVICE_NAME }).onConflict((oc) => oc.column("name").doUpdateSet({ updated_at:
|
|
4073
|
+
await getDb().insertInto("service_heartbeats").values({ name: SERVICE_NAME }).onConflict((oc) => oc.column("name").doUpdateSet({ updated_at: sql5`now()` })).execute();
|
|
4022
4074
|
} catch (err) {
|
|
4023
4075
|
logger13.warn("subgraph-processor heartbeat write failed", {
|
|
4024
4076
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -4040,5 +4092,5 @@ var shutdown = async () => {
|
|
|
4040
4092
|
process.on("SIGINT", shutdown);
|
|
4041
4093
|
process.on("SIGTERM", shutdown);
|
|
4042
4094
|
|
|
4043
|
-
//# debugId=
|
|
4095
|
+
//# debugId=77535A6AB51F4C4C64756E2164756E21
|
|
4044
4096
|
//# sourceMappingURL=service.js.map
|