@secondlayer/subgraphs 3.23.2 → 3.24.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.js +68 -28
- package/dist/src/index.js.map +5 -5
- package/dist/src/runtime/block-processor.d.ts +13 -0
- package/dist/src/runtime/block-processor.js +68 -28
- package/dist/src/runtime/block-processor.js.map +5 -5
- package/dist/src/runtime/catchup.js +102 -28
- package/dist/src/runtime/catchup.js.map +8 -7
- package/dist/src/runtime/context.js +19 -14
- package/dist/src/runtime/context.js.map +4 -4
- package/dist/src/runtime/processor.js +200 -143
- package/dist/src/runtime/processor.js.map +9 -9
- package/dist/src/runtime/reindex.js +68 -28
- package/dist/src/runtime/reindex.js.map +5 -5
- package/dist/src/runtime/reorg.js +200 -118
- package/dist/src/runtime/reorg.js.map +9 -8
- package/dist/src/runtime/replay.js +6 -1
- package/dist/src/runtime/replay.js.map +3 -3
- package/dist/src/schema/index.js +19 -14
- package/dist/src/schema/index.js.map +4 -4
- package/dist/src/service.js +202 -145
- package/dist/src/service.js.map +9 -9
- package/dist/src/testing/index.js +19 -14
- package/dist/src/testing/index.js.map +4 -4
- package/package.json +3 -5
package/dist/src/index.js
CHANGED
|
@@ -226,6 +226,11 @@ import { createHash } from "node:crypto";
|
|
|
226
226
|
|
|
227
227
|
// src/schema/utils.ts
|
|
228
228
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
229
|
+
function quotePgIdent(name) {
|
|
230
|
+
if (/^[a-z_][a-z0-9_]*$/i.test(name))
|
|
231
|
+
return name;
|
|
232
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
233
|
+
}
|
|
229
234
|
|
|
230
235
|
// src/schema/generator.ts
|
|
231
236
|
var TYPE_MAP = {
|
|
@@ -250,7 +255,7 @@ function tableNeedsTrgm(tableDef) {
|
|
|
250
255
|
return Object.values(tableDef.columns).some((col) => col.search);
|
|
251
256
|
}
|
|
252
257
|
function emitTableDDL(schemaName, tableName, tableDef) {
|
|
253
|
-
const qualifiedName = `${schemaName}.${tableName}`;
|
|
258
|
+
const qualifiedName = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
|
|
254
259
|
const statements = [];
|
|
255
260
|
const columnDefs = [
|
|
256
261
|
"_id BIGSERIAL PRIMARY KEY",
|
|
@@ -261,12 +266,12 @@ function emitTableDDL(schemaName, tableName, tableDef) {
|
|
|
261
266
|
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
262
267
|
const sqlType = TYPE_MAP[col.type];
|
|
263
268
|
const nullable = col.nullable ? "" : " NOT NULL";
|
|
264
|
-
let colDef = `${colName} ${sqlType}${nullable}`;
|
|
269
|
+
let colDef = `${quotePgIdent(colName)} ${sqlType}${nullable}`;
|
|
265
270
|
if (col.default !== undefined) {
|
|
266
271
|
colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
|
|
267
272
|
}
|
|
268
273
|
if (col.type === "uint") {
|
|
269
|
-
colDef += ` CHECK (${colName} >= 0)`;
|
|
274
|
+
colDef += ` CHECK (${quotePgIdent(colName)} >= 0)`;
|
|
270
275
|
}
|
|
271
276
|
columnDefs.push(colDef);
|
|
272
277
|
}
|
|
@@ -274,30 +279,30 @@ function emitTableDDL(schemaName, tableName, tableDef) {
|
|
|
274
279
|
${columnDefs.join(`,
|
|
275
280
|
`)}
|
|
276
281
|
)`);
|
|
277
|
-
statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`);
|
|
278
|
-
statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`);
|
|
282
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_block_height`)} ON ${qualifiedName} (_block_height)`);
|
|
283
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_tx_id`)} ON ${qualifiedName} (_tx_id)`);
|
|
279
284
|
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
280
285
|
if (col.indexed) {
|
|
281
|
-
statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`);
|
|
286
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}`)} ON ${qualifiedName} (${quotePgIdent(colName)})`);
|
|
282
287
|
}
|
|
283
288
|
}
|
|
284
289
|
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
285
290
|
if (col.search) {
|
|
286
|
-
statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`);
|
|
291
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}_trgm`)} ON ${qualifiedName} USING gin (${quotePgIdent(colName)} gin_trgm_ops)`);
|
|
287
292
|
}
|
|
288
293
|
}
|
|
289
294
|
if (tableDef.indexes) {
|
|
290
295
|
for (let i = 0;i < tableDef.indexes.length; i++) {
|
|
291
296
|
const cols = tableDef.indexes[i];
|
|
292
|
-
const idxName = `idx_${schemaName}_${tableName}_composite_${i}
|
|
293
|
-
statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`);
|
|
297
|
+
const idxName = quotePgIdent(`idx_${schemaName}_${tableName}_composite_${i}`);
|
|
298
|
+
statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.map(quotePgIdent).join(", ")})`);
|
|
294
299
|
}
|
|
295
300
|
}
|
|
296
301
|
if (tableDef.uniqueKeys) {
|
|
297
302
|
for (let i = 0;i < tableDef.uniqueKeys.length; i++) {
|
|
298
303
|
const cols = tableDef.uniqueKeys[i];
|
|
299
|
-
const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}
|
|
300
|
-
statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`);
|
|
304
|
+
const constraintName = quotePgIdent(`uq_${schemaName}_${tableName}_${cols.join("_")}`);
|
|
305
|
+
statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.map(quotePgIdent).join(", ")})`);
|
|
301
306
|
}
|
|
302
307
|
}
|
|
303
308
|
return statements;
|
|
@@ -329,8 +334,8 @@ function emitFactoryDDL(schemaName) {
|
|
|
329
334
|
}
|
|
330
335
|
function emitForeignKeyDDL(schemaName, tableName, tableDef) {
|
|
331
336
|
return (tableDef.relations ?? []).map((rel) => {
|
|
332
|
-
const constraintName = `fk_${schemaName}_${tableName}_${rel.name}
|
|
333
|
-
return `ALTER TABLE ${schemaName}.${tableName} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.join(", ")}) ` + `REFERENCES ${schemaName}.${rel.references} (${rel.referencedColumns.join(", ")})`;
|
|
337
|
+
const constraintName = quotePgIdent(`fk_${schemaName}_${tableName}_${rel.name}`);
|
|
338
|
+
return `ALTER TABLE ${quotePgIdent(schemaName)}.${quotePgIdent(tableName)} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.map(quotePgIdent).join(", ")}) ` + `REFERENCES ${quotePgIdent(schemaName)}.${quotePgIdent(rel.references)} (${rel.referencedColumns.map(quotePgIdent).join(", ")})`;
|
|
334
339
|
});
|
|
335
340
|
}
|
|
336
341
|
function generateSubgraphSQL(def, schemaNameOverride) {
|
|
@@ -1842,9 +1847,11 @@ import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contract
|
|
|
1842
1847
|
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
1843
1848
|
import {
|
|
1844
1849
|
isByoSubgraph,
|
|
1850
|
+
recordLiveError,
|
|
1845
1851
|
recordLiveProgress,
|
|
1846
1852
|
recordSubgraphProcessed,
|
|
1847
1853
|
resolveSubgraphDb,
|
|
1854
|
+
rewindLiveProgress,
|
|
1848
1855
|
updateSubgraphStatus
|
|
1849
1856
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1850
1857
|
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
@@ -2552,10 +2559,20 @@ class CursorRaceLostError extends Error {
|
|
|
2552
2559
|
this.name = "CursorRaceLostError";
|
|
2553
2560
|
}
|
|
2554
2561
|
}
|
|
2562
|
+
|
|
2563
|
+
class LiveCursorRaceLostError extends Error {
|
|
2564
|
+
constructor(subgraphName, height) {
|
|
2565
|
+
super(`live cursor race lost for ${subgraphName} at block ${height}`);
|
|
2566
|
+
this.name = "LiveCursorRaceLostError";
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2555
2569
|
function opCursorMode(opts) {
|
|
2556
2570
|
const ap = opts?.atomicProgress;
|
|
2557
2571
|
return ap && "operationId" in ap ? ap : undefined;
|
|
2558
2572
|
}
|
|
2573
|
+
function isReorgRewind(opts) {
|
|
2574
|
+
return opts?.reorgRewind === true;
|
|
2575
|
+
}
|
|
2559
2576
|
function statusMode(opts) {
|
|
2560
2577
|
const ap = opts?.atomicProgress;
|
|
2561
2578
|
return ap && "status" in ap ? ap : undefined;
|
|
@@ -2625,7 +2642,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2625
2642
|
result.matched = matched.length;
|
|
2626
2643
|
if (matched.length === 0) {
|
|
2627
2644
|
if (!opts?.skipProgressUpdate) {
|
|
2628
|
-
|
|
2645
|
+
if (isReorgRewind(opts)) {
|
|
2646
|
+
await rewindLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2647
|
+
} else {
|
|
2648
|
+
await recordLiveProgress(targetDb, subgraphName, blockHeight);
|
|
2649
|
+
}
|
|
2629
2650
|
}
|
|
2630
2651
|
return result;
|
|
2631
2652
|
}
|
|
@@ -2644,13 +2665,23 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2644
2665
|
};
|
|
2645
2666
|
let handlerMs = 0;
|
|
2646
2667
|
let flushMs = 0;
|
|
2647
|
-
const applyProgress = async (tx, rr) => {
|
|
2668
|
+
const applyProgress = async (tx, rr, flushedWrites) => {
|
|
2648
2669
|
if (opts?.skipProgressUpdate)
|
|
2649
2670
|
return;
|
|
2671
|
+
const rewind = isReorgRewind(opts);
|
|
2650
2672
|
if (rr.errors > 0 && rr.processed === 0) {
|
|
2651
|
-
|
|
2673
|
+
if (rewind) {
|
|
2674
|
+
await updateSubgraphStatus(tx, subgraphName, "error", blockHeight);
|
|
2675
|
+
} else {
|
|
2676
|
+
await recordLiveError(tx, subgraphName, blockHeight);
|
|
2677
|
+
}
|
|
2678
|
+
} else if (rewind) {
|
|
2679
|
+
await rewindLiveProgress(tx, subgraphName, blockHeight);
|
|
2652
2680
|
} else {
|
|
2653
|
-
await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2681
|
+
const advanced = await recordLiveProgress(tx, subgraphName, blockHeight);
|
|
2682
|
+
if (!advanced && flushedWrites && !route.byo) {
|
|
2683
|
+
throw new LiveCursorRaceLostError(subgraphName, blockHeight);
|
|
2684
|
+
}
|
|
2654
2685
|
}
|
|
2655
2686
|
if (rr.processed > 0 || rr.errors > 0) {
|
|
2656
2687
|
const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
|
|
@@ -2699,13 +2730,15 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2699
2730
|
} else if (byoOm && manifest && manifest.count > 0) {
|
|
2700
2731
|
await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
|
|
2701
2732
|
}
|
|
2702
|
-
await applyProgress(tx, runResult);
|
|
2733
|
+
await applyProgress(tx, runResult, manifest ? manifest.count > 0 : false);
|
|
2703
2734
|
});
|
|
2704
2735
|
} else {
|
|
2705
2736
|
try {
|
|
2706
2737
|
await targetDb.transaction().execute(async (tx) => {
|
|
2707
2738
|
const opMode = opCursorMode(opts);
|
|
2708
|
-
|
|
2739
|
+
const sm = statusMode(opts);
|
|
2740
|
+
const rewind = isReorgRewind(opts);
|
|
2741
|
+
if (sm) {
|
|
2709
2742
|
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2710
2743
|
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2711
2744
|
result.skipped = true;
|
|
@@ -2717,6 +2750,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2717
2750
|
result.skipped = true;
|
|
2718
2751
|
return;
|
|
2719
2752
|
}
|
|
2753
|
+
} else if (!rewind) {
|
|
2754
|
+
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2755
|
+
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2756
|
+
result.skipped = true;
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2720
2759
|
}
|
|
2721
2760
|
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2722
2761
|
const handlerStart = performance.now();
|
|
@@ -2734,7 +2773,6 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2734
2773
|
}
|
|
2735
2774
|
flushMs = performance.now() - flushStart;
|
|
2736
2775
|
}
|
|
2737
|
-
const sm = statusMode(opts);
|
|
2738
2776
|
if (sm && flushedWrites) {
|
|
2739
2777
|
await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
|
|
2740
2778
|
} else if (opMode && flushedWrites) {
|
|
@@ -2743,10 +2781,10 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2743
2781
|
throw new CursorRaceLostError(opMode.operationId, blockHeight);
|
|
2744
2782
|
}
|
|
2745
2783
|
}
|
|
2746
|
-
await applyProgress(tx, runResult);
|
|
2784
|
+
await applyProgress(tx, runResult, flushedWrites);
|
|
2747
2785
|
});
|
|
2748
2786
|
} catch (err) {
|
|
2749
|
-
if (err instanceof CursorRaceLostError) {
|
|
2787
|
+
if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
|
|
2750
2788
|
logger6.warn("cursor race lost — block already covered", {
|
|
2751
2789
|
subgraph: subgraphName,
|
|
2752
2790
|
blockHeight,
|
|
@@ -2759,11 +2797,13 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2759
2797
|
}
|
|
2760
2798
|
}
|
|
2761
2799
|
const totalMs = performance.now() - blockStart;
|
|
2762
|
-
result.
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2800
|
+
if (!result.skipped) {
|
|
2801
|
+
result.timing = {
|
|
2802
|
+
totalMs: Math.round(totalMs),
|
|
2803
|
+
handlerMs: Math.round(handlerMs),
|
|
2804
|
+
flushMs: Math.round(flushMs)
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2767
2807
|
if (blockHeight % 1000 === 0) {
|
|
2768
2808
|
try {
|
|
2769
2809
|
const tables = Object.keys(subgraph.schema);
|
|
@@ -4701,5 +4741,5 @@ export {
|
|
|
4701
4741
|
ByoBreakingChangeError
|
|
4702
4742
|
};
|
|
4703
4743
|
|
|
4704
|
-
//# debugId=
|
|
4744
|
+
//# debugId=CE8974B47137FD3C64756E2164756E21
|
|
4705
4745
|
//# sourceMappingURL=index.js.map
|