/shard

Automatic sharding and resharidng

Primary LanguageLuaBSD 2-Clause "Simplified" LicenseBSD-2-Clause

Введение

Может работать в двух режимах:

  • Режим шардинга
  • Режим решардинга

Режим решардинга определяется по факту наличия двух функций выбора шарда (prev & curr).

Инсталляция

Весь функционал шардинга расположен в одном файле shard.lua.

Подключение производится при выполнении директивы в Вашем init.lua:

    require 'shard'

После этого момента становятся доступными функции box.shard.*.

Алгоритм

Шардинг

Пользователь определяет текущую функцию определения номера шарда (curr), а так же список шардов. Это действие необходимо провести при конфигурировании каждого шарда.

Далее при запросах по ключу в запросе при помощи вызова функции curr определяется номер шарда, на котором расположены нужные данные и выполняется запрос к этому шарду при помощи библиотеки box.net.box.

Решардинг

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

Далее при запросах работает следующий алгоритм:

  1. ключ извлекается из запроса
  2. определяется номер шарда curr.
  3. выполняются операции с шардом curr, если данных на curr нет, то выполняется операции с шардом prev.

Для того чтобы выполнить решардинг нужно произвести следующие операции:

  1. для всех хостов определить функции curr и prev, а так же обновить список шардов.
  2. Запустить процесс copy на всех хостах (этот процесс будет вытеснять данные с хоста prev на хост curr)
  3. По завершении процесса копирования, необходимо удалить функцию prev из конфигурации всех шардов
  4. Запустить процесс cleanup на всех шардах для удаления устаревших данных.
Процесс copy

Условия запуска:

  1. текущий шард помечен как rw
  2. на шарде прописаны обе функции curr и prev

Процесс запускается при вызове функции box.shard.copy(), далее этот процесс обходит все спейсы тарантула и для каждой записи каждого спейса выполняет вычисление номера шарда curr, а так же (при необходимости) номера шарда prev.

В случае если prev указывает на текущий шард, а curr указывает на другой шард, то выполняет copy_from (которая по сути является insert nothrow) тапла на шард curr (удаление тапла на своем шарде не производит). В случае если на шарде curr есть тапл с таким ключом, то insert не производится.

Функция box.shard.copy() возвращает список таплов каждый из которых содержит два поля:

  1. номер обработанного спейса
  2. количество скопированных в нем записей (может быть 0)
Процесс cleanup

Условия запуска:

  1. текущий шард помечен как rw
  2. на шарде прописана только одна функция - curr (то есть режим решардинга отключен)

Процесс запускается при вызове функции box.shard.cleanup(), далее этот процесс обходит все спейсы тарантула и для каждой записи каждого спейса выполняет вычисление номера шарда curr. Если номер шарда curr для записи не равен текущему номеру шарда me, то производится удаление этой записи.

Функция box.shard.cleanup() возвращает список таплов, каждый из которых содержит два поля:

  1. номер обработанного спейса
  2. количество удаленных в нем записей.

API

Конфигурирование

Конфигурирование выполняется одной функцией:

box.shard.schema.config({параметр = значение})

Конфигурирует (или переконфигурирует) шардхост (или прокси-хост), принимая следующие параметры:

  • list - список шардов и их типы
  • me - номер текущего шарда в списке доступов (либо 0, если данный шард является прокси)
  • mode - режим работы текущего шарда (ro, rw)
  • curr - функция, возвращающая по номеру спейса ключу номер шарда на котором должна быть расположена запись в текущей схеме шардирования.
  • prev - функция, возвращающая по номеру спейса и ключу номер шарда на котором должна быть расположена запись в предыдущей схеме шардирования.

Вызов функции box.shard.schema.config реконфигурирует только те параметры которые переданы при данном вызове. Прочие параметры оставляет в предыдущем состоянии.

Список шардов

Список шардов представляет собой lua табличку, которая представляет собой массив массивов, описывающих шарды.

Каждая запись, описывающая шард имеет следующие поля:

  1. режим работы шарда mode. Может иметь значения: ro - шард только для чтения, rw - шард для записи и чтения.
  2. вес шарда (число 0 - 1000)
  3. Параметры для коннекта к шарду (host, port)

Примечания:

  1. в описаниях каждого шарда не может быть более одного шарда в режиме rw
  2. модифицирующие операции всегда обращаются к хостам rw. Немодифицирующие могут обращаться к хостам ro или rw в зависимости от семантики вызова.

box.shard.select(space, key[, subkey, ...])

Выполняет запрос данных по ключу (ключ возможно составной). Вычисляет по переданным space:key значение номера шарда curr и переадресует этот запрос на нужный шард. В случае, если режим решардинга работает, а записи с данным ключем нет, то переадресует запрос так же шарду prev.

Примечания:

  1. в случае составного ключа он все должен быть полным (то есть выборки по частично определенному ключу нескольких записей невозможны)
  2. функция select может обращаться к хостам "только для чтения". При этом используется алгоритм выбора хоста в соответствии с весом хоста, определенным при конфигурировании.

box.shard.eselect(mode, space, key[, subkey, ...])

То же что и box.shard.select, но может принять параметр mode, который указывает что предпочтительнее при выборке. mode может принимать следующие значения:

  • ro - означает что предпочтительно делать выборку с хостов, помеченных как "только для чтения".
  • rw - означает что необходимо сделать выборку исключительно с хостов, помеченных как "для чтения и записи".

Вызов box.shard.eselect('ro', ...) полностью эквивалентен вызову box.shard.select.

box.shard.insert(space, ...)

Вставляет тапл по следующему алгоритму:

  1. извлекает ключ из переданного тапла
  2. вычисляет по ключу номер шарда (curr)
  3. передает управление хосту curr для выполнения дальнейших операций
  4. на хосте curr: если запись с таким ключом есть, то возвращает ошибку
  5. на хосте curr: делает запрос select на хост prev, если тот определен.
  6. на хосте curr: в случае если на хосте prev данная запись существует, то возвращает ошибку, иначе делает insert.

box.shard.replace(space, ...)

Вставляет (возможно заменяя) запись по следующему алгоритму.

  1. извлекает ключ из переданного тапла
  2. вычисляет по ключу номер шарда (curr)
  3. передает управление на хост curr для выполнения дальнейших операций
  4. делает вставку replace на шарде curr

box.shard.delete(space, key[, subkey, ...])

Удаляет запись

  1. передает управление хосту curr
  2. выполняет удаление на prev
  3. выполняет удаление на curr
  4. return exists_from_curr or exists_from_prev

box.shard.update(space, key, [subkey, ... ] format, … )

  1. передает управление хосту curr
  2. если запись присутствует, то выполняет return update( ... )
  3. выбирает запись с хоста prev
  4. если запись присутствует, то выполняет return update( ... )
  5. выполняет insert( {selected} ); return update( ... )

box.shard.call(mode, procname, ... )

  1. выбирает случайный шард
  2. выбирает хост в пределах выбранного шарда, удовлетворяющий mode
  3. делает вызов указанной функции на выбранном хосте