@secondlayer/subgraphs 3.26.0 → 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.
- package/dist/src/index.js +367 -76
- package/dist/src/index.js.map +7 -5
- package/dist/src/runtime/block-processor.js +29 -9
- 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 +42 -11
- package/dist/src/runtime/catchup.js.map +4 -4
- package/dist/src/runtime/context.d.ts +20 -0
- package/dist/src/runtime/context.js +29 -9
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +344 -313
- package/dist/src/runtime/processor.js.map +7 -7
- package/dist/src/runtime/reindex.js +342 -51
- package/dist/src/runtime/reindex.js.map +7 -5
- package/dist/src/runtime/reorg.js +42 -13
- package/dist/src/runtime/reorg.js.map +4 -4
- package/dist/src/runtime/runner.d.ts +20 -0
- package/dist/src/service.js +346 -315
- package/dist/src/service.js.map +7 -7
- package/dist/src/testing/index.js +29 -9
- package/dist/src/testing/index.js.map +3 -3
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -820,14 +820,10 @@ class SubgraphContext {
|
|
|
820
820
|
this.ops.length = 0;
|
|
821
821
|
const statements = this.buildStatements(opsToFlush);
|
|
822
822
|
if ("isTransaction" in this.db) {
|
|
823
|
-
|
|
824
|
-
await sql.raw(stmt).execute(this.db);
|
|
825
|
-
}
|
|
823
|
+
await this.execStatements(this.db, statements);
|
|
826
824
|
} else {
|
|
827
825
|
await this.db.transaction().execute(async (tx) => {
|
|
828
|
-
|
|
829
|
-
await sql.raw(stmt).execute(tx);
|
|
830
|
-
}
|
|
826
|
+
await this.execStatements(tx, statements);
|
|
831
827
|
});
|
|
832
828
|
}
|
|
833
829
|
const writes = opsToFlush.map((op, rowIndex) => {
|
|
@@ -846,6 +842,25 @@ class SubgraphContext {
|
|
|
846
842
|
});
|
|
847
843
|
return { count: opsToFlush.length, writes };
|
|
848
844
|
}
|
|
845
|
+
async execStatements(executor, statements) {
|
|
846
|
+
const affected = (r) => r.numAffectedRows ?? 0n;
|
|
847
|
+
for (const stmt of statements) {
|
|
848
|
+
if (typeof stmt === "string") {
|
|
849
|
+
await sql.raw(stmt).execute(executor);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
const updated = await sql.raw(stmt.update).execute(executor);
|
|
853
|
+
if (affected(updated) > 0n)
|
|
854
|
+
continue;
|
|
855
|
+
const inserted = await sql.raw(stmt.insert).execute(executor);
|
|
856
|
+
if (affected(inserted) > 0n)
|
|
857
|
+
continue;
|
|
858
|
+
const retried = await sql.raw(stmt.update).execute(executor);
|
|
859
|
+
if (affected(retried) === 0n) {
|
|
860
|
+
throw new Error(`increment could not be applied to ${this.pgSchemaName}.${stmt.table} where ${stmt.keyPred}: row neither updated nor inserted`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
849
864
|
prepareInsert(op) {
|
|
850
865
|
const upsertKeys = op.data._upsert_keys;
|
|
851
866
|
const data = { ...op.data };
|
|
@@ -925,7 +940,6 @@ class SubgraphContext {
|
|
|
925
940
|
for (const r of batch.rows.values()) {
|
|
926
941
|
const keyPred = batch.keyCols.map((k) => `"${k}" = ${escapeLiteral(r.keys[k])}`).join(" AND ");
|
|
927
942
|
const updSet = batch.deltaCols.map((c) => `"${c}" = COALESCE("${c}", 0) + (${String(r.deltas[c] ?? 0n)})`).join(", ");
|
|
928
|
-
statements.push(`UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`);
|
|
929
943
|
const insertVals = [
|
|
930
944
|
...batch.keyCols.map((k) => escapeLiteral(r.keys[k])),
|
|
931
945
|
...batch.deltaCols.map((c) => String(r.deltas[c] ?? 0n)),
|
|
@@ -933,7 +947,13 @@ class SubgraphContext {
|
|
|
933
947
|
escapeLiteral(r.meta.txId),
|
|
934
948
|
"NOW()"
|
|
935
949
|
];
|
|
936
|
-
statements.push(
|
|
950
|
+
statements.push({
|
|
951
|
+
kind: "increment",
|
|
952
|
+
table: batch.table,
|
|
953
|
+
keyPred,
|
|
954
|
+
update: `UPDATE ${qualifiedTable} SET ${updSet} WHERE ${keyPred}`,
|
|
955
|
+
insert: `INSERT INTO ${qualifiedTable} (${cols.map((c) => `"${c}"`).join(", ")}) ` + `SELECT ${insertVals.join(", ")} ` + `WHERE NOT EXISTS (SELECT 1 FROM ${qualifiedTable} WHERE ${keyPred})`
|
|
956
|
+
});
|
|
937
957
|
}
|
|
938
958
|
incBatch = null;
|
|
939
959
|
incBatchKey = "";
|
|
@@ -3239,9 +3259,278 @@ class StatsAccumulator {
|
|
|
3239
3259
|
}
|
|
3240
3260
|
}
|
|
3241
3261
|
|
|
3262
|
+
// src/runtime/catchup.ts
|
|
3263
|
+
import { getTargetDb as getTargetDb2, sql as sql4 } from "@secondlayer/shared/db";
|
|
3264
|
+
import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
|
|
3265
|
+
import { logger as logger8 } from "@secondlayer/shared/logger";
|
|
3266
|
+
|
|
3267
|
+
// src/runtime/catchup-leader.ts
|
|
3268
|
+
import {
|
|
3269
|
+
SUBGRAPH_CATCHUP_LOCK_KEY,
|
|
3270
|
+
createPostgresLeaderBackend,
|
|
3271
|
+
withLeaderLock
|
|
3272
|
+
} from "@secondlayer/shared/leader";
|
|
3273
|
+
import { targetListenerUrl } from "@secondlayer/shared/queue/listener";
|
|
3274
|
+
var catchUpLeader = false;
|
|
3275
|
+
function isCatchUpLeader() {
|
|
3276
|
+
return catchUpLeader;
|
|
3277
|
+
}
|
|
3278
|
+
function startCatchUpLeader(opts = {}) {
|
|
3279
|
+
return withLeaderLock(SUBGRAPH_CATCHUP_LOCK_KEY, async () => {
|
|
3280
|
+
catchUpLeader = true;
|
|
3281
|
+
await opts.onAcquire?.();
|
|
3282
|
+
return () => {
|
|
3283
|
+
catchUpLeader = false;
|
|
3284
|
+
};
|
|
3285
|
+
}, {
|
|
3286
|
+
pollMs: opts.pollMs,
|
|
3287
|
+
heartbeatMs: opts.heartbeatMs,
|
|
3288
|
+
createBackend: opts.createBackend ?? (() => createPostgresLeaderBackend(targetListenerUrl()))
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
// src/runtime/catchup.ts
|
|
3293
|
+
var LOG_INTERVAL = 1000;
|
|
3294
|
+
var STANDARD_CATCHUP_BATCH_CONFIG = {
|
|
3295
|
+
defaultBatchSize: 500,
|
|
3296
|
+
minBatchSize: 100,
|
|
3297
|
+
maxBatchSize: 1000,
|
|
3298
|
+
prefetch: true
|
|
3299
|
+
};
|
|
3300
|
+
var catchingUp = new Set;
|
|
3301
|
+
var subgraphBlockLockHeld = new Set;
|
|
3302
|
+
var subgraphBlockLockWaiters = new Map;
|
|
3303
|
+
var reorgEpoch = new Map;
|
|
3304
|
+
async function acquireSubgraphBlockLock(name) {
|
|
3305
|
+
if (!subgraphBlockLockHeld.has(name)) {
|
|
3306
|
+
subgraphBlockLockHeld.add(name);
|
|
3307
|
+
return () => releaseSubgraphBlockLock(name);
|
|
3308
|
+
}
|
|
3309
|
+
return new Promise((resolve) => {
|
|
3310
|
+
const waiters = subgraphBlockLockWaiters.get(name) ?? [];
|
|
3311
|
+
waiters.push(() => resolve(() => releaseSubgraphBlockLock(name)));
|
|
3312
|
+
subgraphBlockLockWaiters.set(name, waiters);
|
|
3313
|
+
});
|
|
3314
|
+
}
|
|
3315
|
+
function releaseSubgraphBlockLock(name) {
|
|
3316
|
+
const waiters = subgraphBlockLockWaiters.get(name);
|
|
3317
|
+
if (waiters && waiters.length > 0) {
|
|
3318
|
+
const next = waiters.shift();
|
|
3319
|
+
if (waiters.length === 0)
|
|
3320
|
+
subgraphBlockLockWaiters.delete(name);
|
|
3321
|
+
next();
|
|
3322
|
+
return;
|
|
3323
|
+
}
|
|
3324
|
+
subgraphBlockLockHeld.delete(name);
|
|
3325
|
+
}
|
|
3326
|
+
async function withSubgraphBlockLock(name, fn) {
|
|
3327
|
+
const release = await acquireSubgraphBlockLock(name);
|
|
3328
|
+
try {
|
|
3329
|
+
return await fn();
|
|
3330
|
+
} finally {
|
|
3331
|
+
release();
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
async function withSubgraphBlockAdvisoryLock(name, fn) {
|
|
3335
|
+
return getTargetDb2().transaction().execute(async (lockTx) => {
|
|
3336
|
+
await sql4`SELECT pg_advisory_xact_lock(hashtext(${`subgraph-block:${name}`}))`.execute(lockTx);
|
|
3337
|
+
return fn();
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
async function withSubgraphBlockWriteLock(name, fn) {
|
|
3341
|
+
return withSubgraphBlockLock(name, () => withSubgraphBlockAdvisoryLock(name, fn));
|
|
3342
|
+
}
|
|
3343
|
+
function bumpReorgEpoch(name) {
|
|
3344
|
+
reorgEpoch.set(name, (reorgEpoch.get(name) ?? 0) + 1);
|
|
3345
|
+
}
|
|
3346
|
+
function getReorgEpoch(name) {
|
|
3347
|
+
return reorgEpoch.get(name) ?? 0;
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
class ReorgEpochChangedError extends Error {
|
|
3351
|
+
constructor(name) {
|
|
3352
|
+
super(`reorg epoch changed for ${name} mid catch-up`);
|
|
3353
|
+
this.name = "ReorgEpochChangedError";
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function parsePositiveInt(value) {
|
|
3357
|
+
if (value == null || value.trim() === "")
|
|
3358
|
+
return;
|
|
3359
|
+
const parsed = Number.parseInt(value, 10);
|
|
3360
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
3361
|
+
}
|
|
3362
|
+
function parseBoolean(value) {
|
|
3363
|
+
if (value == null || value.trim() === "")
|
|
3364
|
+
return;
|
|
3365
|
+
const normalized = value.trim().toLowerCase();
|
|
3366
|
+
if (normalized === "true")
|
|
3367
|
+
return true;
|
|
3368
|
+
if (normalized === "false")
|
|
3369
|
+
return false;
|
|
3370
|
+
return;
|
|
3371
|
+
}
|
|
3372
|
+
function resolveCatchupBatchConfig(env = process.env) {
|
|
3373
|
+
const base = STANDARD_CATCHUP_BATCH_CONFIG;
|
|
3374
|
+
const minBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_MIN_BATCH_SIZE) ?? base.minBatchSize;
|
|
3375
|
+
const maxBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_MAX_BATCH_SIZE) ?? base.maxBatchSize;
|
|
3376
|
+
const defaultBatchSize = parsePositiveInt(env.SUBGRAPH_CATCHUP_BATCH_SIZE) ?? base.defaultBatchSize;
|
|
3377
|
+
return {
|
|
3378
|
+
minBatchSize,
|
|
3379
|
+
maxBatchSize,
|
|
3380
|
+
defaultBatchSize: Math.min(Math.max(defaultBatchSize, minBatchSize), maxBatchSize),
|
|
3381
|
+
prefetch: parseBoolean(env.SUBGRAPH_CATCHUP_PREFETCH) ?? base.prefetch
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
function adjustBatchSize(current, avgEvents, config) {
|
|
3385
|
+
if (avgEvents > 50)
|
|
3386
|
+
return Math.max(Math.round(current * 0.5), config.minBatchSize);
|
|
3387
|
+
if (avgEvents < 10)
|
|
3388
|
+
return Math.min(Math.round(current * 1.5), config.maxBatchSize);
|
|
3389
|
+
return current;
|
|
3390
|
+
}
|
|
3391
|
+
async function catchUpSubgraph(subgraph, subgraphName) {
|
|
3392
|
+
if (catchingUp.has(subgraphName))
|
|
3393
|
+
return 0;
|
|
3394
|
+
catchingUp.add(subgraphName);
|
|
3395
|
+
try {
|
|
3396
|
+
const source = resolveBlockSource(subgraph);
|
|
3397
|
+
const targetDb = getTargetDb2();
|
|
3398
|
+
const subgraphRow = await getSubgraph(targetDb, subgraphName);
|
|
3399
|
+
if (!subgraphRow)
|
|
3400
|
+
return 0;
|
|
3401
|
+
const lastProcessedBlock = Number(subgraphRow.last_processed_block);
|
|
3402
|
+
const chainTip = await source.getTip();
|
|
3403
|
+
if (chainTip <= 0 || lastProcessedBlock >= chainTip)
|
|
3404
|
+
return 0;
|
|
3405
|
+
const subgraphStart = Number(subgraphRow.start_block) || 1;
|
|
3406
|
+
const startBlock = Math.max(lastProcessedBlock + 1, subgraphStart);
|
|
3407
|
+
const totalBlocks = chainTip - lastProcessedBlock;
|
|
3408
|
+
logger8.info("Subgraph catch-up starting", {
|
|
3409
|
+
subgraph: subgraphName,
|
|
3410
|
+
from: startBlock,
|
|
3411
|
+
to: chainTip,
|
|
3412
|
+
blocks: totalBlocks
|
|
3413
|
+
});
|
|
3414
|
+
const stats = new StatsAccumulator(subgraphName, true);
|
|
3415
|
+
let processed = 0;
|
|
3416
|
+
const batchConfig = resolveCatchupBatchConfig();
|
|
3417
|
+
let batchSize = batchConfig.defaultBatchSize;
|
|
3418
|
+
let currentHeight = startBlock;
|
|
3419
|
+
const walkEpoch = getReorgEpoch(subgraphName);
|
|
3420
|
+
let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
3421
|
+
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
3422
|
+
while (currentHeight <= chainTip) {
|
|
3423
|
+
if (!isCatchUpLeader()) {
|
|
3424
|
+
logger8.info("Lost catch-up leadership, stopping catch-up", {
|
|
3425
|
+
subgraph: subgraphName,
|
|
3426
|
+
processed
|
|
3427
|
+
});
|
|
3428
|
+
break;
|
|
3429
|
+
}
|
|
3430
|
+
const currentRow = await getSubgraph(targetDb, subgraphName);
|
|
3431
|
+
if (!currentRow || currentRow.status !== "active") {
|
|
3432
|
+
logger8.info("Subgraph status changed, stopping catch-up", {
|
|
3433
|
+
subgraph: subgraphName,
|
|
3434
|
+
status: currentRow?.status ?? "deleted"
|
|
3435
|
+
});
|
|
3436
|
+
break;
|
|
3437
|
+
}
|
|
3438
|
+
let batchEnd;
|
|
3439
|
+
let batch;
|
|
3440
|
+
if (nextBatchPromise) {
|
|
3441
|
+
batch = await nextBatchPromise;
|
|
3442
|
+
batchEnd = prefetchedBatchEnd;
|
|
3443
|
+
const nextStart = batchEnd + 1;
|
|
3444
|
+
if (nextStart <= chainTip) {
|
|
3445
|
+
prefetchedBatchEnd = Math.min(nextStart + batchSize - 1, chainTip);
|
|
3446
|
+
nextBatchPromise = source.loadBlockRange(nextStart, prefetchedBatchEnd);
|
|
3447
|
+
} else {
|
|
3448
|
+
nextBatchPromise = undefined;
|
|
3449
|
+
}
|
|
3450
|
+
} else {
|
|
3451
|
+
batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
3452
|
+
batch = await source.loadBlockRange(currentHeight, batchEnd);
|
|
3453
|
+
}
|
|
3454
|
+
let stopCatchup = false;
|
|
3455
|
+
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
3456
|
+
let blockData = batch.get(height);
|
|
3457
|
+
if (!blockData) {
|
|
3458
|
+
blockData = (await source.loadBlockRange(height, height)).get(height);
|
|
3459
|
+
}
|
|
3460
|
+
if (!blockData) {
|
|
3461
|
+
logger8.warn("Block missing during catch-up, deferring to next tick", {
|
|
3462
|
+
subgraph: subgraphName,
|
|
3463
|
+
blockHeight: height
|
|
3464
|
+
});
|
|
3465
|
+
stopCatchup = true;
|
|
3466
|
+
break;
|
|
3467
|
+
}
|
|
3468
|
+
const preloaded = blockData;
|
|
3469
|
+
let result;
|
|
3470
|
+
try {
|
|
3471
|
+
result = await withSubgraphBlockWriteLock(subgraphName, async () => {
|
|
3472
|
+
if (getReorgEpoch(subgraphName) !== walkEpoch) {
|
|
3473
|
+
throw new ReorgEpochChangedError(subgraphName);
|
|
3474
|
+
}
|
|
3475
|
+
return processBlockWithRetry(subgraph, subgraphName, height, {
|
|
3476
|
+
preloaded
|
|
3477
|
+
});
|
|
3478
|
+
});
|
|
3479
|
+
} catch (err) {
|
|
3480
|
+
if (err instanceof ReorgEpochChangedError) {
|
|
3481
|
+
logger8.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
|
|
3482
|
+
stopCatchup = true;
|
|
3483
|
+
break;
|
|
3484
|
+
}
|
|
3485
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
3486
|
+
logger8.error("Block processing failed persistently during catch-up", {
|
|
3487
|
+
subgraph: subgraphName,
|
|
3488
|
+
blockHeight: height,
|
|
3489
|
+
error: errorMsg
|
|
3490
|
+
});
|
|
3491
|
+
const { updateSubgraphStatus: updateSubgraphStatus2, recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
3492
|
+
await recordSubgraphProcessed2(targetDb, subgraphName, 0, 1, `catch-up halted at block ${height}: ${errorMsg}`).catch(() => {});
|
|
3493
|
+
await updateSubgraphStatus2(targetDb, subgraphName, "error").catch(() => {});
|
|
3494
|
+
stopCatchup = true;
|
|
3495
|
+
break;
|
|
3496
|
+
}
|
|
3497
|
+
processed++;
|
|
3498
|
+
if (result.timing) {
|
|
3499
|
+
stats.record(result.timing, result.processed);
|
|
3500
|
+
if (stats.shouldFlush()) {
|
|
3501
|
+
await stats.flush(targetDb);
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
if (processed % LOG_INTERVAL === 0) {
|
|
3505
|
+
logger8.info("Subgraph catch-up progress", {
|
|
3506
|
+
subgraph: subgraphName,
|
|
3507
|
+
processed,
|
|
3508
|
+
total: totalBlocks,
|
|
3509
|
+
currentBlock: height,
|
|
3510
|
+
pct: Math.round(processed / totalBlocks * 100)
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
if (stopCatchup)
|
|
3515
|
+
break;
|
|
3516
|
+
const avg = avgEventsPerBlock(batch);
|
|
3517
|
+
batchSize = adjustBatchSize(batchSize, avg, batchConfig);
|
|
3518
|
+
currentHeight = batchEnd + 1;
|
|
3519
|
+
}
|
|
3520
|
+
await stats.flush(targetDb);
|
|
3521
|
+
logger8.info("Subgraph catch-up complete", {
|
|
3522
|
+
subgraph: subgraphName,
|
|
3523
|
+
processed
|
|
3524
|
+
});
|
|
3525
|
+
return processed;
|
|
3526
|
+
} finally {
|
|
3527
|
+
catchingUp.delete(subgraphName);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3242
3531
|
// src/runtime/reindex.ts
|
|
3243
3532
|
import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
|
|
3244
|
-
import { getRawClient, getTargetDb as
|
|
3533
|
+
import { getRawClient, getTargetDb as getTargetDb3 } from "@secondlayer/shared/db";
|
|
3245
3534
|
import {
|
|
3246
3535
|
recordGapBatch,
|
|
3247
3536
|
resolveGaps
|
|
@@ -3254,11 +3543,11 @@ import {
|
|
|
3254
3543
|
recordSubgraphProcessed as recordSubgraphProcessed2,
|
|
3255
3544
|
updateSubgraphStatus as updateSubgraphStatus2
|
|
3256
3545
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
3257
|
-
import { logger as
|
|
3546
|
+
import { logger as logger10 } from "@secondlayer/shared/logger";
|
|
3258
3547
|
|
|
3259
3548
|
// src/runtime/reindex-notify.ts
|
|
3260
3549
|
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
3261
|
-
import { logger as
|
|
3550
|
+
import { logger as logger9 } from "@secondlayer/shared/logger";
|
|
3262
3551
|
async function notifyReindexComplete(db, subgraphName, stats) {
|
|
3263
3552
|
try {
|
|
3264
3553
|
const subgraph = await db.selectFrom("subgraphs").select(["account_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
@@ -3269,7 +3558,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3269
3558
|
return;
|
|
3270
3559
|
const resendKey = process.env.RESEND_API_KEY;
|
|
3271
3560
|
if (!resendKey) {
|
|
3272
|
-
|
|
3561
|
+
logger9.warn("RESEND_API_KEY unset — skipping reindex-complete email", {
|
|
3273
3562
|
subgraph: subgraphName
|
|
3274
3563
|
});
|
|
3275
3564
|
return;
|
|
@@ -3291,14 +3580,14 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3291
3580
|
});
|
|
3292
3581
|
if (!res.ok) {
|
|
3293
3582
|
const text = await res.text().catch(() => "");
|
|
3294
|
-
|
|
3583
|
+
logger9.warn("reindex-complete email failed", {
|
|
3295
3584
|
subgraph: subgraphName,
|
|
3296
3585
|
status: res.status,
|
|
3297
3586
|
body: text.slice(0, 200)
|
|
3298
3587
|
});
|
|
3299
3588
|
}
|
|
3300
3589
|
} catch (err) {
|
|
3301
|
-
|
|
3590
|
+
logger9.warn("reindex-complete email threw", {
|
|
3302
3591
|
subgraph: subgraphName,
|
|
3303
3592
|
error: getErrorMessage2(err)
|
|
3304
3593
|
});
|
|
@@ -3306,7 +3595,7 @@ async function notifyReindexComplete(db, subgraphName, stats) {
|
|
|
3306
3595
|
}
|
|
3307
3596
|
|
|
3308
3597
|
// src/runtime/reindex.ts
|
|
3309
|
-
var
|
|
3598
|
+
var LOG_INTERVAL2 = 1000;
|
|
3310
3599
|
var HEALTH_FLUSH_INTERVAL = 1000;
|
|
3311
3600
|
var PROGRESS_FLUSH_INTERVAL_MS = 5000;
|
|
3312
3601
|
var EMPTY_BATCH_HALT_THRESHOLD = 3;
|
|
@@ -3326,7 +3615,7 @@ function resolveReindexResumeBlock(row) {
|
|
|
3326
3615
|
const reindexFromBlock = Number(row.reindex_from_block);
|
|
3327
3616
|
return Math.max(lastProcessedBlock + 1, reindexFromBlock);
|
|
3328
3617
|
}
|
|
3329
|
-
function
|
|
3618
|
+
function parsePositiveInt2(value) {
|
|
3330
3619
|
if (value == null || value.trim() === "")
|
|
3331
3620
|
return;
|
|
3332
3621
|
const parsed = Number.parseInt(value, 10);
|
|
@@ -3334,9 +3623,9 @@ function parsePositiveInt(value) {
|
|
|
3334
3623
|
}
|
|
3335
3624
|
function resolveReindexBatchConfig(env = process.env) {
|
|
3336
3625
|
const base = STANDARD_REINDEX_BATCH_CONFIG;
|
|
3337
|
-
const minBatchSize =
|
|
3338
|
-
const maxBatchSize =
|
|
3339
|
-
const defaultBatchSize =
|
|
3626
|
+
const minBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_MIN_BATCH_SIZE) ?? base.minBatchSize;
|
|
3627
|
+
const maxBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_MAX_BATCH_SIZE) ?? base.maxBatchSize;
|
|
3628
|
+
const defaultBatchSize = parsePositiveInt2(env.SUBGRAPH_REINDEX_BATCH_SIZE) ?? base.defaultBatchSize;
|
|
3340
3629
|
return {
|
|
3341
3630
|
minBatchSize,
|
|
3342
3631
|
maxBatchSize,
|
|
@@ -3367,7 +3656,7 @@ function coalesceFailedBlocks(blocks) {
|
|
|
3367
3656
|
}
|
|
3368
3657
|
async function processBlockRange(def, opts) {
|
|
3369
3658
|
const source = resolveBlockSource(def);
|
|
3370
|
-
const targetDb =
|
|
3659
|
+
const targetDb = getTargetDb3();
|
|
3371
3660
|
const subgraphName = def.name;
|
|
3372
3661
|
const { fromBlock, toBlock, status } = opts;
|
|
3373
3662
|
const totalBlocks = toBlock - fromBlock + 1;
|
|
@@ -3410,7 +3699,7 @@ async function processBlockRange(def, opts) {
|
|
|
3410
3699
|
while (currentHeight <= toBlock) {
|
|
3411
3700
|
if (opts.signal?.aborted) {
|
|
3412
3701
|
aborted = true;
|
|
3413
|
-
|
|
3702
|
+
logger10.info("Block processing aborted", {
|
|
3414
3703
|
subgraph: subgraphName,
|
|
3415
3704
|
currentBlock: currentHeight,
|
|
3416
3705
|
reason: String(opts.signal.reason ?? "unknown")
|
|
@@ -3452,14 +3741,16 @@ async function processBlockRange(def, opts) {
|
|
|
3452
3741
|
}
|
|
3453
3742
|
let result;
|
|
3454
3743
|
try {
|
|
3455
|
-
|
|
3744
|
+
const mayWrite = blockData.txs.length > 0 || blockData.events.length > 0;
|
|
3745
|
+
const runBlock = () => processBlockWithRetry(def, subgraphName, height, {
|
|
3456
3746
|
skipProgressUpdate: true,
|
|
3457
3747
|
atomicProgress,
|
|
3458
3748
|
preloaded: blockData
|
|
3459
3749
|
});
|
|
3750
|
+
result = mayWrite ? await withSubgraphBlockWriteLock(subgraphName, runBlock) : await runBlock();
|
|
3460
3751
|
} catch (err) {
|
|
3461
3752
|
const errorMsg = getErrorMessage3(err);
|
|
3462
|
-
|
|
3753
|
+
logger10.error("Block processing failed persistently", {
|
|
3463
3754
|
subgraph: subgraphName,
|
|
3464
3755
|
blockHeight: height,
|
|
3465
3756
|
error: errorMsg
|
|
@@ -3504,8 +3795,8 @@ async function processBlockRange(def, opts) {
|
|
|
3504
3795
|
}
|
|
3505
3796
|
lastProgressFlushAt = now;
|
|
3506
3797
|
}
|
|
3507
|
-
if (blocksProcessed %
|
|
3508
|
-
|
|
3798
|
+
if (blocksProcessed % LOG_INTERVAL2 === 0) {
|
|
3799
|
+
logger10.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
|
|
3509
3800
|
subgraph: subgraphName,
|
|
3510
3801
|
processed: blocksProcessed,
|
|
3511
3802
|
total: totalBlocks,
|
|
@@ -3525,7 +3816,7 @@ async function processBlockRange(def, opts) {
|
|
|
3525
3816
|
if (batchFailedBlocks.length > 0 && opts.subgraphId) {
|
|
3526
3817
|
const gaps = coalesceFailedBlocks(batchFailedBlocks);
|
|
3527
3818
|
await recordGapBatch(targetDb, opts.subgraphId, subgraphName, gaps).catch((err) => {
|
|
3528
|
-
|
|
3819
|
+
logger10.warn("Failed to record subgraph gaps", {
|
|
3529
3820
|
subgraph: subgraphName,
|
|
3530
3821
|
error: err instanceof Error ? err.message : String(err)
|
|
3531
3822
|
});
|
|
@@ -3542,7 +3833,7 @@ async function processBlockRange(def, opts) {
|
|
|
3542
3833
|
} else {
|
|
3543
3834
|
await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
|
|
3544
3835
|
}
|
|
3545
|
-
|
|
3836
|
+
logger10.info("Sparse skip", {
|
|
3546
3837
|
subgraph: subgraphName,
|
|
3547
3838
|
from: batchEnd + 1,
|
|
3548
3839
|
to: jumpTo - 1,
|
|
@@ -3577,16 +3868,16 @@ async function clearReindexMetadata(db, subgraphName) {
|
|
|
3577
3868
|
}
|
|
3578
3869
|
async function reindexSubgraph(def, opts) {
|
|
3579
3870
|
const source = resolveBlockSource(def);
|
|
3580
|
-
const targetDb =
|
|
3871
|
+
const targetDb = getTargetDb3();
|
|
3581
3872
|
const client = getRawClient("target");
|
|
3582
3873
|
const subgraphName = def.name;
|
|
3583
3874
|
const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
|
|
3584
3875
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing");
|
|
3585
|
-
|
|
3876
|
+
logger10.info("Reindex starting", { subgraph: subgraphName });
|
|
3586
3877
|
try {
|
|
3587
3878
|
const { fromBlock, toBlock } = await resolveBlockRange(source, def, opts);
|
|
3588
3879
|
if (fromBlock > toBlock) {
|
|
3589
|
-
|
|
3880
|
+
logger10.info("No blocks to reindex", {
|
|
3590
3881
|
subgraph: subgraphName,
|
|
3591
3882
|
fromBlock,
|
|
3592
3883
|
toBlock
|
|
@@ -3600,7 +3891,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3600
3891
|
await client.unsafe(stmt);
|
|
3601
3892
|
}
|
|
3602
3893
|
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
|
|
3603
|
-
|
|
3894
|
+
logger10.info("Schema recreated for reindex", {
|
|
3604
3895
|
subgraph: subgraphName,
|
|
3605
3896
|
cursorResetTo: Math.max(0, fromBlock - 1)
|
|
3606
3897
|
});
|
|
@@ -3614,7 +3905,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3614
3905
|
last_error_at: null,
|
|
3615
3906
|
updated_at: new Date
|
|
3616
3907
|
}).where("name", "=", subgraphName).execute();
|
|
3617
|
-
|
|
3908
|
+
logger10.info("Reindexing blocks", {
|
|
3618
3909
|
subgraph: subgraphName,
|
|
3619
3910
|
fromBlock,
|
|
3620
3911
|
toBlock,
|
|
@@ -3636,9 +3927,9 @@ async function reindexSubgraph(def, opts) {
|
|
|
3636
3927
|
if (reason === "user-cancelled") {
|
|
3637
3928
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3638
3929
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3639
|
-
|
|
3930
|
+
logger10.info("Reindex cancelled by user", { subgraph: subgraphName });
|
|
3640
3931
|
} else {
|
|
3641
|
-
|
|
3932
|
+
logger10.info("Reindex interrupted by shutdown, will resume", {
|
|
3642
3933
|
subgraph: subgraphName
|
|
3643
3934
|
});
|
|
3644
3935
|
}
|
|
@@ -3646,7 +3937,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3646
3937
|
}
|
|
3647
3938
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3648
3939
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3649
|
-
|
|
3940
|
+
logger10.info("Reindex complete", {
|
|
3650
3941
|
subgraph: subgraphName,
|
|
3651
3942
|
blocks: result.blocksProcessed,
|
|
3652
3943
|
events: result.totalEventsProcessed,
|
|
@@ -3659,7 +3950,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3659
3950
|
});
|
|
3660
3951
|
return { processed: result.blocksProcessed };
|
|
3661
3952
|
} catch (err) {
|
|
3662
|
-
|
|
3953
|
+
logger10.error("Reindex failed", {
|
|
3663
3954
|
subgraph: subgraphName,
|
|
3664
3955
|
error: getErrorMessage3(err)
|
|
3665
3956
|
});
|
|
@@ -3668,7 +3959,7 @@ async function reindexSubgraph(def, opts) {
|
|
|
3668
3959
|
}
|
|
3669
3960
|
}
|
|
3670
3961
|
async function resumeReindex(def, opts) {
|
|
3671
|
-
const targetDb =
|
|
3962
|
+
const targetDb = getTargetDb3();
|
|
3672
3963
|
const subgraphName = def.name;
|
|
3673
3964
|
const row = await targetDb.selectFrom("subgraphs").select([
|
|
3674
3965
|
"id",
|
|
@@ -3681,7 +3972,7 @@ async function resumeReindex(def, opts) {
|
|
|
3681
3972
|
const fromBlock = resolveReindexResumeBlock(row);
|
|
3682
3973
|
const toBlock = Number(row.reindex_to_block);
|
|
3683
3974
|
if (fromBlock == null) {
|
|
3684
|
-
|
|
3975
|
+
logger10.info("No reindex metadata, starting fresh reindex", {
|
|
3685
3976
|
subgraph: subgraphName
|
|
3686
3977
|
});
|
|
3687
3978
|
return reindexSubgraph(def, {
|
|
@@ -3690,12 +3981,12 @@ async function resumeReindex(def, opts) {
|
|
|
3690
3981
|
});
|
|
3691
3982
|
}
|
|
3692
3983
|
if (fromBlock > toBlock) {
|
|
3693
|
-
|
|
3984
|
+
logger10.info("Resume: no remaining blocks", { subgraph: subgraphName });
|
|
3694
3985
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3695
3986
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3696
3987
|
return { processed: 0 };
|
|
3697
3988
|
}
|
|
3698
|
-
|
|
3989
|
+
logger10.info("Resuming reindex", {
|
|
3699
3990
|
subgraph: subgraphName,
|
|
3700
3991
|
fromBlock,
|
|
3701
3992
|
toBlock,
|
|
@@ -3717,9 +4008,9 @@ async function resumeReindex(def, opts) {
|
|
|
3717
4008
|
if (reason === "user-cancelled") {
|
|
3718
4009
|
await updateSubgraphStatus2(targetDb, subgraphName, "active");
|
|
3719
4010
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3720
|
-
|
|
4011
|
+
logger10.info("Resume cancelled by user", { subgraph: subgraphName });
|
|
3721
4012
|
} else {
|
|
3722
|
-
|
|
4013
|
+
logger10.info("Resume interrupted by shutdown, will resume again", {
|
|
3723
4014
|
subgraph: subgraphName
|
|
3724
4015
|
});
|
|
3725
4016
|
}
|
|
@@ -3727,13 +4018,13 @@ async function resumeReindex(def, opts) {
|
|
|
3727
4018
|
}
|
|
3728
4019
|
await updateSubgraphStatus2(targetDb, subgraphName, "active", toBlock);
|
|
3729
4020
|
await clearReindexMetadata(targetDb, subgraphName);
|
|
3730
|
-
|
|
4021
|
+
logger10.info("Resumed reindex complete", {
|
|
3731
4022
|
subgraph: subgraphName,
|
|
3732
4023
|
blocks: result.blocksProcessed
|
|
3733
4024
|
});
|
|
3734
4025
|
return { processed: result.blocksProcessed };
|
|
3735
4026
|
} catch (err) {
|
|
3736
|
-
|
|
4027
|
+
logger10.error("Resumed reindex failed", {
|
|
3737
4028
|
subgraph: subgraphName,
|
|
3738
4029
|
error: getErrorMessage3(err)
|
|
3739
4030
|
});
|
|
@@ -3742,9 +4033,9 @@ async function resumeReindex(def, opts) {
|
|
|
3742
4033
|
}
|
|
3743
4034
|
}
|
|
3744
4035
|
async function backfillSubgraph(def, opts) {
|
|
3745
|
-
const targetDb =
|
|
4036
|
+
const targetDb = getTargetDb3();
|
|
3746
4037
|
const subgraphName = def.name;
|
|
3747
|
-
|
|
4038
|
+
logger10.info("Backfill starting", {
|
|
3748
4039
|
subgraph: subgraphName,
|
|
3749
4040
|
from: opts.fromBlock,
|
|
3750
4041
|
to: opts.toBlock
|
|
@@ -3762,17 +4053,17 @@ async function backfillSubgraph(def, opts) {
|
|
|
3762
4053
|
signal: opts.signal
|
|
3763
4054
|
});
|
|
3764
4055
|
if (result.aborted) {
|
|
3765
|
-
|
|
4056
|
+
logger10.info("Backfill aborted", { subgraph: subgraphName });
|
|
3766
4057
|
return { processed: result.blocksProcessed };
|
|
3767
4058
|
}
|
|
3768
4059
|
const resolved = await resolveGaps(targetDb, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
|
|
3769
4060
|
if (resolved > 0) {
|
|
3770
|
-
|
|
4061
|
+
logger10.info("Resolved subgraph gaps via backfill", {
|
|
3771
4062
|
subgraph: subgraphName,
|
|
3772
4063
|
resolved
|
|
3773
4064
|
});
|
|
3774
4065
|
}
|
|
3775
|
-
|
|
4066
|
+
logger10.info("Backfill complete", {
|
|
3776
4067
|
subgraph: subgraphName,
|
|
3777
4068
|
blocks: result.blocksProcessed,
|
|
3778
4069
|
events: result.totalEventsProcessed,
|
|
@@ -3780,7 +4071,7 @@ async function backfillSubgraph(def, opts) {
|
|
|
3780
4071
|
});
|
|
3781
4072
|
return { processed: result.blocksProcessed };
|
|
3782
4073
|
} catch (err) {
|
|
3783
|
-
|
|
4074
|
+
logger10.error("Backfill failed", {
|
|
3784
4075
|
subgraph: subgraphName,
|
|
3785
4076
|
error: getErrorMessage3(err)
|
|
3786
4077
|
});
|
|
@@ -4701,7 +4992,7 @@ function generateIndexSchema(target, opts = {}) {
|
|
|
4701
4992
|
}
|
|
4702
4993
|
var INDEX_CODEGEN_TABLES = Object.keys(SOURCE_READ_TYPES);
|
|
4703
4994
|
// src/schema/deployer.ts
|
|
4704
|
-
import { sql as
|
|
4995
|
+
import { sql as sql5 } from "kysely";
|
|
4705
4996
|
function toJsonSafe(obj) {
|
|
4706
4997
|
return JSON.parse(JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value));
|
|
4707
4998
|
}
|
|
@@ -4816,7 +5107,7 @@ function renderDeployPlan(def, schemaName) {
|
|
|
4816
5107
|
async function deploySchema(db, def, handlerPath, opts) {
|
|
4817
5108
|
validateSubgraphDefinition(def);
|
|
4818
5109
|
const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
|
|
4819
|
-
const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
5110
|
+
const { getSubgraph: getSubgraph2, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
4820
5111
|
const ddlDb = opts?.dataDb ?? db;
|
|
4821
5112
|
const byo = opts?.dataDb != null;
|
|
4822
5113
|
const refuseDestructiveOnByo = (reasons, diff) => {
|
|
@@ -4828,7 +5119,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4828
5119
|
grantScript: plan.grantScript
|
|
4829
5120
|
});
|
|
4830
5121
|
};
|
|
4831
|
-
const existing = await
|
|
5122
|
+
const existing = await getSubgraph2(db, def.name, opts?.accountId);
|
|
4832
5123
|
const schemaName = opts?.schemaName ?? pgSchemaName(def.name);
|
|
4833
5124
|
const newVersion = opts?.version ?? (existing ? bumpPatch(existing.version) : "1.0.0");
|
|
4834
5125
|
const regData = {
|
|
@@ -4853,7 +5144,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4853
5144
|
databaseUrlEnc: opts?.databaseUrlEnc ?? null
|
|
4854
5145
|
};
|
|
4855
5146
|
if (existing) {
|
|
4856
|
-
const schemaExists = await
|
|
5147
|
+
const schemaExists = await sql5`
|
|
4857
5148
|
SELECT EXISTS (
|
|
4858
5149
|
SELECT 1 FROM information_schema.schemata
|
|
4859
5150
|
WHERE schema_name = ${schemaName}
|
|
@@ -4861,7 +5152,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4861
5152
|
`.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
|
|
4862
5153
|
if (!schemaExists) {
|
|
4863
5154
|
for (const stmt of statements) {
|
|
4864
|
-
await
|
|
5155
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
4865
5156
|
}
|
|
4866
5157
|
const sg2 = await registerSubgraph(db, regData);
|
|
4867
5158
|
return { action: "reindexed", subgraphId: sg2.id, version: newVersion };
|
|
@@ -4887,9 +5178,9 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4887
5178
|
if (existing.schema_hash === hash && opts?.forceReindex) {
|
|
4888
5179
|
if (byo)
|
|
4889
5180
|
refuseDestructiveOnByo(["force reindex"], null);
|
|
4890
|
-
await
|
|
5181
|
+
await sql5.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
|
|
4891
5182
|
for (const stmt of statements) {
|
|
4892
|
-
await
|
|
5183
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
4893
5184
|
}
|
|
4894
5185
|
const sg2 = await registerSubgraph(db, regData);
|
|
4895
5186
|
return { action: "reindexed", subgraphId: sg2.id, version: newVersion };
|
|
@@ -4901,9 +5192,9 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4901
5192
|
if (byo) {
|
|
4902
5193
|
refuseDestructiveOnByo(reasons.length > 0 ? reasons : ["force reindex"], diff);
|
|
4903
5194
|
}
|
|
4904
|
-
await
|
|
5195
|
+
await sql5.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
|
|
4905
5196
|
for (const stmt of statements) {
|
|
4906
|
-
await
|
|
5197
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
4907
5198
|
}
|
|
4908
5199
|
const sg3 = await registerSubgraph(db, regData);
|
|
4909
5200
|
const deployDiff2 = toDeployDiff(diff, reasons);
|
|
@@ -4916,16 +5207,16 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4916
5207
|
}
|
|
4917
5208
|
const addedDefs = diff.addedTables.map((tableName) => ({ tableName, tableDef: def.schema[tableName] })).filter((t) => t.tableDef !== undefined);
|
|
4918
5209
|
if (addedDefs.some(({ tableDef }) => tableNeedsTrgm(tableDef))) {
|
|
4919
|
-
await
|
|
5210
|
+
await sql5.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
|
|
4920
5211
|
}
|
|
4921
5212
|
for (const { tableName, tableDef } of addedDefs) {
|
|
4922
5213
|
for (const stmt of emitTableDDL(schemaName, tableName, tableDef)) {
|
|
4923
|
-
await
|
|
5214
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
4924
5215
|
}
|
|
4925
5216
|
}
|
|
4926
5217
|
for (const { tableName, tableDef } of addedDefs) {
|
|
4927
5218
|
for (const stmt of emitForeignKeyDDL(schemaName, tableName, tableDef)) {
|
|
4928
|
-
await
|
|
5219
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
4929
5220
|
}
|
|
4930
5221
|
}
|
|
4931
5222
|
for (const [tableName, colDiff] of Object.entries(diff.tables)) {
|
|
@@ -4943,12 +5234,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4943
5234
|
if (!sqlType)
|
|
4944
5235
|
continue;
|
|
4945
5236
|
const nullable = col.nullable ? "" : ` NOT NULL DEFAULT ${getDefault(col.type)}`;
|
|
4946
|
-
await
|
|
5237
|
+
await sql5.raw(`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`).execute(ddlDb);
|
|
4947
5238
|
if (col.indexed) {
|
|
4948
|
-
await
|
|
5239
|
+
await sql5.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
4949
5240
|
}
|
|
4950
5241
|
if (col.search) {
|
|
4951
|
-
await
|
|
5242
|
+
await sql5.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
4952
5243
|
}
|
|
4953
5244
|
}
|
|
4954
5245
|
}
|
|
@@ -4956,7 +5247,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4956
5247
|
const flagFlips = {};
|
|
4957
5248
|
const needsTrgmForFlips = Object.entries(diff.tables).some(([tableName, colDiff]) => colDiff.indexChanged.some((colName) => def.schema[tableName]?.columns[colName]?.search && !existingSchema[tableName]?.columns[colName]?.search));
|
|
4958
5249
|
if (needsTrgmForFlips) {
|
|
4959
|
-
await
|
|
5250
|
+
await sql5.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
|
|
4960
5251
|
}
|
|
4961
5252
|
for (const [tableName, colDiff] of Object.entries(diff.tables)) {
|
|
4962
5253
|
if (colDiff.indexChanged.length === 0)
|
|
@@ -4971,14 +5262,14 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4971
5262
|
const plainIdx = `idx_${schemaName}_${tableName}_${colName}`;
|
|
4972
5263
|
const trgmIdx = `idx_${schemaName}_${tableName}_${colName}_trgm`;
|
|
4973
5264
|
if (now.indexed && !was?.indexed) {
|
|
4974
|
-
await
|
|
5265
|
+
await sql5.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${plainIdx} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
4975
5266
|
} else if (!now.indexed && was?.indexed) {
|
|
4976
|
-
await
|
|
5267
|
+
await sql5.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${plainIdx}`).execute(ddlDb);
|
|
4977
5268
|
}
|
|
4978
5269
|
if (now.search && !was?.search) {
|
|
4979
|
-
await
|
|
5270
|
+
await sql5.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${trgmIdx} ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
4980
5271
|
} else if (!now.search && was?.search) {
|
|
4981
|
-
await
|
|
5272
|
+
await sql5.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${trgmIdx}`).execute(ddlDb);
|
|
4982
5273
|
}
|
|
4983
5274
|
}
|
|
4984
5275
|
}
|
|
@@ -4987,28 +5278,28 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
4987
5278
|
const qualifiedName = `${schemaName}.${tableName}`;
|
|
4988
5279
|
for (const cols of conDiff.addedUniqueKeys) {
|
|
4989
5280
|
const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
|
|
4990
|
-
const exists = await
|
|
5281
|
+
const exists = await sql5`
|
|
4991
5282
|
SELECT EXISTS (
|
|
4992
5283
|
SELECT 1 FROM pg_constraint
|
|
4993
5284
|
WHERE conname = ${constraintName}
|
|
4994
5285
|
) AS "exists"
|
|
4995
5286
|
`.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
|
|
4996
5287
|
if (!exists) {
|
|
4997
|
-
await
|
|
5288
|
+
await sql5.raw(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`).execute(ddlDb);
|
|
4998
5289
|
}
|
|
4999
5290
|
addedUq[tableName] = [...addedUq[tableName] ?? [], cols];
|
|
5000
5291
|
}
|
|
5001
5292
|
for (const cols of conDiff.addedIndexes) {
|
|
5002
5293
|
const i = (def.schema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
|
|
5003
5294
|
const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
|
|
5004
|
-
await
|
|
5295
|
+
await sql5.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`).execute(ddlDb);
|
|
5005
5296
|
}
|
|
5006
5297
|
for (const cols of conDiff.removedIndexes) {
|
|
5007
5298
|
const i = (existingSchema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
|
|
5008
5299
|
if (i < 0)
|
|
5009
5300
|
continue;
|
|
5010
5301
|
const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
|
|
5011
|
-
await
|
|
5302
|
+
await sql5.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${idxName}`).execute(ddlDb);
|
|
5012
5303
|
}
|
|
5013
5304
|
}
|
|
5014
5305
|
const sg2 = await registerSubgraph(db, regData);
|
|
@@ -5034,7 +5325,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
5034
5325
|
}
|
|
5035
5326
|
}
|
|
5036
5327
|
for (const stmt of statements) {
|
|
5037
|
-
await
|
|
5328
|
+
await sql5.raw(stmt).execute(ddlDb);
|
|
5038
5329
|
}
|
|
5039
5330
|
const sg = await registerSubgraph(db, regData);
|
|
5040
5331
|
return { action: "created", subgraphId: sg.id, version: newVersion };
|
|
@@ -5086,5 +5377,5 @@ export {
|
|
|
5086
5377
|
ByoBreakingChangeError
|
|
5087
5378
|
};
|
|
5088
5379
|
|
|
5089
|
-
//# debugId=
|
|
5380
|
+
//# debugId=99416B000428839064756E2164756E21
|
|
5090
5381
|
//# sourceMappingURL=index.js.map
|