envio 3.3.0-rc.0 → 3.3.1-rc.0

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.
Files changed (54) hide show
  1. package/package.json +6 -7
  2. package/src/ChainFetching.res +4 -32
  3. package/src/ChainFetching.res.mjs +2 -12
  4. package/src/ChainState.res +73 -53
  5. package/src/ChainState.res.mjs +61 -30
  6. package/src/ChainState.resi +13 -17
  7. package/src/Config.res +5 -0
  8. package/src/Config.res.mjs +1 -0
  9. package/src/CrossChainState.res +4 -10
  10. package/src/CrossChainState.res.mjs +7 -9
  11. package/src/CrossChainState.resi +1 -0
  12. package/src/EffectState.res +183 -44
  13. package/src/EffectState.res.mjs +134 -26
  14. package/src/EffectState.resi +26 -3
  15. package/src/EventProcessing.res +14 -9
  16. package/src/EventProcessing.res.mjs +7 -7
  17. package/src/FetchState.res +18 -29
  18. package/src/FetchState.res.mjs +20 -37
  19. package/src/IndexerState.res +280 -31
  20. package/src/IndexerState.res.mjs +234 -25
  21. package/src/IndexerState.resi +22 -0
  22. package/src/LoadLayer.res +19 -65
  23. package/src/LoadLayer.res.mjs +13 -55
  24. package/src/Main.res +42 -23
  25. package/src/Main.res.mjs +32 -35
  26. package/src/Metrics.res +943 -66
  27. package/src/Metrics.res.mjs +367 -50
  28. package/src/Persistence.res +3 -0
  29. package/src/PgStorage.res +3 -8
  30. package/src/PgStorage.res.mjs +3 -4
  31. package/src/PruneStaleHistory.res +1 -1
  32. package/src/PruneStaleHistory.res.mjs +1 -2
  33. package/src/Rollback.res +4 -5
  34. package/src/Rollback.res.mjs +2 -3
  35. package/src/SimulateItems.res.mjs +3 -21
  36. package/src/TestIndexer.res.mjs +4 -23
  37. package/src/TestIndexerProxyStorage.res +1 -0
  38. package/src/TestIndexerProxyStorage.res.mjs +1 -1
  39. package/src/Writing.res +3 -5
  40. package/src/Writing.res.mjs +3 -6
  41. package/src/bindings/ClickHouse.res +101 -17
  42. package/src/bindings/ClickHouse.res.mjs +58 -19
  43. package/src/bindings/NodeJs.res +64 -0
  44. package/src/bindings/NodeJs.res.mjs +6 -0
  45. package/src/sources/HyperSyncClient.res +5 -11
  46. package/src/sources/SourceManager.res +72 -27
  47. package/src/sources/SourceManager.res.mjs +81 -11
  48. package/src/sources/SourceManager.resi +14 -0
  49. package/src/tui/Tui.res +1 -1
  50. package/src/tui/Tui.res.mjs +2 -2
  51. package/src/Prometheus.res +0 -789
  52. package/src/Prometheus.res.mjs +0 -847
  53. package/src/bindings/PromClient.res +0 -72
  54. package/src/bindings/PromClient.res.mjs +0 -38
@@ -1,75 +1,392 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as V8 from "v8";
3
4
  import * as Utils from "./Utils.res.mjs";
4
- import * as ChainState from "./ChainState.res.mjs";
5
- import * as PromClient from "prom-client";
6
- import * as IndexerState from "./IndexerState.res.mjs";
7
- import * as SourceManager from "./sources/SourceManager.res.mjs";
5
+ import * as Process from "process";
6
+ import * as Perf_hooks from "perf_hooks";
7
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
8
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
9
  import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
10
 
10
- let indexingAddressesName = "envio_indexing_addresses";
11
+ function formatValue(value) {
12
+ return (Math.round(value * 1000) / 1000).toString();
13
+ }
14
+
15
+ function escapeLabelValue(value) {
16
+ if (value.includes("\\") || value.includes("\"") || value.includes("\n")) {
17
+ return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n");
18
+ } else {
19
+ return value;
20
+ }
21
+ }
22
+
23
+ function block(b, name, help, kind) {
24
+ let header = `# HELP ` + name + ` ` + help + `\n# TYPE ` + name + ` ` + kind;
25
+ b.out = b.out === "" ? header : b.out + "\n\n" + header;
26
+ }
27
+
28
+ function sample(b, name, labelsOpt, value) {
29
+ let labels = labelsOpt !== undefined ? labelsOpt : "";
30
+ b.out = b.out + "\n" + name + labels + " " + (Math.round(value * 1000) / 1000).toString();
31
+ }
32
+
33
+ function series(b, name, help, kind, entries, value) {
34
+ block(b, name, help, kind);
35
+ for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
36
+ let match = entries[i];
37
+ sample(b, name, match[0], value(match[1]));
38
+ }
39
+ }
40
+
41
+ function seriesOpt(b, name, help, kind, entries, value) {
42
+ block(b, name, help, kind);
43
+ for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
44
+ let match = entries[i];
45
+ let value$1 = value(match[1]);
46
+ if (value$1 !== undefined) {
47
+ sample(b, name, match[0], value$1);
48
+ }
49
+ }
50
+ }
11
51
 
12
- let indexingAddressesHelp = "The number of addresses indexed on chain. Includes both static and dynamic addresses.";
52
+ function single(b, name, help, kind, value) {
53
+ block(b, name, help, kind);
54
+ sample(b, name, undefined, value);
55
+ }
13
56
 
14
- function renderGauge(name, help, chains, value) {
15
- let out = {
16
- contents: `# HELP ` + name + ` ` + help + `\n# TYPE ` + name + ` gauge`
57
+ function renderMetrics(b, metrics) {
58
+ let chains = metrics.chains.map(m => [
59
+ `{chainId="` + m.chainId.toString() + `"}`,
60
+ m
61
+ ]);
62
+ let handlers = metrics.handlers.map(s => [
63
+ `{contract="` + escapeLabelValue(s.contract) + `",event="` + escapeLabelValue(s.event) + `"}`,
64
+ s
65
+ ]);
66
+ let effects = metrics.effects.map(s => [
67
+ `{effect="` + escapeLabelValue(s.effect) + `",scope="` + escapeLabelValue(s.scope) + `"}`,
68
+ s
69
+ ]);
70
+ let storageLoads = metrics.storageLoads.map(s => [
71
+ `{operation="` + escapeLabelValue(s.operation) + `",storage="` + escapeLabelValue(s.storage) + `"}`,
72
+ s
73
+ ]);
74
+ let storageWrites = metrics.storageWrites.map(s => [
75
+ `{storage="` + escapeLabelValue(s.storage) + `"}`,
76
+ s
77
+ ]);
78
+ let historyPrunes = metrics.historyPrunes.map(s => [
79
+ `{entity="` + escapeLabelValue(s.entity) + `"}`,
80
+ s
81
+ ]);
82
+ let byLabels = {};
83
+ metrics.sourceRequests.forEach(s => {
84
+ let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + s.chainId.toString() + `",method="` + escapeLabelValue(s.method) + `"}`;
85
+ let existing = byLabels[labels];
86
+ if (existing !== undefined) {
87
+ byLabels[labels] = {
88
+ source: existing.source,
89
+ chainId: existing.chainId,
90
+ method: existing.method,
91
+ count: existing.count + s.count | 0,
92
+ seconds: existing.seconds + s.seconds
93
+ };
94
+ } else {
95
+ byLabels[labels] = s;
96
+ }
97
+ });
98
+ let sourceRequests = Object.entries(byLabels);
99
+ let byLabels$1 = {};
100
+ metrics.sourceHeights.forEach(s => {
101
+ let labels = `{source="` + escapeLabelValue(s.source) + `",chainId="` + s.chainId.toString() + `"}`;
102
+ let existing = byLabels$1[labels];
103
+ if (existing !== undefined && existing >= s.height) {
104
+ return;
105
+ } else {
106
+ byLabels$1[labels] = s.height;
107
+ return;
108
+ }
109
+ });
110
+ let sources = Object.entries(byLabels$1);
111
+ single(b, "envio_preload_seconds", "Cumulative time spent on preloading entities during batch processing.", "counter", metrics.preloadSeconds);
112
+ single(b, "envio_processing_seconds", "Cumulative time spent executing event handlers during batch processing.", "counter", metrics.processingSeconds);
113
+ series(b, "envio_progress_ready", "Whether the chain is fully synced to the head.", "gauge", chains, m => {
114
+ if (m.isReady) {
115
+ return 1;
116
+ } else {
117
+ return 0;
118
+ }
119
+ });
120
+ single(b, "hyperindex_synced_to_head", "All chains fully synced", "gauge", Utils.$$Array.notEmpty(metrics.chains) && metrics.chains.every(m => m.isReady) ? 1 : 0);
121
+ series(b, "envio_processing_handler_seconds", "Cumulative time spent inside individual event handler executions.", "counter", handlers, s => s.processingSeconds);
122
+ series(b, "envio_processing_handler_total", "Total number of individual event handler executions.", "counter", handlers, s => s.processingCount);
123
+ series(b, "envio_preload_handler_seconds", "Wall-clock time spent inside individual preload handler executions.", "counter", handlers, s => s.preloadSeconds);
124
+ series(b, "envio_preload_handler_total", "Total number of individual preload handler executions.", "counter", handlers, s => s.preloadCount);
125
+ series(b, "envio_preload_handler_seconds_total", "Cumulative time spent inside individual preload handler executions. Can exceed wall-clock time due to parallel execution.", "counter", handlers, s => s.preloadSecondsTotal);
126
+ series(b, "envio_fetching_block_range_seconds", "Cumulative time spent fetching block ranges.", "counter", chains, m => m.blockRangeFetchSeconds);
127
+ series(b, "envio_fetching_block_range_parse_seconds", "Cumulative time spent parsing block range fetch responses.", "counter", chains, m => m.blockRangeParseSeconds);
128
+ series(b, "envio_fetching_block_range_total", "Total number of block range fetch operations.", "counter", chains, m => m.blockRangeFetchCount);
129
+ series(b, "envio_fetching_block_range_events_total", "Cumulative number of events fetched across all block range operations.", "counter", chains, m => m.blockRangeFetchedEvents);
130
+ series(b, "envio_fetching_block_range_size", "Cumulative number of blocks covered across all block range fetch operations.", "counter", chains, m => m.blockRangeFetchedBlocks);
131
+ series(b, "envio_indexing_known_height", "The latest known block number reported by the active indexing source. This value may lag behind the actual chain height, as it is updated only when needed.", "gauge", chains, m => m.sourceBlockNumber);
132
+ single(b, "envio_process_start_time_seconds", "Start time of the process since unix epoch in seconds.", "gauge", metrics.startTime.getTime() / 1000);
133
+ series(b, "envio_indexing_concurrency", "The number of executing concurrent queries to the chain data-source.", "gauge", chains, m => m.concurrency);
134
+ series(b, "envio_indexing_partitions", "The number of partitions used to split fetching logic by addresses and block ranges.", "gauge", chains, m => m.partitionsCount);
135
+ series(b, "envio_indexing_idle_seconds", "The time the indexer source syncing has been idle. A high value may indicate the source sync is a bottleneck.", "counter", chains, m => m.idleSeconds);
136
+ series(b, "envio_indexing_source_waiting_seconds", "The time the indexer has been waiting for new blocks.", "counter", chains, m => m.waitingForNewBlockSeconds);
137
+ series(b, "envio_indexing_source_querying_seconds", "The time spent performing queries to the chain data-source.", "counter", chains, m => m.queryingSeconds);
138
+ series(b, "envio_indexing_buffer_size", "The current number of items in the indexing buffer.", "gauge", chains, m => m.bufferSize);
139
+ single(b, "envio_indexing_target_buffer_size", "The indexer-wide target buffer size shared across all chains. The actual number of items in the queue may exceed this value, but the indexer always tries to keep the buffer filled up to this target.", "gauge", metrics.targetBufferSize);
140
+ series(b, "envio_indexing_buffer_block", "The highest block number that has been fully fetched by the indexer.", "gauge", chains, m => m.bufferBlockNumber);
141
+ seriesOpt(b, "envio_indexing_end_block", "The block number to stop indexing at. (inclusive)", "gauge", chains, m => Stdlib_Option.map(m.endBlock, prim => prim));
142
+ series(b, "envio_source_request_total", "The number of requests made to data sources.", "counter", sourceRequests, s => s.count);
143
+ seriesOpt(b, "envio_source_request_seconds_total", "Cumulative time spent on data source requests.", "counter", sourceRequests, s => {
144
+ if (s.seconds !== 0) {
145
+ return s.seconds;
146
+ }
147
+ });
148
+ series(b, "envio_source_known_height", "The latest known block number reported by the source. This value may lag behind the actual chain height, as it is updated only when queried.", "gauge", sources, height => height);
149
+ seriesOpt(b, "envio_reorg_detected_total", "Total number of reorgs detected", "counter", chains, m => {
150
+ if (m.reorgCount > 0) {
151
+ return m.reorgCount;
152
+ }
153
+ });
154
+ seriesOpt(b, "envio_reorg_detected_block", "The block number where reorg was detected the last time. This doesn't mean that the block was reorged, this is simply where we found block hash to be different.", "gauge", chains, m => Stdlib_Option.map(m.reorgDetectedBlock, prim => prim));
155
+ single(b, "envio_reorg_threshold", "Whether indexing is currently within the reorg threshold", "gauge", metrics.isInReorgThreshold ? 1 : 0);
156
+ single(b, "envio_rollback_enabled", "Whether rollback on reorg is enabled", "gauge", metrics.rollbackEnabled ? 1 : 0);
157
+ single(b, "envio_rollback_seconds", "Rollback on reorg total time.", "counter", metrics.rollbackSeconds);
158
+ single(b, "envio_rollback_total", "Number of successful rollbacks on reorg", "counter", metrics.rollbackCount);
159
+ single(b, "envio_rollback_events", "Number of events rollbacked on reorg", "counter", metrics.rollbackEventsCount);
160
+ series(b, "envio_rollback_history_prune_seconds", "The total time spent pruning entity history which is not in the reorg threshold.", "counter", historyPrunes, s => s.seconds);
161
+ series(b, "envio_rollback_history_prune_total", "Number of successful entity history prunes", "counter", historyPrunes, s => s.count);
162
+ seriesOpt(b, "envio_rollback_target_block", "The block number reorg was rollbacked to the last time.", "gauge", chains, m => Stdlib_Option.map(m.rollbackTargetBlock, prim => prim));
163
+ single(b, "envio_processing_max_batch_size", "The maximum number of items to process in a single batch.", "gauge", metrics.maxBatchSize);
164
+ series(b, "envio_progress_block", "The block number of the latest block processed and stored in the database.", "gauge", chains, m => m.progressBlockNumber);
165
+ series(b, "envio_progress_events", "The number of events processed and reflected in the database.", "gauge", chains, m => m.numEventsProcessed);
166
+ seriesOpt(b, "envio_progress_latency", "The latency in milliseconds between the latest processed event creation and the time it was written to storage.", "gauge", chains, m => Stdlib_Option.map(m.progressLatencyMs, prim => prim));
167
+ let ifCalled = (s, value) => {
168
+ if (s.callCount > 0 || s.activeCallsCount > 0) {
169
+ return Primitive_option.some(value);
170
+ }
17
171
  };
18
- let prefix = `\n` + name + `{chainId="`;
19
- Utils.Dict.forEachWithKey(chains, (chain, chainId) => {
20
- out.contents = out.contents + prefix + chainId + `"} ` + value(chain).toString();
172
+ seriesOpt(b, "envio_effect_call_seconds", "Processing time taken to call the Effect function.", "counter", effects, s => ifCalled(s, s.callSeconds));
173
+ seriesOpt(b, "envio_effect_call_seconds_total", "Cumulative time spent calling the Effect function during the indexing process.", "counter", effects, s => ifCalled(s, s.callSecondsTotal));
174
+ seriesOpt(b, "envio_effect_call_total", "Cumulative number of resolved Effect function calls during the indexing process.", "counter", effects, s => ifCalled(s, s.callCount));
175
+ seriesOpt(b, "envio_effect_active_calls", "The number of Effect function calls that are currently running.", "gauge", effects, s => ifCalled(s, s.activeCallsCount));
176
+ seriesOpt(b, "envio_effect_cache", "The number of items in the effect cache.", "gauge", effects, s => Stdlib_Option.map(s.cacheCount, prim => prim));
177
+ let byEffect = {};
178
+ metrics.effects.forEach(s => {
179
+ let match = byEffect[s.effect];
180
+ if (match !== undefined) {
181
+ byEffect[s.effect] = [
182
+ match[0] + s.invalidationsCount,
183
+ match[1] + s.queueWaitSeconds
184
+ ];
185
+ } else {
186
+ byEffect[s.effect] = [
187
+ s.invalidationsCount,
188
+ s.queueWaitSeconds
189
+ ];
190
+ }
191
+ });
192
+ let entries = [];
193
+ Utils.Dict.forEachWithKey(byEffect, (totals, effect) => {
194
+ entries.push([
195
+ `{effect="` + escapeLabelValue(effect) + `"}`,
196
+ totals
197
+ ]);
198
+ });
199
+ seriesOpt(b, "envio_effect_cache_invalidations", "The number of effect cache invalidations.", "counter", entries, param => {
200
+ let invalidations = param[0];
201
+ if (invalidations > 0) {
202
+ return invalidations;
203
+ }
204
+ });
205
+ seriesOpt(b, "envio_effect_queue", "The number of effect calls waiting in the rate limit queue.", "gauge", effects, s => {
206
+ if (s.queueCount > 0 || s.queueWaitSeconds > 0) {
207
+ return s.queueCount;
208
+ }
21
209
  });
22
- return out.contents;
210
+ seriesOpt(b, "envio_effect_queue_wait_seconds", "The time spent waiting in the rate limit queue.", "counter", entries, param => {
211
+ let queueWaitSeconds = param[1];
212
+ if (queueWaitSeconds !== 0) {
213
+ return queueWaitSeconds;
214
+ }
215
+ });
216
+ series(b, "envio_storage_load_seconds", "Processing time taken to load data from storage.", "counter", storageLoads, s => s.seconds);
217
+ series(b, "envio_storage_load_seconds_total", "Cumulative time spent loading data from storage during the indexing process.", "counter", storageLoads, s => s.secondsTotal);
218
+ series(b, "envio_storage_load_total", "Cumulative number of successful storage load operations during the indexing process.", "counter", storageLoads, s => s.count);
219
+ series(b, "envio_storage_load_where_size", "Cumulative number of filter conditions ('where' items) used in storage load operations during the indexing process.", "counter", storageLoads, s => s.whereSize);
220
+ series(b, "envio_storage_load_size", "Cumulative number of records loaded from storage during the indexing process.", "counter", storageLoads, s => s.size);
221
+ series(b, "envio_storage_write_seconds", "Cumulative time spent writing batch data to storage.", "counter", storageWrites, s => s.seconds);
222
+ series(b, "envio_storage_write_total", "Cumulative number of successful storage write operations during the indexing process.", "counter", storageWrites, s => s.count);
223
+ series(b, "envio_indexing_addresses", "The number of addresses indexed on chain. Includes both static and dynamic addresses.", "gauge", chains, m => m.numAddresses);
23
224
  }
24
225
 
25
- let sourceRequestTotalName = "envio_source_request_total";
26
-
27
- let sourceRequestTotalHelp = "The number of requests made to data sources.";
226
+ function collect(metrics) {
227
+ let b = {
228
+ out: ""
229
+ };
230
+ series(b, "envio_info", "Information about the indexer", "gauge", [[
231
+ `{version="` + escapeLabelValue(Utils.EnvioPackage.value.version) + `"}`,
232
+ undefined
233
+ ]], () => 1);
234
+ if (metrics !== undefined) {
235
+ renderMetrics(b, metrics);
236
+ }
237
+ return b.out + "\n";
238
+ }
28
239
 
29
- let sourceRequestSecondsTotalName = "envio_source_request_seconds_total";
240
+ function gcKindName(kind) {
241
+ switch (kind) {
242
+ case 1 :
243
+ return "minor";
244
+ case 4 :
245
+ return "major";
246
+ case 8 :
247
+ return "incremental";
248
+ case 16 :
249
+ return "weakcb";
250
+ default:
251
+ return "unknown";
252
+ }
253
+ }
30
254
 
31
- let sourceRequestSecondsTotalHelp = "Cumulative time spent on data source requests.";
255
+ let runtimeCollectors = {
256
+ contents: undefined
257
+ };
32
258
 
33
- function renderSourceRequests(samples) {
34
- if (Utils.$$Array.isEmpty(samples)) {
35
- return "";
259
+ function getRuntimeCollectors() {
260
+ let collectors = runtimeCollectors.contents;
261
+ if (collectors !== undefined) {
262
+ return collectors;
36
263
  }
37
- let countOut = {
38
- contents: `\n# HELP ` + sourceRequestTotalName + ` ` + sourceRequestTotalHelp + `\n# TYPE ` + sourceRequestTotalName + ` counter`
264
+ let eventLoopDelay = Perf_hooks.monitorEventLoopDelay({
265
+ resolution: 10
266
+ });
267
+ eventLoopDelay.enable();
268
+ let gcStats = {};
269
+ let observer = new Perf_hooks.PerformanceObserver(list => {
270
+ list.getEntries().forEach(entry => {
271
+ let kind = gcKindName(entry.detail.kind);
272
+ let stat = gcStats[kind];
273
+ let stat$1;
274
+ if (stat !== undefined) {
275
+ stat$1 = stat;
276
+ } else {
277
+ let stat$2 = {
278
+ count: 0,
279
+ seconds: 0
280
+ };
281
+ gcStats[kind] = stat$2;
282
+ stat$1 = stat$2;
283
+ }
284
+ stat$1.count = stat$1.count + 1;
285
+ stat$1.seconds = stat$1.seconds + entry.duration / 1000;
286
+ });
287
+ });
288
+ observer.observe({
289
+ entryTypes: ["gc"]
290
+ });
291
+ let collectors_processStartTimeSeconds = Date.now() / 1000 - Process.uptime();
292
+ let collectors$1 = {
293
+ eventLoopDelay: eventLoopDelay,
294
+ gcStats: gcStats,
295
+ processStartTimeSeconds: collectors_processStartTimeSeconds
39
296
  };
40
- let secondsOut = {
41
- contents: `\n# HELP ` + sourceRequestSecondsTotalName + ` ` + sourceRequestSecondsTotalHelp + `\n# TYPE ` + sourceRequestSecondsTotalName + ` counter`
297
+ runtimeCollectors.contents = collectors$1;
298
+ return collectors$1;
299
+ }
300
+
301
+ function startRuntimeCollectors() {
302
+ getRuntimeCollectors();
303
+ }
304
+
305
+ function collectRuntime() {
306
+ let b = {
307
+ out: ""
42
308
  };
43
- samples.forEach(sample => {
44
- let labels = `{source="` + sample.sourceName + `",chainId="` + sample.chainId.toString() + `",method="` + sample.method + `"}`;
45
- countOut.contents = countOut.contents + (`\n` + sourceRequestTotalName + labels + ` ` + sample.count.toString());
46
- if (sample.seconds !== 0) {
47
- secondsOut.contents = secondsOut.contents + (`\n` + sourceRequestSecondsTotalName + labels + ` ` + sample.seconds.toFixed(3));
48
- return;
309
+ let memory = Process.memoryUsage();
310
+ let cpu = Process.cpuUsage();
311
+ let elu = Perf_hooks.performance.eventLoopUtilization();
312
+ let match = getRuntimeCollectors();
313
+ let eventLoopDelay = match.eventLoopDelay;
314
+ single(b, "process_cpu_user_seconds_total", "Total user CPU time spent in seconds.", "counter", cpu.user / 1000000);
315
+ single(b, "process_cpu_system_seconds_total", "Total system CPU time spent in seconds.", "counter", cpu.system / 1000000);
316
+ single(b, "process_cpu_seconds_total", "Total user and system CPU time spent in seconds.", "counter", (cpu.user + cpu.system) / 1000000);
317
+ single(b, "process_start_time_seconds", "Start time of the process since unix epoch in seconds.", "gauge", match.processStartTimeSeconds);
318
+ single(b, "process_resident_memory_bytes", "Resident memory size in bytes.", "gauge", memory.rss);
319
+ single(b, "nodejs_heap_size_total_bytes", "Process heap size from Node.js in bytes.", "gauge", memory.heapTotal);
320
+ single(b, "nodejs_heap_size_used_bytes", "Process heap size used from Node.js in bytes.", "gauge", memory.heapUsed);
321
+ single(b, "nodejs_external_memory_bytes", "Node.js external memory size in bytes.", "gauge", memory.external);
322
+ single(b, "nodejs_eventloop_utilization", "Ratio of time the event loop is active, since process start.", "gauge", elu.utilization);
323
+ let hasLagSamples = eventLoopDelay.max > 0;
324
+ let nsToSeconds = ns => {
325
+ if (hasLagSamples && !Number.isNaN(ns)) {
326
+ return ns / 1000000000;
327
+ } else {
328
+ return 0;
49
329
  }
330
+ };
331
+ single(b, "nodejs_eventloop_lag_mean_seconds", "The mean of the recorded event loop delays.", "gauge", nsToSeconds(eventLoopDelay.mean));
332
+ single(b, "nodejs_eventloop_lag_min_seconds", "The minimum recorded event loop delay.", "gauge", nsToSeconds(eventLoopDelay.min));
333
+ single(b, "nodejs_eventloop_lag_max_seconds", "The maximum recorded event loop delay.", "gauge", nsToSeconds(eventLoopDelay.max));
334
+ single(b, "nodejs_eventloop_lag_stddev_seconds", "The standard deviation of the recorded event loop delays.", "gauge", nsToSeconds(eventLoopDelay.stddev));
335
+ single(b, "nodejs_eventloop_lag_p50_seconds", "The 50th percentile of the recorded event loop delays.", "gauge", nsToSeconds(eventLoopDelay.percentile(50)));
336
+ single(b, "nodejs_eventloop_lag_p90_seconds", "The 90th percentile of the recorded event loop delays.", "gauge", nsToSeconds(eventLoopDelay.percentile(90)));
337
+ single(b, "nodejs_eventloop_lag_p99_seconds", "The 99th percentile of the recorded event loop delays.", "gauge", nsToSeconds(eventLoopDelay.percentile(99)));
338
+ eventLoopDelay.reset();
339
+ let heapSpaces = V8.getHeapSpaceStatistics().map(s => [
340
+ `{space="` + s.space_name.replace("_space", "") + `"}`,
341
+ s
342
+ ]);
343
+ series(b, "nodejs_heap_space_size_total_bytes", "Process heap space size total from Node.js in bytes.", "gauge", heapSpaces, s => s.space_size);
344
+ series(b, "nodejs_heap_space_size_used_bytes", "Process heap space size used from Node.js in bytes.", "gauge", heapSpaces, s => s.space_used_size);
345
+ series(b, "nodejs_heap_space_size_available_bytes", "Process heap space size available from Node.js in bytes.", "gauge", heapSpaces, s => s.space_available_size);
346
+ let byType = {};
347
+ Process.getActiveResourcesInfo().forEach(resource => {
348
+ let label = `{type="` + escapeLabelValue(resource) + `"}`;
349
+ byType[label] = Stdlib_Option.getOr(byType[label], 0) + 1;
50
350
  });
51
- return countOut.contents + secondsOut.contents;
351
+ let activeResources = Object.entries(byType);
352
+ series(b, "nodejs_active_resources", "Number of active resources that are currently keeping the event loop alive, grouped by async resource type.", "gauge", activeResources, count => count);
353
+ single(b, "nodejs_active_resources_total", "Total number of active resources.", "gauge", Stdlib_Array.reduce(activeResources, 0, (acc, param) => acc + param[1]));
354
+ let gcEntries = [];
355
+ Utils.Dict.forEachWithKey(match.gcStats, (stat, kind) => {
356
+ gcEntries.push([
357
+ `{kind="` + kind + `"}`,
358
+ stat
359
+ ]);
360
+ });
361
+ series(b, "nodejs_gc_duration_seconds_sum", "Cumulative garbage collection pause time by kind, one of major, minor, incremental or weakcb.", "counter", gcEntries, s => s.seconds);
362
+ series(b, "nodejs_gc_duration_seconds_count", "Number of garbage collection pauses by kind, one of major, minor, incremental or weakcb.", "counter", gcEntries, s => s.count);
363
+ let version = Process.version;
364
+ let versionParts = version.replace("v", "").split(".");
365
+ let versionPart = i => Stdlib_Option.getOr(versionParts[i], "0");
366
+ series(b, "nodejs_version_info", "Node.js version info.", "gauge", [[
367
+ `{version="` + version + `",major="` + versionPart(0) + `",minor="` + versionPart(1) + `",patch="` + versionPart(2) + `"}`,
368
+ undefined
369
+ ]], () => 1);
370
+ return b.out + "\n";
52
371
  }
53
372
 
54
- async function collect(state) {
55
- let base = await PromClient.register.metrics();
56
- if (state === undefined) {
57
- return base;
58
- }
59
- let chainStates = IndexerState.chainStates(Primitive_option.valFromOption(state));
60
- let sourceRequestSamples = Object.values(chainStates).flatMap(cs => SourceManager.getRequestStatSamples(ChainState.sourceManager(cs)));
61
- return base + renderGauge(indexingAddressesName, indexingAddressesHelp, chainStates, cs => ChainState.toChainData(cs).numAddresses) + renderSourceRequests(sourceRequestSamples) + `\n`;
62
- }
373
+ let contentType = "text/plain; version=0.0.4; charset=utf-8";
63
374
 
64
375
  export {
65
- indexingAddressesName,
66
- indexingAddressesHelp,
67
- renderGauge,
68
- sourceRequestTotalName,
69
- sourceRequestTotalHelp,
70
- sourceRequestSecondsTotalName,
71
- sourceRequestSecondsTotalHelp,
72
- renderSourceRequests,
376
+ formatValue,
377
+ escapeLabelValue,
378
+ block,
379
+ sample,
380
+ series,
381
+ seriesOpt,
382
+ single,
383
+ renderMetrics,
384
+ contentType,
73
385
  collect,
386
+ gcKindName,
387
+ runtimeCollectors,
388
+ getRuntimeCollectors,
389
+ startRuntimeCollectors,
390
+ collectRuntime,
74
391
  }
75
- /* Utils Not a pure module */
392
+ /* v8 Not a pure module */
@@ -134,6 +134,9 @@ type storage = {
134
134
  // Chain metadata stale since the last write, persisted in the same
135
135
  // transaction so it never races the batch write.
136
136
  ~chainMetaData: option<dict<InternalTable.Chains.metaFields>>,
137
+ // Reports each underlying storage's write duration (e.g. postgres and a
138
+ // configured sink separately), accumulated into the write metrics.
139
+ ~onWrite: (~storage: string, ~timeSeconds: float) => unit,
137
140
  ) => promise<unit>,
138
141
  // Release any long-lived resources (e.g. the postgres connection pool) so
139
142
  // short-lived CLI commands like `db-migrate setup` can exit cleanly.
package/src/PgStorage.res CHANGED
@@ -1763,6 +1763,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1763
1763
  ~updatedEffectsCache,
1764
1764
  ~updatedEntities,
1765
1765
  ~chainMetaData,
1766
+ ~onWrite,
1766
1767
  ) => {
1767
1768
  let pgUpdates = []
1768
1769
  let chUpdates = []
@@ -1784,10 +1785,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1784
1785
  Some(
1785
1786
  sink.writeBatch(~batch, ~updatedEntities=chUpdates)
1786
1787
  ->Promise.thenResolve(_ => {
1787
- Prometheus.StorageWrite.increment(
1788
- ~storage=sink.name,
1789
- ~timeSeconds=timerRef->Performance.secondsSince,
1790
- )
1788
+ onWrite(~storage=sink.name, ~timeSeconds=timerRef->Performance.secondsSince)
1791
1789
  None
1792
1790
  })
1793
1791
  // Otherwise it fails with unhandled exception
@@ -1812,10 +1810,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1812
1810
  ~sinkPromise,
1813
1811
  ~chainMetaData,
1814
1812
  )
1815
- Prometheus.StorageWrite.increment(
1816
- ~storage="postgres",
1817
- ~timeSeconds=primaryTimerRef->Performance.secondsSince,
1818
- )
1813
+ onWrite(~storage="postgres", ~timeSeconds=primaryTimerRef->Performance.secondsSince)
1819
1814
  }
1820
1815
 
1821
1816
  let close = () => sql->Postgres.endSql
@@ -13,7 +13,6 @@ import * as Logging from "./Logging.res.mjs";
13
13
  import * as Internal from "./Internal.res.mjs";
14
14
  import Postgres from "postgres";
15
15
  import * as ChainState from "./ChainState.res.mjs";
16
- import * as Prometheus from "./Prometheus.res.mjs";
17
16
  import * as Performance from "./bindings/Performance.res.mjs";
18
17
  import * as Persistence from "./Persistence.res.mjs";
19
18
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
@@ -1154,7 +1153,7 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1154
1153
  restoredEntitiesResult
1155
1154
  ];
1156
1155
  };
1157
- let writeBatchMethod = async (batch, rollback, isInReorgThreshold, config, allEntities, updatedEffectsCache, updatedEntities, chainMetaData) => {
1156
+ let writeBatchMethod = async (batch, rollback, isInReorgThreshold, config, allEntities, updatedEffectsCache, updatedEntities, chainMetaData, onWrite) => {
1158
1157
  let pgUpdates = [];
1159
1158
  let chUpdates = [];
1160
1159
  for (let i = 0, i_finish = updatedEntities.length; i < i_finish; ++i) {
@@ -1171,14 +1170,14 @@ SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::
1171
1170
  if (sink !== undefined) {
1172
1171
  let timerRef = Performance.now();
1173
1172
  sinkPromise = sink.writeBatch(batch, chUpdates).then(() => {
1174
- Prometheus.StorageWrite.increment(sink.name, Performance.secondsSince(timerRef));
1173
+ onWrite(sink.name, Performance.secondsSince(timerRef));
1175
1174
  }).catch(exn => exn);
1176
1175
  } else {
1177
1176
  sinkPromise = undefined;
1178
1177
  }
1179
1178
  let primaryTimerRef = Performance.now();
1180
1179
  await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, pgUpdates, sinkPromise, chainMetaData, undefined);
1181
- return Prometheus.StorageWrite.increment("postgres", Performance.secondsSince(primaryTimerRef));
1180
+ return onWrite("postgres", Performance.secondsSince(primaryTimerRef));
1182
1181
  };
1183
1182
  let close = () => sql.end();
1184
1183
  return {
@@ -106,7 +106,7 @@ let pruneEntities = async (state: IndexerState.t, ~entities, ~safeCheckpointId)
106
106
  ~safeCheckpointId,
107
107
  ) {
108
108
  | () =>
109
- Prometheus.RollbackHistoryPrune.increment(
109
+ state->IndexerState.recordHistoryPrune(
110
110
  ~timeSeconds=Performance.secondsSince(timeRef),
111
111
  ~entityName=entityConfig.name,
112
112
  )
@@ -4,7 +4,6 @@ import * as Env from "./Env.res.mjs";
4
4
  import * as Utils from "./Utils.res.mjs";
5
5
  import * as Config from "./Config.res.mjs";
6
6
  import * as Logging from "./Logging.res.mjs";
7
- import * as Prometheus from "./Prometheus.res.mjs";
8
7
  import * as Performance from "./bindings/Performance.res.mjs";
9
8
  import * as IndexerState from "./IndexerState.res.mjs";
10
9
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
@@ -80,7 +79,7 @@ async function pruneEntities(state, entities, safeCheckpointId) {
80
79
  }), Utils.prettifyExn(exn), `Failed to prune stale entity history`);
81
80
  }
82
81
  if (exit === 1) {
83
- Prometheus.RollbackHistoryPrune.increment(Performance.secondsSince(timeRef), entityConfig.name);
82
+ IndexerState.recordHistoryPrune(state, entityConfig.name, Performance.secondsSince(timeRef));
84
83
  }
85
84
  }
86
85
  }
package/src/Rollback.res CHANGED
@@ -107,10 +107,9 @@ and executeRollback = async (
107
107
  },
108
108
  )
109
109
  logger->Logging.childInfo("Started rollback on reorg")
110
- Prometheus.RollbackTargetBlockNumber.set(
111
- ~blockNumber=rollbackTargetBlockNumber,
112
- ~chain=reorgChain,
113
- )
110
+ state
111
+ ->IndexerState.getChainState(~chain=reorgChain)
112
+ ->ChainState.setRollbackTargetBlock(~blockNumber=rollbackTargetBlockNumber)
114
113
 
115
114
  let reorgChainId = reorgChain->ChainMap.Chain.toChainId
116
115
 
@@ -212,7 +211,7 @@ and executeRollback = async (
212
211
  "deleted": diff["deletedEntities"],
213
212
  "upserted": diff["setEntities"],
214
213
  })
215
- Prometheus.RollbackSuccess.increment(
214
+ state->IndexerState.recordRollbackSuccess(
216
215
  ~timeSeconds=Performance.secondsSince(startTime),
217
216
  ~rollbackedProcessedEvents=rollbackedProcessedEvents.contents,
218
217
  )
@@ -4,7 +4,6 @@ import * as Utils from "./Utils.res.mjs";
4
4
  import * as Logging from "./Logging.res.mjs";
5
5
  import * as Writing from "./Writing.res.mjs";
6
6
  import * as ChainState from "./ChainState.res.mjs";
7
- import * as Prometheus from "./Prometheus.res.mjs";
8
7
  import * as Performance from "./bindings/Performance.res.mjs";
9
8
  import * as IndexerState from "./IndexerState.res.mjs";
10
9
  import * as Stdlib_Float from "@rescript/runtime/lib/es6/Stdlib_Float.js";
@@ -39,7 +38,7 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
39
38
  targetBlockNumber: rollbackTargetBlockNumber
40
39
  });
41
40
  Logging.childInfo(logger, "Started rollback on reorg");
42
- Prometheus.RollbackTargetBlockNumber.set(rollbackTargetBlockNumber, reorgChain);
41
+ ChainState.setRollbackTargetBlock(IndexerState.getChainState(state, reorgChain), rollbackTargetBlockNumber);
43
42
  await Writing.flush(state);
44
43
  let checkpointId = await IndexerState.persistence(state).storage.getRollbackTargetCheckpoint(reorgChain, rollbackTargetBlockNumber);
45
44
  let rollbackTargetCheckpointId = checkpointId !== undefined ? checkpointId : 0n;
@@ -83,7 +82,7 @@ async function executeRollback(state, reorgChain, rollbackTargetBlockNumber, sch
83
82
  deleted: diff$1.deletedEntities,
84
83
  upserted: diff$1.setEntities
85
84
  });
86
- Prometheus.RollbackSuccess.increment(Performance.secondsSince(startTime), rollbackedProcessedEvents);
85
+ IndexerState.recordRollbackSuccess(state, Performance.secondsSince(startTime), rollbackedProcessedEvents);
87
86
  IndexerState.completeRollback(state, eventsProcessedDiffByChain);
88
87
  scheduleFetch();
89
88
  return scheduleProcessing();
@@ -323,27 +323,9 @@ function patchConfig(config, processConfig, registrationsByChainId) {
323
323
  };
324
324
  return newrecord$1;
325
325
  });
326
- return {
327
- name: config.name,
328
- description: config.description,
329
- handlers: config.handlers,
330
- contractHandlers: config.contractHandlers,
331
- shouldRollbackOnReorg: config.shouldRollbackOnReorg,
332
- shouldSaveFullHistory: config.shouldSaveFullHistory,
333
- storage: config.storage,
334
- chainMap: newChainMap,
335
- defaultChain: config.defaultChain,
336
- ecosystem: config.ecosystem,
337
- enableRawEvents: config.enableRawEvents,
338
- maxAddrInPartition: config.maxAddrInPartition,
339
- batchSize: config.batchSize,
340
- lowercaseAddresses: config.lowercaseAddresses,
341
- isDev: config.isDev,
342
- userEntitiesByName: config.userEntitiesByName,
343
- userEntities: config.userEntities,
344
- allEntities: config.allEntities,
345
- allEnums: config.allEnums
346
- };
326
+ let newrecord = {...config};
327
+ newrecord.chainMap = newChainMap;
328
+ return newrecord;
347
329
  }
348
330
 
349
331
  export {