docs(history): log Step 3 HA implementation, measurements and open questions

This commit is contained in:
Repinoid
2026-10-10 16:32:21 +03:00
parent 27fa2e6ae3
commit 073977286f
+32 -1
View File
@@ -127,6 +127,31 @@
- Проверено: batch в пустую очередь пережил рестарт. - Проверено: batch в пустую очередь пережил рестарт.
- Billing — предусмотрен (модуль есть), но выключен (`BILLING_PG_HOST` не задан). Решение: включить позже. - Billing — предусмотрен (модуль есть), но выключен (`BILLING_PG_HOST` не задан). Решение: включить позже.
### 2.16. Шаг 3 — HA active/passive (leader-election)
- Выбран **вариант C** (active/passive через Redis-лок) вместо полного выноса состояния (A) —
обоснование: причина простоя — недоступность ВМ, а не CPU; C стоит ~200 строк и не трогает ядро
очередей. Подробно: `doc/thinking/state-extraction-and-ha-2026-10-10.md`.
- Код (коммит `bc00643`):
- `app/persistence/redis.go`: `TryAcquireLock` (SETNX+PX), `RefreshLock`/`ReleaseLock`
(Lua: только если значение наше). При `Client == nil` — всегда `true` (мы одни).
- `app/leader/leader.go`: `Elector` — цикл «продлить/взять лок» каждые 3 с, TTL 10 с, `IsLeader()`.
- `app/router/router.go`: `/ready` (в обоих плоскостях) — 200 если готов, 503 если пассивный;
поведение задаётся пакетной `ReadyFunc`.
- `app/cmd/goaws.go`: `--leader-election` (или env `SHARED_SQS_LEADER_ELECTION=true`),
`--leader-ttl`, `--leader-interval`; `ReadyFunc` ставится только при включении;
лок отпускается при graceful shutdown. **По умолчанию выключено — поведение не изменилось.**
- **Замеры (не гипотезы), ВМ 213, два временных инстанса 4200/4210:**
- одна копия → `/ready` `200`, вторая → `503` (split-brain нет); `redis-cli get ssq:leader`
= `<hostname>-<pid>` лидера;
- `kill -9` лидера → пассивный стал лидером, `/ready` → `200`, перехват занял **~12 с**
(максимум по формуле `ttl+interval` = 13 с);
- прод-API после деплоя: `list-queues` / `send-message` / `receive-message` через
`https://sqs.sinefo.ru` — OK; временные инстансы остановлены, ключ `ssq:leader` удалён.
- **В проде выключено**: для включения нужны общий Redis + вторая ВМ + LB с проверкой `/ready`
(иначе пассивный получит трафик и вернёт «пустые» очереди — главный риск варианта C, описан в доке).
- Док (коммит `865db49`): почему C, механика, замеры, ограничения, пошаговое включение, откат,
путь к варианту A (5 шагов) и триггеры перехода.
## 3. Грабли (для повторного использования) ## 3. Грабли (для повторного использования)
1. **`proxy_set_header Host $host` теряет порт** → SigV4 `SignatureDoesNotMatch`. 1. **`proxy_set_header Host $host` теряет порт** → SigV4 `SignatureDoesNotMatch`.
@@ -184,4 +209,10 @@ pkill -x shared-sqs
1. Клиенты без явного `endpoint_url` — есть ли такие (вопрос №5 мануала). `QueueUrl` уже корректный. 1. Клиенты без явного `endpoint_url` — есть ли такие (вопрос №5 мануала). `QueueUrl` уже корректный.
2. Egress ВМ: `curl https://api.ipify.org` с ВМ вернул пусто — влияет на billing (PG) и JWT-API. 2. Egress ВМ: `curl https://api.ipify.org` с ВМ вернул пусто — влияет на billing (PG) и JWT-API.
3. Дальше по мануалу: Шаг 2 (разбиение data-plane/control-plane), Шаг 3 (вынос состояния). 3. `SaveQueue` (метаданные очереди) всё ещё пишется **асинхронно** — при failover новый лидер
может не увидеть очередь, созданную незадолго до падения. Первый шаг к варианту A
(см. §8 дока по HA) — сделать синхронно, как `SaveMessagesSync`.
4. Для включения HA нужны: **общий Redis** (сейчас только локальный на 213 — SPOF),
вторая ВМ, LB с проверкой `/ready`. Пока всё это не готово — флаг выключен.
5. Дальше по мануалу: обновить устаревший `MANUAL-NEXT-2026-10-08.md` (ещё говорит «Шаг 1 — проверить
гипотезу» и «вопрос по домену открыт»); решить вопрос push в origin (18 коммитов впереди).