excache

Слой кэша на Elixir, использующий модель акторов OTP. Конкурентный, устойчивый к сбоям отдельных процессов, с реальными тестами.

excache
TL;DR

Слой кэша на Elixir поверх акторной модели OTP. Параллелизм без гонок, устойчивость к падению отдельных процессов и настоящие тесты. Хитрость в том, чтобы падение одного элемента не потянуло за собой остальные.

Введение

Кэш, разделяемый между многими потоками, - одна из классических трудных задач параллелизма. excache показывает, что Elixir и акторная модель OTP превращают эту задачу в нечто, с чем можно справиться без блокировок и без тихого страха перед взаимной блокировкой. Вместо борьбы с общей памятью вы переносите состояние туда, где к нему никто не обращается параллельно.

Это проект о двух свойствах сразу: параллелизме без гонок и устойчивости к сбоям. Оба вытекают из одной и той же философии OTP, в которой состояние живет в процессе, а ошибки не заметаются под ковер, а изолируются.

Акторная модель выворачивает задачу параллелизма

В большинстве языков кэш, разделяемый между потоками, означает блокировки, мьютексы и тихий страх перед взаимной блокировкой. Elixir идет иначе: каждое состояние живет внутри процесса, который обрабатывает одно сообщение за раз. Вместо того чтобы блокировать общую память, вы отправляете сообщение и получаете ответ.

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

cache.ex · bash
def handle_call({:get, key}, _from, state) do
  {:reply, Map.get(state, key), state}
end

Let it crash вместо защиты

В OTP вы не оборачиваете каждую операцию в оборонительный блок на случай сбоя. Вместо этого вы позволяете процессу упасть, а дерево супервизии поднимает его обратно в чистом состоянии. Это противоположность оборонительному программированию: вы не прячете ошибку, а даете ей опрокинуть один процесс и доверяете супервизии его отстроить.

excache опирается на это напрямую: падение одного процесса кэша не опрокидывает весь слой, потому что остальные процессы живут независимо. Поврежденное состояние исчезает вместе с процессом, а его преемник стартует чистым, а не тащит за собой испорченные данные.

➜
Совет

Тест этого свойства состоит в том, что вы намеренно убиваете процесс прямо в разгар трафика и проверяете, что слой продолжает отвечать. Если весь кэш падает вместе с одним процессом, дерево супервизии собрано неправильно, и хороший тест ловит это сразу.

Это контрфактический тест в чистом виде: вы проверяете не то, что система работает, когда все идет хорошо, а то, что она переживает сбой, который должна пережить. Тест, который никогда не видит падения процесса, не доказывает никакой устойчивости.

Устойчивость как свойство, а не заплатка

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

Свойства слоя и откуда они берутся

СвойствоОткуда берется
Нет гонокодин процесс, одно сообщение за раз
Нет блокировокобщение через сообщения
Изоляция сбоевсостояние заключено в процессе
Возврат в чистое состояниедерево супервизии

Больше проектов

Другие работы из той же категории - посмотрите, как мы решаем похожие задачи.

Есть похожий проект?

Напишите нам - смета бесплатна и приходит в течение часа.