diff --git a/src/transport_layer/etcp_loadbalancer.c b/src/transport_layer/etcp_loadbalancer.c index 85ef5245..160f9beb 100644 --- a/src/transport_layer/etcp_loadbalancer.c +++ b/src/transport_layer/etcp_loadbalancer.c @@ -29,7 +29,7 @@ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp, uint32_t int full = data_len && (data_len > link->inflight_lim_bytes || link->inflight_bytes > link->inflight_lim_bytes - data_len); if (full) link->cwnd_limited = 1; - else if (loadbalancer_link_can_send(link) && (!best || link->inflight_bytes < best->inflight_bytes)) best = link; + else if ((!data_len || loadbalancer_link_can_send(link)) && (!best || link->inflight_bytes < best->inflight_bytes)) best = link; } link = link->next ? link->next : etcp->links; } while (link != start); @@ -67,7 +67,9 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { } // Update shaper after successful send - if (link->burst_active) { + if (!dgram->payload_len) { + DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] ACK/control bypasses DATA pacer link=%u", etcp->log_name, link->local_link_id); + } else if (link->burst_active) { // burst bypasses shaper — не обновляем нагрузку } else if (link->bandwidth == 0) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] unlimited bandwidth, skipping shaper update", etcp->log_name); diff --git a/src/transport_layer/etcp_loadbalancer.h b/src/transport_layer/etcp_loadbalancer.h index 9f4855f1..80243262 100644 --- a/src/transport_layer/etcp_loadbalancer.h +++ b/src/transport_layer/etcp_loadbalancer.h @@ -18,8 +18,9 @@ extern "C" { #define SHAPER_BURST_DELAY_TB 10ULL // 10 = 1ms burst allowance /* Заимствованный рабочий линк с минимальным inflight, round-robin при равенстве; NULL — нет подходящего. - * data_len > 0 требует места для целого DATA, 0 выбирает линк для ACK без проверки cwnd. - * Оба режима учитывают shaper; burst обходит только pacing, индивидуальное окно DATA сохраняется. */ + * data_len > 0 требует места для целого DATA и готовности shaper; 0 выбирает ACK без DATA-cwnd/pacing. + * ACK уже ограничен входящим потоком/ACK_DELAY; исходящая оценка DATA не описывает полосу обратных подтверждений. + * Burst обходит только pacing, индивидуальное окно DATA сохраняется. */ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp, uint32_t data_len); // Передать dgram с установленным link: после отправки (включая ошибку сокета) возвращает dgram в pkt_pool.