pgpro_scheduler — расширение Postgres Pro Enterprise для планирования заданийpgpro_scheduler позволяет планировать выполнение заданий и контролировать их исполнение в базе данных Postgres Pro Enterprise.
Задание — это набор SQL-команд. Расписание заданий задаётся либо строкой в стиле crontab, либо в виде объекта JSON. Также возможно определять расписание, используя оба эти способа.
Каждое задание имеет возможность вычислять время следующего своего запуска. Задание состоит из набора команд SQL, которые могут выполняться в одной транзакции, или каждая в отдельной. Кроме того, возможно задать SQL-оператор, который будет вызываться при ошибке в транзакции основного задания.
pgpro_scheduler — это обычное расширение Postgres Pro Enterprise без каких-либо особых предварительных требований.
Перед сборкой этого расширения убедитесь, что переменная окружения PATH включает путь к утилите pg_config. Также убедитесь, что у вас установлена версия Postgres Pro Enterprise для разработчиков или Postgres Pro Enterprise, собранный из исходного кода.
Процедура установки выглядит следующим образом:
$ cd pgpro_scheduler
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ psql dbname -c "CREATE EXTENSION pgpro_scheduler"
Это расширение определяет ряд переменных в Postgres Pro Enterprise (GUC), которые позволяют управлять его конфигурацией.
schedule.enable (boolean) Этот параметр определяет, включён ли планировщик в данной системе. Значение по умолчанию: false.
schedule.database (text) Этот параметр указывает, с какими базами может работать планировщик. Чтобы указать несколько баз, нужно перечислить их имена через запятую. Значение по умолчанию — пустая строка.
schedule.scheme (text) Этот параметр указывает, в какой схеме находятся служебные таблицы и функции планировщика. При изменении этого параметра требуется перезапустить сервер. Обычно менять его не следует, но это может потребоваться, если вы захотите запускать запланированные задания в базе горячего резерва. В этом случае можно определить на ведущей системе обёртку сторонних данных, чтобы подменить стандартную схему другой и использовать её на реплике, Значение по умолчанию: schedule.
schedule.nodename (text) Этот параметр задаёт имя узла для текущего экземпляра. Значение по умолчанию: master. Если вы используете это расширение в конфигурации с одним сервером, менять или задействовать его не нужно. Но это имя необходимо изменить, если вы запускаете планировщик в базе данных горячего резерва.
schedule.max_workers (integer) Этот параметр задаёт максимальное число одновременно работающих заданий в одной базе. Значение по умолчанию: 2.
schedule.transaction_state (text) Это внутренний параметр. Он содержит состояние выполненного задания. Данный параметр предназначен для использования в процедуре вычисления следующего времени запуска. Он может принимать значения:
success — транзакция завершилась успешно
failure — транзакция завершилась аварийно
running — транзакция в процесс выполнения
undefined — транзакция ещё не запускалась
Последние два значения обычно не должны попадать в пользовательскую процедуру вычисления времени. Если вы получили их, это признак внутренней ошибки в работе планировщика.
Вы можете управлять поведением планировщика, настраивая переменные Postgres Pro Enterprise, описанные выше.
Например, предположим, что у нас есть «свежая» инсталляция Postgres Pro Enterprise с установленным расширением scheduler. Мы хотим использовать планировщик с базами данных с именами database1 и database2. И хотим, чтобы в базе database1 могли выполняться одновременно 5 заданий, а в database2 — 3.
Добавьте следующую строку в postgresql.conf:
shared_preload_libraries = 'pgpro_scheduler'
Затем запустите psql и выполните следующие команды:
ALTER SYSTEM SET schedule.enable = true; ALTER SYSTEM SET schedule.database = 'database1,database2'; ALTER DATABASE database1 SET schedule.max_workers = 5; ALTER DATABASE database2 SET schedule.max_workers = 3; SELECT pg_reload_conf();
Если вам не нужно указывать разные значения в max_workers, вы можете сохранить одно значение в файле конфигурации и перезагрузить конфигурацию. Перезапускать сервер при этом не требуется.
Пример postgresql.conf:
shared_preload_libraries = 'pgpro_scheduler' schedule.enable = on schedule.database = 'database1,database2' schedule.max_workers = 5
Планировщик реализован как фоновый рабочий процесс, который динамически запускает другие фоновые процессы. Вот почему важно выбрать правильное значение для параметра max_worker_processes. Минимальное приемлемое значение для этого параметра можно рассчитать по следующей формуле:
min_processes = 1 + num_databases + max_workers[1] + ... + max_workers[num_databases]
Где:
min_processes — минимальное приемлемое количество фоновых рабочих процессов для данной системы. Учтите, что другим подсистемам (например, для выполнения подзапросов) также нужно запускать фоновые процессы. Поэтому полученное значение нужно скорректировать с учётом этих потребностей.
num_databases — число баз данных, с которыми будет работать планировщик
max_workers — значение переменной schedule.max_workers в контексте каждой базы данных
Это расширение использует SQL-схему schedule для хранения внутренних таблиц и функций. Прямой доступ к этим таблицам запрещён. Все изменения в них должны производиться через функции, реализуемые расширением.
Планировщик определяет два типа SQL и использует их как типы результата некоторых своих функций.
Этот тип содержит информацию о планируемом задании.
CREATE TYPE schedule.cron_rec AS(
id integer, -- идентификатор задания
node text, -- имя узла, на котором оно будет выполняться
name text, -- имя задания
comments text, -- комментарий к заданию
rule jsonb, -- правила расписания
commands text[], -- SQL-команды, которые будут выполнены
run_as text, -- имя пользователя, который будет выполнять задание
owner text, -- имя пользователя-владельца задания
start_date timestamp, -- нижняя граница окна выполнения задания
-- NULL, если дата начала не ограничена
end_date timestamp, -- верхняя граница окна выполнения задания
-- NULL, если дата окончания не ограничена
use_same_transaction boolean, -- true, если набор SQL-команд
-- будет выполняться в одной транзакции
last_start_available interval, -- макс. время, на которое может
-- откладываться запуск задания, если
-- доступных фоновых процессов нет
max_instances int, -- макс. число экземпляров задания, которые
-- могут быть запущены одновременно
max_run_time interval, -- макс. время выполнения
onrollback text, -- SQL-команда, которая будет выполнена
-- в случае сбоя выполнения задания
next_time_statement text, -- SQL-команда, которая будет выполнена
-- по завершении транзакции для вычисления
-- следующего времени запуска
active boolean, -- true, если задание может запускаться по расписанию
broken boolean -- true, если в конфигурации задания есть ошибки,
-- препятствующие его дальнейшему выполнению
);Этот тип содержит информацию о выполнении запланированного задания.
CREATE TYPE schedule.cron_job AS(
cron integer, -- идентификатор задания
node text, -- имя узла, на котором оно будет выполняться
scheduled_at timestamp, -- запланированное время выполнения
name text, -- имя задания
comments text, -- комментарий к заданию
commands text[], -- SQL-команды, которые будут выполнены
run_as text, -- имя пользователя, который будет выполнять задание
owner text, -- имя пользователя-владельца задания
use_same_transaction boolean, -- true, если набор SQL-команд
-- будет выполняться в одной транзакции
started timestamp, -- время, когда задание было запущено
last_start_available timestamp, -- время, к которому задание должно быть запущено
finished timestamp, -- время, когда задание было завершено
max_run_time interval, -- макс. время выполнения
max_instances int, -- макс. число одновременно выполняемых экземпляров
onrollback text, -- SQL-оператор, выполняемый при сбое транзакции
next_time_statement text, -- SQL-оператор, вычисляющий время следующего запуска
status text, -- состояние задания: working (выполняется),
-- done(завершено), error (ошибка)
message text -- сообщение об ошибке
);schedule.create_job(cron text, sql text, node text)
Создаёт задание и делает его активным.
Аргументы:
cron — строка в стиле crontab, задающая график выполнения
sql — SQL-оператор, который будет выполнен
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(cron text, sqls text[], node text)
Создаёт задание и делает его активным.
Аргументы:
cron — строка в стиле crontab, задающая график выполнения
sqls — набор SQL-операторов, которые будут выполнены
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(date timestamp with time zone, sql text, node text)
Создаёт задание и делает его активным.
Аргументы:
date — точная дата выполнения
sql — SQL-оператор, который будет выполнен
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(date timestamp with time zone, sqls text[], node text)
Создаёт задание и делает его активным.
Аргументы:
date — точная дата выполнения
sqls — набор SQL-операторов, которые будут выполнены
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(dates timestamp with time zone[], sql text, node text)
Создаёт задание и делает его активным.
Аргументы:
dates — набор дат выполнения
sql — SQL-оператор, который будет выполнен
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(dates timestamp with time zone[], sqls text[], node text)
Создаёт задание и делает его активным.
Аргументы:
dates — набор дат выполнения
sqls — набор SQL-операторов, которые будут выполнены
node — имя узла, может отсутствовать
Возвращает идентификатор созданного задания.
schedule.create_job(data jsonb)
Создаёт задание и делает его активным.
В единственном аргументе этой функции передаётся объект JSONB с информацией о задании.
Этот объект может содержать следующие ключи, некоторые из которых могут быть опущены:
name — имя задания;
node — имя узла;
comments — комментарий к заданию;
cron — строка в стиле crontab, задающая график выполнения;
rule — параметры плана в виде объекта JSONB (см. описание ниже);
command — SQL-оператор, который будет выполнен;
commands — набор SQL-операторов, которые будут выполнены, в виде массива;
run_as — пользователь, от имени которого будут выполняться команды;
start_date — начало интервала, в котором возможно выполнение запланированной команды, может быть NULL;
end_date — конец интервала, в котором возможно выполнение запланированной команды, может быть NULL;
date — конкретная дата для выполнения команды;
dates — набор определённых дат, когда запланировано выполнение команд;
use_same_transaction — true, если набор команд будет выполняться в одной транзакции. Значение по умолчанию: false;
last_start_available — на какое время может быть отложено выполнение команд, если в запланированный момент оказался достигнут предел одновременно выполняющихся заданий. Время задаётся в формате типа interval. Например, со значением '00:02:34' ожидание может продолжаться 2 минуты 34 секунды. Если это поле содержит NULL или не задано, время ожидание не ограничено. Значение по умолчанию: NULL;
max_run_time — определяет максимальную длительность выполнения задания. Задаётся в формате типа interval. Если это поле содержит NULL или не задано, время не ограничивается. Значение по умолчанию — NULL;
onrollback — SQL-оператор, который будет выполняться при сбое основной транзакции. По умолчанию оператор не определён;
next_time_statement — SQL-оператор, который будет выполнен для вычисления следующего времени запуска задания.
Правила расписания можно задать в виде строки в стиле crontab (ключ cron) или в виде объекта JSONB (ключ rule).
Этот объект может содержать следующие ключи:
minutes — минуты; массив целых чисел в диапазоне 0 .. 59;
hours — часы; массив целых чисел в диапазоне 0 .. 23;
days — дни месяца; массив целых чисел в диапазоне 1 .. 31;
months — месяцы; массив целых чисел в диапазоне 1 .. 12;
wdays — дни недели; массив целых чисел в диапазоне 0 .. 6 (0 — воскресенье);
onstart — целое значение 0 или 1; если это значение равно 1, задание будет выполняться только один раз при запуске планировщика;
Задание может также быть запланировано на конкретную дату или на набор дат. Используйте для этого ключи date и dates, соответственно.
Все эти методы планирования можно использовать одновременно, но как минимум один вариант использовать обязательно.
Поле next_time_statement может содержать SQL-оператор, который будет выполняться после основной транзакции для вычисления времени следующего запуска. Если этот ключ определён, время первого запуска задания будет рассчитано по методам, описанным выше, но последующие запуски будут запланированы на то время, которое вернёт этот оператор. Данный оператор должен вернуть запись, содержащую в первом поле значение типа timestamp with time zone. Если возвращаемое значение имеет другой тип или при выполнении оператора происходит ошибка, задание помечается как давшее сбой, и дальнейшее его выполнение отменяется.
Этот оператор будет выполняться при любом состоянии завершения основной транзакции. Получить состояние завершения транзакции в нём можно из переменной Postgres Pro Enterprise schedule.transaction_state.
Возможные значения переменной:
success — транзакция завершилась успешно
failure — транзакция завершилась с ошибкой
running — транзакция в процесс выполнения
undefined — транзакция ещё не началась
Последние два значения не должны появляться в процедуре, указанной в ключе next_time_statement.
SQL-операторы, которые будут выполняться, можно задать в ключе command или commands. Первый ключ позволяет задать только один оператор, а второй — набор операторов. В действительности и первый ключ может содержать в одной строке набор команд, разделённых точкой с запятой. В этом случае все эти команды будут выполняться в одной транзакции, независимо от значения use_same_transaction. Поэтому для набора операторов лучше использовать ключ commands, так как это позволяет более гибко управлять их выполнением.
Возвращает идентификатор созданного задания.
schedule.set_job_attributes(job_id integer, data jsonb)
Изменяет свойства существующего задания.
Аргументы:
job_id — идентификатор существующего задания;
data — объект JSONB с набором изменяемых свойств. Ключи их структура показаны в описании функции schedule.create_job.
Эта функция возвращает булевское значение:
true — свойства изменены успешно;
false — свойства не были изменены.
Чтобы изменить свойства задания, пользователь должен быть суперпользователем или владельцем этого задания.
schedule.set_job_attribute(job_id integer, name text, value text || anyarray)
Изменяет свойство существующего задания.
Аргументы:
job_id — идентификатор существующего задания;
name — имя свойства
value — значение свойства
Полный список свойств можно увидеть в описании функции schedule.create_job. Некоторые значения представляются массивами, и их надо передавать в таком виде. Но если свойство — не массив, а передано как массив, возникнет ошибка.
Эта функция возвращает булевское значение — true в случае успеха или false при ошибке.
Чтобы изменить свойства задания, пользователь должен быть суперпользователем или владельцем этого задания.
schedule.deactivate_job(job_id integer)
Деактивирует задание и приостанавливает его последующее выполнение.
Аргументы:
job_id — идентификатор существующего задания;
Возвращает true в случае успеха или false при ошибке.
schedule.activate_job(job_id integer)
Активирует задание, в результате чего оно начинает выполняться по расписанию.
Аргументы:
job_id — идентификатор существующего задания;
Возвращает true в случае успеха или false при ошибке.
schedule.drop_job(job_id integer)
Удаляет задание.
Аргументы:
job_id — идентификатор существующего задания;
Возвращает true в случае успеха или false при ошибке.
schedule.get_job(job_id integer)
Удаляет задание.
Аргументы:
job_id — идентификатор существующего задания;
Возвращаемое значение имеет тип cron_rec. Описание этого типа можно найти в Подразделе F.38.6.
schedule.get_user_owned_cron(username text)
Получает список заданий, принадлежащих указанному пользователю.
Аргументы:
username — имя пользователя, может отсутствовать
Возвращает набор записей типа cron_rec. Эти записи содержат информацию о заданиях, принадлежащих пользователю. Если имя пользователя опущено, подразумевается имя текущего пользователя сеанса. Получать задания, принадлежащие другому пользователю, может только суперпользователь.
Описание типа cron_rec можно найти в Подразделе F.38.6.
schedule.get_user_cron(username text)
Получает список заданий, выполняемых указанным пользователем.
Аргументы:
username — имя пользователя, может отсутствовать
Возвращает набор записей типа cron_rec. Эти записи содержат информацию о заданиях, выполняемых пользователем. Если имя пользователя опущено, подразумевается имя текущего пользователя сеанса. Получать задания, выполняемые другим пользователем, может только суперпользователь.
Описание типа cron_rec можно найти в Подразделе F.38.6.
schedule.get_user_active_jobs(username text)
Возвращает список заданий, выполняемых в данный момент указанным пользователем.
Аргументы:
username — имя пользователя, может отсутствовать
Если имя пользователя опущено, подразумевается имя текущего пользователя сеанса. Получать задания, выполняемые другим пользователем, может только суперпользователь.
В качестве результата возвращается набор записей типа cron_job. Описание этого типа можно найти в Подразделе F.38.6.
schedule.get_active_jobs()
Возвращает список заданий, выполняемых в текущий момент. Вызывать эту функцию может только суперпользователь.
В качестве результата возвращается набор записей типа cron_job. Описание этого типа можно найти в Подразделе F.38.6.
schedule.get_log()
Возвращает список всех завершённых заданий. Вызывать эту функцию может только суперпользователь.
В качестве результата возвращается набор записей типа cron_job. Описание этого типа можно найти в Подразделе F.38.6.
schedule.get_user_log()
Возвращает список завершённых заданий, выполненных указанным пользователем.
Аргументы:
username — имя пользователя, может отсутствовать
Если имя пользователя опущено, подразумевается имя текущего пользователя сеанса. Получать список заданий, выполненных другим пользователем, может только суперпользователь.
В качестве результата возвращается набор записей типа cron_job. Описание этого типа можно найти в Подразделе F.38.6.
schedule.clean_log()
Удаляет все записи с информацией о завершённых заданиях. Вызывать эту функцию может только суперпользователь.
Возвращает число удалённых записей.