SUPER_OLEG_DEV Telegram 225
Привет!

Достаточно давно делился статьей где описывал различные механизмы и подходы которые мы применяем для SSR приложений на Tramvai (сейчас доступна на хабре).

Один из механизмов - Request Limiter, модуль который ограничивает количество параллельно обрабатываемых запросов при перегруженном Event Loop приложения, для возможности стабильно отдавать 2xx ответы и рендерить странички даже под большими нагрузками.

Работает по похожим принципам с https://github.com/fastify/under-pressure, только не отбрасывает все запросы при нагрузке, а держит еще LIFO очередь что бы обеспечить большее количество успешных ответов без сильной деградации времени ответа.

Когда переводили наши интеграционные тесты на 20 версию Node.js, начали падать нагрузочные тесты Request Limiter. Основная проблема - перестали быстро отвечать health-чеки приложения (а отзывчивые health-чеки и метрики очень важны и что бы в реальном времени понимать что происходить и для graceful рестартов и так далее)

Долго исследовал проблему, опытным путем обнаружил что проблема где-то на уровне http модуля ноды, запросы как будто в какой-то момент скапливаются в очередь и не обрабатываются далее.

Завел issue, получил много интересного и ценного фидбека от Matteo Collina - https://github.com/nodejs/node/issues/57364

Оказалось, что изменения в libuv убрали запуск таймеров в начале цикла на старте event loop, теперь они запускаются только в конце - то есть после poll (входящие запросы и прочее) и check (setImmediate).

В веб-сервере под капотом Tramvai мы испольуем setImmediate для того что бы иметь возможность "разорвать" event loop между тяжелыми задачами по SSR и ответить на легкие запросы /metrics, /health и так далее.

У libuv есть логика по ограничению одновременно обрабатываемых immediate коллбэков - https://github.com/libuv/libuv/blob/49b6e4db0cfc2bdb4c4151030618981c2fc0795b/src/unix/core.c#L462-L465

http модуль внутри использует различные таймеры/интервалы, до обновления ноды все это вместе с request limiter работало так скажем в гармонии, логика по обработке запроса (в том числе таймеры) выполнялась на одной фазе event loop до immediate коллбэков.

После обновления libuv в Node.js, если я все правильно понял, логика обработки запроса теперь раскидана до и после check фазы с immediate коллбэками, и происходит что-то вроде взаимной блокировки - мы не можем полноценно обрабатывать новые запросы (в том числе быстро ответить 429 кодом) пока в очереди есть много immediate коллбэков.

Также Маттео накинул несколько кейсов почему в целом опасно использовать setImmediate для дробления обработки запросов и что это просаживает перф - https://github.com/fastify/fastify/pull/545

Проблем тут вижу несколько:
- в нашем кейсе, SSR это не тысячи а десятки RPS как в бенчмарках fastify/h3, и такие проблемы нам не важны
- но при этом у нас была реальная и очень полезная возможность оставаться отзывчивыми, и держать под нагрузкой адекватные 2xx RPS
- также я не вижу интеграционных тестов в репозитории under-pressure и сомневаюсь до конца что инструмент работает ожидаемо

Пару дней назад Маттео написал даже в блок Platformatic (это их коммерческий продукт для Node.js стека) про кейс, итого получился подробный обзор проблемы (правда черезчур нейросетевой):
- https://x.com/matteocollina/status/1951322487595090177?s=19
- https://blog.platformatic.dev/the-dangers-of-setimmediate

Основной поинт Маттео который я полностью поддерживаю - надо использовать worker_threads, и само приложение поднимать в воркере, таким образом изолировать его event loop (тут он рекламирует их сервер Watt который так делает из коробки)

Для Tramvai тут проблема что это сильно не вписывается в текущую архитектуру.

Придется делать еще один заход и смотреть как мы можем избавиться от setImmediate, и что в итоге выжать из Request Limiter под нагрузками на свежих версиях Node.js
👍14🔥123



tgoop.com/super_oleg_dev/225
Create:
Last Update:

Привет!

Достаточно давно делился статьей где описывал различные механизмы и подходы которые мы применяем для SSR приложений на Tramvai (сейчас доступна на хабре).

Один из механизмов - Request Limiter, модуль который ограничивает количество параллельно обрабатываемых запросов при перегруженном Event Loop приложения, для возможности стабильно отдавать 2xx ответы и рендерить странички даже под большими нагрузками.

Работает по похожим принципам с https://github.com/fastify/under-pressure, только не отбрасывает все запросы при нагрузке, а держит еще LIFO очередь что бы обеспечить большее количество успешных ответов без сильной деградации времени ответа.

Когда переводили наши интеграционные тесты на 20 версию Node.js, начали падать нагрузочные тесты Request Limiter. Основная проблема - перестали быстро отвечать health-чеки приложения (а отзывчивые health-чеки и метрики очень важны и что бы в реальном времени понимать что происходить и для graceful рестартов и так далее)

Долго исследовал проблему, опытным путем обнаружил что проблема где-то на уровне http модуля ноды, запросы как будто в какой-то момент скапливаются в очередь и не обрабатываются далее.

Завел issue, получил много интересного и ценного фидбека от Matteo Collina - https://github.com/nodejs/node/issues/57364

Оказалось, что изменения в libuv убрали запуск таймеров в начале цикла на старте event loop, теперь они запускаются только в конце - то есть после poll (входящие запросы и прочее) и check (setImmediate).

В веб-сервере под капотом Tramvai мы испольуем setImmediate для того что бы иметь возможность "разорвать" event loop между тяжелыми задачами по SSR и ответить на легкие запросы /metrics, /health и так далее.

У libuv есть логика по ограничению одновременно обрабатываемых immediate коллбэков - https://github.com/libuv/libuv/blob/49b6e4db0cfc2bdb4c4151030618981c2fc0795b/src/unix/core.c#L462-L465

http модуль внутри использует различные таймеры/интервалы, до обновления ноды все это вместе с request limiter работало так скажем в гармонии, логика по обработке запроса (в том числе таймеры) выполнялась на одной фазе event loop до immediate коллбэков.

После обновления libuv в Node.js, если я все правильно понял, логика обработки запроса теперь раскидана до и после check фазы с immediate коллбэками, и происходит что-то вроде взаимной блокировки - мы не можем полноценно обрабатывать новые запросы (в том числе быстро ответить 429 кодом) пока в очереди есть много immediate коллбэков.

Также Маттео накинул несколько кейсов почему в целом опасно использовать setImmediate для дробления обработки запросов и что это просаживает перф - https://github.com/fastify/fastify/pull/545

Проблем тут вижу несколько:
- в нашем кейсе, SSR это не тысячи а десятки RPS как в бенчмарках fastify/h3, и такие проблемы нам не важны
- но при этом у нас была реальная и очень полезная возможность оставаться отзывчивыми, и держать под нагрузкой адекватные 2xx RPS
- также я не вижу интеграционных тестов в репозитории under-pressure и сомневаюсь до конца что инструмент работает ожидаемо

Пару дней назад Маттео написал даже в блок Platformatic (это их коммерческий продукт для Node.js стека) про кейс, итого получился подробный обзор проблемы (правда черезчур нейросетевой):
- https://x.com/matteocollina/status/1951322487595090177?s=19
- https://blog.platformatic.dev/the-dangers-of-setimmediate

Основной поинт Маттео который я полностью поддерживаю - надо использовать worker_threads, и само приложение поднимать в воркере, таким образом изолировать его event loop (тут он рекламирует их сервер Watt который так делает из коробки)

Для Tramvai тут проблема что это сильно не вписывается в текущую архитектуру.

Придется делать еще один заход и смотреть как мы можем избавиться от setImmediate, и что в итоге выжать из Request Limiter под нагрузками на свежих версиях Node.js

BY SuperOleg dev notes




Share with your friend now:
tgoop.com/super_oleg_dev/225

View MORE
Open in Telegram


Telegram News

Date: |

As the broader market downturn continues, yelling online has become the crypto trader’s latest coping mechanism after the rise of Goblintown Ethereum NFTs at the end of May and beginning of June, where holders made incoherent groaning sounds and role-played as urine-loving goblin creatures in late-night Twitter Spaces. Select: Settings – Manage Channel – Administrators – Add administrator. From your list of subscribers, select the correct user. A new window will appear on the screen. Check the rights you’re willing to give to your administrator. Add up to 50 administrators Each account can create up to 10 public channels So far, more than a dozen different members have contributed to the group, posting voice notes of themselves screaming, yelling, groaning, and wailing in various pitches and rhythms.
from us


Telegram SuperOleg dev notes
FROM American