P0.3: синхронный персист SendMessage до ответа 200

Добавлен persistence.SaveMessageSync (синхронный HSet, возвращает ошибку).
SendMessage теперь пишет сообщение в Redis ДО возврата клиенту; при ошибке Redis
сообщение откатывается из памяти и возвращается InternalError (новая ошибка в
models/errors.go). Устраняет окно потери подтверждённого сообщения при краше пода
между 200 OK и асинхронной записью.
This commit is contained in:
Repinoid
2026-10-06 18:04:22 +03:00
parent 928a3fb85d
commit c19a056de3
3 changed files with 39 additions and 2 deletions
+23
View File
@@ -150,6 +150,29 @@ func SaveMessage(queueKey string, msg *models.SqsMessage) {
})
}
// SaveMessageSync — сохраняет ОДНО сообщение в Redis СИНХРОННО и возвращает ошибку.
// Используется на пути SendMessage: ответ 200 клиенту должен выдаваться ТОЛЬКО
// после фактической записи сообщения в Redis, иначе краш пода в окне между
// ответом и async-записью приведёт к потере подтверждённого сообщения.
// Ключ: ssq:msg:{queueKey}, поле: msg.Uuid, значение: JSON(SqsMessage).
// Если Client == nil (memory-only режим) — возвращает nil (персистить некуда).
func SaveMessageSync(queueKey string, msg *models.SqsMessage) error {
if Client == nil {
return nil
}
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("marshal message %s/%s: %w", queueKey, msg.Uuid, err)
}
hashKey := redisMsgHashPrefix + queueKey
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
if err := Client.HSet(ctx, hashKey, msg.Uuid, string(data)).Err(); err != nil {
return fmt.Errorf("HSet message %s/%s: %w", queueKey, msg.Uuid, err)
}
return nil
}
// SaveMessages — сохраняет несколько сообщений в Redis одним pipeline.
// Вызывать под Lock.
func SaveMessages(queueKey string, msgs []models.SqsMessage) {