envio 3.12.1 → 3.13.0-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 (103) hide show
  1. package/package.json +6 -8
  2. package/src/BatchProcessing.res +15 -14
  3. package/src/BatchProcessing.res.mjs +8 -15
  4. package/src/Bin.res +24 -17
  5. package/src/Bin.res.mjs +5 -0
  6. package/src/ChainFetching.res +18 -24
  7. package/src/ChainFetching.res.mjs +14 -15
  8. package/src/ChainState.res +103 -38
  9. package/src/ChainState.res.mjs +79 -22
  10. package/src/ChainState.resi +14 -2
  11. package/src/Config.res +32 -0
  12. package/src/Config.res.mjs +24 -0
  13. package/src/Core.res +4 -0
  14. package/src/Core.res.mjs +4 -0
  15. package/src/CrossChainState.res +109 -11
  16. package/src/CrossChainState.res.mjs +71 -7
  17. package/src/CrossChainState.resi +11 -1
  18. package/src/Env.res +4 -0
  19. package/src/ErrorHandling.res +15 -4
  20. package/src/ErrorHandling.res.mjs +16 -5
  21. package/src/FinalizeBackfill.res +10 -4
  22. package/src/FinalizeBackfill.res.mjs +5 -3
  23. package/src/HandlerRegister.res +0 -37
  24. package/src/HandlerRegister.res.mjs +0 -18
  25. package/src/IndexerLoop.res +2 -0
  26. package/src/IndexerLoop.res.mjs +1 -0
  27. package/src/IndexerState.res +43 -1
  28. package/src/IndexerState.res.mjs +48 -4
  29. package/src/IndexerState.resi +11 -0
  30. package/src/Main.res +128 -268
  31. package/src/Main.res.mjs +27 -153
  32. package/src/Metrics.res +268 -102
  33. package/src/Metrics.res.mjs +227 -48
  34. package/src/Persistence.res +30 -2
  35. package/src/Persistence.res.mjs +12 -3
  36. package/src/PgStorage.res +19 -11
  37. package/src/PgStorage.res.mjs +13 -10
  38. package/src/Rollback.res +3 -3
  39. package/src/Server.res +178 -0
  40. package/src/Server.res.mjs +143 -0
  41. package/src/SimulateItems.res +1 -1
  42. package/src/SimulateItems.res.mjs +1 -1
  43. package/src/Supervisor.res +542 -0
  44. package/src/Supervisor.res.mjs +455 -0
  45. package/src/TestIndexer.res +1 -1
  46. package/src/TestIndexer.res.mjs +2 -2
  47. package/src/Worker.res +101 -0
  48. package/src/Worker.res.mjs +81 -0
  49. package/src/bindings/NodeJs.res +50 -0
  50. package/src/bindings/NodeJs.res.mjs +8 -1
  51. package/src/db/InternalTable.res +8 -1
  52. package/src/db/InternalTable.res.mjs +5 -1
  53. package/src/sources/AddressSet.res +5 -3
  54. package/src/sources/AddressStore.res +19 -11
  55. package/src/sources/AddressStore.res.mjs +2 -7
  56. package/src/sources/BlockStore.res +10 -9
  57. package/src/sources/BlockStore.res.mjs +4 -4
  58. package/src/sources/ChainSources.res +4 -0
  59. package/src/sources/ChainSources.res.mjs +2 -2
  60. package/src/sources/EvmChain.res +4 -0
  61. package/src/sources/EvmChain.res.mjs +3 -1
  62. package/src/sources/EvmEventItem.res +56 -0
  63. package/src/sources/EvmEventItem.res.mjs +32 -0
  64. package/src/sources/EvmHyperSyncSource.res +7 -37
  65. package/src/sources/EvmHyperSyncSource.res.mjs +8 -29
  66. package/src/sources/EvmRpcClient.res +41 -59
  67. package/src/sources/EvmRpcClient.res.mjs +4 -54
  68. package/src/sources/EvmRpcWs.res +4 -1
  69. package/src/sources/EvmRpcWs.res.mjs +6 -2
  70. package/src/sources/FuelHyperSyncSource.res +6 -4
  71. package/src/sources/FuelHyperSyncSource.res.mjs +7 -9
  72. package/src/sources/HeightFeed.res +7 -1
  73. package/src/sources/HeightFeed.res.mjs +9 -7
  74. package/src/sources/HyperSyncClient.res +1 -14
  75. package/src/sources/RequestStat.res +7 -0
  76. package/src/sources/RequestStat.res.mjs +14 -1
  77. package/src/sources/RpcSource.res +100 -1043
  78. package/src/sources/RpcSource.res.mjs +81 -1056
  79. package/src/sources/SimulateSource.res +2 -5
  80. package/src/sources/SimulateSource.res.mjs +2 -3
  81. package/src/sources/Source.res +34 -12
  82. package/src/sources/Source.res.mjs +11 -0
  83. package/src/sources/SourceManager.res +111 -31
  84. package/src/sources/SourceManager.res.mjs +65 -31
  85. package/src/sources/SourceManager.resi +4 -0
  86. package/src/sources/StartBlockResolver.res +8 -5
  87. package/src/sources/StartBlockResolver.res.mjs +4 -2
  88. package/src/sources/SvmHyperSyncSource.res +4 -2
  89. package/src/sources/SvmHyperSyncSource.res.mjs +4 -6
  90. package/src/sources/TransactionStore.res +9 -6
  91. package/src/sources/TransactionStore.res.mjs +2 -2
  92. package/src/tui/Tui.res +10 -0
  93. package/src/tui/Tui.res.mjs +15 -0
  94. package/src/tui/components/SyncETA.res +12 -6
  95. package/src/tui/components/SyncETA.res.mjs +12 -8
  96. package/src/LazyLoader.res +0 -135
  97. package/src/LazyLoader.res.mjs +0 -118
  98. package/src/bindings/SDSL.res +0 -12
  99. package/src/bindings/SDSL.res.mjs +0 -9
  100. package/src/bindings/Yargs.res +0 -8
  101. package/src/bindings/Yargs.res.mjs +0 -2
  102. package/src/sources/Rpc.res +0 -185
  103. package/src/sources/Rpc.res.mjs +0 -183
package/src/Metrics.res CHANGED
@@ -129,6 +129,9 @@ type t = {
129
129
  elapsedSeconds: float,
130
130
  targetBufferSize: int,
131
131
  isInReorgThreshold: bool,
132
+ // This process has got as far as it can without the run's leave. What a
133
+ // supervisor reads to decide that a split run may go realtime as one.
134
+ hasArrivedAtHead: bool,
132
135
  rollbackEnabled: bool,
133
136
  maxBatchSize: int,
134
137
  preloadSeconds: float,
@@ -149,6 +152,115 @@ type t = {
149
152
  sourceHeightStreams: array<sourceHeightStreamMetrics>,
150
153
  }
151
154
 
155
+ // Folds items that share a key into one, keeping first-seen order so the
156
+ // rendered series doesn't reshuffle between scrapes.
157
+ let sumByKey = (items: array<'item>, ~key: 'item => string, ~add: ('item, 'item) => 'item) => {
158
+ let byKey = Dict.make()
159
+ let order = []
160
+ items->Array.forEach(item => {
161
+ let k = item->key
162
+ switch byKey->Utils.Dict.dangerouslyGetNonOption(k) {
163
+ | Some(existing) => byKey->Dict.set(k, add(existing, item))
164
+ | None => {
165
+ byKey->Dict.set(k, item)
166
+ order->Array.push(k)
167
+ }
168
+ }
169
+ })
170
+ order->Array.map(k => byKey->Dict.getUnsafe(k))
171
+ }
172
+
173
+ // Combines the snapshots a supervised run's workers reported into the one an
174
+ // unsplit run would have produced. Series keyed by chain concatenate, since a
175
+ // chain belongs to exactly one worker; series keyed by name are summed, since
176
+ // every worker runs the same handlers and effects over its own chains. The
177
+ // clock and the buffer target are the caller's: both belong to the group, and
178
+ // the target is what the run was configured with rather than anything a worker
179
+ // could add up to.
180
+ let merge = (snapshots: array<t>, ~startTime, ~metricTime, ~elapsedSeconds, ~targetBufferSize) => {
181
+ let concat = select => snapshots->Array.flatMap(select)
182
+ let sumInt = select => snapshots->Array.reduce(0, (acc, snapshot) => acc + snapshot->select)
183
+ let sumFloat = select => snapshots->Array.reduce(0., (acc, snapshot) => acc +. snapshot->select)
184
+
185
+ {
186
+ startTime,
187
+ metricTime,
188
+ elapsedSeconds,
189
+ targetBufferSize,
190
+ // Chains cross into the threshold as one indexer, so a run is in it once
191
+ // every process is, the same reading as the arrival below.
192
+ isInReorgThreshold: snapshots->Utils.Array.notEmpty &&
193
+ snapshots->Array.every(s => s.isInReorgThreshold),
194
+ // The run has arrived only once every process has: one still backfilling
195
+ // speaks for the whole indexer.
196
+ hasArrivedAtHead: snapshots->Utils.Array.notEmpty &&
197
+ snapshots->Array.every(s => s.hasArrivedAtHead),
198
+ rollbackEnabled: snapshots->Array.some(s => s.rollbackEnabled),
199
+ maxBatchSize: snapshots->Array.reduce(0, (acc, s) => Pervasives.max(acc, s.maxBatchSize)),
200
+ preloadSeconds: sumFloat(s => s.preloadSeconds),
201
+ processingSeconds: sumFloat(s => s.processingSeconds),
202
+ processingStalledOnFetchSeconds: sumFloat(s => s.processingStalledOnFetchSeconds),
203
+ processingStalledOnStorageWriteSeconds: sumFloat(s => s.processingStalledOnStorageWriteSeconds),
204
+ rollbackSeconds: sumFloat(s => s.rollbackSeconds),
205
+ rollbackCount: sumInt(s => s.rollbackCount),
206
+ rollbackEventsCount: sumFloat(s => s.rollbackEventsCount),
207
+ chains: concat(s => s.chains),
208
+ sourceRequests: concat(s => s.sourceRequests),
209
+ sourceHeights: concat(s => s.sourceHeights),
210
+ sourceHeightStreams: concat(s => s.sourceHeightStreams),
211
+ handlers: concat(s => s.handlers)->sumByKey(
212
+ ~key=h => `${h.contract}.${h.event}`,
213
+ ~add=(a, b) => {
214
+ ...a,
215
+ processingSeconds: a.processingSeconds +. b.processingSeconds,
216
+ processingCount: a.processingCount +. b.processingCount,
217
+ preloadSeconds: a.preloadSeconds +. b.preloadSeconds,
218
+ preloadCount: a.preloadCount +. b.preloadCount,
219
+ preloadSecondsTotal: a.preloadSecondsTotal +. b.preloadSecondsTotal,
220
+ },
221
+ ),
222
+ effects: concat(s => s.effects)->sumByKey(
223
+ ~key=e => `${e.effect}.${e.scope}`,
224
+ ~add=(a, b) => {
225
+ ...a,
226
+ callSeconds: a.callSeconds +. b.callSeconds,
227
+ callSecondsTotal: a.callSecondsTotal +. b.callSecondsTotal,
228
+ callCount: a.callCount +. b.callCount,
229
+ activeCallsCount: a.activeCallsCount + b.activeCallsCount,
230
+ queueCount: a.queueCount + b.queueCount,
231
+ queueWaitSeconds: a.queueWaitSeconds +. b.queueWaitSeconds,
232
+ invalidationsCount: a.invalidationsCount +. b.invalidationsCount,
233
+ // An effect's cache rows are per chain, so worker counts are disjoint.
234
+ // Absent unless some worker persists the cache at all.
235
+ cacheCount: switch (a.cacheCount, b.cacheCount) {
236
+ | (Some(x), Some(y)) => Some(x + y)
237
+ | (Some(x), None) => Some(x)
238
+ | (None, y) => y
239
+ },
240
+ },
241
+ ),
242
+ storageLoads: concat(s => s.storageLoads)->sumByKey(
243
+ ~key=l => `${l.storage}.${l.operation}`,
244
+ ~add=(a, b) => {
245
+ ...a,
246
+ seconds: a.seconds +. b.seconds,
247
+ secondsTotal: a.secondsTotal +. b.secondsTotal,
248
+ count: a.count +. b.count,
249
+ whereSize: a.whereSize +. b.whereSize,
250
+ size: a.size +. b.size,
251
+ },
252
+ ),
253
+ storageWrites: concat(s => s.storageWrites)->sumByKey(
254
+ ~key=w => w.storage,
255
+ ~add=(a, b) => {...a, seconds: a.seconds +. b.seconds, count: a.count + b.count},
256
+ ),
257
+ historyPrunes: concat(s => s.historyPrunes)->sumByKey(
258
+ ~key=p => p.entity,
259
+ ~add=(a, b) => {...a, seconds: a.seconds +. b.seconds, count: a.count + b.count},
260
+ ),
261
+ }
262
+ }
263
+
152
264
  // Prometheus floats keep at most 3 decimals; integral values render without a
153
265
  // fractional part.
154
266
  @inline
@@ -912,199 +1024,253 @@ let getRuntimeCollectors = () =>
912
1024
  // from the beginning of the run, not from the first scrape.
913
1025
  let startRuntimeCollectors = () => getRuntimeCollectors()->ignore
914
1026
 
915
- let collectRuntime = () => {
916
- let b = {out: ""}
1027
+ type runtimeGc = {kind: string, count: float, seconds: float}
1028
+ type runtimeHeapSpace = {space: string, size: float, used: float, available: float}
1029
+
1030
+ // One process's runtime readings at one moment. Plain numbers, so a worker can
1031
+ // hand its sample to the supervisor over the fork's channel and the supervisor
1032
+ // can render every process's under one label set.
1033
+ type runtimeSample = {
1034
+ cpuUserSeconds: float,
1035
+ cpuSystemSeconds: float,
1036
+ processStartTimeSeconds: float,
1037
+ residentMemoryBytes: float,
1038
+ heapTotalBytes: float,
1039
+ heapUsedBytes: float,
1040
+ externalMemoryBytes: float,
1041
+ eventLoopUtilization: float,
1042
+ eventLoopLagMeanSeconds: float,
1043
+ eventLoopLagMinSeconds: float,
1044
+ eventLoopLagMaxSeconds: float,
1045
+ eventLoopLagStddevSeconds: float,
1046
+ eventLoopLagP50Seconds: float,
1047
+ eventLoopLagP90Seconds: float,
1048
+ eventLoopLagP99Seconds: float,
1049
+ heapSpaces: array<runtimeHeapSpace>,
1050
+ activeResources: array<(string, float)>,
1051
+ gc: array<runtimeGc>,
1052
+ nodeVersion: string,
1053
+ }
1054
+
1055
+ let sampleRuntime = (): runtimeSample => {
917
1056
  let memory = NodeJs.Process.memoryUsage()
918
1057
  let cpu = NodeJs.Process.cpuUsage()
919
1058
  let elu = NodeJs.PerfHooks.performance->NodeJs.PerfHooks.eventLoopUtilization
920
1059
  let {eventLoopDelay, gcStats, processStartTimeSeconds} = getRuntimeCollectors()
921
- b->single(
1060
+ // Nanoseconds in the histogram; reset after sampling so each scrape reports
1061
+ // the delay distribution since the previous one, matching prom-client. With
1062
+ // no samples yet (e.g. the first scrape, which starts the sampler) the
1063
+ // histogram reports NaN means and a sentinel min — report 0 instead.
1064
+ let hasLagSamples = eventLoopDelay.max > 0.
1065
+ let nsToSeconds = ns => hasLagSamples && !(ns->Float.isNaN) ? ns /. 1_000_000_000. : 0.
1066
+ let sample = {
1067
+ cpuUserSeconds: cpu.user /. 1_000_000.,
1068
+ cpuSystemSeconds: cpu.system /. 1_000_000.,
1069
+ processStartTimeSeconds,
1070
+ residentMemoryBytes: memory.rss,
1071
+ heapTotalBytes: memory.heapTotal,
1072
+ heapUsedBytes: memory.heapUsed,
1073
+ externalMemoryBytes: memory.external_,
1074
+ eventLoopUtilization: elu.utilization,
1075
+ eventLoopLagMeanSeconds: eventLoopDelay.mean->nsToSeconds,
1076
+ eventLoopLagMinSeconds: eventLoopDelay.min->nsToSeconds,
1077
+ eventLoopLagMaxSeconds: eventLoopDelay.max->nsToSeconds,
1078
+ eventLoopLagStddevSeconds: eventLoopDelay.stddev->nsToSeconds,
1079
+ eventLoopLagP50Seconds: eventLoopDelay->NodeJs.PerfHooks.percentile(50)->nsToSeconds,
1080
+ eventLoopLagP90Seconds: eventLoopDelay->NodeJs.PerfHooks.percentile(90)->nsToSeconds,
1081
+ eventLoopLagP99Seconds: eventLoopDelay->NodeJs.PerfHooks.percentile(99)->nsToSeconds,
1082
+ heapSpaces: NodeJs.V8.getHeapSpaceStatistics()->Array.map(s => {
1083
+ space: s.spaceName->String.replace("_space", ""),
1084
+ size: s.spaceSize,
1085
+ used: s.spaceUsedSize,
1086
+ available: s.spaceAvailableSize,
1087
+ }),
1088
+ activeResources: {
1089
+ let byType = Dict.make()
1090
+ NodeJs.Process.getActiveResourcesInfo()->Array.forEach(resource =>
1091
+ byType->Dict.set(
1092
+ resource,
1093
+ byType->Utils.Dict.dangerouslyGetNonOption(resource)->Option.getOr(0.) +. 1.,
1094
+ )
1095
+ )
1096
+ byType->Dict.toArray
1097
+ },
1098
+ gc: gcStats
1099
+ ->Dict.toArray
1100
+ ->Array.map(((kind, stat)) => {kind, count: stat.count, seconds: stat.seconds}),
1101
+ nodeVersion: NodeJs.Process.version,
1102
+ }
1103
+ eventLoopDelay->NodeJs.PerfHooks.reset
1104
+ sample
1105
+ }
1106
+
1107
+ // Renders every sample under `scope`, the labels telling one process's readings
1108
+ // from another's (`worker="0"`), or "" for a run that is one process.
1109
+ let renderRuntime = (samples: array<(string, runtimeSample)>) => {
1110
+ let b = {out: ""}
1111
+ let labels = (scope, own) =>
1112
+ switch (scope, own) {
1113
+ | ("", "") => ""
1114
+ | ("", own) | (own, "") => `{${own}}`
1115
+ | (scope, own) => `{${scope},${own}}`
1116
+ }
1117
+ let scoped = samples->Array.map(((scope, sample)) => (labels(scope, ""), sample))
1118
+ let each = select =>
1119
+ samples->Array.flatMap(((scope, sample)) =>
1120
+ select(sample)->Array.map(((own, value)) => (labels(scope, own), value))
1121
+ )
1122
+ let gauge = (~name, ~help, ~value) =>
1123
+ b->series(~name, ~help, ~kind="gauge", ~entries=scoped, ~value)
1124
+ let counter = (~name, ~help, ~value) =>
1125
+ b->series(~name, ~help, ~kind="counter", ~entries=scoped, ~value)
1126
+
1127
+ counter(
922
1128
  ~name="process_cpu_user_seconds_total",
923
1129
  ~help="Total user CPU time spent in seconds.",
924
- ~kind="counter",
925
- ~value=cpu.user /. 1_000_000.,
1130
+ ~value=s => s.cpuUserSeconds,
926
1131
  )
927
- b->single(
1132
+ counter(
928
1133
  ~name="process_cpu_system_seconds_total",
929
1134
  ~help="Total system CPU time spent in seconds.",
930
- ~kind="counter",
931
- ~value=cpu.system /. 1_000_000.,
1135
+ ~value=s => s.cpuSystemSeconds,
932
1136
  )
933
- b->single(
1137
+ counter(
934
1138
  ~name="process_cpu_seconds_total",
935
1139
  ~help="Total user and system CPU time spent in seconds.",
936
- ~kind="counter",
937
- ~value=(cpu.user +. cpu.system) /. 1_000_000.,
1140
+ ~value=s => s.cpuUserSeconds +. s.cpuSystemSeconds,
938
1141
  )
939
- b->single(
1142
+ gauge(
940
1143
  ~name="process_start_time_seconds",
941
1144
  ~help="Start time of the process since unix epoch in seconds.",
942
- ~kind="gauge",
943
- ~value=processStartTimeSeconds,
1145
+ ~value=s => s.processStartTimeSeconds,
944
1146
  )
945
- b->single(
946
- ~name="process_resident_memory_bytes",
947
- ~help="Resident memory size in bytes.",
948
- ~kind="gauge",
949
- ~value=memory.rss,
1147
+ gauge(~name="process_resident_memory_bytes", ~help="Resident memory size in bytes.", ~value=s =>
1148
+ s.residentMemoryBytes
950
1149
  )
951
- b->single(
1150
+ gauge(
952
1151
  ~name="nodejs_heap_size_total_bytes",
953
1152
  ~help="Process heap size from Node.js in bytes.",
954
- ~kind="gauge",
955
- ~value=memory.heapTotal,
1153
+ ~value=s => s.heapTotalBytes,
956
1154
  )
957
- b->single(
1155
+ gauge(
958
1156
  ~name="nodejs_heap_size_used_bytes",
959
1157
  ~help="Process heap size used from Node.js in bytes.",
960
- ~kind="gauge",
961
- ~value=memory.heapUsed,
1158
+ ~value=s => s.heapUsedBytes,
962
1159
  )
963
- b->single(
1160
+ gauge(
964
1161
  ~name="nodejs_external_memory_bytes",
965
1162
  ~help="Node.js external memory size in bytes.",
966
- ~kind="gauge",
967
- ~value=memory.external_,
1163
+ ~value=s => s.externalMemoryBytes,
968
1164
  )
969
- b->single(
1165
+ gauge(
970
1166
  ~name="nodejs_eventloop_utilization",
971
1167
  ~help="Ratio of time the event loop is active, since process start.",
972
- ~kind="gauge",
973
- ~value=elu.utilization,
1168
+ ~value=s => s.eventLoopUtilization,
974
1169
  )
975
- // Nanoseconds in the histogram; reset after rendering so each scrape reports
976
- // the delay distribution since the previous one, matching prom-client. With
977
- // no samples yet (e.g. the first scrape, which starts the sampler) the
978
- // histogram reports NaN means and a sentinel min — render 0 instead.
979
- let hasLagSamples = eventLoopDelay.max > 0.
980
- let nsToSeconds = ns => hasLagSamples && !(ns->Float.isNaN) ? ns /. 1_000_000_000. : 0.
981
- b->single(
1170
+ gauge(
982
1171
  ~name="nodejs_eventloop_lag_mean_seconds",
983
1172
  ~help="The mean of the recorded event loop delays.",
984
- ~kind="gauge",
985
- ~value=eventLoopDelay.mean->nsToSeconds,
1173
+ ~value=s => s.eventLoopLagMeanSeconds,
986
1174
  )
987
- b->single(
1175
+ gauge(
988
1176
  ~name="nodejs_eventloop_lag_min_seconds",
989
1177
  ~help="The minimum recorded event loop delay.",
990
- ~kind="gauge",
991
- ~value=eventLoopDelay.min->nsToSeconds,
1178
+ ~value=s => s.eventLoopLagMinSeconds,
992
1179
  )
993
- b->single(
1180
+ gauge(
994
1181
  ~name="nodejs_eventloop_lag_max_seconds",
995
1182
  ~help="The maximum recorded event loop delay.",
996
- ~kind="gauge",
997
- ~value=eventLoopDelay.max->nsToSeconds,
1183
+ ~value=s => s.eventLoopLagMaxSeconds,
998
1184
  )
999
- b->single(
1185
+ gauge(
1000
1186
  ~name="nodejs_eventloop_lag_stddev_seconds",
1001
1187
  ~help="The standard deviation of the recorded event loop delays.",
1002
- ~kind="gauge",
1003
- ~value=eventLoopDelay.stddev->nsToSeconds,
1188
+ ~value=s => s.eventLoopLagStddevSeconds,
1004
1189
  )
1005
- b->single(
1190
+ gauge(
1006
1191
  ~name="nodejs_eventloop_lag_p50_seconds",
1007
1192
  ~help="The 50th percentile of the recorded event loop delays.",
1008
- ~kind="gauge",
1009
- ~value=eventLoopDelay->NodeJs.PerfHooks.percentile(50)->nsToSeconds,
1193
+ ~value=s => s.eventLoopLagP50Seconds,
1010
1194
  )
1011
- b->single(
1195
+ gauge(
1012
1196
  ~name="nodejs_eventloop_lag_p90_seconds",
1013
1197
  ~help="The 90th percentile of the recorded event loop delays.",
1014
- ~kind="gauge",
1015
- ~value=eventLoopDelay->NodeJs.PerfHooks.percentile(90)->nsToSeconds,
1198
+ ~value=s => s.eventLoopLagP90Seconds,
1016
1199
  )
1017
- b->single(
1200
+ gauge(
1018
1201
  ~name="nodejs_eventloop_lag_p99_seconds",
1019
1202
  ~help="The 99th percentile of the recorded event loop delays.",
1020
- ~kind="gauge",
1021
- ~value=eventLoopDelay->NodeJs.PerfHooks.percentile(99)->nsToSeconds,
1203
+ ~value=s => s.eventLoopLagP99Seconds,
1022
1204
  )
1023
- eventLoopDelay->NodeJs.PerfHooks.reset
1024
- let heapSpaces =
1025
- NodeJs.V8.getHeapSpaceStatistics()->Array.map(s => (
1026
- `{space="${s.spaceName->String.replace("_space", "")}"}`,
1027
- s,
1028
- ))
1205
+
1206
+ let heapSpaces = each(s => s.heapSpaces->Array.map(h => (`space="${h.space}"`, h)))
1029
1207
  b->series(
1030
1208
  ~name="nodejs_heap_space_size_total_bytes",
1031
1209
  ~help="Process heap space size total from Node.js in bytes.",
1032
1210
  ~kind="gauge",
1033
1211
  ~entries=heapSpaces,
1034
- ~value=s => s.spaceSize,
1212
+ ~value=h => h.size,
1035
1213
  )
1036
1214
  b->series(
1037
1215
  ~name="nodejs_heap_space_size_used_bytes",
1038
1216
  ~help="Process heap space size used from Node.js in bytes.",
1039
1217
  ~kind="gauge",
1040
1218
  ~entries=heapSpaces,
1041
- ~value=s => s.spaceUsedSize,
1219
+ ~value=h => h.used,
1042
1220
  )
1043
1221
  b->series(
1044
1222
  ~name="nodejs_heap_space_size_available_bytes",
1045
1223
  ~help="Process heap space size available from Node.js in bytes.",
1046
1224
  ~kind="gauge",
1047
1225
  ~entries=heapSpaces,
1048
- ~value=s => s.spaceAvailableSize,
1049
- )
1050
- let activeResources = {
1051
- let byType = Dict.make()
1052
- NodeJs.Process.getActiveResourcesInfo()->Array.forEach(resource => {
1053
- let label = `{type="${resource->escapeLabelValue}"}`
1054
- byType->Dict.set(
1055
- label,
1056
- byType->Utils.Dict.dangerouslyGetNonOption(label)->Option.getOr(0.) +. 1.,
1057
- )
1058
- })
1059
- byType->Dict.toArray
1060
- }
1226
+ ~value=h => h.available,
1227
+ )
1228
+
1061
1229
  b->series(
1062
1230
  ~name="nodejs_active_resources",
1063
1231
  ~help="Number of active resources that are currently keeping the event loop alive, grouped by async resource type.",
1064
1232
  ~kind="gauge",
1065
- ~entries=activeResources,
1233
+ ~entries=each(s =>
1234
+ s.activeResources->Array.map(
1235
+ ((resource, count)) => (`type="${resource->escapeLabelValue}"`, count),
1236
+ )
1237
+ ),
1066
1238
  ~value=count => count,
1067
1239
  )
1068
- b->single(
1240
+ gauge(
1069
1241
  ~name="nodejs_active_resources_total",
1070
1242
  ~help="Total number of active resources.",
1071
- ~kind="gauge",
1072
- ~value=activeResources->Array.reduce(0., (acc, (_, count)) => acc +. count),
1073
- )
1074
- let gcEntries = []
1075
- gcStats->Utils.Dict.forEachWithKey((stat, kind) =>
1076
- gcEntries->Array.push((`{kind="${kind}"}`, stat))
1243
+ ~value=s => s.activeResources->Array.reduce(0., (acc, (_, count)) => acc +. count),
1077
1244
  )
1245
+
1246
+ let gc = each(s => s.gc->Array.map(g => (`kind="${g.kind}"`, g)))
1078
1247
  b->series(
1079
1248
  ~name="nodejs_gc_duration_seconds_sum",
1080
1249
  ~help="Cumulative garbage collection pause time by kind, one of major, minor, incremental or weakcb.",
1081
1250
  ~kind="counter",
1082
- ~entries=gcEntries,
1083
- ~value=s => s.seconds,
1251
+ ~entries=gc,
1252
+ ~value=g => g.seconds,
1084
1253
  )
1085
1254
  b->series(
1086
1255
  ~name="nodejs_gc_duration_seconds_count",
1087
1256
  ~help="Number of garbage collection pauses by kind, one of major, minor, incremental or weakcb.",
1088
1257
  ~kind="counter",
1089
- ~entries=gcEntries,
1090
- ~value=s => s.count,
1258
+ ~entries=gc,
1259
+ ~value=g => g.count,
1091
1260
  )
1092
- let version = NodeJs.Process.version
1093
- let versionParts = version->String.replace("v", "")->String.split(".")
1094
- let versionPart = i => versionParts->Array.get(i)->Option.getOr("0")
1261
+
1095
1262
  b->series(
1096
1263
  ~name="nodejs_version_info",
1097
1264
  ~help="Node.js version info.",
1098
1265
  ~kind="gauge",
1099
- ~entries=[
1100
- (
1101
- `{version="${version}",major="${versionPart(0)}",minor="${versionPart(
1102
- 1,
1103
- )}",patch="${versionPart(2)}"}`,
1104
- (),
1105
- ),
1106
- ],
1266
+ ~entries=each(s => {
1267
+ let parts = s.nodeVersion->String.replace("v", "")->String.split(".")
1268
+ let part = i => parts->Array.get(i)->Option.getOr("0")
1269
+ [(`version="${s.nodeVersion}",major="${part(0)}",minor="${part(1)}",patch="${part(2)}"`, ())]
1270
+ }),
1107
1271
  ~value=() => 1.,
1108
1272
  )
1109
1273
  b.out ++ "\n"
1110
1274
  }
1275
+
1276
+ let collectRuntime = () => renderRuntime([("", sampleRuntime())])