aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dashboards/general.json279
-rwxr-xr-xoexport.py42
-rw-r--r--ribreader.py5
3 files changed, 317 insertions, 9 deletions
diff --git a/dashboards/general.json b/dashboards/general.json
index 08eae1e..4047d9e 100644
--- a/dashboards/general.json
+++ b/dashboards/general.json
@@ -2796,7 +2796,7 @@
"x": 12,
"y": 76
},
- "id": 211,
+ "id": 216,
"options": {
"legend": {
"calcs": [],
@@ -12961,6 +12961,283 @@
],
"title": "FRCT \u2014 Tx ring drops",
"type": "row"
+ },
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 2,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 6,
+ "x": 18,
+ "y": 76
+ },
+ "id": 215,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "query": "from(bucket: \"ouroboros-metrics\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ouroboros_flow_allocator_loss_cuts_total\")\n |> filter(fn: (r) => r[\"_field\"] == \"loss_cuts_total\")\n |> filter(fn: (r) => contains(value: r[\"ipcp\"], set: ${_ipcp:json}))\n |> filter(fn: (r) => contains(value: r[\"system\"], set: ${_system:json}))\n |> filter(fn: (r) => contains(value: r[\"layer\"], set: ${_layer:json}))\n |> aggregateWindow(every: ${_interval}, fn: max, createEmpty: false)\n |> derivative(unit: 1s, nonNegative: true)\n |> yield(name: \"rate\")",
+ "refId": "A"
+ }
+ ],
+ "title": "Signal-loss cuts/s",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 2,
+ "scaleDistribution": {
+ "log": 2,
+ "type": "log"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ns"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 6,
+ "x": 12,
+ "y": 86
+ },
+ "id": 218,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "query": "from(bucket: \"ouroboros-metrics\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ouroboros_flow_allocator_ss_tc_ns\")\n |> filter(fn: (r) => r[\"_field\"] == \"ss_tc_ns\")\n |> filter(fn: (r) => contains(value: r[\"ipcp\"], set: ${_ipcp:json}))\n |> filter(fn: (r) => contains(value: r[\"system\"], set: ${_system:json}))\n |> filter(fn: (r) => contains(value: r[\"layer\"], set: ${_layer:json}))\n |> aggregateWindow(every: ${_interval}, fn: last, createEmpty: false)\n |> yield(name: \"last\")",
+ "refId": "A"
+ }
+ ],
+ "title": "Ramp time constant (ss_tc)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "axisSoftMin": 0,
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 2,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 10,
+ "w": 6,
+ "x": 18,
+ "y": 86
+ },
+ "id": 217,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "influxdb",
+ "uid": "df36a9e8-5205-4042-9bfb-0516cbcacb4f"
+ },
+ "query": "from(bucket: \"ouroboros-metrics\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"ouroboros_flow_allocator_hb_rtt_samples_total\")\n |> filter(fn: (r) => r[\"_field\"] == \"hb_rtt_samples_total\")\n |> filter(fn: (r) => contains(value: r[\"ipcp\"], set: ${_ipcp:json}))\n |> filter(fn: (r) => contains(value: r[\"system\"], set: ${_system:json}))\n |> filter(fn: (r) => contains(value: r[\"layer\"], set: ${_layer:json}))\n |> aggregateWindow(every: ${_interval}, fn: max, createEmpty: false)\n |> derivative(unit: 1s, nonNegative: true)\n |> yield(name: \"rate\")",
+ "refId": "A"
+ }
+ ],
+ "title": "Heartbeat RTT samples/s",
+ "type": "timeseries"
}
],
"preload": false,
diff --git a/oexport.py b/oexport.py
index 48e99cc..eea98fd 100755
--- a/oexport.py
+++ b/oexport.py
@@ -86,6 +86,38 @@ def _jain_index(values):
return (total * total) / (n * sq)
+# Additive inc, proportional dec, loss recovery: the pacer is binding.
+_FAIRNESS_REGIMES = {1, 2, 4}
+
+
+def _fairness_rates(flows):
+ """Paced rates that count toward the Jain fairness index.
+
+ Fairness is only meaningful over senders whose pacer constrains
+ real data traffic. Slow start (0) has no allocation yet; source
+ limited (3) covers sinks, where paced_rate is reverse-ACK pacing.
+ Older RIBs without a regime code fall back to a byte-ratio test
+ to weed out sinks: ACK-only traffic is a few percent of the
+ received volume, nowhere near the 25% cutoff.
+ """
+ rates = []
+ for flow in flows:
+ rate = flow.get('paced_rate', 0)
+ if rate <= 0:
+ continue
+
+ regime = flow.get('cong_regime')
+ sent = flow.get('sent_bytes_total', 0)
+ rcvd = flow.get('recv_bytes_total', 0)
+ if regime is not None:
+ if regime in _FAIRNESS_REGIMES:
+ rates.append(rate)
+ elif sent * 4 > rcvd:
+ rates.append(rate)
+
+ return rates
+
+
def _make_timerfd(interval_ns: int) -> int:
"""Create and arm a CLOCK_MONOTONIC timerfd for *interval_ns* nanoseconds.
@@ -366,7 +398,6 @@ class OuroborosExporter:
ipcp_name,
layer,
metrics) -> None:
- point = {}
for metric in metrics:
point = {
'measurement': f'ouroboros_data_transfer_{metric}',
@@ -381,8 +412,8 @@ class OuroborosExporter:
'time': now,
}
- self.writer.write(bucket=self.bucket,
- record=Point.from_dict(point))
+ self.writer.write(bucket=self.bucket,
+ record=Point.from_dict(point))
def _write_ouroboros_process_frct_metric(self,
now,
@@ -503,7 +534,6 @@ class OuroborosExporter:
for ipcp in ipcps:
flows = self.ribreader.get_flow_allocator_flow_info_for_ipcp(ipcp['name'])
- rates = []
for flow in flows:
for metric, value in flow.items():
if metric == 'endpoint_id':
@@ -513,11 +543,9 @@ class OuroborosExporter:
metric, str(now), ipcp['name'],
flow['endpoint_id'], ipcp['layer'], value)
- if 'paced_rate' in flow:
- rates.append(flow['paced_rate'])
-
# Per-IPCP Jain fairness over the CA-allocated (paced) rates;
# a bottleneck-fairness proxy, meaningful with >=2 flows.
+ rates = _fairness_rates(flows)
if len(rates) >= 2:
jain = _jain_index(rates)
if jain is not None:
diff --git a/ribreader.py b/ribreader.py
index fa40804..856ca1d 100644
--- a/ribreader.py
+++ b/ribreader.py
@@ -262,7 +262,7 @@ class OuroborosRIBReader:
@staticmethod
def _get_trailing_number(s: str) -> int:
"""Extract trailing integer from a string."""
- m = re.search(r'\d+$', s)
+ m = re.search(r'-?\d+$', s)
return int(m.group()) if m else None
def get_dht_stats_for_ipcp(self,
@@ -344,6 +344,9 @@ class OuroborosRIBReader:
'Capacity rate floor (bytes/s)': 'cap_rate_floor',
'Capacity updates (count)': 'cap_updates_total',
'Slow start peak rate (bytes/s)': 'ss_peak_rate',
+ 'Signal-loss cuts (count)': 'loss_cuts_total',
+ 'Heartbeat RTT samples (count)': 'hb_rtt_samples_total',
+ 'Ramp time constant (ns)': 'ss_tc_ns',
}
ret = {}