Commit Graph
141 Commits
Author SHA1 Message Date
Repinoid 7a2a7e1610 doc: мануал — уточнена ссылка на tenantQueueKey 2026-10-08 17:33:41 +03:00
Repinoid 3b0815155a doc: мануал для нового чата — состояние, решения, план шагов (ВМ -> разбиение -> хранилище), команды, грабли, карта кода 2026-10-08 17:33:03 +03:00
Repinoid 39b162439f doc: архитектурный анализ — публичный SQS, обход через ВМ, модульный монолит (data-plane/control-plane)
- SQS должен быть публичным (как AWS SQS/YMQ) -> внешний доступ обязателен;
- стратегия сети: сейчас публичная точка на ВМ (обход managed-шлюза), позже —
  девопсам чинить шлюз с доказательствами;
- 'без монолитов' уточнено: модульный монолит, критерии разбиения, data-plane vs
  control-plane, billing/metrics/UI — модули;
- ограничение: in-memory stateful-ядро = одиночка, вынос хранилища важнее;
- IoT: внешний MQTT-канал неизбежен, крупные тела мимо шлюза;
- открытые вопросы.
2026-10-08 17:09:21 +03:00
Repinoid b99905469d doc(HANDOFF): тикет отменён — болезнь платформы, решение через обход (ссылки на разборы) 2026-10-08 16:51:58 +03:00
Repinoid d8bdea75f3 doc: разбор применимости обхода drhider к SQS (прокси на ВМ, pull вместо push)
- почему схему 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) и открытые вопросы.
2026-10-08 16:51:45 +03:00
Repinoid a1d72b61d4 doc: находки — как та же сетевая болезнь платформы обойдена в drhider (буфер на ВМ, pull вместо push)
Собраны и проверены по источникам все факты из ~/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;
- вывод: тикет в поддержку — не решение, нужен архитектурный обход.
2026-10-08 16:50:36 +03:00
Repinoid b947f1d467 doc: журнал — полный код-ревью сервиса и фиксы замечаний (2026-10-08) 2026-10-08 15:19:36 +03:00
Repinoid 572c617f44 fix(review): TOCTOU лимита тенантов + персист NubesSub + пакетный http.Client
1. TenantStore.Create: повторная проверка глобального лимита тенантов под Lock
   (ранняя была под RLock — гонка могла превысить MaxTenantsGlobal).
2. CreateFromJWT: обновлённый NubesSub теперь персистится в Redis (раньше
   сохранялся только внутри ветки смены MaxQueues и терялся при рестарте);
   запись в Redis только при реальном изменении.
3. auth.PingNubesAPI: общий http.Client (keep-alive) вместо нового на каждый
   вызов.
2026-10-08 15:19:15 +03:00
Repinoid 33a0adc9be refactor(P1.2-revue): Compact — один RebuildIndexes + ensureIndexes; журнал ревью-фиксов
Пункт 4 ревью: убран дублирующий блок перестройки индексов в Compact
(ensureIndexes на входе + один RebuildIndexes на выходе). Поведение то же,
логика чище. Дополнен HISTORY разделом о фиксах код-ревью.
2026-10-07 08:44:02 +03:00
Repinoid 2e1c594892 fix(P1.2-revue): батч-видимость не заводила в DLQ + TOCTOU лимит в SendMessageBatch
1. ChangeMessageVisibilityBatch при VisibilityTimeout=0 лишь инкрементировал
   Retry и НЕ переносил сообщение в DLQ (логика была только в одиночной версии).
   Вынесен единый метод Queue.ResetVisibilityAndTryDLQ — сброс видимости +
   DLQ-перенос. Обе версии привели к одному пути.
2. SendMessageBatch: проверка OOM-лимита была только по устаревшему снимку
   (RLock → Lock) — добавлена повторная проверка под per-queue Lock (TOCTOU).
3. Инварианты InFlightIndexes/ensureIndexes зафиксированы в док-комментариях.

Тест: TestChangeVisibilityBatch_MovesToDLQ (падает на старой логике, зелёный
на новой).
2026-10-07 08:35:52 +03:00
Repinoid f96649b14e doc: P1.2 сделан — результаты, найденный дефект (утечка per-queue блокировки) и прогоны тестов 2026-10-07 08:27:10 +03:00
Repinoid 529d208576 fix(P1.2): утечка per-queue блокировки при ReceiveMessage на пустой очереди
Возврат до defer queue.UnlockMu() оставлял блокировку захваченной навсегда —
очередь «зависала»: все последующие Lock/RLock ждали вечно. Проявлялось при
LiveCount()==0 (все сообщения удалены/tombstone). Найдено стресс-тестом
(2000 сообщений × циклы): стек горутин показал массовое ожидание RLockMu.

- receive: defer до проверки пустоты.
- регрессионный тест TestReceiveEmptyQueue_DoesNotLeakLock (падает на дефекте,
  проходит на фиксе).
2026-10-07 08:26:34 +03:00
Repinoid ad14fd2f7e test(P1.2): примитивы индексации — tombstone O(1), компакция, receipt/uuid-индексы, Reset 2026-10-07 08:06:07 +03:00
Repinoid c5b2c11f5d P1.2: индексация сообщений — tombstone-удаление O(1), receipt/uuid-индексы, tick по in-flight
- 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).
2026-10-07 08:01:49 +03:00
Repinoid e735563a04 doc(P1.2): дизайн индексации сообщений (tombstone + индексы + амортизированная компакция) 2026-10-07 07:56:56 +03:00
Repinoid 31840b9756 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 зелёный.
2026-10-07 07:18:31 +03:00
Repinoid d53aadecfa doc: P1.2 отложен с обоснованием (после P1.1 O(N) ограничен одной очередью) 2026-10-06 18:59:57 +03:00
Repinoid 8988293628 P2.4: единый путь создания очереди/сообщения (admin переиспользует gosqs)
Добавлены gosqs.BuildQueueForTenant (URL/ARN/дефолты/FIFO-детект как в CreateQueueV1)
и gosqs.BuildSqsMessage (UUID+MD5). admin.createTenantQueue и sendMessageToQueue
переведены на них: раньше UI создавал очередь без URL/ARN/IsFIFO и сообщение без MD5,
теперь поведение UI == SQS API + проверка размера при отправке.
2026-10-06 18:59:36 +03:00
Repinoid fd8ffc329d P1.1: per-queue блокировки вместо глобального лока на операции с очередью
Все операции с содержимым очереди (send/receive/delete/visibility/purge/attributes/
tags/admin/gauge/tick) переведены на per-queue RWMutex (QueueStore.GetLocked/
GetRLocked + queue.LockMu). Глобальный SyncQueues-лок остался только для map и
неизменяемых полей. DLQ-переносы берут лок DLQ отдельно (порядок main→DLQ).
marshalQueueMeta больше не мутирует очередь (DTO-снапшот). Устранён дедлок
create_queue (setQueueAttributesV1 вызывается вне глобального лока). Добавлены
конкурентные тесты; go test -race зелёный. Снимает боттлнек: синхронный Redis-I/O
больше не держит глобальный лок.
2026-10-06 18:58:16 +03:00
Repinoid ed17c6cbd0 P1.1 groundwork: per-queue mutex в Queue + анализ блокеров
Добавлены поле mu (не сериализуется) и LockMu/RLockMu-аксессоры в models.Queue;
исправлено копирование Queue by value (numberOfHiddenMessagesInQueue).
Глобальный лок пока используется везде — поведение не менялось.
Задокументированы блокеры полного перехода (DLQ-указатель, мутирующий
marshalQueueMeta, релинк DLQ после загрузки, атомарность перевода).
2026-10-06 18:39:26 +03:00
Repinoid 3a52960d5b P1.4: tenant-индекс очередей (O(1) подсчёт/список вместо скана)
Введён тип QueueStore с индексом accessKey→{queueName}. Мутации очередей идут через
AddQueue/RemoveQueue (create/delete/restore/config/seed/admin), подсчёт и список —
через CountTenantQueues/TenantQueueNames. Устранены O(total) скан-по-префиксу в
countTenantQueues, deleteTenant, listTenantQueues, createTenantQueue. API SyncQueues
(Queues, Lock) сохранён — остальной код не затронут.
2026-10-06 18:36:03 +03:00
Repinoid fb833ef01c P2.5: биллинг — воркер+буфер вместо горутины на операцию; корректный msg_count
RecordUsage теперь кладёт запись в буферизованный канал (1024), один воркер пишет
в PG — убран навал горутин, ждущих коннект пула (было 5). При переполнении буфера
запись дропается с логом (биллинг жертвуем ради стабильности). Для batch-операций
(SendMessageBatch/DeleteMessageBatch/ChangeMessageVisibilityBatch) msg_count теперь
реальное число записей, а не всегда 1.
2026-10-06 18:19:17 +03:00
Repinoid 07eace3c6b P2.8: Redis reconnect/health-check
Connect сохраняет параметры подключения и запускает фоновую проверку (ping каждые
30с). При обрыве Redis-клиент оставался не-nil и записи молча падали в лог; теперь
соединение восстанавливается автоматически через reconnect().
2026-10-06 18:18:06 +03:00
Repinoid 8c39bc5346 P2.1: BYOC Admin API — произвольные креды при создании тенанта
POST /admin/tenants теперь принимает опциональные access_key/secret_key. Если
задано любое из полей — оба обязательны (мин. длина 8/16). С произвольными
кредами tenantID генерируется случайно (CreateFixedAutoID). Без кредов —
поведение прежнее (store.Create).
2026-10-06 18:17:24 +03:00
Repinoid b7f79b388f P2.6: закрыть /metrics Bearer admin-токеном
/metrics отдаёт per-tenant данные (accessKey, число очередей и сообщений), что
является утечкой наружу на публичном endpoint. Теперь /metrics защищён тем же
Bearer admin-токеном (timing-safe сравнение). Скрепперу VM токен задаётся в конфиге.
2026-10-06 18:16:27 +03:00
Repinoid 2aa9a656a7 P2.7: убрать SNS-следы (Topics/Subscriptions) из goaws.yaml
Модели Environment уже не содержат Topics/SnsPort (форк вычистил SNS), но yaml
хранил легаси Topics и SNS-комментарии. Оставлен только SQS-конфиг.
2026-10-06 18:15:28 +03:00
Repinoid 876971eea8 P2.3: timing-safe сравнение admin bearer-токена
bearerAuthMiddleware теперь сравнивает Authorization через subtle.ConstantTimeCompare
(с предварительной проверкой длины), а не через != — устраняет timing side-channel
при подборе admin-токена.
2026-10-06 18:14:27 +03:00
Repinoid 424ec32a6a P1.5: убрать демо-бэкдор (публичный bypass-токен UI)
Удалены authenticateUIDemoToken, uiDemoToken/uiDemoTenantID/uiDemoEmail и их
константы (demo-ui-shared-sqs-ngcloud-2026) — публичный путь входа в UI мимо
JWT-валидации. seed.go теперь создаёт demo-тенанта со СЛУЧАЙНЫМИ кредами
(tenant.Create), без захардкоженного SecretKey в исходнике. Тест
TestJWTAuth_AllowsDemoToken заменён на TestJWTAuth_RejectsMissingToken.
2026-10-06 18:13:40 +03:00
Repinoid db41878951 P1.3: реализован MessageRetentionPeriod (эвикция просроченных сообщений)
processQueueTick теперь эвиктит сообщения старше retention (SentTime + retention < now):
удаляет их из очереди и из Redis (DeleteMessagesPersist), разблокируя FIFO-группы.
Retention == 0 означает отсутствие истечения. PeriodicTasks итерируется по (key, queue),
чтобы передать queueKey для удаления из Redis. Устраняет забивание очередей до OverLimit.
2026-10-06 18:10:57 +03:00
Repinoid 5642b893a8 chore: удалить мёртвые унаследованные тесты gosqs (несовместимы с мультитенантным форком)
13 файлов *_test.go требовали отсутствующие в git пакеты app/fixtures и
app/test, и даже с ними падали бы: написаны под дофорковый GoAws, где не было
тенанта в контексте и tenant-scoped ключей {accessKey}:{queueName}.
Причина зафиксирована в doc/thinking/tests-inherited-broken-2026-10-06.md.
2026-10-06 18:09:29 +03:00
Repinoid ac06572e25 doc: находка — унаследованные Go-тесты gosqs мёртвые (несовместимы с форком)
Тесты требуют тенанта из контекста и tenant-scoped ключей, а написаны под
дофорковый GoAws. Восстановление app/fixtures + app/test сделает их
компилируемыми, но падающими. Нужны новые тесты под мультитенантное поведение.
2026-10-06 18:07:31 +03:00
Repinoid 12b25e393d P0.2: JWT принимается только при явном 2xx от Nubes API
Раньше любой статус, кроме 401/403 (включая 5xx/429/редиректы), считался
подтверждением токена. Теперь валидность подтверждается только явным 2xx;
401/403 — отклонён, прочие статусы — ошибка стенда (пробуем следующий).
2026-10-06 18:04:42 +03:00
Repinoid c19a056de3 P0.3: синхронный персист SendMessage до ответа 200
Добавлен persistence.SaveMessageSync (синхронный HSet, возвращает ошибку).
SendMessage теперь пишет сообщение в Redis ДО возврата клиенту; при ошибке Redis
сообщение откатывается из памяти и возвращается InternalError (новая ошибка в
models/errors.go). Устраняет окно потери подтверждённого сообщения при краше пода
между 200 OK и асинхронной записью.
2026-10-06 18:04:22 +03:00
Repinoid 928a3fb85d P0.1: случайные креды тенанта — убран детерминированный вывод из email
CreateFromJWT больше не выводит ID/AccessKey/SecretKey из email через SHA256
с захардкоженной солью (это позволяло вычислить чужие креды по публичному email).
Креды генерируются случайно через crypto/rand. Удалены tenantIDFromEmail,
accessKeyFromEmail, secretKeyFromEmail, secretKeySalt и ветка legacy-пересчёта.
Идемпотентность по email сохранена (повторный логин возвращает тот же тенант).
2026-10-06 18:03:40 +03:00
Repinoid 6e96a4d007 doc(sqs): подробный план доработок P0/P1/P2 для DeepSeek Flash 2026-10-05 21:38:03 +03:00
Repinoid 9528441c97 doc(sqs): мой критический анализ отчёта Opus (перепроверка по коду) 2026-10-05 21:38:03 +03:00
Repinoid e8ccd8bae1 doc(sqs): промпт для Opus (allowlist файлов) + его отчёт КРАКО и план 2026-10-05 21:38:03 +03:00
“Naeel” 60b15fdb1c doc: HANDOFF-NEW-CHAT.md — резюме сессии для перехода в новый чат 2026-08-16 07:55:43 +04:00
“Naeel” 7897fda9e7 doc: тикет — дополнение: таймера 31с внутри кластера нет, он на внешнем шлюзе 2026-08-15 21:28:55 +04:00
“Naeel” 96f6d64184 doc: черновик тикета в Nubes (явления A/B, 6 доказательств); все фазы плана выполнены 2026-08-15 21:17:17 +04:00
“Naeel” 6d451c33d1 HISTORY: период таймаутов 32.9с (18 интервалов 32.7-33.0с) — таймер доказан; полная проверка скриптов и кредов OK; +tcp_mtu_probe.py 2026-08-15 20:38:01 +04:00
“Naeel” 5c2180d40e doc: финал плана (решения зафиксированы) + сравнение четырёх агентов 2026-08-15 15:05:12 +04:00
“Naeel” cd7f5a7d5a doc: план v4 от Опуса 4.8 + ответы на уточнения (нет второго ISP, NET_RAW вряд ли, echo отложить) 2026-08-15 15:03:33 +04:00
“Naeel” 5f6c0a5bf7 doc: план v3 от Соннета — разделение явлений A/B, port-forward первым; принят за основу 2026-08-15 14:56:13 +04:00
“Naeel” b91f21a50d doc: план v2 от DeepSeek V4 Pro (новый чат) — стратегия изоляции слоёв; сравнение с Flash 2026-08-15 14:50:02 +04:00
“Naeel” bd63c04ea3 doc: план сетевых экспериментов от DeepSeek V4 Flash (не Соннет) с поправками — nubes-network-bug-plan 2026-08-15 14:41:53 +04:00
“Naeel” 7460f59432 tests: 30-мин сравнение с локали — p50 90мс vs 140мс, но 49 таймаутов у shared (5.5%), YMQ 1 ошибка 2026-08-15 13:01:11 +04:00
“Naeel” 9950271259 tests: сравнение shared-SQS vs YMQ — p50 6-8мс vs 60-62мс; у shared выброс send 12-25с на прогон (платформа?) 2026-08-15 12:10:11 +04:00
“Naeel” 904d34904c tests: fifo_dlq_probe — опрос DLQ до 10с вместо мгновенной проверки (гонка с тиком); финальная регрессия зелёная 2026-08-15 09:31:11 +04:00
“Naeel” 2d47e86425 HISTORY: полная регрессия на ВМ — api_test 22/22, sdk_test 15/15; флап fifo_dlq_probe разобран (гонка теста, сервис 5/5) 2026-08-15 09:25:32 +04:00