From 259dfa35d9ebe642c78b3b414f1395dd37cafec5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 23 Oct 2026 01:56:01 +0200 Subject: [PATCH] Record RTT feedback experiments and refine multipath recovery plan --- doc/etcp_multipath_plan.txt | 197 ++++++++++++++++++++++++++++++++++++ doc/tasks.md | 50 +++++---- 2 files changed, 229 insertions(+), 18 deletions(-) create mode 100644 doc/etcp_multipath_plan.txt diff --git a/doc/etcp_multipath_plan.txt b/doc/etcp_multipath_plan.txt new file mode 100644 index 00000000..ed814efb --- /dev/null +++ b/doc/etcp_multipath_plan.txt @@ -0,0 +1,197 @@ +ETCP: независимая обратная связь каналов и доставка при отказах +Проверка архитектуры и план следующего изменения, 23.10.2026 + +Статус + +Бюджет очереди BBR уже реализован в fix/etcp-multipath-recovery: 20мс в PROBE_BW, +все резервы входят в предел max(4*MSS, BDP + BW*20мс). Это мягкая цель модели; +STARTUP/DRAIN, сериализация и обратный трафик не дают гарантии физической очереди <=20мс. +В штатном тесте equal получено 14.464/16Мбит/с, bandwidth — 9.060/10Мбит/с; +очередь медленного канала в bandwidth сократилась примерно с 47 до 21мс. +Изменения протокола и учёта попыток ниже ещё не реализованы. + +Что подтверждено кодом и диагностикой + +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; индивидуальные дедлайны с такой остановкой обхода + несовместимы. Повтор может снова уйти в зависший канал. + +6. Поток собирается по общему seq: при RTT 20/200мс разность времени доставки + неизбежно создаёт переупорядочивание. Исправление ACK не устраняет ожидание + пропущенного фрагмента. В диагностике при высокой скорости накоплено порядка + 0.17–0.29MB. Память сборки и пауза приложения требуют отдельных проверок. + +7. stcp_link_send() подтверждает постановку в tx_queue. Это не означает прогресс + записи в TCP. Для stalled TCP одной проверки link_status/cwnd/pacer недостаточно. + +Экспериментальная проверка причин + +Диагностический бинарник /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-формат, владение попытками или отказоустойчивость. + +Предлагаемый контракт + +Разделить логическую доставку фрагмента и физическую попытку передачи. + +- Логический объект: общий seq, payload, признак доставки. Он отвечает за порядок + потока, unacked_bytes приложения и очередь повторной доставки. +- Попытка: уникальный uint64 attempt_id, ссылка на канал, seq, размер, время + отправки, delivery snapshots и состояние учёта окна. Последующая отправка + создаёт новую попытку; предыдущие снимки остаются неизменными. +- attempt_id монотонен в пределах согласованной ETCP-сессии и не переиспользуется + при удалении/создании линка. Перезапуск счётчика допустим только с новой эпохой, + когда DATA/ACK старой сессии уже отклоняются. Переполнение закрывает сессию. +- DATA содержит seq + attempt_id. ACK элемента содержит attempt_id и ограниченную + задержку получателя. Задержка передаётся без короткого modulo-wrap; переполнение + помечает измерение недействительным. Пересчитать DATA MSS, MTU и ACK batching. +- Подтверждение попытки возвращается через тот же 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 или освобождение чужого окна. +- Доставка приложения учитывается один раз. Физические попытки учитываются + каждая в своей модели; их суммарная доставка может включать дубликаты DATA. +- ACK или объявление потери освобождает окно попытки ровно один раз. Поздний ACK + не дебетует его повторно. После доставки payload можно освободить, метаданные + других попыток живут до ACK/потери/закрытия, без удержания лишнего payload. + История ограничена по числу и сроку; неизвестный старый ID безопасно игнорируется. +- Удаление линка/сессии отменяет его таймеры, удаляет ACK и попытки, освобождает + учёт ровно один раз. Не хранить указатель на уже освобождённый ETCP_LINK. + +Почему не общий самый быстрый обратный канал + +Такой выбор не согласует интервалы и не устраняет неоднозначность повторов. +Понадобится учитывать смену обратного маршрута в каждой модели, а отказ общей +обратной дороги затронет остальные прямые пути. Собственный ACK сохраняет одну +модель на канал и независимость отказов. Общая логическая граница при этом +может распространяться любым рабочим каналом. + +Последовательность реализации и критерии завершения + +1. Начальный pacing как отдельное исправление без смены wire-формата. + Инициализировать от настоящего начального окна и первого валидного RTT. + Сохранить точные B/s до shaper, отделить unlimited от округления. При смене + ставки пересчитать оставшийся pacing-долг и таймер, не создавая новый burst. + Проверки: короткий служебный DATA -> пауза -> bulk, RTT20/200мс, скорость ниже + 1Kbit/s, увеличение/уменьшение ставки с активным таймером, полный STARTUP. + +2. Цельный переход на логические фрагменты и идентифицируемые попытки. + Добавить индекс attempt_id в очереди попыток каждого линка и отдельную ACK-очередь. + Общий индекс попыток не нужен: ACK ищется на принявшем его линке. + Разделить обработчики logical till и ACK попытки; одновременно обновить DATA, + ACK, декодеры, MTU/MSS, lifecycle и .h. Не оставлять промежуточный формат, + в котором новый ACK продолжает обращаться только к last_link. + Проверки: первая копия A -> повтор B -> поздний ACK A; обратный порядок ACK; + ACK дубликат; чужой/неизвестный ID; till с другого пути; link recreation; + потеря ACK при уже доставленном payload; close/reset с активными попытками. + После этого повторить всю матрицу скоростей. Балансировщик менять только + при сохраняющейся недогрузке: эксперименты уже показывают полную утилизацию + без смены алгоритма выбора DATA, поэтому дополнительная эвристика пока лишняя. + +3. Независимые сроки и восстановление каналов. + RTT/RTO и таймер старейшей попытки принадлежат каналу. FIFO допустима только + при одном текущем RTO для всех его попыток; пересчитывать таймер при его смене. + При потере ограничить новые DATA на проблемном пути, пробовать восстановление + ограниченными probe, повторять недоставленные DATA через рабочие каналы. + Не отправлять следующий bulk вслепую в тот же застрявший путь. + Проверки: DROP/HOLD отдельно по направлениям и вместе, несколько застрявших + каналов, возврат старых копий после HOLD, повторный отказ, смена RTT/BW на ходу. + +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. +Сборка диагностического бинарника: +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 diff --git a/doc/tasks.md b/doc/tasks.md index eec7faee..5e2f6002 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -21,7 +21,7 @@ при разных RTT/BWlim не выполняются требования утилизации, в том числе из-за повторов и переполнения очередей. Базовые исправления BBR/pacing внесены 23.10.2026 в fix/etcp-multipath-recovery (см. завершённую подзадачу ниже). Выбор пути повторов, общий RTO и блокировка сборки ещё не исправлены. - Финальный прогон девяти отказов: drop-reverse/short-hold PASS, остальные семь FAIL по целевым требованиям; + Прогон девяти отказов до бюджета очереди: drop-reverse/short-hold PASS, остальные семь FAIL по целевым требованиям; повтор на обычных часах slow-drop: пауза 450мс, latency 1267мс. Trace показывает повторную отправку в зависший путь. Связанные регрессии 5/5 PASS. ASAN/UBSan + detect_leaks=1: calibration и short-hold PASS; slow-drop сохраняет функциональный FAIL, ошибок санитайзеров/утечек во всех трёх запусках нет. @@ -31,22 +31,35 @@ После исправлений добавить смену профилей на ходу, повторяющиеся отказы и смешанный UDP/STCP со stalled TCP write. Полная матрица дольше 90s общего runner и запускается отдельной целью; нарушения завершают её с ошибкой. Окно PROBE_RTT и DATA MSS согласованы 23.10.2026 (см. завершённую подзадачу ниже). - Одиночный канал RTT=200мс/BWlim=8Мбит/с теперь проходит: полезная скорость 6.99Мбит/с, очередь <=133.1мс. - Далее согласовать обратную связь: ACK выбирает путь по inflight/RR, скорость оценивается по фактическому времени - возврата ACK, а RTT модели берётся из отдельного timestamp-обмена исходного линка. Это разные интервалы. - При повторах ACK не идентифицирует попытку: запись хранит только last_link, старый ACK может быть приписан новому пути. - Нужен явный контракт учёта попыток до изменения выбора пути/повторов; простой запрет прошлого линка его не заменяет. - После этого исправлять здоровье линка, независимые сроки повторов и доставку при DROP/HOLD по существующим тестам. - Профили после исправления окна: equal PASS (14.75 из 16 Мбит/с), bandwidth по скорости PASS (9.11 из 10), - но очередь 47.9мс >40мс. RTT 20/200мс: 10.08 из 16 Мбит/с, быстрый путь недогружен; rtt FAIL. - Пороги очереди и утилизации не ослаблены. Логи: /tmp/etcp-probe-{equal,bandwidth,rtt}.log/.csv. - Диагностика bandwidth с состояниями модели: /tmp/etcp-probe-bandwidth-diagnostic.log. - Дополнительная проверка 23.10: /tmp/etcp-queue-budget.log/.csv; лог включает bw Q16, DATA MSS, ACK-резерв и пик очереди. - Пик штатного PROBE_UP: bw=16465/65536 байт/мкс, min_rtt=24000мкс, MSS=1097; - 2*BDP=12060, ACK-резерв=1893, 2*MSS=2194, cwnd=16147 байт. Сложение и единицы на этом пике корректны. - Профиль bandwidth даёт полезные 9.045 из 10Мбит/с, очередь 47.1мс >40мс; порог теста не изменён. - Для следующей правки требуется выбрать политику: добавить бюджет задержки к BBR либо сохранить штатные резервы - и обосновать допустимый предел очереди с учётом сериализации. Вопрос передан пользователю; алгоритм пока не изменён. + Пользователь выбрал бюджет задержки; реализация и проверки завершены (см. подзадачу ниже). + После бюджета equal PASS (14.464 из 16 Мбит/с), bandwidth PASS (9.060 из 10), очередь медленного пути + 25мс отдельно / 20.7мс в агрегате. RTT 20/200мс остаётся FAIL: 7.1–7.7 из 16 Мбит/с, быстрый путь недогружен. + Пороги очереди и утилизации не ослаблены. Логи: /tmp/etcp-queue-limited-{equal,bandwidth,rtt}.log/.csv. + Диагностика подтвердила несовместимость интервалов ACK и отдельного RTT last_link, обновление чужого BBR + через общий till и неоднозначность ACK повторов. Найден отдельный дефект начального pacing от окна/RTT. + Подробные причины, проверенный контракт попыток, этапы и критерии: [doc/etcp_multipath_plan.txt](etcp_multipath_plan.txt). + Диагностический прототип без правок протокола: после разгона контроль 8.055/16Мбит/с; свой путь ACK, + отдельный till и RTT попытки без фильтрации неоднозначной скорости дают лишь 8.831–8.945/16. + После фильтрации неоднозначных повторов — 14.127 и 14.559/16Мбит/с, steady-проверки PASS. + Разные BWlim 2+8 и 8+2Мбит/с при RTT20/200мс: 8.735 и 8.979/10Мбит/с, steady PASS. + Контроль прототипа по штатному rtt с прогревом 3с: 14.312/16Мбит/с, очередь 23.4/24.9мс, PASS. + Это проверка причин, а не реализация новой архитектуры: нужны attempt_id, владение очередями и lifecycle. + Начать с начального pacing, затем согласованно разделить логический seq и физические попытки/ACK, + после этого исправлять сроки/здоровье линка и DROP/HOLD. Смену DATA-балансировщика пока не навязывать: + прототип уже утилизирует пути без неё. Штатные проверки разгона и отказов остаются обязательными. + +[+] **ETCP/BBR: бюджет очереди в установившемся режиме** — реализовано 23.10.2026 по выбору пользователя: + В PROBE_BW окно ограничено max(4*MSS, BDP + BW*20мс), с насыщением арифметики и учётом ACK/PROBE_UP резервов + внутри общего предела. Ограничение применяется также на fast path и до callback. 0 отключает политику. + STARTUP/DRAIN не ограничены этим бюджетом; это мягкая цель модели, а не гарантия физической очереди <=20мс. + До политики bandwidth давал очередь 47.1–47.9мс >40мс при корректных единицах и сложении резервов; + после политики профиль проходит по скорости и очереди, equal также PASS. Разные RTT и отказы не закрыты. + BBR 18/18, windows 16 сценариев PASS; BBR/windows/session/transport/STCP traffic/NCD ASAN/UBSan + с detect_leaks=1: 6/6 PASS. Полный check.sh: 180/183, FAIL NAT detection/fault и encrypted_attachment; + связь падений с BBR не установлена, отдельная открытая задача сохраняется. + Логи: /tmp/etcp-queue-budget-fastpath.log, /tmp/etcp-queue-budget-windows.log, + /tmp/etcp-queue-limited-asan-results.log, /tmp/etcp-queue-limited-asan-test_*.log, + /tmp/etcp-queue-limited-full-check.log. Политика описана в etcp_bbr.h, etcp_bbr_doc.md и doc/etcp_protocol.txt. [+] **ETCP/BBR: единое окно DATA и корректное осушение PROBE_RTT** — исправлено 23.10.2026: BBR использует MSS=normalizer->frag_size, а не wire-MTU/размер STCP-кадра. Начальное окно UDP/STCP равно 4*MSS; @@ -88,10 +101,11 @@ После исправления окна BBR общий прогон снова 181/183: encrypted_attachment PASS, но FAIL оба NAT-теста. test_nat_detection_faults упал в run_case mode=1 (UNKNOWN/contact_history); test_nat_detection не дождался профиля за 18с на socket=udp. Его отдельный повтор тоже упал, уже на socket=second, с той же причиной contact_history/UNKNOWN. + После бюджета очереди полный check.sh: 180/183, повторились оба NAT-падения и encrypted_attachment. Связь с изменениями BBR не установлена. Разобрать фикстуры/таймеры по логам, не ослаблять ожидания. Логи: tests/logs/test_nat_detection_faults.log, tests/logs/test_encrypted_attachment.log, /tmp/etcp-recovery-nat-retry.log, /tmp/etcp-recovery-attachment-retry.log, - /tmp/etcp-probe-full-check.log, /tmp/etcp-probe-nat-retry.log. + /tmp/etcp-probe-full-check.log, /tmp/etcp-probe-nat-retry.log, /tmp/etcp-queue-limited-full-check.log. [+] **ETCP: единый установщик прямого транспорта через NCD** — исправлено 22.10.2026: Удалён неиспользуемый рабочим кодом etcp_connect с EARLY/LATE, отдельными линками и таймерами;