aboutsummaryrefslogtreecommitdiff
path: root/oexport.py
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-07-23 06:55:29 +0200
committerDimitri Staessens <dimitri@ouroboros.rocks>2026-07-23 06:55:29 +0200
commit6cb72c39bd35046c1cbdfde47c462d646b49c078 (patch)
tree1be3dae97a01840bd8cb04a041158ce3a2042d59 /oexport.py
parentf933cc43789cce0ef5daa7ba06385545b165e4bb (diff)
downloadouroboros-metrics-6cb72c39bd35046c1cbdfde47c462d646b49c078.tar.gz
ouroboros-metrics-6cb72c39bd35046c1cbdfde47c462d646b49c078.zip
metrics: Add ECN signal loss and heartbeats
Adds support for the loss cuts and heartbeats/s in the IPCP ECN/FA stats.
Diffstat (limited to 'oexport.py')
-rwxr-xr-xoexport.py42
1 files changed, 35 insertions, 7 deletions
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: