Skip to content

Latest commit

 

History

History
295 lines (232 loc) · 57.8 KB

replication.md

File metadata and controls

295 lines (232 loc) · 57.8 KB

Настройка репликации в PrefixDB

Как правильно развернуть и запустить смотрим деплой.

Как правильно "перехать" на другой хост смотрим миграция

В этом документе описанно как работает репликация, как правильно ее настроить, что делать если репликация "развалилась"

Как работает репликация

Любые запросы на изменения RocksDB предварительно записывает в так называемый WAL (Write Ahead Log или лог перед записью), прежде чем записать их в основную базу (sst-файлы). Сама RocksDB предоставляет бинарный интерфейс для чтения WAL, а демон PrefixDB соотвествующий JSON-RPC метод get_updates_since для чтения этого WAL. Слейв, процесс PrefixDB который хочет получать все измения данных мастера, подключаеться к мастеру по TCP и, с помощью этого метода, получает все измения для поддержания базы в актуальном состоянни. Иными словами, архитектура репликации реализована так, что слейвы запрашивают изменения на мастере, а не наоборот (когда мастер рассылает измения на слейвы).

На момент реализации проекта PrefixDB бинарный формат WAL для RocksDB был закрыт, и он был определен через реверс-инженериг. Позже он был открыт, и на первый взгляд, формат был определен правильно, но для избежания сюрпризов, репозитарий RocksDB был форкнут в репозитарий мамбы. Если необходимо будет обновить RocksDB, то в первую очередь необходимо убедиться, что репликация для новой версии будет работать.

В JSON-конфигурации для PrefixDB указываеться путь к ini-конфигурации RocksDB, используя которую демон открывет RocksDB бызы для всех префиксов в общем окружении. Сам демон никак не анализирует конфигурацию RocksDB, а просто передает ее в RocksDB. Любые изменения настроек RocksDB влияют только на производительность самой RocksDB, за исключением двух опций, которые определяют время жизни WAL, и, соответсвенно на возможность репликации:

WAL_ttl_seconds=7200
WAL_size_limit_MB=0

Опция WAL_size_limit_MB определяет минимальный размер WAL после которого он может быть сброшен. Про нее сразу забываем и всегда устанавливаем в ноль или не указываем (оптимальный размер определяеться самой RocksDB). Если репликация не предполагаеться, то параметр WAL_ttl_seconds также устанавливаем в ноль. Это не отключает WAL, но позволяет RocksDB управлять им максимально эффективным образом, но при этом ломает репликацию PrefixDB.

Если на уже работающих PrefixDB задано WAL_ttl_seconds=0 то это основной признак того, что у этого процесса нет слейва (он просто не будет работать). Скорее всего для него нет сейва не просто так, а потому что профиль нагрузки на сервер такой, что либо сам механизм репликации не справляется, либо дает недопустимую избыточную нагрузку на местер, увеличивая общее время ответа. Поэтому если возникнет необходимость сделать для него слейв, то делать это нужно аккуратно, убедившись, что репликация влияет на время ответа сервера в допустимых пределах.

Опция WAL_ttl_seconds должна быть ненулевой только на мастере, на слеве ее устанавливать не нужно, за исключением ситуации когда к слейву нужно подключить еще слейв "паровозом" (как правило это используеться на время "переезда" связки мастер-слейв без даунтайма, об этом расскажу в следующих разделах)

В продакшене для мастеров WAL_ttl_seconds=7200 - это два часа. Для нормально работающей связки мастер-слейв это даже избыточно. Его время можно интерпретировать как максимально время отставания слейва от мастера, при котором слейв остается работоспособным. Если отставание превысит это значение, то весьма вероятно (но не гарантированно), что репликация развалится. В штатном режме слейв отстает от мастера на несколько секунд. Если произошел разовый всплеск нагрузки на мастер, в результате работы каких-то скриптов (например очитстка от мусора, или наоборот какая-то массовая запись), то у слейва есть два часа, чтобы нагнать мастер. Но если изменился общий внешний профиль нагрузки на мастер, и слейв начал отставать, то весьма вероятно, что слейв мастер уже не догонит и репликация развалиться (демон слейва завершит работу). В этом случае (возрасла общая нагрука мастер) перезаливка базы и запуск слейва заного, поможет только до суточной пиковой нагрузки на мастер, потом она развалится. Но тем не менее возможна ситуация, когда какой-то скрипт, разово, внесет столько изменений, что слейв не уложится в два часа и демон завершит работу - в этом случае перезаливка поможет. Чтобы определить, почему это произошло, нужно смотреть графики btp.

В этот же интервал, два часа, можно спокойно останавливать слейв для каких-то работ (перезапуск сервера и пр.). После запуска он попытается нагнать мастер и выйти в штатный режим. Если время остановки слейва превышает два часа, то не факт, что потребуется перезаливка. Два часа -это минимально гарантированное время, может повезти и WAL на мастере проживет дольше. Пэтому если остановили слейв на любое время, то сначала можно попытаться запустить его без перезаливки базы. Под любым подразумеваем "абсолютли!" - все зависит от профиля нагрузки, я реально наблюдал ситуации, когда WAL "живет" месяцами (например одна запись в день, отстальное только чтение).

Репликация для каждого префикса производится независимо, но по одному или нескольким общим TCP соединениям, но поток запросов каждый префикс формирует свой. И у каждого префикса на мастере свой WAL. Как правило, если демон останавливает работу с сообщением в логе, что сломалась репликация, то он указывает в логе имя префикса, для которого она развалилась. В последних версиях PrefixDB скрипт перезаливки позволяет загрузить данные конкретного префикса (ну или сделать это вручную) и, после этого, нужно перезапустить после слейв. Только надо иметь ввиду, что за это время критически может отстать другой префикс и т.д. Иногда проще перезалить все приефиксы разом, чем разбираться в деталях.

Настройка мастера

Разница настроек мастера и конфигурации без поддержки репликации, может заключаться только в одной записи в ini-конфигурации RocksDB, а именно в уже описанной выше WAL_ttl_seconds.

В теущем релизе для основных серверов PrefixDB предоставляеться три не слейв конфигурации по умолчанию:

  • prefixdb.conf - без поддержки репликации
  • master.conf - с поддержкой репликации
  • master-slave.conf - это слейв котрый может быть мастером для другог слейва (можно выстраивать бесконечную цепочку репликации)

По большому счету разница у них только в пути к конфигурации ini-файлу RocksDB, а именно prefixdb.ini и master.ini. Для каждого сервера конфигурация может быть адаптирована под конкретный профиль нагрузки, но для репликации важнна только опция WAL_ttl_seconds - для мастера она не нулевая.

Общая информация о конфигурации предоставлена здесь, но в краце опишу следующий момент, чтобы он не сбивал с толку. Для всех серверов PrefixDB в продакшене у нас открыты четыре порта:

  • 23000 - основной порт
  • 23001 - порт для фоновых скриптов
  • 23002 - порт для подключения слейвов
  • 23003 - порт для текстовых запросов через telnet Первые три порта равнозначны по API, просто у них по разному сконфигурирована очередь обработки запросов. Порт 23002 для конфигурации которая не поддерживает репликацию может быть открыт, т.е. по его наличию нему нельзя определить поддерживает эта конфигурация репликацию или нет. Основной ориентир это название конфигурационных файлов и опция WAL_ttl_seconds. АХТУНГ! на момент написания этой документации не все PrefixDB переведены на единый набор портов! Определить можно по имени хоста. Если в имени присутствует суффикс n (например mastern0) или окончание -deb (например master0-deb), то там набор портов соответствует регламенту. Если хост (виртуалка) называется просто master0 то скорее всего там другой набор портов, но он организован по схожему принципу. Но самое главное эти серверы работают на старой, несовместимой, RocksDB. К ним можно подключаться слейвом, но перезаливать базу нужно только через API (см. следующие разделы).

Настройка слейва

Конфигурация RocksDB (ini) на слейве может ничем не отличатся от настройки на мастере, но нам нет нужды держать два часа WAL, поэтому на сейве WAL_ttl_seconds=0. Однако кофигурационный файл PrefixDB (slave.conf, а также master-slave.conf) имеет некторые отличия:

  • Добавлен модуль client-tcp для подключения к мастеру
  • Добавлен модуль prefixdb-gateway для сериализации запросов в JSON-RPC к мастеру
  • В основном модуле prefixdb добавлена секция slave в которой настраиваются режимы работы с мастером.

Если вы задеплоили файл конфигурации для слейва по умолчаню slave.conf, то в секции client-tcp в поле addr вам нужно будет указать имя хоста или ip мастера:

  /*...*/
  "client-tcp": [
    {
      "name": "client-tcp1",
      "startup_priority":-500,
      "addr": "",  /* <- здесь нужно указать имя хоста (или ip) мастера */
      "port": "23002",
      "connect_count":5,
      "threads":1
    }
  ]

Пока еще есть бардак со старыми инстансами в регламенте открытых портов. На данный момент инстансы работающие на виртуалках с суффиксом 'n' в имени (например mastern5) соответсвует регламенту и для слейва открывает порт 23002. Для более древних виртуалок (например master1) порт для слейва нужно смотреть в конфиге и указать его в client-tcp конфига слейва. По умолчанию это поле пустое и в такой конфигурации демон не запустится. Однако если вы ошибетесь, то демон запуститься и будет вечно пытаться долбиться по этому адресу, что в прочем, легко обнаружить посмотрев логи. Настраивать другие поля, если вы точно не знаете, чего хотите добиться, не нужно. Модуль prefixdb-gateway также не требует каких либо изменений в конфигурации.

В общем и секция slave модуля prefixdb не требует дополнительных настроек, но иногда может быть полезно их подкрутить:

{
  /*...*/
  "prefixdb": [
    {
      /*...*/
      "path": "/monamour/prefixdb",
      /*...*/
      "slave": {
        /* Можно установить в false чтобы временно отключить репликацию, не удаляя всю секцию */
        "enabled": true,
        /* Цель, шлюз через который происходят запросы к мастеру */
        "target": "prefixdb-gateway1",
        /* Можно установить время запуска репликации, а не сразу после запуска демона в формате "12:34:56" просто пусть будет */
        "start_time": "",
        /* Путь хранения файла счетчика (не базы), если пуст или не указан то /monamour/prefixdb_slave  */
        "path": "",
        /* Запрашивать с мастера только разрешенные для записи префиксы */
        "writable_only": false,
        /* Спискок разрешенных для репликации префиксов например ["префикс1"]*/
        "allowed_prefixes": [],
        /* Спискок запрещенных для репликации префиксов например ["префикс1"]*/
        "denied_prefixes": [],
        /* Интервал вычитывания WAL*/
        "pull_timeout_ms": 1000,
        /* Интервал вычитывания списка префиксов */
        "query_prefixes_timeout_ms": 2000,
        /* Количестово записей за один запрос */
        "log_limit_per_req": 100,
        /* Только для отладки. Допустимое количество пропусков */
        "acceptable_loss_seq": 0,
        /* При каком количестве пропусков ( если acceptable_loss_seq > 0 ) нужно писать в лог предупреждение */
        "wrn_log_diff_seq": 10000,
        /* Как часто писать в лог записи об отставании об отставании от мастера, если это проиходит */
        "wrn_log_timeout_ms": 1000,
        /* Таймаут запросов к мастеру для получения изменений */
        "seq_log_timeout_ms": 1000,
        /* Выдерживать интервал после каждого запроса. В противном случае, если не достигнут конец WAL, следующий запрос */
        "expires_for_req": false,
        /* Отключить запись в WAL. Испльзовать только при полной выгрузке данных с мастера [при аварийном завершении чать данных */ 
        /* может быть утеряна]  */
        "disableWAL": false
      }
    }
  ]
  /*...*/
}

При настройке слейва важно следить не только за тем, что отстает ли репликация или нет, но и как она влияет на производительность мастера. Если при запуске слейва, время ответа мастера на боевые запросы недопустимо увеличивается, то во первых нужно убедиться, что слейв подключаеться не к общему порту мастера, а к специально выделеному (обычно 23002) и на этот порт навешано не слишком большое число потоков обработки (workflow, на самом деле должен быть один). Здесь двоякая ситуация. Чем больше потоков на мастере обслуживают слейв, тем быстрее происходит репликация. Однако, чем выше нагрузка записи в мастер, тем интенсивнее вычитываються WAL, что дает еще большую нагрузку на мастер и может увеличиваться время ответа на "боевые" запросы от клиентов, превышая критические значения. Поэтому на мастере, обычно, настроен один поток обработки слейва. Этого достатчно в большинстве случаев, но если слейв начинает отставать, то скорее всего на мастер и без того идет избыточная нагрука и, увеличение потоков обработки слейва может и решит проблему с отставанием, но навярняка приведет увеличению времени ответов на боевые запросы. Здесь нужно определить на какой префикс идет избыточная нагрузка и, оптимальным решением, будет перенести это префикс на другой сервер. Но опять же, нужно смотреть по ситуации и на профиль нагрузки мастера.

Настройки слейва также влияют на скорость репликации, нагрузку на сам слейв и на мастер. Рассмотрим более подробно эти опции:

  • "query_prefixes_timeout_ms": 2000 - получать список актуальных префиксов раз в две секунды (значение в миллисекндах). При значениях больше секунды этот параметр особо не влияет на производительность как мастера, так и слейва. С такой переодичностю слейв будет получать информацию о создании и удалении префиксов на мастере. Если из списка пропадет существующий на слейве префик то он закроет для него базу и пренесет файлы в /monamour/prefixdb_detach. При появлении нового префикса, он создаст локальную базу для него и будет пытаться догнать отставание от этого префикса, пока не выйдет в штатный режим. На самом деле эти ситуации настолько редки, что не стоит особо заморачиватся по этому поводу. Может показаться зачем так часто получать список префиксов, если его изменение черезвычайно редкое событие? Ну это просто удобно при отладке или раскладке, но если это не дает нагрузки, то почему бы не сделать реакцию слейва на такое событие быстрой. Главное не устанавливать это значение большиим чем WAL_ttl_seconds (два часа). Да и в районе часа не лучшее решение, при попытке слейвом догнать часовое отстование, он может дать существенную нагрузку на мастер.
  • "log_limit_per_req": 100 - кличество изменений в мастере которые получит слейв за один запрос к нему. Это основной параметр слейва с которым имеет смысл поиграться при отставании слейва или избыточной нагрузке на мастере (даже если на мастере настроен один поток обработки слейва, слейв может влиять на общую производительность мастера). Может показаться контринтуитивным, но чем меньше это значение, тем меньше нагрузка на мастер, несмотря на то, что количество запросов от слейва на мастер в возрастает. Значение в 100 записей на запрос это компромисное решение, которое не дает сильную нагруку на местер и, в тоже время, позволяет слейву не отставать от него, в большинстве случаев. Если слейв начал отставать, то имеет смысл увеличить это значение, например до 1000, но при этом обязательно отслеживать как изменилось время ответа на мастере на боевые запросы, например по графикам btp. Но при этом нужно учитывать, что несмотря на возможное снижение нагрузки на CPU на мастере, связанное с тем что нужно обрабоать в десять раз меньшне запросов, которое, впрочем производиться в отдельном потоке, мастеру нужно в десять раз больше считать данных с диска. И хотя это будет происходить реже (по разным причинам не в десять раз, а меньше), но эта операция может начать выбивать по таймауту пачками боевые запросы, поэтому нужно остлеживать по btp количество отвалившихся запросов по таймауту и решать с тех. подержкой допустимо это или нет.
  • "seq_log_timeout_ms": 1000- интервал между запросами на мастер на получение изменений (одна секунда). Эта опция может показатся важной, так как вроде напрямую связана с предыдущей, но на самом деле нет, она не так критична. Логика тут такая: слейв делает запрос на вычитывание log_limit_per_req записей из WAL местера и, если в ответе получает флаг, что еще не все данные прочитаны, тут же, без таймаута, посылает запрос на вычитывания следующей порции данных. Это позволяет слейву быстро нагнать мастер, при всплеске нагрузки на запись. И вот только когда слейв получает флаг, что все изменения прочитаны, срабатывает этот таймаут и следующий запрос будет произведен через секунду или через заданное значение. Смысла увеличивать этот параметр нет, потому что при слабой загрузке мастера эти запросы никак не влияют на его производительность. Уменьшать тоже смысла нет, при большой нагрузке он и так будет "долбить" мастер непрерывно. Увеличивать особо тоже нет смысла. Если вы зададите, например 10 минут, то раз в десять минут он будет задрюкивать мастер серией запросов на вычитывание WAL, пока весь его не вычитает. Единственный гепотетический кейс когда он может пригодиться, если какой-то скрипт делает большие пачки записей раз в секунду и нужно вывести слейв из этого "резонанса". Но если вы ключите опцию expires_for_req, то совместно они будут давать более значимый эффект. Но лучше этого не делать, если вы точно не знаете какого эффекта вы хотите добиться.
  • "expires_for_req": false - эта опция запрещает вычитывание лога без таймаута seq_log_timeout_ms. Она меняет логику вычитывания WAL так, что каждый запрос на вычитывание WAL идет с интервалом seq_log_timeout_ms вне зависимости от значения флага, что остались еще данные для вычитывания из WAL. Если вы точно не знаете чего хотите добиться, то лучше ее не устанавливать. В лучшем случае она не даст ефекта, но скорее приведет к отставанию слейва и в конечном итоге развалу репликации. Для этой опции можно придумать более реалистичные гипотетические кейсы, где она может пригодиться (например, на мастер заливаеться раз в сутки большой объем данных, а продакшн работает со слейвам, быстрая обновление не особо критично, но важно чтобы слейв не тормозил, тогда слейв потихонку вычитает данные). Короче, эта опция не даст нагруку на мастер, в ряде случаев даст иллюзию ее снижения, но при этом репликация отстанет и развалиться.
  • "disableWAL": false отключает запись в WAL в смысле совсем (не путать ее с WAL_ttl_seconds=0, когда изменения пишуться в WAL но время жизни управляються RocksDB). При отключении записи в WAL входящие данные не записываються на диск, а храняться в оперативной памяти. Это ускоряет репликацию, но при аварийном завершении работы демона или сервера, часть данных может быть утеряна. Эта опция важна для единственного случая, когда делаем полный перезалив бызы через API PrefixDB (более подробную информацию см. в разделе Перезаливка). Вне этого контекста эта опция мало в каких случаях поможет, за исключением разве что если на слейве люто медленный hdd и, в случае аварии, ненапряжно базу перезалить (и она не особо велика). Отключени WAL можно реализовать и на мастере, это может даст серёзный профит при ряде экстремальных профилей нагрузки, но сделает невозможным репликацию. Эта информация для того, что есть такая возможность, реализвать ее не сложно, если это действительно необходимо будет.

Перезаливка базы с мастера на слейв

Перезаливка бызы с мастера на слейв нужна в двух случаях:

  • Развертывние нового слейва
  • При развале репликации, которая может произойти в следующих случаях:
    • Отставание репликации на недопустимое время (WAL был на мастере перезаписан RocksDB, до вычитывания всех данных).
    • Конфигурация мастера была изменена на уменьшение времени жизни WAL или он был запущен с конфигурацией без поддержки репликации.
    • Демон слейва был отстановлен на время большием чем указано в опции WAL_ttl_seconds на мастере
  • На слейве развернут демон с несовместимым форматом RocksDB. В этом случае поможет только перезалика через API демона.

Перезалить базу можно тремя способами:

  • Простое копирование базы после остановки мастера и слейва.
  • Копирование базы без остановки мастера
  • Слейв загружает базу через API мастера. Работает сильно дольше, на позволяет перезалить базу даже если формат не совместим.

Перезаливка базы без остановки мастера

Для того чтобы можно было безопасно скопировать файлы базы с мастера на слейв, нужно, перед копированием, дать команду на мастер, чтобы он временно прекратил любые манипуляции с файлами базы. После завершения копирования, нужно дать команду на отмену запрета и можно запускать слейв. Для упрощения этой процедуры можно использовать скрипт:

./rsync-db.sh master5-deb 600

Первым параметром передаем имя хоста мастера или ip, а вторым время в секундах на запрет манипуляции с файлами на мастере. Скрипт посылает через telnet команду на запрет нв манипуляцию с файлами на 600 секунд, потом копирует, с помощью rsync, файлы с мастера в локальную папку /monamour/prefixdb/* каталоги всех префиксов с мастера и, после этого, снимает запрет на изменения файлов. За указанное время скрипт должен успеть все скопировать, в противном случае возможно нарушение консистентности базы. Ограничение времени нужно на случай прерывания работы скрипта, чтобы мастер не остался в режиме запрета навсегда. Это время должно быть достаточным (возможно с запасом), но не слишком большим, желательно не более двух часов.

Этот скрипт работает только со стандартными портами (23003) и путями (/monamour/prefixdb/*), что легко можно изменить соответствующими изменениями в скрипте.

Порт 23003 предоставляет доступ к API в простом тектовом формате (не JSONRPC), например через telnet. Его нельзя использовать в продакшене, он урезан по функционалу, неоптимизирован по производительности и полноценно не тестировался. Но для админских нужд вполне сойдет. Подключаетесь по telnet к этому порту, набираете help и получаете список доступных комманд. Скрипт ./rsync-db.sh работает примерно следующим образом:

(echo "db 600"; sleep 1) | telnet master5-deb 23003
rsync ... #копируем файлы
(echo "cb"; sleep 1) | telnet master5-deb 23003

Здесь db и cb сокращенные варианты названия команд delay_background и continue_background соответсвенно. Они также доступны и через JSONRPC API.

Перезаливка базы отдельных префиксов без остановки мастера

Для этого достаточно указать список префиксов:

./rsync-db.sh master5-deb 600 префикс1 префикс2

или

(echo "db 600 префикс1 префикс2"; sleep 1) | telnet master5-deb 23003
rsync ... #копируем файлы
(echo "cb"; sleep 1) | telnet master5-deb 23003

Перезалив базы на слейв через API мастера

В двух словах, слейв вычитывает все данные мастера по JSONRPC, используя метод range. Т.е. сначала он долбит мастер вызовами range пока не вкачает все данные, а потом запускает механизм репликации используя вычитывание WAL. Подобные запросы люто не "любит" сама RocksDB, да и сам механизм JSONRPC не особо предназначен для передачи большого объема данных. Однако это единственный способ перезалить данные в базу не совместимую с мастером.

Несмотря на то, что есть возможность резвернуть слейв базой другого формата (сильно другая версия или с без/с подержкой TTL), делать это вне контекста процесса миграции, по меньшей мере, не разумно. Этот процесс описан в разделе миграция

Проверяем, что репликация работает

После запуска слейва внимательно смотрим лог:

2021-03-16 22:58:01.176 IOW      END       Client FAIL connected to addr:23002. 111 Connection refused

То это неверно указан адрес или порт мастера (демон продолжает работать! Предполагаем, что мастер просто упал или перезапускается. Как только порт будет открыт будет установленно соединнение).

Если адрес не указан (значение по умолчанию),то демон завершит работу с сообщением:

2021-03-06 01:22:04.347 SYSTEM   WARNING   !!! WFC ABORTED! Смотрите выше.
2021-03-06 01:22:04.347 FINAL     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
...
2021-03-16 23:13:16.732 FINAL     Message: In the configuration of the 'client-tcp1', you must set the 'addr' field or enable suspend mode
2021-03-06 01:22:04.347 FINAL     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

В этом случае необходимо в конфигурации указать хост мастера.

Если переносим базу с помощью slave-reload.sh (через API) то наблюдаем сообщения типа на время вычитывания базы:

2021-03-16 23:17:08.048 PrefixDB MESSAGE   Initial load...

Для каждого префикса сообщения в лог пишутся примерно раз в секунду, и если подобные сообщения прекратились, то можно делать вывод что загрузка базы из мастера завершена. Каждый префикс загружеаеться независимо и после переходит в нормальный режим.

Чтобы убедиться, что репликация работает в штатном режиме проверям:

2021-03-16 23:17:08.048 PrefixDB MESSAGE   1 (201) updates for 'mlm' in the last 1 seconds. Next seq №3315197030
2021-03-16 23:17:09.048 PrefixDB MESSAGE   No updates for 'mlm' in the last 1 seconds. Next seq №3315197030

Для каждого префикса подобные собщения пишутся в лог примерно раз в секунду. Сообщения типа No updates это нормально и означает, что за прошедшую секунду на мастере небыло обновлений для данного префикса. Однако если ни для одного префикса в течении нескольких десятков секунд не прилетает ни одного обновления, то это может означать, что на мастере нет нагрузки или нагрузка только на чтение. Если на мастере есть нагрузка, то скорее всего что-то пошло не так. Для начала просто перезапустите слейв и если ничего не изменилось, обновления не идут, то обращаейтесь к разработчику.

Сразу после заливки бызы с мастера (второй и третий способ), слейв будет отставать от мастера на время этой перезаливки и будет пытаться сократить это отставание. Если отставание велико, то в лог (раз в секунду) будут писаться сообщения типа:

2021-03-17 22:39:21.560 PrefixDB WARNING   Slave replication too big difference 'cshlp': 1208861(wrn:10000)

Важно убедиться, что отставание сокращаеться, а не нарастает. Для наглядности можно грепнуть по big:

tail -f /logs/slave.log | grep big

В какие-то моменты отставание может увеличиваться (несколько секунд) - это нормально, главное чтобы в общем оно сокращалось. Иначе, в конце концов, репликация у вас развалится. Что делать в этом случае, смотри следующий раздел.

Как определить, что произошла ошибка репликации

Обычно, демоны PrefixDB запущены с параметром автоматического перезапуска, с помощью моняторящего процесса, на случай падения. Но также там, обычно, установлен параметр для предотвращения "кувырканя", которая запрещает перезапуск демона если он проработал менее 10 минут (-a 600). Когда реплиикация разваливаеться, оснновной процесс демона завершает работу с ошибкой. Мониторящий процесс его перезапускает, но так как это не может восстановить репликацию, основной процесс повторно завершает работу, потому что он "понимает" невозможность продолжения репликация без потерь даных. А т.к. это происходит в пределах заданных 10 минут, моняторящий процесс повторно его не перезапускает. Это наиболее вероятный сценарий завершения работы слейва, но не единственный (как минимум, нужно убедиться, что места на диске не закончилось).

Для однозначного определения того, что демон завершил работу в результате развала репликации нужно посмотреть последние записи логов /logs/slave.log или /logs/master-slave.log в зависимости от текущей конфигурации (ну или просто самый свежий лог, если не уверены с какой конфигурацией был запущен демон). Если демон не грохнулся по segfaults, а в результате контролируемой ошибки после которой он не может продолжить корректную работу, то финальными записями лога будет вот такое красивое сообщение:

...
2021-03-06 01:22:04.347 SYSTEM   WARNING   !!! WFC ABORTED! Смотрите выше.
2021-03-06 01:22:04.347 FINAL     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2021-03-06 01:22:04.347 FINAL     ---------- Abnormal Shutdown! ----------
2021-03-06 01:22:04.347 FINAL     Date: 2021-03-06
2021-03-06 01:22:04.347 FINAL     Time: 01:22:03.348
2021-03-06 01:22:04.347 FINAL     Name: PrefixDB
2021-03-06 01:22:04.347 FINAL     Message: Slave replication error. Invalid master responce for 'adcfg' need sequence == 1, but last acceptable == 2 status=InvalidSeqNumber
2021-03-06 01:22:04.347 FINAL     !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

В данном примере части сообщения Slave replication error и status=InvalidSeqNumber однозначно сообщают о том, что репликация развалилась. Запись Invalid master responce for 'adcfg' говорит о том, что именно на префиксе 'adcfg' сломалась репликация. Это не значит, что на других префиксах все в порядке (демон завершает работу при ошибке репликации любого из префиксов). Разница между значениями need sequence и last acceptable показывает масштаб "трагедии". В данном примере пропущена всего одна запись, но этого достаточно чтобы зафиксировать ошибку репликации. На самом деле, с помощью опций PrefixDB можно задать допустимый предел пропусков (по умолчанию недопустимо), но делать этого в продакшене категорически не рекомендуеться (исключительно для отладки настроек).

Единственным вариантом решения этой проблемы являеться полная перезаливка всей базы, либо базы конкретного префикса. Перезаливка базы конкретных префиксов доступна для PrefixDB версии 0.9.2 (на мастере) и выше. Прежде чем принимать решение о презаливке конкретного префикса, нужно учесть следующие моменты (для примера возьмём тот же 'adcfg'):

  • Если база префикса 'adcfg', допустим, занимает 500Гб, а остальные префиксы, суммарно до 100Гб (условно), то проще перезалить всю базу, на время копирования базы это существенно не повлияет.
  • Если все префиксы укладываються в несколько десятков Гб (условно), то проще перезалить всю базу.
  • Если база префикса 'adcfg' небольшая, но есть огромные префиксы на 500Гб, то имеет смысл перезалить имеено 'adcfg'.
  • Если демон слейва не работал более двух часов, то вероятно за это время ошибка репликации возникнет и у остальных префиксов, поэтому делаем полный перезалив. Но это не точно. Если база 'adcfg' небольшая, то все равно можно попытаться перезалить только его и посмотреть, что будет. Если будет ошибка репликации на другом префиксе, и его база небольшая, то перезаливаем его Но если вы вышли в режим "и т.д.", то проще сделать полный перезалив.

Возможные причины приводящие к ошибке репликации

  • Слейв был остановлен на продолжительное время
  • Разовая большие операции на мастере (заливка большого объема данных)
  • Общее увеличение нагрузки (например, выкатили новый сервис в бой)
  • Мастер был запущен с не тем конфигом, или были внесены изменения в конфигурацию без учета работы слейва

Что делать если репликация развалилась

В двух словах RocksDB (это библиотека от facebook на базе которой построен демон PrefixDB) все измения первоночально пишет в WAL (лог перед записью) переодически его сбрасывая, после записи в хранилище (sst-файлы). Слейв, через API мастера, переодически (раз в секунду, настраивается) вычитывает эти данные, для обновления. По умолчаню время "жизни" WAL неопределено и RocksDB может его сбрасывать по своему усмотрению. Но для мастера конфигурация RocksDB (смотри master.ini или master-slave.ini) настроена так, чтобы гарантировано держать записи в WAL не мнее двух часов. За это отвечает поле:

WAL_ttl_seconds=7200

Если значение этого поля на мастере нулевое (смотри prefixdb.ini или slave.ini), то RocksDB работает с WAL по своему усмотрению, и слейв даже какое-то непродолжительное время может с ним работать, но потом все равно отвалиться, как только WAL обнулиться. Когда угодно это может произойти, но если на мастер идет хорошая нагрузка, то в течении нескольких минут или секунд. Если на мастер идут изменения (по всем префиксам) очень редко, то и сутки до развала могут пройти.

При переносе слейва на новый хост для работающей связки мастер-слейв этот параметр на мастере установлен в нужное значение. Если необходимо развернуть слейв для давно работающего PrefixDB без слейва, то скорее всего у него этот параметр нулевой. Его нужно перезапустить в режиме мастер master.sh start, или, если этого скрипта нет, что означает там работает устаревшая версия, и внести изменения вручную. Но скорее всего вам это нужно будет делать только при миграции мастера на обновленную версию (см. далее), а слейв подцеплять уже в штатном режиме (см. выше)

Итак, если репликация равалилась, нам нужно перезалить базу, неважно каким способом. Здесь расмотрим ситуации, что делать если репликация разваливаеться сразу (или почти сразу) после перезаливки.

В первую очередь убеждаемся, что мастер сконфигурирован как мастер. Виртуалка может называться как masterN, демон может быть запущен с конфигом master.conf, но не факт, что этот конфиг настроен на репликацию, поэтому:

  • заходим на хост мастера и в top или еще как смотрим имя конфиг-файла с которым запущен демон
  • в конфиге находим путь к ini-файлу для RocksDB (можно грепнуть по ini)
  • в ini-файле убеждаемся что WAL_ttl_seconds=7200 имеет ненулевое значение

Если на мастере свежая версия ./prefixdbd -v от 0.9.2 и выше то в текущем каталоге есть конфигурации и скрипты запуска в режиме мастера. Если нет, то вносим вручную изменения в INI файл и перезапускаем демон.

Далее расмотрим ситуации, если мастер сконфигурирован правильно, но при запуске слейва, репликация все равно разваливаеться через какое-то время:

  • Если репликация работает некоторое время, но потом отваливаеться, проверте по логам, отставание слейва от мастера со временем сокращаеться, а не увеличивается
  • Убедитесь что на диске есть место
  • Попробуйте вручную очистить директории /monamour/prefixdb и /monamour/prefixdb_slave и запустить перезалив удобным для вас способом
  • Убедитесь, что время копирования с помощью, например time rsync-db.sh master5-deb 600 укладывется в 10 мин, если нет, то задайте нужное время
  • Убедитесь, что интервал запуска ./slave.sh start после rsync-db.sh не привышет двух часов (или времени указаного в WAL_ttl_seconds на мастере).
  • Если перезаливаете третим способом с помощью ./slave-reload.sh убедитесь, что заливка укладываеться в два часа (или время указаного в WAL_ttl_seconds на мастере). Это можно проверить по времени последней записи в логе '...Initial load..' и первой записи в логе.

Небольшая ремарка. Если перезаливаете третим способом с помощью ./slave-reload.sh и время перезаливки превысило два часа, но при этом все работает корректно, демон продолжает работать и загружать данные, то не надо паниковать, останавливать демон и начинать все заного. Опция WAL_ttl_seconds=7200 задает нижнию границу, т.е. не менее двух часов, но может пройти неопределенно больше этого времени, чем WAL обнулится и репликация развалится, все очень сильно зависит от профиля нагрузки на мастер. Кроче, ждем пока демон сам отвалится, а не форсируем события.