whpg-cron is WarehousePG's packaged distribution of pg_cron, a cron-based job scheduler that runs inside the database. It lets you schedule SQL commands, including calls to stored procedures, directly from the database using standard cron syntax.
The WarehousePG whpg-cron package installs the extension under its upstream name. You create it, and refer to it in SQL, as pg_cron. See pg_cron for the upstream project.
Downloading, installing, and loading the extension
Refer to Downloading and installing an extension for installation and setup instructions. The package name is edb-whpg7-whpg-cron.
Important
You can only install pg_cron in one database per WarehousePG cluster.
Set the database where
pg_croncreates its metadata tables. By default, it uses thepostgresdatabase. Setcron.database_namebefore you create the extension:gpconfig -c cron.database_name -v '<db_name>' --skipvalidation
Load the extension as a shared library. Check for existing shared libraries:
gpconfig -s shared_preload_librariesUse the output of the previous command to enable
pg_cron, along with any other shared libraries, and restart WarehousePG:gpconfig -c shared_preload_libraries -v '<other_libraries>, pg_cron' gpstop -raf
Create the extension in the database you configured:
CREATE EXTENSION pg_cron;
Optionally, grant usage to other users so they can schedule and manage their own jobs:
GRANT USAGE ON SCHEMA cron TO <user_name>;
Configuring whpg-cron
After you create the extension, make sure pg_cron can connect to run your jobs, and, optionally, tune other settings.
Ensuring pg_cron can start jobs
Choose a method to allow pg_cron to connect to the database to run each job:
Unix domain socket. Limit access to local OS users, since this option doesn't expose the database over the network. Point
cron.hostat the socket directory and restart the database:gpconfig -c cron.host -v '/tmp' # Or leave cron.host as an empty string ('') to use libpq's default socket directory. gpstop -raf
Then add a matching
localentry topg_hba.confon the coordinator to grant trust authentication on that socket:local all all trust
TCP connection. Restrict the
pg_hba.confentry to the loopback address, sincepg_cronalways connects from the coordinator itself. Leavecron.hostat its default (localhost), and add ahostentry topg_hba.confon the coordinator:host all all 127.0.0.1/32 trust
Alternatively, add the job user's password to a
.pgpassfile forlibpqto use instead of trust authentication.Background workers. Avoid opening a connection at all by having
pg_cronrun jobs as background workers instead. Skipcron.hostandpg_hba.confentirely:gpconfig -c cron.use_background_workers -v 'on' gpconfig -c max_worker_processes -v '20' gpstop -raf
max_worker_processeslimits the number of concurrent jobs, so raise it from its default of 8 if you schedule many jobs.
Tuning pg_cron settings
pg_cron supports the following configuration parameters, which you set with gpconfig:
| Setting | Default | Description |
|---|---|---|
cron.database_name | postgres | Database where the pg_cron background worker runs. |
cron.enable_superuser_jobs | on | Allow jobs to be scheduled as superusers. |
cron.host | localhost | Hostname the background worker connects to. |
cron.launch_active_jobs | on | Turn off to disable every active job without a server restart. |
cron.log_min_messages | WARNING | log_min_messages for the launcher background worker. |
cron.log_run | on | Log every run in the cron.job_run_details table. |
cron.log_statement | on | Log every cron statement before it runs. |
cron.max_running_jobs | 32 | Maximum number of jobs that can run at the same time. |
cron.timezone | GMT | Timezone the pg_cron background worker runs in. |
cron.use_background_workers | off | Use background workers instead of client connections to run jobs. |
View the current settings:
SELECT * FROM pg_settings WHERE name LIKE 'cron.%';
Change a setting with gpconfig, then restart:
gpconfig -c cron.<parameter> -v '<value>' gpstop -raf
Note
cron.log_min_messages and cron.launch_active_jobs take effect immediately, without a restart, once you run SELECT pg_reload_conf();. Every other setting needs a full restart.
Scheduling jobs
Write a cron schedule, then create, alter, remove, or view jobs using the cron schema's functions.
pg_cron runs jobs in parallel, but runs only one instance of a given job at a time. If a job is still running when its next scheduled run comes due, pg_cron queues the new run until the current one finishes.
pg_cron stores every scheduled job as a row in cron.job, with columns for its jobid, schedule, command, and username, among others. pg_cron runs each job in the database where you created it, with the same permissions as the user who scheduled it. A row-level security (RLS) policy limits you to viewing and modifying only the jobs you created, unless you're a superuser or have the bypassrls attribute, which lets a role bypass row-level security entirely.
Important
We recommend managing jobs with the cron schema functions rather than direct UPDATE, INSERT, or DELETE statements against cron.job, since WarehousePG doesn't support the TRIGGER statement pg_cron normally relies on to detect such changes. If the functions can't do what you need, see Editing cron.job directly.
Understanding cron syntax
Write schedules using the same cron syntax as Vixie cron, or build one at crontab.guru.
┌───────────── 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) │ │ │ │ │ │ │ │ │ │ * * * * *
pg_cron also supports:
$to indicate the last day of the month.[1-59] secondsto schedule a job on an interval measured in seconds. You can't combine seconds with the other time units.
Example schedules:
'10 seconds' # every 10 seconds * * * * * # every minute */5 * * * * # every 5 minutes 0 * * * * # every hour 0 0 * * * # daily at 12 AM 0 0 * * 1-5 # 12 AM every weekday 0 1 * * 0 # 1 AM every Sunday 0 13 2 6 * # 1 PM on June 2
Creating a cron job
Create a job, either anonymous or named, with cron.schedule(). The function accepts (schedule, command) for an anonymous job or (job_name, schedule, command) for a named one, and returns the new job's jobid.
Examples
Create an anonymous job that deletes old data every Saturday at 3:30 AM (GMT):
SELECT cron.schedule( '30 3 * * 6', $$DELETE FROM events WHERE event_time < now() - interval '1 week'$$ );
Create a named job that runs
VACUUMevery day at 10:00 AM (GMT):SELECT cron.schedule( 'nightly-vacuum', '0 10 * * *', 'VACUUM' );
Run a query every 30 seconds:
SELECT cron.schedule( 'run_every_30_seconds', '30 seconds', 'SELECT 1' );
Call a stored procedure every 5 seconds:
SELECT cron.schedule( 'process-updates', '5 seconds', 'CALL process_updates()' );
Process payroll at noon on the last day of each month:
SELECT cron.schedule( 'process-payroll', '0 12 $ * *', 'CALL process_payroll()' );
Creating a cron job in a different database
Run a job against a database other than the one where pg_cron is installed with cron.schedule_in_database(). The function accepts job_name, schedule, command, and database, plus the optional username and active parameters, and returns the new job's jobid.
For example, delete old data every Saturday at 3:30 AM (GMT) in another database:
SELECT cron.schedule_in_database( 'delete_old_data', '30 3 * * 6', $$DELETE FROM events WHERE event_time < now() - interval '1 week'$$, 'some_other_database' );
Listing jobs
View your active jobs in cron.job:
SELECT * FROM cron.job;
Removing a cron job
Remove a job by name or by ID with cron.unschedule(). The function returns true if the job was removed.
Examples
Remove a named job:
SELECT cron.unschedule('nightly-vacuum');
Remove a job by ID:
SELECT cron.unschedule(42);
Altering a cron job
Change a job's schedule, command, database, username, or active status without recreating it, using cron.alter_job(). Pass the job_id plus only the parameters you want to change.
Examples
Change a job's schedule:
SELECT cron.alter_job(42, '0 10 * * *');
Change a job's schedule, command, and username together:
SELECT cron.alter_job( 42, '0 10 * * *', 'VACUUM', username := 'some_other_user' );
Deactivate a job:
SELECT cron.alter_job(42, active := false);
Viewing job history
View the 10 most recent job runs:
SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10;
cron.job_run_details isn't cleaned up automatically, but any user who can schedule jobs can also delete their own entries from it. Purge entries older than 14 days on a schedule of their own:
SELECT cron.schedule( '0 0 * * *', $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '14 days'$$ );
Set cron.log_run = off if you don't want to use cron.job_run_details at all.
Editing cron.job directly
Edit cron.job directly only when the cron schema functions can't do what you need, for example a bulk update across many jobs at once. Since WarehousePG doesn't support the TRIGGER statement pg_cron normally relies on to detect such changes automatically, use the cron.reload_job() function to update the pg_cron cache. Any role granted USAGE on the cron schema can call this function.
For example, deactivate a job by updating cron.job directly, then refresh the cache:
UPDATE cron.job SET active = false WHERE jobid = 1; SELECT cron.reload_job();
Limitations
whpg-cron for WarehousePG has the following limitations:
- You can only install
pg_cronin one database per WarehousePG cluster. pg_cronand whpg-anonymizer can't run in the same database. Creating thepg_cronextension in a database wherewhpg-anonymizeris enabled fails, becausewhpg-anonymizer's event trigger doesn't allow another extension to replace the masked views it owns. Schedule jobs in a separate database from the one where you usewhpg-anonymizer.