960 lines
101 KiB
Plaintext
960 lines
101 KiB
Plaintext
ETCP: независимая обратная связь каналов и доставка при отказах
|
||
Проверка архитектуры и последовательные исправления, 24.10.2026
|
||
|
||
Статус
|
||
|
||
24.10.2026: исправлены абсолютные сроки ACK и конкуренция ACK/DATA разных путей.
|
||
Обычный ACK-only обслуживается от received_at_us головы своего пути + 2 мс;
|
||
piggyback готового DATA и срочный ответ остаются немедленными. Среди готовых ACK
|
||
выбирается самый старый срок; DATA другого пути уступает ему, DATA того же пути
|
||
забирает его piggyback. Ограниченная измерительная серия заканчивается до смены пути.
|
||
Один прежний таймер пересчитывается после piggyback/частичной отправки; новый RX
|
||
не отодвигает старые записи после callback таймера. Логический till имеет собственный
|
||
абсолютный срок вместо отдельного boolean pending. При OOM таймера используется
|
||
существующий флаг срочного ACK, исходные received_at_us не меняются.
|
||
Новых очередей/таймеров нет; wire-формат, RTO, min_rtt и модель BBR сохранены.
|
||
|
||
Две регрессии до фикса воспроизводят ACK в возрасте 0 мкс и обход готового ACK
|
||
другого пути исходящим DATA: /tmp/etcp-ack-deadline-before.log,
|
||
/tmp/etcp-ack-due-priority-before.log. Новые проверки покрывают границы 1999/2000 мкс,
|
||
piggyback разных путей, смену головы, early timer callback, RX между timer/sender,
|
||
частичный ACK-кадр, срочный ответ, OOM, sender failure с close, reset и DOWN/UP/удаление.
|
||
Диагностика расширена до 106 полей: ранние записи внутри готовой пачки допустимы;
|
||
early_ordinary_ack_packets отдельно проверяет отправку всего пакета раньше срока.
|
||
|
||
Проверка после исправления: windows 60 сценариев PASS с управляемыми и реальными
|
||
часами; восемь профильных тестов ETCP/STCP ASAN/UBSan с detect_leaks=1 PASS без
|
||
ошибок/утечек. Отдельный ASAN two-stuck с диагностикой 10 мс: 5202 проверенных среза,
|
||
ранних ACK-пакетов нет, доставка/ресурсы PASS. Полный check.sh: 184/184, proxy 9/9,
|
||
burst 1/1, load 8/8 PASS, 134217728 проверенных байт, 185 с со сборкой.
|
||
|
||
Три последовательных bidirectional с диагностикой: ранних обычных ACK-пакетов 0
|
||
во всех трёх прогонах. На одиночном 8 Мбит/с пути полоса ACK 0.143..0.169 Мбит/с
|
||
вместо 0.347/0.390 в предыдущем диагностическом прогоне. Агрегат: A->B
|
||
8.667..8.804, B->A 8.695..8.796 из 10 Мбит/с. Сам агрегат проходит все свои пороги
|
||
каждый раз; весь профиль остаётся FAIL из-за одиночных каналов, с 3/2/4 нарушениями.
|
||
Средний возраст entries ACK-only на 8 Мбит/с может быть меньше 2 мс: внутри готового
|
||
кадра передаются и новые entries. Это учитывается отдельно от ранней отправки кадра.
|
||
|
||
Полная штатная последовательная матрица после калибровки эмулятора: первые шесть
|
||
скоростных профилей и все 9 профилей отказов PASS. Агрегированный goodput,
|
||
Мбит/с: equal 14.416/16, bandwidth 9.032/10, rtt 14.516/16, fast-distant 8.857/10,
|
||
slow-distant 8.978/10, three 11.122/12, bidirectional 8.750/8.768 из 10 на направление.
|
||
Остаются два FAIL одиночного дуплекса: 1.609 вместо 1.700 Мбит/с (путь 2 Мбит/с)
|
||
и 6.730 вместо 6.800 Мбит/с (обратное направление пути 8 Мбит/с).
|
||
Two-stuck: gap 206.227 мс, latency 375.457 мс; load-drop/load-hold: gap 41.402/41.845 мс,
|
||
steady goodput 7.229/7.466 из 8 Мбит/с. Max latency насыщенного источника
|
||
180.693/89.239 мс записана; отдельным порогом она здесь не ограничена.
|
||
Дальше отдельно разбирать недооценку полосы/PROBE_RTT одиночного дуплекса,
|
||
STCP backpressure и проверки изменения RTT/BW и повторного отказа.
|
||
|
||
Логи: /tmp/etcp-ack-deadline-{windows,real-time,full-check}.log,
|
||
/tmp/etcp-ack-deadline-asan-test_*.log, /tmp/etcp-ack-deadline-asan-two-stuck.log/.csv,
|
||
/tmp/etcp-ack-deadline-matrix.log/.csv, /tmp/etcp-ack-deadline-duplex-<1..3>.log/.csv.
|
||
Сводка ACK: /tmp/etcp-ack-deadline-summary.json; актуальный HTML с 106 полями:
|
||
/tmp/etcp-ack-deadline-duplex.html. Проверка CSV и JS smoke графиков/фильтров PASS.
|
||
|
||
Предыдущий этап: диагностика до исправления ACK.
|
||
|
||
24.10.2026: добавлена буферизованная диагностика multipath с шагом 100 мс.
|
||
Интервал задаётся UTUN_MULTIPATH_DIAG_MS=10..1000. Новых таймеров нет: срез снимается
|
||
существующим тестовым циклом, фактический dt_us записывается в каждой строке.
|
||
Конец фазы добавляет короткий срез, поэтому скорость считают по dt_us, а не всегда по 100 мс.
|
||
Данные сохраняются в память и выводятся в CSV/лог после drain каждого стенда;
|
||
при провале CHECK уже собранные срезы выводятся перед exit. Переполнение буфера — ошибка.
|
||
Этот этап меняет только диагностику теста, алгоритмы ETCP/BBR сохранены.
|
||
|
||
Запуск из корня репозитория:
|
||
UTUN_MULTIPATH_DIAG=/tmp/etcp-duplex.csv ./tests/test_etcp_multipath bidirectional > /tmp/etcp-duplex.log 2>&1
|
||
python3 tools/etcp_diag_report.py /tmp/etcp-duplex.csv /tmp/etcp-duplex.html
|
||
Для подробного отказа: UTUN_MULTIPATH_DIAG_MS=10 и профиль two-stuck.
|
||
Уровень категории debug явно включается самим тестом; без UTUN_MULTIPATH_DIAG новый вывод выключен.
|
||
|
||
104 поля связывают очередь приложения, partial/pending/ожидания normalizer, очередь
|
||
фрагментов, send_q (новые/повторы), wait_ack и recv_q с физическим flight/cwnd пути.
|
||
Видны suspended/resume/deferred callbacks, состояние sender, номера seq/till,
|
||
общий бюджет DATA, очередь ACK и возраст её головы, возраст старейшей попытки,
|
||
RTT/jitter/RTO, BW, режим/фаза BBR, pacing, долг и срок готовности pacer, флаг застревания.
|
||
Отдельно записываются DATA TX/первые DATA RX, ACK-only/ACK в DATA, задержки ACK,
|
||
обычные ACK-only раньше 2 мс, samples BBR, проверки блокировки окном и pacer,
|
||
наблюдаемые min/max flight/cwnd/долга и пики send_q/ACK внутри интервала.
|
||
|
||
Очереди соединения повторяются в строках его линков — их нельзя суммировать между путями.
|
||
У pacer положительный долг допускается строго меньше 1 мс; отрицательное значение —
|
||
сырой кредит, который при отправке ограничивается 1 мс. Блокировки выбора — число
|
||
проверок, а не длительность ожидания. Пики внутри среза — наблюдения на TX/RX/ACK/выборе,
|
||
не непрерывная запись каждого изменения. channel_pending включает propagation delay,
|
||
сериализацию и HOLD; channel_peak/delay_max накапливаются с последнего reset_stats.
|
||
Wire-байты — UDP без IP/UDP headers; ACK-only включает timestamp, crypto и SESSION.
|
||
Piggyback учитывает только положительную ACK-секцию, без повторного начисления crypto.
|
||
|
||
HTML автономен, без библиотек/сети: выбор стенда/направления/канала/фазы/интервала,
|
||
восемь графиков и все поля выбранного курсором среза. PROBE_RTT выделен фоном.
|
||
Перед построением проверяются схема CSV, учёт очередей, счётчиков и непрерывность интервалов.
|
||
Проверены 598 срезов дуплекса (100 мс), 5208 срезов two-stuck (10 мс) и 5196 срезов
|
||
ASAN/UBSan two-stuck с detect_leaks=1 без ошибок/утечек. Намеренный CHECK сохранил
|
||
72 среза до выхода. Неверные интервалы 6/6 и повреждённые CSV 3/3 отклонены.
|
||
Общий check.sh итоговой версии: 184/184, proxy 9/9, burst 1/1, load 8/8 PASS,
|
||
134217728 проверенных байт, 166 с. HTML прошёл JS smoke-проверку восьми графиков,
|
||
104 полей, фильтров, диапазона, курсора, пустого диапазона и сброса.
|
||
Длинный дуплекс отдельно по-прежнему FAIL по скорости: в последнем прогоне 5 нарушений,
|
||
агрегат 8.513/8.337 из 10 Мбит/с. Это результат одного прогона, не доказательство эффекта диагностики на скорость.
|
||
|
||
На одиночном дуплексном пути 8 Мбит/с итоговые срезы показывают расход ACK 0.347/0.390 Мбит/с.
|
||
Обычные ACK-only раньше 2 мс: 1774/1828 и 1961/2019 записей; среднее ожидание 230/271 мкс.
|
||
Наблюдаемый пик очереди ACK — 4 записи, включая короткие пики до немедленной отправки.
|
||
У side=1 path=1 mask=2 в 4.5..5.4 с есть 4 кодограммы приложения, фрагмент и до 8 DATA
|
||
в send_q, а pacing составляет около 6.24..6.26 Мбит/с. В 5.5..5.7 с PROBE_RTT уменьшает
|
||
окно примерно с 81.5 до 32.9 тыс. байт; после него pacing возвращается лишь к 6.27..6.35 Мбит/с.
|
||
В этих срезах виден заданный моделью темп, а не отсутствие данных у sender.
|
||
Обнаружен обход ACK_DELAY посторонним wake sender. Диагностическое отключение PROBE_RTT
|
||
повышает скорость одиночного 8 Мбит/с пути примерно с 6.8 до 7.1 Мбит/с, но не закрывает
|
||
весь дуплекс. Прототип ожидания общего таймера уменьшает полосу ACK примерно до 0.21 Мбит/с,
|
||
однако также не закрывает все пороги. Эти прототипы остаются в /tmp, в протокол не внесены.
|
||
Следующий разбор: абсолютные deadlines ACK и переустановка единственного таймера после
|
||
piggyback/частичной отправки. Проверка одного наличия timer недостаточна: новый RX между
|
||
срабатыванием timer и отложенным sender может повторно задержать уже готовые старые ACK.
|
||
|
||
Артефакты: /tmp/etcp-duplex-diag-{100ms,10ms,asan,abort}.csv/.log,
|
||
/tmp/etcp-duplex-diag-view.html, /tmp/etcp-fault-diag-view.html,
|
||
/tmp/etcp-duplex-diag-full-check.log. Полный пакетный трейс и контроль PROBE_RTT:
|
||
/tmp/etcp-duplex-next-{trace,no-probe-control,ack-control}.log,
|
||
/tmp/etcp-duplex-next-analysis.json; observer — /tmp/etcp-probe-review.c/.mk.
|
||
Сводка итоговых срезов: /tmp/etcp-duplex-diag-summary.json.
|
||
|
||
24.10.2026: реализованы сканирование send_q, история путей пакета и быстрое застревание.
|
||
Остались прежние input_send_q/input_wait_ack: первый готовый повтор имеет приоритет,
|
||
иначе выбирается первый готовый новый DATA. Заблокированная запись остаётся на месте
|
||
и не мешает следующим. Если кандидата нет, допускается одна подгрузка из input_queue
|
||
в пределах общего бюджета unacked_bytes/optimal_inflight и приёмного окна пира.
|
||
После синхронного callback проверяются reset/close/эпоха; прежний указатель линка не используется.
|
||
Во время ограниченной burst-серии готовый повтор другого пути ждёт её завершения;
|
||
FILLER сохраняет измерение, а новые DATA не обгоняют этот повтор.
|
||
|
||
Каждый логический пакет хранит 32 байта битовой истории живых слотов local_link_id.
|
||
Пока есть неиспользованный допустимый путь, повтор ждёт его cwnd/pacer, а не начинает
|
||
новый обход уже использованных. После полного обхода прежний путь также уступает
|
||
другой допустимой альтернативе. История/last_rr_link меняются только после принятия
|
||
DATA транспортом; rollback их сохраняет. Удаление линка очищает слот до unlink и
|
||
callbacks, поэтому новый линк с тем же local_link_id не наследует старую историю.
|
||
|
||
Три RTO физических попыток без ACK-прогресса ставят tx_stalled. Пока есть хотя бы
|
||
один незастрявший initialized+UP путь, повторы исключают застрявшие, даже когда
|
||
рабочий временно занят. Если застряли все — разрешены все UP-пути. Новые DATA
|
||
используют прежний выбор и могут проверить восстановление: флаг снимает действующий
|
||
ACK собственной попытки, отправленной после установки флага. Старый/чужой ACK
|
||
этого не делает. Reset очищает состояние; физический UP, ACK/keepalive и формула RTO
|
||
сохранены. Новых очередей, payload-копий, таймеров или ранних повторов до RTO нет.
|
||
|
||
Windows: 51 сценарий PASS с управляемыми и реальными часами. Покрыты заблокированный
|
||
префикс 256 записей, приоритет повторов, общий бюджет, OOM, rollback, полный обход,
|
||
ID 255/переиспользование слота из callback, три RTO, старые ACK, восстановление,
|
||
все застрявшие пути и reset/close/удаление burst-линка из callback подгрузки.
|
||
Восемь профильных тестов ASAN/UBSan с detect_leaks=1 проходят без ошибок/утечек.
|
||
Контроль занятости рабочего пути 1 мс теперь проходит: latency 377.012 мс вместо 736.623 мс,
|
||
gap 206.810 мс, ready_wait 2.318 мс при бюджете 460.552 мс. Отрицательный контроль 200 мс
|
||
по-прежнему проваливает ready_wait (200.764 мс), gap и latency; пороги не ослаблены.
|
||
Логи: /tmp/etcp-send-scan-{windows-final,real-time,busy_1ms,ready_delay_200ms}.log,
|
||
/tmp/etcp-send-scan-asan-test_*.log.
|
||
|
||
Полная последовательная матрица после калибровки эмулятора: все 9 профилей отказов PASS.
|
||
Two-stuck: gap 217.945 мс, latency 379.997 мс при пределе 462.219 мс, ready_wait 1.184 мс.
|
||
Ещё три отдельных повтора two-stuck: 3/3 PASS, latency 374.981..378.557 мс,
|
||
gap 203.057..205.265 мс, ready_wait максимум 13.268 мс. Slow-drop: gap 259.464 мс,
|
||
latency 264.704 мс при пределе 350.129 мс; это профиль с RTT 200 мс и ожиданием RTO.
|
||
Load-drop/load-hold: gap 39.234/39.355 мс, steady goodput 7.221/7.468 из 8 Мбит/с.
|
||
Для насыщенного источника проверяются gap и steady goodput; max_latency 161.287/86.306 мс
|
||
здесь записана, но отдельным порогом не ограничена. ASAN/UBSan two-stuck/load-drop
|
||
также PASS с detect_leaks=1, без ошибок и утечек.
|
||
|
||
Первые шесть скоростных профилей PASS. Агрегированный goodput
|
||
(min(application,fresh DATA), Мбит/с): equal 14.390/16, bandwidth 9.048/10,
|
||
rtt 14.244/16, fast-distant 8.841/10, slow-distant 8.966/10, three 11.017/12.
|
||
Дуплекс 8.412/8.435 из 10 Мбит/с остаётся FAIL: все 7 нарушений матрицы относятся
|
||
к его скоростным порогам (одиночные каналы и агрегат). Доставка, порядок и ресурсы
|
||
проходят. Общая multipath-задача остаётся открытой: далее разбирать дуплекс,
|
||
STCP backpressure и отдельные проверки смены RTT/BW/повторного отказа.
|
||
Логи: /tmp/etcp-send-scan-matrix.log/.csv,
|
||
/tmp/etcp-send-scan-native-two-stuck-<1..3>.log/.csv,
|
||
/tmp/etcp-send-scan-asan-{two-stuck,load-drop}.log/.csv.
|
||
|
||
Полный check.sh после изменений: 184/184, proxy 9/9, burst 1/1, load 8/8 PASS;
|
||
134217728 проверенных байт, 197 с со сборкой. Отдельная длинная multipath-матрица
|
||
в check.sh не входит; её оставшиеся нарушения дуплекса перечислены выше.
|
||
Лог: /tmp/etcp-send-scan-full-check.log.
|
||
|
||
Предыдущая диагностика (до реализации сканирования и истории):
|
||
|
||
24.10.2026: проверки отказов разделены на RTO, готовность повтора и доставку.
|
||
В этом этапе менялись только тесты/диагностика, логика ETCP сохранена.
|
||
TX учитывается после принятия send транспортом, а не при подготовке пакета.
|
||
Возврат логического DATA в send_q фиксирует окончание ожидания RTO; отдельно
|
||
измеряются опоздание обработки RTO, ожидание готового повтора и доставка сверх
|
||
заданной задержки канала. Проверки этих этапов:20мс,100мс и100мс соответственно.
|
||
Транзит измеряется при первом приёме DATA, если номер/путь совпадают с последней
|
||
принятой отправкой; старую копию не приписываем времени новой попытки.
|
||
RTO_count в логе — число возвратов логического DATA по RTO, не все снятия физических попыток.
|
||
Для быстрых профилей общий предел остаётся100мс. Если сумма RTO отказавших путей
|
||
превышает100мс, общий предел — эта сумма плюс100мс. Бюджет даётся на один обход
|
||
отказавших путей; дополнительные круги не увеличивают разрешённую задержку.
|
||
Максимальный RTO пути наблюдается независимо от измеряемых gap/latency.
|
||
|
||
Остальные8 профилей отказов проходят. Two-stuck нестабилен: в обычном прогоне
|
||
сообщение322 ждало738.317мс при пределе459.332мс. Разблокировавший фрагмент324
|
||
прошёл пути1->2->1->2->0 (5 принятых попыток); первая отправка произошла всего
|
||
через41мкс после постановки сообщения. Это дополнительный обход двух путей без
|
||
прогресса, а не обязательное ожидание единственного RTO или очередь источника.
|
||
Контроль с занятостью рабочего пути0 ровно1мс воспроизводит ту же цепочку:
|
||
736.623мс latency,534.548мс gap. При выборе2->1 рабочий путь initialized=UP=1,
|
||
окно свободно (1026/9514 байт), pacing_blocked=1. Повтор немедленно уходит в1,
|
||
после его RTO — снова в2, хотя0 уже готов. Готовый повтор ждёт максимум1.109мс,
|
||
обработка RTO опаздывает максимум1.951мс; лишнее время создают повторные RTO.
|
||
|
||
Отрицательный контроль отдельно задерживает готовый повтор на200мс: проверка
|
||
ready_wait падает независимо от разрешённого бюджета RTO. Самые медленные
|
||
сообщения логируются с номерами сообщения/фрагмента, временем постановки,
|
||
первой отправки и доставки, числом попыток и историей путей (по2 бита, последние16).
|
||
Решения между отказавшими путями буферизуются и печатаются после замера;
|
||
переполнение буфера — ошибка теста. Обычные повторы two-stuck также могут пройти
|
||
с latency около376мс; единичный PASS не закрывает обнаруженный лишний круг.
|
||
Slow-drop/two-stuck/load-drop проходят ASAN/UBSan с detect_leaks=1 без ошибок/утечек.
|
||
Полный check.sh:184/184, proxy9/9, burst1/1, load8/8 PASS,134217728 проверенных байт,
|
||
161с; /tmp/etcp-recovery-stages-full-check.log. Долгая multipath-матрица в check.sh не входит.
|
||
|
||
До дальнейшего изменения транспорта требуется отдельный разбор исключения
|
||
путей без DATA-прогресса из обычного выбора, ограниченных проб и возврата после
|
||
подтверждённого прогресса. Физический UP не следует подменять DATA-готовностью;
|
||
нужно проверить поздние ACK, отсутствие исправных путей и reset/close.
|
||
Ранних копий до RTO в плане нет. Дуплекс в этом этапе не менялся.
|
||
Логи: /tmp/etcp-recovery-stages-final-<profile>-<1..3>.log/.csv,
|
||
/tmp/etcp-recovery-stages-buffered-two-<1..5>.log, /tmp/etcp-recovery-stages-asan-<profile>.log.
|
||
Контроли: /tmp/etcp-recovery-stages-negative-final.log,
|
||
/tmp/etcp-recovery-stages-busy-control.log; исходники контролей — /tmp/etcp-recovery-*-control.c/.mk.
|
||
|
||
Предыдущий этап — ожидание альтернативы после RTO:
|
||
|
||
24.10.2026: уточнено и реализовано правило ретрансмита. При наличии другого
|
||
initialized+UP канала повтор ждёт его cwnd/pacing, даже если прежний уже готов.
|
||
Без таких альтернатив разрешён прежний канал; DOWN последней альтернативы
|
||
пробуждает sender. ACK/control/burst-FILLER проходят во время ожидания DATA.
|
||
Курсор и номер попытки меняются только после принятия DATA транспортом.
|
||
Новых состояний, ресурсов и таймеров нет; формула RTO сохранена.
|
||
|
||
Причины пауз разделены буферизованным трейсом первых800мс отказа: TX/RX/ACK,
|
||
снятие попытки, возврат в очередь, выбор пути, доставка сообщения и состояние очередей.
|
||
В slow-drop первый повтор начинается через232мс после отказа, ещё13мс занимает
|
||
доставка по рабочему пути. Исходная попытка достигла своего RTO около251мс;
|
||
разница объясняется временем в пути до включения отказа. При двух зависших
|
||
каналах ротация может последовательно выбрать оба, добавляя их ожидания RTO.
|
||
Под нагрузкой основная проблема — повторный выбор прежнего зависшего пути,
|
||
когда рабочая альтернатива занята pacer. В диагностическом load-drop за первые800мс
|
||
345 из677 повторов снова выбрали свой прежний путь; ожидание в send_q — медиана101мкс,
|
||
максимум2590мкс. Для load-hold —325 из676, медиана82мкс, максимум3503мкс.
|
||
Следовательно, перенос повтора из хвоста в голову не объясняет и не устраняет
|
||
основную паузу этих профилей. Такой диагностический контроль также не проходит
|
||
порог; изменение порядка очереди в производственный код не перенесено.
|
||
Исключение пути после RTO в отдельном контроле сокращает паузу load-drop до41мс,
|
||
но это диагностический опыт без полноценной модели проб/восстановления,
|
||
он не переносился в production. Исправлено именно правило ожидания альтернативы.
|
||
|
||
Регрессия до исправления падает на отправке через готовый прежний путь при
|
||
заблокированном pacer другого UP-линка. После исправления windows44 сценария PASS
|
||
с управляемыми и реальными часами. Проверены ожидание cwnd/pacer без новых
|
||
попыток/таймеров, ACK и burst во время ожидания, освобождение альтернативы,
|
||
DOWN/удаление последней альтернативы, неинициализированный линк и поздний ACK.
|
||
Восемь профильных тестов ASAN/UBSan с detect_leaks=1 проходят без ошибок/утечек.
|
||
Полный check.sh после исправления:184/184, proxy9/9, burst1/1, load8/8 PASS,
|
||
134217728 проверенных байт,161с; /tmp/etcp-retry-alternative-full-check.log.
|
||
|
||
Штатные профили отказов 8dd03c2b:7 из9 проходят прежние пороги. DROP/HOLD под
|
||
нагрузкой проходят каждый3/3: паузы43.172/44.755/43.493мс и40.965/39.796/44.982мс,
|
||
устойчивая полезная скорость7.189..7.476 из8Мбит/с оставшегося канала.
|
||
Для насыщенного источника тест ограничивает паузу и steady goodput, а не latency
|
||
каждого сообщения; измеренные максимальные latency102.572..114.403мс не скрыты.
|
||
Slow-drop остаётся FAIL: пауза238.569мс, latency264.727мс. Two-stuck — пауза219.338мс,
|
||
latency380.177мс. Это4 нарушения порогов; доставка/порядок/ресурсы проходят во всех9.
|
||
Скоростная матрица целиком после этого изменения не повторялась; дуплекс не закрыт.
|
||
Логи: /tmp/etcp-retry-alternative-{windows,real-time}.log,
|
||
/tmp/etcp-retry-alternative-asan-test_*.log, /tmp/etcp-retry-alternative-<profile>-<1..3>.log/.csv.
|
||
Диагностические исходники/сборка: /tmp/etcp-recovery-probe.c/.mk;
|
||
логи /tmp/etcp-recovery-{trace,priority,exclude}-<profile>.log.
|
||
|
||
Решение пользователя,24.10.2026: ранние копии до RTO исключены из плана.
|
||
Пока путь UP, срок повтора определяется его RTO; закрытие линка по-прежнему
|
||
снимает его попытки и возвращает недоставленные DATA без ожидания этого срока.
|
||
Ротация ждёт готовности другого initialized+UP пути, не возвращаясь в прежний,
|
||
пока такая альтернатива существует. Формула RTO остаётся согласованной:
|
||
clamp(ceil(1.2*RTT+1.5*jitter+3мс),10мс,1с), с округлением до микросекунды.
|
||
При RTT200мс и нулевом jitter RTO уже243мс; фиксированный порог паузы100мс
|
||
не учитывает это обязательное ожидание. Текущие пороги тестов не менялись,
|
||
приведённые выше FAIL сохранены. Следует отдельно оценивать ожидание RTO,
|
||
задержку готового повтора и доставку через выбранную альтернативу.
|
||
Дальнейшие механизмы здоровья каналов и STCP backpressure не предполагают
|
||
спекулятивного дублирования DATA до RTO. Прежние предложения ранних копий
|
||
в исторических результатах ниже отменены этим решением.
|
||
|
||
Состояние после исправления pacer, до уточнения правила ретрансмита:
|
||
|
||
Исправление pacer реализовано: таймер назначается на первый TB, разрешающий отправку,
|
||
а не на полное погашение долга. Кредит и допустимый положительный долг по-прежнему
|
||
ограничены 1мс; BBR/cwnd/min_rtt, RTO, wire-формат и порядок callbacks не менялись.
|
||
Новых полей, очередей и таймеров нет. Успешная постановка таймера видна в ETCP TRACE:
|
||
link/rate_Bps/now_tb/debt_until_tb/fraction/delay_tb. Контракт отражён в .h и протоколе.
|
||
|
||
Регрессия до исправления падает: при долге10TB таймер получает10TB вместо первого
|
||
допустимого1TB. После исправления проверены18 сочетаний долга/дроби, строгая граница,
|
||
INT_MAX и повторная постановка при раннем вызове. Callback с опозданием100мс сохраняет
|
||
ограниченный burst; OOM повторного таймера закрывает соединение без ложного ready.
|
||
Настоящий uasync callback проверен с reset и close, освобождающим физический линк.
|
||
Windows42 сценария PASS и с управляемыми часами, и с UTUN_TEST_REAL_TIME=1.
|
||
Восемь тестов windows/BBR/session/transport/init_liveness/STCP traffic/NCD/control_server
|
||
проходят ASAN/UBSan с detect_leaks=1, без ошибок и утечек. check.sh:184/184,
|
||
proxy9/9, burst1/1, load8/8 PASS,134217728 проверенных байт,162с.
|
||
|
||
Штатная последовательная multipath-матрица c7a53009 после калибровки эмулятора:
|
||
FAIL,14 нарушений прежних порогов. Восемь относятся к скорости дуплекса, включая
|
||
одиночные каналы; шесть — к паузам/задержкам slow-drop,two-stuck,load-drop/load-hold.
|
||
Агрегированный goodput (min(application,fresh DATA),Мбит/с): equal14.438/16,
|
||
bandwidth9.052/10, rtt14.299/16, fast-distant8.836/10, slow-distant9.343/10,
|
||
three11.125/12, bidirectional8.394/8.458 из10 на каждое направление.
|
||
Первые шесть скоростных профилей проходят. Drop-forward проходит с паузой58.001мс
|
||
и задержкой83.656мс. Оставшиеся паузы: slow-drop239.421мс, two-stuck201.381мс,
|
||
load-drop213.644мс, load-hold208.899мс; задержки slow-drop264.044мс и two-stuck482.693мс.
|
||
Все профили завершают доставку и контроль порядка/ресурсов. Это один прогон;
|
||
изменение отдельных скоростей/пауз относительно контроля не доказывает устойчивый эффект.
|
||
Полная матрица ещё не проходит. Далее отдельно разбирать ограничения BBR/cwnd и PROBE_RTT,
|
||
затем ранние копии/здоровье каналов и STCP backpressure; этот фикс их не реализует.
|
||
Логи: /tmp/etcp-pacer-ready-{windows-final,real-time,full-check}.log,
|
||
/tmp/etcp-pacer-ready-asan-test_*.log, /tmp/etcp-pacer-ready-matrix.log/.csv.
|
||
|
||
Диагностика и проверка плана до исправления,24.10.2026:
|
||
|
||
Контроль текущей master a22a27b7,24.10.2026. Эмулятор прошёл калибровку2/8Мбит/с,
|
||
затем штатная матрица выполнена последовательно с прежними порогами:15 нарушений.
|
||
Агрегированный goodput (min(application,fresh DATA),Мбит/с): equal14.238/16,
|
||
bandwidth9.000/10, rtt13.855/16, fast-distant8.602/10, slow-distant8.817/10,
|
||
three10.669/12, bidirectional8.335/8.212 из10 на каждое направление.
|
||
Первые пять профилей проходят. Three: единственный отказ одиночного канала
|
||
2Мбит/с RTT200мс —1.695 вместо1.700; три отдельных повтора профиля проходят,
|
||
этот одиночный канал даёт1.703..1.707. Дуплекс нарушает6 скоростных порогов.
|
||
Ещё8 нарушений — пауза/задержка при drop-forward,slow-drop,two-stuck,load-drop/load-hold.
|
||
Максимальные паузы соответственно122.308,244.610,208.533,304.969/178.050мс.
|
||
Все профили завершают доставку и контроль ресурсов; ошибок формата/порядка/REINIT нет.
|
||
Логи: /tmp/etcp-multipath-current-{build,dummynet,matrix}.log,
|
||
/tmp/etcp-multipath-current-matrix.csv, /tmp/etcp-three-current-{1,2,3}.log/.csv.
|
||
|
||
Первое изолированное исправление после контроля — таймер pacer.
|
||
Буферизованный трейс DATA/ACK/выбора пути/каждого BBR sample записывается в память,
|
||
выводится после замера и не теряет события. На одиночном дуплексном8Мбит/с RTT80мс
|
||
за4с BBR оставался PROBE_BW в обе стороны, но pacer терял6.5/9.4% заданного темпа.
|
||
Расчётные бюджеты отправки3,733,752/3,480,084 байт, фактические DATA TX3,491,124/3,151,512;
|
||
сброс кредита объясняет240,408/328,214 байт дефицита. После ожидания самого pacer
|
||
потеряно168,861/290,263 байта; остальное связано с ожиданием окна.
|
||
|
||
Причина: shaper_arm не ставит таймер, если долг меньше уже разрешённого допуска1мс,
|
||
но при большем долге ждёт его полного погашения. Округление uasync до миллисекунд
|
||
и поздний callback приводят к выходу за кредит1мс; следующий send отбрасывает этот кредит.
|
||
Готовность должна наступать при debt_tb<SHAPER_BURST_DELAY_TB:
|
||
первый допустимый TB = shaper_load_time_tb - SHAPER_BURST_DELAY_TB +1.
|
||
При текущем условии постановки delay>=1; +1 сохраняет строгую границу при любой
|
||
дробной части. Ограничение INT_MAX применяется после расчёта; callback уже повторно
|
||
вызывает shaper_arm перед уведомлением отправителя. Нового состояния/таймера не нужно.
|
||
|
||
Прототип в /tmp сдвигает только etcp_shaper timeout к этой границе, сохраняя кредит1мс,
|
||
обычный BBR/PROBE_RTT, расчёт окна/min_rtt и пороги теста. Два последовательных опыта:
|
||
потеря после pacer вместо169/290КБ составляет2..9КБ за4с; потери после ожидания cwnd остаются.
|
||
Это устраняет95..99% изолированной потери pacer, но профиль дуплекса в целом ещё FAIL.
|
||
Отдельный контроль без PROBE_RTT показывает вклад этой фазы в просадку агрегата,
|
||
однако также не проходит всю матрицу; отключение PROBE_RTT не предлагается как исправление.
|
||
|
||
При переносе выполнены: регрессия позднего timer callback при незаполненном cwnd, проверка
|
||
границы допуска с нулевой/ненулевой дробью и сохранения ограниченного burst после
|
||
долгого простоя; смена ставки/0/unlimited, INT_MAX, OOM таймера, reset/close из callback.
|
||
Профильные тесты и ASAN/UBSan проходят; полная матрица повторена последовательно,
|
||
результаты приведены выше. Следующими отдельно разбирать оставшиеся ограничения BBR/cwnd,
|
||
периодические PROBE_RTT и ранние копии; первый фикс не закрывает эти пункты.
|
||
Артефакты: /tmp/etcp-probe-review.c/.mk, /tmp/etcp-duplex-waits.log,
|
||
/tmp/etcp-ready-timer-{1,2}.log/.csv, /tmp/etcp-pacer-review-analysis.py,
|
||
/tmp/etcp-pacer-review-results.json. Во время исходных контрольных опытов код протокола
|
||
в репозитории не менялся; производственное исправление и его проверки описаны выше.
|
||
|
||
История реализации и предыдущих проверок,23.10.2026:
|
||
|
||
В fix/etcp-multipath-recovery реализованы начальный pacing, точные DATA B/s,
|
||
пересчёт pacing-долга, ограниченный кредит 1мс для округления таймеров и выход
|
||
STARTUP в DRAIN после целого раунда RTT выше min_rtt+бюджет очереди.
|
||
Предел PROBE_BW — max(4*MSS, BDP + BW*20мс), включая ACK/PROBE_UP резервы.
|
||
Это мягкая цель модели, не гарантия физической очереди <=20мс во всех состояниях.
|
||
|
||
Логический seq/payload отделён от физических ETCP_TX_ATTEMPT. Новый DATA содержит
|
||
seq:u32+attempt_no:u16; ACK своего пути содержит seq+attempt_no+receiver_delay_us.
|
||
Номер 1..65535 общий для всех линков одного seq; индекс очередей — внутренний
|
||
ключ (seq<<16)|attempt_no. При исчерпании номера или seq выполняется смена эпохи,
|
||
пара не оборачивается. По сравнению с прежним64-битным wire ID DATA короче на6 байт
|
||
(заголовок7 вместо13), ACK-запись —10 вместо16 байт; normalizer не менялся.
|
||
Till подтверждает только логическую доставку. Собственные очереди попыток,
|
||
ACK, RTT/jitter и RTO принадлежат линку. RTO=clamp(1.2*RTT+1.5*jitter+3мс,10мс,1с),
|
||
с округлением вверх до целой микросекунды;
|
||
до DATA ACK используется KA RTT того же пути, без него начальное значение 1с.
|
||
Retry обходит каналы относительно last_link фрагмента; курсор фиксируется только
|
||
после принятия send транспортом. Поздние/чужие ACK не меняют чужой flight/BBR.
|
||
ACK создаётся после сохранения DATA в recv_q. Потерянный till запрашивается,
|
||
когда окно sequence заполнено; ответ идёт по запросившему пути, без DATA.
|
||
Физические попытки ограничены 16384 на линк/32768 на соединение, ACK — 16384 на линк.
|
||
|
||
Проверка компактного номера,23.10.2026: windows38 сценариев, session/BBR/transport/init_liveness PASS.
|
||
Те же5 тестов ASAN/UBSan с detect_leaks=1 без ошибок/утечек. Проверены одинаковые
|
||
номера разных seq, ACK другого линка/дубликат/ноль, rollback send без возврата номера,
|
||
полный номер65535 в wire, исчерпание номера и seq с REINIT. Реальный AES/ingress
|
||
отвергает старый ACK при повторении пары seq=1/no=1 в новой эпохе.
|
||
Смена эпохи из input_queue callback прекращает текущую сборку пакета, включая burst.
|
||
check.sh184/184, proxy9/9, burst1/1, load8/8 PASS,165с; load проверил134217728 байт.
|
||
Отдельный bandwidth2+8Mbit/s RTT20/20:8.715Mbit/s, retries=0, очереди24.2/20.1мс, PASS.
|
||
Логи: /tmp/etcp-attempt16-full-check.log, /tmp/etcp-attempt16-{windows,session}.log,
|
||
/tmp/etcp-attempt16-asan-*.log, /tmp/etcp-attempt16-bandwidth.log/.csv.
|
||
Приведённые ниже прежние скоростные замеры относятся к исходному8-байтовому ID;
|
||
на момент той проверки матрица разных RTT/дуплекса после сокращения поля не повторялась.
|
||
|
||
Точечные проверки: BBR 20/20, windows 26 сценариев PASS. Восемь тестов
|
||
BBR/windows/session/transport/STCP traffic/NCD/stream_tx_stats/control_server проходят ASAN/UBSan
|
||
с detect_leaks=1, без отчётов об ошибках/утечках.
|
||
Логи: /tmp/etcp-bounds-windows.log, /tmp/etcp-delay-bbr.log,
|
||
/tmp/etcp-attempts-asan-test_*.log.
|
||
Основной регрессионный набор: 183/183 PASS (после адаптации fixture метрик).
|
||
check.sh завершён успешно за166с: дополнительные tcp_proxy_full, burst и load PASS;
|
||
load — 8/8 соединений,134217728 байт с проверкой содержимого за13.66с.
|
||
Лог: /tmp/etcp-attempts-final-check.log. Он не включает долгую multipath-матрицу.
|
||
|
||
До изменения формулы RTO штатная multipath-матрица FAIL: 20 нарушений порогов.
|
||
RTT20/200мс: 13.552/16Мбит/с, длинный канал wire7.173/8Мбит/с;
|
||
после исправления STARTUP пики очереди около 23мс вместо 220мс.
|
||
Есть недобор bidirectional (обратное направление 7.116/10Мбит/с).
|
||
Обычный DROP/HOLD с RTT20мс: пауза около 61–62мс. slow-drop с RTT200мс:
|
||
пауза 401мс; два застрявших канала — 676мс; под нагрузкой — 229–243мс.
|
||
Ни пороги, ни прогрев тестов не ослаблены. Ранние копии/здоровье каналов и
|
||
ограничение нижележащей STCP-очереди ещё не реализованы.
|
||
Полный лог: /tmp/etcp-attempts-matrix.log.
|
||
|
||
После смены RTO на 1.2*RTT+1.5*jitter+3мс: один полный прогон матрицы,
|
||
15 нарушений порогов; в установившихся интервалах исправных каналов retries=0.
|
||
Пауза slow-drop: 400.869 -> 243.650мс; two-stuck: 675.741 -> 277.974мс.
|
||
Под нагрузкой load-drop/load-hold: 243.407/229.179 -> 95.533/83.085мс.
|
||
Пороги не менялись. Недобор части RTT/BW/bidirectional профилей, drop-forward,
|
||
slow-drop и two-stuck ещё не закрыты. Это сравнение единичных прогонов.
|
||
31 сценарий windows, включая 20 наборов RTT/jitter/KA для реального таймера,
|
||
BBR20/20, session и transport PASS. windows/session/transport также проходят
|
||
ASAN/UBSan с detect_leaks=1. Целочисленная формула проверена на округление,
|
||
обе границы, UINT32_MAX, неизвестный RTT и приоритет DATA RTT перед KA RTT.
|
||
Логи: /tmp/etcp-rto-margin-matrix.log/.csv, /tmp/etcp-rto-margin-test_*.log,
|
||
/tmp/etcp-rto-margin-asan-test_*.log.
|
||
|
||
Дополнительный jitter-контроль: штатные ядро/шифрование/dummynet, RTT20/200мс,
|
||
8+8Мбит/с, случайная добавка 0..2/10/30мс в каждом направлении, без заданных потерь.
|
||
Исходники диагностического стенда и контрольная сборка со старой формулой — только /tmp.
|
||
Новая/старая формулы дали 14.272/14.214, 13.932/13.754, 7.561/8.235Мбит/с.
|
||
В измеряемых интервалах новой формулы повторы по двум путям: 0/0, 3/3, 0/8;
|
||
старой — 0/0 для всех трёх профилей. Полная доставка/порядок/память проверены.
|
||
Это единичные диагностические прогоны: оптимальность коэффициентов не установлена,
|
||
а уменьшение запаса при jitter создаёт отдельные лишние повторы. Недобор при
|
||
jitter30мс есть у обеих формул и требует отдельного изучения модели/переупорядочивания.
|
||
Логи: /tmp/etcp-rto-jitter-review.log, /tmp/etcp-rto-jitter-old.log;
|
||
стенд: /tmp/etcp-rto-jitter-review.c/.mk, /tmp/etcp-rto-old-control/.
|
||
Полный check.sh после смены формулы: 182/183, FAIL только test_bgp_route_exchange
|
||
(ожидание удаления A->C после отключения обоих линков). Это падение наблюдалось
|
||
и до смены формулы; отдельный повтор с UTUN_TEST_DEBUG=1 снова PASS. Причина
|
||
зависимости от запуска не установлена. test_encrypted_attachment в этом прогоне PASS;
|
||
ранее подтверждённый дефект удаления активного temp по-прежнему остаётся открытым.
|
||
Логи: /tmp/etcp-rto-margin-full-check.log, /tmp/etcp-rto-margin-bgp-repeat.log.
|
||
|
||
Дефект BGP-падения при односторонней потере A->B исправлен в ETCP.
|
||
Повторный INIT с flag_up=0 больше не сохраняет устаревший remote_keepalive=1:
|
||
RX/remote/status учитываются до перезаписи входного буфера ответом. При DOWN
|
||
handshake отвечает и запускает keepalive для двусторонней проверки. Живой
|
||
альтернативный линк сохраняет UP соединения. RX защищён от close/удаления
|
||
линка из callbacks; повторы прежнего состояния не создают новых DOWN/UP.
|
||
INIT_RESPONSE проверяет полное тело >=64 байт без timestamp/flag_up и node_id
|
||
до изменения состояния; прежняя проверка общей длины пропускала тело 61..63 байта.
|
||
Новых таймеров, очередей, полей или изменений формата нет; пороги тестов сохранены.
|
||
test_etcp_init_liveness: 130 отказов с сохранением link/conn и владения pkt,
|
||
12 допустимых ответов, потери в обе стороны/оба порядка ключей, восстановление,
|
||
резервный линк, close/удаление линка из INIT и KA callbacks PASS.
|
||
Тест проходит также с обычными часами. Семь профильных тестов ASAN/UBSan
|
||
с detect_leaks=1 PASS; дополненный сценарий удаления линка повторён отдельно.
|
||
BGP route exchange: 100/100 PASS (A<B:48, A>B:52; последние пять с debug).
|
||
Основной регрессионный набор: 184/184 PASS. Логи:
|
||
/tmp/etcp-init-boundary-before.log, /tmp/etcp-init-final-{liveness,realtime,asan-liveness}.log,
|
||
/tmp/etcp-init-fix-asan-test_*.log, /tmp/etcp-init-bgp-stable-*.log,
|
||
/tmp/etcp-init-fix-full-check.log. Скоростные ограничения и ранние копии остаются отдельной задачей.
|
||
|
||
Качество мультилинка: три последовательных прогона каждого скоростного профиля,
|
||
3с прогрев +4с измерение, штатный test_clock, UDP/dummynet и настоящее шифрование.
|
||
Ниже полезная скорость min..max в Мбит/с (минимум application и fresh DATA, как в тесте):
|
||
8+8, RTT20/20мс: 13.868..14.083 из16; 95.7..98.5% суммы одиночных скоростей.
|
||
2+8, RTT20/20мс: 8.754.. 9.066 из10; 99.7..102.9% суммы одиночных.
|
||
8+8, RTT20/200мс: 13.567..13.951 из16; 94.8..96.8% суммы одиночных.
|
||
2+8, RTT20/200мс: 8.467.. 8.534 из10; 95.3..96.3% суммы одиночных.
|
||
8+2, RTT20/200мс: 8.683.. 9.087 из10; 96.6..101.8% суммы одиночных.
|
||
2+8+2, RTT20/80/200мс: 10.566..10.749 из12; 99.0..102.8% суммы одиночных.
|
||
2+8, RTT20/80мс, одновременно в обе стороны:
|
||
A->B: 8.153.. 8.318 из10;
|
||
B->A: 8.080.. 8.379 из10.
|
||
Утилизация UDP-полосы агрегата с учётом протокольных байтов — 90.9..97.4% для
|
||
обычных профилей; меньшее приложение учитывает накладные расходы ETCP/шифрования.
|
||
В измеряемых исправных интервалах всех трёх прогонов DATA retries=0.
|
||
Полная доставка после drain, порядок, содержимое, баланс попыток и память проверены.
|
||
Превышение 100% от суммы solo возможно: solo/aggregate измеряются в разных окнах,
|
||
с независимыми фазами BBR; эти проценты не являются процентом физической полосы.
|
||
Недобор на длинных/двусторонних профилях сохраняется. В обратном solo-направлении
|
||
8Мбит/с RTT80мс получено лишь 4.514..4.972Мбит/с; причина в этом прогоне не диагностировалась.
|
||
Все замеры: /tmp/etcp-multilink-quality-20261023.csv/.txt.
|
||
Логи повторов: /tmp/etcp-quality-r{2,3}-{profile}.log/.csv; первый — /tmp/etcp-rto-margin-matrix.log/.csv.
|
||
|
||
Коррекция BW в рабочей реализации, 23.10.2026
|
||
|
||
По выбору пользователя перенесена только граница BW по приёмной точке пути.
|
||
Пара rx_data_bytes/rx_data_time_us добавляется после entries лишь при count>0:
|
||
ACK имеет размер8+10*count+(count>0?16:0), пустой ACK остаётся8-байтовым.
|
||
При первоначальной проверке BW ниже использовался прежний8-байтовый attempt_id
|
||
и16-байтовый ACK entry; текущий компактный формат описан в начале документа.
|
||
Снимок точки хранится в физической попытке, новые очереди/таймеры не нужны.
|
||
После пустого flight база начинается заново. Перестановка ACK не откатывает
|
||
точку, неизвестный/повторный ID её не меняет; переполнения проверяются.
|
||
Формула, единицы и формат: doc/etcp_protocol.txt, etcp_tx.h, etcp_connections.h.
|
||
min_rtt/RTT, политика окна, RTO и дедлайн/пакетирование ACK не менялись;
|
||
busy-флаг и дуплексный коэффициент не добавлены. Обе стороны используют новый формат ACK.
|
||
|
||
Windows35 сценариев, session, transport, init_liveness и BBR PASS;
|
||
те же пять тестов ASAN/UBSan с detect_leaks=1 PASS, без ошибок и утечек.
|
||
Проверены сжатие ACK (170502/186359 вместо170502/150732), перестановка/дубли,
|
||
недостоверные точки, простой/reset, независимые часы, округление/переполнение,
|
||
границы MTU, усечённые ACK и ограниченный диагностический буфер.
|
||
Полный check.sh:184/184 PASS, proxy9/9, burst1/1 и load8/8 PASS;
|
||
load проверил134217728 байт. Лог: /tmp/etcp-bw-correction-full-check.log.
|
||
Логи sanitizer: /tmp/etcp-bw-asan-{windows,session,bbr,transport,init_liveness}.log.
|
||
|
||
Отдельный последовательный bidirectional: агрегат2+8Mbit/s RTT20/80мс,
|
||
goodput8.223/8.182Mbit/s, очереди путей A->B22.8/22.7мс, B->A19.9/21.9мс,
|
||
DATA retries=0 в измеряемом интервале. Профиль FAIL:5 прежних порогов скорости,
|
||
включая solo; агрегат не достиг8.5Mbit/s на направление. Пороги не ослаблены.
|
||
bandwidth2+8Mbit/s RTT20/20мс:8.844Mbit/s, очереди24.2/20.7мс, retries=0,
|
||
профиль PASS. Это единичные контрольные прогоны, не гарантия максимальной утилизации.
|
||
RTT20/200мс,8+8Mbit/s:13.453Mbit/s, очереди19.8/24.0мс, retries=0;
|
||
профиль FAIL по3 прежним порогам скорости. Недобор разных RTT остаётся открытым.
|
||
Логи/CSV: /tmp/etcp-bw-duplex-final.log/.csv, /tmp/etcp-bw-bandwidth.log/.csv, /tmp/etcp-bw-rtt.log/.csv.
|
||
Первый диагностический дуплексный прогон совпал со сборкой/другими проверками
|
||
(/tmp/etcp-bw-duplex.log/.csv); для оценки качества используется отдельный повтор выше.
|
||
|
||
Проверка плана исправления дуплекса до внедрения BW, 23.10.2026
|
||
|
||
Пакетный трейс нашёл ACK compression при осушении обратной очереди: для DATA
|
||
seq257..413 принято 170502 байта за186359мкс, а max(send_elapsed,ack_elapsed)
|
||
составил150732мкс. BBR получил1.131MB/s вместо фактических0.915MB/s.
|
||
В одном устойчивом участке DATA-pacing A=1.104MB/s при BWlim=8Mbit/s;
|
||
очередь A->B держится около40мс и задерживает ACK обратных DATA.
|
||
RTT приходит в BBR на каждом ACK (около124мс), причина не в устаревшем RTT.
|
||
Полный трейс500мс: /tmp/etcp-duplex-packets.log/.csv; история разгона:
|
||
/tmp/etcp-duplex-overload-start-v2.log, /tmp/etcp-duplex-overload-history-v2.csv.
|
||
|
||
Прототипы этих опытов находятся в /tmp/etcp-duplex-native/. На момент опытов исходники проекта
|
||
не менялись. В прототипе wire ACK расширен с8 до24 байт парой rx_bytes:u64
|
||
и rx_time_us:u64; это одна согласованная точка физического приёма DATA пути.
|
||
Счётчик включает допустимые физические копии DATA; это не логический till.
|
||
Попытка хранит последнюю известную точку при отправке. Дополнительная граница
|
||
rate sample: interval >= ceil(delivered * delta_rx_time / delta_rx_bytes).
|
||
Это сохраняет прежние границы send/ACK и не увеличивает оценку полосы.
|
||
В отличие от первого опыта с восстановлением времени из16-битного timestamp,
|
||
согласованная пара счётчик/время не использует агрессивную инвалидацию samples
|
||
при обратном ходе восстановленного timestamp. Первый опыт ухудшал результат
|
||
и не предлагается для внедрения.
|
||
|
||
В header проверен флаг DATA-backlog + недавней передачи своего пути.
|
||
В окне применяется отдельное T_effective=k*min_rtt+delta; min_rtt не меняется.
|
||
Для опыта TTL=20мс, проверяется при расчёте политики на ACK. Это ещё не полный
|
||
контракт затухания при молчании: свежесть нужно проверять и при выборе DATA,
|
||
чтобы не ждать следующего ACK для снятия дополнительного запаса.
|
||
|
||
Пять вариантов, по три последовательных запуска штатного bidirectional:
|
||
по3с прогрев +4с измерение, solo каждого канала и агрегат2+8Mbit/s RTT20/80мс.
|
||
Ниже min..max goodput A->B / B->A агрегата и максимальная очередь всех повторов:
|
||
штатный wire: 7.930..8.610 / 8.309..8.503Mbit/s; 52.9мс;
|
||
ACK+16 байт, без новой границы:8.113..8.189 / 8.032..8.320Mbit/s; 53.7мс;
|
||
новая граница BW: 8.095..8.236 / 8.154..8.396Mbit/s; 23.1мс;
|
||
граница + k=282/256, delta=3мс:8.179..8.404 / 8.142..8.171Mbit/s; 35.5мс;
|
||
граница + k=1, delta=10мс: 8.146..8.234 / 8.133..8.142Mbit/s; 33.7мс.
|
||
В измеряемых интервалах DATA retries=0. Все15 запусков не проходят весь
|
||
исходный скоростной профиль; пороги не менялись. Увеличение окна не дало
|
||
устойчивого выигрыша и увеличило очереди: выбирать коэффициент пока рано.
|
||
|
||
Ещё3 опыта one-way -> duplex -> one-way на том же соединении PASS:
|
||
флаг/поправка не активны у отправителя в односторонних измеряемых фазах,
|
||
включаются при встречной загрузке и отключаются после её завершения.
|
||
Полная доставка, порядок, состояние сессии, попытки/очереди и память проверены.
|
||
ASAN/UBSan с detect_leaks=1: phases PASS; bidirectional FAIL по скоростным
|
||
порогам, отчётов sanitizer/утечек и нарушения полной доставки нет.
|
||
|
||
ACK учтены по принятым пакетам: ACK-only — вся UDP-кодограмма с шифрованием
|
||
и служебными полями; piggyback — только ACK-секция внутри DATA, без повторного
|
||
начисления общих заголовков DATA. Единицы BWlim — UDP-байты без IP/UDP headers.
|
||
Для штатного агрегата ACK=0.515..0.559Mbit/s на направление, 5.15..5.59%
|
||
суммарного BWlim и5.50..5.93% фактического UDP-трафика. В среднем ACK-only:
|
||
0.445..0.455Mbit/s, ACK внутри DATA:0.088..0.090Mbit/s.
|
||
По отдельным каналам ACK занимают0.121..0.131 из2Mbit/s и0.384..0.431 из8Mbit/s.
|
||
При безусловном добавлении16 байт телеметрии ACK расходует0.707..0.743Mbit/s;
|
||
с новой границей BW —0.745..0.763Mbit/s. Накладные расходы реальны и учтены.
|
||
|
||
Итоговый план после выбора только BW пользователем:
|
||
1. Выполнено: сохранить измеренный min_rtt и отдельные send/ACK/receiver интервалы.
|
||
Точку счётчик/время передавать только с положительными ACK, которые её
|
||
используют; не раздувать каждый DATA с пустой ACK-секцией. Изменение
|
||
формата выполнить целиком для TX/RX/dump/MTU и тестов.
|
||
2. Отложено, в текущий фикс не входит: испытать объединение ACK. Piggyback на готовом DATA — сразу; ACK-only
|
||
по существующему дедлайну2мс. Запрос границы окна/срочный ответ не задерживать.
|
||
Один таймер должен обслуживать ближайший deadline очередей путей, включая
|
||
повторное планирование после частичного piggyback/раннего пробуждения.
|
||
Сейчас посторонний wake sender может отправить ACK-only до этого срока.
|
||
3. Сначала повторить скорости/очереди с корректной оценкой BW и экономной
|
||
упаковкой ACK. Дуплексный коэффициент не вводить как обязательную часть
|
||
фикса без устойчивого выигрыша в дополнительных RTT/BW/асимметричных профилях.
|
||
4. Выполнено для BW: проверить reordering/loss/duplicate ACK, поздние попытки,
|
||
reset/recreate пути и простой; валидировать пару counter/time. Если новая
|
||
граница не представима в uint32 interval, пропустить оценку BW, а не
|
||
насыщать интервал с нарушением верхней границы скорости. TTL флага не относится
|
||
к текущей реализации: флаг не добавлялся.
|
||
|
||
Результаты всех18 запусков: /tmp/etcp-duplex-native/results.json,
|
||
/tmp/etcp-duplex-native/summary.csv, /tmp/etcp-duplex-native/ack_bandwidth.csv.
|
||
Сборка: /tmp/etcp-duplex-native/experiment.mk; код/логи сохранены рядом.
|
||
Sanitizer: /tmp/etcp-duplex-native-asan/{bidirectional,phases}.log.
|
||
|
||
OOM следующей ступени приёма исправлен аварийным перезапуском сессии.
|
||
До сохранения RX DATA в recv_q отказ остаётся обычной потерей без ACK.
|
||
После принятия данных OOM в packer/unpacker или TX inflight запускает новую
|
||
локальную эпоху: старый поток отбрасывается, DATA блокируются до подтверждения.
|
||
Физические линки сохраняются; владельцы после REINIT восстанавливают операции
|
||
своего протокола. Это прерывание сессии, без гарантии воспроизведения старых DATA.
|
||
Удалено восстановление старых inflight-фрагментов через realloc и normalizer.
|
||
queue_clear и очистка etcp_conn_reset не выделяют память; normalizer не получил
|
||
новых OOM-очередей, cursor или retry-таймеров. Отдельно от очистки согласование
|
||
эпох требует памяти: OOM control-пакета допускает повтор в пределах исходных 10с,
|
||
отказ обязательного таймера или RNG локальной эпохи приводит к явному закрытию.
|
||
После reset/close из пользовательского callback разбор старого фрагмента прекращается.
|
||
|
||
Новые проверки: windows31 сценарий PASS, ll_queue_regressions77/77 PASS.
|
||
Инъекции отказов покрывают RX первую/следующую кодограмму фрагмента, TX буфер,
|
||
метаданные фрагмента/inflight, flush/waiter, отказ очереди, RNG и session-таймеры.
|
||
При запрете всех allocator-ов etcp_conn_reset не делает ни одной попытки выделения.
|
||
Шифрованный двусторонний тест проходит настоящий HELLO/CHALLENGE/CONFIRM:
|
||
REINIT у обоих владельцев, четыре линка UP, старые DATA отклонены, новые доставлены.
|
||
Десять тестов очередей/normalizer/ETCP/STCP/NCD/метрик проходят ASAN/UBSan
|
||
с detect_leaks=1. Логи: /tmp/etcp-oom-reinit-asan-test_*.log,
|
||
/tmp/etcp-abort-faults-final.log, /tmp/etcp-abort-encrypted-recovery.log.
|
||
Исходное воспроизведение потери: /tmp/etcp-unpack-admission-review.c/.mk/.log.
|
||
Полный check.sh после OOM-правок: 181/183, FAIL test_bgp_route_exchange и
|
||
test_encrypted_attachment. BGP отдельно с диагностикой PASS; причина нестабильности
|
||
общего прогона не установлена. Вложения отдельно снова FAIL: очистка кеша удаляет
|
||
активный .uenc.tmp.<id>, затем запись BLOCK_CHUNK получает ENOENT. В этом сценарии
|
||
ETCP не сбрасывается. Отдельная задача внесена в doc/tasks.md; media-код не изменялся.
|
||
Логи: /tmp/etcp-oom-reinit-full-check.log,
|
||
/tmp/etcp-oom-reinit-repeat-test_bgp_route_exchange.log,
|
||
/tmp/utun_encrypted_attachment_HwkBUM/test.log.
|
||
Отдельный интеграционный прогон proxy/burst/load PASS: proxy9/9, burst1/1,
|
||
load8/8 с 134217728 проверенными байтами за13.03с. Сетевые ресурсы очищены.
|
||
Лог: /tmp/etcp-oom-reinit-proxy-check.log.
|
||
|
||
Исходные нарушения до реализации (пункты 1–5 и вход recv_q из пункта 8 исправлены)
|
||
|
||
1. ACK выбирает канал независимо от DATA: select_link(conn, 0), inflight/RR.
|
||
Одна общая ack_q смешивает подтверждения DATA разных каналов. Поэтому интервал
|
||
delivery sample включает фактический обратный путь ACK, а rtt_us берётся
|
||
из отдельного timestamp-обмена last_link. Для RTT 20/200мс измерены возвраты
|
||
быстрых DATA через медленный канал за >=112мс при RTT модели около 24мс.
|
||
Это несовместимые исходные данные для BDP, pacing и бюджета очереди.
|
||
|
||
2. Общий ACK.till вызывает тот же etcp_ack_recv(), что и явный ACK элемента.
|
||
Даже если явные ACK возвращать своим путём, till подтверждает DATA других
|
||
каналов и обновляет их BBR в чужой момент времени. Перенаправить ACK недостаточно.
|
||
|
||
3. INFLIGHT_PACKET хранит только последнюю попытку: last_link, last_timestamp,
|
||
delivery snapshots. При повторе они заменяются, ACK содержит только общий seq.
|
||
Поздний ACK старой копии может быть приписан новой попытке и другому каналу.
|
||
Неоднозначны и RTT, и скорость, и выбор окна, которое освобождается.
|
||
|
||
4. В начальном pacing есть отдельный дефект. bbr_update_min_rtt() устанавливает
|
||
has_seen_rtt до bbr_set_pacing_rate(), поэтому предусмотренная инициализация
|
||
на первом RTT пропускается. Её аргумент также ошибочен: передаётся B/s вместо
|
||
размера окна в байтах. Затем перевод pacing в целые Kbit/s превращает малую
|
||
ненулевую скорость в 0, который shaper трактует как unlimited. Обновление
|
||
скорости само по себе не пересчитывает уже поставленный shaper-таймер.
|
||
|
||
5. RTO общий, основан на RTT/jitter соединения. Очередь wait_ack просматривается
|
||
как FIFO с единым timeout; индивидуальные дедлайны с такой остановкой обхода
|
||
несовместимы. Повтор может снова уйти в зависший канал. Приоритет следующего
|
||
канала сейчас не реализован: select_link не получает last_link пакета,
|
||
send_hist только записывается, а общий RR относится ко всему соединению.
|
||
Воспроизведение на настоящих очередях/RTO при двух готовых UP-каналах дало
|
||
seq1 A->A и seq2 B->B. Тест late_ack_and_migration этого не покрывает:
|
||
он явно отключает A перед передачей через B.
|
||
|
||
6. Поток собирается по общему seq: при RTT 20/200мс разность времени доставки
|
||
неизбежно создаёт переупорядочивание. Исправление ACK не устраняет ожидание
|
||
пропущенного фрагмента. В диагностике при высокой скорости накоплено порядка
|
||
0.17–0.29MB. Память сборки и пауза приложения требуют отдельных проверок.
|
||
|
||
7. stcp_link_send() подтверждает постановку в tx_queue. Это не означает прогресс
|
||
записи в TCP. Для stalled TCP одной проверки link_status/cwnd/pacer недостаточно.
|
||
|
||
8. В приёмнике ACK ставится в ack_q до выделения payload/ETCP_FRAGMENT и вставки
|
||
в recv_q. При последующем OOM или отказе вставки уже подготовленный ACK может
|
||
подтвердить несохранённые DATA. В новом контракте положительный ACK допустим
|
||
только после сохранения фрагмента либо проверки, что он уже сохранён/доставлен.
|
||
Инъекция отказа data_pool подтвердила: ACK_queued=1, DATA_stored=0,
|
||
delivered_till=0. Это дефект надёжности, а не только неточность статистики BBR.
|
||
После исправления этой ступени дополнительно проверен unpacker: его OOM
|
||
теперь явно прерывает сессию вместо продолжения повреждённого потока (см. статус).
|
||
|
||
Экспериментальная проверка причин
|
||
|
||
Диагностический бинарник /tmp/etcp-rtt-experiment включает штатный multipath-стенд
|
||
и настоящее ядро. Подмена происходит через linker --wrap, исходники протокола
|
||
не менялись. Используются реальные шифрование, UDP, dummynet и проверка порядка,
|
||
содержимого, полного завершения доставки, таймеров и счётчика выделений.
|
||
|
||
Контроль RTT 20/200мс, 8+8Мбит/с, с начальным pacing от окна/RTT:
|
||
- после завершения STARTUP полезная скорость 8.055/16Мбит/с, быстрый канал 0.633;
|
||
- свой путь ACK, без обновления BBR через till, фактический RTT попытки:
|
||
8.831 и 8.945/16Мбит/с; длинный канал недогружен;
|
||
- дополнительно исключены неоднозначные повторы из оценки скорости:
|
||
14.127 и 14.559/16Мбит/с, проверки установившегося режима прошли.
|
||
|
||
С последним вариантом дополнительно проверены RTT20/200мс при разных BWlim:
|
||
- 2+8Мбит/с (fast-distant): полезная скорость 8.735/10Мбит/с;
|
||
- 8+2Мбит/с (slow-distant): полезная скорость 8.979/10Мбит/с.
|
||
Проверки каждого канала, суммарной скорости, очереди и полного drain прошли.
|
||
В агрегате максимум очереди составил соответственно 24.9/26.4мс и 22.6/27.6мс.
|
||
Для равной BW выполнено два успешных повтора, для разных BW — по одному;
|
||
этого достаточно для выбора направления, но не для заявления о полной устойчивости.
|
||
Контроль последнего варианта по штатному rtt с фиксированным прогревом 3с:
|
||
14.312/16Мбит/с, очереди в агрегате 23.4/24.9мс, failures=0.
|
||
Это один успешный прогон диагностического прототипа, а не всего рабочего ядра.
|
||
|
||
Выборочные логи показывают преждевременный выход длинного канала из STARTUP
|
||
с заниженной оценкой BW. До фильтрации в модель попадал sample от повторной
|
||
передачи: RTT отвергнут, но delivery interval и скорость использованы.
|
||
Это подтверждает необходимость идентификации попытки; фильтр по send_count
|
||
годится для диагностики, но теряет достоверные измерения повторов и не заменяет ID.
|
||
|
||
Успешный единичный запуск без полного разделения учёта давал 14.45Мбит/с,
|
||
но повторные проверки выявили нестабильность. Он не является критерием готовности.
|
||
Отдельный прогрев до выхода из STARTUP использован только для диагностики:
|
||
штатные требования к разгону, очереди и утилизации не ослаблялись.
|
||
Фильтрация till в последнем прототипе сохраняет кумулятивную границу rx_ack_till;
|
||
её удаление из протокола сломало бы ограничение окна приёмника.
|
||
Прототип не проверяет новый wire-формат, владение попытками или отказоустойчивость.
|
||
|
||
Контракт (базовый учёт реализован; STCP-очередь остаётся открытой; ранние копии исключены)
|
||
|
||
Разделить логическую доставку фрагмента и физическую попытку передачи.
|
||
|
||
- Логический объект: общий seq, payload, признак доставки. Он отвечает за порядок
|
||
потока, unacked_bytes приложения и очередь повторной доставки.
|
||
Подтверждение фрагмента означает принятие его приёмником в надёжную очередь;
|
||
сборка и выдача приложению идут своим порядком. Переход в очередь повтора
|
||
идемпотентен: таймеры нескольких копий не создают несколько записей одного seq.
|
||
Первый положительный ACK или till, покрывающий этот seq, удаляет ожидающий повтор.
|
||
- Попытка: номер uint16 attempt_no в пределах seq, ссылка на канал, размер, время
|
||
отправки, delivery snapshots и состояние учёта окна. Последующая отправка
|
||
создаёт новую попытку; предыдущие снимки остаются неизменными.
|
||
Глобальные очереди содержат логические фрагменты: готовые к отправке и ожидающие
|
||
доставки. Отдельные записи попыток принадлежат очередям линков; один ll_entry
|
||
не включается в две очереди. Попытка хранит seq, а не висячую ссылку на уже
|
||
доставленный логический объект.
|
||
Индекс очереди — uint64 attempt_id=(seq<<16)|attempt_no; это внутренний ключ,
|
||
не самостоятельное wire-поле. Номер выдаётся при prepare, даже если send отказал.
|
||
- attempt_no монотонен в пределах seq, общий для всех линков, начинается с1.
|
||
Удаление/создание линка его не сбрасывает. После65535 попыток либо UINT32_MAX
|
||
значений seq следующая выдача меняет эпоху через REINIT, не повторяя пару.
|
||
При reset поток отбрасывается; DATA/ACK прежней эпохи не попадают в учёт.
|
||
- DATA содержит seq + attempt_no. ACK элемента сохраняет оба идентификатора:
|
||
seq:u32 + attempt_no:u16 + receiver_delay_us:u32. seq позволяет подтвердить
|
||
логическую доставку даже после удаления метаданных старой попытки, без отдельной
|
||
истории попыток. Для известного attempt_id проверять соответствие seq.
|
||
Задержка насыщается, UINT32_MAX означает недействительный RTT sample.
|
||
Пересчитать DATA MSS, MTU и ACK batching; не использовать modulo-wrap задержки.
|
||
- ACK означает принятие DATA в надёжную очередь приёмника, не приход в сокет.
|
||
Сначала проверить границы, сохранить DATA/вставить в recv_q либо найти дубликат,
|
||
затем поставить ACK и лишь потом вызывать сборку/пользовательские callbacks.
|
||
Это сохраняет порядок при reentrant close/reset. При OOM до сохранения DATA
|
||
положительного ACK нет.
|
||
При OOM самого ACK данные остаются сохранёнными; следующая копия получает ACK.
|
||
- Подтверждение попытки возвращается через тот же ETCP_LINK, где она принята.
|
||
Очередь ACK принадлежит линку; piggyback содержит только ACK этого линка.
|
||
Дубликат логического DATA с новым attempt_id тоже подтверждается.
|
||
- BBR обновляется по реально подтверждённой попытке своего канала. RTT — elapsed
|
||
этой попытки минус допустимый receiver delay. KA RTT остаётся метрикой живости.
|
||
ACK другого канала не формирует sample. ACK/control по-прежнему обходят только
|
||
локальный DATA-pacer; общая физическая очередь коммуникации сохраняется.
|
||
- Общий till остаётся подтверждением логической доставки и места приёмника.
|
||
Он может прийти по любому живому пути, освобождает payload/очередь повторов,
|
||
но не придумывает доставку попытки, RTT/BW или освобождение чужого окна.
|
||
Продвижение till должно пробуждать отправку отдельного ACK с count=0 через
|
||
готовый рабочий канал, даже без DATA и без ACK-элементов на нём. Объединять
|
||
продвижения и ограничивать частоту, не создавать ответ на каждый ACK.
|
||
Потеря последнего till не должна оставлять полное окно приёмника навсегда:
|
||
ожидание места имеет таймер запроса актуального till через рабочий путь,
|
||
получатель отвечает текущей границей. Таймер принадлежит соединению, активен
|
||
только при ожидающих DATA и полном окне, отменяется при прогрессе/close/reset.
|
||
Session-wrapped запрос/ответ не меняет модели BBR; частота ограничена.
|
||
- Доставка приложения учитывается один раз. Физические попытки учитываются
|
||
каждая в своей модели; их суммарная доставка может включать дубликаты DATA.
|
||
- ACK или объявление потери освобождает окно попытки ровно один раз. Поздний ACK
|
||
не дебетует его повторно. После доставки payload можно освободить, метаданные
|
||
других попыток живут до ACK/потери/закрытия, без удержания лишнего payload.
|
||
При потере запись попытки можно удалить: поздний ACK по seq ещё подтверждает
|
||
логическую доставку, но неизвестный attempt_id не обновляет BBR или окно.
|
||
Отдельная история/tombstone для уже потерянных попыток не нужна.
|
||
- Удаление линка/сессии отменяет его таймеры, удаляет ACK и попытки, освобождает
|
||
учёт ровно один раз. Не хранить указатель на уже освобождённый ETCP_LINK.
|
||
|
||
Правило повторной передачи, независимо от определения DOWN
|
||
|
||
Повтор использует историю именно своего логического фрагмента, не last_rr_link
|
||
соединения и не позиции каналов в send_hist. При готовых A/B/C порядок попыток
|
||
одного фрагмента — A->B->C->A; отправки других фрагментов и ACK его не меняют.
|
||
Начать обход после last_link фрагмента, выбрать первый подходящий другой линк.
|
||
Пропускать DOWN, неготовые, не вмещающие DATA и заблокированные pacer/транспортом.
|
||
Если есть другой initialized+UP линк, прежний запрещён даже при временно занятых
|
||
альтернативах. Тогда фрагмент ждёт штатного события ACK/pacer/transport-ready,
|
||
без busy loop, новой попытки и нового RTO. Временно занятый следующий линк
|
||
не блокирует поиск остальных альтернатив. При отсутствии других initialized+UP
|
||
линков разрешён готовый прежний; DOWN последней альтернативы будит sender.
|
||
Отправлять без cwnd/pacing нельзя. Курсор удалённого линка очищается до free.
|
||
|
||
Для первоначальной отправки остаётся обычный выбор по загрузке. Retry — отдельный
|
||
режим той же функции выбора, с едиными проверками готовности, без второго набора
|
||
условий cwnd/pacing. Burst не навязывает DATA свой линк: при конфликте измерение
|
||
идёт с FILLER, повтор отправляется по своему правилу. Старый линк не объявляется
|
||
DOWN только потому, что копия отправлена другим путём.
|
||
|
||
last_link логического фрагмента — только заимствованный курсор выбора. При удалении
|
||
линка очистить такие курсоры до free; при NULL начать с первого рабочего линка.
|
||
Переиспользуемый uint8 local_link_id и порядковый номер списка не являются
|
||
идентичностью попытки. send_count не должен определять retry через оборачиваемый
|
||
uint8: хранить факт состоявшейся отправки отдельно от статистики.
|
||
Курсор меняется после принятия новой попытки транспортом. Ошибка подготовки/OOM
|
||
или отказ отправки не должны создавать фиктивный flight и ждать сетевого RTO.
|
||
|
||
Повтор планируется после RTO либо снятия попытки при закрытии линка; ранних
|
||
спекулятивных копий нет. После RTO исходная DATA ещё может прийти из сети:
|
||
поздний ACK подтверждает логический seq, но не создаёт sample удалённой попытки.
|
||
Потеря для BBR учитывается при RTO, не при постановке или отправке повтора.
|
||
Все попытки ограничены общим бюджетом памяти и окнами своих каналов.
|
||
Ротация сокращает число попыток через зависший путь, но не сокращает ожидание
|
||
первого таймера. Нельзя обещать паузу <=100мс только сменой канала при RTO>100мс.
|
||
Сохранять физически достижимые требования конкретных профилей, не обещать один
|
||
предел задержки для произвольных RTT, нагрузки и потерь.
|
||
|
||
Почему не общий самый быстрый обратный канал
|
||
|
||
Такой выбор не согласует интервалы и не устраняет неоднозначность повторов.
|
||
Понадобится учитывать смену обратного маршрута в каждой модели, а отказ общей
|
||
обратной дороги затронет остальные прямые пути. Собственный ACK сохраняет одну
|
||
модель на канал и независимость отказов. Общая логическая граница при этом
|
||
может распространяться любым рабочим каналом.
|
||
|
||
Последовательность реализации и критерии завершения (до завершения открытых проверок задача не закрыта)
|
||
|
||
1. Начальный pacing — реализован и проверен отдельно от смены wire-формата.
|
||
Инициализировать от настоящего начального окна и первого валидного RTT.
|
||
Сохранить точные B/s до shaper, отделить unlimited от округления. При смене
|
||
ставки пересчитать оставшийся pacing-долг и таймер, не создавая новый burst.
|
||
Проверки: короткий служебный DATA -> пауза -> bulk, RTT20/200мс, скорость ниже
|
||
1Kbit/s, увеличение/уменьшение ставки с активным таймером, полный STARTUP.
|
||
|
||
2. Цельный переход на логические фрагменты и идентифицируемые попытки — реализован;
|
||
функциональные и sanitizer-проверки проходят, вся матрица скорости пока нет.
|
||
Добавить индекс attempt_id в очереди попыток каждого линка и отдельную ACK-очередь.
|
||
Общий индекс попыток не нужен: ACK ищется на принявшем его линке.
|
||
Разделить обработчики logical till и ACK попытки; одновременно обновить DATA,
|
||
ACK, декодеры, MTU/MSS, lifecycle и .h. В этом же изменении нужны очередь/таймер
|
||
попыток каждого линка и собственный RTT/RTO: иначе старый глобальный FIFO
|
||
перестаёт соответствовать новому владению. FIFO допустима при одном текущем
|
||
RTO для всех попыток линка; пересчитывать таймер при его смене.
|
||
До первого DATA ACK использовать RTT того же KA-пути как начальную оценку
|
||
либо консервативный начальный RTO. Расчёт по собственным RTT/jitter с запасом
|
||
ограничить диапазоном 10мс..1с; 10мс — нижняя граница, не верхняя.
|
||
Текущая формула по выбору пользователя: 1.2*RTT+1.5*jitter+3мс, округление вверх;
|
||
начальный jitter после первого DATA ACK равен RTT/2, установившийся — EWMA отклонения.
|
||
Добавить ротацию retry сразу, пока старый канал ещё UP; не откладывать до DOWN.
|
||
Не оставлять промежуточный формат,
|
||
в котором новый ACK продолжает обращаться только к last_link.
|
||
Проверки: первая копия A -> повтор B -> поздний ACK A; обратный порядок ACK;
|
||
ACK дубликат; чужой/неизвестный ID; till с другого пути; link recreation;
|
||
потеря ACK при уже доставленном payload; close/reset с активными попытками;
|
||
отказ принятия пакета транспортом без продвижения курсора и фиктивного flight;
|
||
OOM сохранения DATA, вставки в recv_q и создания ACK. Проверить передачу till
|
||
без DATA по другому пути и восстановление после потери последнего till.
|
||
Для retry: A->B при обоих UP и меньшем inflight у A; A->B->C->A;
|
||
посторонние DATA/ACK между попытками; пропуск заблокированных каналов;
|
||
один доступный путь; все временно заняты; burst старого пути; удаление курсора.
|
||
Матрица повторена после сканирования send_q и истории путей; текущие результаты приведены в статусе.
|
||
Выбор пути первоначальных DATA менять только
|
||
при сохраняющейся недогрузке: эксперименты уже показывают полную утилизацию
|
||
без смены алгоритма выбора DATA, поэтому дополнительная эвристика пока лишняя.
|
||
|
||
3. Быстрое восстановление и здоровье каналов — согласованный этап реализован.
|
||
Предварительное исправление OOM завершено: выбран сброс всей сессии с сохранением
|
||
линков, без удержания старого fragment/cursor и без отдельного OOM retry.
|
||
Очистка не выделяет память, старые callbacks/ожидания отменяются; активные
|
||
обработчики удерживают teardown и проверяют reset_count/close после callbacks.
|
||
Проверены OOM на первой/следующей кодограмме и восстановление новой эпохи.
|
||
Собственный RTT/RTO и ротация уже обязательны в этапе 2. Проверять ожидание
|
||
RTO отдельно от задержки готового повтора и доставки альтернативным путём.
|
||
Ранние копии до RTO исключены по решению пользователя от24.10.2026.
|
||
Реализованы флаг после трёх RTO, исключение застрявшего пути только из повторов,
|
||
полная история обхода, сканирование send_q и восстановление по свежему собственному ACK.
|
||
Отдельные probes/ограничение новых DATA не входят в выбранное решение: исходный
|
||
выбор новых DATA сохранён, подтверждённая новая отправка снимает флаг застревания.
|
||
DROP/HOLD отдельно по направлениям и вместе, несколько застрявших каналов,
|
||
возврат старых копий после HOLD проходят; повторный отказ и смена RTT/BW на ходу
|
||
остаются отдельными проверками. Отсутствие рабочих альтернатив и reset/close покрыты.
|
||
|
||
4. Учёт нижележащей очереди STCP.
|
||
Ограниченный tx_queue и уведомление о реальном прогрессе/готовности должны
|
||
участвовать в выборе пути. Определить время начала попытки и учёт данных,
|
||
принятых STCP, но ещё не записанных в сокет; не смешивать их с wire delivery.
|
||
Проверки: полностью остановленная TCP-запись рядом с рабочим UDP/STCP,
|
||
последующий выпуск очереди, закрытие соединения и отсутствие роста памяти.
|
||
|
||
Общие проверки
|
||
|
||
Матрица: equal, bandwidth, rtt, fast-distant, slow-distant, three, bidirectional.
|
||
Отдельно измерять время STARTUP, steady goodput, очередь каждого направления,
|
||
переупорядочивание, повторы и паузу приложения. Отчёт содержит данные каждого
|
||
повтора и минимум, а не только лучший результат. Не выполнять сетевые замеры
|
||
параллельно. Сохранить исходные пороги штатного test_etcp_multipath.
|
||
|
||
Добавить асимметричные прямой/обратный пути, ACK compression, поздние копии,
|
||
потери ACK и исчерпание лимитов памяти. Проверять счётчики по очередям, однократный
|
||
учёт подтверждения попытки и одну логическую доставку; утечки/UAF — ASAN/UBSan.
|
||
Логи: session/seq/attempt_id, DATA link, ACK link, send/receive/ack times,
|
||
receiver delay, причина retire, inflight до/после и вход/выход BBR.
|
||
|
||
Проверено для текущего бюджета: BBR18/18, windows16 сценариев, шесть транспортных
|
||
ASAN/UBSan тестов с detect_leaks=1 PASS. Общий check.sh: 180/183; FAIL
|
||
test_nat_detection_faults, test_nat_detection, test_encrypted_attachment.
|
||
Эти падения внесены отдельной задачей; связь с BBR не установлена.
|
||
|
||
Логи опытов: /tmp/etcp-rtt-ab-baseline.log,
|
||
/tmp/etcp-rtt-settled-control.log, /tmp/etcp-rtt-settled-candidate-diagnostic.log,
|
||
/tmp/etcp-rtt-settled-karn.log, /tmp/etcp-rtt-settled-karn-repeat.log,
|
||
/tmp/etcp-rtt-settled-fast-distant.log, /tmp/etcp-rtt-settled-slow-distant.log,
|
||
/tmp/etcp-rtt-original-karn.log.
|
||
Проверки отсутствующей ротации и ложного ACK при OOM: /tmp/etcp-retry-review.c,
|
||
/tmp/etcp-retry-review.mk, /tmp/etcp-retry-review.log
|
||
(exit=1: retry_rotation=2, false_ack=1, total=3; учёт/освобождение ресурсов проверены).
|
||
Сборка диагностического бинарника:
|
||
make -C tests -f Makefile -f /tmp/etcp-rtt-experiment.mk /tmp/etcp-rtt-experiment
|
||
Основной повтор:
|
||
RTT_SETTLED=1 RTT_SEED=1 RTT_SAME_ACK=1 RTT_NO_CUM=1 RTT_ACTUAL=1 RTT_KARN_RATE=1 \
|
||
/tmp/etcp-rtt-experiment rtt
|