- ContentBasedDeduplication=true: dedupId computed from body SHA-256 when not provided
- do NOT drop dedupId on message delete: AWS keeps the 5-minute window regardless
(previously re-send after delete delivered the message again)
- utils: GetSHA256Hash
- GetQueueAttributes now returns FifoQueue, ContentBasedDeduplication, Policy and
real Created/LastModifiedTimestamp (previously missing/stubbed '0000000000')
- Queue: add CreatedAt, LastModifiedAt, Policy, ContentBasedDeduplication
- parse FifoQueue/ContentBasedDeduplication in both protocols (StringToBool)
- Policy/RedriveAllowPolicy: normalize JSON-string form to object (JSON protocol
sent them as string -> SetQueueAttributes failed with InvalidParameterValue)
- persistence: keep new fields in meta DTO + backfill empty timestamps on load
- FIFO dedup is inherent: IsDuplicate/InitDuplicatation gate on IsFIFO only
(env flag EnableDuplicates=false disabled dedup for all FIFO queues)
- jwt: drop test/dev gateway from nubesAPIEndpoints (test-stand token was accepted as prod)
- cache: grace bounded by graceDeadline from last successful validation (no more indefinite renewal)
- 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
- почему схему 1:1 скопировать нельзя (клиенты на AWS SDK, синхронный протокол) ->
аналог: прозрачный прокси на ВМ;
- условия: сохранение Host для SigV4 (auth_middleware.go:315-317), внешние QueueUrl
(сейчас http://region.host:port из goaws.yaml, tenant_helpers.go:29-37),
TLS, доступ ВМ->кластер, отключение буферизации;
- 4 варианта с оценкой (прокси / настройки ingress / MTU / ретраи) и таблица
'что какой лечит';
- обязательные замеры до решения (раздел 5) и открытые вопросы.
Собраны и проверены по источникам все факты из ~/nubes/drhider:
- диагноз: ingress рвёт входящие тела >64КБ, egress не ограничен;
- схема пофайлового транзита через ВМ-буфер + egress-GET (pull);
- применённые фиксы платформы (cilium mtu:1400, keep-alive 10->75,
proxy-body-size 1024m, HTTP/2 off) через ShturvalServiceConfig;
- ложные следы, лимиты, SSRF/path-traversal защиты, тесты 25/25;
- вывод: тикет в поддержку — не решение, нужен архитектурный обход.
1. TenantStore.Create: повторная проверка глобального лимита тенантов под Lock
(ранняя была под RLock — гонка могла превысить MaxTenantsGlobal).
2. CreateFromJWT: обновлённый NubesSub теперь персистится в Redis (раньше
сохранялся только внутри ветки смены MaxQueues и терялся при рестарте);
запись в Redis только при реальном изменении.
3. auth.PingNubesAPI: общий http.Client (keep-alive) вместо нового на каждый
вызов.
Пункт 4 ревью: убран дублирующий блок перестройки индексов в Compact
(ensureIndexes на входе + один RebuildIndexes на выходе). Поведение то же,
логика чище. Дополнен HISTORY разделом о фиксах код-ревью.
1. ChangeMessageVisibilityBatch при VisibilityTimeout=0 лишь инкрементировал
Retry и НЕ переносил сообщение в DLQ (логика была только в одиночной версии).
Вынесен единый метод Queue.ResetVisibilityAndTryDLQ — сброс видимости +
DLQ-перенос. Обе версии привели к одному пути.
2. SendMessageBatch: проверка OOM-лимита была только по устаревшему снимку
(RLock → Lock) — добавлена повторная проверка под per-queue Lock (TOCTOU).
3. Инварианты InFlightIndexes/ensureIndexes зафиксированы в док-комментариях.
Тест: TestChangeVisibilityBatch_MovesToDLQ (падает на старой логике, зелёный
на новой).
Возврат до defer queue.UnlockMu() оставлял блокировку захваченной навсегда —
очередь «зависала»: все последующие Lock/RLock ждали вечно. Проявлялось при
LiveCount()==0 (все сообщения удалены/tombstone). Найдено стресс-тестом
(2000 сообщений × циклы): стек горутин показал массовое ожидание RLockMu.
- receive: defer до проверки пустоты.
- регрессионный тест TestReceiveEmptyQueue_DoesNotLeakLock (падает на дефекте,
проходит на фиксе).
- models.Queue: deadIdx/receiptIdx/uuidIdx/liveCount/deadCount + примитивы
(AppendMessage/RemoveIndex/Compact/CompactIfNeeded/Reset/EnsureIndexes/
IndexOfReceipt/IndexOfUUID/MarkInFlight/ClearInFlight/InFlightIndexes/LiveCount).
Удаление — tombstone за O(1), физическая чистка амортизированная.
- DeleteMessage/ChangeMessageVisibility(±Batch): поиск по индексу вместо скана
среза; удаление без сдвига остатка.
- DeleteMessageBatch: tombstone вместо полной пересборки среза.
- processQueueTick: visibility/DLQ по индексу in-flight (O(in-flight)),
retention — tombstone, в конце — CompactIfNeeded.
- send-пути: AppendMessage + LiveCount (OOM-лимит по живым).
- admin/metrics/persistence: учёт живых сообщений, индексы после загрузки из Redis.
- Redis-схема не менялась (сообщения и так в per-message HASH).