@secondlayer/subgraphs 3.13.0 → 3.14.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 +47 -1
- package/dist/src/index.js +438 -36
- package/dist/src/index.js.map +7 -6
- package/dist/src/runtime/block-processor.d.ts +16 -11
- package/dist/src/runtime/block-processor.js +83 -30
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.js +83 -30
- package/dist/src/runtime/catchup.js.map +3 -3
- package/dist/src/runtime/processor.js +106 -36
- package/dist/src/runtime/processor.js.map +5 -5
- package/dist/src/runtime/reindex.js +104 -35
- package/dist/src/runtime/reindex.js.map +4 -4
- package/dist/src/runtime/reorg.js +83 -30
- package/dist/src/runtime/reorg.js.map +3 -3
- package/dist/src/schema/index.js +3 -2
- package/dist/src/schema/index.js.map +3 -3
- package/dist/src/service.js +106 -36
- package/dist/src/service.js.map +5 -5
- package/dist/src/validate.js +3 -2
- package/dist/src/validate.js.map +3 -3
- package/package.json +2 -2
|
@@ -250,23 +250,28 @@ interface ProcessBlockOptions {
|
|
|
250
250
|
/** Pre-loaded block data — skips DB reads when provided (used by batch catch-up). */
|
|
251
251
|
preloaded?: PreloadedBlockData;
|
|
252
252
|
/**
|
|
253
|
-
* Crash-safe sequential processing
|
|
254
|
-
* -
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* the
|
|
253
|
+
* Crash-safe sequential processing. Two checkpoint scopes:
|
|
254
|
+
* - `{ status }` (reindex): a written block commits
|
|
255
|
+
* `subgraphs.last_processed_block = blockHeight` in the SAME transaction
|
|
256
|
+
* as its writes; replays skip at/below the cursor. Only for strictly
|
|
257
|
+
* ascending walks over the subgraph's own cursor.
|
|
258
|
+
* - `{ operationId }` (backfill): same guarantee against the OPERATION's
|
|
259
|
+
* own `cursor_block` — backfills legitimately revisit heights below the
|
|
260
|
+
* live cursor, so they must never checkpoint (or read) the subgraph
|
|
261
|
+
* cursor. The advance is a CONDITIONAL monotonic UPDATE: concurrent
|
|
262
|
+
* writers serialize on it, and the loser's whole block tx rolls back
|
|
263
|
+
* (surfaced as `skipped`, never an error/gap).
|
|
264
|
+
* Either way: a crash can never leave committed deltas ahead of the
|
|
265
|
+
* relevant checkpoint.
|
|
263
266
|
*/
|
|
264
267
|
atomicProgress?: {
|
|
265
268
|
status: string
|
|
269
|
+
} | {
|
|
270
|
+
operationId: string
|
|
266
271
|
};
|
|
267
272
|
}
|
|
268
273
|
/** Default per-block retry schedule before a failure counts as persistent. */
|
|
269
|
-
declare const BLOCK_RETRY_DELAYS_MS:
|
|
274
|
+
declare const BLOCK_RETRY_DELAYS_MS: number[];
|
|
270
275
|
/**
|
|
271
276
|
* processBlock with bounded retries. Throws the last error once the schedule
|
|
272
277
|
* is exhausted — callers decide whether that halts the walk (strict paths) or
|
|
@@ -1332,6 +1332,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
1332
1332
|
// src/runtime/block-processor.ts
|
|
1333
1333
|
import { getTargetDb } from "@secondlayer/shared/db";
|
|
1334
1334
|
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
1335
|
+
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
1335
1336
|
import {
|
|
1336
1337
|
isByoSubgraph,
|
|
1337
1338
|
recordSubgraphProcessed,
|
|
@@ -1951,6 +1952,21 @@ async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
|
1951
1952
|
}
|
|
1952
1953
|
return resolved;
|
|
1953
1954
|
}
|
|
1955
|
+
|
|
1956
|
+
class CursorRaceLostError extends Error {
|
|
1957
|
+
constructor(operationId, height) {
|
|
1958
|
+
super(`op ${operationId} lost cursor race at block ${height}`);
|
|
1959
|
+
this.name = "CursorRaceLostError";
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
function opCursorMode(opts) {
|
|
1963
|
+
const ap = opts?.atomicProgress;
|
|
1964
|
+
return ap && "operationId" in ap ? ap : undefined;
|
|
1965
|
+
}
|
|
1966
|
+
function statusMode(opts) {
|
|
1967
|
+
const ap = opts?.atomicProgress;
|
|
1968
|
+
return ap && "status" in ap ? ap : undefined;
|
|
1969
|
+
}
|
|
1954
1970
|
var BLOCK_RETRY_DELAYS_MS = [500, 2000, 5000];
|
|
1955
1971
|
function journalEnabled(opts) {
|
|
1956
1972
|
return !opts?.skipProgressUpdate;
|
|
@@ -2044,12 +2060,19 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2044
2060
|
}
|
|
2045
2061
|
};
|
|
2046
2062
|
if (route.byo) {
|
|
2047
|
-
if (opts
|
|
2063
|
+
if (statusMode(opts)) {
|
|
2048
2064
|
const row = await targetDb.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2049
2065
|
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2050
2066
|
result.skipped = true;
|
|
2051
2067
|
return result;
|
|
2052
2068
|
}
|
|
2069
|
+
} else if (opCursorMode(opts)) {
|
|
2070
|
+
const om = opCursorMode(opts);
|
|
2071
|
+
const row = await targetDb.selectFrom("subgraph_operations").select("cursor_block").where("id", "=", om.operationId).executeTakeFirst();
|
|
2072
|
+
if (row?.cursor_block != null && Number(row.cursor_block) >= blockHeight) {
|
|
2073
|
+
result.skipped = true;
|
|
2074
|
+
return result;
|
|
2075
|
+
}
|
|
2053
2076
|
}
|
|
2054
2077
|
let runResult = { processed: 0, errors: 0 };
|
|
2055
2078
|
let manifest;
|
|
@@ -2070,41 +2093,71 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2070
2093
|
if (manifest && manifest.count > 0) {
|
|
2071
2094
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
2072
2095
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2096
|
+
const byoSm = statusMode(opts);
|
|
2097
|
+
const byoOm = opCursorMode(opts);
|
|
2098
|
+
if (byoSm && manifest && manifest.count > 0) {
|
|
2099
|
+
await updateSubgraphStatus(tx, subgraphName, byoSm.status, blockHeight);
|
|
2100
|
+
} else if (byoOm && manifest && manifest.count > 0) {
|
|
2101
|
+
await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
|
|
2075
2102
|
}
|
|
2076
2103
|
await applyProgress(tx, runResult);
|
|
2077
2104
|
});
|
|
2078
2105
|
} else {
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
const
|
|
2082
|
-
if (
|
|
2083
|
-
|
|
2084
|
-
|
|
2106
|
+
try {
|
|
2107
|
+
await targetDb.transaction().execute(async (tx) => {
|
|
2108
|
+
const opMode = opCursorMode(opts);
|
|
2109
|
+
if (statusMode(opts)) {
|
|
2110
|
+
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2111
|
+
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2112
|
+
result.skipped = true;
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
} else if (opMode) {
|
|
2116
|
+
const row = await tx.selectFrom("subgraph_operations").select("cursor_block").where("id", "=", opMode.operationId).executeTakeFirst();
|
|
2117
|
+
if (row?.cursor_block != null && Number(row.cursor_block) >= blockHeight) {
|
|
2118
|
+
result.skipped = true;
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2085
2121
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2122
|
+
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2123
|
+
const handlerStart = performance.now();
|
|
2124
|
+
const runResult = await runHandlers(subgraph, matched, ctx);
|
|
2125
|
+
handlerMs = performance.now() - handlerStart;
|
|
2126
|
+
result.processed = runResult.processed;
|
|
2127
|
+
result.errors = runResult.errors;
|
|
2128
|
+
let flushedWrites = false;
|
|
2129
|
+
if (ctx.pendingOps > 0) {
|
|
2130
|
+
const flushStart = performance.now();
|
|
2131
|
+
const manifest = await ctx.flush();
|
|
2132
|
+
flushedWrites = manifest.count > 0;
|
|
2133
|
+
if (manifest.count > 0) {
|
|
2134
|
+
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
2135
|
+
}
|
|
2136
|
+
flushMs = performance.now() - flushStart;
|
|
2100
2137
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2138
|
+
const sm = statusMode(opts);
|
|
2139
|
+
if (sm && flushedWrites) {
|
|
2140
|
+
await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
|
|
2141
|
+
} else if (opMode && flushedWrites) {
|
|
2142
|
+
const advanced = await advanceOperationCursor(tx, opMode.operationId, blockHeight);
|
|
2143
|
+
if (!advanced) {
|
|
2144
|
+
throw new CursorRaceLostError(opMode.operationId, blockHeight);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
await applyProgress(tx, runResult);
|
|
2148
|
+
});
|
|
2149
|
+
} catch (err) {
|
|
2150
|
+
if (err instanceof CursorRaceLostError) {
|
|
2151
|
+
logger5.warn("cursor race lost — block already covered", {
|
|
2152
|
+
subgraph: subgraphName,
|
|
2153
|
+
blockHeight,
|
|
2154
|
+
error: err.message
|
|
2155
|
+
});
|
|
2156
|
+
result.skipped = true;
|
|
2157
|
+
return result;
|
|
2105
2158
|
}
|
|
2106
|
-
|
|
2107
|
-
}
|
|
2159
|
+
throw err;
|
|
2160
|
+
}
|
|
2108
2161
|
}
|
|
2109
2162
|
const totalMs = performance.now() - blockStart;
|
|
2110
2163
|
result.timing = {
|
|
@@ -2145,5 +2198,5 @@ export {
|
|
|
2145
2198
|
BLOCK_RETRY_DELAYS_MS
|
|
2146
2199
|
};
|
|
2147
2200
|
|
|
2148
|
-
//# debugId=
|
|
2201
|
+
//# debugId=85BBF80E16AD899B64756E2164756E21
|
|
2149
2202
|
//# sourceMappingURL=block-processor.js.map
|