@secondlayer/subgraphs 3.19.2 → 3.19.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/runtime/catchup.d.ts +8 -1
- package/dist/src/runtime/catchup.js +64 -4
- package/dist/src/runtime/catchup.js.map +3 -3
- package/dist/src/runtime/emitter.d.ts +39 -2
- package/dist/src/runtime/emitter.js +77 -24
- package/dist/src/runtime/emitter.js.map +3 -3
- package/dist/src/runtime/processor.js +138 -77
- package/dist/src/runtime/processor.js.map +4 -4
- package/dist/src/runtime/reorg.js +372 -80
- package/dist/src/runtime/reorg.js.map +6 -4
- package/dist/src/service.js +138 -77
- package/dist/src/service.js.map +4 -4
- package/package.json +1 -1
package/dist/src/service.js
CHANGED
|
@@ -2862,6 +2862,52 @@ var STANDARD_CATCHUP_BATCH_CONFIG = {
|
|
|
2862
2862
|
prefetch: true
|
|
2863
2863
|
};
|
|
2864
2864
|
var catchingUp = new Set;
|
|
2865
|
+
var subgraphBlockLockHeld = new Set;
|
|
2866
|
+
var subgraphBlockLockWaiters = new Map;
|
|
2867
|
+
var reorgEpoch = new Map;
|
|
2868
|
+
async function acquireSubgraphBlockLock(name) {
|
|
2869
|
+
if (!subgraphBlockLockHeld.has(name)) {
|
|
2870
|
+
subgraphBlockLockHeld.add(name);
|
|
2871
|
+
return () => releaseSubgraphBlockLock(name);
|
|
2872
|
+
}
|
|
2873
|
+
return new Promise((resolve) => {
|
|
2874
|
+
const waiters = subgraphBlockLockWaiters.get(name) ?? [];
|
|
2875
|
+
waiters.push(() => resolve(() => releaseSubgraphBlockLock(name)));
|
|
2876
|
+
subgraphBlockLockWaiters.set(name, waiters);
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2879
|
+
function releaseSubgraphBlockLock(name) {
|
|
2880
|
+
const waiters = subgraphBlockLockWaiters.get(name);
|
|
2881
|
+
if (waiters && waiters.length > 0) {
|
|
2882
|
+
const next = waiters.shift();
|
|
2883
|
+
if (waiters.length === 0)
|
|
2884
|
+
subgraphBlockLockWaiters.delete(name);
|
|
2885
|
+
next();
|
|
2886
|
+
return;
|
|
2887
|
+
}
|
|
2888
|
+
subgraphBlockLockHeld.delete(name);
|
|
2889
|
+
}
|
|
2890
|
+
async function withSubgraphBlockLock(name, fn) {
|
|
2891
|
+
const release = await acquireSubgraphBlockLock(name);
|
|
2892
|
+
try {
|
|
2893
|
+
return await fn();
|
|
2894
|
+
} finally {
|
|
2895
|
+
release();
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
function bumpReorgEpoch(name) {
|
|
2899
|
+
reorgEpoch.set(name, (reorgEpoch.get(name) ?? 0) + 1);
|
|
2900
|
+
}
|
|
2901
|
+
function getReorgEpoch(name) {
|
|
2902
|
+
return reorgEpoch.get(name) ?? 0;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
class ReorgEpochChangedError extends Error {
|
|
2906
|
+
constructor(name) {
|
|
2907
|
+
super(`reorg epoch changed for ${name} mid catch-up`);
|
|
2908
|
+
this.name = "ReorgEpochChangedError";
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2865
2911
|
function parsePositiveInt2(value) {
|
|
2866
2912
|
if (value == null || value.trim() === "")
|
|
2867
2913
|
return;
|
|
@@ -2925,6 +2971,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
2925
2971
|
const batchConfig = resolveCatchupBatchConfig();
|
|
2926
2972
|
let batchSize = batchConfig.defaultBatchSize;
|
|
2927
2973
|
let currentHeight = startBlock;
|
|
2974
|
+
const walkEpoch = getReorgEpoch(subgraphName);
|
|
2928
2975
|
let prefetchedBatchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
|
|
2929
2976
|
let nextBatchPromise = batchConfig.prefetch ? source.loadBlockRange(currentHeight, prefetchedBatchEnd) : undefined;
|
|
2930
2977
|
while (currentHeight <= chainTip) {
|
|
@@ -2966,12 +3013,23 @@ async function catchUpSubgraph(subgraph, subgraphName) {
|
|
|
2966
3013
|
stopCatchup = true;
|
|
2967
3014
|
break;
|
|
2968
3015
|
}
|
|
3016
|
+
const preloaded = blockData;
|
|
2969
3017
|
let result;
|
|
2970
3018
|
try {
|
|
2971
|
-
result = await
|
|
2972
|
-
|
|
3019
|
+
result = await withSubgraphBlockLock(subgraphName, async () => {
|
|
3020
|
+
if (getReorgEpoch(subgraphName) !== walkEpoch) {
|
|
3021
|
+
throw new ReorgEpochChangedError(subgraphName);
|
|
3022
|
+
}
|
|
3023
|
+
return processBlockWithRetry(subgraph, subgraphName, height, {
|
|
3024
|
+
preloaded
|
|
3025
|
+
});
|
|
2973
3026
|
});
|
|
2974
3027
|
} catch (err) {
|
|
3028
|
+
if (err instanceof ReorgEpochChangedError) {
|
|
3029
|
+
logger8.info("Reorg detected mid catch-up, aborting walk without writing further progress", { subgraph: subgraphName, blockHeight: height });
|
|
3030
|
+
stopCatchup = true;
|
|
3031
|
+
break;
|
|
3032
|
+
}
|
|
2975
3033
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2976
3034
|
logger8.error("Block processing failed persistently during catch-up", {
|
|
2977
3035
|
subgraph: subgraphName,
|
|
@@ -3040,85 +3098,88 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
|
|
|
3040
3098
|
const schema = sg.definition.schema;
|
|
3041
3099
|
if (!schema)
|
|
3042
3100
|
continue;
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
const
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
await client.
|
|
3101
|
+
bumpReorgEpoch(sg.name);
|
|
3102
|
+
await withSubgraphBlockLock(sg.name, async () => {
|
|
3103
|
+
const schemaName = sg.schema_name ?? pgSchemaName(sg.name);
|
|
3104
|
+
const client = resolveSubgraphRawClient(sg);
|
|
3105
|
+
const tableNames = Object.keys(schema);
|
|
3106
|
+
const revertedByTable = {};
|
|
3107
|
+
for (const tableName of tableNames) {
|
|
3108
|
+
const rows = await client.unsafe(`SELECT * FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1 LIMIT 1000`, [blockHeight]);
|
|
3109
|
+
if (rows.length > 0)
|
|
3110
|
+
revertedByTable[tableName] = rows;
|
|
3111
|
+
}
|
|
3112
|
+
const hasJournal = (await client.unsafe(`SELECT to_regclass('"${schemaName}"."_journal"') AS r`))[0]?.r;
|
|
3113
|
+
if (hasJournal) {
|
|
3114
|
+
await client.begin(async (tx) => {
|
|
3115
|
+
for (const tableName of tableNames) {
|
|
3116
|
+
const earliest = `
|
|
3117
|
+
SELECT DISTINCT ON (row_key) row_key, prev_row
|
|
3118
|
+
FROM "${schemaName}"."_journal"
|
|
3119
|
+
WHERE block_height >= $1 AND table_name = $2
|
|
3120
|
+
ORDER BY row_key, _jid ASC`;
|
|
3121
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" t USING (${earliest}) e WHERE to_jsonb(t.*) @> e.row_key`, [blockHeight, tableName]);
|
|
3122
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3123
|
+
await tx.unsafe(`INSERT INTO "${schemaName}"."${tableName}"
|
|
3124
|
+
SELECT r.* FROM (${earliest}) e
|
|
3125
|
+
CROSS JOIN LATERAL jsonb_populate_record(NULL::"${schemaName}"."${tableName}", e.prev_row) r
|
|
3126
|
+
WHERE e.prev_row IS NOT NULL`, [blockHeight, tableName]);
|
|
3127
|
+
}
|
|
3128
|
+
await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
|
|
3129
|
+
});
|
|
3130
|
+
} else {
|
|
3131
|
+
logger9.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
|
|
3055
3132
|
for (const tableName of tableNames) {
|
|
3056
|
-
|
|
3057
|
-
SELECT DISTINCT ON (row_key) row_key, prev_row
|
|
3058
|
-
FROM "${schemaName}"."_journal"
|
|
3059
|
-
WHERE block_height >= $1 AND table_name = $2
|
|
3060
|
-
ORDER BY row_key, _jid ASC`;
|
|
3061
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" t USING (${earliest}) e WHERE to_jsonb(t.*) @> e.row_key`, [blockHeight, tableName]);
|
|
3062
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3063
|
-
await tx.unsafe(`INSERT INTO "${schemaName}"."${tableName}"
|
|
3064
|
-
SELECT r.* FROM (${earliest}) e
|
|
3065
|
-
CROSS JOIN LATERAL jsonb_populate_record(NULL::"${schemaName}"."${tableName}", e.prev_row) r
|
|
3066
|
-
WHERE e.prev_row IS NOT NULL`, [blockHeight, tableName]);
|
|
3133
|
+
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3067
3134
|
}
|
|
3068
|
-
await tx.unsafe(`DELETE FROM "${schemaName}"."_journal" WHERE block_height >= $1`, [blockHeight]);
|
|
3069
|
-
});
|
|
3070
|
-
} else {
|
|
3071
|
-
logger9.warn("Subgraph has no revert journal — falling back to height delete (accumulator rows may lose history)", { subgraph: sg.name, blockHeight });
|
|
3072
|
-
for (const tableName of tableNames) {
|
|
3073
|
-
await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" >= $1`, [blockHeight]);
|
|
3074
3135
|
}
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
"
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3136
|
+
for (const [tableName, rows] of Object.entries(revertedByTable)) {
|
|
3137
|
+
if (rows.length === 0)
|
|
3138
|
+
continue;
|
|
3139
|
+
await targetDb.insertInto("subscription_outbox").columns([
|
|
3140
|
+
"subscription_id",
|
|
3141
|
+
"event_type",
|
|
3142
|
+
"payload",
|
|
3143
|
+
"dedup_key",
|
|
3144
|
+
"row_pk",
|
|
3145
|
+
"status"
|
|
3146
|
+
]).expression((eb) => eb.selectFrom("subscriptions").select((eb2) => [
|
|
3147
|
+
"id as subscription_id",
|
|
3148
|
+
eb2.val(`${sg.name}.${tableName}.reverted`).as("event_type"),
|
|
3149
|
+
eb2.val(JSON.stringify({
|
|
3150
|
+
subgraph: sg.name,
|
|
3151
|
+
table: tableName,
|
|
3152
|
+
reorgFromHeight: blockHeight,
|
|
3153
|
+
rows
|
|
3154
|
+
})).as("payload"),
|
|
3155
|
+
eb2.val(`reorg:${sg.name}:${tableName}:${blockHeight}`).as("dedup_key"),
|
|
3156
|
+
eb2.val({
|
|
3157
|
+
subgraph: sg.name,
|
|
3158
|
+
table: tableName,
|
|
3159
|
+
reorgRoot: blockHeight
|
|
3160
|
+
}).as("row_pk"),
|
|
3161
|
+
eb2.val("pending").as("status")
|
|
3162
|
+
]).where("subgraph_name", "=", sg.name).where("table_name", "=", tableName).where("status", "=", "active")).onConflict((oc) => oc.column("dedup_key").doNothing()).execute().catch((err) => {
|
|
3163
|
+
logger9.error("Failed to emit revert event for subscriptions", {
|
|
3164
|
+
event: "reorg_revert_emit_dropped",
|
|
3165
|
+
subgraph: sg.name,
|
|
3166
|
+
table: tableName,
|
|
3167
|
+
blockHeight,
|
|
3168
|
+
error: getErrorMessage4(err)
|
|
3169
|
+
});
|
|
3109
3170
|
});
|
|
3171
|
+
}
|
|
3172
|
+
logger9.info("Subgraph reorg cleanup done", {
|
|
3173
|
+
subgraph: sg.name,
|
|
3174
|
+
blockHeight,
|
|
3175
|
+
tablesAffected: Object.keys(revertedByTable).length
|
|
3176
|
+
});
|
|
3177
|
+
const def = await loadSubgraphDef(sg);
|
|
3178
|
+
await processBlock(def, sg.name, blockHeight);
|
|
3179
|
+
logger9.info("Subgraph reorg reprocessed", {
|
|
3180
|
+
subgraph: sg.name,
|
|
3181
|
+
blockHeight
|
|
3110
3182
|
});
|
|
3111
|
-
}
|
|
3112
|
-
logger9.info("Subgraph reorg cleanup done", {
|
|
3113
|
-
subgraph: sg.name,
|
|
3114
|
-
blockHeight,
|
|
3115
|
-
tablesAffected: Object.keys(revertedByTable).length
|
|
3116
|
-
});
|
|
3117
|
-
const def = await loadSubgraphDef(sg);
|
|
3118
|
-
await processBlock(def, sg.name, blockHeight);
|
|
3119
|
-
logger9.info("Subgraph reorg reprocessed", {
|
|
3120
|
-
subgraph: sg.name,
|
|
3121
|
-
blockHeight
|
|
3122
3183
|
});
|
|
3123
3184
|
} catch (err) {
|
|
3124
3185
|
logger9.error("Subgraph reorg handling failed", {
|
|
@@ -3618,5 +3679,5 @@ var shutdown = async () => {
|
|
|
3618
3679
|
process.on("SIGINT", shutdown);
|
|
3619
3680
|
process.on("SIGTERM", shutdown);
|
|
3620
3681
|
|
|
3621
|
-
//# debugId=
|
|
3682
|
+
//# debugId=2031C35EBA87F8DD64756E2164756E21
|
|
3622
3683
|
//# sourceMappingURL=service.js.map
|