perf(traffic): optimize production reconciliation

This commit is contained in:
2026-08-28 02:43:26 +05:00
parent 185991504a
commit 9787cb2d23
20 changed files with 1265 additions and 433 deletions
+13 -3
View File
@@ -38,7 +38,7 @@
- [ ] На AWG/WG-интерфейсе есть root class `1:1` с `traffic.root_rate`.
- [ ] Для каждого peer есть class `1:<id>` с `traffic.peer_limit`.
- [ ] Для каждого peer есть `fq_codel` под `1:<id>`.
- [ ] Для каждого peer есть egress filter `dst peer_ip -> 1:<id>`.
- [ ] Для каждо peer есть egress filter `flower pref 10 dst_ip peer_ip/32 -> 1:<id>`.
- [ ] Есть ingress qdisc `ffff:` на AWG/WG-интерфейсе.
- [ ] Есть ingress redirect через `mirred ... redirect dev ifb0`.
- [ ] `ifb0` существует и находится в UP state.
@@ -46,7 +46,8 @@
- [ ] На `ifb0` есть root class `2:1` с `traffic.root_rate`.
- [ ] Для каждого peer есть class `2:<id>` с `traffic.peer_limit`.
- [ ] Для каждого peer есть `fq_codel` под `2:<id>`.
- [ ] Для каждого peer есть ingress filter `src peer_ip -> 2:<id>`.
- [ ] Для каждого peer есть ingress filter `flower pref 10 src_ip peer_ip/32 -> 2:<id>`.
- [ ] Все peer-фильтры на каждом parent используют один `pref 10`; `u32` остаётся только у catch-all ingress redirect.
- [ ] Class ids начинаются с `traffic.class_start_id`, default `10`.
- [ ] Peer IP → class id mapping использует стабильный лексикографический контракт: IP сортируются как строки, не numeric IPv4.
@@ -63,7 +64,10 @@
- [ ] Если изменить `rate/ceil` peer class, drift детектируется и topology восстанавливается.
- [ ] Если добавить stale peer class/filter, drift детектируется и topology восстанавливается.
- [ ] После удаления peer устаревшие class/filter исчезают через rebuild.
- [ ] После удаления последнего peer вся AWG2-owned traffic topology удаляется; повторный reconcile не выполняет teardown снова.
- [ ] После добавления peer появляются новые class/filter.
- [ ] Legacy peer-фильтры `u32` детектируются как migration drift и заменяются на `flower` через rebuild.
- [ ] Ошибка в любой строке `tc -batch -` останавливает apply; после успеха проходит verify-after-apply.
## E. Самовосстановление в жизненном цикле
@@ -80,6 +84,7 @@
- [ ] На старте daemon сбой `sysctl_tuning` или `swap` завершает процесс, а сбой traffic/firewall только логируется — сервис остаётся живым.
- [ ] После обрыва потока `docker events` он перезапускается автоматически (проверяется, например, `systemctl restart docker`).
- [ ] После восстановления потока `docker events` ошибка подсистемы снимается: в `events.log` появляется `docker_events=recovered`, а `last_action.state` не остаётся с этой ошибкой навсегда.
- [ ] Один startup/periodic/event-цикл использует один discovery `RuntimeTarget` для traffic и anti-torrent, а не повторяет `docker inspect` и interface discovery.
- [ ] Успешный reconcile одной подсистемы не гасит незакрытую ошибку другой: при постоянно падающем `traffic` успешный `firewall` не очищает `last_error` и не пишет `firewall=recovered`.
- [ ] Параллельный запуск `apply-limits` во время работы daemon не приводит к гонке: второй процесс ждёт блокировку до 10 секунд и затем сообщает `lock busy timeout`.
- [ ] После `kill -9` блокировка освобождается ядром автоматически; оставшийся lock-файл не мешает следующему захвату `/run/awg2-harness/traffic.lock`.
@@ -93,7 +98,9 @@
- [ ] `status` показывает недостающие и устаревшие правила peers: в `Traffic actual` — строка `reason` (например, `missing egress class`, `egress per-peer class set mismatch`, `duplicate egress class flowid mapping detected`), в `peer_table` — колонка `drift` по каждому peer.
- [ ] `status` явно показывает частичное состояние и drift, а не общий признак «всё хорошо».
- [ ] `status` показывает tc filter counters по peer или `filter statistics: unavailable`.
- [ ] Flower counters и legacy u32 counters в `status` разбираются одним parser-ом и привязываются к правильному peer IP.
- [ ] Логи доступны через `journalctl -u awg2-harness`.
- [ ] Повтор одной и той же reconcile-ошибки не создаёт `error`-запись на каждом цикле; первая ошибка, изменение её текста и recovery видны как отдельные переходы.
- [ ] Отсутствие обязательной host-утилиты (`tc`, `ip`, `docker`, `nsenter`) логируется явно с именем утилиты.
- [ ] Последний reconcile result виден в status или journal.
- [ ] `status` показывает последние события reconcile/firewall из `/run/awg2-harness/events.log`.
@@ -143,6 +150,9 @@
- [ ] В обоих managed-файлах первая строка содержит marker `Managed by awg2-harness sysctl_tuning`.
- [ ] Файл без marker по managed-пути не перезаписывается и не удаляется: команда завершается ошибкой.
- [ ] В managed-файле отсутствует `udp_wmem_min`.
- [ ] В shipped/default config `netdev_backlog_enabled = false`, `netdev_max_backlog = 10000`, а `net.core.netdev_max_backlog` отсутствует в managed-файле.
- [ ] При смене `netdev_backlog_enabled = true` на `false` key удаляется из persistence; оператор учитывает, что текущее runtime-значение до reboot автоматически не сбрасывается.
- [ ] До/после ручного включения backlog сняты `/proc/net/softnet_stat`, CPU и latency под реальной нагрузкой.
- [ ] `net.ipv4.tcp_congestion_control` и `net.core.default_qdisc` присутствуют в managed-файле только при активном BBR.
- [ ] `swap` пишет `vm.swappiness` в `/etc/sysctl.d/90-awg2-harness-swap.conf`, а не в `99-awg2-harness.conf`.
- [ ] При обновлении с прежней версии `/etc/sysctl.d/99-awg2-harness.conf` удаляется, если несёт маркер `# Managed by awg2-harness`.
@@ -184,7 +194,7 @@
- [ ] При превышении порога новые flows дропаются, established flows ниже порога не ломаются.
- [ ] После restart контейнера nftables rule пересоздаётся в новом netns.
- [ ] При `anti_torrent.enabled = false` удаляется/отключается только AWG2-owned nftables state; Docker/Amnezia rules не flush-ятся.
- [ ] `status` показывает `nf_conntrack_count` / `nf_conntrack_max`, если sysctl доступны.
- [ ] `status` показывает `nf_conntrack_count`, `nf_conntrack_max` и `nf_conntrack_utilization`, если sysctl доступны.
## J. Откат