awesome-everything EN
↑ Обратно к восхождению

Кеширование

XFetch: вероятностное раннее истечение без координации

Суть Алгоритм XFetch Vattani et al. обновляет кеш до истечения TTL с помощью вероятностного правила решения — без локов, без координации, в среднем ровно один ранний refresh на TTL-окно независимо от размера флота.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 12 min

Lock-based митигация добавляет Redis round-trip к каждому cache miss — нормально для ультра-горячих ключей, расточительно для 99% ключей, которые редко сталкиваются. XFetch достигает той же гарантии «только один rebuild на TTL-окно» с нулевой координацией и нулевыми сетевыми затратами.

Правило решения

Каждое чтение кеша вычисляет:

should_refresh = (-beta * delta * ln(rand())) >= time_to_expire

Где:

  • delta — типичное время rebuild в секундах (например, 0.4)
  • beta — настроечная константа, по умолчанию 1.0
  • rand() — равномерное случайное в (0, 1)
  • time_to_expire — секунды до срабатывания текущего TTL

По мере приближения time_to_expire к 0 (близко к истечению), порог слева (-beta * delta * ln(rand())) всё вероятнее его превысит. Далеко от истечения вероятность близка к нулю. Вблизи истечения она быстро растёт.

Результат: ключ кеша обновляется до истечения. Первый запрос, поймавший вероятностный порог, запускает rebuild, пока существующее значение ещё отдаётся всем остальным. К моменту, когда TTL мог бы сработать, кеш уже имеет свежее значение.

Время до TTLdelta=0.4 с, beta=1.0Вероятность раннего refresh на чтение
10 с~4%
2 с~18%
0.4 с (= delta)~63%
0.1 с~98%

Точные числа зависят от распределения чтений, но тренд ясен: вероятность концентрируется в последних нескольких секундах до истечения.

Ожидаемые refresh в TTL-окне = 1

Ключевое свойство, доказанное Vattani et al.: интегрирование вероятности по TTL-окну даёт ровно один ожидаемый ранний refresh, независимо от размера флота или ставки чтения. Флот из 100 нод, читающий ключ 1 000 раз в секунду, не производит 100 000 ранних refresh — он всё равно производит примерно 1. Экспоненциальное распределение имеет свойство, что минимум N независимых выборок тоже экспоненциальный со скоростью N × lambda. С ростом флота «первый» читатель поймает порог раньше, но ожидаемый счёт остаётся ~1.

Когда XFetch работает, а когда нет

XFetch требует достаточно частых чтений, чтобы «какой-то читатель» поймал вероятностный порог до TTL. Алгоритм прозрачен, когда чтения происходят, например, каждые 100 мс за 60-секундный TTL — много шансов поймать порог в последних секундах.

Случай провала: холодные или спорадические ключи. Ключ, читаемый раз в 30 с при TTL=60 с, может иметь только одно-два чтения во всём «раннем refresh» окне (последние ~1 с). При малом N вероятность промахнуться по порогу значительна. Ключ истекает нормально и stampede-ит.

Правило большого пальца: используй XFetch на ключах с ≥ 10 чтений за ожидаемое-rebuild-duration-окно до TTL. Для более холодных ключей используй лок или SWR.

Почему это работает

Почему экспоненциальное распределение? Это непрерывное безмятежное (memoryless) распределение — минимум N независимых экспоненциальных выборок тоже экспоненциальный. Это означает, что свойство XFetch «ровно 1 ожидаемый ранний refresh» выполняется независимо от числа читателей или нод. Любое не-экспоненциальное правило решения либо требует большего числа ожидаемых refresh с ростом флота (потраченная работа), либо принимает ненулевую вероятность промаха (stampede). Экспоненциальное — уникально оптимальное среди coordination-free алгоритмов — основной результат Vattani et al. (VLDB 2015).

Настройка beta

beta управляет тем, насколько рано открывается окно refresh:

  • beta < 1.0: более ранние refresh, шире окно, больше потраченных ранних rebuild на холодных ключах. Используй, когда стоимость rebuild очень низка и staleness нужно минимизировать.
  • beta = 1.0 (по умолчанию): окно открывается, когда time_to_expire ≈ delta. Баланс.
  • beta > 1.0: более поздние refresh, уже окно, выше риск промахнуться по порогу на холодных ключах. Используй только когда rebuild дорог и ранний staleness нужно избежать.
Викторина

Ключ кеша имеет TTL=60 с и время rebuild delta=0.4 с. Примерно за какое время до TTL XFetch (beta=1.0) обычно запускает первый ранний refresh?

Викторина

Read-heavy кеш имеет холодные ключи с доступом раз в 30 с (TTL=60 с). Почему XFetch здесь плохо работает?

Викторина

Флот из 200 нод читает горячий ключ 50 000 раз в секунду. С включённым XFetch примерно сколько ранних rebuild происходит за TTL-окно?

Вспомните перед уходом
  1. 01
    Почему XFetch работает без cross-fleet координации, а распределённые локи требуют явного cross-node взаимодействия?
  2. 02
    Что делает увеличение beta выше 1.0 с поведением XFetch и какова цена?
Итог

XFetch (Vattani et al., VLDB 2015) решает проблему stampede для горячих ключей без координации. Каждое чтение кеша вычисляет -beta * delta * ln(rand()) &gt;= time_to_expire. По мере приближения TTL вероятность растёт; в среднем ровно один читатель за TTL-окно запускает ранний rebuild, пока все остальные продолжают попадать в ещё-валидное значение кеша. Экспоненциальное распределение уникально оптимально среди coordination-free алгоритмов. XFetch проваливается для холодных ключей (спорадические чтения могут промахнуться по узкому окну). Практическое правило: используй XFetch для ключей с многими чтениями за rebuild-duration-окно до TTL, и возвращайся к локу или stale-while-revalidate для остального.

Связанные уроки
встречается в178
Продолжить восхождение ↑Stale-while-revalidate и CDN request coalescing
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.