Полная трассировка: чистка легаси, переименование в master, правки по ревью Opus, PRO-ревью (3 находки), Этап 3/4/5, P0-3 bcrypt, тесты, открытые находки (§5), осталось (§6).
8.4 KiB
8.4 KiB
Журнал сессии — 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, −614go.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)
632e551MQTT-пароль устройства отдаётся один раз в 201 +phaseпишется в БД (Create).a6ef9f9created_at→ RFC3339 + перечитывание после Create.7e3828ftelemetry_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#1os.Exitобходилdefer iotStore.Close()→ финальный flush терялся. Вынес логику вrun() int,main = os.Exit(run()).6e4a844#2 «не подтверждать битое при недоступной DLQ» = ядовитое зацикливается. Добавлено чтениеApproximateReceiveCount(послеmaxReceiveCount=5— дроп).c1733c5#3/ready= «горутина запущена», а не «подписан». Bridge сам управляет флагом черезonSubscribed.
Этап 4 (изоляция/JWT)
ea569daJWT: обязательныйexp, 401 вместо 403, guard тест-режима, JSON-ошибки.2bea1dereplaceDSNDatabase(устранено смешение тенантов: ошибка для key=value DSN, корректный разборsslrootcert=/path) + guard namespace (safeNamespaceRe,ErrInvalidNamespace).
Этап 3 (жизненный цикл)
75c30e5readiness consumer по SQS (onReady), ограничениеwg.Wait(waitWithTimeout, 15с), идемпотентныйClose(sync.Once).
Этап 5 (мелочи)
f37f216удалён мёртвыйmqtt_auth.go+ constant-time админ-токен.fa98020decodeJSON(MaxBytesReader 64КиБ + DisallowUnknownFields) + таймауты сервера.d7cb850DeleteMessageBatch(до 10 за вызов).1510efeпагинация: tiebreakerts DESC, id DESC+ потолокlimit+ точный лог.11bb38eMaxReconnectInterval30с + Debug для health-проб/телеметрии.
Terraform / безопасность
eacc740PATCH обновляетmetadata.0a51d70409 различает конфликтnamevsdevice_id(ErrDeviceIDExists).c1adb26P0-3 bcrypt-хэш паролей устройств и бриджа + EMQXname=bcrypt, salt_rounds=10(ключsalt_roundsподтверждён по исходникам emqx/emqx; GoDefaultCost=10).
Тесты / доки
46744acюнит-тесты:validateIdentifier(handler),replaceDSNDatabase+safeNamespaceRe(iotpg).1600339деплой-дока: пароль отдаётся один раз, в БД bcrypt.
5. Повторное PRO-ревью — открытые находки
- 🟠 ReceivedAt — точность до секунды.
bridge/handler.goпишетtime.RFC3339,consumer/processor.goпарситtime.RFC3339; субсекунды теряются. Очередь standard (не FIFO) → два показания в одну секунду могут встать не в том порядке; tiebreakerid 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 + JWTexp). - Terraform-провайдер (следующая большая задача после IoT).
- SQS-хвосты (
CreateQueue→QueueNameExists,ListQueues, JWT поsub, лимит по байтам).
Правила (напомню себе)
- Коммитить после каждой правки; пуш — только по команде.
- Документировать всё, включая ошибки (HISTORY/ + doc/thinking/).
- Не задавать вопросов, ответ на которые есть в коде/доках.
- «Рекомендация без замера — не рекомендация».