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 (без ложного дедупа).
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// TelemetryEnvelope — сообщение, перекладываемое в SQS (разбирается consumer).
|
||||
@@ -20,6 +21,9 @@ type TelemetryEnvelope struct {
|
||||
Topic string `json:"topic"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
ReceivedAt string `json:"received_at"`
|
||||
// MessageID — уникальный id сообщения. Нужен для идемпотентности: SQS
|
||||
// at-least-once может доставить дубликат — consumer дедуплицирует по нему.
|
||||
MessageID string `json:"message_id"`
|
||||
}
|
||||
|
||||
// newMessageHandler возвращает обработчик MQTT-сообщений:
|
||||
@@ -51,6 +55,7 @@ func newMessageHandler(dispatcher *sqsDispatcher, log *slog.Logger) mqtt.Message
|
||||
Topic: topic,
|
||||
Payload: rawPayload,
|
||||
ReceivedAt: time.Now().UTC().Format(time.RFC3339),
|
||||
MessageID: uuid.NewString(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func processTelemetry(ctx context.Context, body string, store *iotpg.IoTPostgres
|
||||
if err := store.EnsureTenantDB(ctx, envelope.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := store.InsertTelemetry(ctx, envelope.Namespace, envelope.DeviceID, envelope.Payload); err != nil {
|
||||
if err := store.InsertTelemetry(ctx, envelope.Namespace, envelope.DeviceID, envelope.MessageID, envelope.Payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user