# Thinking Log — 2026-04-12 # Agent: GitHub Copilot (GPT-5.4) --- ## Задача: Довести расследование проблемы 64KB+ payload до окончательного технического вывода ### Контекст На момент начала этой сессии уже было подтверждено следующее: - сервер shared-sqs после перехода на Redis schema v2 и per-message persistence работает быстро на малых и средних сообщениях; - проблема проявляется именно на payload около 64KB и выше; - через port-forward тот же запрос проходит быстро, значит Go-сервис и Redis не являются первичным узким местом; - через ingress проблема воспроизводится у boto3 и AWS CLI, но не воспроизводится у curl, http.client и низкоуровневого urllib3. Главный незакрытый вопрос был таким: это баг нашего сервиса или поведение платформенного ingress controller штурвала? ### Рабочая гипотеза в начале сессии Если объект ingress у shared-sqs настроен корректно, а итоговый nginx.conf внутри ingress controller не отражает часть аннотаций, то причина находится в платформенном ingress controller, а не в приложении. ### Почему выбрал именно эту гипотезу Потому что она была самой дешёвой для проверки и лучше всего объясняла противоречие: - в YAML ingress аннотация `nginx.ingress.kubernetes.io/proxy-request-buffering: "off"` есть; - в фактическом nginx.conf для host `qu.kube5s.ru` всё равно остаётся `proxy_request_buffering on;`. Если это подтверждается, дальнейший поиск в коде shared-sqs теряет смысл. --- ## Ход расследования ### 1. Проверка, чей это ingress вообще Сначала была цель не гадать, а проверить ownership в кластере. Что было подтверждено: - ingress для `qu.kube5s.ru` обслуживается IngressClass `nginx`; - этот класс ведёт на deployment `shturval-ingress-controller-controller`; - контроллер живёт в namespace `ingress`; - используется образ `r.shturval.tech/ingress-nginx/controller:v1.12.6`; - это не ingress, встроенный в shared-sqs, а платформенный ingress controller кластера. Вывод: проблема находится в общей ingress-инфраструктуре штурвала. ### 2. Проверка, нет ли конфликта нескольких ingress-ресурсов Следующая гипотеза была локальная и простая: возможно, для одного host существует несколько Ingress-объектов, и location-блок в nginx собирается из другого ресурса, не из того YAML, который мы смотрим. Проверка показала: - в кластере для host `qu.kube5s.ru` существует только один ingress: `shared-sqs/shared-sqs-ingress`. Вывод: это не конфликт нескольких ingress-объектов на один host. ### 3. Сверка объекта ingress с фактическим nginx.conf Дальше был ключевой шаг: сравнить декларацию и факт. В самом ingress-объекте у shared-sqs присутствуют: - `nginx.ingress.kubernetes.io/proxy-body-size: "10m"` - `nginx.ingress.kubernetes.io/client-body-buffer-size: "512k"` - `nginx.ingress.kubernetes.io/proxy-request-buffering: "off"` - `nginx.ingress.kubernetes.io/server-snippet: client_body_timeout 120s;` - proxy timeouts. В сгенерированном nginx.conf для `qu.kube5s.ru` было найдено: - `client_max_body_size 10m;` - `client_body_buffer_size 512k;` - `proxy_send_timeout 30s;` - `proxy_read_timeout 30s;` - `proxy_buffering off;` - `proxy_request_buffering on;` Это важнейшая развилка расследования. Что это означает: - ingress controller видит ingress-ресурс; - часть аннотаций применяет корректно; - но конкретно `proxy-request-buffering` не доходит до итоговой конфигурации; - следовательно проблема не в том, что ingress целиком игнорируется; - проблема в selective handling конкретных директив/аннотаций. ### 4. Проверка версии и документации ingress-nginx Дальше нужно было отсечь ещё одну ложную ветку: а вдруг upstream ingress-nginx вообще не поддерживает `proxy-request-buffering` в нашей версии? Проверка документации и исходников upstream ingress-nginx показала: - аннотация `nginx.ingress.kubernetes.io/proxy-request-buffering` официально поддерживается; - в коде есть parser для этого поля; - в upstream есть e2e-тест на сценарий `should turn off proxy-request-buffering`; - в шаблоне nginx используется переменная `location.Proxy.RequestBuffering`. Вывод: upstream ingress-nginx такую аннотацию умеет. Значит поведение штурвала отличается не потому, что аннотация “не существует”, а потому что либо: - в платформенной сборке/конфигурации происходит баг; - либо значение не прокидывается на этапе построения location model; - либо контроллер живёт в состоянии, где дефолт `on` побеждает annotation override. ### 5. Проверка самого шаблона в pod ingress controller Чтобы не строить догадки про кастомный шаблон, была проверка прямо внутри pod. Что найдено: - в `/etc/nginx/template/nginx.tmpl` директива не захардкожена; - там стоит шаблонная подстановка `{{ $location.Proxy.RequestBuffering }}`; - в итоговом `/etc/nginx/nginx.conf` для конкретного host всё равно стоит `on`. Это сузило диагноз ещё сильнее: - шаблон не виноват; - parser и upstream поддержка есть; - значит проблема в данных, которыми шаблон кормят, либо в платформенном runtime поведении контроллера. Именно здесь стало окончательно понятно, что дальше копать shared-sqs бессмысленно. ### 6. Почему `server-snippet` тоже не дал ожидаемого эффекта Параллельно был вопрос: если `proxy-request-buffering` не работает, можно ли продавить workaround через snippet. Проверка документации ingress-nginx показала: - snippet-аннотации по умолчанию контролируются флагом `allow-snippet-annotations`; - его дефолтное значение — `false`. В ConfigMap ingress controller у штурвала этот флаг не был включён. Вывод: - рассчитывать на `server-snippet` и `configuration-snippet` без изменения platform ConfigMap нельзя; - даже если ingress-объект принимает такую аннотацию, итоговая конфигурация может её не внедрить по политике безопасности. ### 7. Наблюдение про ConfigMap drift Ещё один важный операционный вывод дал повторный просмотр ConfigMap ingress controller. Ранее вручную поднимался `client-body-timeout`, но позже в ConfigMap снова был виден `client-body-timeout: "10"`. Это сильный индикатор того, что: - ручные правки штурвального ingress controller могут откатываться; - platform layer, вероятно, управляется Helm/GitOps/reconcile-процессом; - даже если бы ручной patch помог, он мог бы быть временным. Вывод: править такие настройки нужно не как разовую операцию в живом кластере, а в источнике правды платформы. --- ## Итоговые технические выводы ### Что подтверждено надёжно 1. **Go-сервис shared-sqs не является первичной причиной зависания 64KB+ сообщений.** Это доказано быстрым прохождением запросов через port-forward. 2. **Проблемный слой находится на ingress path.** Конкретно — в поведении платформенного ingress controller штурвала. 3. **Ingress YAML приложения сам по себе не является ошибочным.** Нужные аннотации на объекте есть. 4. **Контроллер применяет аннотации выборочно.** `proxy-body-size` и `client-body-buffer-size` доходят до nginx.conf, а `proxy-request-buffering` — нет. 5. **Upstream ingress-nginx поддерживает `proxy-request-buffering`.** Следовательно, это не “неподдерживаемая фича”, а platform-specific проблема/баг/ограничение. 6. **Snippet-аннотации в текущем штурвальном контроллере по факту недоступны как безопасный пользовательский workaround** без отдельного platform-level разрешения. 7. **Ручные правки ConfigMap контроллера выглядят нестабильными и могут откатываться.** ### Что больше НЕ считаю разумным делать 1. Продолжать искать root cause в Go-коде shared-sqs. 2. Тратить время на новые попытки “починить” только ingress приложения без изменения platform controller. 3. Внедрять большой рефакторинг сервиса ради проблемы, лежащей за пределами сервиса. 4. Форсить hard-limit в коде без острой продуктовой необходимости. ### Финальное продуктово-техническое решение После всех проверок наиболее прагматичный вывод такой: - протокольный лимит SQS остаётся 256KB; - **в коде shared-sqs hard-limit 32KB не вводим**; - **операционно считаем 32KB безопасным практическим размером** для клиентов AWS CLI/botocore в текущей инфраструктуре; - проблему 64KB+ классифицируем как ограничение платформенного ingress path, а не баг shared-sqs business logic. --- ## Мысли и оценка инженерного качества решения ### Почему не стоит вводить hard-limit 32KB в коде Изначально идея казалась хорошей: жёстко ограничить размер сообщения и снять проблему. Но по мере расследования стало ясно, что это слишком грубое лечение чужой инфраструктурной болезни. Если мы режем размер в коде, мы: - маскируем platform issue под якобы ограничение сервиса; - вводим продуктовое ограничение, которого нет в протоколе SQS; - создаём технический долг: потом придётся объяснять, почему сервис “совместим с SQS”, но режет на 32KB. То есть hard-limit удобен как короткий workaround, но архитектурно это неправильное место для фикса. ### Почему 32KB всё-таки остаётся хорошей практической рекомендацией Потому что 32KB: - заметно ниже порога деградации; - стабильно проходит через AWS CLI/botocore в текущем ingress path; - покрывает подавляющее большинство типовых сообщений SQS; - даёт пользователю рабочее эксплуатационное правило без вранья про реальные причины. ### Почему идея “переписать сервис с нуля” не выглядит рациональной Этот вопрос возник естественно на фоне раздражения из-за 64KB+ проблемы. Но расследование показало обратное: - core shared-sqs работает хорошо; - сервер не является бутылочным горлышком в текущем кейсе; - переписывание сервиса не уберёт поведение ingress controller штурвала; - значит ROI у полного переписывания низкий. Гораздо разумнее развивать текущий код и отдельно эскалировать platform ingress issue. --- ## Что считать окончательным статусом инцидента ### Статус **Исследование завершено на уровне, достаточном для инженерного решения.** ### Причина остановки дальнейшего копания Не потому, что “не нашли”, а потому что нашли достаточно: - место проблемы локализовано; - границы ответственности определены; - прикладное решение выбрано; - дальнейшее время будет тратиться уже с плохим ROI. ### Если когда-нибудь возвращаться к теме Возвращаться стоит только в двух случаях: - если появится доступ к source-of-truth штурвального ingress controller; - если 64KB+ payload станет реально важным use-case для пользователей. Иначе правильнее оставить это как известное platform limitation. --- ## Короткий финальный вывод одним абзацем Проблема 64KB+ payload у shared-sqs оказалась не багом Go-сервиса и не проблемой Redis/persistence, а ограничением ingress path в кластере штурвала: объект ingress у приложения настроен корректно, часть аннотаций применяется, но именно `proxy-request-buffering` platform controller в итоговый nginx.conf не прокидывает, при этом upstream ingress-nginx такую аннотацию поддерживает. Поэтому вводить hard-limit 32KB в коде я считаю неправильным; правильное практическое решение на текущий момент — оставить сервис без искусственного code-level ограничения, а 32KB считать безопасным эксплуатационным размером и документировать это как известную особенность инфраструктуры.