Асинхронная обработка данных и команда CONSUME

Основная идея асинхронной обработки данных в контексте СУБД заключается в том, чтобы сократить время выполнения транзакций, тем самым решая проблему ожидания на блокировках ресурсов, которые используются конкурирующими за них процессами.

Это особенно актуально для OLTP (оперативных) баз данных.

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

Технически реализация идеи выглядит следующим образом: в рамках основной транзакции выполняется формирование заданий или сообщений с необходимыми для их выполнения данными. Эти задания (сообщения) записываются в специальную таблицу-очередь. Таким образом часть обработки данных делегируется основной транзакцией другим процессам, работа которых может быть организована, например, по расписанию в периоды наименьшей загруженности СУБД.

Схема вспомогательной таблицы-очереди и способы её обработки могут быть очень разнообразны. Это целиком и полностью зависит от решаемой задачи. Тем не менее следует обратить внимание на следующие моменты или типовые функциональные требования:

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

Блокировки СУБД при записи в таблицу-очередь

Отсутствие каких-либо блокировок при записи в таблицу-очередь возможно только при использовании команды INSERT. Во всех остальных случаях возникает риск конкурентного доступа к соответствующим записям со стороны разных процессов. Оценка возможности и допустимости этих рисков должна быть выполнена на этапе проектирования асинхронной обработки данных для каждого конкретного случая отдельно.

Последовательность обработки и/или приоритизация

Последовательная запись в таблицу-очередь может быть реализована при помощи специализированных объектов СУБД, например, IDENTITY или SEQUENCE. Другими словами необходимо использовать какой-то генератор последовательности чисел или иных значений. При этом следует иметь ввиду, что несколько изменений одного и того же объекта в базе данных может порождать множественную запись в таблицу-очередь, отражающую данные события, то есть генератор последовательности в данном случае должен это учитывать и грантировать уникальность своих значений. Разрез или пространство уникальности таких значений определяется существующими функциональными требованиями для данного вида объектов.

Кроме этого, важно упомянуть о том, что последовательноcть обработки одних и тех же объектов СУБД может быть гарантирована механизмами изоляции транзакций и блокирования ресурсов базы данных, например, по их уникальным ключам.

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


Подводя итог вышесказанному, фиксируем следующее:
  • Основная транзакция делегирует обработку данных
  • Используется вспомогательная таблица-очередь
  • Запись в таблицу-очередь выполняется в транзакции

Обработка записей таблицы-очереди (потребление)

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

В первом случае записи выбираются и обрабатываются при помощи команды SELECT, а затем обработанные записи помечаются как утилизированные (потреблённые) при помощи команды UPDATE (обновляется специальное поле статуса таблицы-очереди). Обычно это делается в единой транзакции процесса обработки очереди.
После этого, чаще всего по расписанию, как правило уже другой процесс, выполняет физическое удаление обработанных записей из таблицы-очереди при помощи команды DELETE.
Примечание:
Обработка данных может быть организована таким образом, что записи таблицы-очереди могут иметь несколько статусов и за обработку каждого статуса может отвечать отдельный процесс.

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

Команда CONSUME языка запросов DaJet QL

Команда CONSUME языка запросов DaJet QL реализует семантику деструктивного чтения для баз данных 1С:Предприятие 8, работающих под управлением СУБД MS SQL Server и PostgreSQL.

Транспайлер DaJet преобразует команду CONSUME в соответствующие этим СУБД синтаксические конструкции, соблюдая все необходимые семантические требования команды, средствами этих СУБД.

Транзакционную работу с записями таблицы-очереди при помощи команды CONSUME в рамках подсистемы DaJet Flow Pipelines реализует блок-потребитель (источник данных) OneDbConsumer:

Синтаксис команды CONSUME

Синтаксис команды аналогичен синтаксису команды SELECT.

CONSUME TOP <размер пакета>
[WITH {STRICT|RANDOM} ORDER]
<список полей>
[INTO <временная таблица>]
FROM <таблица>
[{LEFT|INNER} JOIN <таблица> ON <условие>]
WHERE <отбор>
ORDER BY <последовательность>

<размер пакета> - количество записей таблицы-очереди, возвращаемых клиенту и обрабатываемых им в рамках одной транзакции, например, блоком OneDbConsumer.

<список полей> - имена полей в терминах 1С:Предприятие 8, значения которых возвращаются клиенту, например, блоку OneDbConsumer.

<таблица> - идентификатор таблицы-очереди в терминах 1С:Предприятие 8, например, РегистрСведений.ИсходящаяОчередь.

<отбор> - фильтрация записей таблицы по значениям её полей.
Использование выражения WHERE команды CONSUME необязательно.

<последовательность> - список полей таблицы для сортировки её записей, определяющей последовательность их обработки.
Использование выражения ORDER BY команды CONSUME необязательно.

Следующий пример удаляет первые 1000 записей таблицы-очереди и возвращает их клиенту для обработки. Отбор записей выполняется по коду получателя сообщений, а последовательность обработки сообщений определяется по их приоритету и порядковому номеру.

CONSUME TOP 1000
  НомерСообщения,
  ТипСообщения,
  ТелоСообщения
FROM
  РегистрСведений.ИсходящаяОчередь
WHERE
  Получатель = 'POS-0333'
ORDER BY
  Приоритет      ASC,
  НомерСообщения ASC

Следующий пример возвращаемых данных демонстрирует ситуацию, когда приоритет обработки записей, содержащих данные справочника "Номенклатура", определяется приложением выше, чем документа "ЗаказКлиента". При этом мы видим, что один и тот же заказ клиента с номером "WEB-1111" был зарегистрирован для обработки два раза, например, по причине добавления в него нового товара, но обработка этих изменений будет выполнена согласно последовательности, определяемой неким генератором номеров сообщений. В данном примере формат сообщений - JSON.

10Справочник.Номенклатура{ "Код": "WEB-1234", ... }
17Справочник.Номенклатура{ "Код": "WEB-4321", ... }
12Документ.ЗаказКлиента{ "Номер": "WEB-1111", ... }
19Документ.ЗаказКлиента{ "Номер": "WEB-1111", ... }