Merge origin/master with encrypted attachment ownership fixes

Validation: ./build.sh --no-gui -j4 and make check -j4 passed; 186 tests, no failures.
This commit is contained in:
evgeny committed 2026-10-24 15:11:50 +02:00
commit bafda5ae3a
38 files changed
+2854 -984

No files matched your search

+192 -7
View File
@@ -3,6 +3,188 @@ ETCP: независимая обратная связь каналов и до
Статус
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 транспортом, а не при подготовке пакета.
@@ -714,11 +896,12 @@ uint8: хранить факт состоявшейся отправки отд
Для retry: A->B при обоих UP и меньшем inflight у A; A->B->C->A;
посторонние DATA/ACK между попытками; пропуск заблокированных каналов;
один доступный путь; все временно заняты; burst старого пути; удаление курсора.
После этого повторить всю матрицу скоростей. Выбор пути первоначальных DATA менять только
Матрица повторена после сканирования send_q и истории путей; текущие результаты приведены в статусе.
Выбор пути первоначальных DATA менять только
при сохраняющейся недогрузке: эксперименты уже показывают полную утилизацию
без смены алгоритма выбора DATA, поэтому дополнительная эвристика пока лишняя.
3. Быстрое восстановление и здоровье каналов.
3. Быстрое восстановление и здоровье каналов — согласованный этап реализован.
Предварительное исправление OOM завершено: выбран сброс всей сессии с сохранением
линков, без удержания старого fragment/cursor и без отдельного OOM retry.
Очистка не выделяет память, старые callbacks/ожидания отменяются; активные
@@ -727,11 +910,13 @@ uint8: хранить факт состоявшейся отправки отд
Собственный RTT/RTO и ротация уже обязательны в этапе 2. Проверять ожидание
RTO отдельно от задержки готового повтора и доставки альтернативным путём.
Ранние копии до RTO исключены по решению пользователя от24.10.2026.
При потере ограничить новые DATA на проблемном пути, пробовать восстановление
ограниченными probe, повторять недоставленные DATA через рабочие каналы.
Не отправлять следующий bulk вслепую в тот же застрявший путь.
Проверки: DROP/HOLD отдельно по направлениям и вместе, несколько застрявших
каналов, возврат старых копий после HOLD, повторный отказ, смена RTT/BW на ходу.
Реализованы флаг после трёх RTO, исключение застрявшего пути только из повторов,
полная история обхода, сканирование send_q и восстановление по свежему собственному ACK.
Отдельные probes/ограничение новых DATA не входят в выбранное решение: исходный
выбор новых DATA сохранён, подтверждённая новая отправка снимает флаг застревания.
DROP/HOLD отдельно по направлениям и вместе, несколько застрявших каналов,
возврат старых копий после HOLD проходят; повторный отказ и смена RTT/BW на ходу
остаются отдельными проверками. Отсутствие рабочих альтернатив и reset/close покрыты.
4. Учёт нижележащей очереди STCP.
Ограниченный tx_queue и уведомление о реальном прогрессе/готовности должны
+17 -2
View File
@@ -26,8 +26,9 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
При наличии незастрявшего initialized+UP пути застрявшие исключены из повторов, даже если альтернатива занята.
Если застряли все, доступны все UP-пути. Прежний путь разрешён лишь при отсутствии других допустимых;
иначе ждём альтернативу. DOWN последней альтернативы пробуждает отправителя.
3. Если DATA не помещается, он остаётся в очереди; для накопленных ACK выбирается
тот же линк, который принял соответствующий DATA, без ограничения DATA-cwnd/pacing.
3. Наступивший по сроку или срочный ACK обслуживает тот же линк, который принял DATA,
без ограничения DATA-cwnd/pacing. Готовый DATA другого линка уступает этому ACK;
DATA того же линка забирает ACK piggyback. Если DATA не помещается, он остаётся в очереди.
Кумулятивный till можно отправить по любому рабочему пути. ACK-only не увеличивает нагрузку DATA-shaper;
физическая очередь канала остаётся общей, её накопление ограничивают DATA-cwnd/pacing BBR.
4. Выделить ETCP_DGRAM до удаления DATA из очереди и изменения счётчиков.
@@ -61,6 +62,20 @@ HELLO/CHALLENGE/CONFIRM и keepalive передаются своим незав
Если логический seq ещё не доставлен и других активных копий нет, он возвращается в input_send_q.
Уже доставленный seq не возвращается, даже если его физическая копия истекла позже.
Обычный ACK-only ждёт received_at_us старейшей записи своего пути + 2000мкс.
Piggyback на готовом DATA передаёт ACK сразу. Готовый ACK может включать более свежие записи:
срок проверяется по голове пакета, а не отдельно для каждой записи внутри него.
Среди готовых обычных ACK выбирается самый старый срок; срочный ответ на ACK_REQUEST имеет приоритет.
Короткая ограниченная измерительная burst-серия завершается до перехода к ACK другого пути.
Продвижение логической границы также задаёт срок 2мс от первого неподтверждённого продвижения;
следующие DATA его не отодвигают. Любой принятый пакет с актуальным till снимает этот запрос.
Один ack_resp_timer соединения обслуживает минимум сроков очередей рабочих путей и логической границы.
После piggyback/частичной отправки срок пересчитывается по оставшимся головам. Новый RX между
timer и отложенным sender не задерживает старые ACK: готовность проверяется по абсолютному времени.
При DOWN очередь пути не создаёт таймер; после UP её старый наступивший срок действует сразу.
OOM ACK-таймера логируется и включает существующий срочный ответ, сохраняя received_at_us для RTT.
Reset/close отменяют таймер и очищают сроки; DATA/RTO/BBR и wire-формат этим механизмом не меняются.
inflight - две очереди (ll_queue):
- input_send_q: список пакетов ожидающих отправку
- input_wait_ack: список неподтвержденных пакетов ожидающих ack
+16
View File
@@ -6,6 +6,14 @@
заменяются вместе. Timestamp входит в подпись. Групповые пути и подсети
остаются в `TOPO_GROUP_NODE` и не являются частью общей записи узла.
В NODEINFO секции подсетей присутствуют только при `TOPO_FLAG_SEND_SUBNETS`.
Без флага оба счётчика подсетей равны нулю; противоречащий этому заголовок отклоняется.
Parser проверяет точную длину пакета и проходит все присутствующие секции независимо
от запрошенных `out_*`: например, `out_subnets=NULL` отключает создание объектов подсетей,
но сохраняет проверку префиксов и правильное положение следующего hop-list.
Выходы выдаются только после полного успеха; при ошибке они обнулены, частичные объекты
освобождены. Подпись и допустимость пути проверяются после структурного разбора.
Получатель проверяет подпись до изменения реестра или маршрутов. Более свежая
запись заменяет старую целиком, включая пустые списки адресов. Старые записи
игнорируются; разные подписанные записи с одинаковым timestamp — конфликт.
@@ -13,6 +21,12 @@
8-битный `ver` сохраняется как диагностический счётчик; свежесть определяется
только timestamp.
Локальный узел группы готовится до удаления прежнего `local_node`: сравниваются сами адреса
и префиксы подсетей, включая замену при прежнем количестве. При отказе памяти, подписи или
конфликте IPv4-маршрутов прежние узел и маршруты сохраняются. Подсети CHAT не публикуются.
`topo_group_update_my_nodeinfo` возвращает число IPv4-подсетей, для CHAT — 0, при ошибке — -1;
рассылку подготовленного NODEINFO выполняет вызывающий.
Живая телефонная занятость передаётся без изменения структуры NODEINFO: в существующем
`flags` бит `0x04` означает известное состояние, `0x08` — занятость звонком.
Это групповые флаги `TOPO_GROUP_NODE.phone_status`, как флаг рации `0x02`;
@@ -50,6 +64,8 @@ snapshot загрузка использует его. Локальная пуб
Уникальность определяется endpoint и видом адреса,
а не отображаемым классом NAT. Старый производный формат пересоздаётся;
bootstrap-загрузка без подписанного snapshot не подтверждает NAT-профиль.
Такая загрузка требует хотя бы одного адреса; отказ SQL/памяти освобождает все частичные списки
и возвращает NULL. Наличие валидного snapshot с пустым списком адресов, напротив, допустимо.
При удалении старых `nat_evidence`/`nat_checked` адреса и REALITY сохраняются для bootstrap,
старые профили сбрасываются, сериализованные snapshots инвалидируются.
Максимальные timestamp snapshots сохраняются, чтобы новая локальная публикация
+44
View File
@@ -43,6 +43,50 @@
Windows/FreeBSD runtime не проверялся.
[ ] **ETCP: полезная утилизация нескольких каналов и доставка при зависании части линков** — общая задача открыта.
24.10.2026: исправлен обход срока ACK посторонним wake. Абсолютный срок головы пути +2 мс,
один общий таймер, пересчёт после piggyback/частичной отправки; новые DATA не отодвигают старые ACK.
Наступивший ACK другого пути имеет приоритет перед DATA; свой DATA забирает его piggyback.
Сохранены срочный ответ, bounded burst, OOM fallback и очистка при reset/close; wire/BBR/RTO не менялись.
Windows 60 сценариев с обоими режимами часов PASS; восемь ASAN/UBSan-тестов и ASAN two-stuck без ошибок/утечек.
В трёх дуплексных прогонах ранних ACK-пакетов 0; на 8 Мбит/с ACK занимают 0.143..0.169 вместо прежних 0.347/0.390 Мбит/с.
Агрегат проходит все свои пороги: 8.667..8.804 / 8.695..8.796 из 10 Мбит/с; одиночные каналы ещё дают FAIL.
Полная матрица: первые 6 скоростных профилей и все 9 профилей отказов PASS, агрегат дуплекса 8.750/8.768.
Остались 2 FAIL одиночного дуплекса: 1.609<1.700 и 6.730<6.800 Мбит/с; дальше разбирать BW/PROBE_RTT и STCP backpressure.
Load-drop/load-hold: паузы 41.402/41.845 мс, steady 7.229/7.466 из 8 Мбит/с; max latency 180.693/89.239 мс записана.
Check.sh 184/184, proxy 9/9, burst 1/1, load 8/8 PASS, 134217728 проверенных байт, 185 с.
Диагностика 106 полей различает раннюю запись в готовой пачке и отправку всего ACK-пакета раньше срока.
Логи/CSV: /tmp/etcp-ack-deadline-*; HTML: /tmp/etcp-ack-deadline-duplex.html; детали — doc/etcp_multipath_plan.txt.
Предыдущий этап — диагностика до исправления ACK:
Продолжение 24.10.2026: диагностика срезов 100 мс (настройка 10..1000 мс), 104 поля; CSV и лог буферизуются до drain.
Видны очереди, callbacks/deferred waiter, DATA/ACK, flight/cwnd, BBR/RTT/RTO, долг/готовность pacer и канал эмулятора.
tools/etcp_diag_report.py проверяет CSV и строит автономные интерактивные графики с исходными срезами.
Дуплекс: 598 срезов; two-stuck/ASAN: 5208/5196 срезов, без ошибок sanitizer/утечек; CHECK сохраняет накопленный лог.
Итоговый check.sh 184/184, proxy 9/9, burst 1/1, load 8/8 PASS, 166 с; JS smoke-проверка графиков/фильтров PASS.
На одиночном 8 Мбит/с пути ACK занимают 0.347/0.390 Мбит/с, среднее ожидание ACK-only 230/271 мкс вместо окна 2 мс.
В срезах side=1 path=1 DATA готовы, а BBR задаёт около 6.3 Мбит/с; PROBE_RTT временно уменьшает окно до 32.9 тыс. байт.
Дедлайн обходит посторонний wake sender. Дальше разбирать абсолютные deadlines ACK; простого !timer недостаточно.
Алгоритмы протокола на этом этапе не менялись; полный дуплекс всё ещё не проходит скоростные пороги (5 нарушений, 8.513/8.337).
Логи/CSV: /tmp/etcp-duplex-diag-*; просмотр: /tmp/etcp-duplex-diag-view.html; детали — doc/etcp_multipath_plan.txt.
24.10.2026: реализованы сканирование send_q с приоритетом готовых повторов и ограниченная подгрузка новых DATA.
Заблокированный пакет пропускается. История путей пакета (32 байта) исключает новый обход до перебора допустимых;
история/RR фиксируются после принятого send, удаление линка очищает слот до callbacks/повторной выдачи ID.
После 3 RTO без ACK-прогресса tx_stalled исключает путь из повторов при наличии незастрявшего UP-пути.
Все застрявшие пути допускаются, новые DATA сохраняют прежний выбор; действующий ACK отправки после флага снимает его.
Старый/чужой ACK не восстанавливает путь; reset очищает состояние. Новых очередей/таймеров/копий нет, RTO сохранён.
Windows: 51 сценарий PASS с управляемыми/реальными часами; восемь ASAN/UBSan-тестов без ошибок/утечек.
Контроль занятого на 1 мс рабочего пути: latency 377.012 вместо 736.623 мс, ready_wait 2.318 мс, профиль PASS.
Отрицательный контроль задержки готового повтора 200 мс остаётся FAIL по очереди и общей задержке.
Полная последовательная multipath-матрица: все 9 профилей отказов PASS, первые 6 скоростных профилей PASS.
Two-stuck: latency 379.997 мс при пределе 462.219 мс; ещё 3/3 повтора — 374.981..378.557 мс.
Load-drop/load-hold: паузы 39.234/39.355 мс, steady goodput 7.221/7.468 из 8 Мбит/с.
Max latency под насыщенной нагрузкой 161.287/86.306 мс записана; отдельным порогом она здесь не ограничена.
Two-stuck/load-drop также проходят ASAN/UBSan с detect_leaks=1 без ошибок/утечек.
Остаётся дуплекс: 8.412/8.435 из 10 Мбит/с, все 7 нарушений матрицы — его скоростные пороги.
Далее — дуплекс, STCP backpressure, отдельные проверки смены RTT/BW и повторного отказа.
Полный check.sh: 184/184, proxy 9/9, burst 1/1, load 8/8 PASS, 134217728 проверенных байт, 197 с со сборкой.
Длинная multipath-матрица запускается отдельно; её общий результат остаётся FAIL из-за дуплекса.
Логи: /tmp/etcp-send-scan-*.log; подробности — doc/etcp_multipath_plan.txt.
Предыдущая диагностика:
24.10.2026: тесты отдельно проверяют обработку RTO (опоздание<=20мс), ожидание готового повтора (<=100мс)
и доставку сверх заданной задержки канала (<=100мс). TX учитывается после принятия send.
Быстрые профили сохраняют общий предел100мс; длинные учитывают по одному RTO отказавших путей плюс100мс.