1.3. Программный интерфейс репозитория | Tdg
Tarantool
Check out the new release policy
1. Руководство по разработке приложений 1.3. Программный интерфейс репозитория

1.3. Программный интерфейс репозитория

Пользовательский код в TDG исполняется в изолированной среде — так называемой «песочнице» (sandbox). При этом для доступа к данным должны использоваться функции программного интерфейса репозитория (repository API).

Все запросы к данным в TDG от внешних информационных систем, включая GraphQL-запросы (а также запросы, выполняемые при помощи вкладки GraphQL), также реализованы на основе функций программного интерфейса репозитория.

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

  • repository.find — выборка по полям объектов одного типа, включая несколько условий;
  • repository.get — выборка по индексу;
  • repository.put — вставка нового или замена существующего объекта;
  • repository.update — обновление объектов модели;
  • repository.delete — удаление объектов модели;
  • repository.call_on_storage — вызов функции на экземпляре с ролью storage для локальной обработки данных;
  • repository.push_job — асинхронный запуск задач/функций;
  • repository.map_reduce — сложная агрегация по кластеру.

Синтаксис функций:

repository.find(type_name, filter, options, context)

repository.get(type_name, index_name, value, options, context)

repository.put(type_name, object, options, context)

repository.update(type_name, filter, updaters, options, context)

repository.delete(type_name, filter, options, context)

repository.call_on_storage(type_name, index_name, value, func_name, func_args, options, context)

repository.push_job(name, arguments, options, context)

repository.map_reduce(type_name, filter, version, map_fn, combine_fn, reduce_fn, opts)

Функции get, find, update, delete и map-reduce поддерживают порядковую нумерацию страниц (пагинацию) с помощью параметров first и after.

Чтобы фильтровать объекты, запросы используют условия-предикаты (filter) — булевы выражения, синтаксис которых описан ниже.

1.3.1. Синтаксис предикатов

В запросах предикаты (filter) записываются в виде:

{{left, comparator, right}, ...}

где:

  • left и right — это левая и правая части выражения.

    Правая часть выражения (right) может содержать:

    • либо полный путь к полю объекта ($foo.bar), где имя поля начинается со знака $;
    • либо строковое или численное значение.

    Левая часть (left) может содержать только полный путь к полю.

  • comparator — оператор сравнения: ">", ">=", "==", "<=" или "<".

Если в предикате несколько условий, по умолчанию они объединяются логической операцией and (конъюнкцией).

Примеры предикатов:

{{"$id", ">", 10}}
{{"$id", ">", 10}, {"$id", "<", 100}}
{{"$name", "==", "foo"}, {"$birth_year", "==", 1990}}
{{"$name", "==", "foo"}, {"$reg_date", "==", {1990, 04, 23}}}

1.3.2. Общее хранилище

Для передачи объектов между функциями и экземплярами TDG используйте общее хранилище (shared storage). При помощи следующей команды подключите существующее или создайте новое (если хранилище с таким именем ещё не существует) общее хранилище:

local shared_storage_object = shared_storage.new('some_namespace')

Переменная shared_storage_object в данном примере содержит указатель на созданное общее хранилище.

Примечание

При создании общего хранилища создаётся спейс, который хранится на одном из наборов реплик с ролью storage. Однако персистентность данных в общем хранилище не гарантируется — данные из него могут быть потеряны, например, при перезапуске кластера.

Данные в общее хранилище помещаются в формате key, value, например, следующей командой:

shared_storage_object:set('abc', 123)

где 'abc' — это ключ (key), а 123 — значение (value).

Для получения данных из общего хранилища выполните следующую команду:

shared_storage_object:get('abc')

1.3.3. Исторические данные

Запросы позволяют получить или обработать объекты, которые предшествуют или равны определённой версии. За это отвечает параметр version в options.

Примечание

Пример: Допустим, существуют версии 1, 3 и 5 объекта, а мы запросили версию 4. В таком случае, мы получим версию либо равную запрошенной, либо ближайшую предшествующую, в данном случае версию 3.

При получении данных параметр version определяет версию получаемого объекта. Если параметр version не задан, то будет получена последняя версия объекта.

При обновлении данных параметр version определяет изменяемую версию объекта. Если задано значение параметра version, то запрос получит указанную версию объекта (или ближайшую предшествующую, см. пример выше), выполнит обновление и сохранит результат с той же версией. Если параметр version не задан, то запрос получит последнюю версию объекта, обновит её и сохранит с новой версией, значение которой будет взято по умолчанию (большое целое монотонно возрастающее число).

При вставке (добавлении или замене) объекта параметр version определяет версию объекта, который будет добавлен или заменён. Если параметр version не задан, то используется значение по умолчанию (см. выше).

При удалении данных параметр version определяет удаляемую версию объекта. Если параметр version не задан, то будет удалена последняя версия объекта.

Для запросов на выборку или удаление объектов (find или delete), при установленном флаге all_versions (значение true) обработка выполняется для всех версий объекта. Если задан параметр version, то обрабатываются все версии меньше или равные заданной.

1.3.4. Оптимистичные блокировки

В случае конкурентной модификации объекта, когда изменения в один объект могут вноситься параллельно из разных обработчиков, что может вызывать конфликты, необходимо использовать механизм оптимистичных блокировок, основанный на версиях объектов. Для этого в options запроса задайте значение параметра only_if_version, и запрос выполнится только в случае, если данная версия объекта является последней и совпадает с заданной в запросе.

Пример:

repository.put("users", {id = "100500", name = "Kirito777"}, {version = 6, only_if_version = 5})

Этот запрос попытается выполнить поиск по первичному ключу (в данном случае — id). Если запись с таким значением первичного ключа будет найдена, то она будет обновлена только в том случае, если версия объекта в системе на момент исполнения совпадёт со значением, переданным в параметре only_if_version (в данном примере это 5).

После успешного исполнения запроса последняя версия объекта станет равна 6. Если запись с переданным значением первичного ключа отсутствует, то новая запись добавлена не будет, так как версия несуществующей записи не может быть равна 5.

При вставке новых объектов можно использовать параметр if_not_exists, который также задается в options запроса. Параметр имеет тип boolean (значения true или false). Может использоваться только с функцией repository.put().

Если задано значение true, система проверит, существует ли уже такой объект, и только при условии его отсутствия новый объект будет добавлен в хранилище. Если параметр не задан, это равносильно значению false.

Пример:

repository.put("users", {id = "100500", name = "Kirito777"}, if_not_exists = true)

Примечание

Параметры only_if_version и if_not_exists взаимоисключающие и не могут использоваться вместе в одном запросе. В этом случае система выдаст ошибку запроса.

1.3.5. Функции программного интерфейса репозитория

1.3.5.1. Выборка по полям объектов одного типа (find)

Синтаксис

repository.find(type_name, filter, options, context)

где:

  • type_name — тип объекта;
  • filter — список условий-предикатов для выбора (фильтрации) объектов указанного типа;
  • options — параметры для управления запросом:
    • first — количество элементов;
    • after — курсор пагинации на первый элемент;
    • version — версия объекта;
    • all_versions — указатель для поиска по всем версиям объекта, если задано значение true;
    • mode — определяет целевой экземпляр для выполнения запроса. Возможные значения: read и write. Если задано write, целью будет мастер;
    • prefer_replica — определяет целевой экземпляр для выполнения запроса. Возможные значения: true и false. Если задано true, то предпочитаемая цель — одна из реплик. Если доступной реплики нет, то целью будет мастер. Опция полезна для ресурсозатратных функций, чтобы избежать замедления работы мастера;
    • balance — управление балансировкой нагрузки. Возможные значения: true и false. Если задано true, добавится балансировка нагрузки — запросы на чтение распределяются по всем узлам набора реплик по кругу. Если при этом параметр prefer_replica определен как true, предпочтение отдается репликам;
  • context — контекст выполнения запроса:
    • tenant — тенант.

Пример

repository.find(
  "Client",
  {{"$id", "==", 42}}
)

1.3.5.2. Выборка по индексу (get)

Синтаксис

repository.get(type_name, index_name, value, options, context)

где:

  • type_name — тип объекта;
  • index_name — имя индекса;
  • value — значение ключа поиска;
  • options — параметры для управления запросом:
    • first — количество элементов;
    • after — курсор пагинации на первый элемент;
    • version — версия объекта;
    • all_versions — указатель для поиска по всем версиям объекта, если задано значение true;
    • mode — определяет целевой экземпляр для выполнения запроса. Возможные значения: read и write. Если задано write, целью будет мастер;
    • prefer_replica — определяет целевой экземпляр для выполнения запроса. Возможные значения: true и false. Если задано true, то предпочитаемая цель — одна из реплик. Если доступной реплики нет, то целью будет мастер. Опция полезна для ресурсозатратных функций, чтобы избежать замедления работы мастера;
    • balance — управление балансировкой нагрузки. Возможные значения: true и false. Если задано true, добавится балансировка нагрузки — запросы на чтение распределяются по всем узлам набора реплик по кругу. Если при этом параметр prefer_replica определен как true, предпочтение отдается репликам;
  • context — контекст выполнения запроса:
    • tenant — тенант.

Пример

repository.get("Client", "id", 42)

1.3.5.3. Запрос на добавление данных (put)

Запрос put позволяет вставить новые данные или заменить существующие. Он поддерживает версионирование и оптимистичные блокировки.

Если отсутствует параметр version, то используется значение по умолчанию (большое целое монотонно возрастающее число).

При заданном параметре version запрос выполняет добавление или замену объекта с заданной версией (обычно используется целое число).

При указанном параметре only_if_version запрос добавляет или заменяет объект только в том случае, если последняя версия объекта совпадает с указанной.

Синтаксис

Запрос на вставку объекта выглядит следующим образом:

repository.put(type_name, object, options, context)

где:

  • type_name — тип объекта;
  • object — объект для вставки;
  • options — параметры для управления запросом:
    • version — версия объекта;
    • only_if_version — проверка имеющейся версии перед вставкой;
  • context — контекст выполнения запроса:
    • tenant — тенант.

Пример

repository.put("users", {id = "100500", name = "Kirito777"})

Данный запрос из примера добавит новый объект с первичным ключом id, равным 100500, если таковой ещё не существует. При этом значение параметра version будет равным значению по умолчанию (большое целое монотонно возрастающее число). Если объект с таким первичным ключом уже есть, то будет добавлена новая версия объекта со значением параметра version, равным значению по умолчанию.

1.3.5.4. Запрос на обновление данных (update)

Запрос на обновление данных (update) поддерживает версионирование и выполняется в две стадии:

  • исполнение запроса на каждом хранилище, которое может содержать объекты по условию, передаваемому аргументом filter;
  • сбор результатов на роутере.

Синтаксис

Запрос на обновление объекта выглядит следующим образом:

repository.update(type_name, filter, updaters, options, context)

где:

  • type_name — тип объекта;

  • filter — список условий-предикатов для выбора (фильтрации) объектов указанного типа;

  • updaters — список обновлений для объекта, состоящий из списка мутаторов {{mutator, path, new_value}, ...}, где:

    • mutator — имя мутатора, например:

      • set — устанавливает значение;
      • add — увеличивает значение на указанное число;
      • sub — уменьшает значение на указанное число;
    • path — строковый путь до поля объекта с точкой-разделителем (.).

      Путь до объекта(ов) массива должен включать индекс массива или символ * для захвата всех подобъектов;

    • new_value` — новое значение;

  • options — параметры для управления запросом:

    • first — количество элементов для обновления;
    • after — курсор пагинации на первый элемент;
    • version — версия объекта;
    • only_if_version — проверка имеющейся версии перед вставкой;
    • dont_skip_deleted — использовать в поиске удаленные версии объектов;
  • context — контекст выполнения запроса:

    • tenant — тенант.

Примеры

Для модели, рассмотренной в примере:

  • Чтобы обновить имя клиента с идентификатором 42, используйте следующий запрос:

    repository.update(
     "Client",
     {{"$id", "==", 42}},
     {{"set", "first_name", "John"},
      {"set", "last_name", "Doe"}})
    
  • Если у того же клиента истек срок действия первого паспорта и необходимо обновить соответствующее поле expired_flag, используйте следующий запрос:

    repository.update(
     "Client",
     {{"$id", "==", 42}},
     {{"set", "passports.1.expired_flag", "true"}}
    

    где .1. — индекс массива, содержащего экземпляры сущности Passport агрегата Client, т.е. первый паспорт клиента.

1.3.5.5. Запрос на удаление объектов (delete)

Чтобы прозрачно и удобно исключить объект из потенциальных результатов запросов, используйте запрос на удаление (delete). Он помещает объект или его версию в «удаленное состояние» (добавляя флаг delete:true), что исключает его из выборки.

Запрос на удаление поддерживает версионирование и оптимистичные блокировки.

Синтаксис

Чтобы удалить объект, используйте следующий запрос:

repository.delete(type_name, filter, options, context)

где:

  • type_name — имя типа объекта для удаления;
  • filter — список условий-предикатов для выбора (фильтрации) объектов указанного типа;
  • options — параметры для управления запросом:
    • first — количество элементов для удаления;
    • after — курсор пагинации на первый элемент;
    • version — версия объекта для копирования в удаленное состояние;
    • only_if_version — проверка имеющейся версии перед вставкой;
    • all-versions — этот флаг для функции удаления (delete) нельзя использовать без флага permanent_delete;
    • permanent_delete — при установленном флаге (значение True) происходит физическое удаление информации об объекте из TDG. Используется вместе с флагом all-versions;
  • context — контекст выполнения запроса:
    • tenant — тенант.

Пример

Чтобы удалить клиентов с именем «QWERTY» для модели из примера, используйте следующий запрос:

repository.delete(
 "Client",
 {{"$first_name", "==", "QWERTY"}})

В результате такого запроса все объекты, удовлетворяющие условию (first name равное QWERTY), будут дополнены новой версией (значение версии по умолчанию) с флагом delete:true.

1.3.5.6. Вызов функции на экземпляре с ролью storage (call_on_storage)

Синтаксис

repository.call_on_storage(type_name, index_name, value, func_name, func_args, options, context)

где:

  • type_name — тип объекта;
  • index_name — имя индекса;
  • value — значение ключа поиска;
  • func_name — имя вызываемой функции;
  • func_args — аргументы, которые передаются в вызываемую функцию;
  • options — параметры для управления запросом:
    • timeout — время ожидания выполнения запроса, секунды. Значение не должно быть больше, чем значение параметра конфигурации vshard_timeout;
    • mode — определяет целевой экземпляр для выполнения запроса. Возможные значения: read и write. Если задано write, целью будет мастер;
    • prefer_replica — определяет целевой экземпляр для выполнения запроса. Возможные значения: true и false. Если задано true, то предпочитаемая цель — одна из реплик. Если доступной реплики нет, то целью будет мастер. Опция полезна для ресурсозатратных функций, чтобы избежать замедления работы мастера;
    • balance — управление балансировкой нагрузки. Возможные значения: true и false. Если задано true, добавится балансировка нагрузки — запросы на чтение распределяются по всем узлам набора реплик по кругу. Если при этом параметр prefer_replica определен как true, предпочтение отдается репликам;
  • context — контекст выполнения запроса:
    • tenant — тенант.

1.3.5.7. Асинхронный запуск задач/функций (push_job)

Синтаксис

repository.push_job(name, arguments, options, context)

где:

  • name — имя функции/пайплайна, определенное в файле конфигурации;
  • arguments — аргументы функции;
  • options — параметры для управления запросом (не используется);
  • context — контекст выполнения запроса (не используется).