Commit Graph
10 Commits
Author SHA1 Message Date
Repinoid e31f88ba6b fix(iot): построчный fallback при постоянной ошибке батча (🔴2)
Одна «ядовитая» строка (NULL/пустой payload, \u0000 в JSONB, битый JSON)
ронила весь multi-VALUES батч (до telemetryBatchSize валидных строк), который
классифицировался как permanent и выбрасывался целиком.

Добавлен flushRowsIndividually: при не-retryable ошибке батча вставляем строки
по одной (ON CONFLICT DO NOTHING) — валидные сохраняются, битая логируется.
2026-10-10 18:53:34 +03:00
Repinoid 7407de5fd3 fix(iot): сериализовать flush (гонка с фоновым flusher'ом — потеря данных)
🔴1 ревью: flushAll и синхронная запись полного батча в InsertTelemetry писали
параллельно с фоновым flusher'ом (каждые 200мс). Flush() из consumer видел
пустой буфер (фоновый flusher уже забрал строки) и возвращал nil → consumer
подтверждал SQS-сообщение, пока INSERT ещё в полёте/упал.

Добавлен flushMu: сериализует grab буфера + INSERT между фоновым flusher'ом,
Flush() и полным батчом InsertTelemetry. Теперь Flush дожидается in-flight
записи и возвращает реальную ошибку.
2026-10-10 18:52:43 +03:00
Repinoid e8873fa24d fix(telemetry): ts = время приёма (ReceivedAt), а не момент INSERT
Раньше ts проставлялся now() в БД при INSERT → при батчинге и параллельных
записях порядок телеметрии (ORDER BY ts DESC) ломался. Теперь в ts идёт
ReceivedAt из envelope (серверное время приёма, ставит bridge); битый/пустой
received_at → zero → стор подставляет now(). Схема не меняется (используется
существующая колонка ts). Обновлены callers: consumer/processor.go и legacy
cmd/sqs-consumer.
2026-10-07 07:47:10 +03:00
Repinoid 4ee0fc3b61 fix(telemetry): различать retryable/permanent ошибки flush (не залипать на ядовитом батче)
isRetryableFlushErr: '08' (connection) и 57P01/02/03 (shutdown/crash) — retryable
(возврат в буфер); '22' (data exception) и '23' (integrity violation) — permanent
(батч выбрасывается с логом, НЕ возвращается в буфер). Иначе один невалидный
payload зацикливал flush namespace навсегда (вечные 200мс-ретраи). Относится к
обоим путям: flushAll и InsertTelemetry(full).
2026-10-07 07:41:05 +03:00
Repinoid b6c083e80f fix(telemetry): не терять строки при ошибке flush (flushAll + InsertTelemetry)
flushAll и InsertTelemetry (путь заполнения батча) вынимали строки из буфера и
подменяли буфер ДО записи: при ошибке flushTenant строки исчезали. Хуже —
фоновый flusher мог забрать строки, которые consumer ждал в Flush, тогда Flush
видел пустой буфер, возвращал nil, и consumer подтверждал SQS-сообщение с
незаписанными данными (потеря). Теперь при ошибке строки возвращаются в буфер
в обоих путях. ctx проброшен в flushAll/flushTenant. Consumer хранит receipt
handle значением (string), не указателем SDK.
2026-10-07 07:32:00 +03:00
Repinoid 38602b7560 P0-8: идемпотентность телеметрии (dedup по message_id)
Envelope получил MessageID (UUID); таблица iot_telemetry — столбец message_id +
UNIQUE(device_id,message_id); батч-INSERT ON CONFLICT DO NOTHING. Дубли при
at-least-once повторе из SQS больше не вставляются. Пустой message_id → NULL
(без ложного дедупа).
2026-10-07 07:26:50 +03:00
Repinoid 3175605931 P0-7: подтверждение из SQS только после успешного flush телеметрии в БД
InsertTelemetry лишь буферизует и возвращает nil до фактической записи. Consumer
удалял сообщение сразу после processTelemetry == nil — краш/flush-ошибка в этом
окне = безвозвратная потеря. Теперь: копим receipt handles, вызываем store.Flush,
и только при успехе DeleteMessage; при ошибке flush — не удаляем (сообщения
вернутся после visibility timeout). Добавлен iotpg.Flush().
2026-10-07 07:23:25 +03:00
“Naeel” 68becfd6bc fix: review findings (async SQS dispatcher, backoff, batch insert, shutdown order, pagination, HMAC) + loadtest fixes; v0.1.6 2026-08-16 18:24:09 +04:00
Naeel bbc26555fc fix: MQTTAuth device lookup by Spec.DeviceID + PG15+ GRANT (v0.2.5)
- MQTTAuth: replaced Get(Name=deviceID) with List+filter by Spec.DeviceID
  (K8s object name != deviceID — caused deny for all devices)
- EnsureTenantDB: added GRANT role TO CURRENT_USER before CREATE DATABASE OWNER
  (PG15+ requires SET ROLE privileges for target owner)
- Image: naeel/iot-operator:v0.2.5
- E2E test passed: device create → MQTT publish → SQS → Postgres → REST API
2026-04-12 19:00:56 +03:00
Naeel aba7683be2 feat: перенос IoT managed service из sless в отдельную репу
Компоненты:
- iot-operator: controller-manager (IoTDevice CRD) + REST API (порт 9090)
- mqtt-bridge: MQTT (EMQX) → Kafka bridge
- kafka-consumer: Kafka → Postgres pipeline

Модуль: gitea.services.ngcloud.ru/Nail/IoT
Все 3 бинарника собираются, import paths адаптированы.
2026-04-12 14:29:43 +03:00