research: работа пула с мастерами/репликами

на текущий момент пул работает следующим образом

изначальное построение коннектов:

  • для discovery режима пул устанавливает коннекты ко всем нодам кластера (если возможно), независимо от реплика/мастер
  • для bootstrap режима пул устанавливает коннекты ко всем указанным в DSN нодам (независимо от реплика/мастер)

перестройка состава пула по топологии:

  • для discovery режима пул периодически закрывает/открывает новые соединения в соответствии с изменениями топологии (ноды умерли/ожили) независимо от реплика/мастер начиная с 2.0
  • для bootstrap режима не поддерживается поддерживается начиная с 3.0

роутинг запросов:

  • для DQL, DDL, DML* запросов пул использует соединение в соответствии со своей политикой балансировки (по умолчанию rr). таким образом, могут использоваться как мастера, так и реплики
  • для DML: insert (parametrized query) пул вычисляет по метаданным от пикодаты (если возможно) необходимый репликасет и направляет запрос на мастер нужного репликасета. если сделать этого не удалось (вычислить репликасет или мастера нет в пуле и тп), пул направляет запрос по тому же принципу, как написано выше

Предмет задачи

Необходимо изучить и составить решение, каким образом наполнять пул (к каким нодам) и каким образом использовать для запросов

Является предпочтительным использовать всегда мастера (тогда, возможно, и наполнять пул стоит только мастерами)

Опции Picodata

-FORWARD (pico 26.2) опция дает ошибки при неверном роутинге (например если при forward=off пикодате необходимо запрос переадресовать внутри себя, будет ошибка). в целом forward вынесен в отдельную задачу #114

  • READ_PREFERENCE (pico 26.1) опция имеет три значения: leader (по умолчанию) выполняет чтение только на лидерах репликасетов, replica только на репликах, any на любом узле репликасета

не дает ошибки при неверном роутинге, но может влиять на производительность. например, если при read_preference=replica послать запрос на мастер, то пикодата перешлет его на реплику для чтение и потом отдаст ответ. получаем дополнительный сетевой хоп, но в то же время разгружаем роутер (координатор)

при этом опции можем задать несколькими способами:

  1. OPTION (READ_PREFERENCE = ...) в тексте запроса -- драйвер не распознает;
  2. startup-параметр сессии options=read_preference=<v> в доп аргументах или прямо в DSN ?options=read_preference%3D<v> в DSN -- драйвер может распознать и это не дорого для пула, так как делается один раз
  3. ALTER SYSTEM SET read_preference = '<v>' -- драйвер может распознать через запрос к системной таблице, но смысла не имеет, т.к. может меняться в системе постоянно

Ограничения и ошибки:

  • если в запроса DML/DDL задать OPTION (READ_PREFERENCE = ...), то ошибка read_preference option is supported only for DQL queries
  • DML всегда диспатчится как leader, если READ_PREFERENCE указан на уровне сессии, то есть нет ошибки
  • если значение replica и в репликасете нет реплик, то вероятно ошибка таумаута
  • для unlogged-таблиц только одно значение валидно: read_preference must be set to 'leader' when querying unlogged tables
  • при режиме sql_preemption только одно значение валидно: read_preference must be set to 'leader' when sql_preemption is enabled

Надо как-то предупредить пользователя или сослаться на предупреждение от Picodata, что чтение с реплик допустимо, если пишущая нагрузка не пересекается с читающей и топология не меняется во время чтения, иначе возможны неконсистентные результаты

Предлагаемое решение

Новая настройка пула

Добавить настройку instance_role с значениями ["any" (default), "leader", "replica"]. Эта настройка доп фильтр на создание пользовательских соединений к узлам, как forbidden tiers. Предположительно, доработка минимальна, т.к. reconcile и rebalance алгоритм умеют работать с топологией с доп ограничениями (forbidden tiers, DSN при bootstrap режиме). Доработка в основном понадобится только для старта пула, т.к. на старте мы пользуемся на топологией, а системными таблицами. У нас есть TODO на ресерч данной темы (не стоит ли перейти на топологию и тут)

При выключенной топологии также можно задавать instance_role, но тогда список узлов фиксируется один раз на старте и драйвер не будет реагировать на изменения топологии, в тч смену лидера. Надо проговорить в доке

Поддержка startup опций

Если опции настроены через аргументы/DSN, на нашей стороне необходимо:

  • проверить, что нет конфликта с instance_role (если есть => явная ошибка)
  • использовать режим на основе переданной настройки. read_preference=leader => instance_role=leader, read_preference=any => instance_role=any, read_preference=replica => instance_role=any. Последнее связано с тем, что replica будет использоваться пикодатой только на чтение. Если мы откроем пул только из реплик (без прямого указания от пользователя), мы получим значительное ухудшение записи.

Остальные способы настройки read_preference драйвером не обрабатываются из-за отсутствия возможности или целесообразности

Definition of done

  • изучение новых опций пикодата и их работы, по ним составить описание
  • составленные кейсы использования
  • дизайн на стороне драйвера: новая конфигурация, дефолтное поведение, фиксация инструкции для пользователя (пусть разработчик сам ищет баги в своем коде -- тоже вариант)
  • заведенные задачи на разработку/баги по принятым решениям
Edited by Asya Lomakina