diff --git a/internal/service/consumer/processor.go b/internal/service/consumer/processor.go index 254947b..abf0c24 100644 --- a/internal/service/consumer/processor.go +++ b/internal/service/consumer/processor.go @@ -26,6 +26,17 @@ func processTelemetry(ctx context.Context, body string, store *iotpg.IoTPostgres return errInvalidEnvelope } + // Семантическая валидация envelope ДО записи в БД: пустые namespace/device_id + // или пустой payload — битое сообщение, ретраем не исправится. Пустой + // namespace → EnsureTenantDB создал бы БД «tenant_», пустой payload → NULL + // (23502) или битый JSONB. Такое шлём в DLQ (errInvalidEnvelope), а не + // бесконечно ретраим (ревью 2026-10-10, 🔴2). + if envelope.Namespace == "" || envelope.DeviceID == "" || len(envelope.Payload) == 0 { + log.Warn("consumer: invalid envelope (empty namespace/device_id/payload), routing to DLQ", + "namespace", envelope.Namespace, "device", envelope.DeviceID) + return errInvalidEnvelope + } + // EnsureTenantDB идемпотентен, кэшируется после первого вызова. if err := store.EnsureTenantDB(ctx, envelope.Namespace); err != nil { return err