ponder 0.9.2 → 0.9.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/bin/ponder.js +1933 -1606
- package/dist/bin/ponder.js.map +1 -1
- package/dist/{chunk-IFTUFVCL.js → chunk-LHCA5XFV.js} +2 -5
- package/dist/chunk-LHCA5XFV.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bin/commands/codegen.ts +8 -10
- package/src/bin/commands/dev.ts +30 -42
- package/src/bin/commands/list.ts +9 -14
- package/src/bin/commands/serve.ts +26 -39
- package/src/bin/commands/start.ts +29 -42
- package/src/bin/utils/{shutdown.ts → exit.ts} +23 -37
- package/src/bin/utils/run.ts +275 -175
- package/src/bin/utils/runServer.ts +1 -5
- package/src/build/index.ts +3 -8
- package/src/build/pre.ts +3 -0
- package/src/config/index.ts +5 -2
- package/src/database/index.ts +72 -72
- package/src/drizzle/kit/index.ts +3 -3
- package/src/indexing/index.ts +0 -4
- package/src/indexing/service.ts +31 -93
- package/src/indexing-store/historical.ts +2 -4
- package/src/internal/common.ts +2 -0
- package/src/internal/errors.ts +9 -9
- package/src/internal/logger.ts +1 -1
- package/src/internal/metrics.ts +75 -103
- package/src/internal/shutdown.ts +25 -0
- package/src/internal/telemetry.ts +16 -18
- package/src/internal/types.ts +9 -1
- package/src/server/index.ts +3 -5
- package/src/sync/events.ts +4 -4
- package/src/sync/filter.ts +1 -0
- package/src/sync/index.ts +1046 -805
- package/src/sync-historical/index.ts +0 -37
- package/src/sync-realtime/index.ts +48 -48
- package/src/sync-store/encoding.ts +5 -5
- package/src/sync-store/index.ts +5 -23
- package/src/ui/index.ts +2 -11
- package/src/utils/checkpoint.ts +17 -3
- package/src/utils/chunk.ts +7 -0
- package/src/utils/generators.ts +66 -0
- package/src/utils/mutex.ts +34 -0
- package/src/utils/partition.ts +41 -0
- package/src/utils/requestQueue.ts +19 -10
- package/src/utils/zipper.ts +80 -0
- package/dist/chunk-IFTUFVCL.js.map +0 -1
package/src/build/index.ts
CHANGED
|
@@ -72,7 +72,6 @@ export type Build = {
|
|
|
72
72
|
startDev: (params: {
|
|
73
73
|
onReload: (kind: "indexing" | "api") => void;
|
|
74
74
|
}) => void;
|
|
75
|
-
kill: () => Promise<void>;
|
|
76
75
|
};
|
|
77
76
|
|
|
78
77
|
export const createBuild = async ({
|
|
@@ -133,6 +132,8 @@ export const createBuild = async ({
|
|
|
133
132
|
plugins: [viteTsconfigPathsPlugin(), vitePluginPonder(common.options)],
|
|
134
133
|
});
|
|
135
134
|
|
|
135
|
+
common.shutdown.add(() => viteDevServer.close());
|
|
136
|
+
|
|
136
137
|
// This is Vite boilerplate (initializes the Rollup container).
|
|
137
138
|
await viteDevServer.pluginContainer.buildStart({});
|
|
138
139
|
|
|
@@ -373,6 +374,7 @@ export const createBuild = async ({
|
|
|
373
374
|
status: "success",
|
|
374
375
|
result: {
|
|
375
376
|
databaseConfig: preBuild.databaseConfig,
|
|
377
|
+
ordering: preBuild.ordering,
|
|
376
378
|
},
|
|
377
379
|
} as const;
|
|
378
380
|
},
|
|
@@ -585,13 +587,6 @@ export const createBuild = async ({
|
|
|
585
587
|
|
|
586
588
|
viteDevServer.watcher.on("change", onFileChange);
|
|
587
589
|
},
|
|
588
|
-
async kill() {
|
|
589
|
-
await viteDevServer?.close();
|
|
590
|
-
common.logger.debug({
|
|
591
|
-
service: "build",
|
|
592
|
-
msg: "Killed build service",
|
|
593
|
-
});
|
|
594
|
-
},
|
|
595
590
|
} satisfies Build;
|
|
596
591
|
|
|
597
592
|
return build;
|
package/src/build/pre.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function buildPre({
|
|
|
18
18
|
options: Pick<Options, "rootDir" | "ponderDir">;
|
|
19
19
|
}): {
|
|
20
20
|
databaseConfig: DatabaseConfig;
|
|
21
|
+
ordering: NonNullable<Config["ordering"]>;
|
|
21
22
|
logs: { level: "warn" | "info" | "debug"; msg: string }[];
|
|
22
23
|
} {
|
|
23
24
|
const logs: { level: "warn" | "info" | "debug"; msg: string }[] = [];
|
|
@@ -112,6 +113,7 @@ export function buildPre({
|
|
|
112
113
|
return {
|
|
113
114
|
databaseConfig,
|
|
114
115
|
logs,
|
|
116
|
+
ordering: config.ordering ?? "omnichain",
|
|
115
117
|
};
|
|
116
118
|
}
|
|
117
119
|
|
|
@@ -131,6 +133,7 @@ export function safeBuildPre({
|
|
|
131
133
|
return {
|
|
132
134
|
status: "success",
|
|
133
135
|
databaseConfig: result.databaseConfig,
|
|
136
|
+
ordering: result.ordering,
|
|
134
137
|
logs: result.logs,
|
|
135
138
|
} as const;
|
|
136
139
|
} catch (_error) {
|
package/src/config/index.ts
CHANGED
|
@@ -5,20 +5,22 @@ import type { AddressConfig } from "./address.js";
|
|
|
5
5
|
import type { GetEventFilter } from "./eventFilter.js";
|
|
6
6
|
|
|
7
7
|
export type Config = {
|
|
8
|
+
database?: DatabaseConfig;
|
|
9
|
+
ordering?: "omnichain" | "multichain";
|
|
8
10
|
networks: { [networkName: string]: NetworkConfig<unknown> };
|
|
9
11
|
contracts: { [contractName: string]: GetContract };
|
|
10
12
|
accounts: { [accountName: string]: AccountConfig<unknown> };
|
|
11
|
-
database?: DatabaseConfig;
|
|
12
13
|
blocks: {
|
|
13
14
|
[sourceName: string]: GetBlockFilter<unknown>;
|
|
14
15
|
};
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export type CreateConfigReturnType<networks, contracts, accounts, blocks> = {
|
|
19
|
+
database?: DatabaseConfig;
|
|
20
|
+
ordering?: "omnichain" | "multichain";
|
|
18
21
|
networks: networks;
|
|
19
22
|
contracts: contracts;
|
|
20
23
|
accounts: accounts;
|
|
21
|
-
database?: DatabaseConfig;
|
|
22
24
|
blocks: blocks;
|
|
23
25
|
};
|
|
24
26
|
|
|
@@ -29,6 +31,7 @@ export const createConfig = <
|
|
|
29
31
|
const blocks = {},
|
|
30
32
|
>(config: {
|
|
31
33
|
database?: DatabaseConfig;
|
|
34
|
+
ordering?: "omnichain" | "multichain";
|
|
32
35
|
// TODO: add jsdoc to these properties.
|
|
33
36
|
networks: NetworksConfig<Narrow<networks>>;
|
|
34
37
|
contracts?: ContractsConfig<networks, Narrow<contracts>>;
|
package/src/database/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { getPrimaryKeyColumns, getTableNames } from "@/drizzle/index.js";
|
|
3
3
|
import { getColumnCasing } from "@/drizzle/kit/index.js";
|
|
4
4
|
import type { Common } from "@/internal/common.js";
|
|
5
|
-
import {
|
|
5
|
+
import { NonRetryableError, ShutdownError } from "@/internal/errors.js";
|
|
6
6
|
import type {
|
|
7
7
|
IndexingBuild,
|
|
8
8
|
NamespaceBuild,
|
|
@@ -17,10 +17,9 @@ import {
|
|
|
17
17
|
} from "@/sync-store/migrations.js";
|
|
18
18
|
import type { Drizzle } from "@/types/db.js";
|
|
19
19
|
import {
|
|
20
|
+
MAX_CHECKPOINT_STRING,
|
|
21
|
+
ZERO_CHECKPOINT_STRING,
|
|
20
22
|
decodeCheckpoint,
|
|
21
|
-
encodeCheckpoint,
|
|
22
|
-
maxCheckpoint,
|
|
23
|
-
zeroCheckpoint,
|
|
24
23
|
} from "@/utils/checkpoint.js";
|
|
25
24
|
import { formatEta } from "@/utils/format.js";
|
|
26
25
|
import { createPool, createReadonlyPool } from "@/utils/pg.js";
|
|
@@ -62,8 +61,6 @@ export type Database = {
|
|
|
62
61
|
revert(args: { checkpoint: string }): Promise<void>;
|
|
63
62
|
finalize(args: { checkpoint: string }): Promise<void>;
|
|
64
63
|
complete(args: { checkpoint: string }): Promise<void>;
|
|
65
|
-
unlock(): Promise<void>;
|
|
66
|
-
kill(): Promise<void>;
|
|
67
64
|
};
|
|
68
65
|
|
|
69
66
|
export type PonderApp = {
|
|
@@ -91,8 +88,8 @@ export type PonderInternalSchema = {
|
|
|
91
88
|
} & {
|
|
92
89
|
[_: ReturnType<typeof getTableNames>[number]["reorg"]]: unknown & {
|
|
93
90
|
operation_id: number;
|
|
94
|
-
checkpoint: string;
|
|
95
91
|
operation: 0 | 1 | 2;
|
|
92
|
+
checkpoint: string;
|
|
96
93
|
};
|
|
97
94
|
};
|
|
98
95
|
|
|
@@ -127,11 +124,10 @@ export const createDatabase = async ({
|
|
|
127
124
|
}: {
|
|
128
125
|
common: Common;
|
|
129
126
|
namespace: NamespaceBuild;
|
|
130
|
-
preBuild: PreBuild
|
|
127
|
+
preBuild: Pick<PreBuild, "databaseConfig">;
|
|
131
128
|
schemaBuild: Omit<SchemaBuild, "graphqlSchema">;
|
|
132
129
|
}): Promise<Database> => {
|
|
133
130
|
let heartbeatInterval: NodeJS.Timeout | undefined;
|
|
134
|
-
let isKilled = false;
|
|
135
131
|
|
|
136
132
|
////////
|
|
137
133
|
// Create schema, drivers, roles, and query builders
|
|
@@ -155,6 +151,22 @@ export const createDatabase = async ({
|
|
|
155
151
|
: preBuild.databaseConfig.instance,
|
|
156
152
|
};
|
|
157
153
|
|
|
154
|
+
common.shutdown.add(async () => {
|
|
155
|
+
clearInterval(heartbeatInterval);
|
|
156
|
+
|
|
157
|
+
await qb.internal
|
|
158
|
+
.updateTable("_ponder_meta")
|
|
159
|
+
.where("key", "=", "app")
|
|
160
|
+
.set({
|
|
161
|
+
value: sql`jsonb_set(value, '{is_locked}', to_jsonb(0))`,
|
|
162
|
+
})
|
|
163
|
+
.execute();
|
|
164
|
+
|
|
165
|
+
if (dialect === "pglite") {
|
|
166
|
+
await (driver as PGliteDriver).instance.close();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
158
170
|
const kyselyDialect = createPgliteKyselyDialect(driver.instance);
|
|
159
171
|
|
|
160
172
|
await driver.instance.query(`CREATE SCHEMA IF NOT EXISTS "${namespace}"`);
|
|
@@ -251,6 +263,27 @@ export const createDatabase = async ({
|
|
|
251
263
|
listen: undefined,
|
|
252
264
|
} as PostgresDriver;
|
|
253
265
|
|
|
266
|
+
common.shutdown.add(async () => {
|
|
267
|
+
clearInterval(heartbeatInterval);
|
|
268
|
+
|
|
269
|
+
await qb.internal
|
|
270
|
+
.updateTable("_ponder_meta")
|
|
271
|
+
.where("key", "=", "app")
|
|
272
|
+
.set({
|
|
273
|
+
value: sql`jsonb_set(value, '{is_locked}', to_jsonb(0))`,
|
|
274
|
+
})
|
|
275
|
+
.execute();
|
|
276
|
+
|
|
277
|
+
const d = driver as PostgresDriver;
|
|
278
|
+
d.listen?.release();
|
|
279
|
+
await Promise.all([
|
|
280
|
+
d.internal.end(),
|
|
281
|
+
d.user.end(),
|
|
282
|
+
d.readonly.end(),
|
|
283
|
+
d.sync.end(),
|
|
284
|
+
]);
|
|
285
|
+
});
|
|
286
|
+
|
|
254
287
|
await driver.internal.query(`CREATE SCHEMA IF NOT EXISTS "${namespace}"`);
|
|
255
288
|
|
|
256
289
|
qb = {
|
|
@@ -340,20 +373,15 @@ export const createDatabase = async ({
|
|
|
340
373
|
// Helpers
|
|
341
374
|
////////
|
|
342
375
|
|
|
343
|
-
/**
|
|
344
|
-
* Undo operations in user tables by using the "reorg" tables.
|
|
345
|
-
*
|
|
346
|
-
* Note: "reorg" tables may contain operations that have not been applied to the
|
|
347
|
-
* underlying tables, but only be 1 operation at most.
|
|
348
|
-
*/
|
|
376
|
+
/** Undo operations in user tables by using the "reorg" tables. */
|
|
349
377
|
const revert = async ({
|
|
378
|
+
tx,
|
|
350
379
|
tableName,
|
|
351
380
|
checkpoint,
|
|
352
|
-
tx,
|
|
353
381
|
}: {
|
|
382
|
+
tx: Transaction<PonderInternalSchema>;
|
|
354
383
|
tableName: ReturnType<typeof getTableNames>[number];
|
|
355
384
|
checkpoint: string;
|
|
356
|
-
tx: Transaction<PonderInternalSchema>;
|
|
357
385
|
}) => {
|
|
358
386
|
const primaryKeyColumns = getPrimaryKeyColumns(
|
|
359
387
|
schemaBuild.schema[tableName.js] as PgTable,
|
|
@@ -432,7 +460,8 @@ export const createDatabase = async ({
|
|
|
432
460
|
});
|
|
433
461
|
};
|
|
434
462
|
|
|
435
|
-
|
|
463
|
+
/** 'true' if `migrate` created new tables. */
|
|
464
|
+
let createdTables: boolean;
|
|
436
465
|
|
|
437
466
|
const database = {
|
|
438
467
|
driver,
|
|
@@ -458,15 +487,27 @@ export const createDatabase = async ({
|
|
|
458
487
|
}
|
|
459
488
|
|
|
460
489
|
try {
|
|
490
|
+
if (common.shutdown.isKilled) {
|
|
491
|
+
throw new ShutdownError();
|
|
492
|
+
}
|
|
493
|
+
|
|
461
494
|
const result = await fn();
|
|
462
495
|
common.metrics.ponder_database_method_duration.observe(
|
|
463
496
|
{ method: options.method },
|
|
464
497
|
endClock(),
|
|
465
498
|
);
|
|
499
|
+
|
|
500
|
+
if (common.shutdown.isKilled) {
|
|
501
|
+
throw new ShutdownError();
|
|
502
|
+
}
|
|
466
503
|
return result;
|
|
467
504
|
} catch (_error) {
|
|
468
505
|
const error = _error as Error;
|
|
469
506
|
|
|
507
|
+
if (common.shutdown.isKilled) {
|
|
508
|
+
throw new ShutdownError();
|
|
509
|
+
}
|
|
510
|
+
|
|
470
511
|
common.metrics.ponder_database_method_duration.observe(
|
|
471
512
|
{ method: options.method },
|
|
472
513
|
endClock(),
|
|
@@ -475,14 +516,6 @@ export const createDatabase = async ({
|
|
|
475
516
|
method: options.method,
|
|
476
517
|
});
|
|
477
518
|
|
|
478
|
-
if (isKilled) {
|
|
479
|
-
common.logger.trace({
|
|
480
|
-
service: "database",
|
|
481
|
-
msg: `Ignored error during '${options.method}' database method, service is killed (id=${id})`,
|
|
482
|
-
});
|
|
483
|
-
throw new IgnorableError();
|
|
484
|
-
}
|
|
485
|
-
|
|
486
519
|
if (!hasError) {
|
|
487
520
|
hasError = true;
|
|
488
521
|
firstError = error;
|
|
@@ -839,7 +872,7 @@ export const createDatabase = async ({
|
|
|
839
872
|
.executeTakeFirst()
|
|
840
873
|
.then((row) => row?.value);
|
|
841
874
|
|
|
842
|
-
|
|
875
|
+
createdTables = false;
|
|
843
876
|
|
|
844
877
|
if (previousApp === undefined) {
|
|
845
878
|
await createEnums();
|
|
@@ -850,7 +883,7 @@ export const createDatabase = async ({
|
|
|
850
883
|
(process.env.PONDER_EXPERIMENTAL_DB === "platform" &&
|
|
851
884
|
previousApp.build_id !== buildId) ||
|
|
852
885
|
(process.env.PONDER_EXPERIMENTAL_DB === "platform" &&
|
|
853
|
-
previousApp.checkpoint ===
|
|
886
|
+
previousApp.checkpoint === ZERO_CHECKPOINT_STRING)
|
|
854
887
|
) {
|
|
855
888
|
for (const tableName of getTableNames(schemaBuild.schema)) {
|
|
856
889
|
await tx.schema
|
|
@@ -887,14 +920,12 @@ export const createDatabase = async ({
|
|
|
887
920
|
|
|
888
921
|
// write metadata
|
|
889
922
|
|
|
890
|
-
checkpoint = encodeCheckpoint(zeroCheckpoint);
|
|
891
|
-
|
|
892
923
|
const newApp = {
|
|
893
924
|
is_locked: 1,
|
|
894
925
|
is_dev: common.options.command === "dev" ? 1 : 0,
|
|
895
926
|
heartbeat_at: Date.now(),
|
|
896
927
|
build_id: buildId,
|
|
897
|
-
checkpoint:
|
|
928
|
+
checkpoint: ZERO_CHECKPOINT_STRING,
|
|
898
929
|
table_names: getTableNames(schemaBuild.schema).map(
|
|
899
930
|
({ sql }) => sql,
|
|
900
931
|
),
|
|
@@ -1012,9 +1043,8 @@ export const createDatabase = async ({
|
|
|
1012
1043
|
}, common.options.databaseHeartbeatInterval);
|
|
1013
1044
|
},
|
|
1014
1045
|
async recoverCheckpoint() {
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
}
|
|
1046
|
+
// new tables are empty
|
|
1047
|
+
if (createdTables) return ZERO_CHECKPOINT_STRING;
|
|
1018
1048
|
|
|
1019
1049
|
return this.wrap(
|
|
1020
1050
|
{ method: "recoverCheckpoint", includeTraceLogs: true },
|
|
@@ -1027,7 +1057,7 @@ export const createDatabase = async ({
|
|
|
1027
1057
|
.executeTakeFirstOrThrow()
|
|
1028
1058
|
.then((row) => row.value);
|
|
1029
1059
|
|
|
1030
|
-
if (app.checkpoint ===
|
|
1060
|
+
if (app.checkpoint === ZERO_CHECKPOINT_STRING) {
|
|
1031
1061
|
for (const tableName of getTableNames(schemaBuild.schema)) {
|
|
1032
1062
|
await sql
|
|
1033
1063
|
.raw(
|
|
@@ -1108,13 +1138,13 @@ RETURNS TRIGGER AS $$
|
|
|
1108
1138
|
BEGIN
|
|
1109
1139
|
IF TG_OP = 'INSERT' THEN
|
|
1110
1140
|
INSERT INTO "${namespace}"."${tableName.reorg}" (${columnNames.join(",")}, operation, checkpoint)
|
|
1111
|
-
VALUES (${columnNames.map((name) => `NEW.${name}`).join(",")}, 0, '${
|
|
1141
|
+
VALUES (${columnNames.map((name) => `NEW.${name}`).join(",")}, 0, '${MAX_CHECKPOINT_STRING}');
|
|
1112
1142
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
1113
1143
|
INSERT INTO "${namespace}"."${tableName.reorg}" (${columnNames.join(",")}, operation, checkpoint)
|
|
1114
|
-
VALUES (${columnNames.map((name) => `OLD.${name}`).join(",")}, 1, '${
|
|
1144
|
+
VALUES (${columnNames.map((name) => `OLD.${name}`).join(",")}, 1, '${MAX_CHECKPOINT_STRING}');
|
|
1115
1145
|
ELSIF TG_OP = 'DELETE' THEN
|
|
1116
1146
|
INSERT INTO "${namespace}"."${tableName.reorg}" (${columnNames.join(",")}, operation, checkpoint)
|
|
1117
|
-
VALUES (${columnNames.map((name) => `OLD.${name}`).join(",")}, 2, '${
|
|
1147
|
+
VALUES (${columnNames.map((name) => `OLD.${name}`).join(",")}, 2, '${MAX_CHECKPOINT_STRING}');
|
|
1118
1148
|
END IF;
|
|
1119
1149
|
RETURN NULL;
|
|
1120
1150
|
END;
|
|
@@ -1151,13 +1181,9 @@ $$ LANGUAGE plpgsql
|
|
|
1151
1181
|
await this.wrap({ method: "revert", includeTraceLogs: true }, () =>
|
|
1152
1182
|
Promise.all(
|
|
1153
1183
|
getTableNames(schemaBuild.schema).map((tableName) =>
|
|
1154
|
-
qb.internal
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
checkpoint,
|
|
1158
|
-
tx,
|
|
1159
|
-
}),
|
|
1160
|
-
),
|
|
1184
|
+
qb.internal
|
|
1185
|
+
.transaction()
|
|
1186
|
+
.execute((tx) => revert({ tx, tableName, checkpoint })),
|
|
1161
1187
|
),
|
|
1162
1188
|
),
|
|
1163
1189
|
);
|
|
@@ -1201,7 +1227,7 @@ $$ LANGUAGE plpgsql
|
|
|
1201
1227
|
await qb.internal
|
|
1202
1228
|
.updateTable(tableName.reorg)
|
|
1203
1229
|
.set({ checkpoint })
|
|
1204
|
-
.where("checkpoint", "=",
|
|
1230
|
+
.where("checkpoint", "=", MAX_CHECKPOINT_STRING)
|
|
1205
1231
|
.execute();
|
|
1206
1232
|
},
|
|
1207
1233
|
),
|
|
@@ -1224,32 +1250,6 @@ $$ LANGUAGE plpgsql
|
|
|
1224
1250
|
},
|
|
1225
1251
|
);
|
|
1226
1252
|
},
|
|
1227
|
-
async kill() {
|
|
1228
|
-
isKilled = true;
|
|
1229
|
-
|
|
1230
|
-
if (dialect === "pglite") {
|
|
1231
|
-
const d = driver as PGliteDriver;
|
|
1232
|
-
await d.instance.close();
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
if (dialect === "pglite_test") {
|
|
1236
|
-
// no-op, allow test harness to clean up the instance
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
if (dialect === "postgres") {
|
|
1240
|
-
const d = driver as PostgresDriver;
|
|
1241
|
-
d.listen?.release();
|
|
1242
|
-
await d.internal.end();
|
|
1243
|
-
await d.user.end();
|
|
1244
|
-
await d.readonly.end();
|
|
1245
|
-
await d.sync.end();
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
common.logger.debug({
|
|
1249
|
-
service: "database",
|
|
1250
|
-
msg: "Closed connection to database",
|
|
1251
|
-
});
|
|
1252
|
-
},
|
|
1253
1253
|
} satisfies Database;
|
|
1254
1254
|
|
|
1255
1255
|
// @ts-ignore
|
package/src/drizzle/kit/index.ts
CHANGED
|
@@ -109,9 +109,9 @@ const createReorgTableStatement = (statement: JsonCreateTableStatement) => {
|
|
|
109
109
|
generatePgSnapshot(
|
|
110
110
|
[
|
|
111
111
|
pgTable("", {
|
|
112
|
-
operation_id: serial(
|
|
113
|
-
operation: integer(
|
|
114
|
-
checkpoint: varchar(
|
|
112
|
+
operation_id: serial().notNull().primaryKey(),
|
|
113
|
+
operation: integer().notNull(),
|
|
114
|
+
checkpoint: varchar({
|
|
115
115
|
length: 75,
|
|
116
116
|
}).notNull(),
|
|
117
117
|
}),
|
package/src/indexing/index.ts
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import { type Extend, extend } from "@/utils/extend.js";
|
|
2
2
|
import {
|
|
3
3
|
create,
|
|
4
|
-
kill,
|
|
5
4
|
processEvents,
|
|
6
5
|
processSetupEvents,
|
|
7
6
|
setIndexingStore,
|
|
8
|
-
updateTotalSeconds,
|
|
9
7
|
} from "./service.js";
|
|
10
8
|
import type { Context, Service } from "./service.js";
|
|
11
9
|
|
|
12
10
|
const methods = {
|
|
13
11
|
create,
|
|
14
|
-
kill,
|
|
15
12
|
processEvents,
|
|
16
13
|
processSetupEvents,
|
|
17
|
-
updateTotalSeconds,
|
|
18
14
|
setIndexingStore,
|
|
19
15
|
};
|
|
20
16
|
|
package/src/indexing/service.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IndexingStore } from "@/indexing-store/index.js";
|
|
2
2
|
import type { Common } from "@/internal/common.js";
|
|
3
|
+
import { ShutdownError } from "@/internal/errors.js";
|
|
3
4
|
import type {
|
|
4
5
|
ContractSource,
|
|
5
6
|
Event,
|
|
@@ -10,23 +11,25 @@ import type {
|
|
|
10
11
|
SetupEvent,
|
|
11
12
|
Source,
|
|
12
13
|
} from "@/internal/types.js";
|
|
14
|
+
import type { SyncStore } from "@/sync-store/index.js";
|
|
13
15
|
import { isAddressFactory } from "@/sync/filter.js";
|
|
14
|
-
import
|
|
16
|
+
import { cachedTransport } from "@/sync/transport.js";
|
|
15
17
|
import type { Db } from "@/types/db.js";
|
|
16
18
|
import type { Block, Log, Trace, Transaction } from "@/types/eth.js";
|
|
17
19
|
import type { DeepPartial } from "@/types/utils.js";
|
|
18
20
|
import {
|
|
19
|
-
|
|
21
|
+
ZERO_CHECKPOINT,
|
|
20
22
|
decodeCheckpoint,
|
|
21
23
|
encodeCheckpoint,
|
|
22
|
-
zeroCheckpoint,
|
|
23
24
|
} from "@/utils/checkpoint.js";
|
|
24
25
|
import { prettyPrint } from "@/utils/print.js";
|
|
26
|
+
import type { RequestQueue } from "@/utils/requestQueue.js";
|
|
25
27
|
import { startClock } from "@/utils/timer.js";
|
|
26
|
-
import type
|
|
27
|
-
import { checksumAddress
|
|
28
|
+
import { type Abi, type Address, createClient } from "viem";
|
|
29
|
+
import { checksumAddress } from "viem";
|
|
28
30
|
import { addStackTrace } from "./addStackTrace.js";
|
|
29
|
-
import {
|
|
31
|
+
import type { ReadOnlyClient } from "./ponderActions.js";
|
|
32
|
+
import { getPonderActions } from "./ponderActions.js";
|
|
30
33
|
|
|
31
34
|
export type Context = {
|
|
32
35
|
network: { chainId: number; name: string };
|
|
@@ -49,12 +52,9 @@ export type Service = {
|
|
|
49
52
|
indexingFunctions: IndexingFunctions;
|
|
50
53
|
|
|
51
54
|
// state
|
|
52
|
-
isKilled: boolean;
|
|
53
|
-
|
|
54
55
|
eventCount: {
|
|
55
56
|
[eventName: string]: number;
|
|
56
57
|
};
|
|
57
|
-
startCheckpoint: Checkpoint;
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Reduce memory usage by reserving space for objects ahead of time
|
|
@@ -76,14 +76,16 @@ export type Service = {
|
|
|
76
76
|
export const create = ({
|
|
77
77
|
common,
|
|
78
78
|
indexingBuild: { sources, networks, indexingFunctions },
|
|
79
|
-
|
|
79
|
+
requestQueues,
|
|
80
|
+
syncStore,
|
|
80
81
|
}: {
|
|
81
82
|
common: Common;
|
|
82
83
|
indexingBuild: Pick<
|
|
83
84
|
IndexingBuild,
|
|
84
85
|
"sources" | "networks" | "indexingFunctions"
|
|
85
86
|
>;
|
|
86
|
-
|
|
87
|
+
requestQueues: RequestQueue[];
|
|
88
|
+
syncStore: SyncStore;
|
|
87
89
|
}): Service => {
|
|
88
90
|
const contextState: Service["currentEvent"]["contextState"] = {
|
|
89
91
|
blockNumber: undefined!,
|
|
@@ -139,10 +141,12 @@ export const create = ({
|
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
// build clientByChainId
|
|
142
|
-
for (
|
|
143
|
-
const
|
|
144
|
+
for (let i = 0; i < networks.length; i++) {
|
|
145
|
+
const network = networks[i]!;
|
|
146
|
+
const requestQueue = requestQueues[i]!;
|
|
147
|
+
|
|
144
148
|
clientByChainId[network.chainId] = createClient({
|
|
145
|
-
transport,
|
|
149
|
+
transport: cachedTransport({ requestQueue, syncStore }),
|
|
146
150
|
chain: network.chain,
|
|
147
151
|
// @ts-ignore
|
|
148
152
|
}).extend(getPonderActions(contextState));
|
|
@@ -157,9 +161,7 @@ export const create = ({
|
|
|
157
161
|
return {
|
|
158
162
|
common,
|
|
159
163
|
indexingFunctions,
|
|
160
|
-
isKilled: false,
|
|
161
164
|
eventCount,
|
|
162
|
-
startCheckpoint: decodeCheckpoint(sync.getStartCheckpoint()),
|
|
163
165
|
currentEvent: {
|
|
164
166
|
contextState,
|
|
165
167
|
context: {
|
|
@@ -184,11 +186,7 @@ export const processSetupEvents = async (
|
|
|
184
186
|
sources: Source[];
|
|
185
187
|
networks: Network[];
|
|
186
188
|
},
|
|
187
|
-
): Promise<
|
|
188
|
-
| { status: "error"; error: Error }
|
|
189
|
-
| { status: "success" }
|
|
190
|
-
| { status: "killed" }
|
|
191
|
-
> => {
|
|
189
|
+
): Promise<{ status: "error"; error: Error } | { status: "success" }> => {
|
|
192
190
|
for (const eventName of Object.keys(indexingService.indexingFunctions)) {
|
|
193
191
|
if (!eventName.endsWith(":setup")) continue;
|
|
194
192
|
|
|
@@ -204,8 +202,6 @@ export const processSetupEvents = async (
|
|
|
204
202
|
|
|
205
203
|
if (source === undefined) continue;
|
|
206
204
|
|
|
207
|
-
if (indexingService.isKilled) return { status: "killed" };
|
|
208
|
-
|
|
209
205
|
indexingService.eventCount[eventName]!++;
|
|
210
206
|
|
|
211
207
|
const result = await executeSetup(indexingService, {
|
|
@@ -213,7 +209,7 @@ export const processSetupEvents = async (
|
|
|
213
209
|
type: "setup",
|
|
214
210
|
chainId: network.chainId,
|
|
215
211
|
checkpoint: encodeCheckpoint({
|
|
216
|
-
...
|
|
212
|
+
...ZERO_CHECKPOINT,
|
|
217
213
|
chainId: BigInt(network.chainId),
|
|
218
214
|
blockNumber: BigInt(source.filter.fromBlock ?? 0),
|
|
219
215
|
}),
|
|
@@ -236,14 +232,8 @@ export const processSetupEvents = async (
|
|
|
236
232
|
export const processEvents = async (
|
|
237
233
|
indexingService: Service,
|
|
238
234
|
{ events }: { events: Event[] },
|
|
239
|
-
): Promise<
|
|
240
|
-
| { status: "error"; error: Error }
|
|
241
|
-
| { status: "success" }
|
|
242
|
-
| { status: "killed" }
|
|
243
|
-
> => {
|
|
235
|
+
): Promise<{ status: "error"; error: Error } | { status: "success" }> => {
|
|
244
236
|
for (let i = 0; i < events.length; i++) {
|
|
245
|
-
if (indexingService.isKilled) return { status: "killed" };
|
|
246
|
-
|
|
247
237
|
const event = events[i]!;
|
|
248
238
|
|
|
249
239
|
indexingService.eventCount[event.name]!++;
|
|
@@ -262,40 +252,8 @@ export const processEvents = async (
|
|
|
262
252
|
service: "indexing",
|
|
263
253
|
msg: `Completed indexing function (event="${event.name}", checkpoint=${event.checkpoint})`,
|
|
264
254
|
});
|
|
265
|
-
|
|
266
|
-
// periodically update metrics
|
|
267
|
-
if (i % 93 === 0) {
|
|
268
|
-
updateCompletedEvents(indexingService);
|
|
269
|
-
|
|
270
|
-
const eventTimestamp = decodeCheckpoint(event.checkpoint).blockTimestamp;
|
|
271
|
-
|
|
272
|
-
indexingService.common.metrics.ponder_indexing_completed_seconds.set(
|
|
273
|
-
eventTimestamp - indexingService.startCheckpoint.blockTimestamp,
|
|
274
|
-
);
|
|
275
|
-
indexingService.common.metrics.ponder_indexing_completed_timestamp.set(
|
|
276
|
-
eventTimestamp,
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
// Note: allows for terminal and logs to be updated
|
|
280
|
-
await new Promise(setImmediate);
|
|
281
|
-
}
|
|
282
255
|
}
|
|
283
256
|
|
|
284
|
-
// set completed seconds
|
|
285
|
-
if (events.length > 0) {
|
|
286
|
-
const lastEventInBatchTimestamp = decodeCheckpoint(
|
|
287
|
-
events[events.length - 1]!.checkpoint,
|
|
288
|
-
).blockTimestamp;
|
|
289
|
-
|
|
290
|
-
indexingService.common.metrics.ponder_indexing_completed_seconds.set(
|
|
291
|
-
lastEventInBatchTimestamp -
|
|
292
|
-
indexingService.startCheckpoint.blockTimestamp,
|
|
293
|
-
);
|
|
294
|
-
indexingService.common.metrics.ponder_indexing_completed_timestamp.set(
|
|
295
|
-
lastEventInBatchTimestamp,
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
// set completed events
|
|
299
257
|
updateCompletedEvents(indexingService);
|
|
300
258
|
|
|
301
259
|
return { status: "success" };
|
|
@@ -314,24 +272,6 @@ export const setIndexingStore = (
|
|
|
314
272
|
};
|
|
315
273
|
};
|
|
316
274
|
|
|
317
|
-
export const kill = (indexingService: Service) => {
|
|
318
|
-
indexingService.common.logger.debug({
|
|
319
|
-
service: "indexing",
|
|
320
|
-
msg: "Killed indexing service",
|
|
321
|
-
});
|
|
322
|
-
indexingService.isKilled = true;
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
export const updateTotalSeconds = (
|
|
326
|
-
indexingService: Service,
|
|
327
|
-
endCheckpoint: Checkpoint,
|
|
328
|
-
) => {
|
|
329
|
-
indexingService.common.metrics.ponder_indexing_total_seconds.set(
|
|
330
|
-
endCheckpoint.blockTimestamp -
|
|
331
|
-
indexingService.startCheckpoint.blockTimestamp,
|
|
332
|
-
);
|
|
333
|
-
};
|
|
334
|
-
|
|
335
275
|
const updateCompletedEvents = (indexingService: Service) => {
|
|
336
276
|
for (const event of Object.keys(indexingService.eventCount)) {
|
|
337
277
|
const metricLabel = {
|
|
@@ -347,11 +287,7 @@ const updateCompletedEvents = (indexingService: Service) => {
|
|
|
347
287
|
const executeSetup = async (
|
|
348
288
|
indexingService: Service,
|
|
349
289
|
{ event }: { event: SetupEvent },
|
|
350
|
-
): Promise<
|
|
351
|
-
| { status: "error"; error: Error }
|
|
352
|
-
| { status: "success" }
|
|
353
|
-
| { status: "killed" }
|
|
354
|
-
> => {
|
|
290
|
+
): Promise<{ status: "error"; error: Error } | { status: "success" }> => {
|
|
355
291
|
const {
|
|
356
292
|
common,
|
|
357
293
|
indexingFunctions,
|
|
@@ -382,9 +318,12 @@ const executeSetup = async (
|
|
|
382
318
|
endClock(),
|
|
383
319
|
);
|
|
384
320
|
} catch (_error) {
|
|
385
|
-
if (indexingService.isKilled) return { status: "killed" };
|
|
386
321
|
const error = _error instanceof Error ? _error : new Error(String(_error));
|
|
387
322
|
|
|
323
|
+
if (common.shutdown.isKilled) {
|
|
324
|
+
throw new ShutdownError();
|
|
325
|
+
}
|
|
326
|
+
|
|
388
327
|
addStackTrace(error, common.options);
|
|
389
328
|
addErrorMeta(error, toErrorMeta(event));
|
|
390
329
|
|
|
@@ -406,11 +345,7 @@ const executeSetup = async (
|
|
|
406
345
|
const executeEvent = async (
|
|
407
346
|
indexingService: Service,
|
|
408
347
|
{ event }: { event: Event },
|
|
409
|
-
): Promise<
|
|
410
|
-
| { status: "error"; error: Error }
|
|
411
|
-
| { status: "success" }
|
|
412
|
-
| { status: "killed" }
|
|
413
|
-
> => {
|
|
348
|
+
): Promise<{ status: "error"; error: Error } | { status: "success" }> => {
|
|
414
349
|
const {
|
|
415
350
|
common,
|
|
416
351
|
indexingFunctions,
|
|
@@ -442,9 +377,12 @@ const executeEvent = async (
|
|
|
442
377
|
endClock(),
|
|
443
378
|
);
|
|
444
379
|
} catch (_error) {
|
|
445
|
-
if (indexingService.isKilled) return { status: "killed" };
|
|
446
380
|
const error = _error instanceof Error ? _error : new Error(String(_error));
|
|
447
381
|
|
|
382
|
+
if (common.shutdown.isKilled) {
|
|
383
|
+
throw new ShutdownError();
|
|
384
|
+
}
|
|
385
|
+
|
|
448
386
|
addStackTrace(error, common.options);
|
|
449
387
|
addErrorMeta(error, toErrorMeta(event));
|
|
450
388
|
|