Pull to refresh

Comments 42

Коротко: при создании сложных многопоточных, многозадачных, и распределенных систем грамотный подход и такие люди, как я, с большим трудом заменяются тестами и "большой дружной командой".

А мы я вижу от скромности не страдаем :)

А по теме - отличная статья. К концу все-таки потерял нить рассуждения, возможно у моего мозгового слизня упал уровень сахара.

Вопрос - насколько реально легко использовать вещи вроде Coq для доказательства отсутствия дедлоков? Или это вообще не из той степи?

А мы я вижу от скромности не страдаем

Сам себя не похвалишь - никто не похвалит.

Про попытки сделать автоматический доказыватель корректности многопоточной программы я слышал, но я слабо верю в их применимость к задачам сложнее обедающих философов, поскольку достаточно сложный и/или несериализуемый (https://en.wikipedia.org/wiki/Serializability) алгоритм может легко улететь в бесконечную сложность при наивном анализе выполнения. В начале статьи я упоминал пример с простыми четырьмя операциями и циклом из четырех инструкций, но в реальной задаче это может быть два бесконечных цикла с условиями внутри, для которых уже теоретически невозможно реализовать алгоритм, который хотя бы оценил конечность выполнения этих циклов (https://ru.wikipedia.org/wiki/Проблема_остановки), не говоря уже про корректность операций, которые будут в этих циклах исполняться. По этой причине эдак половина ноши "доказательства корректности" прежде всего заключается в том, чтобы не писать некорректные алгоритмы, которые никто не сможет проанализировать. То есть, не наращивать сложность алгоритма без причины.

А мы я вижу от скромности не страдаем :)

Это так. Оставим скромность неудачникам - она их здорово украшает.

Из словаря Ожёгова

СКРО́МНЫЙ ...

3. перен. Небольшой, ограниченный, едва достаточный. Скромный. заработок. Весьма скромный результат.

Фамилия лингвиста ведь Óжегов.

А скромности (где про тесты и команду) прибавляет bus factor.

Спасибо за замечание. Именно так - с ударением на первом слоге. Каюсь - всю жизнь произносил эту фамилию неправильно - и ведь никто, зараза, не поправил. И не подскажете ли - как правильно, Рерих или Рёрих?

Вопрос - насколько реально легко использовать вещи вроде Coq для доказательства отсутствия дедлоков? Или это вообще не из той степи?

Если кратко, то нелегко. Доказать отсутствие дедлоков с помощью Coq можно. Но, наверное, более сложный вопрос не в том, можно верифицировать или нет, а в том, как связать верификацию и конкретную реализацию. Один из способов это сделать экстракцию формально верифицированного кода из Coq в OCaml или Haskell и интегрировать в основной проект.

Один из способов это сделать экстракцию формально верифицированного кода из Coq в OCaml или Haskell и интегрировать в основной проект.

Неужели Coq поможет доказать корректность кода, в котором мы хотим реализовать, например, атомарные ячейки памяти Data.Atomics?

https://hackage.haskell.org/package/atomic-primops-0.8.4/docs/Data-Atomics.html

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

Остаётся открытым вопрос, зачем Кальвину Zookeeper.

Остаётся открытым вопрос, зачем Кальвину Zookeeper.

Я уже не стал вскрывать эту тему. Вы молодые, шутливые, вам все легко. Статейка-то была про многозадачность, я и так сильно растянул про распределенные системы.

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

Задачу же работы со строго согласованным состоянием с гарантиями отказоустойчивости и приемлимой производительностью во всей индустрии умеют решать ровно три софтины -- Zookeeper, Etcd, и Google Chubby. Конечно, может быть есть еще какое-то проприетарное решение, которое публике не показывают.

На счёт молодости можно поспорить, но я бы лучше поспорил на счёт Zookeeper. Не нужен там zookeeper. Тот компонент, который упорядочивает транзакции, называется секвенсер, и он может быть монолитным, т. к. не обязан быть отказоустойчивым. В референсной реализации кальвин-протокола секвенсеров много, они независимы друг от друга, а упорядоченность достигается синхронизацией часов и пакетированием транзакций по 10 мс.

Если есть zookeeper, то никакой кальвин уже не нужен.

В референсной реализации кальвин-протокола секвенсеров много, они независимы друг от друга, а упорядоченность достигается синхронизацией часов и пакетированием транзакций по 10 мс.

Советую читать оригинальную статью, а не буклетик по первой ссылке из гугла:

http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf

At the end of each epoch, all requests that have arrived at a sequencer node are compiled into a batch. This is the point at which replication of transactional inputs (discussed below) occurs.

Calvin currently supports two modes for replicating transactional input: asynchronous replication and Paxos-based synchronous replication

In asynchronous replication mode, one replica is designated as a master replica, and all transaction requests are forwarded immediately to sequencers located at nodes of this replica

Calvin also supports Paxos-based synchronous replication of transactional inputs. In this mode, all sequencers within a replication group use Paxos to agree on a combined batch of transaction requests for each epoch. Calvin’s current implementation uses ZooKeeper

Забавно, как авторы Calvin, ровно как и люди из Яндекса, которые на презентациях рассказывали про YandexDB, упорно маскировали тот факт, что вся их разработка по сути является небольшой надстройкой над Zookeeper.

Если есть zookeeper, то никакой кальвин уже не нужен

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

Я, конечно, статью ещё раз перечитаю.

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

А во-вторых я не понимаю, зачем там paxos-based (ну или zab-based) репликация, если можно заранее договориться, в какой последовательности идут транзакции от разных секвенсоров.

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

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

А во-вторых я не понимаю, зачем там paxos-based (ну или zab-based) репликация, если можно заранее договориться, в какой последовательности идут транзакции от разных секвенсоров.

Да, можно. Но кто потом будет публиковать эту последовательность? Допустим, публикация происходит "все ко всем". Представь, что во время публикации один из узлов отказывает. К части узлов транзакции с него дошли, к другой части -- нет. Что делать? Нужен консенсус, то есть, одинаковый набор команд на всех узлах кластера. Как делать консенсус? Либо назначить один из узлов Calvin эталонным, и таким образом потерять отказоустойчивость, либо применять внешнее решение для распределенного консенсуса -- а это и есть ZooKeeper.

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

Мы точно про один и тот же кальвин говорим? Что такое «входной запрос»?

Кальвин – это чистый key/value, входной запрос там равен данным.

Дополнительную ценность система приобретает, когда кто-то (например, SQL engine в YandexDB) преобразует какой-то осмысленный запрос в последовательность изменения ключей, но и там реплицируется именно эта последовательность, а не SQL. Репликация запросов – это очень стрёмная штука, мне известна реализация только в MySQL. Все остальные реплицируют изменения данных с ключами (любой CDC).

К части узлов транзакции с него дошли, к другой части -- нет. Что делать?

Это проблема того секвенсера, до которого изменения не дошли. Он знает, что у него должна быть последовательность A1 B1 C1 A2 B2 C2 и т. д. Если B2 почему-то нет, можно поинтересоваться у секвенсера B, что у него там было под номером 2?

Raft и прочие ZAB'ы нужны только лишь потому, что никто заранее не знает, что будет в логах. Секвенсеры в кальвине знают это заранее, поэтому им проще. Но конечно, zookeeper туда воткнуть можно – примерно так же, как в наручные часы можно воткнуть атомный механизм. Точно, надёжно, понтово – но не нужно.

Мы точно про один и тот же кальвин говорим? Что такое «входной запрос»?

Кальвин – это чистый key/value, входной запрос там равен данным.

Ну в мире key-value СУБД запросы не ограничиваются простым "запросить по ключу" и "установить по ключу". Потому что в таком случае, как я уже согласился, можно взять ZooKeeper и не морочить голову.

Он знает, что у него должна быть последовательность A1 B1 C1 A2 B2 C2 и т. д. Если B2 почему-то нет, можно поинтересоваться у секвенсера B, что у него там было под номером 2?

Поинтересовался -- получил таймаут, дальше что? А я скажу, что дальше: на части узлов транзакции B2 есть, а на другой части -- нет. Всё, приехали.

Raft и прочие ZAB'ы нужны только лишь потому, что никто заранее не знает, что будет в логах. Секвенсеры в кальвине знают это заранее, поэтому им проще

Заранее? Схема на Figure 1 говорит, что заранее про транзакции знает лишь один набор узлов -- реплики не знают ничего.

А что же ещё может быть в мире key-value СУБД? Особенно если учесть, что даже в мире реляционных СУБД логическая репликация основана на подходе "установить по ключу"?

получил таймаут, дальше что

Подождал, спросил ещё раз. Совершенно не вижу проблемы собрать кластер высокой доступности для секвенсера.

заранее про транзакции знает лишь один набор узлов

в наборе узлов один секвенсер. И этот секвенсер заранен знает не весь набор транзакций, а последовательность сообщений от других секвенсеров. Этого достаточно.

А что же ещё может быть в мире key-value СУБД? Особенно если учесть, что даже в мире реляционных СУБД логическая репликация основана на подходе "установить по ключу"?

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

Подождал, спросил ещё раз. Совершенно не вижу проблемы собрать кластер высокой доступности для секвенсера.

Один узел сдох. Совсем. Или даже целая реплика пропала, потому что роут к датацентру потерялся. Разве не в этом смысл высокой доступности?

в наборе узлов один секвенсер. И этот секвенсер заранен знает не весь набор транзакций, а последовательность сообщений от других секвенсеров. Этого достаточно.

От каких "других секвенсоров" он будет знать последовательность в условиях отказов? Я и пишу -- от каких-то случайных, случайное подмножество последовательности операций. Без отказов мне ZooKeeper не нужен -- я просто кину SQLite базу на один комп и на этом проблемы мои закончатся.

транзакций, затрагивающие сразу несколько шардов

Мы точно о разных кальвинах. Какая разница, сколько шардов затрагивает транзакция? Когда идёт двухфазная фиксация, это важно, когда кальвин – никакой разницы вообще.

Или даже целая реплика пропала, потому что роут к датацентру потерялся.

Ну да, согласен, в этом случае надо всем договориться о том, какая была последняя эпоха у пропавшего секвенсера. Для этого действительно проще всего взять готовый Zookeeper. Но из-за этого считать всю систему «надстройкой над Zookeeper’ом» – явное преувеличение.

Без отказов мне ZooKeeper не нужен

Вот и я об этом. Баллонный ключ должен лежать в багажнике на случай прокола колеса, но это не повод называть весь автомобиль «надстройкой над баллонным ключом».

Мы точно о разных кальвинах. Какая разница, сколько шардов затрагивает транзакция? Когда идёт двухфазная фиксация, это важно, когда кальвин – никакой разницы вообще.

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

Для этого действительно проще всего взять готовый Zookeeper. Но из-за этого считать всю систему «надстройкой над Zookeeper’ом» – явное преувеличение.

Без ZooKeeper система теряет отказоустойчивость -- checked.

ZooKeeper составляет основную сложность системы и его ничем нельзя заменить -- checked.

ZooKeeper определяет производительность выполнения транзакций -- checked.

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

ZooKeeper составляет основную сложность системы и его ничем нельзя заменить -- checked.

ZooKeeper определяет производительность выполнения транзакций -- checked.

Нет. Это ниоткуда не следует.

Ещё раз. У нас есть несколько секвенсеров. Можно назвать их по алфавиту – A, B, C... Можно также договориться, что операции сортируются по номеру эпох, а внутри эпохи – по номеру секвенсера. То есть секвенсер A отправляет свои операции секвенсерам B и C, и никакого консенсуса не нужно, потому что никто, кроме секвенсера A, не может сгенерировать операции от секвенсера A. Никакого Zookeeper’а здесь нет.

Теперь посмотрим на ситуацию глазами секвенсера B. У его есть последовательность операций A42 B42 C42 A43 B43 C43... Он может выполнять все эти операции, ни с кем не согласовывая их выполнения. Никакого Zookeeper’а здесь нет.

Теперь, допустим, у секвенсера B последовательность A666 B666 C666 B667 C667 B668 C668... Видно, что секвенсер A пропал. Вроде как последней его эпохой была 666, но вот этого секвенсер B наверняка знать не может – то ли правда секвенсер пропал, то ли канал связи лёг. Тут необходим консенсус, и вот тут как раз в игру вступает Zookeeper.

Система функционирует нормально в течение 99.9% времени (ну если это не списанные серверы в деревенском сарае). В оставшиеся 0.1% система восстанавливается после сбоев. При восстановлении Zookeeper нужен, и скорость восстановления зависит от производительности Zookeeper. Но скорость нормальной обработки транзакций от Zookeeper’а никак не зависит.

Ну и опять же, «ничем нельзя заменить» – это тоже фантазия. Raft опубликован, бери и реализуй. Другой вопрос, зачем это делать, если есть готовая реализация в виде etcd. А Zookeeper – это даже не Raft, это просто «дело привычки». Вон у Oracle, например, есть DataGuard Broker, который умеет строить консенсус. Но для кластеризации TimesTen они взяли Zookeeper – просто чтобы не возиться.

То есть секвенсер A отправляет свои операции секвенсерам B и C, и никакого консенсуса не нужно, потому что никто, кроме секвенсера A, не может сгенерировать операции от секвенсера A. Никакого Zookeeper’а здесь нет.

У меня тоже складывается ощущение, что мы говорим про какие-то разные Кальвины. Но я перечитываю статью, и вижу подтверждение именно своего кальвина. Узлы в системе имеют два типа взаимоотношений: реплика и шард. То есть, вся БД разделена на несколько шардов с уникальными данными, а потом эти шарды реплицированы. Обычно реплики сидят в разных датацентрах, а шарды -- в одном.

Так вот, ни в каком из двух вариантов взаимоотношений нет ситуации "секвенсер A отправляет операции B, и никто кроме A не может их сгенерировать". Секвенсоры-шарды вообще друг-другу ничего не посылают -- они шлют операции планировщикам всех шардов одной реплики. Секвенсоры-реплики дублируют друг друга, они на равных создают пачки операций и согласовывают их -- весь раздел 2 был уделен обоснованию этого приема, то есть, система продолжает работать как ни в чем ни бывало при отказе одной из реплик. На самом деле такой бесшовный failover невозможен, это тот же "горячий пирожок", который превращается в очередную хитромудрую обертку над ZooKeeper, но я сейчас не об этом -- я о том, что всё создание системы было обосновано только отказоустойчивостью.

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

Система функционирует нормально в течение 99.9% времени (ну если это не списанные серверы в деревенском сарае). В оставшиеся 0.1% система восстанавливается после сбоев. При восстановлении Zookeeper нужен, и скорость восстановления зависит от производительности Zookeeper.

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

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

Ну и опять же, «ничем нельзя заменить» – это тоже фантазия. Raft опубликован, бери и реализуй. Другой вопрос, зачем это делать, если есть готовая реализация в виде etcd

Ха, "реализуй" -- легко сказать, но реализовать сильно сложней. По этой причине столько систем уже наклепали на базе ZooKeeper, но ZooKeeper по прежнему остается почти безальтернативным -- потому что он и составляет самую большую сложность разработки.

Вон у Oracle, например, есть DataGuard Broker, который умеет строить консенсус

Я не могу найти подтверждение того, что сам Data Guard Broker устойчив к отказам.

https://www.oracle.com/technical-resources/articles/smiley-fsfo.html

Здесь указывается, что функция автоматического принятия решения о fail-over производится некоторым внешним агентом. То есть, сам Data Guard Broker этого делать не умеет -- он может только выполнять команды извне. Причем, судя по инструкциям для ручной синхронизации ролей

https://docs.oracle.com/cd/E69294_01/html/E71432/gpxvm.html

я могу предположить, что рассинхронизированные конфиги Data Guard являются нормой.

Секвенсоры-шарды вообще друг-другу ничего не посылают -- они шлют операции планировщикам всех шардов одной реплики.

Ну ок, я немного упростил картину. Конечно, есть отдельные узлы-планировщики. Но общую ситуацию это не меняет.

Вы же понимаете, что для репликации никакой консенсус (и zookeeper) не нужен. Репликация хоть в MS SQL, хоть в Oracle хоть где прекрасно работает без zookeeper’а. Потому что лидер жёстко зафиксирован. Поэтому гонять все операции через Zookeeper – это, конечно, хорошее быстрое решение для академического проекта, но совсем не обязательное условие для коммерческого продукта.

Секвенсоры-реплики дублируют друг друга, они на равных создают пачки операций и согласовывают их

Зачем секвенсерам их согласовывать? Это совершенно лишнее.

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

  1. Транзакции упорядочиваются по номеру эпохи, а внутри эпохи – по номеру секвенсера.

  2. Вся эпоха, сгенерированная секвенсером, выполняется подряд, без прерывания.

Этих правил достаточно, чтобы на каждом планировщике последовательность транзакций была одна и та же, не надо ничего согласовывать.

работать как ни в чем ни бывало при отказе одной из реплик

Вот как раз при отказе одной из реплик всем надо договориться, какая последняя эпоха в этой реплике. Это единственное место, где нужен консенсус.

но ZooKeeper по прежнему остается почти безальтернативным

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

Вот Confluent захотел избавиться от Zookeeper – и избавился. Не до конца понимаю, зачем, но внезапно выяснилось, что так можно.

функция автоматического принятия решения о failover производится некоторым внешним агентом

Давно я не работал с Oracle, забыл фирменную продуктовую линейку :) Этот внешний компонент называется Observer и тоже входит в поставку Oracle RDBMS.

https://www.doag.org/formes/pubfiles/303232/2008-K-IT-Bracher-Dataguard_Observer_ohne_Rechenzentrum.pdf

Вы же понимаете, что для репликации никакой консенсус (и zookeeper) не нужен. Репликация хоть в MS SQL, хоть в Oracle хоть где прекрасно работает без zookeeper’а. Потому что лидер жёстко зафиксирован

Это что-то вроде "как попаду в аварию -- так обязательно пристегну ремень". Да, для репликации доступность распределенного консенсуса не обязателена и отказоустойчивость тоже не обязательна, но если нужно при отказе одного узла сохранять работоспособность без потери подтвержденных данных и без публикации неподтвержденных, то придётся "пристёгиваться" заранее. ZooKeeper, для справки, при нормальной работе тоже никаким распределенным консенсусом не занимается -- он работает именно в режиме синхронной репликации с одним стабильным лидером. Вся огромная сложность реализации ZooKeeper заключалась в том, чтобы идеально совместить эту высокопроизводительную репликацию с обработкой отказа. Именно при отказах вся архитектура БД и проверяется на прочность, а без отказов можно хоть SQLite реплицировать.

Транзакции упорядочиваются по номеру эпохи, а внутри эпохи – по номеру секвенсера...

Этих правил достаточно, чтобы на каждом планировщике последовательность транзакций была одна и та же, не надо ничего согласовывать.

Да, я уже понял этот аргумент, и уже ответил, что такая архитектура не дает отказоустойчивости. А отказоустойчивость -- это весь смысл создания Calvin. Масштабируемость у него так-себе, поскольку для достижения оной придется серьезно переосмыслять принципы записи в БД.

Вот как раз при отказе одной из реплик всем надо договориться, какая последняя эпоха в этой реплике. Это единственное место, где нужен консенсус.

Ну, и кто его будет делать? ZooKeeper, в БД которого пусто?

ZooKeeper, в БД которого пусто?

Да. Достаточно в момент сбоя всем оставшимся секвенсерам/планировщикам написать, какой последний пакет они видели от пропавшего секвенсера, и выбрать наименьший. Ну или пусть тот, у кого есть наибольший, поделится с остальными – можно через Zookeeper.

Достаточно в момент сбоя всем оставшимся секвенсерам/планировщикам написать, какой последний пакет они видели от пропавшего секвенсера, и выбрать наименьший.

Спешу огорчить, что это даже не eventual consistency. Это "может быть подтвержденная транзакция останется в базе, а может быть нет", то есть, никаких гарантий персистентности. Например, клиент отправляет транзакцию секвенсеру, секвенсор подтверждает ее, ставит ее в очередь на отправку через сеть, и в этот момент весело пропадает из сети. Транзакция подтверждена, но про транзакцию никто в оставшейся сети никогда не слышал.

Конечно, можно решить проблему "в лоб" сделав синхронную репликацию. Правда, здесь внезапно возникает проблема -- как засчитывать успешность транзакции? Если по успешному ответу ото всех ведомых узлов, то система умрет при отказе одного ведомого узла. Если делать это через кворум, то по сути повторяешь всю логику подтверждения репликации Zab/Raft/MultiPaxos. И при отказе тебе придется решать, какой из узлов содержит корректные подтвержденные данные на основе данных, содержащихся в узлах. То есть, это копия логики ZooKeeper. Со всеми вытекающими, вроде долгого подтверждения транзакции и необходимостью гонять кучу данных о транзакциях по сети.

клиент отправляет транзакцию секвенсеру, секвенсор подтверждает ее

Да? А так можно?

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

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

Так или иначе, это значит одно -- клиент отослал запрос, но не знает, как этот запрос был обработан. Что я могу сказать -- удачи писать клиентский код под это дело.

удачи писать клиентский код под это дело.

Вот тут соглашусь на 100%.

Я вообще противник любых распределённых БД, поскольку убеждённость в том, что «распределённая БД ничем не отличается от монолитной, кроме размера» – крайне опасное заблуждение. Разработчики всё равно будут писать код как для обычной монолитной базы, и на «домашних» нагрузках это даже будет работать. А вот на реальных нагрузках всегда будут неожиданные трудноуловимые спецэффекты. И ещё раз повторю – это относится не только к Кальвину, а вообще ко всем распределённым БД (за исключением аналитических).

Если хочется строить большую распределённую систему, за образец надо брать Facebook.

Я вообще противник любых распределённых БД, поскольку убеждённость в том, что «распределённая БД ничем не отличается от монолитной, кроме размера» – крайне опасное заблуждение

Распределенные БД прекрасно дают гарантии, strong consistency, eventual consistency, да, их сложно делать, да, только несколько распределенных БД умеют в strong consistency, но это не повод говорить "давайте избегать распределенных БД" -- особенно если есть производственная потребность иметь таковую. Прототип Calvin потому и использовал ZooKeeper -- это был для них единственный способ сделать прототип отказоустойчивым.

Если хочется строить большую распределённую систему, за образец надо брать Facebook.

В каком смысле "брать за образец"? Лепить из чего попало?

особенно если есть производственная потребность иметь таковую

Не могу придумать «производственную потребность» под распределённую базу (ещё раз подчеркну, речь не об аналитической СУБД). Не поделитесь?

Лепить из чего попало?

Я не это имел в виду. Facebook состоит из множества шардов, данные каждого из которых хранятся в MySQL. При необходимости межшардовых транзакций организуется сага. На мой взгляд, такой подход намного более отказоустойчивый и масштабируемый, чем единая распределённая БД.

Не могу придумать «производственную потребность» под распределённую базу (ещё раз подчеркну, речь не об аналитической СУБД). Не поделитесь?

Поделюсь. 24/7 сервис, в котором нужно выводить из эксплуатации реплики и вводит в эксплуатацию другие, при этом есть ограничения на корректность информации. То есть, грубо говоря, баланс на счету пользователя, где недопустимо, чтобы он временно приобрел непонятное значение, мол "ща засинхронизируемся и всё норм будет" -- потому что начнется вой от пользователей "у вас тут система сломалась". Или пользователи участвуют в редактировании общего документа -- здесь я подчеркну, что достаточно поддерживать согласованность на уровне мелкого общего сервера, который используется для этих пользователей и этого документа, не обязательно иметь глобальную согласованность. Масштабирование через шардирование, короче говоря. И чтоб отдельный шард не выдавал непонятные результаты во время сбоя -- нужна строгая согласованность между репликами шарда. Никакими MySQL и сагами этого не сделать.

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

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

Как человек, последние 15 лет работающий в банковском секторе, а до этого 6 лет в телекоме, я несколько удивлён приведёнными примерами. Достаточно поместить все счета клиента на один монолитный сервер – и никакого воя не будет. А то, что Вася деньги отправил, а Маша получила их только через 5 секунд вообще никого никогда не парило.

Пример с документами тем более непонятен.

Ещё раз подчеркну, я не «против распределённых систем», а против «распределённых БД». Фейсбук – система распределённая, но все БД там монолитные.

нужна строгая согласованность между репликами шарда. Никакими MySQL и сагами этого не сделать.

MySQL – не сделать, хотя вроде Алибаба и к MySQL прикрутила физическую репликацию. А вот любая другая монолитная БД – MS SQL, Oracle, PostgreSQL, DB2 – вполне. Ну и да, над ними маленький raft для автоматизации переключения :)

согласованности данных не будет даже при полном отсутствии отказов.

А это сильно зависит от требований к согласованности.

Как человек, последние 15 лет работающий в банковском секторе, а до этого 6 лет в телекоме, я несколько удивлён приведёнными примерами.

Я не знаю про телеком, мой скромный опыт говорит про то, что там немало годных спецов, но банковский сектор, именно сами банки -- это помойка, ржавая и загнившая с концами, с зависшими банкоматами, с зарезервированными средствами, для пользования которыми нужно ждать окончания операционного дня, говно-SPA на jQuery и React одновременно с кучей ошибок, через которые регулярно не получается то одно оплатить, то другой услугой воспользоваться. Вот чего там точно нет -- так это надежности и отказоустойчивости. Пока всё работает, каналы связи исправны, сервера стоят на ИБП -- вроде бы всё "отказоустойчиво". Как что-то падает -- начинается вой "у меня деньги со счетам списались, а никуда не пришли. Я в поддержку звоню-пишу, меня пускают по кругу".

Достаточно поместить все счета клиента на один монолитный сервер – и никакого воя не будет. А то, что Вася деньги отправил, а Маша получила их только через 5 секунд вообще никого никогда не парило.

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

Как в этой системе обновлять сервера без остановки обработки транзакций? Да никак. Просто останавливают всё к чертям и вешают табличку на переднем веб-сервере "ведутся технические работы".

А вот любая другая монолитная БД – MS SQL, Oracle, PostgreSQL, DB2 – вполне. Ну и да, над ними маленький raft для автоматизации переключения

Может быть в лабораториях рейха такая БД и есть, но из публичных софтин ни одно решение не смогло показать отказоустойчивость. Да, реализации рафта и multi-paxos есть -- только они не работают. Ну то есть либо бьют данные при отказе, либо, как упомянутый выше Galera Cluster, портят данные даже при полном отсутствии отказов. Это не "маленький Raft" -- трудоемкость реализации отказоустойчивой БД на Raft сравнима с реализацией чего-то вроде InnoDB с нуля. И очень тяжело пояснить технически неграмотному заказчику/инвестору (а это 95% оных), почему вы пилите реализацию "маленького Raft" уже два года, а не сделали даже половины.

помойка, ржавая и загнившая с концами, с зависшими банкоматами,

Чувствуется, у вас что-то глубоко личное с корнями, уходящими в далёкое детство. В разных банках всё очень по-разному.

нет никакого оправдания для того, чтобы транзакция проходила дольше секунды

Сразу видно человека с богатым практическим опытом. Вот объясните – а зачем быстрее? Ответить человеку «ваша транзакция выполнена» надо быстро, тут SLA обычно полсекунды. Пока он звонит или пишет другу «отправил, лови», деньги дойдут до адресата. Можно вывернуться мехом внутрь и сделать быстрее, но зачем? Банк – это про зарабатывание денег, а не про компаративную фаллометрию.

что при отказе транзакция зависает в лимбо

Не зависает. Прочтите ещё раз, как работает сага.

Как в этой системе обновлять сервера без остановки обработки транзакций?

Смотря что вы понимаете под «обновлять сервера». Если обновление ОС и минорные патчи на СУБД, то спасает физическая репликация на уровне БД (DataGuard, AlwaysOn и т. п.) Если регламентные работы на БД, включая мажорные обновления, и накат новых версий ПО, подразумевающих изменение модели данных, то тут – логическая репликация. С одним, заметьте, лидером.

из публичных софтин ни одно решение не смогло показать отказоустойчивость. Да, реализации рафта и multi-paxos есть -- только они не работают.

Расскажите это разработчикам Oracle Observer и etcd.

Читайте внимательно – маленький raft не для хранения данных, а для автоматизации переключения.

Чувствуется, у вас что-то глубоко личное с корнями, уходящими в далёкое детство. В разных банках всё очень по-разному.

С радостью изменю свое мнение, если узнаю про хотя бы один банк, где это не так.

Сразу видно человека с богатым практическим опытом. Вот объясните – а зачем быстрее?

Можно вывернуться мехом внутрь и сделать быстрее, но зачем? Банк – это про зарабатывание денег, а не про компаративную фаллометрию.

Я сразу написал, что это играет мало значения, но ты решил придраться. Я просто иронично вскользь упомянул забавный феномен IT индустрии "софт будет выполнен настолько плохо, насколько это возможно". То есть, пока этого хватает -- никто не пошевелит и пальцем. И не надо мне рассказывать, что бедный региональный банк с чистой прибылью в $1 млрд в год не может себе позволить потратить пару миллионов на разработку софта. Не говоря уже про крупняк. Проблема не в том, что у них нет денег -- проблема в том, что у них нет исполнителей и они не смогут их найти ни за какие миллиарды. Потому что дворник не сможет организовать конвеерное производство автомобилей.

>что при отказе транзакция зависает в лимбо

Не зависает. Прочтите ещё раз, как работает сага.

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

Если регламентные работы на БД, включая мажорные обновления, и накат новых версий ПО, подразумевающих изменение модели данных, то тут – логическая репликация. С одним, заметьте, лидером.

Я не смог распарсить эту фразу. Лидер -- он на то и лидер, чтобы быть единственным.

Расскажите это разработчикам Oracle Observer и etcd.

Etcd я уже упомянул. Я так понимаю, что и его, и Google Chubby разработали в гугле. То есть, две из 3 существующих в индустрии strong consistency баз данных разработаны в гугле. Oracle Observer сам по себе не является отказоустойчивым, потому сравнивать его с etcd смешно.

хотя бы один банк, где это не так

Из того, что я знаю, – Сбер и Тинькофф.

не может себе позволить потратить пару миллионов на разработку софта

Да может и тратит. Но за эти деньги решаются другие задачи.

сложность реализации глобально атомарных операций в распределенных системах

Зачем «глобальная атомарность»? Что это за фетиш такой?

В научных экспериментах это нужно, в финансовых сервисах – нет.

грамотная реализация саг по прежнему является крайне сложной задачей

Даже не знаю, как это комментировать.

Эта тема столько раз разжёвана и переварена, что – – –

Лидер -- он на то и лидер, чтобы быть единственным.

В Zookeeper, которому мы тут полощем кости, лидер может меняться. В каждый конкретный момент он один, но если вдруг с лидером что-то случится, то Zookeeper сам принимает решение, кто станет новым лидером (собственно, весь zab/raft/paxos – об этом). А Oracle/PostgreSQL/etc сам такое решение принять не может – у него должен быть внешний переключатель, который для принятия решения использует что-то из перечисленного софта.

Я так понимаю, что его .. разработали в гугле.

Нет, это RedHat в рамках проекта CoreOS

Oracle Observer сам по себе не является отказоустойчивым

Я не понимаю, что вы вкладываете в понятие «отказоустойчивость» в данном случае. Он обеспечивает корректный выбор лидера в случае сбоя в HA-кластере Oracle – и этого достаточно. У Microsoft, IBM и Veritas есть аналогичные решения.

Зачем «глобальная атомарность»? Что это за фетиш такой?

Хорошо, локальная для реплицированного шарда. Одна эта задача по факту неподъемна для подавляющей части контор в IT индустрии.

Даже не знаю, как это комментировать.

Эта тема столько раз разжёвана и переварена, что – – –

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

если вдруг с лидером что-то случится, то Zookeeper сам принимает решение, кто станет новым лидером (собственно, весь zab/raft/paxos – об этом)

Нет. Выбрать лидера случайной задержкой ожидания -- это довольно просто. Не побить хранимые данные в результате этого процесса и при этом иметь приемлимую производительность -- это задача, которую удалось решить лишь нескольким разработчиком во всем мире. Ты повторяешь ту же самую ошибку "с данными как-нибудь разберемся". Как нибудь разберемся и что-то на выходе получим -- вот потому сохранности данных при отказах никто не может обеспечить, если не применяет ZooKeeper/etcd.

Я не понимаю, что вы вкладываете в понятие «отказоустойчивость» в данном случае. Он обеспечивает корректный выбор лидера в случае сбоя в HA-кластере Oracle – и этого достаточно.

Я имел в виду, что сам Observer -- это один узел, и с его отказом заканчивается вся отказоустойчивость системы. Но в целом у БД с Observer-ом некая отказоустойчивость есть, бесспорно, это лучше, чем ничего.

"Если мы возьмем первую инструкцию процесса 1, то параллельно с ней может выполниться любая из четырех команд."
Дальше читать не стал. Даже термин "скромный" мне показался бы оптимистично завышенным.

Два процесса выполняются параллельно и независимо

Тогда вопрос (по самому первому примеру) насчет независимого выполнения: в обоих потоках используются одни и те же переменные = уже как бы намек,. что жди беды, т.е. что рано или поздно возникнут коллизии (кроме того, насколько это из реальной жизни я имею в виду такое использование переменных)? Кроме того, второй поток - бесконечный цикл. Насколько это корректно, один из потоков делать таковым и насколько эта ситуация из реальной жизни.

Вообще, насколько грамотнее бы было тогда всю программу оформить в виде бесконечного цикла, а внутри него уже потоки организовывать?

Тогда вопрос (по самому первому примеру) насчет независимого выполнения: в обоих потоках используются одни и те же переменные = уже как бы намек,. что жди беды, т.е. что рано или поздно возникнут коллизии (кроме того, насколько это из реальной жизни я имею в виду такое использование переменных)? Кроме того, второй поток - бесконечный цикл. Насколько это корректно, один из потоков делать таковым и насколько эта ситуация из реальной жизни

Этот пример неявно подразумевает, что операция инкремента-декремента атомарна, то есть, это по сути lock-free алгоритм. Даже достаточно простые прикладные lock-free алгоритмы весьма мозгодробильны, потому я постарался не приводить никаких примеров из жизни.

Вообще, насколько грамотнее бы было тогда всю программу оформить в виде бесконечного цикла, а внутри него уже потоки организовывать?

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

Sign up to leave a comment.

Articles

Change theme settings