From 959411b7222eeb90943500e7696a7e24f7c77372 Mon Sep 17 00:00:00 2001 From: EmirVildanov <reddog201030@gmail.com> Date: Thu, 14 Nov 2024 12:25:05 +0300 Subject: [PATCH] adr: TRUNCATE sql command this adr is a proposal of implementing TRUNCATE sql command, its context and possible problems --- doc/adr/14-11-2024-truncate.md | 239 +++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 doc/adr/14-11-2024-truncate.md diff --git a/doc/adr/14-11-2024-truncate.md b/doc/adr/14-11-2024-truncate.md new file mode 100644 index 0000000000..ee2cf59ba4 --- /dev/null +++ b/doc/adr/14-11-2024-truncate.md @@ -0,0 +1,239 @@ +status: accepted + +decision-makers: @kostja, @darthunix, @gmoshkin + +consulted: @gerold103 + +-------------------------------- + +# TRUNCATE нужно Ñчитать DDL-запроÑом и иÑполнÑÑ‚ÑŒ через Ñтандартный алгоритм DDL Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸ÐµÐ¼ `map_callrw` + +## ОпиÑание запроÑа +* `TRUNCATE t` -- Ñто SQL-запроÑ, который очищает вÑÑ‘ Ñодержимое таблицы (по Ñути +`DELETE FROM t`) +* Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ TRUNCATE-запроÑа на Ñтороджах необходимо вызвать метод + `box.space.truncate` +* Он не вызывает `on_replace` тригеры Тарантула (в отличие от INSERT, UPDATE и + DELETE) +* Тарантул отноÑит Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ðº категории schema-change (DDL), а не data-change + (DML) (Ñм. [документацию + SQL](https://www.tarantool.io/en/doc/latest/reference/reference_sql/sql_statements_and_clauses/#truncate)), + Ñ…Ð¾Ñ‚Ñ Ð² нашем форке иÑполнение операции номер Ñхемы не менÑет. Ðужно иметь в + виду, что локальный TRUNCATE Тарантула -- Ñто операциÑ, к которой в рамках + транзакции Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ rollback +* При вызове на Ñторадже функции `box.space.truncate`, она иÑполнÑетÑÑ Ð² фоне, + не Ð±Ð»Ð¾ÐºÐ¸Ñ€ÑƒÑ Ð¿Ð¾Ñледующие вызовы (Ñм. [документацию + space-api](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/truncate/#lua-function.space_object.truncate)) +* Ð”Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ TRUNCATE на таблице t пользователю необходимо получить + привилегию WRITE (в будущем её Ñтоит заменить на отдельную привилегию + TRUNCATE) + +## Продуктовые Ñ‚Ñ€ÐµÐ±Ð¾Ð²Ð°Ð½Ð¸Ñ Ðº раÑпределённому TRUNCATE + +ИÑÑ…Ð¾Ð´Ð½Ð°Ñ +[задача](https://git.picodata.io/picodata/picodata/picodata/-/issues/927) на GitLab. + +Ðиже предÑтавлен ÑпиÑок продуктовых требований к реализации TRUNCATE: +* TRUNCATE должен корректно иÑполнÑÑ‚ÑŒÑÑ Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÑŽÑ‰Ð¸Ð¼ ребаланÑером +* TRUNCATE должен быть линеаризован Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ DDL +* За Ñчёт проверки привилегий TRUNCATE должен быть линеаризован Ñ GRANT/REVOKE +* TRUNCATE должен либо полноÑтью применÑÑ‚ÑŒÑÑ, либо полноÑтью не применÑÑ‚ÑŒÑÑ. + ÐÐµÐ»ÑŒÐ·Ñ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ чаÑÑ‚ÑŒ данных и вернуть ошибку. Ошибку можно вернуть, только еÑли + мы гаранитрованно не удалим ничего или удалим вÑÑ‘ +* TRUNCATE не должен блокировать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð³Ð¸Ð¸. ЕÑли TRUNCATE заÑÑ‚Ñ€Ñл + из-за того, что чаÑÑ‚ÑŒ узлов не могут выполнить какой-то из его шагов, то + должна быть возможноÑÑ‚ÑŒ удалить Ñти узлы из клаÑтера или иначе их починить + (например, узлы опущены и надо их проÑто поднÑÑ‚ÑŒ) так, чтобы TRUNCATE завершилÑÑ + уÑпешно. Ð’ идеале TRUNCATE не должен ждать только Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð° вÑех маÑтерах + репликаÑетов, и не блокироватьÑÑ Ð¾ недоÑтупные read-only реплики + +ПожеланиÑ: +* TRUNCATE не должен блокировать DDL-запроÑÑ‹ к другим таблицам + +#### РебаланÑировка + +Ðеобходимо избежать Ñледующего аномального ÑÑ†ÐµÐ½Ð°Ñ€Ð¸Ñ Ñ Ñ€ÐµÐ±Ð°Ð»Ð°Ð½Ñировкой: +1. ÐачинаетÑÑ vshard-ребаланÑировка бакетов. Данные Ñ Ð¾Ð´Ð½Ð¸Ñ… инÑтанÑов начинают + переезжать на другие (инÑÑ‚Ð°Ð½Ñ ÑƒÐ´Ð°Ð»Ñет у ÑÐµÐ±Ñ Ð´Ð°Ð½Ð½Ñ‹Ðµ таблицы и отправлÑет + запроÑ, переÑылающий Ñти данные на другой инÑтанÑ) +2. ИÑполнение TRUNCATE t начинаетÑÑ Ð½Ð° узле-координаторе +3. До вершин клаÑтера доходит TRUNCATE t, Ð·Ð°Ñ‚Ð¸Ñ€Ð°Ñ Ð²Ñе данные +4. До таблиц на инÑтанÑах доходÑÑ‚ данные, переехавшие из-за ребаланÑинга +5. ПолучаетÑÑ, что TRUNCATE иÑполнилÑÑ, а в таблицах вÑÑ‘ равно оÑталиÑÑŒ данные + +#### Ð›Ð¸Ð½ÐµÐ°Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ Ñ Ð¾Ñтальными DDL + +Ðеобходимо избежать Ñледующей ABA-проблемы: + +1. ИÑполнÑетÑÑ CREATE TABLE t +2. Таблица наполнÑетÑÑ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ (через INSERT) +3. ИÑполнение TRUNCATE t начинаетÑÑ Ð½Ð° узле-координаторе +4. ИÑполнÑетÑÑ DROP TABLE t +5. Ещё раз иÑполнÑетÑÑ CREATE TABLE t +6. Ещё раз таблица наполнÑетÑÑ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ (через INSERT) +7. До вершин клаÑтера доходит TRUNCATE t, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð¸ÑполнÑетÑÑ Ð½Ð° новой таблице + +иÑполнение TRUNCATE должно упаÑÑ‚ÑŒ из-за того, что верÑÐ¸Ñ Ð½Ð¾Ð²Ð¾Ð¹ таблицы не +Ñовпадает Ñ Ð²ÐµÑ€Ñией таблицы, над которой необходимо иÑполнить TRUNCATE. + +## Ðынешний механизм иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ DDL и DML-запроÑов +ИÑполнение DDL-запроÑа добавлÑет запиÑÑŒ (CaS-опкод) в рафт-журнал и увеличивает +номер Ñхемы, конÑиÑтентно менÑÑ ÑоÑтоÑние вÑего клаÑтера. Общий Ñценарий работы +Ñ Ñ€Ð°Ñ„Ñ‚-журналом и обработки опкода ÑÐµÐ¹Ñ‡Ð°Ñ Ñ‚Ð°ÐºÐ¾Ð¹: +* ДобавлÑетÑÑ DdlPrepare-опкод в рафт-журнал на лидере +* raft-main-loop (`advance` -> `handle_committed_entries` -> + `handle_committed_normal_entry`) на вÑех узлах клаÑтера обрабатывает + DdlPrepare. ВыполнÑет Ñпецифичную Ð´Ð»Ñ Ð¾Ð¿ÐºÐ¾Ð´Ð° работу (например, Ð´Ð»Ñ CreateTable + Ñоздаёт запиÑи в _pico_table и _pico_index) и Ñоздаёт запиÑÑŒ в _pico_property + Ñ PendingSchemaChange (Ñто значит, что Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° применÑÑ‚ÑŒÑÑ) +* Губернатор, проÑыпаÑÑÑŒ в очередной раз, видит PendingSchemaChange, Ñоздаёт + Plan::ApplySchemaChange Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸ÐµÐ¼ вÑех маÑтеров репликаÑетов. Через + `proc_apply_schema_change` он поÑылает вÑем маÑтерам Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° применение Ñтого + Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ +* МаÑтера, иÑполнÑÑ `apply_schema_change`, выполнÑÑŽÑ‚ Ñпецифичную Ð´Ð»Ñ Ð¾Ð¿ÐºÐ¾Ð´Ð° + работу (например, Ð´Ð»Ñ CreateTable Ñоздаёт таблицу и primary key в локальных + таблицах _space и _index) и через `set_local_schema_version` обновлÑÑŽÑ‚ + локальную верÑию Ñхемы до PendingSchemaVersion (в ÑиÑтемной таблице Тарантула + _schema мы храним Ñвоё значение Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð¼ "local_schema_version") +* Получив положительные ответы Ñо вÑех маÑтеров, говернор добавлÑет в + raft-журнал DdlCommit-опкод. Ð’ противном Ñлучае добавлÑет DdlAbort-опкод. + Стоит обратить внимание, что в Ñлучае Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ `OnError::Retry(e)` (например, + по таймауту) говернор будет в беÑконечном цикле пытатьÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾ иÑполнить + Ñетевые вызовы. +* raft-main-loop на вÑех узлах обрабатывает DdlCommit/DdlAbort + * Ð”Ð»Ñ DdlCommit иÑполнÑет `apply_schema_change` (в Ñлучае еÑли Ñтот маÑтер + отÑтал от рафт-лидера), выÑтавлÑет флаг `is_operable` в true + (предполагаетÑÑ, что Dml-запроÑÑ‹ к таблице Ñ Ð½ÐµÐ²Ñ‹Ñтавленным флагом не будут + обрабатыватьÑÑ) и удалÑет PendingSchemaChange + * Ð”Ð»Ñ DdlAbort иÑполнÑет `ddl_abort_on_master` (например, Ð´Ð»Ñ CreateTable + подчищает запиÑи в локальных таблицах _space и _index) + +Логика иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ DML-запроÑа завиÑит от того, Ñ ÐºÐ°ÐºÐ¾Ð¹ таблицей мы работаем: +* Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð°Ð´ глобальными пользовательÑкими таблицами выполнÑетÑÑ Ñ‡ÐµÑ€ÐµÐ· + raft-журнал в виде CaS DML-опкодов (так же конÑиÑтентно менÑÑ ÑоÑтоÑние + клаÑтера, как в Ñлучае Ñ DDL-запроÑами). +* Ð”Ð»Ñ ÑˆÐ°Ñ€Ð´Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ñ‹Ñ… таблиц выполнÑетÑÑ vshard-Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ replicaset::callrw. Ð¡ÐµÐ¹Ñ‡Ð°Ñ + идёт переход на новую, более конÑиÑтентную функцию `map_callrw`, + поÑтому будем дальше ÑÑылатьÑÑ Ð½Ð° неё. Ð’ Ñту функцию передаётÑÑ + Ñгенерированный ExecutionPlan, который на Ñтороджах транÑформируетÑÑ Ð² + транзакционный вызов функций space-api либо в иÑполнение локального SQL. + * DML-запроÑÑ‹ над таблицей t Ñ ÑƒÐ·Ð»Ð°-координатора на Ñтораджи отправлÑÑŽÑ‚ÑÑ Ñ + указанием table_version, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð±Ñ‹Ð»Ð° прочитана на координаторе на момент + вызова `map_callrw`. Любой DDL-запроÑ, затрагивающий таблицу t, поÑле + уÑпешного иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°ÐµÑ‚ table_version Ñтой таблицы (в ÑиÑтемной + таблице _pico_table на каждом инÑтанÑе). Ð’ Ñлучае, еÑли между отправкой + DML-запроÑа Ñ ÑƒÐ·Ð»Ð°-координатора и иÑполнением Ñтого запроÑа на Ñтороджах над + Ñтой таблицей был иÑполнен DDL-Ð·Ð°Ð¿Ñ€Ð¾Ñ (мы заметим, что Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ð°Ñ Ñ‡ÐµÑ€ÐµÐ· + `map_callrw` верÑÐ¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ отличаетÑÑ Ð¾Ñ‚ той, что мы прочитали + локально), DML-Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ñполнен не будет и мы получим ошибку. + * Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ `map_callrw` до иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¸ÑˆÑƒÑˆÐµÐ³Ð¾ запроÑа на Ñтораджах + рефает (ref) вÑе затрагиваемые Ñтороджа. См. + [комментарии](https://github.com/tarantool/vshard/blob/60a7089eb4a4ddcb33d33895811d6662f8ace353/vshard/router/init.lua#L1119) + к функции. Делает она Ñто Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы убедитьÑÑ Ð² отÑутÑтвии + ребаланÑировки во Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¸ÑˆÑƒÑ‰ÐµÐ³Ð¾ запроÑа. Иначе мы бы могли ÑтолкнутьÑÑ Ñ + **ÐЕ**применением пишущих запроÑов к данным таблиц переезжающих Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ + Ñтораджа на другой. + +Скажем Ñразу, чем при опиÑанном механизме иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ñ…Ð¾Ñ€Ð¾ÑˆÐ¾ Ñчитать TRUNCATE -- DDL-запроÑом: +* мы будем отбивать чаÑÑ‚ÑŒ DML-запроÑов. Таких, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… между отправкой Ñ + узла-координатора до момента иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð½Ð° Ñторадже уже уÑпел иÑполнитьÑÑ + TRUNCATE t. Или в целом вÑе запроÑÑ‹ на таблицу t, еÑли учеÑÑ‚ÑŒ, что мы + ÑобираемÑÑ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÑÑ‚ÑŒ `is_operable` флаг +* отбивать вÑе оÑтальные DDL-запроÑÑ‹ к Ñтой таблице до тех пор, пока + `is_operable` не будет выÑтавлен в true + +## СвÑзанные задачи +Ð ÐµÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ TRUNCATE, удовлетворÑÑŽÑ‰Ð°Ñ Ð¾Ð¿Ð¸Ñанным выше требованиÑм, затрагивает +другие ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ·Ð°ÐºÑ€Ñ‹Ñ‚Ñ‹Ðµ задачи: +1. ÐеконÑиÑтентное иÑполнение DDL-операций при работающем ребаланÑировщике. См. + [тикет](https://git.picodata.io/core/picodata/-/issues/1091) +2. ÐеконÑиÑтентное иÑполнение через вызов `map_callrw` Ð´Ð»Ñ DML-запроÑов + на шардированных таблицах. Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð¼Ð¾Ð¶ÐµÑ‚ провалитьÑÑ Ð½Ð° чаÑти из узлов + (например, по таймауту при разрыве Ñети), и мы Ñ Ñтим ничего не делаем. Ðту + проблему нужно будет решить в том Ñлучае, еÑли мы решим реалзивывать TRUNCATE + как DML-операцию + +## Выбранное решение + +Ð’ качеÑтве Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð»Ð°Ð³Ð°ÐµÑ‚ÑÑ Ñчитать TRUNCATE DDL-запроÑом и пуÑкать +иÑполнение TRUNCATE по опиÑанному выше алгоритму DDL, но на Ñтапе обработки +Plan::ApplySchemaChange на гаверноре вызывать `proc_apply_schema_change` не +напрÑмую через rpc, а через `map_callrw`. Таким образом мы избежим необходимоÑти +отдельно чинить ребаланÑировку бакетов Ð´Ð»Ñ DDL, потому что `map_callrw` уже +поддерживает ref Ñтораджей (pin бакетов). Механимзм рафт-журнала обеÑпечит нам +автоматичеÑкое применение TRUNCATE на упавших узлах, которые Ð²Ð¾Ð²Ñ€ÐµÐ¼Ñ Ð½Ðµ получили +Ð·Ð°Ð¿Ñ€Ð¾Ñ (то еÑÑ‚ÑŒ во Ð²Ñ€ÐµÐ¼Ñ Ð¸ÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ TRUNCATE может и не иÑполнитьÑÑ Ð½Ð° вÑех +узлах, но зато иÑполнитÑÑ Ð² момент воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ ÑƒÐ·Ð»Ð° клаÑтера при чтении +рафт-журнала лидера). Ðлгоритм целиком будет выгледеть так: +* Добавить DdlPrepare-опкод в рафт-журнал на лидере +* При обработке DdlPrepare Ñоздать PendingSchemaChange и выÑтавить Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ‡ÐºÐ¸ + флаг is_operable в false +* Ðа губернаторе при обработке Plan::ApplySchemaChange через `map_callrw` + вызвать `proc_apply_schema_change` Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð½Ð° вÑех узлах + `box.space.truncate`. ПовторÑÑ‚ÑŒ Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾ тех пор, пока он не удаÑÑ‚ÑÑ. ПоÑле + уÑпешного иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ `map_callrw` добавить DdlCommit-опкод в рафт-журнал +* При обработке DdlCommit выÑтавить флаг `is_operable` в true и удалить + PendingSchemaChange + +Данное решение требует внеÑÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð¼ÐµÐ½ÑŒÑˆÐµÐ³Ð¾ количеÑтва изменений и иÑпользует +уже реализованные алгоритмы. Из-за работы Ñ PendingSchemaChange иÑполнение +`TRUNCATE t` будет блокировать оÑтальные DDL-запроÑÑ‹, даже не ÑвÑзанные Ñ +таблицей t. Однофазное иÑполнение будет более оптимальным решением, но пока что +было решено отложить Ñту оптимизацию. + +## Ðльтернативные (отвергнутые) варианты Ñ€ÐµÑˆÐµÐ½Ð¸Ñ + +1. Считать его DML-запроÑом и иÑполнÑÑ‚ÑŒ через `map_callrw`. Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ñама + оÑтанавливает ребаланÑировку, не блокирует другие DDL-запроÑÑ‹, но может + иÑполнитьÑÑ Ð½ÐµÐºÐ¾Ð½ÑиÑтентно (Ñм. проблему выше). ЕдинÑтвенное, что мы можем + предпринÑÑ‚ÑŒ в Ñлучае Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¸ Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ из маÑтеров -- Ñто беÑконечно + повторÑÑ‚ÑŒ иÑполнение до тех пор, пока оно не удаÑÑ‚ÑÑ Ð½Ð° вÑех маÑтерах, что + кажетÑÑ Ð¿Ð»Ð¾Ñ…Ð¸Ð¼ решением +2. Считать его DDL-запроÑом, иÑполнÑÑ‚ÑŒ по Ñхеме DDL, починить иÑполнение DDL при + работающем ребаланÑировщике (Ñм. Ñекцию ниже). Ðужно будет добавить Ñпецифичную Ð´Ð»Ñ TRUNCATE + логику в процеÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ DDL: + * Ðа Ñтапе DdlPrepare выÑтавить флаг is_operable в false + * Ðа Ñтапе `proc_apply_schema_change` проверÑÑ‚ÑŒ, что таблица ÑущеÑтвует + * Ðа Ñтапе DdlCommit иÑполнÑÑ‚ÑŒ `box.space.truncate` (добавить Ñту логику в + `apply_schema_change` в Ñлучае выÑтавленного флага `is_commit`) + * Ðа Ñтапе DdlAbort иÑполнÑÑ‚ÑŒ `box.space.truncate` +3. Реализовать иÑполнение TRUNCATE через одну запиÑÑŒ в рафт-журнал: + 1. ЗапиÑать TRUNCATE в рафт-журнал. Ð’ Ñтой же транзакции повыÑить верÑию Ñхемы + 2. Во Ð²Ñ€ÐµÐ¼Ñ apply иÑполнить `box.space.truncate` + + Ð’ момент иÑÐ¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ TRUNCATE или DROP TABLE мы уверены в том, что не получим + Abort ответ, потому что подобный ответ мы можем получить только в Ñлучае + отÑутÑÑ‚Ð²Ð¸Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹, над которой работаем (а raft гарантирует, что Ñтого не + произойдёт, потому что вÑе операции линеаризованы). Ð’ таком варианте мы не + будем блокировать оÑтальные DDL-запроÑÑ‹ из-за уÑтановленного + PendingSchemaChange, но придётÑÑ Ñ€ÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ñ‚ÑŒ новый механизм иÑполнениÑ. Было + предложено отложить реализацию Ñтого алгоритма. + +#### DDL и ребаланÑировка + +Проблему Ñ DDL и ребаланÑировкой предлагаетÑÑ Ñ€ÐµÑˆÐ¸Ñ‚ÑŒ Ñледующим ÑпоÑобом. + +1. Перед иÑполнением любой DDL-операции вручную оÑтанавливать ребаланÑинг, + Ð´Ñ‘Ñ€Ð³Ð°Ñ Ñ€ÑƒÑ‡ÐºÑƒ `vshard.storage.rebalancer_disable` на каждом из маÑтеров. + Ðапример, при обработке ApplySchemaChange добавить второй цикл, который + пробежитьÑÑ Ð¿Ð¾ вÑем маÑтерам и дёрнет на них Ñту ручку. Рпри завершении + обработки позвать `vshard.storage.rebalancer_enable` +2. Пропатчить vshard таким образом, чтобы при ребаланÑировке (Ñм., например, + `bucket_send_xc` и `bucket_recv_xc` в vshard/storage/init.lua) отправлÑÑ‚ÑŒ + помимо данных таблиц ещё и номер Ñхемы (наш local_schema_change), прочитанный на узле-отправителе в + момент вызова `replicaset:callrw`. Ð’ Ñлучае, еÑли номер Ñхемы на получателе + оказалÑÑ Ð²Ñ‹ÑˆÐµ номера Ñхемы на отправителе (то еÑÑ‚ÑŒ за Ñто времÑ, например, уÑпел + иÑполнитьÑÑ ÐºÐ°ÐºÐ¾Ð¹-то DDL-запроÑ) предлагаетÑÑ Ð¾Ñтанавливать процеÑÑ + ребаланÑировки. + * `rebalancer_worker_f` -- Ñто тело файберов, которые запуÑкаютÑÑ Ð´Ð»Ñ + ребаланÑировки. Ð’Ñе они делÑÑ‚ так называемый dispenser, который предÑтавлÑет + Ñобой контейнер путей между узлами, по которым необходимо Ñовершить + ребаланÑировку. `rebalancer_worker_f` крутитÑÑ Ð² цикле до тех пор, пока + dispenser выдаёт пути Ð´Ð»Ñ Ñ€ÐµÐ±Ð°Ð»Ð°Ð½Ñировки + * При вызове `bucket_send` в Ñлучае Ð²Ð¾Ð·Ð½Ð¸ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¸ **ÐЕ**ShardingError + или ошибки ShardingError кроме TOO_MANY_RECEIVING (воркер заÑнёт в качеÑтве + троттлинга) путь из dispenser удалÑетÑÑ, потому что ÑчитаетÑÑ, что клаÑтер + поломан. Ð’ Ñлучае иной ошибки, путь кладётÑÑ Ð¾Ð±Ñ€Ð°Ñ‚Ð½Ð¾ в dispenser. КажетÑÑ, + что наподобие TOO_MANY_RECEIVING можно добавить ещё и ошибку вроде + OLD_SCHEMA_VERSION, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ñ‚Ð°Ðº же положит путь обратно в dispenser, чтобы + он потом вызвал `bucket_send` Ñ Ð½Ð¾Ð²Ð¾Ð¹ верÑией Ñхемы -- GitLab