Короче, как понял, ваша цель - это сделать интерфейс leader election, который будет няшни мжвячни и с которым не надо будет думать.
TL;DR: это нереализуемо, надо писать клиентскую библиотеку к etcd, но она наверняка уже кем-то написана.
Он реализуется в виде одного регистра в произвольном хранилище, к которому применяется Compare-And-Set операция, это есть как в etcd/consul на основе рафта, как в кассандре на основе paxos, так и в более специфичных алгоритмах консенсуса.
Элекция у лидера происходит следующим путем:
- Прочитать значение регистра
- Если в регистре лежит значение "лидер = х по такую-то дату", и дата еще не истекла - прекратить пытаться стать лидером
- Если в регистре пусто или лежит устаревшее значение, то попытаться произвести атомарную операцию замены старого значения на новое, в котором будет указано, что лидер - это тот узел, который оперирует, а дата будет поставлена в виде текущей + произвольное время, в течение которого узел и сеть имеют право подтупливать
- Если атомарная операция не прошла, значит другой узел оказался шустрее и надо вернуться в п.1.
- Если удалось избраться лидером, то надо регулярно обновлять регистр с помощью атомарной замены старого значения на такое же, но с обновленной датой истечения срока. Если атомарная замена не прошла, то добро пожаловать в п.1
Никакой высшей математики с самостоятельным вкручиванием рафта или паксоса не нужно - и боже упаси вас делать, все равно придет Афир и скажет, что ниччего не работает.
Как бы вы ни крутились, вся эта фантасмагория (кроме, может быть, проставления таймстампов) все равно будет лежать внутри приложения, поэтому никакого магического контейнера не будет - будет магическая либа, которая взаимодействует с etcd, consul, cassandra, zookepper, name it. От read - modify - cas никуда не уйти.
И на этом боль не кончается, потому что есть нюанс в виде времени:
- Во-первых никто не гарантирует, что на нодах не разойдутся часы
- Во-вторых внутри того алгоритма, который должен выполняться в контексте мастера, каждый чекпоинт должен сверяться с тем, является ли текущий узел мастером. Потому что никто не знает, сколько времени заняла эта операция, своппила ли ОС на диск, был ли CPU spike, был ли GC spike, алгоритм оказался квадратичной сложности, сеть затупила, чтение из базы данных привело к дедлоку, другая транзакция держала данные, короче, неважно как, но оно пробьет. Адекватное решение - это в той же либе сделать обертку, которая будет делать это за вас, и будет иметь сигнатуру типа
<State> Future<State> execute(List<Function<State, Future<State>>> pipeline, State initial);
Резюме: в пизду это, делайте идемпотентные алгоритмы, которые разбирают таски из очереди, и никакого мастера не нужно.