From f781de75cfdcefb4dad44756a9b7003d2314be86 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sun, 11 Oct 2026 09:14:12 +0300 Subject: [PATCH] =?UTF-8?q?docs(history):=20=D0=B2=D0=BD=D0=B5=D1=88=D0=BD?= =?UTF-8?q?=D0=B5=D0=B5=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20=D0=B8=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?2026-10-11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY/2026-10-11-session-log.md | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 HISTORY/2026-10-11-session-log.md diff --git a/HISTORY/2026-10-11-session-log.md b/HISTORY/2026-10-11-session-log.md new file mode 100644 index 0000000..3c58179 --- /dev/null +++ b/HISTORY/2026-10-11-session-log.md @@ -0,0 +1,51 @@ +# Журнал сессии — 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` | + +### Осознанно НЕ менялось (решения) + +- 🟡16 (QoS1 ack): оставлен async-ack + метрика дропов (throughput важнее; синхронный + ack блокировал бы MQTT-колбэк — это уже чинили ранее). +- 🟡12 (`AUTH_TEST_MODE`): уже fail-closed — при отсутствии `JWT_HMAC_SECRET` и + `AUTH_TEST_MODE=false` middleware отказывает в обслуживании. + +## 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`.