Акула (в) IT
522 subscribers
4 photos
69 links
Канал о прекрасном в мире IT. Вайтпейперы по распределённым системам, БД и всему что с ними связанно. 🦈

По вопросам/советам/предложениям: @aquatir
Download Telegram
История CAP теоремы (6/6)

Последняя работа, о которой я бы хотел упомянуть, это критика CAP от Martin Kleppman, того самого автора книжки с кабанчиком.

Первая часть посвящена, собственно, разбору CAP из 3 частей. Что не так, что не понятно, где термины даются не четко, и какие из этого всего вытекают проблемы. К концу даже доказывается ряд теорем, очень похожих на CAP, но без двусмысленных формулировок. Эта часть на 100% формальная, и мы её пропустим, но сама терминология и доказательство поразительно лаконичны. Рекомендую почитать, если вам нравится такой формат.

Вторая часть рассказывает про фреймворк delay-sensitivity. Идея заключается в том, что в настоящий момент существует пропасть между теоретическими изысканиями, такими как формальное доказательство CAP теоремы и практическими системами. Например, в современном мире слишком высокая задержка (latency) приравнивается к недоступности сервиса (SLA/O, вот это все). Значит нужно построить такую модель терминов, которая помогла бы разговаривать на одном языке исследователям и разработчикам, при этом покрывала бы реальные кейсы.

Модель выстраивается в 2 этапа. Сначала задаются теоретические нижние границы времени выполнения операций чтения и записи для разных уровней консистентности в зависимости от сетевых задержек (обозначаются здесь через d). Каждый уровень сопровождается ссылкой на доказательство.

- linearizability — и чтение, и запись O(d). Пруф.
- sequential consistency — либо чтение, либо запись O(d). Второе из двух может быть O(1). Пруф.
- casual consistency — обе операции за O(1). Пруф 1, пруф 2.

Следует помнить, что наличие формального доказательства вовсе не обозначает применимость в реальном мире. Например, оказывается что casually consistent хранилище может и читать и писать за константу. Вот только для этого нужно передавать еще мешок данных, по которым можно определить эту самую casualty, что в реальных системах нецелесообразно. Поэтому, в частности, в реальном мире существуют более слабые гарантии, например eventually consistent системы.

А дальше, собственно, идет терминология delay-sensitivity фреймворка:

- Availability следует считать эмпирической величиной, равной проценту успешных запросов от количества общих в период времени. Чуть подробнее про эмпирическое значение availability я писал тут.
- Delay-sensitive — это свойство алгоритма, обозначающее, что ему нужно ждать задержку d пока шаг выполнится. В противоположность ставиться delay-insensitive, которым ждать не нужно. Как пример — запись в мастер может сделаться сразу же, а вот репликация данных в другие ноды минимум за d.
- Network Faults. Должны включать в себя не только полный network partition, но так же и периодические потери пакетов (ака "сеть моргнула") и внезапное превышение средних задержек. Все три показателя важны для реальной системы.
- Fault Tolerance — следует использовать вместо понятия partition tolerance. Алгоритм должен описывать какие ошибки он может пережить (например, потерю менее половины нод), а также что происходит, если лимит ошибок превышен.
- Consistency должен обозначать одну из известных моделей. Слово strong consistency не имеет смысла и лишь усложняет понимание.

Приживется ли терминология, покажет лишь время. Работа молодая, ей едва ли стукнуло 6 лет!

А на этом наш сказ про CAP закончился. Осталось только подвести итог:

- Под словом CAP понимают две вещи. Первая — формально доказанное утверждение, которое практически не имеет применений в реальности. Вторая — набор суждений про вечный трейдофф consistency/availability/latency/throughput/что-угодно-ещё.
- Разделение сети (partition tolerance) — это возможная, но далеко не единственная ошибка, которая может возникнуть в системе. В реальном мире нужно защищаться от всего.
- Доступность (Availability) в литературе и в реальном мире — две в сущности независимые вещи. В работах это "возможность получить результат", в мире — эмпирическая метрика.
- Даже без явных ошибок, в распределенной системе есть трейдоффы, а все эти консистентности и доступности — не бинарные величины, а точки на спектре.
🔥2
Про алгоритмы на собеседованиях

За последнюю неделю, я наткнулся аж на раз и два поста про то, как неправильно большие компании нанимают разработчиков. Истории звучат всегда как под копирку... либо «десять тысяч лет я делаю реальный софт, а меня не берут», либо «задачи непрактичные и бесполезные. Кому нужны ваши алгоритмы». Позиции кандидатов можно посочувствовать, но это скучно, поэтому время вызывать адвоката дьявола.

Начнём издалека. IT системы постоянно растут, обрабатывают все больше и больше запросов-в-секунду и петабайт-в-месяц. Каждые новые x10, а то и х5 клиентов требует рефакторингов, новых подходов, архитектуры. Создание чего-либо с нуля это дорого, долго и никаких гарантий, поэтому по возможности предпочтение отдаётся "проверенным решениям". Зачем писать свою кеш-систему, если можно взять готовую? Для чего думать над корректной асинхронностью, если уже изобрели системы обмена сообщениями?

Подход подкупает логичностью. Возьмём решение, которое у кого-то уже сработало, реализуем у себя. Затем оставим на веки вечные, до тех пор, пока оно справляется с нагрузкой / профилем / объемом данных / и т.д. Естественно все нужные нам параметры замерим. Получается и риски минимизировали, и время на разработку сократили, и еще можно хвастаться, что у нас в стеке +1 модный баззворд. Красота!

Вернёмся снова к компаниям и их собеседованиям. Вы создатель нового успешного стартапа, наняли первых пятерых разработчиков методом... разговора по душам конечно же, как же ещё, ведь вы люто ненавидите алгоритмические задачки на интервью. Проходит несколько месяцев. Теперь в компании уже 50 разработчиков. Проводить собеседования самому становится тяжело, поэтому функция оценки кандидата уходит в команды. Но вот незадача, то наймут короля токсичности, то супер-звезду, которому через полгода надоест, то сэра самозванца (не тот, который синдром, а тот который бездарен в своей профессии) Надо что-то делать...

Начинает создаваться некий формальный процесс найма, цель которого — нанимать более подходящих кандидатов. Как и в IT, этот процесс покрывается метриками от оценки кандидата до среднего времени жизни сотрудника и удовлетворённости своей работой. Появляются пулы вопросов, обязательная culture fit часть, exit-интервью, круговые ревью, в общем используются готовые решения, которые у кого-то уже сработали.

Ваш стартап стал серьёзной компанией. Теперь вы нанимаете по 50 человек в месяц, вы уже мыслите числами и метриками. При вашем текущем подходе только 60% кандидатов выходят на средний уровень производительности команды за 4 месяца. Получается остальные 40% не дотягивают! Возможно, думаете вы, проблема в том, что к нам приходят технически слабо подготовленные специалисты. Ваш Вице-Президент-По-HR советует вам посмотреть на опыт компании "условно-гугл" и давать алгоритмические задачи. Вам эта идея противна, но с ней соглашается совет директоров. Придется делать. Но мы же большая и умная компания, поэтому проведём A/B тест. 80% кандидатов поведём по старому процессу, а 20% по-новому, с алгоритмами.

И о чудо, через полгода замеров оказывается, что из тестовой когорты, которая проходила алгоритмические задачи, вышло на нужную вам производительность за 4 месяца 89% сотрудников. Это на 29% больше, чем ранее, прирост почти в два раза! Вы не верите в алгоритмы, пытаетесь еще одной A/B когорте дать упрощенные задачки, метрика падает до 63%. Следующей даёте 3 дизайн секции и 1 на программирование. Поднимается до 74%, но это не 89%. Да что же такое! Вам самому противно в душе, но цифры говорят, что алгоритмы работают, а остальные подходы не работают.

Вас ненавидят в интеренатах, поносят "не имеющие ничего общего с реальностью" алгоритмические секции. Но почему-то когда процесс найма становится другим, кандидаты нанимаются плохие. Да, ваш процесс раз в месяц отсеивает очередного создателя homebrew (если что, он извинился), да ваш HR бренд в каком-то смысле страдает... Но ведь оно работает. А работает — не трогай. Хочешь поменять — сначала измерь. Только не пытайся доказать, что с алгоритмами статистически и вправду лучше найм, не поверят!
🔥1
Кеширование и TinyLFU

#shark_whitepaper
#shark_recommends

Работы как сегодняшняя (ссылка-клон) не позволяют делать по посту о пейперах раз в неделю, так как информации слишком много. Это исследование настолько открывает границы сознания, что его нужно перечитать несколько раз. Сначала, чтобы восхититься красотой идеи, затем чтобы понять суть.

Для начала отойдём на несколько шагов назад. Когда дело касается "оптимизации доступа", в первую очередь речь идет про техники, рассматривающие идею data locality. Данные "ближе" получить быстрее, чем данные "дальше". В L1 кеш процессора обратиться быстрее, чем в память, а в память быстрее, чем на жёсткий диск. Идея не ограничивается одной машиной, потому что запрос в тот же дата-центр также дешевле, чем в соседний.

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

Логично предположить, что элемент будет полезным, если его скоро вызовут ещё раз, т.е. на временной шкале два вызова находятся близко. Но инсайт заключается ещё и в том, что важность представляет не только расстояние во времени между вызовами, но также и частота этих вызовов. Техники, рассматривающие и оптимизирующие частотные и временные характеристики данных, относятся к идеи time locality по аналогии с data locality.

Нагрузки в реальных системах имеют непредсказуемый характер, а размер кеша ограничен. Какой элемент нужнее, который вызывался только что или который не вызывался давно, но за пару минут до этого был самым популярным по частоте вызовов? Пейпер показывает, приводит бенчмарки, и доказывает, что частота — тоже важный критерий для инвалидации кеша.

TinyLFU (Least frequently used) — это небольшая прослойка перед основным кешом, который может работать, например, на LRU. Её задача контролировать удаление данных в соответствие с частотой их вхождения. Основной кеш предлагает свой вариант, а TinyLFU решает, удалить ли то что предложил кеш или что-то другое. Подсчёт частоты может реализовываться через фильтр Блума с подсчётом или Count-min sketch (CM sketch).

Задача подсчёта частоты сама по себе не решается в лоб, поскольку смотреть просто на количество вхождений нельзя. Данные могут быть сначала очень полезны, но с течением времени их важность падает. Поэтому TinyLFU также использует технику ageing или cooling. Периодически или при достижении определенных условий, вес определенного ключа или набора ключей уменьшается. Таким образом, частота стремится к нулю, если не происходят повторные запросы.

Существует и другая особенность. Часто бывает удобнее все данные класть в кеш и получать только оттуда. Если все записи проходят через кеш, среди них скорее всего будут и одноразовые записи, которые лучше бы вообще не сохранять. Эта идея реализуется через механизм под названием doorkeeping. Смысл в том, чтобы при первом обращении класть данные в обычный LRU кеш, и только если по этим же ключам будут еще обращения, перекладывать их в основное хранилище в памяти. Размер doorkeeping области — параметр конфигурации кеша.

Все эти мысли и техники объединяются в пейпере в алгоритм W-TinyLFU (Window TinyLFU). Он используется в Java библиотеке Caffein. Это уже реализованная идея, опробованная в продакшене, протестированная, как на сгенерированных данных (Zipfian distribution, SPC1-like трейсы. Последнее — некий синтетический трейс для проверки I/O операций. Я нашёл упоминание только здесь), так и на реальных примерах. Алгоритм работает как минимум не хуже любого другого, а чаще немного лучше. Больше бенчмарков есть в самой работе.

Отдельно стоит отметить богатейшую теоретическую базу для этого исследования. Это аж 57 источников! Не каждая книга может похвастаться таким длинным списком. Если всё читать не хочется, основная выжимка, которая включает другие алгоритмы кеширования и инвалидации, представлена в доке Caffein в конце.
🔥1
Всё есть трейдофф

Трейдофф — решение, в рамках которого одни характеристики рассматриваемой системы улучшаются за счёт ухудшения других. Обычно трейдоффы рассматриваются в контексте технических решений. Например, добавим кеш — получим меньше latency, но больше потребление памяти. Добавим ещё 2 ноды в кластер из 3, получим больше availability, но усложним схождение консенсуса и возможно схватим проблем с консистентностью (CAP, вот это всё). В экономике есть схожее понятие — альтернативные издержки. Это максимальная потерянная выгода из-за принятого решения. Сколько бы можно было заработать, если вместо школы построить торговый центр? А если парковку вместо автомойки?

Трейдоффы — хороший способ ведения дискуссий. Можно спорить до хрипоты в поисках оптимального решения, а можно нарисовать табличку с плюсами и минусами каждого и отталкиваться от объективных фактов и метрик. Здесь правда сразу появятся две проблемы. Во-первых, не все факты объективны, а не все метрики можно быстро собрать. Во-вторых, факторов может быть очень много, так как практически всё есть часть трейдоффа. О некоторых таких факторах мы поговорим.

Рассмотрим ситуацию. Вы штурман в ралли-команде и готовитесь к новому маршруту. У вас много времени на подготовку, но маршрут для вас новый. Вы предполагаете с какой скоростью нужно проезжать разные участки маршрута и составляете на первый взгляд разумный план. Теперь вы мчите на трассе, одно колесо полуспущено, а вместо обещанного гравия на земле песок. По плану нужно лететь на X км/ч, но вы предпочитаете замедлиться, главное — доехать. Трасса не изменилась, но вы приняли разное решение при планировании и на дороге, потому что время на принятие решения и объём доступной информации — два важных фактора в трейдоффах. Можно потратить время и узнать больше о системе, а лишь затем принять решение, либо действовать прямо сейчас. К разработке эти два фактора относятся самым непосредственным образом. Никто не умеет правильно разделять системы на доменные области и вводить правильные абстракции с первого раза. Просто информации не хватит. Сделать сейчас как видно, а потом переделать — не просто разумный шаг, а единственно верный при отсутствии всей нужной информации и времени на её сбор.

Ещё один важный фактор — это сложность системы. Любое дополнение к продукту понижает его понятность, так как разбираться в отсутствующем коде не нужно. Сложность системы вступает в конфликт с несколькими другими факторами. Во-первых, с производительностью. Здесь я лучше Шипилёва не скажу (доклад + расшифровка), обязательный доклад вообще для всех. Во-вторых, сложность находится в конфликте со скоростью внесения дальнейших изменений. Чем труднее разобраться в текущем состоянии, тем сложнее это состояние поменять. Проблема здесь в том, что обе эти характеристики объективно подсчитать практически невозможно. Помогает только опыт и собранные грабли. Отдельно стоит сказать про "давайте всё перепишем с новым подходом". Период времени пока система будет находиться в состоянии между "старым" и "новым" варьируется и часто не заканчивается никогда. В безупречном мире система остаётся понятной и функционирующей даже в промежуточном состоянии, но этого бывает тяжело достичь.

Есть и много других интересных факторов. На каком языке писать? Это зависит и от размера коммьюнити, и от количества кандидатов на локальном рынка труда, и от бюджета, и лишь где-то в последнюю очередь от личных предпочтений, количества фичей и "модности" языка. Что выбрать, новую фичу в продукт или задачу из техдолга? Опять-таки, огромный вагон характеристик в трейдоффе. Финансовые показатели, на которые повлияет фича, время на реализацию, степень риска нестабильности продукта при откладывании техдолга, степень риска, что конкуренты реализуют фичу быстрее. Куда ни посмотри, любое решение — это трейдофф между десятками разных факторов. Только если учитывать все их можно сделать оптимальный выбор. А пока я в трейдоффе между карьерой и психологическим здоровьем выберу второе, и пойду смотреть сериальчики вместо литкода.
🔥1
Вас уже сотня человек! Спасибо всем большое, что читаете, я все ещё радуюсь каждому новому подписчику 🎉

В честь знаменательной даты, распишу план на ближайшие месяцы (годы):

- Запилить сайт. Иногда хочется делать разборы пейперов с картинками, возможно даже с анимашками. При этом закрываться за пейволом медиума ну такое. Это план на долго вперёд, мб к концу года и появится.
- Больше циклов про распределенные системы: как минимум разбор пейперов по алгоритмам failure detection, leader election, а также моделям консистентности и алгоритмам консенсуса. Хочется сделать полезный ресурс в стиле Jepsen, но с дорожной картой по топикам распределённых систем.
- Классические пейперы по базам данных: про ARIES, про B-Trees и прочее.
- Рандомные посты про всё на свете: От мотивации до архитектуры.
- Возможно несколько работ по обучению как науке, т.к. недавно заинтересовался темой.

А и конечно же важный опрос!
🔥1
Комментарии?
Anonymous Poll
86%
Пили
14%
Не пили
👍1
Отзыв на Database Internals

#shark_recommends

Пару месяцев назад тот-самый Martin Kleppmann, автор книжки с кабанчиком, был автором недели в слаке DataTalks (ссылка на слак там же). Это такое интересное мероприятие, когда автор какой-нибудь книжки в основном по data science или ml в течение недели отвечает на любые вопросы участников. На следующей после него неделе рассматривалась книжка Database Internals от Alex Petrov, которая для меня стала настоящим открытием!

Книга поделена на две части. Первая полностью про архитектуру БД, преимущественно о хранении. И это самая подробная таксономизация алгоритмов хранения в базах данных, которая мне пока что встречалась в литературе. Не, я конечно слышал, что там в постгре используется B-tree, но этих самых B-tree оказывается сильно больше, чем 5 видов, так ещё для каждого мешок разных оптимизаций. Забавно как можно каждый день пользоваться БД, при этом иметь очень посредственное представление, как оно там работает... Однозначно рекомендую.

Вторая часть про распределенные системы. Здесь снова больше про алгоритмы, чем про "построение правильных приложений". Вот вам 4 вида Gossip и 6 разных Paxos'ов, разбирайтесь! Благо есть ссылки на все 222 пейпера-источника. Половину из них мы здесь ещё рассмотрим. 💪

Если вы думали, что почитать про распределенные системы после книжки с кабанчиком или просто всегда хотели узнать, как там база данных внутри двигает битики, однозначно читайте, не пожалеете.
🔥1
Epidemic Algorithms for Replicated Database Maintenance (1/2)

#shark_whitepaper

Классическая дремучая работа из 1987 года, когда самыми крутыми компаниями в IT были Xerox и IBM, которая в будущем станет основой для большинства алгоритмов поиска сбоев (failure detection) и уменьшения энтропии (entropy reduction). Благодаря этой работе появится, например целая семья алгоритмов-протоколов с общий названием gossip. Они используются практически везде, где есть необходимость поддерживать несколько узлов, например в Consul, Cassandra, AWS S3 (раскрывают в постмортеме) и в десятках других продакшон-грейд продуктах.

Эпидемические алгоритмы/протоколы
так называются не случайно, а в честь особого вида математики, изучающего распространение эпидемий. Да, такая математика тоже существует. Вот например целая книжка по теме, очень актуально. Только в случае с алгоритмами цель — заразить наибольшее количество узлов, а не предотвратить заражение. Эдакий Plague Inc на транзисторах.

Терминология берётся из всё той же математики эпидемий. Узлы сети разделяются на 3 вида:
- susceptible — ещё не получили обновление.
- infective — уже получили и распространяют.
- removed — уже получили и не распространяют.

Задача стоит в том, чтобы распространить обновление по распределенной сети узлов, при этом уменьшить число узлов, которые ни разу не получили обновление, т.е. остались susceptible (такие узлы называются residue). При этом эпидемия должна завершиться за минимальное количество сообщений (traffic), а также алгоритм должен сойтись (convergence) максимально быстро. Сходимость измеряется как по среднему времени, так и по времени между первым и последним сообщением.

Эпидемия начинается с того, что некий узел переходит в состояние infective, и начинает распространять обновления. Их распространение происходит на случайным образом выбранные соседние узлы. После попытки заражения, узел с заранее заданной вероятностью k переходит в состояние removed, т.е. перестает распространять обновления. Эпидемия завершается, когда в сети отсутствуют infective узлы, т.е. все узлы либо уже распространили и перешли в removed, либо никогда не получили обновление и остались в susceptible. Процесс заражения можно разделить по нескольким критериями:

- Blind / Feedback. При blind распространении узел всегда после отправки сообщения проверяет, нужно ли ему перейти в removed. При feedback только если новая нода уже получала обновление. Использование feedback увеличивает трафик, так как нужно вернуть и ответ, но зато позволяет резко сократить процент residue узлов после завершения пандемии.
- Counter / Coin. В общем случае, узел переходит в removed с вероятностью 1/k, т.е. по броску k-гранной монетки. Подход counter значит, что узел не бросает монетку, а отключается только после n отправленных сообщений. Трейдофф здесь между "хранить счётчик" и не хранить. Кажется мелочь, но в системе может одновременно происходить несколько волн эпидемии с разными обновлениями, а счётчик нужно хранить на каждую из них на всех infective узлах, так что накладные расходы могут быть большими. Возможно также использовать и комбинированный подход, когда сначала отсылается n сообщений, а затем узел отключается с вероятностью k.
- Push / Pull. Обычно узлы заражают соседей по push модели, так как рассылают сообщения сами. Можно сделать алгоритм наоборот, когда все узлы сети сами начнут запрашивать сообщения от соседей. При наличии большого количества эпидемий одновременно, это работает даже лучше, чем push (пруфы в статье, спойлер: там матан с производными), но генерирует больше трафика. Оба подхода можно использовать одновременно в push-pull модели.
🔥1
Epidemic Algorithms for Replicated Database Maintenance (2/2)

Замечательные рассуждения приводятся и об удаление данных при эпидемиях. Недостаточно отправить сообщение "удалить данные", потому что при наличии конкурирующих сообщений с изменениями, эпидемия по обновлению может перебороть эпидемию по удалению. Поэтому приходится хранить death certificate — некоторое сообщение, при получении которого все более старые сообщения можно игнорировать. Эти сообщения нужно хранить какое-то время, но не всё так просто.

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

В таких случаях можно использовать как минимум две техники. Первая — это использовать вместе с эпидемическим алгоритмом дополнительный способ синхронизации. Вторая — хранить death certificate на некоторых нодах в спящем состоянии. Когда такой узел получает обновление для спящего мертвого сертификата, он пробуждает сертификат и запускает новую эпидемию по удалению этого уже, казалось бы, удаленного элементы. Тут мы успешно перейдем к производной первой проблемы — когда удалить спящий сертификат...

Есть и другая сложность. Часть обновлений — это в самом деле легальное "возрождение" ранее убитых данных. Решить её в статье предлагается через заведение второго таймштампа. Один используется при обычных обновлениях, второй для активизации после удаления. Если узел активизирован после таймштампа сертификата, значит сертификат неверен и все узлы, которые одновременно пытаются распространить и удаление и обновление, должны прекратить распространять удаление и вообще уничтожить свой death certificate.

В заключении работы рассматривается, как вообще такие алгоритмы работает в реальных сетях, где узлы находятся на разном расстоянии, но при этом все соединены. Вывод в целом очевиден: эффективнее построить пространственный индекс (spatial index) и ходить по более близким элементам с большей вероятностью, чем по менее близким. При чем пруф есть как математический (который я не понял ¯\_(ツ)_/¯), так и экспериментальный. А вот если взять более необычные топологии сети, например когда узлы соединены в мини-кластеры и лишь малая часть нод общается между кластерами, тут всё становится ужасно как во времени сходимости так и в проценте residual узлов. И если pull-модель ещё как-то справляется, то push-модель при симуляциях может вообще не выйти за пределы одного кластера.
🔥1
Consistent Hashing и Akamai Technologies (1/2)

#shark_whitepaper

Я честно пытался написать привычный tl/dr по работе по консистентному хешированию (Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web), но пока готовился, понял, что интереснее даже не сама работа, а то, как развивался мир после нее. Кому все-таки хочется хардкора, матан начинается с четвёртой страницы и не заканчивается до самого конца. А если вы в первый раз видите словосочетание «консистентное хеширование» то советую посмотреть ссылка один просто хорошая и ссылка два лекция Стенфорда. Матана там нет, я проверил.

Сначала о работе. Идея заключается в том, чтобы выстраивать кеши друг за другом в Random Tree. Каждой странице при помощи случайной равномерной хеш-функции h присваивается некий путь от первого кеша до вершины. Таких функцию несколько, т.е. есть больше чем 1 путь, по которому можно получить страницу. Браузер может вычислить этот путь, выбрав одну из хеш-фунцию. Запрос приходит на первый кеш, и если тот содержит страницу, отдаёт её, а если нет, направляет запрос на следующий кеш. Тот на следующий, на следующий и так до тех пор, пока запрос не доберётся до сервера, где страница уж точно есть. Страница сохраняется в кеше только после того, как кеш увидит запрос этой страницы несколько раз. Здесь важно, что у каждой страницы несколько случайных путей по дереву кешей в зависимости от выбранной хеш-функции. Разные клиенты будут получать разный путь к одной и той же странице, эффективно размазывая её по всему дереву кешей, что позволяет избавиться от перегруженных нод. В работе перегруженные ноды называют swamping. Доказательство того, что такое размазывание работает тоже есть.

Поскольку сеть в целом нестабильная, а нагрузка даже без swamping узлов может расти, нужно как-то добавлять дополнительные ноды в сеть и поднимать старые. Но ведь мы использует хеш-функции для определения пути от первого кеша до сервера. Обычно при изменении размера хеш-таблицы (в нашем случае, при вылете ноды), нужно заново вычислять значение хеш-функции для большинства ключей. Здесь как раз на помощь и приходит consistent hashing. Если использовать консистентную хеш-функцию в качестве функции h ещё на самом первом шаге при выборе случайного пути, можно добиться того, что большая часть случайных путей не будет перестраиваться при добавлении или удалении новых нод из сети.
🔥1
Consistent Hashing и Akamai Technologies (2/2)

А теперь история. Двое из авторов работы Daniel Lewin и Frank Leighton — основатели Akamai Technologies. Компания делает CDN и security решения. Это Cloudflare/Fastly в мире ентерпрайза. Через них проходит по разным оценкам до 30% всего трафика в мире. Работа датируется 1997 годом и говорит как раз о том, как эффективно раздавать страницы в Интернете. В 1998 году авторы попали в финал конкурса MIT $50к Entrepreneurship Competition (сейчас MIT $100k. Инфляция!), основав в этом же году компанию. Собственно на двух идеях из работы: Random Trees и Consistent hashing, и основана архитектура Akamai CDN.

31 марта 1999 года происходит знаменательное событие для Akamai и для мира. В свет выходят Звездные войны. Эпизод 1: Скрытая угроза. Да ещё и онлайн. «Онлайн» в конце 20 века до появления ютуба значило «Вы можете скачать файл в формате Real Video, QuickTime или AVI и посмотреть на своем ПК». Официальный и эксклюзивный дистрибьютор — apple.com. Собственно, apple.com практически сразу же падает и продолжает лежать недоступным на протяжении половины дня. Но сеть Akamai, которая уже успешно справилась с раздачей более миллиона копий трейлера звездных войн за сутки, продолжает жить. PR скандала не случилось.

01 апреля 1999 года Стив Джобс решает лично позвонить одному из сооснователей компании Paul Segan, но тот принимает звоном за первоапрельскую шутку своих коллег и кладет трубку. На сотрудничество компаний этот инцидент не повлиял.

11 сентября 2001 года трагически погибает один из основателей Akamai и соавторов работы Daniel Lewin. Убит одним из террористов на борту самолёта American Airlines Flight 11, который врезался в первую, северную башню Всемирного торгового центра.

2001 год: консистентное хеширование начинает применяться в первых P2P сетях. В таких сетях нужно чтобы все узлы могли знать, где лежит какой файл. Первые версии, например Napster, хранили такую информацию централизовано, что позволяет легко отключить всю сеть при падении главного узла. Далее ко второму поколению, например сеть Gnutella, появилась идея раздавать такую информацию через broadcast. Это работало пока узлов в сети было мало. Наконец третье поколение P2P сетей, начиная с Chord, уже начали использовать консистентное хеширование. Преимуществом такого хеширования для P2P является то, что не обязательно знать о расположении всех остальных серверов в сети. Достаточно знать несколько, которые находятся рядом. Частично даже современный BitTorrent продолжает использовать consistent hashing.

2006 год: Амазон запускает пока недоступную для широкой общественности Dynamo, которая тоже использует консистентное хеширование для партиционирования и репликации. Цель — хранить максимум информации надежно на простом железе и быстро её раздавать. Это важная веха, так как Амазон через год также публикует вот эту работу (Dynamo: Amazon’s Highly Available Key-value Store).

В дальнейшем consistent hashing стал применяться и в Cassandra, и в Ryak, и в Couchbase, Akka, Gluster, и ещё в десятках других продуктах. Сейчас это одна из базовых структур данных / алгоритмов для построения сетей с частой сменой количества участников.
🔥1
Когнитивные искажения (1/2)

Пока настаивается пост про gossip в продолжении этого, поговорим о когнитивных искажениях — устойчивых шаблонах поведения, свойственных для людей в определенных ситуациях. Мне вообще не очень нравится русскоязычный вариант термина, так как «искажение» как будто слово из области Матрицы или путешествий во времени. В английском варианте этот термин звучит как cognitive bias, где bias переводится как предвзятость или склонность. Склонность — не правило. Суть в том, что какая-то часть людей, оказавшись в конкретной ситуации, с большей вероятностью поведёт себя определенным образом. Именно на вас «искажение» может не действовать, но в целом тенденция прослеживается. И конечно никто не пытается объяснить все сложные механизмы человеческого мышления через пару примеров.

«Склонностей» в мире людей бесчисленное множество и мы с ними сталкиваемся каждый день. Рассмотреть их все точно не получится, поэтому я поговорю только о тех, с которыми сам пытаюсь бороться, но всё ещё не умеют. На вики есть замечательная инфографика по теме, кто захочет изучить подробнее или просто залипнуть. А ещё алфавитный список на английской вики.

Начнем с chois-supportive bias или искажения сделанного выбора. В IT постоянно приходится принимать какие-то решения. Они бывают глобальные: на каком языке писать, какое хранилище данных выбрать, кого из двух кандидатов нанять или более локальные, например какой из 3 видов синтаксиса выбрать, чтобы реализовать задачу. При принятии решений применяется разная аргумента от «вот тут дядька на медиуме написал» и пресловутого "best practice" до экспериментов и замеров. «Искажение» заключается в том, что после принятия решения, вся методология летит в трубу. Наш выбор начинает казаться более правильным по необъективным причинам, а вместе с тем, отвергнутый вариант становится менее интересным. Всякий раз, когда на вопрос «почему вы всё не сделали на технологии B, вместо A?» вам хочется поднять голос на человека, остановитесь и задумайтесь, а почему, собственно, не сделали? Если ваш выбор A > B основывался на фактах, то они скорее всего всё ещё верны. Если не основывался, а покричать хочется, то вы столкнулись с искажением. Вам хочется защитить ваш вариант, потому что он ваш, а не потому что он объективно верный.

Вносить изменения в процессы или технологии в IT вообще крайне тяжело. Мало того, что сделанный выбор кажется более верным, так ещё люди в принципе предпочитают оставить всё как есть. Это называется status quo bias. Под эту категорию попадают и допотопное легаси, которое никто не хочет трогать не потому что риски, а потому что так сложилось. И бесконечные алгоритмы на собеседованиях (хотя я пытался их рационализировать). И что самое плохое, процессы в IT. Вам кажется что скрам не работает и ежедневные созвоны не нужны? Попробуйте доказать это ближайшему менеджеру.
🔥1
Когнитивные искажения (2/2)

Следующий на очереди у нас selection bias или ошибка отбора. Она заключается в неправильно собранной выборке при измерении статистической величины. Например, если вы в новостях услышали про одного-двух людей, которые заразились коронавирусом после прививки, из-за этого нельзя делать вывод что прививки не работают. Если у ваших друзей сработало решение A, из этого не следует, что вам нужно его повторять. Возможно, стоит найти более репрезентативную выборку. Разновидностью ошибки отбора является ошибка выжившего или survival bias. Люди в интернетах сплошь и рядом рассказывают об успехах, но практически никто не говорит о неудачах. Это создаёт ложную уверенность, поскольку голоса тех, у кого не получилось или тех, кто «не выжил», не попадают в выборку.

Перейдём к спонсору одновременно и токсичных комьюнити, и синдрома самозванца, а именно к ошибке атрибуции или attribution bias. Её суть заключается в том, что некоторым людям при рационализации успехов или неудач других людей более свойственно приписывать эти успехи/неудачи внутренним характеристикам индивида, а не сложившимся обстоятельствам. Почему ваш джун сломал приложение в проде? «Потому что он глупый» — ответ неверный. Любая ситуация состоит из суммы человека и условий, в которых он находится. Стресс, отсутствие понятных инструкций, незащищенная от человеческих ошибок среда — всё это не менее, а скорее более важно, чем интеллектуальные способности конкретно взятого индивида.

Здесь же синдром самозванца. Некоторым людям свойственно занижать свои способности на фоне других людей. Во многом это происходит, потому что успех других воспринимается как следствие их героического характера, силы воли или поля искажения реальности. Ошибка атрибуции заключается в полном игнорировании состояния реальности, которая также имеет влияние на успех или неудачу. Каждая мелочь в многогранном мире, начиная от количества родительской заботы в детстве, заканчивая макроэномической ситуации, влияет на успешность. Исключительных людей, гениев, которые и вправду имеют врожденные способности, единицы по сравнению с остальными.

Напоследок, «утро вечера мудренее» или эффект недавнего, он же recency bias. Из-за этого «искажения», более ранние события или информация воспринимаются как более важные. После долгой и нудной ручной работы всегда хочется потратить дни на её автоматизацию, хотя возможно ручную работу нужно делать раз в год и это нецелесообразно. Или любимый мной процесс ретроспективы. Как много раз вам приходилось придумывать пункты для ретро прямо во время этого ретро? При таком подходе, вы непроизвольно будете считать за более важные самые недавние события. Кстати, если до этого вы не слышали про когнитивные искажения и завтра же попробуйте списать все проблемы на них, это тоже будет проявлением эффекта недавнего.
🔥1
Введение в семейство алгоритмов Gossip (1/4)

#shark_whitepaper

Gossip — ещё одно слово, часто встречающиеся в литературе и бложиках на медиуме, которое скрывает за собой бесконечные пласты статей разных умных дядек. В качестве знакомства, я осилил четыре из них, но расскажу о трёх, а по четвертой пройдусь вскользь, так как она оказалась супер специфичной. Исторически, gossip появился из дремучей работы 1987 году про эпидемические алгоритмы, о которой я писал тут. Информация оттуда не нужна, чтобы понять этот пост, но может быть интересно. Пост из четырёх частей, так что устраивайтесь поудобнее. В первой введение, затем описание самого протокола на псевдокоде, далее работа gossip в очень больших сетях и в завершении, практические применения и проблемы.

Семейство протоколов gossip — это специфичный набор алгоритмов по обмену данными в сети узлов. Идея в следующем: пусть каждый узел раз в промежуток времени находит один из случайных соседних узлов и направляет ему некоторое сообщение. Так делает каждый узел, который уже получил обновления. Когда все узлы направили сообщение, завершается первый раунд обмена. Раунды проходят до тех пор, пока обновление «не потеряет актуальность». Сообщения содержат не только полезные данные, но и информацию о сети, которой обладает узел. Если проще — каждая нода направляет список соседей, о которых знает она сама. Если долго передавать такую информацию, рано или поздно все ноды сети будут знать о существовании всех узлов. Удобно. В некоторых вариантах gossip информация о других нодах сети — это и есть вся полезная нагрузка.

Киллер фича алгоритмов именно в случайном выборе соседней ноды. Именно случайность позволят gossip алгоритму работать даже при отказах значительной части сети. Почему используется слово «протокол», а не «алгоритм» определить сложно. Возможно это связано с тем, что исторически gossip использовался как низкоуровневый подход для обнаружения топологий в огромных сетях. Сетевики вообще любят всё вокруг называть протоколами.

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

- Периодический, попарной обмен данными между узлами сети.
- При обмене данных передаётся небольшое количество информации. Gossip протокол в идеальном мире не должен съедать всю пропускную способность сети.
- Обмен данными происходит редко. «Редко» в данном случае значит сильно реже чем средние задержки (latency) в сети. Идеальный gossip не создаёт значимой нагрузки на сеть узлов. Это позволяет использовать gossip, как дополнение к другим алгоритмам обмена данными.
- Надежный канал связи не требуется. Более того, новые узлы могут вступать в сеть даже во время работы алгоритма.
- При взаимодействии между узлами A и B, либо один из них, либо оба сразу меняют своё состояние. Кинуть пинг другому узлу это ещё не gossip.
- Соседние ноды выбираются случайным образом. В идеальном мире с равномерно распределенной вероятностью. В реальности правда достичь её всё равно не удастся, об этом тоже ниже.
- Время распространения обновления по всем узлам сопоставимо с O(log(n)).

Обмен данными между случайными нодами приводит к тому, что gossip протоколы дают только вероятностью консистентность. Здесь речь идёт не столько о вероятности того, что система перейдёт в консистентное состояния, сколько о времени, когда это произойдет — большинство вариаций практически гарантированно смогут распространить данные по сети когда-нибудь. В случае с gossip консистентность можно объяснить фразой на подобии «с вероятностью 99%, через 30 раундов каждая нода в сети получит обновления». Так сказать, how eventual is eventually consistent?
🔥1
Введение в семейство алгоритмов Gossip (2/4)

Gossip — это не только удивительно полезные, но и достаточно простые алгоритмы. Каждая нода в сети может исполнять один и тот же код обмена, состоящий из двух тредов: активный занимается отправкой данных узлам сети, а пассивный их обработкой.

Работать алгоритмы могут не только в push режиме, когда ноды направляют свои сообщения соседям, но также и в pull, когда узлы наоборот «затягивают» сообщения от соседей. Может быть также и pull-push модель, когда один узёл делает и то, и другое. Очень упрощенно, алгоритм выглядит так:

Активный тред:
1. Выбрать соседний узел
2. Если push
2.1. выбрать сообщения для отправки
2.2. направить в выбранный узел
3. Если pull
3.1. получить сообщения от узла
3.2. обработать сообщения

Пассивный тред:
1. Получить сообщение
2. Если pull
2.1. выбрать сообщения для отправки
2.2. направить в выбранный узел
3. обработать сообщения.

Шаг «обработать сообщения» может включать много разных действий. Например, алгоритм обычно следит за тем, получал ли он уже одно и тоже обновление и сколько раз, а также как далеко (далеко в количество прыжков, например) находятся узлы, от которых или к котором прилетают сообщения.

Напомню, что ноды обмениваются информацией только о тех соседях, о которых они знают. Сложности наступают, когда количество узлов в сети начинает переваливать за пару тысяч. Во-первых, с таким количеством нод, передать информацию о всех соседях становится решительно невозможным — слишком много данных в одном сообщении. Во-вторых, в большой сети постоянно отваливаются и появляются новые узлы, их как-то тоже нужно включать в алгоритм. В-третьих, топология становится достаточно сложной, чтобы в один прыжок нельзя было добраться до любого соседа. Соответственно появляется выбор: отправить ближайшему соседу, так как это близко или всё равно пытаться переслать сообщение подальше. Всё это приводит к тому, что равномерно распределенный выбора случайного соседа перестаёт быть равномерным.
🔥1
Введение в семейство алгоритмов Gossip (3/4)

Для решения всех этих проблем часто используется абстракция под названием peer sampling service. Идея такая: давайте хранить только некое число c «соседних узлов». Тогда при получении нового сообщения со списком узлов, нужно проводить некую агрегацию двух состояний — того, которое есть на ноде приёмнике и того, которое пришло с ноды передатчика. Наличия ограничения в c также значит, что фраза «отправить случайно ноде» меняет свой смысла. Случайных всего c и из них нужно выбрать какую-то из нод.

Более формально, с peer sampling service алгоритмы gossip разделяются по трём параметрам.

1. Peer selection. Периодически узел берёт из своего списка c одного соседа, с которым он обменяется состоянием. Можно выделить 3 основные способа, как это делается:
--- rand. Выбор случайного узла.
--- head. Выбор самого близкого узла.
--- tail. Выбор самого дальнего узла.
2. View selection. После обмена информацией о соседях, на узле находятся два списка соседей. Один, который был на узле изначально, и второй, пришедший от соседа. Их нужно соединить, учитывая ограничение в c соседей. Делается это тоже одним из трёх способов:
--- rand. Общий список c соседей формируется из произвольных узлов.
--- head. Списки объединяются, а затем остаются только c самых близких к текущему узлу соседей.
--- tail. Списки объединятся, а затем остаются только c самых дальних от текущего узла соседа.
3. View propagation. Определяет алгоритм, которым обмениваются узлы обмениваются информацией. Об этом уже было выше. Есть 3 способа:
--- push. Узел направляет свое состояние соседям.
--- pull. Узел забирает состояние от соседей.
--- push-pull. Объединение двух способов. Например сначала направляет, потом забирает.

Таким образом, работу peer sampling service можно описать кортежем из трёх элементов (ps, vs, vp). Разумеется можно придумать и другие, гибридные алгоритмы для ps и vs, например, но пока нам хватит самых простых. Реальные алгоритмы это к примеру (head, tail, push-pull) или (rand, rand, push).

Тут встаёт логичный вопрос, какой из 27 вариантов самый лучший? Как обычно, самого лучшего во всех ситуациях нет, всё трейдофф и мы обречены на несовершенные решения. Но отдельно взятые сочетания всё-таки выделяются перед другими.

Больше половины алгоритмов можно отбросить сразу, поскольку они стабильно приводят к нежелательным ситуациям:

- (head, *, *). Если выбирать для отправки только ближайшие ноды, в сети узлов будут образовываться кластеры. Это нежелательно, так как кластеры нод менее устойчивы к разделению. К тому же любые не-случайные топологии ухудшают вероятностные гарантии gossip.
- (*, tail, *). Если при отбрасывании нод из списка соседей всегда оставлять только самые дальние, новые ноды попросту не смогут стать частью топологии. Я очень долго думал, почему это так, но всё-таки дошло. Понадобилось всего 2 страницы рисования картинок...
- (*, *, pull). Проблема с этими вариантами очень похожа на первый, но немного по-другому. Модель pull приводит к образованию звездообразных топологий. Такая же проблема с гарантиями, как в первом пункте.

Из хороших алгоритмом можно выделить следующие:

- (*, *, push) хуже со сходимостью, чем у аналогов, но push протоколы лучше всего восстанавливаются при массовых отказах сети. При чём для ненормального математика отказ — это сразу -50% нод или больше. Кстати, процент узлов, при отключении которого практически гарантировано произойдёт разделение сети, ведёт себя в случае со случайными алгоритмами как percolation threshold. Больше математики!
- (*, *, pushpull) в среднем лучше по всем показателям, кроме восстановления при отказах.
- (rand, head, *) создают полное покрытие быстрее и равномернее, чем аналоги. Сеть, построенная такие алгоритмом, сложнее всего располовинить.
- (tail, *, *) немного больше случайности и немного медленнее сходимость чем другие. Но с tail надо быть осторожным, так как например (tail, rand, push) медленно увеличивает количество мёртвых нод, потому что рано или поздно в списке соседей остаются только самые дальние ноды.
🔥1
Введение в семейство алгоритмов Gossip (4/4)

Кажется осталось только поговорить о самом главном... Где, собственно, gossip используется, а где лучше не стоит. Для начала практические применения:

- Распространение обновления данных по сети (data dissemination). Самое банальное и логичное применение. Ещё когда слова gossip не было, эпидемические алгоритмы использовались как раз для распространения данных.
- Создание топологий. Один из интересных подходов — сначала построить по сети случайное дерево, и пускать обновления уже по нему. Наличие фиксированной структуры резко увеличивает скорость сходимости, так как алгоритм фактически может стать детерминированным, вместо вероятностного. В некоторых сетях важно просто иметь более-менее актуальную информацию о топологии, например в сети маломощных устройств, коммуницирующих через какой-нибудь wifi. Для слабых устройств gossip очень хорошо подходит ещё и из-за малого размера сообщений и нагрузок на сеть.
- Мониторинг доступности. Gossip можно использовать для пресловутого failure detection. Вообще мониторинг доступности в распределенных системах — это большая тема, в которой не всё так однозначно. Отложим её до одного из следующих циклов.
- Подсчёт агрегатов. Внезапно, gossip подходит и для поиска сумм, средних и прочих агрегатов. Работает это за счёт немного хитрых алгоритмов и передаваемой в сообщении полезной нагрузки. Снизу приложу статью почитать поподробнее.
- Resource allocation. Gossip можно применять, чтобы отсортировать всё узлы в большой сети по некоторому показателю (например cpu, memory, network). Такая сортировка подходит для решения задачи: «как разделить X задач по Y машинам, чтобы всем хватило ресурсов»

Но не все так гладко. Поскольку gossip «медленно и верно» докатывается до сходимости, есть ситуации, в которых следует подумать о целесообразности его использования:

⁃ Большое количество изменений, каждое из которых распространяется по протоколу. Всё дело в том, что полное распространение происходит за O(log(n)). Если в системе живут одновременно несколько событий, они и распространяются одновременно. Либо сразу все вместе. В первом случае, это много трафика, во втором, сообщения большого размера. И то, и другое — вещи, которых gossip пытается избежать.
⁃ Не подходит для быстрой синхронизации данных, так как сама суть gossip в том, что раунды происходят редко, по сравнению со временем сетевых задержек.
⁃ Любые гарантии в gossip имеют вероятностный характер. Иногда таких гарантий может быть недостаточно, особенно когда важна производительность высоких процентилей задержек.
⁃ Мало информации о том, как gossip работает в системе, предполагающей Byzantine fault. Всё-таки это система, основанная на том, что соседние ноды не пытаются вредить.
⁃ В маленьких сетях с редкой сменой количества участников броадкаст/алгоритмы консенсуса могут работать и быстрее, и эффективнее.
⁃ Если gossip используется в качестве механизма уменьшения энтропии, нужно всегда отдавать себе отчёт, почему энтропия возникла. Бороться нужно с причиной, а не следствием. Gossip подходит как механизм для обхода редких отказов детерминированного алгоритма. Как магическая пуля для исправления любых отказов даже он не справиться.

Источники:
- Про плюсы и минусы: Birman, Ken. 2007. "The promise, and limitations, of gossip protocols"
- Про peer sampling service: Mark Jelasity, Rachid Guerraoui, Anne-Marie Kermarrec, and Maarten van Steen. "The Peer Sampling Service: Experimental Evaluation of Unstructured Gossip-Based Implementations".
- Где используется gossip: Kermarrec, Anne-Marie, and Maarten van Steen. "Gossiping in distributed systems."
- Про агрегаты на gossip: David Kempe, Alin Dobra, and Johannes Gehrke. "Gossip-Based Computation of Aggregate Information."
🔥2
Впервые в жизни зарегистрировался на серьёзное мероприятие с кучей статей: https://www.hotstorage.org/2021/attend.html

Программа тут. Всё бесплатно, приходите тоже!

Ожидание: ничего не пойму.

Как минимум один пейпер будет не адово хардкорный. Почему важно измерять не только абсолютную производительность, но и эффективность на примере алгоритмов консенсуса. Краткое содержание есть в статье: http://charap.co/scalable-but-wasteful-or-why-fast-replication-protocols-are-actually-slow/
🔥1
FoundationDB: A Distributed Unbundled Transactional Key Value Store

#shark_whitepaper

Пока я пытаюсь осилить ARIES, немного ненапряжного и очень свежего case-study от июня 2021 года. Работа про распределенное KV хранилище FoundationDB. Всех авторов перечислять не буду, так как это 21 человек. Оригинал можно почитать по первой ссылке здесь.

FoundationDB (FDB) была разработана более 10 лет назад, и с тех пор используется в Apple, как одно из основных хранилищ данных, а с 2017 года проект доступен на github. Это NewSQL хранилище, т.е. помимо гибкости и средств для масштабирование, свойственных NoSQL решениям, FDB также поддерживает транзакционность аж до strict serializability, но не без ограничений, конечно же.

Обычно базы данных предоставляют все свои компоненты в виде одного процесса. Т.е. условный инстанс postgres — это одновременно и транспортный слой, и оптимизатор запросов, и execution engine, и storage engine. FDB идёт другим путем, используя опыт современных cloud-first подходов к созданию хранилищ данных. Она основана на unbundled architecture, т.е. база данных — это не один процесс, а несколько взаимодействующих процессов, каждый из которых может быть запущен на отдельном сервере и масштабироваться независимо. Такой подход позволяет подстраивать БД под профиль нагрузки по чтению и записи.

FoundationDB позиционирует себя, как фундамент, поверх которого можно реализовать дополнительную логику. Например, на основе FDB работает и графовый движок JanusGraph, и record-layer — очень упрощенный вариант реляционной БД, и даже прямо сейчас происходят попытки перевести CouchDB на FDB.

FDB логически состоит из двух слоёв: управляющий слой (Control Plane) и слой данных (Data Plane). Слой данных дополнительно делится ещё на три компонента: Transaction System (TS), Log System (LS) и Storage System (SS).

При чтении, клиент запрашивает версию записи в TS, а потом идёт напрямую в Storage System. При записи, запрос попадает в TS, который обеспечивает транзакционность через сочетание optimistic concurrency control (OCC) и multi-version concurrent control (MVCC). Всё происходит lock-free прямо в памяти. Далее запрос записывается в Log System, где хранится Write-Ahead Log (WAL), после чего ответ возвращается клиенту. Обновления WAL затем агрессивно считываются компонентами Storage System. Сейчас SS представляет собой инстансы SQLite (а я думал она нигде не используется...), но в планах заменить их на RocksDB. Кстати период MVCC насильно установлен в пять секунд. По словам авторов, это заставляет клиентов больше думать над тем, как правильно составлять запросы, что в целом звучит как правильный путь для OLTP хранилищ. Скалировать много маленьких запросов проще, чем подстраиваться и под тяжёлые, и под легкие запросы одновременно.

Ещё одной особенностью FDB является процесс восстановления при сбоях, называемый реконфигурацией. Когда Control Plane обнаруживает ошибки в TS/LS или просто попытку развернуть новую версию БД, он просто останавливает старые инстансы и запускает новые. Весь процесс занимает до пяти секунд. Это позволяет использовать реконфигурацию как для обновлений, так и для восстановления при ошибках. Более того, настолько быстрый процесс восстановления — то что доктор прописал для нестабильной cloud-based среды.

И последняя убер фича FDB — это simulation фреймворк, который писался ещё до начала работы над самой БД. Дело в том, что тестировать базы данных довольно тяжело. Здесь часто не помогают просто «юнит-тесты», так как проверить, что база всегда выполняет свои гарантии консистентности или durability очень сложно. Количество кейсов практически неограниченно. Симуляционный фреймворк позволяет производить сотни запусков компонентов БД в минуту, вносить произвольные ошибки на разных уровнях (сеть, диски, запросы и т.д.) и проверять, работает ли FDB, как ожидается. Более того, неудачные запуски всегда можно переиграть, так как сам фреймворк детерминированный.
🔥1