@secondlayer/subgraphs 3.26.1 → 3.26.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 +371 -116
- package/dist/src/index.js.map +11 -9
- package/dist/src/runtime/block-processor.js +8 -1
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.d.ts +34 -1
- package/dist/src/runtime/catchup.js +21 -3
- package/dist/src/runtime/catchup.js.map +4 -4
- package/dist/src/runtime/context.js +8 -1
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +323 -305
- package/dist/src/runtime/processor.js.map +7 -7
- package/dist/src/runtime/reindex.js +321 -43
- package/dist/src/runtime/reindex.js.map +7 -5
- package/dist/src/runtime/reorg.js +21 -5
- package/dist/src/runtime/reorg.js.map +4 -4
- package/dist/src/runtime/replay.js +8 -1
- package/dist/src/runtime/replay.js.map +3 -3
- package/dist/src/schema/index.js +8 -1
- package/dist/src/schema/index.js.map +3 -3
- package/dist/src/service.js +325 -307
- package/dist/src/service.js.map +7 -7
- package/dist/src/testing/index.js +8 -1
- package/dist/src/testing/index.js.map +3 -3
- package/package.json +1 -1
|
@@ -16,6 +16,13 @@ function quotePgIdent(name) {
|
|
|
16
16
|
return name;
|
|
17
17
|
return `"${name.replace(/"/g, '""')}"`;
|
|
18
18
|
}
|
|
19
|
+
function snakeToCamel(name) {
|
|
20
|
+
return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
21
|
+
}
|
|
22
|
+
function pascalCase(name) {
|
|
23
|
+
const camel = snakeToCamel(name);
|
|
24
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
25
|
+
}
|
|
19
26
|
|
|
20
27
|
// src/schema/generator.ts
|
|
21
28
|
var TYPE_MAP = {
|
|
@@ -3044,9 +3051,278 @@ class StatsAccumulator {
|
|
|
3044
3051
|
}
|
|
3045
3052
|
}
|
|
3046
3053
|
|
|
3054
|
+
// src/runtime/catchup.ts
|
|
3055
|
+
import { getTargetDb as getTargetDb2, sql as sql4 } from "@secondlayer/shared/db";
|
|
3056
|
+
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
3057
|
+
import { logger as logger8 } from "@secondlayer/shared/logger";
|
|
3058
|
+
|
|
3059
|
+
// src/runtime/catchup-leader.ts
|
|
3060
|
+
import {
|
|
3061
|
+
SUBGRAPH_CATCHUP_LOCK_KEY,
|
|
3062
|
+
createPostgresLeaderBackend,
|
|
3063
|
+
withLeaderLock
|
|
3064
|
+
} from "@secondlayer/shared/leader";
|
|
3065
|
+
import { targetListenerUrl } from "@secondlayer/shared/queue/listener";
|
|
3066
|
+
var catchUpLeader = false;
|
|
3067
|
+
function isCatchUpLeader() {
|
|
3068
|
+
return catchUpLeader;
|
|
3069
|
+
}
|
|
3070
|
+
function startCatchUpLeader(opts = {}) {
|
|
3071
|
+
return withLeaderLock(SUBGRAPH_CATCHUP_LOCK_KEY, async () => {
|
|
3072
|
+
catchUpLeader = true;
|
|
3073
|
+
await opts.onAcquire?.();
|
|
3074
|
+
return () => {
|
|
3075
|
+
catchUpLeader = false;
|
|
3076
|
+
};
|
|
3077
|
+
}, {
|
|
3078
|
+
pollMs: opts.pollMs,
|
|
3079
|
+
heartbeatMs: opts.heartbeatMs,
|
|
3080
|
+
createBackend: opts.createBackend ?? (() => createPostgresLeaderBackend(targetListenerUrl()))
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/runtime/catchup.ts
|
|
3085
|
+
var LOG_INTERVAL = 1000;
|
|
3086
|
+
var STANDARD_CATCHUP_BATCH_CONFIG = {
|
|
3087
|
+
defaultBatchSize: 500,
|
|
3088
|
+
minBatchSize: 100,
|
|
3089
|
+
maxBatchSize: 1000,
|
|
3090
|
+
prefetch: true
|
|
3091
|
+
};
|
|
3092
|
+
var catchingUp = new Set;
|
|
3093
|
+
var subgraphBlockLockHeld = new Set;
|
|
3094
|
+
var subgraphBlockLockWaiters = new Map;
|
|
3095
|
+
var reorgEpoch = new Map;
|
|
3096
|
+
async function acquireSubgraphBlockLock(name) {
|
|
3097
|
+
if (!subgraphBlockLockHeld.has(name)) {
|
|
3098
|
+
subgraphBlockLockHeld.add(name);
|
|
3099
|
+
return () => releaseSubgraphBlockLock(name);
|
|
3100
|
+
}
|
|
3101
|
+
return new Promise((resolve) => {
|
|
3102
|
+
const waiters = subgraphBlockLockWaiters.get(name) ?? [];
|
|
3103
|
+
waiters.push(() => resolve(() => releaseSubgraphBlockLock(name)));
|
|
3104
|
+
subgraphBlockLockWaiters.set(name, waiters);
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
function releaseSubgraphBlockLock(name) {
|
|
3108
|
+
const waiters = subgraphBlockLockWaiters.get(name);
|
|
3109
|
+
if (waiters && waiters.length > 0) {
|
|
3110
|
+
const next = waiters.shift();
|
|
3111
|
+
if (waiters.length === 0)
|
|
3112
|
+
subgraphBlockLockWaiters.delete(name);
|
|
3113
|
+
next();
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
subgraphBlockLockHeld.delete(name);
|
|
3117
|
+
}
|
|
3118
|
+
async function withSubgraphBlockLock(name, fn) {
|
|
3119
|
+
const release = await acquireSubgraphBlockLock(name);
|
|
3120
|
+
try {
|
|
3121
|
+
return await fn();
|
|
3122
|
+
} finally {
|
|
3123
|
+
release();
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
async function withSubgraphBlockAdvisoryLock(name, fn) {
|
|
3127
|
+
return getTargetDb2().transaction().execute(async (lockTx) => {
|
|
3128
|
+
await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
|
|
3129
|
+
return fn();
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
async function withSubgraphBlockWriteLock(name, fn) {
|
|
3133
|
+
return withSubgraphBlockLock(name, () => withSubgraphBlockAdvisoryLock(name, fn));
|
|
3134
|
+
}
|
|
3135
|
+
function bumpReorgEpoch(name) {
|
|
3136
|
+
reorgEpoch.set(name, (reorgEpoch.get(name) ?? 0) + 1);
|
|
3137
|
+
}
|
|
3138
|
+
function getReorgEpoch(name) {
|
|
3139
|
+
return reorgEpoch.get(name) ?? 0;
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
class ReorgEpochChangedError extends Error {
|
|
3143
|
+
constructor(name) {
|
|
3144
|
+
super(`reorg epoch changed for ${name} mid catch-up`);
|
|
3145
|
+
this.name = "ReorgEpochChangedError";
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
function parsePositiveInt(value) {
|
|
3149
|
+
if (value == null || value.trim() === "")
|
|
3150
|
+
return;
|
|
3151
|
+
const parsed = Number.parseInt(value, 10);
|
|
3152
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
3153
|
+
}
|
|
3154
|
+
function parseBoolean(value) {
|
|
3155
|
+
if (value == null || value.trim() === "")
|
|
3156
|
+
return;
|
|
3157
|
+
const normalized = value.trim().toLowerCase();
|
|
3158
|
+
if (normalized === "true")
|
|
3159
|
+
return true;
|
|
3160
|
+
if (normalized === "false")
|
|
3161
|
+
return false;
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
function resolveCatchupBatchConfig(env = process.env) {
|
|
3165
|
+
const base = STANDARD_CATCHUP_BATCH_CONFIG;
|
|
3166
|
+
const minBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_MIN_BATCH_SIZE) ?? base.minBatchSize;
|
|
3167
|
+
const maxBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_MAX_BATCH_SIZE) ?? base.maxBatchSize;
|
|
3168
|
+
const defaultBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_BATCH_SIZE) ?? base.defaultBatchSize;
|
|
3169
|
+
return {
|
|
3170
|
+
minBatchSize,
|
|
3171
|
+
maxBatchSize,
|
|
3172
|
+
defaultBatchSize: Math.min(Math.max(defaultBatchSize, minBatchSize), maxBatchSize),
|
|
3173
|
+
prefetch: parseBoolean(env.SUBGRAPH_CATCHUP_PREFETCH) ?? base.prefetch
|
|
3174
|
+
};
|
|
3175
|
+
}
|
|
3176
|
+
function adjustBatchSize(current, avgEvents, config) {
|
|
3177
|
+
if (avgEvents > 50)
|
|
3178
|
+
return Math.max(Math.round(current * 0.5), config.minBatchSize);
|
|
3179
|
+
if (avgEvents < 10)
|
|
3180
|
+
return Math.min(Math.round(current * 1.5), config.maxBatchSize);
|
|
3181
|
+
return current;
|
|
3182
|
+
}
|
|
3183
|
+
async function catchUpSubgraph(subgraph, subgraphName) {
|
|
3184
|
+
if (catchingUp.has(subgraphName))
|
|
3185
|
+
return 0;
|
|
3186
|
+
catchingUp.add(subgraphName);
|
|
3187
|
+
try {
|
|
3188
|
+
const source = resolveBlockSource(subgraph);
|
|
3189
|
+
const targetDb = getTargetDb2();
|
|
3190
|
+
const subgraphRow = await getSubgraph(targetDb, subgraphName);
|
|
3191
|
+
if (!subgraphRow)
|
|
3192
|
+
return 0;
|
|
3193
|
+
const lastProcessedBlock = Number(subgraphRow.last_processed_block);
|
|
3194
|
+
const chainTip = await source.getTip();
|
|
3195
|
+
if (chainTip <= 0 || lastProcessedBlock >= chainTip)
|
|
3196
|
+
return 0;
|
|
3197
|
+
const subgraphStart = Number(subgraphRow.start_block) || 1;
|
|
3198
|
+
const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
|
|
3199
|
+
const totalBlocks = chainTip - lastProcessedBlock;
|
|
3200
|
+
logger8.info("Subgraph catch-up starting", {
|
|
3201
|
+
subgraph: subgraphName,
|
|
3202
|
+
from: startBlock,
|
|
3203
|
+
to: chainTip,
|
|
3204
|
+
blocks: totalBlocks
|
|
3205
|
+
});
|
|
3206
|
+
const stats = new StatsAccumulator(subgraphName, true);
|
|
3207
|
+
let processed = 0;
|
|
3208
|
+
const batchConfig = resolveCatchupBatchConfig();
|
|
3209
|
+
let batchSize = batchConfig.defaultBatchSize;
|
|
3210
|
+
let currentHeight = startBlock;
|
|
3211
|
+
const walkEpoch = getReorgEpoch(subgraphName);
|
|
3212
|
+
let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
3213
|
+
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
3214
|
+
while (currentHeight <= chainTip) {
|
|
3215
|
+
if (!isCatchUpLeader()) {
|
|
3216
|
+
logger8.info("Lost catch-up leadership, stopping catch-up", {
|
|
3217
|
+
subgraph: subgraphName,
|
|
3218
|
+
processed
|
|
3219
|
+
});
|
|
3220
|
+
break;
|
|
3221
|
+
}
|
|
3222
|
+
const currentRow = await getSubgraph(targetDb, subgraphName);
|
|
3223
|
+
if (!currentRow || currentRow.status !== "active") {
|
|
3224
|
+
logger8.info("Subgraph status changed, stopping catch-up", {
|
|
3225
|
+
subgraph: subgraphName,
|
|
3226
|
+
status: currentRow?.status ?? "deleted"
|
|
3227
|
+
});
|
|
3228
|
+
break;
|
|
3229
|
+
}
|
|
3230
|
+
let batchEnd;
|
|
3231
|
+
let batch;
|
|
3232
|
+
if (nextBatchPromise) {
|
|
3233
|
+
batch = await nextBatchPromise;
|
|
3234
|
+
batchEnd = prefetchedBatchEnd;
|
|
3235
|
+
const nextStart = batchEnd + 1;
|
|
3236
|
+
if (nextStart <= chainTip) {
|
|
3237
|
+
prefetchedBatchEnd = Math.min(nextStart + batchSize - 1, chainTip);
|
|
3238
|
+
nextBatchPromise = source.loadBlockRange(nextStart, prefetchedBatchEnd);
|
|
3239
|
+
} else {
|
|
3240
|
+
nextBatchPromise = undefined;
|
|
3241
|
+
}
|
|
3242
|
+
} else {
|
|
3243
|
+
batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
3244
|
+
batch = await source.loadBlockRange(currentHeight, batchEnd);
|
|
3245
|
+
}
|
|
3246
|
+
let stopCatchup = false;
|
|
3247
|
+
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
3248
|
+
let blockData = batch.get(height);
|
|
3249
|
+
if (!blockData) {
|
|
3250
|
+
blockData = (await source.loadBlockRange(height, height)).get(height);
|
|
3251
|
+
}
|
|
3252
|
+
if (!blockData) {
|
|
3253
|
+
logger8.warn("Block missing during catch-up, deferring to next tick", {
|
|
3254
|
+
subgraph: subgraphName,
|
|
3255
|
+
blockHeight: height
|
|
3256
|
+
});
|
|
3257
|
+
stopCatchup = true;
|
|
3258
|
+
break;
|
|
3259
|
+
}
|
|
3260
|
+
const preloaded = blockData;
|
|
3261
|
+
let result;
|
|
3262
|
+
try {
|
|
3263
|
+
result = await withSubgraphBlockWriteLock(subgraphName, async () => {
|
|
3264
|
+
if (getReorgEpoch(subgraphName) !== walkEpoch) {
|
|
3265
|
+
throw new ReorgEpochChangedError(subgraphName);
|
|
3266
|
+
}
|
|
3267
|
+
return processBlockWithRetry(subgraph, subgraphName, height, {
|
|
3268
|
+
preloaded
|
|
3269
|
+
});
|
|
3270
|
+
});
|
|
3271
|
+
} catch (err) {
|
|
3272
|
+
if (err instanceof ReorgEpochChangedError) {
|
|
3273
|
+
logger8.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
|
|
3274
|
+
stopCatchup = true;
|
|
3275
|
+
break;
|
|
3276
|
+
}
|
|
3277
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3278
|
+
logger8.error("Block processing failed persistently during catch-up", {
|
|
3279
|
+
subgraph: subgraphName,
|
|
3280
|
+
blockHeight: height,
|
|
3281
|
+
error: errorMsg
|
|
3282
|
+
});
|
|
3283
|
+
const { updateSubgraphStatus: updateSubgraphStatus2, recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
3284
|
+
await recordSubgraphProcessed2(targetDb, subgraphName, 0, 1, `catch-up halted at block ${height}: ${errorMsg}`).catch(() => {});
|
|
3285
|
+
await updateSubgraphStatus2(targetDb, subgraphName, "error").catch(() => {});
|
|
3286
|
+
stopCatchup = true;
|
|
3287
|
+
break;
|
|
3288
|
+
}
|
|
3289
|
+
processed++;
|
|
3290
|
+
if (result.timing) {
|
|
3291
|
+
stats.record(result.timing, result.processed);
|
|
3292
|
+
if (stats.shouldFlush()) {
|
|
3293
|
+
await stats.flush(targetDb);
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
if (processed % LOG_INTERVAL === 0) {
|
|
3297
|
+
logger8.info("Subgraph catch-up progress", {
|
|
3298
|
+
subgraph: subgraphName,
|
|
3299
|
+
processed,
|
|
3300
|
+
total: totalBlocks,
|
|
3301
|
+
currentBlock: height,
|
|
3302
|
+
pct: Math.round(processed / totalBlocks * 100)
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
if (stopCatchup)
|
|
3307
|
+
break;
|
|
3308
|
+
const avg = avgEventsPerBlock(batch);
|
|
3309
|
+
batchSize = adjustBatchSize(batchSize, avg, batchConfig);
|
|
3310
|
+
currentHeight = batchEnd + 1;
|
|
3311
|
+
}
|
|
3312
|
+
await stats.flush(targetDb);
|
|
3313
|
+
logger8.info("Subgraph catch-up complete", {
|
|
3314
|
+
subgraph: subgraphName,
|
|
3315
|
+
processed
|
|
3316
|
+
});
|
|
3317
|
+
return processed;
|
|
3318
|
+
} finally {
|
|
3319
|
+
catchingUp.delete(subgraphName);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3047
3323
|
// src/runtime/reindex.ts
|
|
3048
3324
|
import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
|
|
3049
|
-
import { getRawClient, getTargetDb as
|
|
3325
|
+
import { getRawClient, getTargetDb as getTargetDb3 } from "@secondlayer/shared/db";
|
|
3050
3326
|
import {
|
|
3051
3327
|
recordGapBatch,
|
|
3052
3328
|
resolveGaps
|
|
@@ -3059,11 +3335,11 @@ import {
|
|
|
3059
3335
|
recordSubgraphProcessed as recordSubgraphProcessed2,
|
|
3060
3336
|
updateSubgraphStatus as updateSubgraphStatus2
|
|
3061
3337
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
3062
|
-
import { logger as
|
|
3338
|
+
import { logger as logger10 } from "@secondlayer/shared/logger";
|
|
3063
3339
|
|
|
3064
3340
|
// src/runtime/reindex-notify.ts
|
|
3065
3341
|
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
3066
|
-
import { logger as
|
|
3342
|
+
import { logger as logger9 } from "@secondlayer/shared/logger";
|
|
3067
3343
|
async function notifyReindexComplete(db, subgraphName, stats) {
|
|
3068
3344
|
try {
|
|
3069
3345
|
const subgraph = await db.selectFrom("subgraphs").select(["account_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
@@ -3074,7 +3350,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3074
3350
|
return;
|
|
3075
3351
|
const resendKey = process.env.RESEND_API_KEY;
|
|
3076
3352
|
if (!resendKey) {
|
|
3077
|
-
|
|
3353
|
+
logger9.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
|
|
3078
3354
|
subgraph: subgraphName
|
|
3079
3355
|
});
|
|
3080
3356
|
return;
|
|
@@ -3096,14 +3372,14 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3096
3372
|
});
|
|
3097
3373
|
if (!res.ok) {
|
|
3098
3374
|
const text = await res.text().catch(() => "");
|
|
3099
|
-
|
|
3375
|
+
logger9.warn("reindex-complete email failed", {
|
|
3100
3376
|
subgraph: subgraphName,
|
|
3101
3377
|
status: res.status,
|
|
3102
3378
|
body: text.slice(0, 200)
|
|
3103
3379
|
});
|
|
3104
3380
|
}
|
|
3105
3381
|
} catch (err) {
|
|
3106
|
-
|
|
3382
|
+
logger9.warn("reindex-complete email threw", {
|
|
3107
3383
|
subgraph: subgraphName,
|
|
3108
3384
|
error: getErrorMessage2(err)
|
|
3109
3385
|
});
|
|
@@ -3111,7 +3387,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3111
3387
|
}
|
|
3112
3388
|
|
|
3113
3389
|
// src/runtime/reindex.ts
|
|
3114
|
-
var
|
|
3390
|
+
var LOG_INTERVAL2 = 1000;
|
|
3115
3391
|
var HEALTH_FLUSH_INTERVAL = 1000;
|
|
3116
3392
|
var PROGRESS_FLUSH_INTERVAL_MS = 5000;
|
|
3117
3393
|
var EMPTY_BATCH_HALT_THRESHOLD = 3;
|
|
@@ -3131,7 +3407,7 @@ function resolveReindexResumeBlock(row) {
|
|
|
3131
3407
|
const reindexFromBlock = Number(row.reindex_from_block);
|
|
3132
3408
|
return Math.max(lastProcessedBlock + 1, reindexFromBlock);
|
|
3133
3409
|
}
|
|
3134
|
-
function
|
|
3410
|
+
function parsePositiveInt2(value) {
|
|
3135
3411
|
if (value == null || value.trim() === "")
|
|
3136
3412
|
return;
|
|
3137
3413
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -3139,9 +3415,9 @@ function parsePositiveInt(value) {
|
|
|
3139
3415
|
}
|
|
3140
3416
|
function resolveReindexBatchConfig(env = process.env) {
|
|
3141
3417
|
const base = STANDARD_REINDEX_BATCH_CONFIG;
|
|
3142
|
-
const minBatchSize =
|
|
3143
|
-
const maxBatchSize =
|
|
3144
|
-
const defaultBatchSize =
|
|
3418
|
+
const minBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_MIN_BATCH_SIZE) ?? base.minBatchSize;
|
|
3419
|
+
const maxBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_MAX_BATCH_SIZE) ?? base.maxBatchSize;
|
|
3420
|
+
const defaultBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_BATCH_SIZE) ?? base.defaultBatchSize;
|
|
3145
3421
|
return {
|
|
3146
3422
|
minBatchSize,
|
|
3147
3423
|
maxBatchSize,
|
|
@@ -3172,7 +3448,7 @@ function coalesceFailedBlocks(blocks) {
|
|
|
3172
3448
|
}
|
|
3173
3449
|
async function processBlockRange(def, opts) {
|
|
3174
3450
|
const source = resolveBlockSource(def);
|
|
3175
|
-
const targetDb =
|
|
3451
|
+
const targetDb = getTargetDb3();
|
|
3176
3452
|
const subgraphName = def.name;
|
|
3177
3453
|
const { fromBlock, toBlock, status } = opts;
|
|
3178
3454
|
const totalBlocks = toBlock - fromBlock + 1;
|
|
@@ -3215,7 +3491,7 @@ async function processBlockRange(def, opts) {
|
|
|
3215
3491
|
while (currentHeight <= toBlock) {
|
|
3216
3492
|
if (opts.signal?.aborted) {
|
|
3217
3493
|
aborted = true;
|
|
3218
|
-
|
|
3494
|
+
logger10.info("Block processing aborted", {
|
|
3219
3495
|
subgraph: subgraphName,
|
|
3220
3496
|
currentBlock: currentHeight,
|
|
3221
3497
|
reason: String(opts.signal.reason ?? "unknown")
|
|
@@ -3257,14 +3533,16 @@ async function processBlockRange(def, opts) {
|
|
|
3257
3533
|
}
|
|
3258
3534
|
let result;
|
|
3259
3535
|
try {
|
|
3260
|
-
|
|
3536
|
+
const mayWrite = blockData.txs.length > 0 || blockData.events.length > 0;
|
|
3537
|
+
const runBlock = () => processBlockWithRetry(def, subgraphName, height, {
|
|
3261
3538
|
skipProgressUpdate: true,
|
|
3262
3539
|
atomicProgress,
|
|
3263
3540
|
preloaded: blockData
|
|
3264
3541
|
});
|
|
3542
|
+
result = mayWrite ? await withSubgraphBlockWriteLock(subgraphName, runBlock) : await runBlock();
|
|
3265
3543
|
} catch (err) {
|
|
3266
3544
|
const errorMsg = getErrorMessage3(err);
|
|
3267
|
-
|
|
3545
|
+
logger10.error("Block processing failed persistently", {
|
|
3268
3546
|
subgraph: subgraphName,
|
|
3269
3547
|
blockHeight: height,
|
|
3270
3548
|
error: errorMsg
|
|
@@ -3309,8 +3587,8 @@ async function processBlockRange(def, opts) {
|
|
|
3309
3587
|
}
|
|
3310
3588
|
lastProgressFlushAt = now;
|
|
3311
3589
|
}
|
|
3312
|
-
if (blocksProcessed %
|
|
3313
|
-
|
|
3590
|
+
if (blocksProcessed % LOG_INTERVAL2 === 0) {
|
|
3591
|
+
logger10.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
|
|
3314
3592
|
subgraph: subgraphName,
|
|
3315
3593
|
processed: blocksProcessed,
|
|
3316
3594
|
total: totalBlocks,
|
|
@@ -3330,7 +3608,7 @@ async function processBlockRange(def, opts) {
|
|
|
3330
3608
|
if (batchFailedBlocks.length > 0 && opts.subgraphId) {
|
|
3331
3609
|
const gaps = coalesceFailedBlocks(batchFailedBlocks);
|
|
3332
3610
|
await recordGapBatch(targetDb, opts.subgraphId, subgraphName, gaps).catch((err) => {
|
|
3333
|
-
|
|
3611
|
+
logger10.warn("Failed to record subgraph gaps", {
|
|
3334
3612
|
subgraph: subgraphName,
|
|
3335
3613
|
error: err instanceof Error ? err.message : String(err)
|
|
3336
3614
|
});
|
|
@@ -3347,7 +3625,7 @@ async function processBlockRange(def, opts) {
|
|
|
3347
3625
|
} else {
|
|
3348
3626
|
await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
|
|
3349
3627
|
}
|
|
3350
|
-
|
|
3628
|
+
logger10.info("Sparse skip", {
|
|
3351
3629
|
subgraph: subgraphName,
|
|
3352
3630
|
from: batchEnd + 1,
|
|
3353
3631
|
to: jumpTo - 1,
|
|
@@ -3382,16 +3660,16 @@ async function clearReindexMetadata(db, subgraphName) {
|
|
|
3382
3660
|
}
|
|
3383
3661
|
async function reindexSubgraph(def, opts) {
|
|
3384
3662
|
const source = resolveBlockSource(def);
|
|
3385
|
-
const targetDb =
|
|
3663
|
+
const targetDb = getTargetDb3();
|
|
3386
3664
|
const client = getRawClient("target");
|
|
3387
3665
|
const subgraphName = def.name;
|
|
3388
3666
|
const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
|
|
3389
3667
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing");
|
|
3390
|
-
|
|
3668
|
+
logger10.info("Reindex starting", { subgraph: subgraphName });
|
|
3391
3669
|
try {
|
|
3392
3670
|
const { fromBlock, toBlock } = await resolveBlockRange(source, def, opts);
|
|
3393
3671
|
if (fromBlock > toBlock) {
|
|
3394
|
-
|
|
3672
|
+
logger10.info("No blocks to reindex", {
|
|
3395
3673
|
subgraph: subgraphName,
|
|
3396
3674
|
fromBlock,
|
|
3397
3675
|
toBlock
|
|
@@ -3405,7 +3683,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3405
3683
|
await client.unsafe(stmt);
|
|
3406
3684
|
}
|
|
3407
3685
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
|
|
3408
|
-
|
|
3686
|
+
logger10.info("Schema recreated for reindex", {
|
|
3409
3687
|
subgraph: subgraphName,
|
|
3410
3688
|
cursorResetTo: Math.max(0, fromBlock - 1)
|
|
3411
3689
|
});
|
|
@@ -3419,7 +3697,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3419
3697
|
last_error_at: null,
|
|
3420
3698
|
updated_at: new Date
|
|
3421
3699
|
}).where("name", "=", subgraphName).execute();
|
|
3422
|
-
|
|
3700
|
+
logger10.info("Reindexing blocks", {
|
|
3423
3701
|
subgraph: subgraphName,
|
|
3424
3702
|
fromBlock,
|
|
3425
3703
|
toBlock,
|
|
@@ -3441,9 +3719,9 @@ async function reindexSubgraph(def, opts) {
|
|
|
3441
3719
|
if (reason === "user-cancelled") {
|
|
3442
3720
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3443
3721
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3444
|
-
|
|
3722
|
+
logger10.info("Reindex cancelled by user", { subgraph: subgraphName });
|
|
3445
3723
|
} else {
|
|
3446
|
-
|
|
3724
|
+
logger10.info("Reindex interrupted by shutdown, will resume", {
|
|
3447
3725
|
subgraph: subgraphName
|
|
3448
3726
|
});
|
|
3449
3727
|
}
|
|
@@ -3451,7 +3729,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3451
3729
|
}
|
|
3452
3730
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3453
3731
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3454
|
-
|
|
3732
|
+
logger10.info("Reindex complete", {
|
|
3455
3733
|
subgraph: subgraphName,
|
|
3456
3734
|
blocks: result.blocksProcessed,
|
|
3457
3735
|
events: result.totalEventsProcessed,
|
|
@@ -3464,7 +3742,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3464
3742
|
});
|
|
3465
3743
|
return { processed: result.blocksProcessed };
|
|
3466
3744
|
} catch (err) {
|
|
3467
|
-
|
|
3745
|
+
logger10.error("Reindex failed", {
|
|
3468
3746
|
subgraph: subgraphName,
|
|
3469
3747
|
error: getErrorMessage3(err)
|
|
3470
3748
|
});
|
|
@@ -3473,7 +3751,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3473
3751
|
}
|
|
3474
3752
|
}
|
|
3475
3753
|
async function resumeReindex(def, opts) {
|
|
3476
|
-
const targetDb =
|
|
3754
|
+
const targetDb = getTargetDb3();
|
|
3477
3755
|
const subgraphName = def.name;
|
|
3478
3756
|
const row = await targetDb.selectFrom("subgraphs").select([
|
|
3479
3757
|
"id",
|
|
@@ -3486,7 +3764,7 @@ async function resumeReindex(def, opts) {
|
|
|
3486
3764
|
const fromBlock = resolveReindexResumeBlock(row);
|
|
3487
3765
|
const toBlock = Number(row.reindex_to_block);
|
|
3488
3766
|
if (fromBlock == null) {
|
|
3489
|
-
|
|
3767
|
+
logger10.info("No reindex metadata, starting fresh reindex", {
|
|
3490
3768
|
subgraph: subgraphName
|
|
3491
3769
|
});
|
|
3492
3770
|
return reindexSubgraph(def, {
|
|
@@ -3495,12 +3773,12 @@ async function resumeReindex(def, opts) {
|
|
|
3495
3773
|
});
|
|
3496
3774
|
}
|
|
3497
3775
|
if (fromBlock > toBlock) {
|
|
3498
|
-
|
|
3776
|
+
logger10.info("Resume: no remaining blocks", { subgraph: subgraphName });
|
|
3499
3777
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3500
3778
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3501
3779
|
return { processed: 0 };
|
|
3502
3780
|
}
|
|
3503
|
-
|
|
3781
|
+
logger10.info("Resuming reindex", {
|
|
3504
3782
|
subgraph: subgraphName,
|
|
3505
3783
|
fromBlock,
|
|
3506
3784
|
toBlock,
|
|
@@ -3522,9 +3800,9 @@ async function resumeReindex(def, opts) {
|
|
|
3522
3800
|
if (reason === "user-cancelled") {
|
|
3523
3801
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3524
3802
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3525
|
-
|
|
3803
|
+
logger10.info("Resume cancelled by user", { subgraph: subgraphName });
|
|
3526
3804
|
} else {
|
|
3527
|
-
|
|
3805
|
+
logger10.info("Resume interrupted by shutdown, will resume again", {
|
|
3528
3806
|
subgraph: subgraphName
|
|
3529
3807
|
});
|
|
3530
3808
|
}
|
|
@@ -3532,13 +3810,13 @@ async function resumeReindex(def, opts) {
|
|
|
3532
3810
|
}
|
|
3533
3811
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3534
3812
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3535
|
-
|
|
3813
|
+
logger10.info("Resumed reindex complete", {
|
|
3536
3814
|
subgraph: subgraphName,
|
|
3537
3815
|
blocks: result.blocksProcessed
|
|
3538
3816
|
});
|
|
3539
3817
|
return { processed: result.blocksProcessed };
|
|
3540
3818
|
} catch (err) {
|
|
3541
|
-
|
|
3819
|
+
logger10.error("Resumed reindex failed", {
|
|
3542
3820
|
subgraph: subgraphName,
|
|
3543
3821
|
error: getErrorMessage3(err)
|
|
3544
3822
|
});
|
|
@@ -3547,9 +3825,9 @@ async function resumeReindex(def, opts) {
|
|
|
3547
3825
|
}
|
|
3548
3826
|
}
|
|
3549
3827
|
async function backfillSubgraph(def, opts) {
|
|
3550
|
-
const targetDb =
|
|
3828
|
+
const targetDb = getTargetDb3();
|
|
3551
3829
|
const subgraphName = def.name;
|
|
3552
|
-
|
|
3830
|
+
logger10.info("Backfill starting", {
|
|
3553
3831
|
subgraph: subgraphName,
|
|
3554
3832
|
from: opts.fromBlock,
|
|
3555
3833
|
to: opts.toBlock
|
|
@@ -3567,17 +3845,17 @@ async function backfillSubgraph(def, opts) {
|
|
|
3567
3845
|
signal: opts.signal
|
|
3568
3846
|
});
|
|
3569
3847
|
if (result.aborted) {
|
|
3570
|
-
|
|
3848
|
+
logger10.info("Backfill aborted", { subgraph: subgraphName });
|
|
3571
3849
|
return { processed: result.blocksProcessed };
|
|
3572
3850
|
}
|
|
3573
3851
|
const resolved = await resolveGaps(targetDb, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
|
|
3574
3852
|
if (resolved > 0) {
|
|
3575
|
-
|
|
3853
|
+
logger10.info("Resolved subgraph gaps via backfill", {
|
|
3576
3854
|
subgraph: subgraphName,
|
|
3577
3855
|
resolved
|
|
3578
3856
|
});
|
|
3579
3857
|
}
|
|
3580
|
-
|
|
3858
|
+
logger10.info("Backfill complete", {
|
|
3581
3859
|
subgraph: subgraphName,
|
|
3582
3860
|
blocks: result.blocksProcessed,
|
|
3583
3861
|
events: result.totalEventsProcessed,
|
|
@@ -3585,7 +3863,7 @@ async function backfillSubgraph(def, opts) {
|
|
|
3585
3863
|
});
|
|
3586
3864
|
return { processed: result.blocksProcessed };
|
|
3587
3865
|
} catch (err) {
|
|
3588
|
-
|
|
3866
|
+
logger10.error("Backfill failed", {
|
|
3589
3867
|
subgraph: subgraphName,
|
|
3590
3868
|
error: getErrorMessage3(err)
|
|
3591
3869
|
});
|
|
@@ -3602,5 +3880,5 @@ export {
|
|
|
3602
3880
|
backfillSubgraph
|
|
3603
3881
|
};
|
|
3604
3882
|
|
|
3605
|
-
//# debugId=
|
|
3883
|
+
//# debugId=89B9EBD9DF358DD064756E2164756E21
|
|
3606
3884
|
//# sourceMappingURL=reindex.js.map
|