@secondlayer/subgraphs 3.24.1 → 3.24.3
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 +372 -39
- package/dist/src/index.js.map +7 -4
- package/dist/src/runtime/block-processor.d.ts +20 -2
- package/dist/src/runtime/block-processor.js +343 -9
- package/dist/src/runtime/block-processor.js.map +7 -4
- package/dist/src/runtime/catchup.js +351 -18
- package/dist/src/runtime/catchup.js.map +7 -4
- package/dist/src/runtime/processor.js +412 -79
- package/dist/src/runtime/processor.js.map +7 -4
- package/dist/src/runtime/reindex.js +372 -39
- package/dist/src/runtime/reindex.js.map +7 -4
- package/dist/src/runtime/reorg.js +358 -25
- package/dist/src/runtime/reorg.js.map +7 -4
- package/dist/src/service.js +415 -82
- package/dist/src/service.js.map +7 -4
- package/package.json +2 -2
|
@@ -366,7 +366,25 @@ type EventRecord = {
|
|
|
366
366
|
};
|
|
367
367
|
import { Database as Database2 } from "@secondlayer/shared/db";
|
|
368
368
|
import { Kysely as Kysely2, Transaction } from "kysely";
|
|
369
|
-
/**
|
|
369
|
+
/** f072: thrown by `resolveRoute` when a BYO subgraph needs its user-DB
|
|
370
|
+
* connection decrypted but this process holds no `SECONDLAYER_SECRETS_KEY`
|
|
371
|
+
* — the subgraph processor deliberately does not hold the master key (it
|
|
372
|
+
* runs untrusted handler code in-process, every block; see f049 site 3 /
|
|
373
|
+
* the sandbox spike doc's D3). Deny by default, fail loud: this must NEVER
|
|
374
|
+
* be caught and silently rerouted to the managed target DB, since that
|
|
375
|
+
* would write a tenant's rows into ours — far worse than halting the
|
|
376
|
+
* block. */
|
|
377
|
+
declare class ByoKeyUnavailableError extends Error {
|
|
378
|
+
constructor(subgraphName: string);
|
|
379
|
+
}
|
|
380
|
+
/** Drop a subgraph's cached route — call on redeploy/delete (conn may
|
|
381
|
+
* change). Also evicts any warm sandbox subprocess for this subgraph
|
|
382
|
+
* (f071 Stage A `host.ts`) on the same edge: a redeploy can change
|
|
383
|
+
* `handler_code`/`version`/the BYO connection together, and a stale warm
|
|
384
|
+
* subprocess must not survive past this invalidation any more than the
|
|
385
|
+
* cached route does. A no-op today since nothing sets `sandbox_workers`,
|
|
386
|
+
* but keeps the pool's documented invalidation contract honest for when it
|
|
387
|
+
* is. */
|
|
370
388
|
declare function invalidateSubgraphRoute(subgraphName: string): void;
|
|
371
389
|
/**
|
|
372
390
|
* Resolve each distinct trait used by a subgraph's sources to its conforming
|
|
@@ -473,4 +491,4 @@ declare const BLOCK_RETRY_DELAYS_MS: number[];
|
|
|
473
491
|
*/
|
|
474
492
|
declare function processBlockWithRetry(subgraph: SubgraphDefinition, subgraphName: string, blockHeight: number, opts?: ProcessBlockOptions, retryDelaysMs?: number[]): Promise<ProcessBlockResult>;
|
|
475
493
|
declare function processBlock(subgraph: SubgraphDefinition, subgraphName: string, blockHeight: number, opts?: ProcessBlockOptions): Promise<ProcessBlockResult>;
|
|
476
|
-
export { resolveFactoryContracts, processBlockWithRetry, processBlock, invalidateSubgraphRoute, ProcessBlockTiming, ProcessBlockResult, ProcessBlockOptions, PreloadedBlockData, BLOCK_RETRY_DELAYS_MS };
|
|
494
|
+
export { resolveFactoryContracts, processBlockWithRetry, processBlock, invalidateSubgraphRoute, ProcessBlockTiming, ProcessBlockResult, ProcessBlockOptions, PreloadedBlockData, ByoKeyUnavailableError, BLOCK_RETRY_DELAYS_MS };
|
|
@@ -1627,6 +1627,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map, f
|
|
|
1627
1627
|
}
|
|
1628
1628
|
|
|
1629
1629
|
// src/runtime/block-processor.ts
|
|
1630
|
+
import { secretsKeyAvailable } from "@secondlayer/shared/crypto/secrets";
|
|
1630
1631
|
import { getTargetDb } from "@secondlayer/shared/db";
|
|
1631
1632
|
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
1632
1633
|
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
@@ -1639,7 +1640,7 @@ import {
|
|
|
1639
1640
|
rewindLiveProgress,
|
|
1640
1641
|
updateSubgraphStatus
|
|
1641
1642
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
1642
|
-
import { logger as
|
|
1643
|
+
import { logger as logger7 } from "@secondlayer/shared/logger";
|
|
1643
1644
|
import { sql as sql3 } from "kysely";
|
|
1644
1645
|
|
|
1645
1646
|
// src/runtime/block-source.ts
|
|
@@ -2080,6 +2081,315 @@ async function emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block
|
|
|
2080
2081
|
return rows.length;
|
|
2081
2082
|
}
|
|
2082
2083
|
|
|
2084
|
+
// src/runtime/sandbox/flag.ts
|
|
2085
|
+
function sandboxWorkersGloballyEnabled() {
|
|
2086
|
+
return process.env.SUBGRAPH_SANDBOX_WORKERS === "1";
|
|
2087
|
+
}
|
|
2088
|
+
function sandboxEnabled(subgraph) {
|
|
2089
|
+
return sandboxWorkersGloballyEnabled() && subgraph.sandbox_workers === true;
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
// src/runtime/sandbox/host.ts
|
|
2093
|
+
import { tmpdir } from "node:os";
|
|
2094
|
+
import { fileURLToPath } from "node:url";
|
|
2095
|
+
import { logger as logger6 } from "@secondlayer/shared/logger";
|
|
2096
|
+
|
|
2097
|
+
// src/runtime/sandbox/bundle.ts
|
|
2098
|
+
import esbuild from "esbuild";
|
|
2099
|
+
var ALLOWED_BARE_SPECIFIERS = new Set;
|
|
2100
|
+
var SUBGRAPHS_PACKAGE_SPECIFIER = "@secondlayer/subgraphs";
|
|
2101
|
+
var SUBGRAPHS_STUB_SOURCE = `export function defineSubgraph(def) { return def; }
|
|
2102
|
+
`;
|
|
2103
|
+
function resolverLockdownPlugin() {
|
|
2104
|
+
return {
|
|
2105
|
+
name: "f071-sandbox-resolver-lockdown",
|
|
2106
|
+
setup(build) {
|
|
2107
|
+
build.onResolve({ filter: /^@secondlayer\/subgraphs$/ }, (args) => ({
|
|
2108
|
+
path: args.path,
|
|
2109
|
+
namespace: "f071-sandbox-subgraphs-stub"
|
|
2110
|
+
}));
|
|
2111
|
+
build.onLoad({ filter: /.*/, namespace: "f071-sandbox-subgraphs-stub" }, () => ({ contents: SUBGRAPHS_STUB_SOURCE, loader: "js" }));
|
|
2112
|
+
build.onResolve({ filter: /^(node:|bun:)/ }, (args) => ({
|
|
2113
|
+
path: args.path,
|
|
2114
|
+
namespace: "f071-sandbox-blocked"
|
|
2115
|
+
}));
|
|
2116
|
+
build.onResolve({ filter: /^[^./]/ }, (args) => {
|
|
2117
|
+
if (args.path === SUBGRAPHS_PACKAGE_SPECIFIER)
|
|
2118
|
+
return;
|
|
2119
|
+
if (ALLOWED_BARE_SPECIFIERS.has(args.path))
|
|
2120
|
+
return;
|
|
2121
|
+
return { path: args.path, namespace: "f071-sandbox-blocked" };
|
|
2122
|
+
});
|
|
2123
|
+
build.onLoad({ filter: /.*/, namespace: "f071-sandbox-blocked" }, (args) => ({
|
|
2124
|
+
contents: `throw new Error(${JSON.stringify(`subgraph sandbox: import of "${args.path}" is blocked in sandboxed handler code — handlers must use the injected \`ctx\` for all I/O`)});`,
|
|
2125
|
+
loader: "js"
|
|
2126
|
+
}));
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
async function bundleHandlerCode(handlerCode) {
|
|
2131
|
+
const result = await esbuild.build({
|
|
2132
|
+
stdin: {
|
|
2133
|
+
contents: handlerCode,
|
|
2134
|
+
loader: "ts",
|
|
2135
|
+
resolveDir: process.cwd()
|
|
2136
|
+
},
|
|
2137
|
+
bundle: true,
|
|
2138
|
+
platform: "neutral",
|
|
2139
|
+
format: "esm",
|
|
2140
|
+
write: false,
|
|
2141
|
+
logLevel: "silent",
|
|
2142
|
+
plugins: [resolverLockdownPlugin()]
|
|
2143
|
+
});
|
|
2144
|
+
const out = result.outputFiles?.[0];
|
|
2145
|
+
if (!out)
|
|
2146
|
+
throw new Error("esbuild produced no output for handler_code bundle");
|
|
2147
|
+
return new TextDecoder().decode(out.contents);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
// src/runtime/sandbox/host.ts
|
|
2151
|
+
var BLOCK_TIMEOUT_MS = Number.parseInt(process.env.SUBGRAPH_SANDBOX_BLOCK_TIMEOUT_MS ?? "60000");
|
|
2152
|
+
var SUBPROCESS_ENTRY_PATH = fileURLToPath(new URL("./subprocess-entry.ts", import.meta.url));
|
|
2153
|
+
function spawnSandboxProcess() {
|
|
2154
|
+
let onMessage = null;
|
|
2155
|
+
const proc = Bun.spawn({
|
|
2156
|
+
cmd: [process.execPath, "--no-env-file", SUBPROCESS_ENTRY_PATH],
|
|
2157
|
+
env: { PATH: process.env.PATH ?? "" },
|
|
2158
|
+
cwd: tmpdir(),
|
|
2159
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
2160
|
+
serialization: "advanced",
|
|
2161
|
+
ipc(message) {
|
|
2162
|
+
onMessage?.(message);
|
|
2163
|
+
}
|
|
2164
|
+
});
|
|
2165
|
+
(async () => {
|
|
2166
|
+
const reader = proc.stderr.getReader();
|
|
2167
|
+
const decoder = new TextDecoder;
|
|
2168
|
+
try {
|
|
2169
|
+
while (true) {
|
|
2170
|
+
const { done, value } = await reader.read();
|
|
2171
|
+
if (done)
|
|
2172
|
+
break;
|
|
2173
|
+
const text = decoder.decode(value, { stream: true });
|
|
2174
|
+
if (text.trim()) {
|
|
2175
|
+
logger6.warn("Sandbox subprocess stderr", { stderr: text.trim() });
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
} catch {}
|
|
2179
|
+
})();
|
|
2180
|
+
return {
|
|
2181
|
+
send: (msg) => proc.send(msg),
|
|
2182
|
+
kill: () => proc.kill(),
|
|
2183
|
+
exited: proc.exited,
|
|
2184
|
+
setOnMessage: (handler) => {
|
|
2185
|
+
onMessage = handler;
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
var pool = new Map;
|
|
2190
|
+
|
|
2191
|
+
class SandboxWorkerCrash extends Error {
|
|
2192
|
+
constructor(subgraphName, cause) {
|
|
2193
|
+
super(`sandbox subprocess for "${subgraphName}" failed: ${cause}`);
|
|
2194
|
+
this.name = "SandboxWorkerCrash";
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
async function initSandboxProcess(proc, subgraphName, version, handlerCode) {
|
|
2198
|
+
const bundledCode = await bundleHandlerCode(handlerCode);
|
|
2199
|
+
let settled = false;
|
|
2200
|
+
const ready = new Promise((resolve, reject) => {
|
|
2201
|
+
proc.setOnMessage((msg) => {
|
|
2202
|
+
if (msg.type === "ready" && msg.version === version) {
|
|
2203
|
+
settled = true;
|
|
2204
|
+
proc.setOnMessage(null);
|
|
2205
|
+
resolve();
|
|
2206
|
+
} else if (msg.type === "error") {
|
|
2207
|
+
settled = true;
|
|
2208
|
+
proc.setOnMessage(null);
|
|
2209
|
+
reject(new SandboxWorkerCrash(subgraphName, msg.message));
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
const init = { type: "init", bundledCode, version };
|
|
2213
|
+
proc.send(init);
|
|
2214
|
+
});
|
|
2215
|
+
const crashed = proc.exited.then((code) => {
|
|
2216
|
+
if (settled)
|
|
2217
|
+
return;
|
|
2218
|
+
throw new SandboxWorkerCrash(subgraphName, `subprocess exited (code ${code}) during init`);
|
|
2219
|
+
});
|
|
2220
|
+
await Promise.race([ready, crashed]);
|
|
2221
|
+
}
|
|
2222
|
+
async function ensureSandboxProcess(subgraphName, version, handlerCode) {
|
|
2223
|
+
if (handlerCode == null) {
|
|
2224
|
+
throw new SandboxWorkerCrash(subgraphName, "subgraph has no handler_code; cannot bundle for the sandbox path");
|
|
2225
|
+
}
|
|
2226
|
+
const existing = pool.get(subgraphName);
|
|
2227
|
+
if (existing && existing.version === version)
|
|
2228
|
+
return existing;
|
|
2229
|
+
if (existing) {
|
|
2230
|
+
try {
|
|
2231
|
+
await initSandboxProcess(existing.proc, subgraphName, version, handlerCode);
|
|
2232
|
+
existing.version = version;
|
|
2233
|
+
logger6.info("Sandbox subprocess handler reloaded", {
|
|
2234
|
+
subgraph: subgraphName,
|
|
2235
|
+
version
|
|
2236
|
+
});
|
|
2237
|
+
return existing;
|
|
2238
|
+
} catch (err) {
|
|
2239
|
+
existing.proc.kill();
|
|
2240
|
+
pool.delete(subgraphName);
|
|
2241
|
+
logger6.warn("Sandbox subprocess re-init failed; respawning", {
|
|
2242
|
+
subgraph: subgraphName,
|
|
2243
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
const proc = spawnSandboxProcess();
|
|
2248
|
+
try {
|
|
2249
|
+
await initSandboxProcess(proc, subgraphName, version, handlerCode);
|
|
2250
|
+
} catch (err) {
|
|
2251
|
+
proc.kill();
|
|
2252
|
+
throw err;
|
|
2253
|
+
}
|
|
2254
|
+
const entry = { proc, version, chain: Promise.resolve() };
|
|
2255
|
+
pool.set(subgraphName, entry);
|
|
2256
|
+
return entry;
|
|
2257
|
+
}
|
|
2258
|
+
function evictSandboxWorker(subgraphName) {
|
|
2259
|
+
const entry = pool.get(subgraphName);
|
|
2260
|
+
if (!entry)
|
|
2261
|
+
return;
|
|
2262
|
+
entry.proc.kill();
|
|
2263
|
+
pool.delete(subgraphName);
|
|
2264
|
+
}
|
|
2265
|
+
async function runHandlersSandboxed(params) {
|
|
2266
|
+
const { subgraphName, version, handlerCode, hostCtx, block, matched } = params;
|
|
2267
|
+
const entry = await ensureSandboxProcess(subgraphName, version, handlerCode);
|
|
2268
|
+
const run = entry.chain.then(() => runBlockThroughProcess(entry, subgraphName, hostCtx, block, matched));
|
|
2269
|
+
entry.chain = run.catch(() => {
|
|
2270
|
+
return;
|
|
2271
|
+
});
|
|
2272
|
+
return run;
|
|
2273
|
+
}
|
|
2274
|
+
async function runBlockThroughProcess(entry, subgraphName, hostCtx, block, matched) {
|
|
2275
|
+
const { proc } = entry;
|
|
2276
|
+
let timeoutHandle;
|
|
2277
|
+
let settled = false;
|
|
2278
|
+
try {
|
|
2279
|
+
const done = new Promise((resolve, reject) => {
|
|
2280
|
+
proc.setOnMessage((msg) => {
|
|
2281
|
+
if (msg.type === "readRequest") {
|
|
2282
|
+
answerRead(hostCtx, msg).then((reply) => {
|
|
2283
|
+
proc.send({ type: "readResponse", id: msg.id, reply });
|
|
2284
|
+
}).catch((err) => {
|
|
2285
|
+
settled = true;
|
|
2286
|
+
reject(new SandboxWorkerCrash(subgraphName, `host read failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
2287
|
+
});
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
if (msg.type === "blockDone") {
|
|
2291
|
+
settled = true;
|
|
2292
|
+
resolve({
|
|
2293
|
+
ops: msg.ops,
|
|
2294
|
+
result: { processed: msg.processed, errors: msg.errors }
|
|
2295
|
+
});
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
if (msg.type === "error") {
|
|
2299
|
+
settled = true;
|
|
2300
|
+
reject(new SandboxWorkerCrash(subgraphName, msg.message));
|
|
2301
|
+
}
|
|
2302
|
+
});
|
|
2303
|
+
proc.send({ type: "runBlock", block, matched });
|
|
2304
|
+
});
|
|
2305
|
+
const crashed = proc.exited.then((code) => {
|
|
2306
|
+
if (settled)
|
|
2307
|
+
return;
|
|
2308
|
+
throw new SandboxWorkerCrash(subgraphName, `subprocess exited unexpectedly (code ${code}) mid-block`);
|
|
2309
|
+
});
|
|
2310
|
+
const deadline = new Promise((_, reject) => {
|
|
2311
|
+
timeoutHandle = setTimeout(() => {
|
|
2312
|
+
settled = true;
|
|
2313
|
+
reject(new SandboxWorkerCrash(subgraphName, `block ${block.height} exceeded ${BLOCK_TIMEOUT_MS}ms deadline`));
|
|
2314
|
+
}, BLOCK_TIMEOUT_MS);
|
|
2315
|
+
});
|
|
2316
|
+
const { ops, result } = await Promise.race([done, crashed, deadline]);
|
|
2317
|
+
for (const op of ops) {
|
|
2318
|
+
hostCtx.setTx(op.tx);
|
|
2319
|
+
replayOp(hostCtx, op);
|
|
2320
|
+
}
|
|
2321
|
+
return result;
|
|
2322
|
+
} catch (err) {
|
|
2323
|
+
evictSandboxWorker(subgraphName);
|
|
2324
|
+
logger6.warn("Sandbox subprocess run failed; subprocess evicted", {
|
|
2325
|
+
subgraph: subgraphName,
|
|
2326
|
+
blockHeight: block.height,
|
|
2327
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2328
|
+
});
|
|
2329
|
+
throw err;
|
|
2330
|
+
} finally {
|
|
2331
|
+
if (timeoutHandle !== undefined)
|
|
2332
|
+
clearTimeout(timeoutHandle);
|
|
2333
|
+
proc.setOnMessage(null);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
function replayOp(hostCtx, op) {
|
|
2337
|
+
switch (op.method) {
|
|
2338
|
+
case "insert":
|
|
2339
|
+
hostCtx.insert(op.table, op.args[0]);
|
|
2340
|
+
return;
|
|
2341
|
+
case "upsert":
|
|
2342
|
+
hostCtx.upsert(op.table, op.args[0], op.args[1]);
|
|
2343
|
+
return;
|
|
2344
|
+
case "increment":
|
|
2345
|
+
hostCtx.increment(op.table, op.args[0], op.args[1]);
|
|
2346
|
+
return;
|
|
2347
|
+
case "update":
|
|
2348
|
+
hostCtx.update(op.table, op.args[0], op.args[1]);
|
|
2349
|
+
return;
|
|
2350
|
+
case "delete":
|
|
2351
|
+
hostCtx.delete(op.table, op.args[0]);
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
async function answerRead(hostCtx, msg) {
|
|
2356
|
+
const { method, table, where, column } = msg;
|
|
2357
|
+
switch (method) {
|
|
2358
|
+
case "findOne":
|
|
2359
|
+
return { kind: "row", row: await hostCtx.findOne(table, where) };
|
|
2360
|
+
case "findMany":
|
|
2361
|
+
return { kind: "rows", rows: await hostCtx.findMany(table, where) };
|
|
2362
|
+
case "count":
|
|
2363
|
+
return { kind: "count", count: await hostCtx.count(table, where) };
|
|
2364
|
+
case "countDistinct": {
|
|
2365
|
+
if (!column)
|
|
2366
|
+
throw new Error("countDistinct read missing column");
|
|
2367
|
+
return {
|
|
2368
|
+
kind: "count",
|
|
2369
|
+
count: await hostCtx.countDistinct(table, column, where)
|
|
2370
|
+
};
|
|
2371
|
+
}
|
|
2372
|
+
case "sum": {
|
|
2373
|
+
if (!column)
|
|
2374
|
+
throw new Error("sum read missing column");
|
|
2375
|
+
const v = await hostCtx.sum(table, column, where);
|
|
2376
|
+
return { kind: "amount", amount: v.toString() };
|
|
2377
|
+
}
|
|
2378
|
+
case "min": {
|
|
2379
|
+
if (!column)
|
|
2380
|
+
throw new Error("min read missing column");
|
|
2381
|
+
const v = await hostCtx.min(table, column, where);
|
|
2382
|
+
return { kind: "amount", amount: v == null ? null : v.toString() };
|
|
2383
|
+
}
|
|
2384
|
+
case "max": {
|
|
2385
|
+
if (!column)
|
|
2386
|
+
throw new Error("max read missing column");
|
|
2387
|
+
const v = await hostCtx.max(table, column, where);
|
|
2388
|
+
return { kind: "amount", amount: v == null ? null : v.toString() };
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2083
2393
|
// src/runtime/subscription-state.ts
|
|
2084
2394
|
import { listSubscriptions } from "@secondlayer/shared/db/queries/subscriptions";
|
|
2085
2395
|
import { sql as sql2 } from "kysely";
|
|
@@ -2255,22 +2565,36 @@ async function refreshMatcher(db) {
|
|
|
2255
2565
|
|
|
2256
2566
|
// src/runtime/block-processor.ts
|
|
2257
2567
|
var routeCache = new Map;
|
|
2568
|
+
|
|
2569
|
+
class ByoKeyUnavailableError extends Error {
|
|
2570
|
+
constructor(subgraphName) {
|
|
2571
|
+
super(`subgraph ${subgraphName} is BYO (database_url_enc set) but this process has no SECONDLAYER_SECRETS_KEY — the subgraph processor deliberately does not hold it (f072). Route BYO subgraphs to a process that does, or unset BYO.`);
|
|
2572
|
+
this.name = "ByoKeyUnavailableError";
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2258
2575
|
async function resolveRoute(subgraphName, targetDb) {
|
|
2259
2576
|
const cached = routeCache.get(subgraphName);
|
|
2260
2577
|
if (cached)
|
|
2261
2578
|
return cached;
|
|
2262
2579
|
const row = await targetDb.selectFrom("subgraphs").selectAll().where("name", "=", subgraphName).executeTakeFirst();
|
|
2263
2580
|
const byo = row ? isByoSubgraph(row) : false;
|
|
2581
|
+
if (row && byo && !secretsKeyAvailable()) {
|
|
2582
|
+
throw new ByoKeyUnavailableError(subgraphName);
|
|
2583
|
+
}
|
|
2264
2584
|
const route = {
|
|
2265
2585
|
schemaName: row?.schema_name ?? pgSchemaName(subgraphName),
|
|
2266
2586
|
dataDb: row && byo ? resolveSubgraphDb(row) : targetDb,
|
|
2267
|
-
byo
|
|
2587
|
+
byo,
|
|
2588
|
+
sandboxWorkers: row?.sandbox_workers === true,
|
|
2589
|
+
handlerCode: row?.handler_code ?? null,
|
|
2590
|
+
version: row?.version ?? ""
|
|
2268
2591
|
};
|
|
2269
2592
|
routeCache.set(subgraphName, route);
|
|
2270
2593
|
return route;
|
|
2271
2594
|
}
|
|
2272
2595
|
function invalidateSubgraphRoute(subgraphName) {
|
|
2273
2596
|
routeCache.delete(subgraphName);
|
|
2597
|
+
evictSandboxWorker(subgraphName);
|
|
2274
2598
|
}
|
|
2275
2599
|
async function resolveFactoryContracts(subgraph, blockHeight, schemaName, db, txs, evts) {
|
|
2276
2600
|
const factories = new Map;
|
|
@@ -2376,7 +2700,7 @@ async function processBlockWithRetry(subgraph, subgraphName, blockHeight, opts,
|
|
|
2376
2700
|
const delay = retryDelaysMs[attempt];
|
|
2377
2701
|
if (delay === undefined)
|
|
2378
2702
|
break;
|
|
2379
|
-
|
|
2703
|
+
logger7.warn("Block processing failed, retrying", {
|
|
2380
2704
|
subgraph: subgraphName,
|
|
2381
2705
|
blockHeight,
|
|
2382
2706
|
attempt: attempt + 1,
|
|
@@ -2408,7 +2732,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2408
2732
|
} else {
|
|
2409
2733
|
const data = (await resolveBlockSource(subgraph).loadBlockRange(blockHeight, blockHeight)).get(blockHeight);
|
|
2410
2734
|
if (!data) {
|
|
2411
|
-
|
|
2735
|
+
logger7.debug("Block not found or non-canonical for subgraph processing", {
|
|
2412
2736
|
subgraph: subgraphName,
|
|
2413
2737
|
blockHeight
|
|
2414
2738
|
});
|
|
@@ -2544,7 +2868,16 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2544
2868
|
}
|
|
2545
2869
|
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2546
2870
|
const handlerStart = performance.now();
|
|
2547
|
-
const runResult =
|
|
2871
|
+
const runResult = sandboxEnabled({
|
|
2872
|
+
sandbox_workers: route.sandboxWorkers
|
|
2873
|
+
}) ? await runHandlersSandboxed({
|
|
2874
|
+
subgraphName,
|
|
2875
|
+
version: route.version,
|
|
2876
|
+
handlerCode: route.handlerCode,
|
|
2877
|
+
hostCtx: ctx,
|
|
2878
|
+
block: blockMeta,
|
|
2879
|
+
matched
|
|
2880
|
+
}) : await runHandlers(subgraph, matched, ctx);
|
|
2548
2881
|
handlerMs = performance.now() - handlerStart;
|
|
2549
2882
|
result.processed = runResult.processed;
|
|
2550
2883
|
result.errors = runResult.errors;
|
|
@@ -2570,7 +2903,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2570
2903
|
});
|
|
2571
2904
|
} catch (err) {
|
|
2572
2905
|
if (err instanceof CursorRaceLostError || err instanceof LiveCursorRaceLostError) {
|
|
2573
|
-
|
|
2906
|
+
logger7.warn("cursor race lost — block already covered", {
|
|
2574
2907
|
subgraph: subgraphName,
|
|
2575
2908
|
blockHeight,
|
|
2576
2909
|
error: err.message
|
|
@@ -2596,7 +2929,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2596
2929
|
const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
|
|
2597
2930
|
const count = Number(rows[0]?.count ?? 0);
|
|
2598
2931
|
if (count >= 1e7) {
|
|
2599
|
-
|
|
2932
|
+
logger7.warn("Subgraph table exceeds 10M rows (estimate)", {
|
|
2600
2933
|
subgraph: subgraphName,
|
|
2601
2934
|
table,
|
|
2602
2935
|
count
|
|
@@ -2604,7 +2937,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2604
2937
|
}
|
|
2605
2938
|
}
|
|
2606
2939
|
} catch (err) {
|
|
2607
|
-
|
|
2940
|
+
logger7.debug("Row count sample failed", {
|
|
2608
2941
|
subgraph: subgraphName,
|
|
2609
2942
|
error: err instanceof Error ? err.message : String(err)
|
|
2610
2943
|
});
|
|
@@ -2620,8 +2953,9 @@ export {
|
|
|
2620
2953
|
processBlockWithRetry,
|
|
2621
2954
|
processBlock,
|
|
2622
2955
|
invalidateSubgraphRoute,
|
|
2956
|
+
ByoKeyUnavailableError,
|
|
2623
2957
|
BLOCK_RETRY_DELAYS_MS
|
|
2624
2958
|
};
|
|
2625
2959
|
|
|
2626
|
-
//# debugId=
|
|
2960
|
+
//# debugId=B97EC9302707D2CA64756E2164756E21
|
|
2627
2961
|
//# sourceMappingURL=block-processor.js.map
|