fix(concurrency,durability): address Opus review critical findings

- create_queue: SaveQueue under per-queue lock, not global SyncQueues.Lock
  (fixes fatal concurrent map iteration/write on idempotent CreateQueue of existing FIFO queue)
- delete_message_batch: reject empty ReceiptHandle (JSON protocol bypass deleted all unreceived msgs)
- send_message/send_message_batch: register dedupId only after successful persist
  (retry with same dedupId no longer returns 200 without persisting)
- memory_guard: global HeapAlloc guard (SHARED_SQS_MAX_HEAP_MB, 0=off) in send paths
This commit is contained in:
Repinoid
2026-10-10 17:07:52 +03:00
parent ea7b539d19
commit 521e3d85d0
5 changed files with 80 additions and 12 deletions
+11 -1
View File
@@ -111,9 +111,14 @@ func SendMessageBatchV1(req *http.Request) (int, interfaces.AbstractResponseBody
if queue.LiveCount()+len(sendEntries) > MaxMessagesForQueue(queue.IsFIFO) {
return utils.CreateErrorResponseV1("OverLimit", true)
}
// Глобальный предохранитель памяти (HeapAlloc) — защита от OOM суммой тенантов.
if heapOverLimit() {
return utils.CreateErrorResponseV1("OverLimit", true)
}
newMsgs := make([]models.SqsMessage, 0, len(sendEntries))
appendedIdx := make([]int, 0, len(sendEntries))
appendedDedupIDs := make([]string, 0, len(sendEntries))
for _, sendEntry := range sendEntries {
msg := models.SqsMessage{MessageBody: sendEntry.MessageBody}
if len(sendEntry.MessageAttributes) > 0 {
@@ -133,10 +138,10 @@ func SendMessageBatchV1(req *http.Request) (int, interfaces.AbstractResponseBody
if !queue.IsDuplicate(sendEntry.MessageDeduplicationId) {
appendedIdx = append(appendedIdx, queue.AppendMessage(msg))
newMsgs = append(newMsgs, msg)
appendedDedupIDs = append(appendedDedupIDs, sendEntry.MessageDeduplicationId)
} else {
log.Debugf("Duplicate deduplicationId [%s] in queue [%s]", sendEntry.MessageDeduplicationId, queueName)
}
queue.InitDuplicatation(sendEntry.MessageDeduplicationId)
sentEntries = append(sentEntries, models.SendMessageBatchResultEntry{
Id: sendEntry.Id,
@@ -157,6 +162,11 @@ func SendMessageBatchV1(req *http.Request) (int, interfaces.AbstractResponseBody
}
return utils.CreateErrorResponseV1("InternalError", true)
}
// Регистрируем dedupId ТОЛЬКО после успешной записи — иначе ретрай с теми же
// dedupId получил бы 200 без сохранённых сообщений.
for _, id := range appendedDedupIDs {
queue.InitDuplicatation(id)
}
// Метаданные очереди (FIFO state, duplicates) — без Messages
persistence.SaveQueue(key, queue)