ponder 0.9.2 → 0.9.4-debug.1
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 +2470 -3762
- package/dist/bin/ponder.js.map +1 -1
- package/dist/chunk-6AOFLZJ4.js +1692 -0
- package/dist/chunk-6AOFLZJ4.js.map +1 -0
- package/dist/chunk-DZFRP3KH.js +70 -0
- package/dist/chunk-DZFRP3KH.js.map +1 -0
- package/dist/{chunk-IFTUFVCL.js → chunk-MJKRYIBO.js} +3 -73
- package/dist/chunk-MJKRYIBO.js.map +1 -0
- package/dist/db-in86nyw7.d.ts +625 -0
- package/dist/experimental_unsafe_stores.d.ts +375 -0
- package/dist/experimental_unsafe_stores.js +11 -0
- package/dist/experimental_unsafe_stores.js.map +1 -0
- package/dist/index.d.ts +17 -429
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -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/configAndIndexingFunctions.ts +547 -512
- package/src/build/index.ts +5 -8
- package/src/build/pre.ts +3 -0
- package/src/config/index.ts +9 -6
- package/src/database/index.ts +72 -72
- package/src/drizzle/kit/index.ts +3 -3
- package/src/experimental_unsafe_stores.ts +4 -0
- 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/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
|
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
UniqueConstraintError,
|
|
14
14
|
} from "@/internal/errors.js";
|
|
15
15
|
import type { SchemaBuild } from "@/internal/types.js";
|
|
16
|
-
import { encodeCheckpoint, zeroCheckpoint } from "@/utils/checkpoint.js";
|
|
17
16
|
import { prettyPrint } from "@/utils/print.js";
|
|
18
17
|
import { createQueue } from "@ponder/common";
|
|
19
18
|
import {
|
|
@@ -146,12 +145,12 @@ export const createHistoricalIndexingStore = ({
|
|
|
146
145
|
common,
|
|
147
146
|
schemaBuild: { schema },
|
|
148
147
|
database,
|
|
149
|
-
|
|
148
|
+
isDatabaseEmpty,
|
|
150
149
|
}: {
|
|
151
150
|
common: Common;
|
|
152
151
|
schemaBuild: Pick<SchemaBuild, "schema">;
|
|
153
152
|
database: Database;
|
|
154
|
-
|
|
153
|
+
isDatabaseEmpty: boolean;
|
|
155
154
|
}): IndexingStore<"historical"> => {
|
|
156
155
|
// Operation queue to make sure all queries are run in order, circumventing race conditions
|
|
157
156
|
const queue = createQueue<unknown, () => Promise<unknown>>({
|
|
@@ -302,7 +301,6 @@ export const createHistoricalIndexingStore = ({
|
|
|
302
301
|
return size;
|
|
303
302
|
};
|
|
304
303
|
|
|
305
|
-
let isDatabaseEmpty = initialCheckpoint === encodeCheckpoint(zeroCheckpoint);
|
|
306
304
|
/** Estimated number of bytes used by cache. */
|
|
307
305
|
let cacheBytes = 0;
|
|
308
306
|
/** LRU counter. */
|
package/src/internal/common.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
2
|
import type { MetricsService } from "./metrics.js";
|
|
3
3
|
import type { Options } from "./options.js";
|
|
4
|
+
import type { Shutdown } from "./shutdown.js";
|
|
4
5
|
import type { Telemetry } from "./telemetry.js";
|
|
5
6
|
|
|
6
7
|
export type Common = {
|
|
@@ -8,4 +9,5 @@ export type Common = {
|
|
|
8
9
|
logger: Logger;
|
|
9
10
|
metrics: MetricsService;
|
|
10
11
|
telemetry: Telemetry;
|
|
12
|
+
shutdown: Shutdown;
|
|
11
13
|
};
|
package/src/internal/errors.ts
CHANGED
|
@@ -35,15 +35,6 @@ export class NonRetryableError extends BaseError {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export class IgnorableError extends BaseError {
|
|
39
|
-
override name = "IgnorableError";
|
|
40
|
-
|
|
41
|
-
constructor(message?: string | undefined) {
|
|
42
|
-
super(message);
|
|
43
|
-
Object.setPrototypeOf(this, IgnorableError.prototype);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
38
|
// Indexing store errors
|
|
48
39
|
|
|
49
40
|
export class StoreError extends NonRetryableError {
|
|
@@ -126,3 +117,12 @@ export class FlushError extends NonRetryableError {
|
|
|
126
117
|
Object.setPrototypeOf(this, FlushError.prototype);
|
|
127
118
|
}
|
|
128
119
|
}
|
|
120
|
+
|
|
121
|
+
export class ShutdownError extends NonRetryableError {
|
|
122
|
+
override name = "ShutdownError";
|
|
123
|
+
|
|
124
|
+
constructor(message?: string | undefined) {
|
|
125
|
+
super(message);
|
|
126
|
+
Object.setPrototypeOf(this, ShutdownError.prototype);
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/internal/logger.ts
CHANGED
package/src/internal/metrics.ts
CHANGED
|
@@ -17,14 +17,17 @@ const httpRequestSizeBytes = [
|
|
|
17
17
|
|
|
18
18
|
export class MetricsService {
|
|
19
19
|
registry: prometheus.Registry;
|
|
20
|
+
start_timestamp: number;
|
|
21
|
+
rps: { [network: string]: { count: number; timestamp: number }[] };
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
ponder_historical_total_indexing_seconds: prometheus.Gauge<"network">;
|
|
24
|
+
ponder_historical_cached_indexing_seconds: prometheus.Gauge<"network">;
|
|
25
|
+
ponder_historical_completed_indexing_seconds: prometheus.Gauge<"network">;
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
ponder_indexing_has_error: prometheus.Gauge
|
|
27
|
+
ponder_indexing_timestamp: prometheus.Gauge<"network">;
|
|
28
|
+
ponder_indexing_has_error: prometheus.Gauge<"network">;
|
|
27
29
|
|
|
30
|
+
ponder_indexing_completed_events: prometheus.Gauge<"event">;
|
|
28
31
|
ponder_indexing_function_duration: prometheus.Histogram<"event">;
|
|
29
32
|
ponder_indexing_abi_decoding_duration: prometheus.Histogram;
|
|
30
33
|
|
|
@@ -64,15 +67,25 @@ export class MetricsService {
|
|
|
64
67
|
|
|
65
68
|
constructor() {
|
|
66
69
|
this.registry = new prometheus.Registry();
|
|
70
|
+
this.start_timestamp = Date.now();
|
|
71
|
+
this.rps = {};
|
|
67
72
|
|
|
68
|
-
this.
|
|
69
|
-
name: "
|
|
73
|
+
this.ponder_historical_total_indexing_seconds = new prometheus.Gauge({
|
|
74
|
+
name: "ponder_historical_total_indexing_seconds",
|
|
70
75
|
help: "Total number of seconds that are required",
|
|
76
|
+
labelNames: ["network"] as const,
|
|
77
|
+
registers: [this.registry],
|
|
78
|
+
});
|
|
79
|
+
this.ponder_historical_cached_indexing_seconds = new prometheus.Gauge({
|
|
80
|
+
name: "ponder_historical_cached_indexing_seconds",
|
|
81
|
+
help: "Number of seconds that have been cached",
|
|
82
|
+
labelNames: ["network"] as const,
|
|
71
83
|
registers: [this.registry],
|
|
72
84
|
});
|
|
73
|
-
this.
|
|
74
|
-
name: "
|
|
85
|
+
this.ponder_historical_completed_indexing_seconds = new prometheus.Gauge({
|
|
86
|
+
name: "ponder_historical_completed_indexing_seconds",
|
|
75
87
|
help: "Number of seconds that have been completed",
|
|
88
|
+
labelNames: ["network"] as const,
|
|
76
89
|
registers: [this.registry],
|
|
77
90
|
});
|
|
78
91
|
this.ponder_indexing_completed_events = new prometheus.Gauge({
|
|
@@ -81,9 +94,10 @@ export class MetricsService {
|
|
|
81
94
|
labelNames: ["network", "event"] as const,
|
|
82
95
|
registers: [this.registry],
|
|
83
96
|
});
|
|
84
|
-
this.
|
|
85
|
-
name: "
|
|
97
|
+
this.ponder_indexing_timestamp = new prometheus.Gauge({
|
|
98
|
+
name: "ponder_indexing_timestamp",
|
|
86
99
|
help: "Timestamp through which all events have been completed",
|
|
100
|
+
labelNames: ["network"] as const,
|
|
87
101
|
registers: [this.registry],
|
|
88
102
|
});
|
|
89
103
|
this.ponder_indexing_has_error = new prometheus.Gauge({
|
|
@@ -245,10 +259,14 @@ export class MetricsService {
|
|
|
245
259
|
}
|
|
246
260
|
|
|
247
261
|
resetIndexingMetrics() {
|
|
248
|
-
this.
|
|
249
|
-
this.
|
|
262
|
+
this.start_timestamp = Date.now();
|
|
263
|
+
this.rps = {};
|
|
264
|
+
|
|
265
|
+
this.ponder_historical_total_indexing_seconds.reset();
|
|
266
|
+
this.ponder_historical_cached_indexing_seconds.reset();
|
|
267
|
+
this.ponder_historical_completed_indexing_seconds.reset();
|
|
250
268
|
this.ponder_indexing_completed_events.reset();
|
|
251
|
-
this.
|
|
269
|
+
this.ponder_indexing_timestamp.reset();
|
|
252
270
|
this.ponder_indexing_has_error.reset();
|
|
253
271
|
this.ponder_indexing_function_duration.reset();
|
|
254
272
|
this.ponder_indexing_abi_decoding_duration.reset();
|
|
@@ -284,8 +302,6 @@ export class MetricsService {
|
|
|
284
302
|
}
|
|
285
303
|
}
|
|
286
304
|
|
|
287
|
-
const rps: { [network: string]: { count: number; timestamp: number }[] } = {};
|
|
288
|
-
|
|
289
305
|
export async function getSyncProgress(metrics: MetricsService): Promise<
|
|
290
306
|
{
|
|
291
307
|
networkName: string;
|
|
@@ -338,14 +354,14 @@ export async function getSyncProgress(metrics: MetricsService): Promise<
|
|
|
338
354
|
}
|
|
339
355
|
|
|
340
356
|
for (const [networkName, count] of Object.entries(requestCount)) {
|
|
341
|
-
if (rps[networkName] === undefined) {
|
|
342
|
-
rps[networkName] = [{ count, timestamp: Date.now() }];
|
|
357
|
+
if (metrics.rps[networkName] === undefined) {
|
|
358
|
+
metrics.rps[networkName] = [{ count, timestamp: Date.now() }];
|
|
343
359
|
} else {
|
|
344
|
-
rps[networkName]!.push({ count, timestamp: Date.now() });
|
|
360
|
+
metrics.rps[networkName]!.push({ count, timestamp: Date.now() });
|
|
345
361
|
}
|
|
346
362
|
|
|
347
|
-
if (rps[networkName]!.length > 100) {
|
|
348
|
-
rps[networkName]!.shift();
|
|
363
|
+
if (metrics.rps[networkName]!.length > 100) {
|
|
364
|
+
metrics.rps[networkName]!.shift();
|
|
349
365
|
}
|
|
350
366
|
}
|
|
351
367
|
|
|
@@ -365,9 +381,9 @@ export async function getSyncProgress(metrics: MetricsService): Promise<
|
|
|
365
381
|
// The ETA is low quality if we've completed only one or two blocks.
|
|
366
382
|
const eta = completedBlocks >= 3 ? total - elapsed : undefined;
|
|
367
383
|
|
|
368
|
-
const _length = rps[labels.network!]!.length;
|
|
369
|
-
const _firstRps = rps[labels.network!]![0]!;
|
|
370
|
-
const _lastRps = rps[labels.network!]![_length - 1]!;
|
|
384
|
+
const _length = metrics.rps[labels.network!]!.length;
|
|
385
|
+
const _firstRps = metrics.rps[labels.network!]![0]!;
|
|
386
|
+
const _lastRps = metrics.rps[labels.network!]![_length - 1]!;
|
|
371
387
|
|
|
372
388
|
const requests = _lastRps.count - (_length > 1 ? _firstRps.count : 0);
|
|
373
389
|
const seconds =
|
|
@@ -389,16 +405,33 @@ export async function getIndexingProgress(metrics: MetricsService) {
|
|
|
389
405
|
.values[0]?.value;
|
|
390
406
|
const hasError = hasErrorMetric === 1;
|
|
391
407
|
|
|
392
|
-
const
|
|
393
|
-
|
|
408
|
+
const sum = (x: number[]) => x.reduce((a, b) => a + b, 0);
|
|
409
|
+
const max = (x: number[]) => x.reduce((a, b) => Math.max(a, b), 0);
|
|
410
|
+
|
|
411
|
+
const totalSeconds = await metrics.ponder_historical_total_indexing_seconds
|
|
412
|
+
.get()
|
|
413
|
+
.then(({ values }) => values.map(({ value }) => value))
|
|
414
|
+
.then(sum);
|
|
415
|
+
const cachedSeconds = await metrics.ponder_historical_cached_indexing_seconds
|
|
416
|
+
.get()
|
|
417
|
+
.then(({ values }) => values.map(({ value }) => value))
|
|
418
|
+
.then(sum);
|
|
394
419
|
const completedSeconds =
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
420
|
+
await metrics.ponder_historical_completed_indexing_seconds
|
|
421
|
+
.get()
|
|
422
|
+
.then(({ values }) => values.map(({ value }) => value))
|
|
423
|
+
.then(sum);
|
|
424
|
+
const timestamp = await metrics.ponder_indexing_timestamp
|
|
425
|
+
.get()
|
|
426
|
+
.then(({ values }) => values.map(({ value }) => value))
|
|
427
|
+
.then(max);
|
|
400
428
|
|
|
401
|
-
const progress =
|
|
429
|
+
const progress =
|
|
430
|
+
timestamp === 0
|
|
431
|
+
? 0
|
|
432
|
+
: totalSeconds === 0
|
|
433
|
+
? 1
|
|
434
|
+
: (completedSeconds + cachedSeconds) / totalSeconds;
|
|
402
435
|
|
|
403
436
|
const indexingCompletedEventsMetric = (
|
|
404
437
|
await metrics.ponder_indexing_completed_events.get()
|
|
@@ -433,10 +466,10 @@ export async function getIndexingProgress(metrics: MetricsService) {
|
|
|
433
466
|
return {
|
|
434
467
|
hasError,
|
|
435
468
|
overall: {
|
|
436
|
-
completedSeconds,
|
|
437
469
|
totalSeconds,
|
|
470
|
+
cachedSeconds,
|
|
471
|
+
completedSeconds,
|
|
438
472
|
progress,
|
|
439
|
-
completedToTimestamp,
|
|
440
473
|
totalEvents,
|
|
441
474
|
},
|
|
442
475
|
events,
|
|
@@ -444,86 +477,25 @@ export async function getIndexingProgress(metrics: MetricsService) {
|
|
|
444
477
|
}
|
|
445
478
|
|
|
446
479
|
export async function getAppProgress(metrics: MetricsService): Promise<{
|
|
447
|
-
mode: "historical" | "realtime" |
|
|
480
|
+
mode: "historical" | "realtime" | undefined;
|
|
448
481
|
progress: number;
|
|
449
482
|
eta: number | undefined;
|
|
450
483
|
}> {
|
|
451
|
-
const sync = await getSyncProgress(metrics);
|
|
452
484
|
const indexing = await getIndexingProgress(metrics);
|
|
453
|
-
const decodingSum = await metrics.ponder_indexing_abi_decoding_duration
|
|
454
|
-
.get()
|
|
455
|
-
.then(
|
|
456
|
-
(m) =>
|
|
457
|
-
m.values.find(
|
|
458
|
-
(v) => v.metricName === "ponder_indexing_abi_decoding_duration_sum",
|
|
459
|
-
)?.value,
|
|
460
|
-
);
|
|
461
|
-
const getEventsSum = await metrics.ponder_database_method_duration
|
|
462
|
-
.get()
|
|
463
|
-
.then(
|
|
464
|
-
(m) =>
|
|
465
|
-
m.values.find(
|
|
466
|
-
(v) =>
|
|
467
|
-
v.labels.method === "getEvents" &&
|
|
468
|
-
v.metricName === "ponder_database_method_duration_sum",
|
|
469
|
-
)?.value,
|
|
470
|
-
);
|
|
471
|
-
const indexingSum = indexing.events.reduce(
|
|
472
|
-
(acc, cur) => acc + cur.averageDuration * cur.count,
|
|
473
|
-
0,
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
let maxSync: (typeof sync)[number] | undefined;
|
|
477
|
-
for (const networkSync of sync) {
|
|
478
|
-
if (
|
|
479
|
-
maxSync === undefined ||
|
|
480
|
-
maxSync.eta === undefined ||
|
|
481
|
-
(networkSync.eta && networkSync.eta > maxSync.eta)
|
|
482
|
-
) {
|
|
483
|
-
maxSync = networkSync;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
485
|
|
|
487
486
|
const remainingSeconds =
|
|
488
|
-
indexing.overall.totalSeconds -
|
|
487
|
+
indexing.overall.totalSeconds -
|
|
488
|
+
(indexing.overall.completedSeconds + indexing.overall.cachedSeconds);
|
|
489
|
+
const elapsedSeconds = (Date.now() - metrics.start_timestamp) / 1_000;
|
|
489
490
|
|
|
490
|
-
const
|
|
491
|
+
const eta =
|
|
491
492
|
indexing.overall.completedSeconds === 0
|
|
492
|
-
? undefined
|
|
493
|
-
: (((decodingSum ?? 0) + (getEventsSum ?? 0) + indexingSum) *
|
|
494
|
-
remainingSeconds) /
|
|
495
|
-
indexing.overall.completedSeconds;
|
|
496
|
-
|
|
497
|
-
const eta = sync.every((n) => n.progress === 1)
|
|
498
|
-
? indexingEta
|
|
499
|
-
: maxSync?.eta === undefined && indexingEta === undefined
|
|
500
|
-
? undefined
|
|
501
|
-
: maxSync?.eta === undefined && maxSync?.progress !== undefined
|
|
502
|
-
? undefined
|
|
503
|
-
: Math.max(maxSync?.eta ?? 0, indexingEta ?? 0);
|
|
504
|
-
|
|
505
|
-
// Edge case: If all matched events occurred in the same unix timestamp (second), progress will
|
|
506
|
-
// be zero, even though indexing is complete. When this happens, totalEvents will be non-zero.
|
|
507
|
-
const indexingProgress =
|
|
508
|
-
indexing.overall.progress === 0 && indexing.overall.totalEvents > 0
|
|
509
|
-
? 1
|
|
510
|
-
: indexing.overall.progress;
|
|
511
|
-
|
|
512
|
-
const progress = sync.every((n) => n.progress === 1)
|
|
513
|
-
? indexingProgress
|
|
514
|
-
: maxSync?.progress === undefined
|
|
515
493
|
? 0
|
|
516
|
-
:
|
|
494
|
+
: (elapsedSeconds / indexing.overall.completedSeconds) * remainingSeconds;
|
|
517
495
|
|
|
518
496
|
return {
|
|
519
|
-
mode:
|
|
520
|
-
|
|
521
|
-
: sync.every((n) => n.status === "complete")
|
|
522
|
-
? "complete"
|
|
523
|
-
: sync.length === 0
|
|
524
|
-
? undefined
|
|
525
|
-
: "historical",
|
|
526
|
-
progress,
|
|
497
|
+
mode: indexing.overall.progress === 1 ? "realtime" : "historical",
|
|
498
|
+
progress: indexing.overall.progress,
|
|
527
499
|
eta,
|
|
528
500
|
};
|
|
529
501
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type Shutdown = {
|
|
2
|
+
add: (callback: () => unknown | Promise<unknown>) => void;
|
|
3
|
+
kill: () => Promise<void>;
|
|
4
|
+
isKilled: boolean;
|
|
5
|
+
abortController: AbortController;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const createShutdown = (): Shutdown => {
|
|
9
|
+
const abortController = new AbortController();
|
|
10
|
+
const callbacks: (() => unknown | Promise<unknown>)[] = [];
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
add: (callback) => {
|
|
14
|
+
callbacks.push(callback);
|
|
15
|
+
},
|
|
16
|
+
kill: async () => {
|
|
17
|
+
abortController.abort();
|
|
18
|
+
await Promise.all(callbacks.map((callback) => callback()));
|
|
19
|
+
},
|
|
20
|
+
get isKilled() {
|
|
21
|
+
return abortController.signal.aborted;
|
|
22
|
+
},
|
|
23
|
+
abortController,
|
|
24
|
+
};
|
|
25
|
+
};
|