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