@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.
@@ -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 getTargetDb2 } from "@secondlayer/shared/db";
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 logger9 } from "@secondlayer/shared/logger";
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 logger8 } from "@secondlayer/shared/logger";
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
- logger8.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
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
- logger8.warn("reindex-complete email failed", {
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
- logger8.warn("reindex-complete email threw", {
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 LOG_INTERVAL = 1000;
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 parsePositiveInt(value) {
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 = parsePositiveInt(env.SUBGRAPH_REINDEX_MIN_BATCH_SIZE) ?? base.minBatchSize;
3143
- const maxBatchSize = parsePositiveInt(env.SUBGRAPH_REINDEX_MAX_BATCH_SIZE) ?? base.maxBatchSize;
3144
- const defaultBatchSize = parsePositiveInt(env.SUBGRAPH_REINDEX_BATCH_SIZE) ?? base.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 = getTargetDb2();
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
- logger9.info("Block processing aborted", {
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
- result = await processBlockWithRetry(def, subgraphName, height, {
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
- logger9.error("Block processing failed persistently", {
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 % LOG_INTERVAL === 0) {
3313
- logger9.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
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
- logger9.warn("Failed to record subgraph gaps", {
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
- logger9.info("Sparse skip", {
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 = getTargetDb2();
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
- logger9.info("Reindex starting", { subgraph: subgraphName });
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
- logger9.info("No blocks to reindex", {
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
- logger9.info("Schema recreated for reindex", {
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
- logger9.info("Reindexing blocks", {
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
- logger9.info("Reindex cancelled by user", { subgraph: subgraphName });
3722
+ logger10.info("Reindex cancelled by user", { subgraph: subgraphName });
3445
3723
  } else {
3446
- logger9.info("Reindex interrupted by shutdown, will resume", {
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
- logger9.info("Reindex complete", {
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
- logger9.error("Reindex failed", {
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 = getTargetDb2();
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
- logger9.info("No reindex metadata, starting fresh reindex", {
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
- logger9.info("Resume: no remaining blocks", { subgraph: subgraphName });
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
- logger9.info("Resuming reindex", {
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
- logger9.info("Resume cancelled by user", { subgraph: subgraphName });
3803
+ logger10.info("Resume cancelled by user", { subgraph: subgraphName });
3526
3804
  } else {
3527
- logger9.info("Resume interrupted by shutdown, will resume again", {
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
- logger9.info("Resumed reindex complete", {
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
- logger9.error("Resumed reindex failed", {
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 = getTargetDb2();
3828
+ const targetDb = getTargetDb3();
3551
3829
  const subgraphName = def.name;
3552
- logger9.info("Backfill starting", {
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
- logger9.info("Backfill aborted", { subgraph: subgraphName });
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
- logger9.info("Resolved subgraph gaps via backfill", {
3853
+ logger10.info("Resolved subgraph gaps via backfill", {
3576
3854
  subgraph: subgraphName,
3577
3855
  resolved
3578
3856
  });
3579
3857
  }
3580
- logger9.info("Backfill complete", {
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
- logger9.error("Backfill failed", {
3866
+ logger10.error("Backfill failed", {
3589
3867
  subgraph: subgraphName,
3590
3868
  error: getErrorMessage3(err)
3591
3869
  });
@@ -3593,266 +3871,6 @@ async function backfillSubgraph(def, opts) {
3593
3871
  }
3594
3872
  }
3595
3873
 
3596
- // src/runtime/catchup.ts
3597
- import { getTargetDb as getTargetDb3 } from "@secondlayer/shared/db";
3598
- import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
3599
- import { logger as logger10 } from "@secondlayer/shared/logger";
3600
-
3601
- // src/runtime/catchup-leader.ts
3602
- import {
3603
- SUBGRAPH_CATCHUP_LOCK_KEY,
3604
- createPostgresLeaderBackend,
3605
- withLeaderLock
3606
- } from "@secondlayer/shared/leader";
3607
- import { targetListenerUrl } from "@secondlayer/shared/queue/listener";
3608
- var catchUpLeader = false;
3609
- function isCatchUpLeader() {
3610
- return catchUpLeader;
3611
- }
3612
- function startCatchUpLeader(opts = {}) {
3613
- return withLeaderLock(SUBGRAPH_CATCHUP_LOCK_KEY, async () => {
3614
- catchUpLeader = true;
3615
- await opts.onAcquire?.();
3616
- return () => {
3617
- catchUpLeader = false;
3618
- };
3619
- }, {
3620
- pollMs: opts.pollMs,
3621
- heartbeatMs: opts.heartbeatMs,
3622
- createBackend: opts.createBackend ?? (() => createPostgresLeaderBackend(targetListenerUrl()))
3623
- });
3624
- }
3625
-
3626
- // src/runtime/catchup.ts
3627
- var LOG_INTERVAL2 = 1000;
3628
- var STANDARD_CATCHUP_BATCH_CONFIG = {
3629
- defaultBatchSize: 500,
3630
- minBatchSize: 100,
3631
- maxBatchSize: 1000,
3632
- prefetch: true
3633
- };
3634
- var catchingUp = new Set;
3635
- var subgraphBlockLockHeld = new Set;
3636
- var subgraphBlockLockWaiters = new Map;
3637
- var reorgEpoch = new Map;
3638
- async function acquireSubgraphBlockLock(name) {
3639
- if (!subgraphBlockLockHeld.has(name)) {
3640
- subgraphBlockLockHeld.add(name);
3641
- return () => releaseSubgraphBlockLock(name);
3642
- }
3643
- return new Promise((resolve) => {
3644
- const waiters = subgraphBlockLockWaiters.get(name) ?? [];
3645
- waiters.push(() => resolve(() => releaseSubgraphBlockLock(name)));
3646
- subgraphBlockLockWaiters.set(name, waiters);
3647
- });
3648
- }
3649
- function releaseSubgraphBlockLock(name) {
3650
- const waiters = subgraphBlockLockWaiters.get(name);
3651
- if (waiters && waiters.length > 0) {
3652
- const next = waiters.shift();
3653
- if (waiters.length === 0)
3654
- subgraphBlockLockWaiters.delete(name);
3655
- next();
3656
- return;
3657
- }
3658
- subgraphBlockLockHeld.delete(name);
3659
- }
3660
- async function withSubgraphBlockLock(name, fn) {
3661
- const release = await acquireSubgraphBlockLock(name);
3662
- try {
3663
- return await fn();
3664
- } finally {
3665
- release();
3666
- }
3667
- }
3668
- function bumpReorgEpoch(name) {
3669
- reorgEpoch.set(name, (reorgEpoch.get(name) ?? 0) + 1);
3670
- }
3671
- function getReorgEpoch(name) {
3672
- return reorgEpoch.get(name) ?? 0;
3673
- }
3674
-
3675
- class ReorgEpochChangedError extends Error {
3676
- constructor(name) {
3677
- super(`reorg epoch changed for ${name} mid catch-up`);
3678
- this.name = "ReorgEpochChangedError";
3679
- }
3680
- }
3681
- function parsePositiveInt2(value) {
3682
- if (value == null || value.trim() === "")
3683
- return;
3684
- const parsed = Number.parseInt(value, 10);
3685
- return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
3686
- }
3687
- function parseBoolean(value) {
3688
- if (value == null || value.trim() === "")
3689
- return;
3690
- const normalized = value.trim().toLowerCase();
3691
- if (normalized === "true")
3692
- return true;
3693
- if (normalized === "false")
3694
- return false;
3695
- return;
3696
- }
3697
- function resolveCatchupBatchConfig(env = process.env) {
3698
- const base = STANDARD_CATCHUP_BATCH_CONFIG;
3699
- const minBatchSize = parsePositiveInt2(env.SUBGRAPH_CATCHUP_MIN_BATCH_SIZE) ?? base.minBatchSize;
3700
- const maxBatchSize = parsePositiveInt2(env.SUBGRAPH_CATCHUP_MAX_BATCH_SIZE) ?? base.maxBatchSize;
3701
- const defaultBatchSize = parsePositiveInt2(env.SUBGRAPH_CATCHUP_BATCH_SIZE) ?? base.defaultBatchSize;
3702
- return {
3703
- minBatchSize,
3704
- maxBatchSize,
3705
- defaultBatchSize: Math.min(Math.max(defaultBatchSize, minBatchSize), maxBatchSize),
3706
- prefetch: parseBoolean(env.SUBGRAPH_CATCHUP_PREFETCH) ?? base.prefetch
3707
- };
3708
- }
3709
- function adjustBatchSize(current, avgEvents, config) {
3710
- if (avgEvents > 50)
3711
- return Math.max(Math.round(current * 0.5), config.minBatchSize);
3712
- if (avgEvents < 10)
3713
- return Math.min(Math.round(current * 1.5), config.maxBatchSize);
3714
- return current;
3715
- }
3716
- async function catchUpSubgraph(subgraph, subgraphName) {
3717
- if (catchingUp.has(subgraphName))
3718
- return 0;
3719
- catchingUp.add(subgraphName);
3720
- try {
3721
- const source = resolveBlockSource(subgraph);
3722
- const targetDb = getTargetDb3();
3723
- const subgraphRow = await getSubgraph(targetDb, subgraphName);
3724
- if (!subgraphRow)
3725
- return 0;
3726
- const lastProcessedBlock = Number(subgraphRow.last_processed_block);
3727
- const chainTip = await source.getTip();
3728
- if (chainTip <= 0 || lastProcessedBlock >= chainTip)
3729
- return 0;
3730
- const subgraphStart = Number(subgraphRow.start_block) || 1;
3731
- const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
3732
- const totalBlocks = chainTip - lastProcessedBlock;
3733
- logger10.info("Subgraph catch-up starting", {
3734
- subgraph: subgraphName,
3735
- from: startBlock,
3736
- to: chainTip,
3737
- blocks: totalBlocks
3738
- });
3739
- const stats = new StatsAccumulator(subgraphName, true);
3740
- let processed = 0;
3741
- const batchConfig = resolveCatchupBatchConfig();
3742
- let batchSize = batchConfig.defaultBatchSize;
3743
- let currentHeight = startBlock;
3744
- const walkEpoch = getReorgEpoch(subgraphName);
3745
- let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
3746
- let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
3747
- while (currentHeight <= chainTip) {
3748
- if (!isCatchUpLeader()) {
3749
- logger10.info("Lost catch-up leadership, stopping catch-up", {
3750
- subgraph: subgraphName,
3751
- processed
3752
- });
3753
- break;
3754
- }
3755
- const currentRow = await getSubgraph(targetDb, subgraphName);
3756
- if (!currentRow || currentRow.status !== "active") {
3757
- logger10.info("Subgraph status changed, stopping catch-up", {
3758
- subgraph: subgraphName,
3759
- status: currentRow?.status ?? "deleted"
3760
- });
3761
- break;
3762
- }
3763
- let batchEnd;
3764
- let batch;
3765
- if (nextBatchPromise) {
3766
- batch = await nextBatchPromise;
3767
- batchEnd = prefetchedBatchEnd;
3768
- const nextStart = batchEnd + 1;
3769
- if (nextStart <= chainTip) {
3770
- prefetchedBatchEnd = Math.min(nextStart + batchSize - 1, chainTip);
3771
- nextBatchPromise = source.loadBlockRange(nextStart, prefetchedBatchEnd);
3772
- } else {
3773
- nextBatchPromise = undefined;
3774
- }
3775
- } else {
3776
- batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
3777
- batch = await source.loadBlockRange(currentHeight, batchEnd);
3778
- }
3779
- let stopCatchup = false;
3780
- for (let height = currentHeight;height <= batchEnd; height++) {
3781
- let blockData = batch.get(height);
3782
- if (!blockData) {
3783
- blockData = (await source.loadBlockRange(height, height)).get(height);
3784
- }
3785
- if (!blockData) {
3786
- logger10.warn("Block missing during catch-up, deferring to next tick", {
3787
- subgraph: subgraphName,
3788
- blockHeight: height
3789
- });
3790
- stopCatchup = true;
3791
- break;
3792
- }
3793
- const preloaded = blockData;
3794
- let result;
3795
- try {
3796
- result = await withSubgraphBlockLock(subgraphName, async () => {
3797
- if (getReorgEpoch(subgraphName) !== walkEpoch) {
3798
- throw new ReorgEpochChangedError(subgraphName);
3799
- }
3800
- return processBlockWithRetry(subgraph, subgraphName, height, {
3801
- preloaded
3802
- });
3803
- });
3804
- } catch (err) {
3805
- if (err instanceof ReorgEpochChangedError) {
3806
- logger10.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
3807
- stopCatchup = true;
3808
- break;
3809
- }
3810
- const errorMsg = err instanceof Error ? err.message : String(err);
3811
- logger10.error("Block processing failed persistently during catch-up", {
3812
- subgraph: subgraphName,
3813
- blockHeight: height,
3814
- error: errorMsg
3815
- });
3816
- const { updateSubgraphStatus: updateSubgraphStatus3, recordSubgraphProcessed: recordSubgraphProcessed3 } = await import("@secondlayer/shared/db/queries/subgraphs");
3817
- await recordSubgraphProcessed3(targetDb, subgraphName, 0, 1, `catch-up halted at block ${height}: ${errorMsg}`).catch(() => {});
3818
- await updateSubgraphStatus3(targetDb, subgraphName, "error").catch(() => {});
3819
- stopCatchup = true;
3820
- break;
3821
- }
3822
- processed++;
3823
- if (result.timing) {
3824
- stats.record(result.timing, result.processed);
3825
- if (stats.shouldFlush()) {
3826
- await stats.flush(targetDb);
3827
- }
3828
- }
3829
- if (processed % LOG_INTERVAL2 === 0) {
3830
- logger10.info("Subgraph catch-up progress", {
3831
- subgraph: subgraphName,
3832
- processed,
3833
- total: totalBlocks,
3834
- currentBlock: height,
3835
- pct: Math.round(processed / totalBlocks * 100)
3836
- });
3837
- }
3838
- }
3839
- if (stopCatchup)
3840
- break;
3841
- const avg = avgEventsPerBlock(batch);
3842
- batchSize = adjustBatchSize(batchSize, avg, batchConfig);
3843
- currentHeight = batchEnd + 1;
3844
- }
3845
- await stats.flush(targetDb);
3846
- logger10.info("Subgraph catch-up complete", {
3847
- subgraph: subgraphName,
3848
- processed
3849
- });
3850
- return processed;
3851
- } finally {
3852
- catchingUp.delete(subgraphName);
3853
- }
3854
- }
3855
-
3856
3874
  // src/runtime/reorg.ts
3857
3875
  import { getErrorMessage as getErrorMessage4 } from "@secondlayer/shared";
3858
3876
  import { getTargetDb as getTargetDb4 } from "@secondlayer/shared/db";
@@ -3861,7 +3879,7 @@ import {
3861
3879
  resolveSubgraphRawClient
3862
3880
  } from "@secondlayer/shared/db/queries/subgraphs";
3863
3881
  import { logger as logger11 } from "@secondlayer/shared/logger";
3864
- import { sql as sql4 } from "kysely";
3882
+ import { sql as sql5 } from "kysely";
3865
3883
  async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
3866
3884
  const targetDb = getTargetDb4();
3867
3885
  const activeSubgraphs = (await listSubgraphs(targetDb)).filter((v) => v.status === "active");
@@ -3879,7 +3897,7 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
3879
3897
  bumpReorgEpoch(sg.name);
3880
3898
  await withSubgraphBlockLock(sg.name, async () => {
3881
3899
  await getTargetDb4().transaction().execute(async (lockTx) => {
3882
- await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
3900
+ await sql5`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-reorg:${sg.name}`}))`.execute(lockTx);
3883
3901
  await reorgOneSubgraph(sg, schema, blockHeight, loadSubgraphDef);
3884
3902
  });
3885
3903
  });
@@ -4434,12 +4452,12 @@ async function startSubgraphProcessor(opts) {
4434
4452
  // src/service.ts
4435
4453
  import { assertDbSplit, getDb } from "@secondlayer/shared/db";
4436
4454
  import { logger as logger14 } from "@secondlayer/shared/logger";
4437
- import { sql as sql5 } from "kysely";
4455
+ import { sql as sql6 } from "kysely";
4438
4456
  var HEARTBEAT_INTERVAL_MS2 = 30000;
4439
4457
  var SERVICE_NAME = "subgraph-processor";
4440
4458
  async function writeHeartbeat() {
4441
4459
  try {
4442
- await getDb().insertInto("service_heartbeats").values({ name: SERVICE_NAME }).onConflict((oc) => oc.column("name").doUpdateSet({ updated_at: sql5`now()` })).execute();
4460
+ await getDb().insertInto("service_heartbeats").values({ name: SERVICE_NAME }).onConflict((oc) => oc.column("name").doUpdateSet({ updated_at: sql6`now()` })).execute();
4443
4461
  } catch (err) {
4444
4462
  logger14.warn("subgraph-processor heartbeat write failed", {
4445
4463
  error: err instanceof Error ? err.message : String(err)
@@ -4461,5 +4479,5 @@ var shutdown = async () => {
4461
4479
  process.on("SIGINT", shutdown);
4462
4480
  process.on("SIGTERM", shutdown);
4463
4481
 
4464
- //# debugId=80FE8B3630A7D83D64756E2164756E21
4482
+ //# debugId=32D018F7AF5EE25E64756E2164756E21
4465
4483
  //# sourceMappingURL=service.js.map