From 099900580b45cc4625f6e576c96492443850b1bf Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sat, 10 Oct 2026 19:29:06 +0300 Subject: [PATCH] =?UTF-8?q?docs(HISTORY):=20=D0=B6=D1=83=D1=80=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=202026-10-10=20?= =?UTF-8?q?=E2=80=94=20=D0=BB=D0=B5=D0=B3=D0=B0=D1=81=D0=B8,=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=82=D0=BA=D0=B0=20master,=20=D1=80=D0=B5=D0=B2=D1=8C?= =?UTF-8?q?=D1=8E=20=D0=B8=20=D1=84=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Полная трассировка: чистка легаси, переименование в master, правки по ревью Opus, PRO-ревью (3 находки), Этап 3/4/5, P0-3 bcrypt, тесты, открытые находки (§5), осталось (§6). --- HISTORY/2026-10-10-session-log.md | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 HISTORY/2026-10-10-session-log.md diff --git a/HISTORY/2026-10-10-session-log.md b/HISTORY/2026-10-10-session-log.md new file mode 100644 index 0000000..92c0660 --- /dev/null +++ b/HISTORY/2026-10-10-session-log.md @@ -0,0 +1,101 @@ +# Журнал сессии — 2026-10-10 (IoT: чистка легаси → ветка master → ревью → фиксы) + +**Рабочая папка:** /home/naeel/nubes/SQS_und_IoT/IoT +**Ветка:** `master` (переименована из `feature/replace-kafka-with-sqs`) +**Состояние на конец сессии:** 28 коммитов сверх `origin/master`, **не запушено** (пуш — только по команде). + +--- + +## 1. Чистка легаси + +Цель владельца: «старое ГАВНО не всплывало». + +- 🗑 Удалён код-кластер старого IoT (не импортировался монолитом, не собирался): + `cmd/iot-operator/`, `cmd/mqtt-bridge/`, `controllers/`, `api/v1alpha1/`, + `internal/api/` (старый REST + UI-embeds admin/console). Коммит `f152f54`. +- `go mod tidy` убрал k8s-зависимости (`k8s.io/*`, `controller-runtime`) — −57 строк `go.mod`, −614 `go.sum`. +- 🗑 `bin/` со старыми бинарниками (operator/kafka-consumer/mqtt-bridge). +- `iot-admin.html` (живой `/iot-admin`): секции Kafka/K8s Pods → «Очередь shared-SQS» + «Рантайм»; удалён CSS `.pod-*/.badge-*/.dot-*/.lag-bar*`. Коммит `42f0d6a`. +- `.gitignore`: убраны сталые пути бинарников. +- `LEGACY.md` обновлён (помечено `УДАЛЁН 2026-10-10`). + +Осталось на диске вне git: `iot-console.html` со **старыми доменами** `sless.kube5s.ru` / `iot.kube5s.ru` (см. §6, требует решения владельца — какой домен). + +## 2. Ветка → master, коммит/пуш + +- SQS-service: `main` → `master`; IoT: `feature/replace-kafka-with-sqs` → `master` (старая `master` перезаписана, её 3 коммита влиты merge'ом, конфликт только в `.gitignore`). +- Пуш в Gitea (`gitea.services.ngcloud.ru/Nail/{SQS-service,IoT}`): `origin/master` создан/обновлён. Force не использовался. + +## 3. Правки кода по ревью Opus + +### Блокеры Terraform (Этап 2) +- `632e551` MQTT-пароль устройства отдаётся один раз в 201 + `phase` пишется в БД (Create). +- `a6ef9f9` `created_at` → RFC3339 + перечитывание после Create. +- `7e3828f` `telemetry_topic` + нормализация `metadata` (без drift `{}`/null). +- `7ea4923` валидация `namespace/name/device_id` (`^[a-zA-Z0-9][a-zA-Z0-9-]*$`, запрет `_ + # /`). + +### Потери данных (Этап 1) +- `046db68` 🔴1 сериализация flush (`flushMu`) — Flush дожидается in-flight записи. +- `e0cd1ca` 🔴2 построчный fallback (ядовитая строка не роняет весь батч). +- `006dbfa` 🔴2 семантическая валидация envelope (пустые поля → DLQ). +- `01d21f6` 🔴3 слив буфера bridge при shutdown + безопасное закрытие канала. +- `241c98a` 🔴4 ретраи SQS до shutdown + таймаут на попытку. +- `69ae2ff` 🔴6 не рвать автореконнект (ретрай подписки, без `Disconnect`). +- `530ee7b` 🔴7 ретрай резолва DLQ + не терять битые при недоступной DLQ. +- `47d9a04` 🔴5 ретрай GetQueueUrl на старте + выход ≠0 при сбое подсистемы. + +## 4. PRO-ревью (моё) — находки и фиксы + +Разобрал работу предыдущей модели, нашёл 3 🔴 + 🟡/🟢: + +- `98e6def` **#1** `os.Exit` обходил `defer iotStore.Close()` → финальный flush терялся. Вынес логику в `run() int`, `main = os.Exit(run())`. +- `6e4a844` **#2** «не подтверждать битое при недоступной DLQ» = ядовитое зацикливается. Добавлено чтение `ApproximateReceiveCount` (после `maxReceiveCount=5` — дроп). +- `c1733c5` **#3** `/ready` = «горутина запущена», а не «подписан». Bridge сам управляет флагом через `onSubscribed`. + +### Этап 4 (изоляция/JWT) +- `ea569da` JWT: обязательный `exp`, 401 вместо 403, guard тест-режима, JSON-ошибки. +- `2bea1de` `replaceDSNDatabase` (устранено смешение тенантов: ошибка для key=value DSN, корректный разбор `sslrootcert=/path`) + guard namespace (`safeNamespaceRe`, `ErrInvalidNamespace`). + +### Этап 3 (жизненный цикл) +- `75c30e5` readiness consumer по SQS (`onReady`), ограничение `wg.Wait` (`waitWithTimeout`, 15с), идемпотентный `Close` (`sync.Once`). + +### Этап 5 (мелочи) +- `f37f216` удалён мёртвый `mqtt_auth.go` + constant-time админ-токен. +- `fa98020` `decodeJSON` (MaxBytesReader 64КиБ + DisallowUnknownFields) + таймауты сервера. +- `d7cb850` `DeleteMessageBatch` (до 10 за вызов). +- `1510efe` пагинация: tiebreaker `ts DESC, id DESC` + потолок `limit` + точный лог. +- `11bb38e` `MaxReconnectInterval` 30с + Debug для health-проб/телеметрии. + +### Terraform / безопасность +- `eacc740` PATCH обновляет `metadata`. +- `0a51d70` 409 различает конфликт `name` vs `device_id` (`ErrDeviceIDExists`). +- `c1adb26` **P0-3** bcrypt-хэш паролей устройств и бриджа + EMQX `name=bcrypt, salt_rounds=10` (ключ `salt_rounds` подтверждён по исходникам emqx/emqx; Go `DefaultCost=10`). + +### Тесты / доки +- `46744ac` юнит-тесты: `validateIdentifier` (handler), `replaceDSNDatabase` + `safeNamespaceRe` (iotpg). +- `1600339` деплой-дока: пароль отдаётся один раз, в БД bcrypt. + +## 5. Повторное PRO-ревью — открытые находки + +- 🟠 **ReceivedAt — точность до секунды.** `bridge/handler.go` пишет `time.RFC3339`, `consumer/processor.go` парсит `time.RFC3339`; субсекунды теряются. Очередь standard (не FIFO) → два показания в одну секунду могут встать не в том порядке; tiebreaker `id DESC` не чинит (id = порядок SQS-доставки). **Фикс: RFC3339Nano на обеих сторонах.** +- 🟡 Пустое тело (`msg.Body == ""`) подтверждается без DLQ — для консистентности тоже в DLQ. +- 🟡 `processBackoff`-sleep внутри цикла батча задерживает подтверждение соседей → redelivery (спасают дедуп + фоновый flusher). Пре-существующий дизайн. +- ⚠️ Валидация `namespace`/`exp` зависят от формата `sub`/токена платформы — не проверено. +- ⚠️ `flushMu` — глобальная сериализация (осознанный компромисс 🔴1 vs throughput). +- ⚠️ Гонки не прогнаны под `go test -race`. + +## 6. Осталось (не сделано) + +- 🟠 RFC3339Nano для `ReceivedAt` (находка §5). +- 🟡 пустое тело → DLQ. +- Решить домены в `iot-console.html` (старые `kube5s` → выбрать актуальные; как в `landing.html`: `iot.containerk8s.dev.nubes.ru` / `exqx.containerk8s.dev.nubes.ru`). +- `go test -race` + тесты на `flushAll`/`Close`. +- Проверить формат `sub` платформы (валидация namespace + JWT `exp`). +- Terraform-провайдер (следующая большая задача после IoT). +- SQS-хвосты (`CreateQueue`→`QueueNameExists`, `ListQueues`, JWT по `sub`, лимит по байтам). + +## Правила (напомню себе) +- Коммитить после каждой правки; пуш — только по команде. +- Документировать всё, включая ошибки (HISTORY/ + doc/thinking/). +- Не задавать вопросов, ответ на которые есть в коде/доках. +- «Рекомендация без замера — не рекомендация».