В 2022 году у нас был postback latency 280мс p95. В 2025 - 28мс. Этот пост про то, как мы дошли с одной цифры до другой, что попробовали по дороге и какие штуки не сработали.

Если коротко

Главные выигрыши дали три штуки: вынос обработки на edge (Cloudflare Workers + наши собственные ноды), переход с REST на батчинг постбэков через UDP, и переписывание hot path на Rust. Меньшие, но заметные выигрыши - kernel-bypass для логирования и убийство всех JSON-парсеров на пути.

Почему postback latency вообще важна

Постбэк - это HTTP-запрос, который партнёрка делает на наш сервер, когда у клиента случилась конверсия. Мы ловим этот запрос, мапим click_id обратно на исходный клик и записываем конверсию.

Если мы отвечаем медленно, происходят неприятные вещи. Партнёрки ставят таймауты (обычно 1-3 секунды), а внутри они складывают postback-и в очередь. На большой нагрузке - очереди вырастают, постбэки начинают теряться, статистика расходится. У одного клиента в 2022 это вылилось в расхождение 4% за месяц - около $12 тысяч в его кейсе.

Кроме того, медленный постбэк значит, что cap на оффере проверяется с задержкой. Если у тебя оффер с лимитом 100 конверсий в день, и постбэк думает 300мс - ты легко уйдёшь в overbid на 5-10%.

Откуда взялись 280мс

Профилирование показало вот такой путь запроса в 2022:

partner → DNS (15ms) → TLS handshake (40ms) → nginx (8ms)
       → Laravel/PHP (110ms) → MySQL (45ms) → ClickHouse (38ms)
       → response (15ms) = ~270ms p95

Половина бюджета съедалась на PHP-стек и MySQL-запросы. ClickHouse для атрибуции - тяжёлый, но единственный способ хранить миллиарды кликов с разумной стоимостью.

Первое, что попробовали - кешировать атрибуцию click_id → conversion_id в Redis. Это убрало половину MySQL-запросов и сняло около 30мс. Получили 240мс p95. Хорошо, но мало.

Шаг 1: вынос на edge

В начале 2023 мы стали обрабатывать постбэки прямо на edge-нодах - наших и Cloudflare Workers. Идея простая: вместо того чтобы тащить запрос через DNS, балансер, nginx, PHP-FPM, мы принимаем его в одном из 50 геораспределённых дата-центров и сразу пишем в локальный буфер.

Это убрало DNS-резолв (большая часть партнёрок резолвили нас каждые 60 секунд) и TLS-overhead (Cloudflare держит сессии на edge). Latency упала до 110мс p95.

Подводный камень: атрибуция click_id живёт в центральных нодах, не на edge. Мы решили это асинхронной репликацией: edge принимает постбэк, кладёт в локальный Redis с TTL 5 минут и отвечает 200 OK. В фоне отдельный воркер реплицирует это в центральную систему. Если центр недоступен - постбэк всё равно записан и не теряется.

Что НЕ сработало

Мы пробовали запускать PHP-обработчики прямо на edge через FrankenPHP. Идея красивая, но получили проблемы с локальными расширениями (mbstring оказался не везде идентичным) и латентность не упала. Откатили через месяц.

Шаг 2: батчинг через UDP

Когда мы переписали в 2024 партнёрский ингест на gRPC-streaming (для тех партнёров, кто умеет), latency p50 упала ниже 50мс. Для остальных - тех, кто шлёт классический GET-постбэк - мы добавили внутреннюю штуку: батчинг через UDP.

HTTP-запрос приходит, edge моментально отвечает 200 OK, потом кладёт payload в shared-memory очередь. Раз в 10мс отдельный процесс собирает всё накопленное в один UDP-пакет и пуляет на центральный сборщик.

Плюсы: client-side latency определяется только TLS handshake'ом и одним сетевым роунд-трипом. Минусы: ты должен быть готов к тому, что UDP-пакеты теряются (около 0.001% в нашем замере), поэтому каждый event имеет дедупликационный ключ, и центр умеет идемпотентно их обрабатывать.

После этой штуки p95 упала до 45мс.

Шаг 3: Rust на горячем пути

Hot path - это код, который выполняется на каждом запросе. В нашем случае: парсинг URL, поиск click_id, валидация подписи, постановка в очередь. До 2024 это была Go-программа. В 2025 переписали в Rust.

Что дало переписывание:

  • Отсутствие GC-пауз. Go давал 2-4мс паузы под нагрузкой. На p99 это было заметно.
  • Zero-copy парсинг. serde_urlencoded работает прямо на &[u8], без аллокаций.
  • Tokio + io_uring для async I/O. Это снизило overhead сетевого стека на 30-40%.

После переписывания p95 упала до 32мс, p99 - до 80мс. Финальная оптимизация - убийство JSON-парсера в логировании. Мы складывали логи в JSON, потом ELK их парсил. Перевели на бинарный формат (Cap'n Proto), парсинг ушёл, p95 упал до 28мс.

Цифры сегодня

По данным за апрель 2026, на трафике порядка 4 миллиардов кликов в месяц:

  • p50: 12мс
  • p95: 28мс
  • p99: 78мс
  • p99.9: 220мс (обычно хвост из CIS-трафика с плохими ISP)

Дальше двигаться сложно - мы упираемся в физику. Один сетевой round-trip Europe → US-East это 80-90мс RTT, и для пары крупных партнёрок мы уже не можем сделать быстрее, потому что у них нет US-эндпоинта. Думаем добавить proxy на нашей стороне в их DC.

Если хочешь повторить

Самое сильное соотношение усилий и выигрыша было от выноса на edge - там просто 100мс улетело за одну итерацию. Если ты сидишь на классической схеме nginx + бекенд - попробуй сначала это. Cloudflare Workers + Redis на edge стоят дёшево и быстро внедряются.

Rust имеет смысл, когда ты уже выжал всё из архитектуры и упираешься в милисекунды. Если у тебя сейчас latency 500мс, переписывание на Rust не поможет - проблема не в языке.