WEB-476 · Дефект · — · web
Телефония: шлюз после одного неудачного опроса аренды (рестарт nc-a1 при посадке) навсегда ставит lease_lost и отбивает все звонки до ручного рестарта — 8000 «reject» у owner-а 12:15
Закрыт
P1 · важно
ведёт: Fable
Суть
Факт (A1, 02.09): owner позвонил на 8000 в 12:15:24 и 12:15:32 (endpoint 7016 на A1-Asterisk, диалплан 8000@internal → AGI agi://127.0.0.1:4573) — AGI завершался за 14 мс, Hangup → для абонента «reject/нет соединения». Журнал шлюза /tmp/ag-asterisk-logs/gateway.out: после звонка 10:37 (PIN-инцидент WEB-395) появилась строка `[active-passive-lease] BEACON=ACTIVE_PASSIVE_LEASE_LOST gateway=asterisk reason=lease_lost detail=active_passive_lease_denied`, и оба звонка 12:15 упали в `Error: active_passive_lease_lost` (assertGatewayLease, index.js:99) ещё ДО обращения к приложению (в журнале nc-a1 звонков нет). Приложение здорово (paid 200).
Корень (infra/sip/gateway/index.js:76–120; живой шлюз /home/pi/ag-voice/infra/sip/gateway = b6b0069407): монитор аренды `scheduleGatewayLeaseMonitor` (poll 5 с, стартует после первого звонка, index.js:1754) при ПЕРВОЙ же ошибке `checkGatewayLease()` ставит `gatewayLeaseLost=true` и прекращает опрос (return без переплана) — фенс односторонний, снимается только рестартом процесса. Любой рестарт nc-a1 (посадка l90 ~11:05: приложение несколько секунд недоступно / отвечает lease denied) → телефония мертва до ручного рестарта шлюза, без алерта. При этом `ACTIVE_PASSIVE_LEASE_REQUIRED` по умолчанию '0' — фенс рвёт звонки даже там, где аренда «не обязательна».
Мера сейчас (12:23 A1): `systemctl restart ag-sip-native ag-sip-native-opus-experimental` — оба стека ready 200, greeting прогрет; owner попрошен позвонить повторно. Правило посадки до фикса: после каждого рестарта nc-a1 перезапускать SIP-стек (добавить в land-l9X.sh стадию verify).
Фикс (волна 1204-gwleaserecover): при потере аренды — beacon LOST, но продолжать опрос; при успешной проверке (2 подряд) — `gatewayLeaseLost=false` + `BEACON=ACTIVE_PASSIVE_LEASE_RESTORED`; при `ACTIVE_PASSIVE_LEASE_REQUIRED=0` отказ аренды не рвёт звонки (только beacon); отличать «приложение недоступно» (ECONNREFUSED/5xx) от явного `lease_denied`; тесты node:test: потеря→восстановление→звонок проходит; негатив: явный denied при REQUIRED=1 → отбой с фразой «line inactive». Приёмка — честный сухой старт супервизором (telephony-gateway-landing-dry-start-no-hooks), посадка по рецепту backups/gateway-*.tar.gz.
Класс: односторонний фенс без восстановления = «тихая смерть» (родня asterisk-shared-astdb-lock-kills-calls, rule10). Связано: WEB-395, WEB-370/405 (active-passive lease), LANE8000-CHECKLIST.
Лента
2026-09-02T12:31:21.861Z · Fable[A1 12:35 Фабл] Живьём: 12:23 рестарт ag-sip-native/opus (lease снова активна; звонки доходят до приложения). 12:26 второй дефект шлюза: resolveLaneClaimSecretDetails (laneClaim.js) проверял TELEPHONY_LANE_CLAIM_SECRET, но подписывал TELEPHONY_GATEWAY_SECRET → приложение (lane secret первым) = bad_signature → PIN. Патч живого laneClaim.js (backup backups/gateway-20260902T122619Z.tar.gz), рестарт, readiness 200/200; журнал 12:27: lane_claim принят. Репо: коммит 9165532f87 ветка gwlanesecret-1205 (mirrors/lines). Волна 1204-gwleaserecover (восстановление аренды, классы ошибок, монитор со старта) идёт на A1 поверх 1205. land-l90.sh (шаблон l91): стадия flip теперь перезапускает SIP-стек и считает LEASE_LOST.
2026-09-02T12:51:06.780Z · Fable[A1 13:15 Фабл] Волна 1204-gwleaserecover сдана: 0506c4cfee8262c30c48a72a7e557c1243cea63c (ветка gwleaserecover-1204 поверх 9165532f87): монитор аренды стартует при старте процесса, после потери продолжает опрос с backoff, 2 успеха → BEACON RESTORED, классы app_unreachable/lease_denied, при ACTIVE_PASSIVE_LEASE_REQUIRED=0 звонки не рвутся. Приёмка 1214-accgwleaserecover (A1) с честным сухим стартом (vendor/ json, без require-hook). Посадка на живой шлюз — после GO по рецепту backup→copy→restart→readiness→звонок.
2026-09-02T13:05:10.522Z · Fable[A1 13:40 Фабл] Приёмка 1214-accgwleaserecover: **NO-GO** — (1) LOST после первой неудачи опроса вместо трёх; (2) reason для 4xx-отказа = active_passive_lease_denied, требуется lease_denied; (3) честный сухой старт копии каталога не стартует: laneClaim.js:4 require ../../../config/telephony-security-policy.json (живой каталог использует ./vendor/…) → Cannot find module, health/readiness/beacon не получены. Корректировка — волна 1217-gwleaserecover2 (A1, поверх 0506c4cf): 3 последовательных неудачи, reason=lease_denied, vendor/telephony-security-policy.json в репо + require ./vendor с fallback, обязательный сухой старт в отчёте. Живой шлюз пока на патче 12:26 (lane-secret) + рестарт по правилу после каждого рестарта nc-a1.
2026-09-02T13:21:51.144Z · Fable[A1 13:22 Фабл] Корректировка 1217-gwleaserecover2 сдана: 5c4c9d2b5742ac32c8e017dffd211c46e49ed2fe (LOST после 3 неудач подряд, reason lease_denied/app_unreachable, vendor/telephony-security-policy.json + require ./vendor с fallback, ленивая загрузка экспериментальных модулей ws/socket.io-client для самодостаточного сухого старта; тесты 12/12). Повторная приёмка 1219-accgwleaserecover2 (A1) с обязательным собственным сухим стартом копии каталога и проверкой, что включённые экспериментальные ветки не сломаны. Бандл mirrors/lines/gwleaserecover2-1217-5c4c9d2b57.bundle.
2026-09-02T13:36:10.034Z · Fable[A1 13:37 Фабл] Повторная приёмка 1219-accgwleaserecover2 (A1, дерево от 5c4c9d2b): **GO** — 3 неудачи → LOST, восстановление после 2 успехов (RESTORED), классы lease_denied/app_unreachable, REQUIRED=0 не рвёт звонки, честный сухой старт копии каталога прошёл (health/readiness 200, beacon app_unreachable), policy vendor==config. Сажаю на живой шлюз A1 по рецепту backup → копия → restart → readiness → beacon.
2026-09-02T13:38:02.038Z · Fable[A1 13:37 Фабл] ПОСАЖЕНО на живой шлюз A1: кандидат 5c4c9d2b (gwleaserecover2-1217 = sipreconcile6 b6b0069407 + lane-secret 9165532f87 + восстановление аренды) скопирован в /home/pi/ag-voice/infra/sip/gateway (rsync без __tests__; live-only .bak/backups сохранены), backup backups/gateway-20260902T133635Z.tar.gz, оба стека перезапущены: readiness 4080/4180 = 200, greeting прогрет, монитор аренды с старта: LOST=0, poll failed=0. Откат: tar из backup + restart. Остаётся app-side PIN (WEB-395, l91).
2026-09-02T14:12:26.080Z · Fable[A1 14:12 Фабл] ПОСАЖЕНО l91 на прод A1: release arm64-l91-20260902T140416Z, run l91-9ad68bc2, sourceCommit 9ad68bc261c833e792bc17523245975e15f48612, артефакт sha 96b3466b…dc3f; ready TRUE (boot/database/release/drain ok), paid TRUE enforce_ready, public 200, live-sha 9ad68bc2; SIP-стек перезапущен (readiness 200/200, монитор аренды пережил рестарт: 2 poll-failed, LOST=0). Откат: land-l90 run/l90-26da36fa (nc-a1.service.bak-l90). Проверка восстановления аренды при рестарте nc-a1 (посадка l91): gateway.out — 2 «lease poll failed» (app_unreachable во время рестарта), LOST=0, звонки не рвутся — фикс 5c4c9d2b работает как задумано. Тикет → done после живого звонка owner-а.
2026-09-02T14:26:54.465Z · Fable[A1 14:30 Фабл] ПОДТВЕРЖДЕНО: при двух рестартах nc-a1 (14:10 посадка l91, 14:21 откат P11) шлюз пережил без LOST (poll failed ×2 app_unreachable/lease_denied active_holder, threshold 3), звонки owner-а 14:23/14:24 прошли. Наблюдение: после рестарта приложение отвечает lease denied reason=active_holder owner=a1 holder=a1 до истечения старой аренды (~8 мин) — тот же owner должен считаться renew, а не denied (follow-up P2 в WEB-370/405). Тикет → done.
2026-09-02T19:58:52.129Z · Fable[A1 19:58 Фабл] ИНЦИДЕНТ 19:49–19:57: при откате P11 (restart nc-a1) я перезапустил ОБА SIP-сервиса одновременно (ag-sip-native + ag-sip-native-opus-experimental). Они убивают друг у друга Asterisk: start-sip-native.sh останавливает experimental gateway, experimental стартует свой Asterisk с /tmp/ag-asterisk-native-opus-experimental/asterisk.conf → native «Asterisk PID is gone» → петля (14 рестартов). В окне petли dynamic sync (pnpm install в /home/pi/web падает ERR_PNPM_IGNORED_BUILDS) оставлял pjsip-generated.conf 0 байт → Linphone 7016 «неправильный пароль», звонок owner-а на 8000 оборвался после приветствия. Разрешение: stop experimental → restart native один → asterisk=1, endpoint 7016 200, Stasis app 1, readiness 4080=200, 0 рестартов за 75 с. Experimental (4180) ОСТАВЛЕН ВЫКЛЮЧЕННЫМ до фикса. Правило до фикса: после restart nc-a1 рестартовать ТОЛЬКО ag-sip-native. В волну телефонии: (1) experimental не должен поднимать второй Asterisk/убивать боевой (один Asterisk, lock-guard); (2) убрать git pull + pnpm install из старта; (3) sync не должен truncate-ить pjsip-generated.conf при провале (atomic write).
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-476","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-02T14:27:04.371Z