From 61faca84d7979a8a28a9b7bfc67b365b62f0e571 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 18:31:52 +0200 Subject: [PATCH] Add aligned router delivery metering for video adaptation --- src/routing_layer/route_tx_stats.c | 45 ++++++++++++++++++++++++++++++ src/routing_layer/route_tx_stats.h | 25 +++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/routing_layer/route_tx_stats.c create mode 100644 src/routing_layer/route_tx_stats.h diff --git a/src/routing_layer/route_tx_stats.c b/src/routing_layer/route_tx_stats.c new file mode 100644 index 00000000..9b1ea1e6 --- /dev/null +++ b/src/routing_layer/route_tx_stats.c @@ -0,0 +1,45 @@ +#include "route_tx_stats.h" +#include "../../lib/debug_config.h" +#include + +/* Эпоха принадлежит каналу; освобождение неподтверждённых пакетов не вызывает ack. */ +void route_tx_meter_reset(struct route_tx_meter *meter, uint64_t now_us) { + if (!meter) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "TX meter reset without state"); return; } + memset(meter, 0, sizeof(*meter)); + meter->started_us = meter->last_us = now_us; +} + +/* Сохраняем реальные моменты ACK, независимо от задержки таймера потребителя статистики. */ +int route_tx_meter_ack(struct route_tx_meter *meter, uint64_t now_us, uint64_t bytes) { + if (!meter || !bytes || now_us < meter->last_us || bytes > UINT64_MAX - meter->bytes || meter->packets == UINT64_MAX) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "TX meter invalid ACK time=%llu bytes=%llu", + (unsigned long long)now_us, (unsigned long long)bytes); + return -1; + } + uint64_t tick = now_us / ROUTE_TX_SAMPLE_US; + struct route_tx_bucket *bucket = &meter->buckets[tick % ROUTE_TX_BUCKETS]; + if (bucket->tick_plus_one != tick + 1) *bucket = (struct route_tx_bucket){.tick_plus_one = tick + 1}; + bucket->bytes += bytes; bucket->packets++; + meter->bytes += bytes; meter->packets++; meter->last_us = now_us; + return 0; +} + +/* Сумма только завершённых ячеек: idle интервалы остаются нулевыми. */ +int route_tx_meter_window(const struct route_tx_meter *meter, uint64_t now_us, struct route_tx_window *out) { + if (!meter || !out || now_us < meter->last_us) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "TX meter invalid snapshot time=%llu", (unsigned long long)now_us); + return -1; + } + uint64_t end = now_us / ROUTE_TX_SAMPLE_US; + struct route_tx_window result = {.end_us = end * ROUTE_TX_SAMPLE_US}; + result.valid = result.end_us >= meter->started_us && result.end_us - meter->started_us >= ROUTE_TX_WINDOW_US; + for (unsigned i = 0; i < ROUTE_TX_BUCKETS; i++) { + const struct route_tx_bucket *bucket = &meter->buckets[i]; + if (!bucket->tick_plus_one) continue; + uint64_t tick = bucket->tick_plus_one - 1; + if (tick >= end || end - tick > 10) continue; + result.bytes += bucket->bytes; result.packets += bucket->packets; + } + *out = result; + return 0; +} diff --git a/src/routing_layer/route_tx_stats.h b/src/routing_layer/route_tx_stats.h new file mode 100644 index 00000000..c7336117 --- /dev/null +++ b/src/routing_layer/route_tx_stats.h @@ -0,0 +1,25 @@ +/* Учёт первой доставки payload логического канала. Один uasync-поток. + * Десять завершённых интервалов по 50мс и текущий интервал; reset начинает новую историю. + * Повторы учитывает вызывающий: add вызывается только при снятии подтверждённого inflight. */ +#ifndef ROUTE_TX_STATS_H +#define ROUTE_TX_STATS_H + +#include + +#define ROUTE_TX_SAMPLE_US 50000u +#define ROUTE_TX_WINDOW_US 500000u +#define ROUTE_TX_BUCKETS 11 + +struct route_tx_bucket { uint64_t tick_plus_one, bytes, packets; }; +struct route_tx_meter { + struct route_tx_bucket buckets[ROUTE_TX_BUCKETS]; + uint64_t started_us, last_us, bytes, packets; +}; +struct route_tx_window { uint64_t end_us, bytes, packets; int valid; }; + +void route_tx_meter_reset(struct route_tx_meter *meter, uint64_t now_us); +int route_tx_meter_ack(struct route_tx_meter *meter, uint64_t now_us, uint64_t bytes); +/* Неполное окно после reset возвращает valid=0; текущая незавершённая ячейка исключается. */ +int route_tx_meter_window(const struct route_tx_meter *meter, uint64_t now_us, struct route_tx_window *out); + +#endif