Инструкция 3 (safe-версия) Цель: Сделать улучшения резолвера без risky-переписывания, сохранить обратную совместимость с текущим GUI/API, и убрать главные источники шума: массовые NXDOMAIN, таймауты и нечитабельные метрики. -------------------------------------------------------------------- 0) Что подтверждено по текущему состоянию -------------------------------------------------------------------- 1. Основной рабочий код сейчас в: - app/resolver.go - app/routes_update.go - app/dns_settings.go - app/domains_handlers.go 2. Логи показывают: - много NXDOMAIN (ожидаемо при широком base x subs) - заметную долю timeout - один агрегированный счетчик dns_errors, из-за чего трудно понять причину деградации. 3. Формат dns upstream у нас: host#port (например 94.140.14.15#53 или 127.0.0.1#6053). Это важно: нельзя использовать валидацию, которая принимает только host:port. -------------------------------------------------------------------- 1) Архитектурное решение (рекомендуемое) -------------------------------------------------------------------- Не оставлять только переключатель direct <-> smartdns. Сделать 3 режима резолвера: - direct: обычные домены через default/meta upstream. - smartdns: все домены через SmartDNS address. - hybrid_wildcard (recommended): только wildcard-домены через SmartDNS, остальные напрямую через default/meta. Почему так лучше: - сохраняем скорость и отказоустойчивость direct для обычных доменов; - wildcard-логику держим строго в SmartDNS, как ты и хотел; - не ломаем текущий UX: можно оставить старый bool и маппить его на mode. -------------------------------------------------------------------- 2) Что НЕ внедряем из старой инструкция3 -------------------------------------------------------------------- 1. Не используем netip.ParseAddrPort для upstream-валидации (ломает host#port). 2. Не используем netip.MustParseAddr в hot path (может паниковать). 3. Не добавляем лишний semaphore поверх worker pool (сложность без явной выгоды). 4. Не делаем агрессивный рефактор API-контрактов без backward-compat. -------------------------------------------------------------------- 3) Пакет безопасных правок (приоритет P1) -------------------------------------------------------------------- P1.1 - Режимы DNS (backward compatible) Файлы: - app/types.go - app/dns_settings.go - app/routes_update.go - app/resolver.go Изменения: 1) Ввести enum режима: type DNSResolverMode string const ( DNSModeDirect DNSResolverMode = "direct" DNSModeSmartDNS DNSResolverMode = "smartdns" DNSModeHybrid DNSResolverMode = "hybrid_wildcard" ) 2) Расширить DNSMode/DNSStatusResponse/DNSModeRequest полем Mode, но оставить ViaSmartDNS для старого GUI: - если Mode пустой, использовать ViaSmartDNS: - true -> smartdns - false -> direct 3) В ResolverOpts передавать Mode и список wildcard-доменов (один раз на job). 4) В resolveHostGo выбирать dnsList так: - mode == smartdns: []{smartdnsAddr} - mode == hybrid_wildcard и host совпал с wildcard: []{smartdnsAddr} - иначе: meta или default по текущей логике. Примечание: Wildcard-список уже хранится в smartdns-wildcards.json через /api/v1/smartdns/wildcards. Нужно только использовать его в резолвере. P1.2 - Upstream fallback с классификацией ошибок Файл: - app/resolver.go Изменения: 1) В digA: - идти по upstream последовательно; - timeout/temporary -> fallback на следующий upstream; - nxdomain -> остановить попытки для домена (дальше пробовать бессмысленно). 2) Классифицировать ошибки через net.DNSError + fallback по тексту: - nxdomain - timeout - temporary - other 3) Вместо одного dns_errors вести структуру счетчиков: dns_attempts, dns_ok, dns_nxdomain, dns_timeout, dns_temporary, dns_other. P1.3 - Разделенные метрики в summary Файл: - app/resolver.go Изменения: 1) Обновить финальный лог "resolve summary" с раздельными счетчиками DNS-ошибок. 2) Добавить per-upstream агрегаты (минимум attempts/ok/timeout/nxdomain/other). Формат может быть одной строкой JSON, чтобы GUI/анализатору было проще парсить. P1.4 - Ограничение domain expansion Файл: - app/routes_update.go Изменения: 1) Добавить конфиг-лимиты через env: - RESOLVE_SUBS_PER_BASE_LIMIT (например default 25) - RESOLVE_DOMAINS_HARD_CAP (например default 12000) 2) После построения domainSet: - сортировать домены; - при превышении hard cap обрезать хвост детерминированно; - писать явный warning в trace. 3) Логировать breakdown: bases_count, subs_count, expanded_count, total_domains. -------------------------------------------------------------------- 4) Пакет улучшений P2 (после P1) -------------------------------------------------------------------- P2.1 - Negative cache Файл: - app/resolver.go Идея: - кэшировать nxdomain/servfail на короткий TTL (например 10-20 минут), чтобы не долбить одинаковые несуществующие имена каждую прогонку. P2.2 - PTR retry (ограниченный) Файл: - app/resolver.go Идея: - для digPTR сделать 1-2 retry только на timeout/temporary; - не ретраить nxdomain. P2.3 - GUI/API отображение режима Файлы GUI: - selective-vpn-gui/api_client.py - selective-vpn-gui/dashboard_controller.py - selective-vpn-gui/vpn_dashboard_qt.py Идея: - показать mode = direct/smartdns/hybrid_wildcard; - оставить старый toggle рабочим (маппинг direct/smartdns), а hybrid можно добавить как отдельный выбор (позже). -------------------------------------------------------------------- 5) Порядок внедрения (рекомендуемый) -------------------------------------------------------------------- Шаг 1: Реализовать режимы + fallback + split-метрики (P1.1, P1.2, P1.3). Шаг 2: Добавить лимиты expansion (P1.4), проверить изменение unresolved/timeouts. Шаг 3: Добавить negative cache и PTR retry (P2.1, P2.2). -------------------------------------------------------------------- 6) Критерии готовности -------------------------------------------------------------------- 1. В trace видно не один dns_errors, а раздельные причины. 2. В режиме direct при падении одного upstream запросы частично продолжают проходить через fallback. 3. В режиме hybrid_wildcard wildcard-домены уходят через SmartDNS, остальные через direct. 4. Количество timeout и общее время прогона снижаются относительно текущего baseline. -------------------------------------------------------------------- 7) Короткий диагноз по сути -------------------------------------------------------------------- Проблема не в одном SmartDNS bottleneck. Основной вклад сейчас дает комбинация: - большой domain expansion, - много несуществующих имен (NXDOMAIN), - и отсутствие четкой стратегии fallback/метрик. Safe-путь: ввести гибридный routing DNS + раздельную диагностику + контролируемый expansion.