@leverege/build-tools 2.119.0-pedro.1 → 2.119.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leverege/build-tools",
3
- "version": "2.119.0-pedro.1",
3
+ "version": "2.119.0",
4
4
  "description": "A collection of build / support tools for Leverege developers",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "uid": "pubsub-repeater",
3
3
  "title": "PubSub Repeater",
4
- "description": "PubSub Repeater. Metric names come from repeaterMetricNames() in src/processors/RepeaterMetrics.js, the single declared list - cross-check panels against it rather than against grep. Four things to know. (1) @leverege/metrics has NO label support, so the mapping name is baked into the metric NAME; a mapping's hyphens appear as underscores. Because rate() strips __name__, per-mapping series cannot be produced by one query, so the fleet row is a panel repeated over $mappings instead. (2) Names are DOUBLY prefixed: PROMETHEUS_STATUS_PREFIX (pubsub_repeater) plus the counter's own repeater_ prefix, giving pubsub_repeater_repeater_<mapping>_<suffix>. Verified against the live stg-pitcrew endpoint. (3) A counter that has never been incremented does not exist in Prometheus, so panels that add counters together use `or vector(0)` per term: PromQL addition with a missing operand yields no result at all, which would blank a healthy mapping's ratio panels entirely. A single-series panel for a reason that never fired still reads as No data rather than 0. (4) Retired mappings keep reporting until their series age out of retention, so $mappings can offer names no longer in REPEATER_MAPPINGS.",
4
+ "description": "PubSub Repeater. Metric names come from repeaterMetricNames() in src/processors/RepeaterMetrics.js, the single declared list - cross-check panels against it rather than against grep. Four things to know. (1) @leverege/metrics has NO label support, so the mapping name is baked into the metric NAME; a mapping's hyphens appear as underscores. Because rate() strips __name__, per-mapping series cannot be produced by one query, so the fleet row is a panel repeated over $mappings instead. (2) Names are DOUBLY prefixed: PROMETHEUS_STATUS_PREFIX (pubsub_repeater) plus the counter's own repeater_ prefix, giving pubsub_repeater_repeater_<mapping>_<suffix>. Verified against the live stg-pitcrew endpoint. (3) A counter that has never been incremented does not exist in Prometheus, so panels that add counters together use `or vector(0)` per term: PromQL addition with a missing operand yields no result at all, which would blank a healthy mapping's ratio panels entirely. A single-series panel for a reason that never fired still reads as No data rather than 0. (4) Retired mappings keep reporting until their series age out of retention, so $mappings can offer names no longer in REPEATER_MAPPINGS. (5) The imagine_api_* counters are not this service's own: @leverege/comms NodeComms emits <prefix>_<method> per request and <prefix>_<method>_error on any non-ok response, with the prefix defaulted to imagine_api by @leverege/api. No metricsMethod is passed, so there is no per-operation breakdown.",
5
5
  "tags": [
6
6
  "pubsub-repeater",
7
7
  "platform",
@@ -861,8 +861,8 @@
861
861
  },
862
862
  {
863
863
  "type": "timeseries",
864
- "title": "Imagine API calls / sec",
865
- "description": "GET is device resolution, so it is the device-header cache miss rate: it should sit far below the inbound message rate, and a GET rate tracking inbound means the cache is not helping (check REPEATER_DEVICE_CACHE_EXPIRES). POST is resource header creation on the target.",
864
+ "title": "Event loop lag",
865
+ "description": "The ordered queues are in-memory and drained on this event loop, so lag here is directly messages sitting behind a correlation id. Watch it alongside the awaited resource copy: requireRsrcCopy holds a message for up to rsrcCopyAwaitTimeout, and with ensureOrdering everything behind that correlation id waits with it.",
866
866
  "id": 15,
867
867
  "datasource": {
868
868
  "type": "prometheus",
@@ -876,7 +876,7 @@
876
876
  },
877
877
  "fieldConfig": {
878
878
  "defaults": {
879
- "unit": "reqps",
879
+ "unit": "s",
880
880
  "min": 0,
881
881
  "custom": {
882
882
  "drawStyle": "line",
@@ -910,8 +910,8 @@
910
910
  "type": "prometheus",
911
911
  "uid": "${datasource}"
912
912
  },
913
- "expr": "sum(rate(pubsub_repeater_imagine_api_get{job=~\"$job\"}[5m]))",
914
- "legendFormat": "get/s (device lookups)",
913
+ "expr": "max(nodejs_eventloop_lag_p99_seconds{job=~\"$job\"})",
914
+ "legendFormat": "p99",
915
915
  "refId": "A"
916
916
  },
917
917
  {
@@ -919,16 +919,16 @@
919
919
  "type": "prometheus",
920
920
  "uid": "${datasource}"
921
921
  },
922
- "expr": "sum(rate(pubsub_repeater_imagine_api_post{job=~\"$job\"}[5m]))",
923
- "legendFormat": "post/s (rsrc creates)",
922
+ "expr": "max(nodejs_eventloop_lag_mean_seconds{job=~\"$job\"})",
923
+ "legendFormat": "mean",
924
924
  "refId": "B"
925
925
  }
926
926
  ]
927
927
  },
928
928
  {
929
929
  "type": "timeseries",
930
- "title": "Event loop lag p99",
931
- "description": "The ordered queues are in-memory and drained on this event loop, so lag here is directly messages sitting behind a correlation id. Watch it alongside the awaited resource copy: requireRsrcCopy holds a message for up to rsrcCopyAwaitTimeout, and with ensureOrdering everything behind that correlation id waits with it.",
930
+ "title": "Heap used",
931
+ "description": "Per pod. The in-memory ordered queues have no bound, so a target topic that stops accepting publishes shows up here as heap climbing before it shows up anywhere else.",
932
932
  "id": 16,
933
933
  "datasource": {
934
934
  "type": "prometheus",
@@ -936,13 +936,13 @@
936
936
  },
937
937
  "gridPos": {
938
938
  "h": 8,
939
- "w": 8,
939
+ "w": 12,
940
940
  "x": 0,
941
941
  "y": 48
942
942
  },
943
943
  "fieldConfig": {
944
944
  "defaults": {
945
- "unit": "s",
945
+ "unit": "bytes",
946
946
  "min": 0,
947
947
  "custom": {
948
948
  "drawStyle": "line",
@@ -976,26 +976,96 @@
976
976
  "type": "prometheus",
977
977
  "uid": "${datasource}"
978
978
  },
979
- "expr": "max(nodejs_eventloop_lag_p99_seconds{job=~\"$job\"})",
980
- "legendFormat": "p99",
979
+ "expr": "nodejs_heap_size_used_bytes{job=~\"$job\"}",
980
+ "legendFormat": "{{pod}}",
981
981
  "refId": "A"
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "type": "timeseries",
987
+ "title": "Pods reporting and CPU",
988
+ "description": "A pod restart loses whatever the in-memory queues were holding. Those messages were acknowledged on receipt, so nothing redelivers them: a restart is silent data loss, visible only as a step here.",
989
+ "id": 17,
990
+ "datasource": {
991
+ "type": "prometheus",
992
+ "uid": "${datasource}"
993
+ },
994
+ "gridPos": {
995
+ "h": 8,
996
+ "w": 12,
997
+ "x": 12,
998
+ "y": 48
999
+ },
1000
+ "fieldConfig": {
1001
+ "defaults": {
1002
+ "unit": "short",
1003
+ "min": 0,
1004
+ "custom": {
1005
+ "drawStyle": "line",
1006
+ "lineWidth": 1,
1007
+ "fillOpacity": 10,
1008
+ "showPoints": "never",
1009
+ "stacking": {
1010
+ "mode": "none"
1011
+ }
1012
+ }
1013
+ },
1014
+ "overrides": []
1015
+ },
1016
+ "options": {
1017
+ "legend": {
1018
+ "displayMode": "table",
1019
+ "placement": "bottom",
1020
+ "calcs": [
1021
+ "lastNotNull",
1022
+ "max"
1023
+ ]
982
1024
  },
1025
+ "tooltip": {
1026
+ "mode": "multi",
1027
+ "sort": "desc"
1028
+ }
1029
+ },
1030
+ "targets": [
983
1031
  {
984
1032
  "datasource": {
985
1033
  "type": "prometheus",
986
1034
  "uid": "${datasource}"
987
1035
  },
988
- "expr": "max(nodejs_eventloop_lag_mean_seconds{job=~\"$job\"})",
989
- "legendFormat": "mean",
1036
+ "expr": "count(count by (pod) (nodejs_heap_size_used_bytes{job=~\"$job\"}))",
1037
+ "legendFormat": "pods reporting",
1038
+ "refId": "A"
1039
+ },
1040
+ {
1041
+ "datasource": {
1042
+ "type": "prometheus",
1043
+ "uid": "${datasource}"
1044
+ },
1045
+ "expr": "sum(rate(process_cpu_seconds_total{job=~\"$job\"}[5m]))",
1046
+ "legendFormat": "cpu cores",
990
1047
  "refId": "B"
991
1048
  }
992
1049
  ]
993
1050
  },
1051
+ {
1052
+ "type": "row",
1053
+ "title": "Imagine API: call volume and outcomes",
1054
+ "id": 18,
1055
+ "gridPos": {
1056
+ "h": 1,
1057
+ "w": 24,
1058
+ "x": 0,
1059
+ "y": 56
1060
+ },
1061
+ "collapsed": false,
1062
+ "panels": []
1063
+ },
994
1064
  {
995
1065
  "type": "timeseries",
996
- "title": "Heap used",
997
- "description": "Per pod. The in-memory ordered queues have no bound, so a target topic that stops accepting publishes shows up here as heap climbing before it shows up anywhere else.",
998
- "id": 17,
1066
+ "title": "Imagine API calls / sec",
1067
+ "description": "Emitted by @leverege/comms NodeComms.preRequest, one counter per HTTP method, so this is every request the Imagine client made. GET is device resolution and it is counted only on a cache MISS, so it should sit far below the inbound message rate; a GET rate tracking inbound means the device-header cache is not helping (check REPEATER_DEVICE_CACHE_EXPIRES). POST is resource header creation on the target, so it tracks rsrc_copied.",
1068
+ "id": 19,
999
1069
  "datasource": {
1000
1070
  "type": "prometheus",
1001
1071
  "uid": "${datasource}"
@@ -1003,12 +1073,12 @@
1003
1073
  "gridPos": {
1004
1074
  "h": 8,
1005
1075
  "w": 8,
1006
- "x": 8,
1007
- "y": 48
1076
+ "x": 0,
1077
+ "y": 57
1008
1078
  },
1009
1079
  "fieldConfig": {
1010
1080
  "defaults": {
1011
- "unit": "bytes",
1081
+ "unit": "reqps",
1012
1082
  "min": 0,
1013
1083
  "custom": {
1014
1084
  "drawStyle": "line",
@@ -1042,17 +1112,26 @@
1042
1112
  "type": "prometheus",
1043
1113
  "uid": "${datasource}"
1044
1114
  },
1045
- "expr": "nodejs_heap_size_used_bytes{job=~\"$job\"}",
1046
- "legendFormat": "{{pod}}",
1115
+ "expr": "sum(rate(pubsub_repeater_imagine_api_get{job=~\"$job\"}[5m])) or vector(0)",
1116
+ "legendFormat": "get/s (device lookups)",
1047
1117
  "refId": "A"
1118
+ },
1119
+ {
1120
+ "datasource": {
1121
+ "type": "prometheus",
1122
+ "uid": "${datasource}"
1123
+ },
1124
+ "expr": "sum(rate(pubsub_repeater_imagine_api_post{job=~\"$job\"}[5m])) or vector(0)",
1125
+ "legendFormat": "post/s (rsrc creates)",
1126
+ "refId": "B"
1048
1127
  }
1049
1128
  ]
1050
1129
  },
1051
1130
  {
1052
1131
  "type": "timeseries",
1053
- "title": "Restarts and replica count",
1054
- "description": "A pod restart loses whatever the in-memory queues were holding. Those messages were acknowledged on receipt, so nothing redelivers them: a restart is silent data loss, visible only here.",
1055
- "id": 18,
1132
+ "title": "Imagine API errors / sec",
1133
+ "description": "NodeComms.postRequest increments <method>_error whenever the response is missing or not ok, so this is EVERY non-2xx, not only transport failures. Two consequences. A 404 from a device lookup that legitimately found nothing lands here, so a healthy get_error floor is normal and should track dropped_deviceNotFound rather than alarm you. And 401 or 403 here is the Imagine API options secret being wrong or expired, which otherwise surfaces only as every message dropping as deviceNotFound. The base method counter is incremented for the same request too, so errors are a subset of the calls panel, never additional to it. Expect a nonzero post_error floor by design: copyRsrcOnce creates the target resource header first, and resource-server answers an existing header with a 400 whose message says already exists. RepeaterProcessor treats that as the success it describes, which is what makes the copy retry idempotent, but the comms lib has already counted the 400 here. So post_error tracking rsrc_copied at a low ratio is the retry path working, not a fault.",
1134
+ "id": 20,
1056
1135
  "datasource": {
1057
1136
  "type": "prometheus",
1058
1137
  "uid": "${datasource}"
@@ -1060,12 +1139,12 @@
1060
1139
  "gridPos": {
1061
1140
  "h": 8,
1062
1141
  "w": 8,
1063
- "x": 16,
1064
- "y": 48
1142
+ "x": 8,
1143
+ "y": 57
1065
1144
  },
1066
1145
  "fieldConfig": {
1067
1146
  "defaults": {
1068
- "unit": "short",
1147
+ "unit": "reqps",
1069
1148
  "min": 0,
1070
1149
  "custom": {
1071
1150
  "drawStyle": "line",
@@ -1099,8 +1178,8 @@
1099
1178
  "type": "prometheus",
1100
1179
  "uid": "${datasource}"
1101
1180
  },
1102
- "expr": "count(count by (pod) (nodejs_heap_size_used_bytes{job=~\"$job\"}))",
1103
- "legendFormat": "pods reporting",
1181
+ "expr": "sum(rate(pubsub_repeater_imagine_api_get_error{job=~\"$job\"}[5m])) or vector(0)",
1182
+ "legendFormat": "get errors/s",
1104
1183
  "refId": "A"
1105
1184
  },
1106
1185
  {
@@ -1108,11 +1187,57 @@
1108
1187
  "type": "prometheus",
1109
1188
  "uid": "${datasource}"
1110
1189
  },
1111
- "expr": "sum(rate(process_cpu_seconds_total{job=~\"$job\"}[5m]))",
1112
- "legendFormat": "cpu cores",
1190
+ "expr": "sum(rate(pubsub_repeater_imagine_api_post_error{job=~\"$job\"}[5m])) or vector(0)",
1191
+ "legendFormat": "post errors/s",
1113
1192
  "refId": "B"
1114
1193
  }
1115
1194
  ]
1195
+ },
1196
+ {
1197
+ "type": "stat",
1198
+ "title": "Imagine API error share",
1199
+ "description": "Errors over all calls, across the selected range. Reads 0 while no error counter exists at all, which is the healthy state: the _error counters are created lazily on the first failure, so they are absent rather than zero until something fails. A step change here is worth more than the absolute value, since the floor is whatever the legitimate 404 rate happens to be.",
1200
+ "id": 21,
1201
+ "datasource": {
1202
+ "type": "prometheus",
1203
+ "uid": "${datasource}"
1204
+ },
1205
+ "gridPos": {
1206
+ "h": 8,
1207
+ "w": 8,
1208
+ "x": 16,
1209
+ "y": 57
1210
+ },
1211
+ "fieldConfig": {
1212
+ "defaults": {
1213
+ "unit": "percentunit",
1214
+ "min": 0
1215
+ },
1216
+ "overrides": []
1217
+ },
1218
+ "options": {
1219
+ "reduceOptions": {
1220
+ "calcs": [
1221
+ "lastNotNull"
1222
+ ],
1223
+ "fields": "",
1224
+ "values": false
1225
+ },
1226
+ "textMode": "auto",
1227
+ "colorMode": "value",
1228
+ "graphMode": "area"
1229
+ },
1230
+ "targets": [
1231
+ {
1232
+ "datasource": {
1233
+ "type": "prometheus",
1234
+ "uid": "${datasource}"
1235
+ },
1236
+ "expr": "((sum(increase(pubsub_repeater_imagine_api_get_error{job=~\"$job\"}[$__range])) or vector(0)) + (sum(increase(pubsub_repeater_imagine_api_post_error{job=~\"$job\"}[$__range])) or vector(0))) / clamp_min((sum(increase(pubsub_repeater_imagine_api_get{job=~\"$job\"}[$__range])) or vector(0)) + (sum(increase(pubsub_repeater_imagine_api_post{job=~\"$job\"}[$__range])) or vector(0)), 1e-9)",
1237
+ "legendFormat": "error share",
1238
+ "refId": "A"
1239
+ }
1240
+ ]
1116
1241
  }
1117
1242
  ]
1118
1243
  }