9.2 KiB
История правок shared-sqs — 2026-10-11 (внешнее код-ревью и фиксы)
Запись по требованию «документировать всё, включая собственные ошибки» (приказ 2026-09-24). Предыдущие:
HISTORY/2026-10-10-session-log.md,doc/thinking/*-2026-10-08.md.
1. Задача
Провести внешнее код-ревью двух сервисов (SQS-service + IoT) моделью Claude Sonnet
(в режиме Plan — правки не вносит, только находит), затем своими силами исправить
найденное. Промпт для ревью — CLAUDE-REVIEW-PROMPT.md в корне монорепо
(со списком файлов, фокус-точками и строгим форматом ответа; явно запрещены
правки, оценка тестов/UI/legacy/стиля).
Ревью дало 40 пунктов (10 🔴 / 29 🟡 / 1 🟢). Разбор вопросов ревьюера и уточнение некорректных выводов — в п.3.
2. Что исправлено (SQS-service)
🔴 Критичные (10/10)
| Пункт | Файл | Суть | Коммит |
|---|---|---|---|
| 🔴1 | app/admin/admin.go |
jwtMiddleware пропускал любой путь с суффиксом /auth (обход JWT + IDOR на DELETE .../queues/auth). Теперь — точное сравнение /ui/api/auth |
1241093 |
| 🔴2 | app/gosqs/queue_attributes.go |
Запрет self-/циклического RedrivePolicy: перенос в DLQ берёт второй mu под первым → deadlock |
9b9bbe1 |
| 🔴3 | app/gosqs/gosqs.go, models.go, change_message_visibility*.go |
Перенос в DLQ не персистился (сообщение «воскрешало» в исходной и пропадало из DLQ) | 1e334c2 |
| 🔴4 | app/persistence/redis.go, app/tenant/tenant_store.go |
DeleteTenant не чистил индекс ssq:tenant_ak — отозванный тенант возвращался через read-through |
f68b0a4 |
| 🔴5 | app/persistence/redis.go, app/cmd/goaws.go |
Глобальный Client перезаписывался в reconnect() без синхронизации (data race). → atomic.Pointer |
d03ce36 |
| 🔴6 | app/conf/config.go |
return ports без SyncQueues.Unlock() при ошибке setQueueRedrivePolicy → вечный deadlock |
1520753 |
| 🔴7 | app/billing/billing.go |
Close() писал db=nil параллельно с usageWorker (nil-паника). → мьютекс + остановка воркера до db.Close |
4e36ba0 |
| 🔴10 | app/auth/auth_middleware.go |
X-Amz-Content-Sha256 не сверялся с телом — подмена тела под подписанным заголовком |
2918a41 |
(🔴8/🔴9 — на стороне IoT, см. IoT/HISTORY/2026-10-11-session-log.md.)
🟡 Важные
| Пункт | Файл | Суть | Коммит |
|---|---|---|---|
| 🟡20 | app/admin/admin.go |
createTenantQueue не валидировал имя очереди (:,/ ломали ключ) |
efd8c8e |
| 🟡23 | app/auth/jwt.go |
Токен без exp принимался бессрочно → требуем exp |
efd8c8e |
| 🟡24 | app/leader/leader.go |
При сбое RefreshLock инстанс оставался лидером (split-brain) → сброс лидерства после TTL |
efd8c8e |
| 🟡21 | app/tenant/tenant_store.go |
Запрет : в BYOC access_key (коллизии ключа {ak}:{name}) |
dc4815d |
| 🟡22 | app/admin/admin.go |
sendMessageToQueue без лимитов + async persist → лимит MaxMessagesForQueue + SaveMessageSync |
dc4815d |
| 🟡33 | app/models/models.go |
InitDuplicatation падал на nil-map Duplicates → ленивая инициализация |
dc4815d |
| 🟡35 | app/models/models.go |
Reset() не чистил FIFOMessages (после purge группы не выдавались) |
dc4815d |
| 🟡36 | app/gosqs/receive_message.go |
ApproximateReceiveCount был завышен на 1 |
dc4815d |
| 🟡38 | app/cmd/goaws.go |
billing.Close() вызывался ДО Shutdown серверов → перенесён после |
dc4815d |
| 🟡26 | app/gosqs/delete_message_batch.go |
Ключ map по ReceiptHandle (два entry с одним handle теряли Id) → по Id |
e0b5f24 |
| 🟡27 | app/gosqs/send_message.go |
DelaySeconds не валидировался → 0–900 |
e0b5f24 |
| 🟡25 | app/gosqs/send_message_batch.go |
Dedup регистрировался после цикла (два entry с одним id оба добавлялись); NextSequenceNumber до проверки дубликата; per-entry DelaySeconds не применялся |
6f69755 |
| 🟡29 | app/gosqs/delete_message*.go, persistence/redis.go |
Ошибка HDel только логировалась (клиент получал 200, после рестарта сообщение воскрешало) → DeleteMessage(s)Persist возвращает ошибку, 500 клиенту |
5d38118 |
| 🟡31 | app/persistence/redis.go, app/cmd/goaws.go |
Ошибка загрузки сообщений очереди давала continue (очередь стартовала пустой) → fail-fast (log.Fatalf) |
5d38118 |
| 🟡30 | app/persistence/redis.go |
Async SaveMessage без порядковой нумерации (старое состояние могло перезаписать новое) → seq по `{queueKey} |
{uuid}` + очистка при удалении |
| 🟡32 | app/gosqs/gosqs.go |
PeriodicTasks без recover (паника убивала процесс) |
dc4815d |
| 🟢 | app/utils/utils.go |
SqsErrors[errKey] без проверки → nil-deref на неизвестном ключе |
5d38118 |
| (🟡 new) | app/gosqs/create_queue.go |
Очередь из API получала VisibilityTimeout=0 и пр. (дефолты не подставлялись) |
5d38118 |
Осознанно НЕ менялось (решения владельца/архитектуры)
- 🟡34 (атрибуты «0 = не передано») — не менять совместимость.
- 🟡37 (дефолт
SHARED_SQS_MAX_HEAP_MB) — число зависит от RAM ВМ, решает владелец. - 🟡28 (не держать
queue.muво время сетевых вызовов) — не сделано: см. п.4.
3. Уточнение выводов ревьюера
Ревьюер задал вопросы по коду, ответы найдены в коде (файл:строка):
- ACL EMQX — единственный барьер для топиков
{ns}/telemetry/{deviceId}? Да:bridge/handler.goберётns/deviceIDиз топика без сверки принадлежности, барьер — EMQX authz (emqx/emqx.conf). Добавлена defense-in-depth проверка формата (IoT). - Self-/циклический
RedrivePolicyреально задаётся через API? Да (setQueueAttributesV1не проверял) — отсюда фикс 🔴2. - Пустой
adminToken? Недопустим —goaws.go:~74log.Fatalпри отсутствии. - Дефолты очереди расходятся (
create_queue.govsconf/config.go:100-109)? Да — отсюда фикс (🟡 new). utils.goпаника на неизвестном ключе? Текущей паники нет (все ключи есть вmodels.SqsErrors), но добавлена защита.
Также ревьюер отметил некорректный пункт (удалён из списка по лимиту 40): nil-map
Duplicates (🟡33) фактически не воспроизводился в текущем коде — но защита
добавлена как дешёвая страховка.
4. Осталось (требует решения/отдельного захода)
- 🟡28 (
send_message.go:150,delete_message.go,gosqs.go): синхронныйSaveMessageSync(до 3 с) выполняется подqueue.mu. Простой вынос сети заmuвносит регрессию: междуUnlockи повторнымLockпоявляется окно, в котором два конкурентныхSendMessageс однимdeduplicationIdоба пройдутIsDuplicate(дубль), либо при откатеdedupтеряется. Корректное решение требует более сложной синхронизации — отложено до отдельного захода. - 🟡34/🟡37 — см. п.2.
5. Верификация
go build ./...,go vet ./...,go test -race ./...— зелёные на всех коммитах.- Push:
2c6d2d7..7a2340dвorigin/master.