Guard proc_replication from lagging governors
TL;DR: Надо добавить node.status().check_term(req.term)?;
в начало всех хранимок, где этого нет
After !549 (merged) proc_replication is no longer guarded by wait_index, and now there can be a situation where due to network failure an old governor instance has obsolete data and sends a request which will override replication on a new instance.
UPDATE: Это предложение не имеет смысла To avoid this, we should add the applied index of the governor to the request, which should be checked against _schema.config_version
(or something), and if it's lesser, the request should be discarded, else it's applied and _schema.config_version
gets set to the index from request.
Проблема
Почти все RPC от губернатора защищены от устаревших запросов тем, что вначале вызываемая сторона делает wait_index и проверяет текущий term с term указанным в запросе. Если рафт лидер сменился на кворуме воутеров, то отстающий инстанс с разрывом сету будет иметь term меньше чем у остальных и все будут игнорить его запросы. Раньше это было верно и для proc_replication хранимой процедуры.
Проблема в том что добавляя кластерный DDL мы столкнулись со следующей проблемой: read-only инстанс, который догоняется до кластера по рафт журналу, после того как DDL уже был закомичен, то для применения операции DdlCommit инстанс будет ожидать, пока описание схемы не доедет до него по тарантульной репликации. (см. https://git.picodata.io/core/picodata/-/blob/a3176a1334012b4a18cb82a8b4b38c9d58e2a96f/src/traft/node.rs#L963). Из этого следует, что мы при вызове proc_replication и proc_replication_demote не можем блокироваться до продвижения рафт журнала, т.к. рафт журнал не будет продвинут до тех пор, пока не продвинется тарантульная репликация.
Решение
На самом деле для защиты от старых губернаторов нам нужен не wait_index, а check_term (вот пример). wait_index нужен для синхронизации, т.к. почти все RPC от губернатора полагаются на данные реплицируемые по рафт журналу.
То есть все хранимки должны делать node.status().check_term(req.term)?;
, и все, которые могут должны делать wait_index перед этим.
check_term будет работать, так как hard_state приходит асинхронно с рафт журналом, значит блокировки не будет