v0.1.35: фиксы — синхронные удаления в Redis (воскрешение очередей) + RedrivePolicy tenant-scoped DLQ
This commit is contained in:
+40
-45
@@ -184,55 +184,49 @@ func SaveMessages(queueKey string, msgs []models.SqsMessage) {
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteMessagePersist — удаляет одно сообщение из Redis асинхронно.
|
||||
// Вызывать при DeleteMessage (после удаления из in-memory).
|
||||
// DeleteMessagePersist — удаляет одно сообщение из Redis СИНХРОННО.
|
||||
// Синхронность обязательна: если удаление потеряется при рестарте пода,
|
||||
// сообщение «воскреснет» из Redis (факт: удалённые очереди возвращались
|
||||
// после рестарта, когда удаления были асинхронными).
|
||||
func DeleteMessagePersist(queueKey string, msgUuid string) {
|
||||
if Client == nil || msgUuid == "" {
|
||||
return
|
||||
}
|
||||
hashKey := redisMsgHashPrefix + queueKey
|
||||
asyncWrite(func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.HDel(ctx, hashKey, msgUuid).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel message %s/%s: %v", queueKey, msgUuid, err)
|
||||
}
|
||||
})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.HDel(ctx, hashKey, msgUuid).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel message %s/%s: %v", queueKey, msgUuid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteMessagesPersist — удаляет несколько сообщений из Redis.
|
||||
// Вызывать при DeleteMessageBatch.
|
||||
// DeleteMessagesPersist — удаляет несколько сообщений из Redis СИНХРОННО
|
||||
// (иначе удалённые сообщения воскреснут после рестарта пода).
|
||||
func DeleteMessagesPersist(queueKey string, uuids []string) {
|
||||
if Client == nil || len(uuids) == 0 {
|
||||
return
|
||||
}
|
||||
hashKey := redisMsgHashPrefix + queueKey
|
||||
// Копируем uuids — вызывающий может переиспользовать slice
|
||||
ids := make([]string, len(uuids))
|
||||
copy(ids, uuids)
|
||||
asyncWrite(func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.HDel(ctx, hashKey, ids...).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel messages %s (%d): %v", queueKey, len(ids), err)
|
||||
}
|
||||
})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.HDel(ctx, hashKey, uuids...).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel messages %s (%d): %v", queueKey, len(uuids), err)
|
||||
}
|
||||
}
|
||||
|
||||
// PurgeMessagesPersist — удаляет ВСЕ сообщения очереди из Redis (для PurgeQueue).
|
||||
// Удаляет весь HASH ssq:msg:{queueKey}.
|
||||
// PurgeMessagesPersist — удаляет ВСЕ сообщения очереди из Redis СИНХРОННО.
|
||||
// Синхронность обязательна: при рестарте пода удалённые сообщения не должны
|
||||
// воскреснуть (факт: асинхронное удаление очередей приводило к их возврату).
|
||||
func PurgeMessagesPersist(queueKey string) {
|
||||
if Client == nil {
|
||||
return
|
||||
}
|
||||
hashKey := redisMsgHashPrefix + queueKey
|
||||
asyncWrite(func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.Del(ctx, hashKey).Err(); err != nil {
|
||||
log.Errorf("persistence: DEL messages hash %s: %v", queueKey, err)
|
||||
}
|
||||
})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := Client.Del(ctx, hashKey).Err(); err != nil {
|
||||
log.Errorf("persistence: DEL messages hash %s: %v", queueKey, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteQueue — удаляет метаданные очереди И все её сообщения из Redis асинхронно.
|
||||
@@ -242,21 +236,22 @@ func DeleteQueue(key string) {
|
||||
}
|
||||
writeSeq := nextQueueWriteSeq(key)
|
||||
msgHashKey := redisMsgHashPrefix + key
|
||||
asyncWrite(func() {
|
||||
if !isLatestQueueWriteSeq(key, writeSeq) {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
// Удаляем метаданные из общего HASH
|
||||
if err := Client.HDel(ctx, redisHashQueues, key).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel queue meta %q: %v", key, err)
|
||||
}
|
||||
// Удаляем весь HASH с сообщениями
|
||||
if err := Client.Del(ctx, msgHashKey).Err(); err != nil {
|
||||
log.Errorf("persistence: DEL messages hash %q: %v", key, err)
|
||||
}
|
||||
})
|
||||
// СИНХРОННО: удаление очереди обязано попасть в Redis до ответа клиенту.
|
||||
// Асинхронное удаление приводило к «воскрешению» удалённых очередей после
|
||||
// рестарта пода (факт 2026-08-15: 50 мусорных очередей вернулись из Redis).
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if !isLatestQueueWriteSeq(key, writeSeq) {
|
||||
return
|
||||
}
|
||||
// Удаляем метаданные из общего HASH
|
||||
if err := Client.HDel(ctx, redisHashQueues, key).Err(); err != nil {
|
||||
log.Errorf("persistence: HDel queue meta %q: %v", key, err)
|
||||
}
|
||||
// Удаляем весь HASH с сообщениями
|
||||
if err := Client.Del(ctx, msgHashKey).Err(); err != nil {
|
||||
log.Errorf("persistence: DEL messages hash %q: %v", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAllQueues — загружает все очереди из Redis в память при старте сервиса.
|
||||
|
||||
Reference in New Issue
Block a user