Какие есть подходы для стабилизации потребляемой памяти в го-приложении?
Как можно стабилизировать потребление памяти сервисами, чтобы они всегда потребляли плюс-минус одинаковое количество памяти?
Работаю в большой компании, есть свой кластер k8s.
У меня во владении не очень нагруженные микросервисы, в среднем он потребляет десятки мегабайт, но периодически приходят пользователи и нагрузка неконтролируемо растет, есть легкие запросы, а есть когда пользователь запрашивает кучу данных (с максимальным limit в интерфейсе) и если таких много - сервис падает по оом.
Что делать?
С одной стороны, кубоадмины не хотят выделять больше ресурсов - в среднем потребление мизерное, предлагают разбираться с потреблением памяти самостоятельно и грозятся, что скоро закрутят гайки, и нельзя будет поставить лимит отличным от реквеста.
Можно, конечно, пойти административным ресурсов и выделят столько, сколько попрошу.
Но вот все-равно, непонятно, сколько-то просить.
Может есть какие-то подходы для стабилизации потребления памяти?
Вообще непонятно, сколько ресурсов требовать, если нагрузка непостоянная. Сейчас оно работает за счет того, что лимиты выставлены в среднее мизерное потребление памяти 32-64 мб, но лимиты выкручены в гигабайт, что пока один сервис отвечает большим чанокм данных, другим вряд ли понадобится эта память. Но есть что-то в этом неправильное.
Смотрел потребление памяти, в целом, ничего подозрительного - приходит запрос, создаются стурктуры, следом приходит gc и все освобождает.
Но непонятно как в целом жить, когда request=limit при непостоянных нагрузках. Разве что начать переписывать на использование синкпула, а при исчерпании объектов в пуле просто отьбрасывать запросы, но ведь это как-то странно.
Смотреть на что выделяется такое количество памяти. Для того, что бы решить проблему с памятью, нужно понять причину и только после пытаться искать способы решения проблемы
Что бы потреблять меньше памяти надо потреблять меньше памяти.
Поскольку в вопросе никакой конкретики нет, то вот вам общие соображения:
Например если у вас запросили 1000 элементов, то не вычитывать из базы всю 1000, а прочитать 10, отправить часть ответа, прочитать следующие 10 и так далее.
Второй подход, ограничить одновременные «тяжёлые» запросы — если вы уже обрабатываете «тяжёлый» запрос, то второй такой запрос надо поставить в очередь и начать обрабатывать только после завершения первого.
Если нужно мутировать много строк, то не используйте строки, а работайте с байтами. Пакет bytes в помощь.
Используйте sync.Pool для уменьшения количества аллокаций.
Ограничивайте количество одновременных запросов. Тут могу посоветовать использовать семафор с весами. Идея в том, чтобы задать каждому запросу свой вес и при переполнении семафора остановить обработку новых запросов до тех пор, пока ресурс не освободится. Здесь главное ввести лимит на максимальное количество соединений, после которого можно и дропать запросы (сейчас у вас по сути то же самое происходит, но из-за падения сервиса, что намного хуже).
В самом крайнем случае можно попросить рантайм освободить занятую память: