F.39. pg_cron#

F.39. pg_cron

F.39. pg_cron #

F.39.1. О расширении pg_cron #

Версия: 1.6.2

Страница проекта www.citusdata.com

GitHub

Copyright © Citus Data, Inc.

F.39.2. Что такое pg_cron? #

pg_cron - это простой планировщик заданий на основе cron для Tantor SE-1C который работает внутри базы данных в качестве расширения. Он использует ту же синтаксическую конструкцию, что и обычный cron, но позволяет вам планировать Tantor SE-1C команды непосредственно из базы данных. Вы также можете использовать [1-59] секунд для планирования задания на основе интервала.

pg_cron также позволяет вам использовать $ для обозначения последнего дня месяца.

-- Delete old data on Saturday at 3:30am (GMT)
SELECT cron.schedule('30 3 * * 6', $$DELETE FROM events WHERE event_time < now() - interval '1 week'$$);
 schedule
----------
       42

-- Vacuum every day at 10:00am (GMT)
SELECT cron.schedule('nightly-vacuum', '0 10 * * *', 'VACUUM');
 schedule
----------
       43

-- Change to vacuum at 3:00am (GMT)
SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
 schedule
----------
       43

-- Stop scheduling jobs
SELECT cron.unschedule('nightly-vacuum' );
 unschedule 
------------
 t

SELECT cron.unschedule(42);
 unschedule
------------
          t

-- Vacuum every Sunday at 4:00am (GMT) in a database other than the one pg_cron is installed in
SELECT cron.schedule_in_database('weekly-vacuum', '0 4 * * 0', 'VACUUM', 'some_other_database');
 schedule
----------
       44

-- Call a stored procedure every 5 seconds
SELECT cron.schedule('process-updates', '5 seconds', 'CALL process_updates()');

-- Process payroll at 12:00 of the last day of each month
SELECT cron.schedule('process-payroll', '0 12 $ * *', 'CALL process_payroll()');

pg_cron может выполнять несколько заданий параллельно, но одновременно может выполняться только один экземпляр задания. Если второй запуск должен начаться до завершения первого, то второй запуск ставится в очередь и начинается сразу после завершения первого запуска.

Расписание использует стандартный синтаксис cron, в котором * означает запускать каждый период времени, а конкретное число означает но только в это время:

 ┌───────────── min (0 - 59)
 │ ┌────────────── hour (0 - 23)
 │ │ ┌─────────────── day of month (1 - 31) or last day of the month ($)
 │ │ │ ┌──────────────── month (1 - 12)
 │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to
 │ │ │ │ │                  Saturday, or use names; 7 is also Sunday)
 │ │ │ │ │
 │ │ │ │ │
 * * * * *

Легкий способ создать расписание cron: crontab.guru.

Код в pg_cron, который обрабатывает разбор и планирование, взят непосредственно из исходного кода cron Пола Викси, поэтому поддерживаются те же параметры.

F.39.3. Настройка pg_cron #

Чтобы запустить фоновый рабочий процесс pg_cron, когда Tantor SE-1C запускается, вам нужно добавить pg_cron в shared_preload_libraries в postgresql.conf. Обратите внимание, что pg_cron не выполняет никаких задач, пока сервер находится в режиме горячего резерва, но он автоматически запускается, когда статус сервера повышается.

# add to postgresql.conf

# required to load pg_cron background worker on start-up
shared_preload_libraries = 'pg_cron'

По умолчанию фоновый рабочий процесс pg_cron ожидает, что его метаданные таблицы будут созданы в базе данных postgres. Однако, вы можете настроить это, установив параметр конфигурации cron.database_name в файле postgresql.conf.

# add to postgresql.conf

# optionally, specify the database in which the pg_cron background worker should run (defaults to postgres)
cron.database_name = 'postgres'

pg_cron может быть установлен только в одну базу данных в кластере. Если необходимо запускать задания в нескольких базах данных, используйте cron.schedule_in_database().

Ранее pg_cron мог использовать только время по Гринвичу, но теперь вы можете адаптировать ваше время, установив cron.timezone в postgresql.conf.

# add to postgresql.conf

# optionally, specify the timezone in which the pg_cron background worker should run (defaults to GMT). E.g:
cron.timezone = 'PRC'

После перезапуска Tantor SE-1C , вы можете создать функции pg_cron и метаданные таблиц, используя CREATE EXTENSION pg_cron.

-- run as superuser:
CREATE EXTENSION pg_cron;

-- optionally, grant usage to regular users:
GRANT USAGE ON SCHEMA cron TO marco;

F.39.3.1. Обеспечение возможности запуска заданий pg_cron #

Предостережение

По умолчанию, pg_cron использует libpq для открытия нового соединения с локальной базой данных, что должно быть разрешено pg_hba.conf. Возможно, потребуется включить аутентификацию trust для соединений, поступающих с localhost для пользователя, выполняющего задание cron, или вы можете добавить пароль в файл .pgpass, который libpq будет использовать при открытии соединения.

Вы также можете использовать каталог сокетов домена Unix в качестве имени хоста и включить аутентификацию trust для локальных подключений в pg_hba.conf, что обычно безопасно:

# Connect via a unix domain socket:
cron.host = '/tmp'

# Can also be an empty string to look for the default directory:
cron.host = ''

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

# Schedule jobs via background workers instead of localhost connections
cron.use_background_workers = on
# Increase the number of available background workers from the default of 8
max_worker_processes = 20

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

F.39.4. Просмотр деталей выполнения задания #

Вы можете просмотреть статус выполняющихся и недавно завершенных заданий в cron.job_run_details:

select * from cron.job_run_details order by start_time desc limit 5;
┌───────┬───────┬─────────┬──────────┬──────────┬───────────────────┬───────────┬──────────────────┬───────────────────────────────┬───────────────────────────────┐
│ jobid │ runid │ job_pid │ database │ username │      command      │  status   │  return_message  │          start_time           │           end_time            │
├───────┼───────┼─────────┼──────────┼──────────┼───────────────────┼───────────┼──────────────────┼───────────────────────────────┼───────────────────────────────┤
│    10 │  4328 │    2610 │ postgres │ marco    │ select process()  │ succeeded │ SELECT 1         │ 2023-02-07 09:30:00.098164+01 │ 2023-02-07 09:30:00.130729+01 │
│    10 │  4327 │    2609 │ postgres │ marco    │ select process()  │ succeeded │ SELECT 1         │ 2023-02-07 09:29:00.015168+01 │ 2023-02-07 09:29:00.832308+01 │
│    10 │  4321 │    2603 │ postgres │ marco    │ select process()  │ succeeded │ SELECT 1         │ 2023-02-07 09:28:00.011965+01 │ 2023-02-07 09:28:01.420901+01 │
│    10 │  4320 │    2602 │ postgres │ marco    │ select process()  │ failed    │ server restarted │ 2023-02-07 09:27:00.011833+01 │ 2023-02-07 09:27:00.72121+01  │
│     9 │  4320 │    2602 │ postgres │ marco    │ select do_stuff() │ failed    │ job canceled     │ 2023-02-07 09:26:00.011833+01 │ 2023-02-07 09:26:00.22121+01  │
└───────┴───────┴─────────┴──────────┴──────────┴───────────────────┴───────────┴──────────────────┴───────────────────────────────┴───────────────────────────────┘
(10 rows)

Записи в cron.job_run_details не очищаются автоматически, но каждый пользователь, который может планировать задания cron, также имеет разрешение на удаление своих собственных записей cron.job_run_details.

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

-- Delete old cron.job_run_details records of the current user every day at noon
SELECT  cron.schedule('delete-job-run-details', '0 12 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days'$$);

Если вы вообще не хотите использовать cron.job_run_details, то вы можете добавить cron.log_run = off в postgresql.conf.