Автор выражает благодарность людям, которые помогли в написании статьи:
И в особенности Махешу Балакришнану, к числу почитателей которого я присоединился:
Ничто как фундаментальный архитектурный подход
Какая программа может решить проблему остановки в общем виде? Что поможет сделать успешный стартап по созданию убийцы гугла с бюджетом $100k? Что позволит реализовать масштабируемую систему на микросервисах быстрее и дешевле, чем монолит? Какая БД позволяет обеспечить сразу три гарантии теоремы CAP? Что позволит удовлетворить типичные взаимно противоречащие требования от клиента? Знакомьтесь: Ничто — самый востребованный продукт на рынке IT, и в условиях короновируса возник острый дефицит кадров, способных делать ничего.
Вы не умеете ничего делать? Тогда вы в пролёте, рынку нужны люди, которые умеют делать ничего. Рынок нуждается в людях с богатым коммерческим опытом успешной реализации ничего и хорошей репутацией, со стабильным отсутствием оригинальных идей, которое они способны предложить ищущим стабильности в условиях постоянно меняющегося рынка. Этот спрос не безоснователен: ничто изначально имеет серьезное конкурентное проимущество над многими альтернативными решениями, ведь, как правило, ничто не требует большого объема ресурсов на реализацию.
В каком-то смысле вероучение Agile идет рука об руку с другими религиями и с ничем, поскольку имеют своей целью не какие-то там малопонятные технические штуки, а простую удовлетворенность человека (заказчика и исполнителя). Как там говорят заповеди Agile: "Customer satisfaction by early and continuous delivery of valuable software". Позитивный настрой — это главная фича моего ничего, то настроение, с которым я доставляю свой софт, и та ценность, с которой я его преподношу, значит больше, чем сам софт. У чего-то есть недостатки, но у ничего смысла столько же, сколько во мне самом — вот она, свобода творца выбирать свой мир.
Минутка проповеди
"Куда идем мы с пятачком? Большой большой секрет. А смысл жизни нашей в чем? А смысла в жизни нет". Культура Индии, одной из старейших цивилизаций Земли, тесно переплетена с религией и давным-давно пришла к тому, что нет никакого "совершенствования" и "развития": нет развития, потому что вне человека нет цели для развития и нет объективных критериев развитости (с точки зрения амёбы клетки человека намного примитивнее); нет совершенствования, потому что совершенство — это смерть, неизменяемое совершенное состояние материи.
Однако же, мы можем из ничего выдумать цели, критерии развития, что в русском фольклоре называется "не было у бабы хлопот, так купила баба порося". Что такое современная глобальная экономика? Идущая вразнос система, направленная на максимизацию выработки отходов и преумножения несчастья субъектов экономики для того, чтобы убедить их производить больше отходов и таким образом осчастливиться. Но вы же не скажете, что покупаете айфон для того, чтобы создать больше мусора на земле? Нет, вы скажете что-то вроде "я хочу выразить свою индивидуальность", или какую-то другую бессмыслицу, вроде "этим устройством удобно пользоваться". Объективное сравнение с другими вариантами никого не волнует, поскольку такая жизнь была бы скучна, да и, к тому же, никакой объективности не существует — лучше то, что я считаю лучшим.
Индусы одними из первых сообразили, как коммерциализировать невидимое и неощущаемое, торговать желаниями, задолго до иудеев и христиан. Более того, воображаемые вещи могут стать более ценными, чем ощущаемые вещи, а эфемерное чудо тысячами лет притягивает своим таинством человека, уставшего от обыденности бытия.
Но вернемся к разработке ПО
В прошлой статье я наплевал на опыт тысячелетий и наивно высказал глупость, смысл которой очень упрощенно такой: ничто не может быть лучше ZooKeeper. Да, признаю: ничто может быть лучше ZooKeeper. По крайней мере, разработав ничто можно сделать что-то новое, а оставаясь с ZooKeeper вы будете иметь старую унылую систему, которая просто работает. Бизнес хочет выше, быстрее, сильнее, хотя бы по сравнению с конкурентами, и ваша обязанность, как программиста и архитектора — эту потребность удовлетворить.
Клиент думает, что ему для процветания бизнеса ничего не нужно? Ваша задача — убедить, что без ничего он не сможет прожить и дня. И для этого нужно прежде всего убедить себя в том, то ваше ничто кому-то нужно. Когда владельцу бизнеса/продукта ничего не нужно масштабировать, то ваша задача — убедить его, что ничто обязательно должно быть смасштабировано на Kafka, Hadoop, и микросервисах. От такой реализации будут довольны все, как показывает практический опыт. Теперь вы примерно начинаете догадываться, почему индус стал руководителем Microsoft — мудрость поколений, въевшуюся в гены, не пропьешь, никакой Гейтс или Балмер не годятся в подмётки Сатье.
Как Facebook разработал ничто
Как некоторые из вас знают, не так давно Facebook решил поручить моему гуру, опытному исследователю Махешу Балакришнан, перевод своей инфраструктуры с ZooKeeper на новое кастомное решение. Изначально задание было поставлено примерно такое:
Rethinking monolithic system design
...we needed durability, strong consistency, and high availability. We also needed a rich and flexible API:… a relational model with support for transactions, secondary indices, and range queries,… other APIs, such as hierarchical namespaces and queues. In addition, each use case demanded different performance and reliability guarantees. Finally, we needed to incrementally develop and deploy the system over a staggered timeline: We needed to deploy the system within a few months… we also needed to support the high transaction rates and low latency...
Высокая производительность, отказоустойчивость, строгая согласованность данных, при этом с потенциальной поддержкой разных уровней согласованности данных и отказоустойчивости, неограниченная гибкость формы данных и API — и всё это должно быть выкачено в прод за несколько месяцев. Это идеальное задание для разработки Ничего: Virtual Consensus in Delos. Детали реализации вы можете прочитать в самой статье — я сделаю лишь краткий обзор ключевых свойств и назначения этой системы.
Слой материализации данных (Database в статье) — фиговина, которая применяет операции из распределенного лога к локальной копии данных, то есть, формирует видимые извне данные. Первая крупная БД подобного типа, Google Chubby, всем своим интерфейсом предписывала применение иерархичной структуры из мелких файлов, запись их содержимого только атомарно и целиком, то есть, оптимизацию под хранение управляющих структур, а не самих данных. Что нам предлагает Delos в качестве низкоуровневого интерфейса? Правильно — ничего, то есть, свободу выбора и позитивный взгляд в будущее:
Delos: Simple, flexible storage for the Facebook control plane
Flexible replicated systems: New materializations above the VirtualLog can provide different abstractions for storage such as a relational model, a key-value store, or a file system API.
DelosTable offers a rich API, with support for transactions, secondary indexes, and range queries
Пессимистичные душнилы скажут "да что же вы не можете наконец определиться с низкоуровневым представлением данных, и называете это фичей?" — но мы их не слушаем, они сами выбрали себе ярмо на шею. Душнила бы сказал, что при желании можно реализовать O(N^2) составные транзакции над табличками и на ZooKeeper, но для чего будет приемлема такая нагрузка на ключевую БД всей инфраструктуры Facebook? Правильно — для ничего, а это и является нашей основной целью разработки, не забываем.
VirtualLog с MetaStore — это распределенный отказоустойчивый роутер, который знает, какой из логлетов (хранилище логов) содержит самые последние изменения и доступен на запись, а в каких логлетах архивные транзакции доступны лишь на чтение. VirtualLog роутит запросы append/readNext (запись/чтение) таким образом, что все логи из логлетов выглядят конкатенированными, а доступный на запись активный логлет находится в хвосте этого конкатенированного лога. MetaStore — это атомарный распределенный счетчик, в котором записан ID активного логлета (commit ID, но это не важно), то есть, MetaStore — основной механизм координации экземпляров VirtualStore-роутера. Поскольку этот счетчик меняется только при отказах, то его реализовали простым медленным Paxos.
Delos: Simple, flexible storage for the Facebook control plane
The VirtualLog can dynamically switch between different SimpleLogs to obtain different performance and fault-tolerance properties.
Сразу упреждая вопрос "а что же в этой системе нам позволит перейти от VirtualLog к прямому соединению с ZooKeeper или на другую БД?". Вы уже догадались — ничто, оно всегда приходит на помощь в трудную минуту.
Посмотрим на устройство NativeLoglet, раздел 4.2.1.
Each Delos server – in addition to running the materialization logic and the VirtualLog code – runs a NativeLoglet client and a NativeLoglet server (or LogServer). One of the Delos servers also runs a sequencer component…
Each LogServer locally commits a particular log position n only after the previous position n — 1 has either (1) been locally committed on the same server, or (2) has been globally committed on a majority of servers (i.e., knownTail > n — 1)…
The sequencer,… considers the append globally committed (and acknowledges to the client) once it receives successful responses from a majority of unsealed LogServers
Как вы можете заметить, это по сути репликация ZAB/Raft в комбинации с ничем в роли механизма обнаружения отказов и возобновления работы — операцией seal, которая навсегда запечатывает логлет, переводя его в режим "только чтение". Обнаружение отказов находится в неопределенном месте вне системы, при отказе текущий логлет запечатывается и запись переводится на другой логлет (забегая вперед — ZooKeeper).
3.2. VirtualLog design
individual Loglets that do not implement their own leader election or reconfiguration are responsible for detecting failures and requesting a reconfigExtend on the VirtualLog, as we describe later4.2.1 Loglets sans consensus: NativeLoglet:
When the sequencer or one of the LogServers fails, the NativeLoglet is responsible for detecting this failure and invoking reconfiguration on the VirtualLog (which in turn seals it and switches to a new NativeLoglet)и в конце этого раздела:
In our implementation, we use a combination of in-band detection (e.g., the sequencer detecting that it has rebooted, or that other servers are persistently down) and out-of-band signals (via a gossip-based failure detector, as well as information from the container manager) to trigger reconfiguration6. Discussion:
If two converged replicas crash out of five, another failure can cause unavailability and data loss for the log. In this situation (which was not uncommon), we found it valuable to reconfigure the system to a disaggregated log, temporarily decoupling the fate of the database and the log. Once the database was restored to five replicas, we reconfigured back. This style of temporary decoupling also proved valuable when we discovered latent bugs in the NativeLoglet; we reconfigured to ZKLoglet, rolled out hotfixes, and then reconfigured back.
Как вы можете увидеть, на более высоком уровне в качестве механизмов обнаружения отказов используется модифицированный вариант ничего — непонятно что, работающее по неясным алгоритмам. Последняя цитата по сути означает, что при срабатывании непонятно чего VirtualLog переключает хранение логов на ZooKeeper, поскольку NativeLoglet, как упомянуто выше, не способен работать в условиях систематических сбоев.
Надо заметить, что наш любимый Яндекс долгое время тоже применял непонятно что в качестве механизма реакции на отказы, но на полпути сдрейфил и начал использовать ZooKeeper для всех задач координации.
Одно из заявленных достижений проекта Delos — уменьшение времени ответа БД. Мой гуру безупречно реализует это ничто, за ширмой оно выглядит так: берется, ZooKeeper, поверх него строится слой трансляции (ZKLoglet), проводится нагрузочное тестирование ZKLoglet против NativeLoglet — NativeLoglet отвечает в 10 раз быстрее. Эту безупречную картину портят мерзкие душнилы-соавторы статьи, раздел 5.1 The Benefit of Virtualization:
The latency improvement is largely due to the unoptimized nature of our ZKLoglet implementation, which simply writes a new sequential ZooKeeper key on each append.И раздел 5.2 The Cost of Virtualization:
Figure 11 (Right) shows that ZooKeeper can provide over 30K puts/sec before p99 latency degrades beyond 15ms. In contrast, Delos+NativeLoglet manages around 26K puts/sec. The primary reason for the performance difference is that ZooKeeper stores its materialized state in memory while Delos uses RocksDB. We also ran a version of Delos where the materialized state lives in memory; this prototype hit 40K puts/sec.
То есть, они хотят сказать, что время ответа у Delos такое же, как у прямого доступа к ZooKeeper. Ну как с такими работать?
Жирную точку в архитектуре ставит абзац "ZooKeeper over Delos… over ZooKeeper" раздела 6 Discussion, где архитектура системы становится примерно такой:
Black-box Concurrent Data Structures for NUMA Architectures
Как вы понимаете, я признал Махеша своим гуру не за один единственный проект. У Махеша есть публикация на ту тему, которая мне очень близка, поскольку подобную цель я преследовал в Python Shared Objects — создание универсального механизма параллелизации алгоритмов. Эта публикация получила Best Paper Award, став кульминацией многих лет совершенствования навыка созидания ничего.
Суть алгоритма заключается в том, что вместо классического поочередного выполнения операций непосредственно над общей памятью под глобальной RW блокировкой автор предлагает каждому NUMA-узлу иметь независимую (приватную) копию "общих данных", а пересылать между NUMA-узлами только команды по изменению этих данных через лог в разделяемой памяти, чтобы каждый NUMA-узел уже на приватной копии данных строго последовательно выполнял эти операции из лога. То есть, общаемся между NUMA-узлами не через общие данные, а через общий лог операций. Добавление пишущих операций в лог очевидно просто: зарезервировать диапазон ячеек в конце лога через атомарный счетчик, записать содержимое в ячейки этого блока.
Но и такой алгоритм работы с логом был бы неэффективен на NUMA-системе. потому гуру сделал оптимизацию — сбор операций с нескольких потоков NUMA-узла в одну пачку, чтобы таким образом уменьшить число запросов между NUMA-узлами. Гуру адвокатирует flat combining для организации пакетирования, хотя здесь мог быть использован совершенно любой механизм блокировок в рамках одного NUMA-узла.
Вторая оптимизация (раздел 5.4) — операции чтения, которые выполняются на приватной копии данных без взаимодействия с другими NUMA-узлами, читатель лишь по необходимости проматывает локальную копию до последнего корректного элемента общего лога, видимого при начале работы читателя, то есть, выполняет над локальными данными все известные операции из лога. Таким образом получается что-то между read committed и snapshot isolation, с вытекающими проблемами в виде чтения старых значений.
Предлагаемый алгоритм по сути является NUMA-aware аналогом RW-блокировки: операции записи глобально сериализованы, операции чтения независимы. Забавно, что соавтор этой статьи, Irina Calciu, также являлась соавтором статьи моего любимого Нир Шавита: NUMA-Aware Reader-Writer Locks. В связи с чем передаю Ирочке пламенный поцелуй, а Нир Шавит пусть лучше старается — авось тогда и его кто-то заметит, кроме меня.
Да, предлагаемое моим гуру ничто на немногочисленных сравнениях с lock-free алгоритмами (и с STM, на самом деле) показывает примерно никакую масштабируемость. Но давайте не забывать, что самое главное в проектировании ничего — это позитивный настрой и удовлетворенность клиента. Потому ничто нужно сравнивать с ничем, то есть, c простыми алгоритмами без поддержки NUMA.
Самое выдающееся достижение моего гуру в данной статье — это, конечно же, заявка на ускорение Redis дописыванием всего-лишь 20 строчек кода в исходники Redis для интеграции новой библиотеки. Да, эти 20 строчек кода вводят поддержку многопоточности лишь в две из сотен доступных в Redis команд, а также в бенчмарках гуру применил ничто в качестве сетевого интерфейса (поскольку иначе сеть бы занимала большую часть времени обработки команд). Но заявка красивейшая ведь, согласитесь, Best Paper Award она получила заслужено за подачу RW-блокировки под видом универсальной lock-free конструкции, а вы уже захотели скачать патчи на Redis с поддержкой этой многопоточности, и даже захотели что-то подобное реализовать для своей софтины. Помним: удовлетворенность хотелок клиент — высший смысл разработки софта.
Но это всё мелочи. Самые ценные советы я приберег напоследок...
42 things I learned from building a production database
Да, вы можете душнить по технической части, мол "советы слишком общие" или "слишком конкретные и относятся только к его проекту", но давайте вспомним, что перед нами гуру разработки ничего, и именно в этом ключе его советы бесценны.
[1] Keep your customers happy; else the rest of this document doesn’t matter.
[3] Interface directly with customer ICs. A lot of intra-team conflict can be resolved by saying “I talked to the customer just now and they said…”. In infra we often don’t need to speculate about what customers want.
[4] But realize that customers may not express what they really need; don’t take requirements at face-value, instead spend the time to understand their use case in detail. Read their code.
[31] Ask yourself on some cadence: why does the team/project exist? If it didn’t exist, what would happen (which other team / system would fill the gap)? How is the team adding value to the company and how can it continue doing so in the future?
Всегда понимайте, что продавая ничего вы должны удовлетворить хотелки заказчика, а не выдать эффективное решение. Потому мнение душнил-исполнителей никого не волнует, а взаимоисключающие требования заказчика подлажат тщательной интерпретации.
[5] Have a simple, crisp mission statement that expresses your raison d’etre. For Delos it was: we will be a reliable foundation for FB infra
[8] A road-map is a means, not an end
[9] If you get good and/or aligned managers, be as understanding, supportive, and accommodating as you can. If you don’t get such managers… well, I haven’t figured this one out, let me know if you do.
[10] Make your project robust to re-orgs. A company management hierarchy is inherently fragile; socialize the project continuously with managers who might take over in the future....
Ничто должно иметь больше позитива в своем фундаменте. Мы не знаем что делаем, но мы точно знаем, какую реакцию руководства хотим получить — она может быть только позитивной. Даже если это руководство меняется.
[10] ...Do whatever it takes to make sure that manager churn does not result in unfair career outcomes for ICs
[19] Late-bind to designs: encourage the team to think about the entire design space without committing to a particular point solution. Running brainstorming meetings with a bunch of high-IQ, opinionated ICs is an art worth mastering.
[24] Create a culture where ICs are constantly thinking about radically different designs; do not shut down conversations about hypothetical alternative designs. Encourage curiosity.
[32] Keep track of every other major project in your space within the company: you should be able to explain their technical design better than their own ICs. Grab any opportunities to debate scope with the leads of other similar projects: you should be able to articulate how your project fits into the larger ecosystem of options. Inter-team competition is healthy and necessary. Make friends with ICs in these projects.
Особенно для достижения этой цели важно иметь единомышленников среди исполнителей и даже в соседних проектах.
[7] Task allocation to ICs is critical; ask to be in the critical path of any decision, because you typically have a much better understanding of the problem, the codebase, and the IC’s strengths than the manager.
[16] Design as a team; implement as individuals. This will make design the bottleneck, but it’s worth it: push back on impulses to parallelize design.
И боже упаси, чтобы отдельный исполнитель в вашей команде перестал делать ничего и ударился в какую-то техническую прагматику, за этим всегда нужно следить.
[33] Do not compete on raw performance or efficiency with other teams; this will escalate into an arms race where both teams waste time optimizing their systems for point workloads, generating apples-to-oranges comparisons, etc. Compete on fundamental design characteristics.
[35] Measurement is a means, not an end.
Помните: никогда не используйте объективную метрику для оценки проекта. Удовлетворенность заказчика является единственной метрикой успешности.
[34] If someone objectively has a better system for your use case and wants to take it on, go find something else to do.
Перевод: лучше моего проекта не может быть ничего, поскольку им занимаюсь я.
[36] You should be able to detect problems in your service before your customer does.
Даже если у вашего ничего всплыли какие-то проблемы в проде — заказчик должен быть последним, кто об этом узнает.
[41] Try new things. Bias towards novelty within the space of feasible solutions. Fight the impulse to copy designs verbatim. Every major system was just a half-baked idea in someone’s head at some point.
Даже если ты сам последние 6 лет в четырех разных проектах с минимальными изменениями и завидным упрямством реализуешь те же самые алгоритмы и структуры данных — говори, что постоянно ищешь что-то новое. Разработка ничего заключается в том, что мы говорим и какие эмоции в людях это вызывает, а не в том, что может сделать полученная вещь.
[42] Write papers. Writing for an audience that has zero context on what you are doing will force you to examine and clarify your assumptions. Papers make it easier to hire good people and to on-board them.
И помните: люди, которые не разбираются в вашей предметной области — это ваши ценнейшие союзники.
Минутка альтернативного техзанудства
Возможно ли разработать некую распределенную БД или систему координации так, чтобы она была расширяемой, простой в использовании, и при этом сама что-то из себя представляла, а не только очередную прокладку из ничего? Да, но это подразумевает смену парадигмы, как то отход от императивных операций над реляционными структурами: императивные операции над состоянием подразумевают строго пошаговое выполнение, что крайне плохо натягивается на многозадачность и распределенность; реляционные таблички подразумевают требование строгой согласованности между данными, что, опять же, плохо параллелится и распределяется.
По этой причине, например, вы можете увидеть такой рост популярности key-value и прочих NoSQL решений. Однако, по большей части это стремление к смене парадигмы ограничивается специализированными решениями для конкретной задачи, то есть, серьезной потерей гибкости и функциональности — это же относится ко многим параллелизуемым/распределенным key-value БД, которые поддерживают либо очень простые, либо специализированные операции.
Я уверен, что в Facebook прекрасно понимают существование этой проблемы, а также понимают, что, в нынешних условиях сокращения финансирования, ничто — это лучшее решение данной проблемы. Надеюсь, вам это понимание тоже поможет в вашем бизнесе.