4.6 KiB
4.6 KiB
Журнал сессии — 2026-10-11 (IoT: внешнее код-ревью и фиксы)
Запись по требованию «документировать всё, включая собственные ошибки» (приказ 2026-09-24). Предыдущие:
HISTORY/2026-10-07-session-log.md,HISTORY/2026-10-10-session-log.md. Основание: внешнее ревью Claude Sonnet по промпту../CLAUDE-REVIEW-PROMPT.md(40 пунктов на два сервиса; SQS-часть — вSQS-service/HISTORY/2026-10-11-session-log.md).
1. Задача
Разобрать находки ревью по монолиту iot-service и исправить их.
2. Что исправлено
🔴 Критичные
- 🔴8/🔴9 — poison-строка в
flushAll(internal/storage/iotpg/iot_telemetry_store.go:166,177). При постоянной (не retryable) ошибкеflushAllвозвращал ошибку → consumer (consumer.go:159) не удалял батч из SQS → бесконечный redelivery всего батча. Теперь ошибку ставит только retryable-ветка; постоянная ошибка означает «валидные строки уже вставлены построчно, битые отброшены» →Flushвозвращает nil, батч подтверждается. Коммит1fe543f.
🟡 Важные
| Пункт | Файл | Суть | Коммит |
|---|---|---|---|
| 🟡13 | bridge/sender.go |
Ретраил бесконечно любые ошибки SQS; permanent (4xx/размер/нет очереди) занимали воркеры → классификация isPermanentSQSErr (smithy APIError) |
657850d |
| 🟡14 | bridge/handler.go |
MessageID = uuid на каждый колбэк → повторная QoS1-доставка не дедуплицировалась. Теперь SHA256(ns|device|topic|payload) |
657850d |
| 🟡15 | bridge/bridge.go |
После 3 неудач подписки ждали следующего реконнекта (которого не будет при живом соединении) → бесконечный ретрай подписки | 657850d |
| (new) | bridge/handler.go |
Defense-in-depth: валидация формата ns/deviceID из топика (барьер — EMQX authz) |
657850d |
| 🟡17 | consumer/consumer.go |
receiveCount проверялся только для битого envelope → применяем maxReceiveCount ко всем ошибкам |
5114320 |
| 🟡18 | cmd/iot-service/main.go |
run() выходил до завершения server.Shutdown → deferred Close срабатывали до дренажа in-flight. Добавлен shutdownDone |
5114320 |
| 🟡19 | api/handler/devices.go |
Ошибка GetByName после успешного INSERT давала 500 (пароль терялся, повтор → 409) → отдаём 201 с in-memory объектом |
5114320 |
| 🟡11 | iotpg/iot_telemetry_store.go |
getTenantDB/QueryTelemetry не проверяли safeNamespaceRe (a_b ↔ a-b → одна БД) → валидация на всех путях |
f41a9fe |
Осознанно НЕ менялось — РЕШЕНИЯ (2026-10-11)
- 🟡16 (QoS1 ack): решение — оставить async-ack + метрика дропов. Синхронный
ack (подтверждать только после отправки в SQS) блокировал бы MQTT-колбэк при
недоступности SQS — это уже чинили ранее (потеря ВСЕХ сообщений). Throughput
важнее; дроп при переполнении фиксируется счётчиком
dropped. - 🟡12 (
AUTH_TEST_MODE): решение — оставить как есть. Уже fail-closed: при отсутствииJWT_HMAC_SECRETиAUTH_TEST_MODE=falsemiddleware отказывает в обслуживании (500), а test-mode срабатывает только без секрета.
3. Прочее (перед ревью, в этой же сессии)
193509c— консоль: namespace =sub(убралиsless-{SHA256}, расходившийся с middleware) и удалили несуществующийPOST /v1/namespaces/{ns}/ensure.
4. Верификация
go build ./...,go vet ./...,go test -race ./...— зелёные.- Push:
54fe1f5..f41a9feвorigin/master.