@secondlayer/subgraphs 3.26.1 → 3.26.2

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.
@@ -348,6 +348,39 @@ interface SubgraphDefinition {
348
348
  /** Run `fn` (a single block's write, or a reorg's whole rewind) holding the
349
349
  * per-subgraph block lock. Exported for reorg.ts. */
350
350
  declare function withSubgraphBlockLock<T>(name: string, fn: () => Promise<T>): Promise<T>;
351
+ /**
352
+ * Cross-process half of the block lock: a transaction-scoped Postgres advisory
353
+ * lock keyed `subgraph-block:<name>`, held for the duration of `fn`.
354
+ *
355
+ * Held, not merely acquired: each block's own write transaction is opened
356
+ * INSIDE `processBlock` (block-processor.ts — `targetDb.transaction()` for
357
+ * managed subgraphs, `route.dataDb.transaction()` for BYO), and that boundary
358
+ * is load-bearing for the f069 replay guard, so the lock cannot be taken on
359
+ * the writing connection. Instead this wraps the write in a separate
360
+ * lock-holding transaction on the target DB, exactly as the reorg lock does
361
+ * (reorg.ts): `pg_advisory_xact_lock` releases only when THIS transaction
362
+ * commits, regardless of which other connections `fn` awaits meanwhile.
363
+ * Transaction-scoped rather than session-scoped so a crashed walk can never
364
+ * strand the lock.
365
+ *
366
+ * The lock lives on the target DB even for BYO subgraphs — same as the reorg
367
+ * lock — because it coordinates control-plane walkers, not data-plane rows.
368
+ *
369
+ * Connection budget: this holds one pooled target-DB connection while `fn`
370
+ * takes another for the write, so an in-flight block costs 2 of
371
+ * `DATABASE_POOL_MAX` (default 20). Concurrent block writes are bounded by
372
+ * catch-up fan-out (5 subgraphs) plus operation concurrency (1) = 6 blocks =
373
+ * 12 connections. Raising either concurrency knob must be checked against the
374
+ * pool, or the wrapping transactions can starve the writes they wrap.
375
+ */
376
+ declare function withSubgraphBlockAdvisoryLock<T>(name: string, fn: () => Promise<T>): Promise<T>;
377
+ /**
378
+ * One block's write, holding BOTH halves of the block lock in the documented
379
+ * order: the in-process mutex, then the cross-process advisory lock. Every
380
+ * walk that writes a subgraph's schema — live catch-up, reindex, backfill —
381
+ * must go through this, or it serializes against nothing.
382
+ */
383
+ declare function withSubgraphBlockWriteLock<T>(name: string, fn: () => Promise<T>): Promise<T>;
351
384
  /** Bump a subgraph's reorg epoch. Call BEFORE acquiring the block lock so the
352
385
  * bump is visible to any concurrent catch-up immediately. Exported for
353
386
  * reorg.ts. */
@@ -371,4 +404,4 @@ declare function resolveCatchupBatchConfig(env?: CatchupBatchEnv): CatchupBatchC
371
404
  * plan-aware pipeline prefetching.
372
405
  */
373
406
  declare function catchUpSubgraph(subgraph: SubgraphDefinition, subgraphName: string): Promise<number>;
374
- export { withSubgraphBlockLock, resolveCatchupBatchConfig, catchUpSubgraph, bumpReorgEpoch };
407
+ export { withSubgraphBlockWriteLock, withSubgraphBlockLock, withSubgraphBlockAdvisoryLock, resolveCatchupBatchConfig, catchUpSubgraph, bumpReorgEpoch };
@@ -3045,7 +3045,7 @@ class StatsAccumulator {
3045
3045
  }
3046
3046
 
3047
3047
  // src/runtime/catchup.ts
3048
- import { getTargetDb as getTargetDb2 } from "@secondlayer/shared/db";
3048
+ import { getTargetDb as getTargetDb2, sql as sql4 } from "@secondlayer/shared/db";
3049
3049
  import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
3050
3050
  import { logger as logger8 } from "@secondlayer/shared/logger";
3051
3051
 
@@ -3116,6 +3116,15 @@ async function withSubgraphBlockLock(name, fn) {
3116
3116
  release();
3117
3117
  }
3118
3118
  }
3119
+ async function withSubgraphBlockAdvisoryLock(name, fn) {
3120
+ return getTargetDb2().transaction().execute(async (lockTx) => {
3121
+ await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
3122
+ return fn();
3123
+ });
3124
+ }
3125
+ async function withSubgraphBlockWriteLock(name, fn) {
3126
+ return withSubgraphBlockLock(name, () => withSubgraphBlockAdvisoryLock(name, fn));
3127
+ }
3119
3128
  function bumpReorgEpoch(name) {
3120
3129
  reorgEpoch.set(name, (reorgEpoch.get(name) ?? 0) + 1);
3121
3130
  }
@@ -3244,7 +3253,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3244
3253
  const preloaded = blockData;
3245
3254
  let result;
3246
3255
  try {
3247
- result = await withSubgraphBlockLock(subgraphName, async () => {
3256
+ result = await withSubgraphBlockWriteLock(subgraphName, async () => {
3248
3257
  if (getReorgEpoch(subgraphName) !== walkEpoch) {
3249
3258
  throw new ReorgEpochChangedError(subgraphName);
3250
3259
  }
@@ -3304,11 +3313,13 @@ async function catchUpSubgraph(subgraph, subgraphName) {
3304
3313
  }
3305
3314
  }
3306
3315
  export {
3316
+ withSubgraphBlockWriteLock,
3307
3317
  withSubgraphBlockLock,
3318
+ withSubgraphBlockAdvisoryLock,
3308
3319
  resolveCatchupBatchConfig,
3309
3320
  catchUpSubgraph,
3310
3321
  bumpReorgEpoch
3311
3322
  };
3312
3323
 
3313
- //# debugId=1544B1119237E68264756E2164756E21
3324
+ //# debugId=376967D5C07D633664756E2164756E21
3314
3325
  //# sourceMappingURL=catchup.js.map