// processor.go — разбор envelope телеметрии и запись в per-tenant PostgreSQL. package consumer import ( "context" "encoding/json" "errors" "log/slog" "gitea.services.ngcloud.ru/Nail/IoT/internal/service/bridge" "gitea.services.ngcloud.ru/Nail/IoT/internal/storage/iotpg" ) // errInvalidEnvelope — битый/неразборчивый envelope: повторная обработка // бессмысленна, сообщение следует отправить в DLQ, а не ретраить бесконечно. var errInvalidEnvelope = errors.New("invalid telemetry envelope") // processTelemetry десериализует envelope и пишет в tenant DB. // Битый JSON — возвращает errInvalidEnvelope (consumer шлёт в DLQ), // а НЕ молчаливый nil: иначе мусор подтверждается и теряется безвозвратно. func processTelemetry(ctx context.Context, body string, store *iotpg.IoTPostgresStore, log *slog.Logger) error { var envelope bridge.TelemetryEnvelope if err := json.Unmarshal([]byte(body), &envelope); err != nil { log.Warn("consumer: invalid envelope, routing to DLQ", "err", err) return errInvalidEnvelope } // EnsureTenantDB идемпотентен, кэшируется после первого вызова. if err := store.EnsureTenantDB(ctx, envelope.Namespace); err != nil { return err } if err := store.InsertTelemetry(ctx, envelope.Namespace, envelope.DeviceID, envelope.MessageID, envelope.Payload); err != nil { return err } log.Info("consumer: telemetry saved", "namespace", envelope.Namespace, "device", envelope.DeviceID, ) return nil }