Use RTT jitter and fixed margin for ETCP retransmission timeout

This commit is contained in:
evgeny committed 2026-10-23 12:52:35 +02:00
1 parent e84e17c396
commit 9dbef48eb3
3 files changed
+23 -15

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@ static struct INFLIGHT_PACKET* logical_find(struct ETCP_CONN* conn, uint32_t seq
uint32_t etcp_tx_rto_us(const struct ETCP_LINK* link) {
uint64_t rtt = link->data_rtt_us ? link->data_rtt_us : (uint64_t)link->rtt_last * 100;
uint64_t rto = rtt ? 2 * (rtt + link->data_jitter_us) : 1000000;
uint64_t rto = rtt ? (12 * rtt + 15ULL * link->data_jitter_us + 9) / 10 + 3000 : 1000000;
if (rto < 10000) rto = 10000;
if (rto > 1000000) rto = 1000000;
return (uint32_t)rto;
+2 -1
View File
@@ -33,7 +33,8 @@ int etcp_tx_link_init(struct ETCP_LINK* link);
* reset не меняет физический UP; destroy дополнительно освобождает очереди. */
void etcp_tx_link_reset(struct ETCP_LINK* link, int retry);
void etcp_tx_link_destroy(struct ETCP_LINK* link);
/* Расчётный RTO в микросекундах: 2*(собственный RTT+jitter), 10мс..1с; до DATA ACK использует KA RTT/1с. */
/* RTO = clamp(ceil(1.2*RTT + 1.5*jitter + 3000), 10000, 1000000), все величины в мкс.
* RTT/jitter — собственные DATA-измерения линка; до DATA ACK использует KA RTT, без RTT — 1с. */
uint32_t etcp_tx_rto_us(const struct ETCP_LINK* link);
/* Резервировать окно для packet; NULL — ошибка с логом, packet остаётся в очереди отправки.
* Физические записи ограничены MAX_INFLIGHT_SIZE на линк и 2*MAX_INFLIGHT_SIZE на соединение.
+20 -13
View File
@@ -329,27 +329,34 @@ static void shaper_ready(void* arg) {
loadbalancer_link_ready(link);
}
/* Реальный RTO: нижняя/верхняя границы и влияние RTT/jitter, без подмены обработчика таймера. */
/* Реальный RTO: границы, округление, начальный jitter и выбор DATA/KA RTT; без подмены обработчика таймера. */
static void retransmission_timeout(void) {
const struct { uint16_t rtt, jitter; uint32_t expected_tb; } cases[] = {
{0, 0, 10000}, {20, 10, 100}, {200, 0, 400}, {200, 75, 550},
{2000, 100, 4200}, {6000, 1000, 10000}, {UINT16_MAX, UINT16_MAX, 10000}
const struct { uint32_t rtt_us, jitter_us; uint16_t ka_rtt_tb; uint32_t expected_us; } cases[] = {
{0, 0, 0, 1000000}, {0, UINT32_MAX, 0, 1000000}, {2000, 1000, 0, 10000},
{20000, 0, 0, 27000}, {20000, 7500, 0, 38250}, {200000, 10000, 0, 258000},
{600000, 100000, 0, 873000}, {1000000, 0, 0, 1000000}, {UINT32_MAX, UINT32_MAX, 0, 1000000},
{5833, 0, 0, 10000}, {5834, 0, 0, 10001}, {20001, 1, 0, 27003},
{20000, 10000, 0, 42000}, {200000, 100000, 0, 393000},
{0, 0, 200, 27000}, {20000, 0, 6000, 27000}, {0, 0, UINT16_MAX, 1000000},
{830832, 0, 0, 999999}, {830833, 0, 0, 1000000}, {830834, 0, 0, 1000000}
};
for (unsigned i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
setup(); b->link_status = 0;
conn->rtt_avg_10 = cases[i].rtt; conn->jitter = cases[i].jitter;
a->rtt_last = 0; a->data_rtt_us = cases[i].rtt * 100; a->data_jitter_us = cases[i].jitter * 100;
conn->rtt_avg_10 = UINT16_MAX; conn->jitter = UINT16_MAX; // Общие/чужие метрики не участвуют в RTO этого пути.
a->rtt_last = cases[i].ka_rtt_tb; a->data_rtt_us = cases[i].rtt_us; a->data_jitter_us = cases[i].jitter_us;
CHECK(etcp_tx_rto_us(a) == cases[i].expected_us);
fill(a, 1, 100);
struct INFLIGHT_PACKET* packet = (struct INFLIGHT_PACKET*)conn->input_wait_ack->head;
uint64_t sent = packet->last_timestamp, deadline = sent + cases[i].expected_tb + 1000;
uint64_t sent = last_attempt(packet)->sent_at_us, deadline = sent + cases[i].expected_us + 100000;
unsigned expected_tb = (cases[i].expected_us + 99) / 100;
// Блокируем только новую передачу: статус UP и настоящий retrans-таймер сохраняются.
a->shaper_timer = uasync_set_timeout(ua, cases[i].expected_tb + 10000, a, shaper_ready, "test_rto_shaper");
a->shaper_timer = uasync_set_timeout(ua, expected_tb + 10000, a, shaper_ready, "test_rto_shaper");
CHECK(a->shaper_timer);
while (packet->state == INFLIGHT_STATE_WAIT_ACK && get_time_tb() < deadline) test_clock_poll(ua, 10);
uint64_t elapsed = get_time_tb() - sent;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RTO: RTT_tb=%u jitter_tb=%u expected_tb=%u observed_tb=%llu state=%u",
cases[i].rtt, cases[i].jitter, cases[i].expected_tb, (unsigned long long)elapsed, packet->state);
CHECK(packet->state == INFLIGHT_STATE_WAIT_SEND && elapsed >= cases[i].expected_tb && elapsed <= cases[i].expected_tb + 1000);
while (packet->state == INFLIGHT_STATE_WAIT_ACK && get_time_us() < deadline) test_clock_poll(ua, 10);
uint64_t elapsed = get_time_us() - sent;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "RTO: DATA_RTT_us=%u jitter_us=%u KA_RTT_tb=%u expected_us=%u observed_us=%llu state=%u",
cases[i].rtt_us, cases[i].jitter_us, cases[i].ka_rtt_tb, cases[i].expected_us, (unsigned long long)elapsed, packet->state);
CHECK(packet->state == INFLIGHT_STATE_WAIT_SEND && elapsed >= cases[i].expected_us && elapsed <= cases[i].expected_us + 100000);
CHECK(!sends && !a->inflight_bytes && conn->input_send_q->count == 1 && conn->unacked_bytes == 100);
check_accounting(); ack(packet->seq); teardown();
}