fix: DLQ-релинк после загрузки из Redis + безопасная эвикция retention + ParseForm
1. DLQ больше не сериализуется вложенным объектом (была detached-копия после загрузки + неограниченная рекурсия при цикле): в метаданных хранится только имя (dead_letter_queue_name), после LoadAllQueues указатель релинкуется на объект из map. Закрывает потерянные переносы в DLQ и чужой мьютекс. 2. Retention-эвикция: компакция НА МЕСТЕ (чтение value в i, запись в n<=i) вместо kept := Messages[:0] — устранён алиасинг на переиспользуемом срезе. 3. countBatchEntries: явный req.ParseForm() — корректный msg_count для батчей. Тесты: +DLQ-перенос, +эвикция с сохранением порядка; go test -race зелёный.
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -163,11 +164,12 @@ type queueMetaDTO struct {
|
||||
Duplicates map[string]time.Time
|
||||
Tags map[string]string
|
||||
LastPurgeTime time.Time
|
||||
DeadLetterQueue *queueMetaDTO
|
||||
DeadLetterQueueName string `json:"dead_letter_queue_name,omitempty"`
|
||||
}
|
||||
|
||||
// queueToMetaDTO — строит снимок очереди без сообщений (и без сообщений DLQ),
|
||||
// НЕ мутируя исходную очередь. Рекурсия ограничена глубиной 1.
|
||||
// queueToMetaDTO — строит снимок очереди без сообщений, НЕ мутируя исходную очередь.
|
||||
// DLQ сохраняется как ИМЯ (не вложенный объект): иначе — detached-копия после
|
||||
// загрузки и неограниченная рекурсия при циклической DLQ.
|
||||
func queueToMetaDTO(q *models.Queue) *queueMetaDTO {
|
||||
if q == nil {
|
||||
return nil
|
||||
@@ -189,9 +191,10 @@ func queueToMetaDTO(q *models.Queue) *queueMetaDTO {
|
||||
Duplicates: q.Duplicates,
|
||||
Tags: q.Tags,
|
||||
LastPurgeTime: q.LastPurgeTime,
|
||||
DeadLetterQueueName: q.DeadLetterQueueName,
|
||||
}
|
||||
if q.DeadLetterQueue != nil {
|
||||
dto.DeadLetterQueue = queueToMetaDTO(q.DeadLetterQueue)
|
||||
dto.DeadLetterQueueName = q.DeadLetterQueue.Name
|
||||
}
|
||||
return dto
|
||||
}
|
||||
@@ -481,6 +484,24 @@ func LoadAllQueues() (map[string]*models.Queue, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Релинк DLQ: в метаданных хранится только имя DLQ — связываем указатель
|
||||
// с реальным объектом из map. Иначе DeadLetterQueue оказался бы detached-копией
|
||||
// (переносы в DLQ писали бы в неё, а не в живую очередь; мьютекс — чужой).
|
||||
for k, q := range queues {
|
||||
if q.DeadLetterQueueName == "" {
|
||||
continue
|
||||
}
|
||||
idx := strings.Index(k, ":")
|
||||
if idx <= 0 {
|
||||
continue
|
||||
}
|
||||
if dlq, ok := queues[k[:idx+1]+q.DeadLetterQueueName]; ok {
|
||||
q.DeadLetterQueue = dlq
|
||||
} else {
|
||||
log.Warnf("persistence: DLQ %q для очереди %q не найдена при загрузке", q.DeadLetterQueueName, k)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("persistence: загружено %d очередей из Redis (миграций: %d)", len(queues), len(migrateKeys))
|
||||
return queues, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user