Обновить
129
0
Григорий Демченко@gridem

Software Engineer

Отправить сообщение
Отчасти я вашей статьёй и вдохновился


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


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


Вот здесь описан алгоритм:
Masterless Consensus Algorithm

Здесь код:
https://github.com/gridem/ReplobPrototype

Для верификации консенсуса использовал DAVE:
https://github.com/gridem/DAVE
Бегло глянул вашу статью про masterless консенсус — выглядит интересно. Это ваш собственный алогритм? Какая у него лицензия?


Да, собственный. Лицензия — Apache 2.0
Это очень похоже на то, что описано в статье: Реплицируемый объект. Только там я использовал С++ и другой алгоритм консенсуса.

Хотел спросить: а отличаются ли операции чтения от операций записи? Все ли операции проходят через raft?
В этом аспекте С++ выглядит интереснее: поддержки на уровне языка — нет, а, тем не менее, использовать корутины сильно проще.
Самая мутная и нетривиальная часть Raft алгоритма, причем не без багов.
Ответил ниже
Во-первых, я не утверждал, что эта фича является уникальной. А во-вторых, можете привести ссылки с такой логикой? Мне было бы очень интересно ознакомиться.
Покажу, но в другой статье. В списке она есть под номером 8.
Когда от оставшегося сегмента из 3 отвалилась одна, не исключена вероятность того, что она подключилась к другому сегменту
Такая возможность исключена, т.е. алгоритм такого сделать не позволит.

Какая функциональность подразумевается под консистентностью? Непротиворечивость в рамках транзакционности или актуальность данных?
Я не очень понял вопрос, но отвечу. Под консистентностью я понимаю линеаризуемость. Что это такое, почитать, например, в википедии. Если я как-то не так ответил, то хочется услышать более чёткий вопрос, т.к. мне не очень понятно, как можно под консистентностью понимать функциональность. В линеаризуемости задается достаточно чётко условия об актуальности и согласованности данных.
В данном случае при развале 5 -> 3+2 мы получаем, что для большинства нод (3) можно продолжать операции изменения данных, а для 2 — допускаются только локальные чтения. После этого, у 3-х нод одна может упасть, тогда получается переход 3 -> 2 с сохранением консистентности. Итого, получаем переход 5 -> 2.
Вопросы очень хорошие. Но имеет смысл на них отвечать после рассмотрения самого алгоритма. Что касательно самого алгоритма, то это новый алгоритм.

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

Есть языки с GC, а есть языки без GC. Далее, есть случай, когда используется только один язык программирования. Но это — неинтересный случай, т.к. бери и используй. Удобно С++? Хорошо. Java — тоже хорошо. Go — замечательно, ну в смысле тоже одинаково хорошо. Как говорится, зависит от задач и опыта разработчика(ов).

Другое дело, когда у нас необходимо скрещивать несколько языков, и вызывать один язык из другого. Если у нас все языки без GC, то проблемы со скрещиванием минимальны: С/C++/Fortran — практически безболезненно. А вот если скрещивать языки с GC, то тут могут возникнуть проблемы: у каждого свой рантайм, свой GC, и они могут внезапно подраться за память, объекты и т.п. Типичные проблемы managed/unmanaged помножаются на то, что тут связь уже может быть managed/unmanaged/managed или ещё что похлеще. По крайней мере, я знаю, как связывать C++ с Java/C#/Python/Lua и др., а вот как связать C#, Go и Java — не очень. Разве что через С++. Но кажется, что это будет адская хрень.

Тут в общем-то не поднимается вопрос о том, а нужно ли это. Тут скорее рассуждения на тему. Ну а моё личное мнение — языки с GC, безусловно, сильно удобнее в разработке. Хотя C++ с новыми стандартами уже начинает дышать в спину, но по удобству всё равно не дотягивает.
Линеаризуемость и сериализуемость — разные понятия.

Линеаризуемость — изменения применяются практически мгновенно.
Сериализуемость — результат получается из некоторой последовательности транзакций.

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

Даже если не обрабатывать могут случатся плохие ситуации. Если исключение кидается в том месте где мы ожидали, мы можем с использование RAII выстроить корректный откат, но ведь мы можем выйти из этого блока кода и перейти в следующий:
Для этого как раз есть EventsGuard, который позволяет «опасный» кусок изолировать. Необходимо также помнить, что в любой момент сопрограмму можно отменить, и код к этому должен быть готов. Поэтому только RAII, только нормальный C++. Однако если хочется грязных хаков, то, как я уже говорил, есть EventsGuard.
Лямбда которую мы засовываем в defer фактически выполняется уже после переключения контекста, вне корутины. И получается, что если там кидается исключение — корутина об этом не знает и не может обработать, исключение перехватывается менеджером задач и о нем никто не узнает, кроме логера. Я бы предложил решать проблему другим способом: отказаться от defer и при вызове resume — проверять, если корутина еще выполняется — выставлять ей специальный флаг — который проверится в ближайшем yield и если нужно поставит менеджеру потоков еще одну задачу на resume. По моему самое удачное решение, предложенные вами альтернативы в виде: ожидания на мьютексах yield — блокируют надолго поток, бустовый io_service::strand не работает с несколькими очередями задач.
Да, есть такое. Поэтому фактически, код внутри defer — асинхронный, а значит — не может и не должен кидать исключение. Смысл тут в том, чтобы все такие вызовы обернуть и больше никогда не использовать defer.

Я этот момент планирую переписать, defer мне тоже перестал нравиться. Хотя изначально идея была великолепная.

Таймауты: т.к. они выполняются в качестве отдельной задачи, возможно в другом потоке и без синхронизации, возможны некоторые нехорошие ситуации:
н-р процессор доходит до «goer.timedout();» приостанавливает поток и начинает выполнять другие потоки, в том числе и нашу корутину, где должен произойти таймаут, успешно выполняет еще кучу кода и переключается на «goer.timedout();» и исполняет. В итоге получим таймаут в совершенно другом месте, где мы вообще никаких таймеров не ставили.
Ну тут я не вижу проблем: если таймаут случился, то он может сработать и позже, т.к. все равно полетит исключение. Другое дело — а что, если мы эти исключения обрабатываем? Тогда да, неприятность может возникнуть. Но она скорее умозрительная, т.к. скорее всего после таймаута все что нужно сделать — это подчистить за собой и отменить всякие действия, что должно выполняться автоматом с использованием RAII.

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

Да и спасибо за популяризацию корутин.
Пожалуйста. Будет еще!
Я на своих менял только ремешок. Сапфировое стекло в полном порядке, ни одной царапины (в отличие от всяких телефонов и других гаджетов). Корпус тоже ничего, однако стекло лучше — как будто новое.

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

И вот теперь мне предлагают умное устройство, которое я должен заряжать несколько раз в неделю, при этом скорее всего время они будут показывать на экране не перманентно, т.к. это затратно для батарейки. Мне и телефона с такими требованиями хватает. Короче, я не согласен так часто заряжать часы, учитывая то, что у меня имеется сейчас.
Суть в том, что АНБ не надо расшифровывать всё подряд. Надо только отдельных личностей. А для этого достаточно текущих мощностей. Так что пока вы никому не нужны, вас вряд ли будут «расшифровывать». Это как в анекдоте про неуловимого Джо.
Хочется понять реальный пример, когда такое необходимо. В постановке задачи сказано, что «поведение зависит от того, какие классы являются базовыми для обрабатываемого класса». А когда такое вообще нужно? Какова исходная постановка задачи?

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

Информация

В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность