doc: мануал для нового чата — состояние, решения, план шагов (ВМ -> разбиение -> хранилище), команды, грабли, карта кода
This commit is contained in:
@@ -0,0 +1,169 @@
|
|||||||
|
# МАНУАЛ: что делать дальше (состояние на 2026-10-08)
|
||||||
|
|
||||||
|
> Файл для НОВОГО чата. Прочитай его первым, затем документы из раздела 2.
|
||||||
|
> Репозиторий: `/home/naeel/nubes/SQS_und_IoT/SQS-service` (сервис `shared-sqs`).
|
||||||
|
> Всё, что описано как «сделано», уже в ветке `main` и на сервере (`origin/main`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Где мы сейчас
|
||||||
|
|
||||||
|
**Код SQS готов и запушен.** Ветка `main` = `origin/main`, рабочая копия чистая.
|
||||||
|
|
||||||
|
Что сделано за 2026-10-05…08:
|
||||||
|
- закрыт весь план доработок P0/P1/P2 (`doc/thinking/plan-for-flash-2026-10-05.md`);
|
||||||
|
- P1.2 — индексация сообщений (tombstone + индексы + амортизированная компакция);
|
||||||
|
- полный код-ревью сервиса + исправления (утечка блокировки очереди, DLQ в батче,
|
||||||
|
гонка лимита тенантов, потеря `NubesSub`, общий HTTP-клиент);
|
||||||
|
- интеграционные прогоны: `api_test.sh` 22/22, `sdk_test.py` 15/15, `fifo_dlq_probe.py` — PASS,
|
||||||
|
стресс 3×2000 сообщений — без зависаний.
|
||||||
|
|
||||||
|
**Прод-сервис никто не использует** (podтверждено владельцем), IoT переделывается с нуля.
|
||||||
|
Поэтому мы свободны менять архитектуру без обратной совместимости.
|
||||||
|
|
||||||
|
## 2. Документы, которые надо прочитать в этом порядке
|
||||||
|
|
||||||
|
| Документ | Что внутри |
|
||||||
|
|---|---|
|
||||||
|
| `doc/thinking/architecture-analysis-2026-10-08.md` | **ГЛАВНЫЙ**: решения по размещению и разбиению (публичный SQS, обход через ВМ, модульный монолит data-plane/control-plane) |
|
||||||
|
| `doc/thinking/nubes-large-body-workaround-findings-2026-10-08.md` | как та же болезнь платформы обойдена в `~/nubes/drhider` (буфер на ВМ, pull вместо push) — со ссылками «файл:строка» |
|
||||||
|
| `doc/thinking/nubes-workaround-applicability-to-sqs-2026-10-08.md` | применимость к SQS: 4 варианта, обязательные замеры, техусловия (Host для подписи, внешние `QueueUrl`, TLS) |
|
||||||
|
| `HISTORY/2026-10-07-session-log.md` | журнал правок: что и почему менялось, найденные дефекты, уроки |
|
||||||
|
| `doc/thinking/nubes-ticket.md` | исходная диагностика сетевой болезни (MSS/MTU + таймер шлюза). **Тикет отправлять не нужно** — см. `HANDOFF-NEW-CHAT.md`, раздел 7 |
|
||||||
|
| `doc/thinking/plan-for-flash-2026-10-05.md` | исходный план доработок (выполнен) |
|
||||||
|
| `HANDOFF-NEW-CHAT.md` | доступы, креды, конфигурация кластера, история проекта |
|
||||||
|
|
||||||
|
## 3. Принятые решения (не пересматривать без причины)
|
||||||
|
|
||||||
|
1. **SQS — публичный сервис**, как AWS SQS / YMQ. Внешний доступ обязателен,
|
||||||
|
совместимость с AWS SDK — обязательна.
|
||||||
|
2. **Сетевую болезнь платформы не «лечим» тикетом** — обходим: публичная точка входа
|
||||||
|
на ВМ, минуя managed-шлюз.
|
||||||
|
3. **Пока — ВМ (сейчас `5.172.178.213`), потом отдельная прод-ВМ.** Девопсов не дёргаем,
|
||||||
|
пока не отлажено; затем — уже с доказательствами (tcpdump, период 31–33 с, MSS 1448).
|
||||||
|
4. **«Без монолитов» = модульный монолит**: в отдельные процессы выносим только то,
|
||||||
|
что живёт по-разному. Для SQS граница одна: **data-plane** (очереди/сообщения/SigV4)
|
||||||
|
и **control-plane** (тенанты, креды, админ). Биллинг/метрики/UI — модули, не сервисы.
|
||||||
|
5. **Ограничение, которое важнее разбиения**: ядро очередей держит состояние в памяти
|
||||||
|
одного пода → одиночка. Пока хранилище не вынесено, «не монолит» ≠ «масштабируется».
|
||||||
|
|
||||||
|
## 4. План работ (порядок обязателен)
|
||||||
|
|
||||||
|
### Шаг 1. Проверить гипотезу на ВМ — БЕЗ кода (критерий входа: домен и сертификат)
|
||||||
|
Цель: убедиться, что на ВМ болезни нет, **до** вложений в разбиение.
|
||||||
|
|
||||||
|
1. Собрать бинарник: `make build` (или `CGO_ENABLED=0 go build -o shared-sqs ./app/cmd/`).
|
||||||
|
2. Поднять на ВМ (memory-only достаточно): см. команды в разделе 5.
|
||||||
|
3. Поставить nginx с TLS на выбранный домен, проксировать на `127.0.0.1:4100`.
|
||||||
|
**Обязательно** `proxy_set_header Host $host;` — иначе AWS-подпись не сойдётся
|
||||||
|
(сервис берёт хост из запроса: `app/auth/auth_middleware.go:315-317`).
|
||||||
|
4. Замерить **с внешней машины** (не с ВМ): малые тела, 1 КБ, 10 КБ, 100 КБ, 256 КБ,
|
||||||
|
плюс длинный прогон на таймауты. Инструменты: `tests/tcp_mtu_probe.py`,
|
||||||
|
`tests/long_compare_local.py`, `tests/compare_ymq_vs_shared.py`.
|
||||||
|
5. **Критерий успеха:** 0 зависаний ~51 с, 0 таймаутов 31–33 с.
|
||||||
|
|
||||||
|
Если гипотеза не подтвердится — СТОП, вернуться к разбору (раздел 2, документ о применимости).
|
||||||
|
|
||||||
|
### Шаг 2. Код: разбиение на два процесса
|
||||||
|
- `data-plane` — SQS API и очереди (`app/gosqs/*`, `app/router/router.go`, `app/auth/auth_middleware.go`).
|
||||||
|
- `control-plane` — админка (`app/admin/admin.go`) и управление тенантами (`app/tenant/*`).
|
||||||
|
- Что уже учитывать при разбиении:
|
||||||
|
- адреса очередей формируются жёстко: `app/gosqs/tenant_helpers.go:29-37` →
|
||||||
|
`http://{region}.{host}:{port}/{tenantID}/{queue}`; хост/порт только из `app/conf/goaws.yaml`
|
||||||
|
(`app/conf/config.go:87-90`), env-переменных для Host нет → **внешние `QueueUrl` потребуют правки**;
|
||||||
|
- подпись SigV4 считается по заголовку `Host` (см. выше);
|
||||||
|
- ключ очереди — `{accessKey}:{queueName}` (`app/gosqs/tenant_helpers.go:15`).
|
||||||
|
|
||||||
|
### Шаг 3. Код: вынести состояние (стратегический пункт)
|
||||||
|
- Сейчас: память — источник истины, Redis — write-through (`app/persistence/redis.go`,
|
||||||
|
`LoadAllQueues`), восстановление при старте.
|
||||||
|
- На ВМ поставить Redis и решить, как читать: остаться write-through или перейти к Redis
|
||||||
|
как источнику истины (тогда появится возможность нескольких экземпляров).
|
||||||
|
- Это решение определяет потолок сервиса (сейчас — один под).
|
||||||
|
|
||||||
|
### Шаг 4. IoT — отдельно, после SQS
|
||||||
|
- Внешний MQTT-канал неизбежен (устройства), но там только мелкая телеметрия;
|
||||||
|
критичен разрыв wss ~150 с (лечится переподключением).
|
||||||
|
- Крупные тела устройств через managed-шлюз не пускать (лимит/дробление/приём на ВМ).
|
||||||
|
- IoT переделывается с нуля не монолитом: bridge (MQTT), consumer (→storage), API.
|
||||||
|
|
||||||
|
## 5. Команды (проверены)
|
||||||
|
|
||||||
|
**Локальный запуск (memory-only, без Redis):**
|
||||||
|
```bash
|
||||||
|
cd /home/naeel/nubes/SQS_und_IoT/SQS-service
|
||||||
|
go build -o /tmp/shared-sqs ./app/cmd/
|
||||||
|
SHARED_SQS_SEED_DEMO=true /tmp/shared-sqs --admin-token=dev-token-123 --port=4100
|
||||||
|
# креды демо-тенанта печатаются в лог при старте
|
||||||
|
```
|
||||||
|
⚠️ Не запускать через `| head` — SIGPIPE убивает процесс на середине теста.
|
||||||
|
|
||||||
|
**Проверки кода:**
|
||||||
|
```bash
|
||||||
|
go build ./... && go vet ./... && go test -race ./app/...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Деплой на ВМ:**
|
||||||
|
- зеркало репозитория: `naeel@5.172.178.213:~/terra/SQS-service`;
|
||||||
|
- SSH: `ssh -i ~/.ssh/naeel_vm_id_ed25519 naeel@5.172.178.213`;
|
||||||
|
- ⚠️ порт **443 на 213 занят** (nginx/xray/hysteria), поэтому публичная точка SQS —
|
||||||
|
отдельный домен на существующем nginx (виртуальный хост/SNI) или отдельный порт;
|
||||||
|
- ⚠️ в образе `Dockerfile` зашит `REDIS_ADDR` внутреннего кластера — на ВМ переопределить
|
||||||
|
(или не задавать, чтобы работать memory-only).
|
||||||
|
|
||||||
|
**Сборка образа (если через Docker):** `make docker-build` (`Makefile`, образ `naeel/shared-sqs`).
|
||||||
|
|
||||||
|
## 6. Грабли и правила (не наступать повторно)
|
||||||
|
|
||||||
|
1. `app/cmd` в `.gitignore` → при добавлении файлов `git add -f app/cmd/...`.
|
||||||
|
2. `deployments/` в этом репозитории **нет** — не искать.
|
||||||
|
3. `Queue.Messages` — tombstone-хранилище: менять только через `AppendMessage`/`RemoveIndex`/
|
||||||
|
`Compact`/`Reset`; число живых = `queue.LiveCount()`, а НЕ `len(queue.Messages)`
|
||||||
|
(`app/models/models.go`).
|
||||||
|
4. После `GetLocked`/`GetRLocked` — `defer Unlock` **до** любых `return` (иначе очередь
|
||||||
|
зависает навсегда; было в `receive_message.go`, исправлено).
|
||||||
|
5. Интеграционные тесты: `tests/api_test.sh` читает `ENDPOINT`, python-тесты — `ENDPOINT_URL`.
|
||||||
|
`boto3` в системе нет (PEP 668) → скачать колеса `pip download boto3`, распаковать в `/tmp`
|
||||||
|
и запускать с `PYTHONPATH`.
|
||||||
|
6. Лимит очередей тенанта = 10 → после упавших прогонов чистить очереди.
|
||||||
|
7. Диагностика зависаний сервиса: стек горутин по `kill -QUIT` (SIGQUIT).
|
||||||
|
8. Правки ingress на платформе — только через `ShturvalServiceConfig` (kubectl откатывает ArgoCD).
|
||||||
|
|
||||||
|
## 7. Правила работы с владельцем (обязательные)
|
||||||
|
|
||||||
|
- Коммитить после каждой логически завершённой правки; **пуш — только по прямой команде**.
|
||||||
|
- Документировать всё, включая собственные ошибки (HISTORY + `doc/thinking/`).
|
||||||
|
- Не выполнять `terraform apply`/`destroy`, не менять платформу, не удалять инстансы.
|
||||||
|
- Не расширять область работ; не лезть в соседние репозитории без прямой команды.
|
||||||
|
- Рекомендация без замера — не рекомендация, а догадка: сначала мерить, потом предлагать.
|
||||||
|
- Вопрос от владельца = только ответ (ничего не делать, пока не последует команда).
|
||||||
|
|
||||||
|
## 8. Открытые вопросы (нужны решения владельца)
|
||||||
|
|
||||||
|
1. **Домен и сертификат** для публичной точки SQS на ВМ (порт 443 занят → виртуальный хост?).
|
||||||
|
2. **Отдельная прод-ВМ** — когда создавать и с какими параметрами (пока хватит 213 для тестов;
|
||||||
|
хватит ли 213 — проверить замером: ядра, память, диск, загрузка).
|
||||||
|
3. **Redis на ВМ** — ставить сейчас или пока memory-only.
|
||||||
|
4. **Вынос состояния** (Шаг 3) — когда начинать; это определяет масштабируемость.
|
||||||
|
5. **Клиенты без явного `endpoint_url`** — есть ли такие (влияет на внешние `QueueUrl`).
|
||||||
|
|
||||||
|
## 9. Карта кода (что где)
|
||||||
|
|
||||||
|
| Путь | Ответственность |
|
||||||
|
|---|---|
|
||||||
|
| `app/cmd/goaws.go` | точка входа, флаги, загрузка конфига, periodic tasks, HTTP-сервер |
|
||||||
|
| `app/router/router.go` | HTTP-роутинг, лимит тела 3 МБ, биллинг/метрики по операциям |
|
||||||
|
| `app/auth/auth_middleware.go` | SigV4 (header + presigned), извлечение тенанта |
|
||||||
|
| `app/auth/jwt.go`, `app/auth/cache.go` | валидация JWT через внешний API + кэш |
|
||||||
|
| `app/gosqs/*` | SQS-операции (send/receive/delete/visibility/queue/attributes/tags) |
|
||||||
|
| `app/gosqs/receive_message.go` | long-polling, выдача сообщений (в т.ч. per-queue лок) |
|
||||||
|
| `app/gosqs/gosqs.go` | фоновый тик: retention, дедупликация, видимость, DLQ |
|
||||||
|
| `app/models/models.go` | `Queue`/`SqsMessage`, индексация (tombstone), FIFO-состояние |
|
||||||
|
| `app/models/globals.go` | `SyncQueues`: map очередей + tenant-индекс + блокировки |
|
||||||
|
| `app/persistence/redis.go` | write-through в Redis, загрузка при старте, переподключение |
|
||||||
|
| `app/admin/admin.go` | админка (тенанты/очереди/сообщения), UI-API по JWT |
|
||||||
|
| `app/tenant/tenant_store.go` | хранилище тенантов, генерация кредов, авто-провижининг по JWT |
|
||||||
|
| `app/billing/billing.go` | учёт операций в PostgreSQL (воркер + буфер) |
|
||||||
|
| `app/metrics/*` | Prometheus-метрики и gauge-обновление |
|
||||||
|
| `app/conf/goaws.yaml` | Host/Port/Region, дефолты очередей (используется для URL) |
|
||||||
|
| `tests/*` | интеграционные проверки (aws CLI, boto3, пробы сети/MTU) |
|
||||||
Reference in New Issue
Block a user