如何在另一份工作后 运行 Postgres pg_cron 工作?
How to run Postgres pg_cron Job AFTER another Job?
我 运行 晚上使用 pg_cron 扩展在我的 postgres 数据库上执行一些自动化任务。我正在将某些旧记录移动到存档数据库表中。我在 5 个不同的后台工作人员上 运行 同时 宁 5 个存储过程,所以他们都同时开始 运行 在不同的工作人员上(我假设这个类似于 运行 在 Java 中在不同的线程上设置不同的任务)。这5个Stored Procedures是独立的(移动记录到归档表),所以它们可以同时运行。我使用
这样的命令安排它们
cron.schedule (myJob1,
'* * * * *',
'call my_stored_proc_1()'
);
cron.schedule (myJob2,
'* * * * *',
'call my_stored_proc_2()'
);
.
..
...
cron.schedule (myJob5,
'* * * * *',
'call my_stored_proc_5()'
);
现在,我还有一些 dependent 我想要的存储过程 运行。但是他们需要 运行 在这 5 个作业 finish/complete 之后,因为他们正在做一些 DELETE...sql 操作。
如何在我的前 5 个存储过程作业完成后获得第二个存储过程(执行 DELETE 查询的那个)作业运行?我不想为执行 DELETES 的第二个存储过程设置 CRON 表达式,因为我什至不知道前 5 个存储过程什么时候完成...
下面我提供了一个小示意图,说明当前如何触发作业以及我希望它如何工作(如果可能):
前言:我是如何理解问题的
希望我理解OP描述的问题。
如果我错了,那么下面的所有内容都会无效。
我想这是关于 CPU and/or IO 中繁重的周期性夜间任务。
例如:
- 有任务 A-C 用于归档数据
- 也许任务 D-E 用于重建聚合/刷新垫视图
- 最后是 运行s reindexing/analyze 在整个 DB
上的任务 F
所以只有在任务 A-E 完成后 运行 任务 F 才有意义。
每个任务都需要运行在一段时间内只需要一次:
- 一天或一小时或一周一次,或仅在周末晚上一次
- 最好不要运行服务器负载过大
是否符合 OP 要求 - IDK。
为了简单起见,我们假设每项任务 运行 每晚只有一次。很容易扩展到其他 periods/requirements.
数据驱动方法
1。添加日志table
例如
CREATE TABLE job_log (
log_id bigint,
job_name text,
log_date timestamptz
)
任务 A-E
开始时
对每个工作职能进行检查:
IF EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskA' # TaskB-TaskE for each functiont
AND log_date::DATE = NOW()::DATE # check that function already executed this night
) OR EXISTS(
SELECT 1 FROM pg_stat_activity
WHERE
query like 'SELECT * FROM jobA_function();' # check that job not executing right now
) THEN RETURN;
END IF;
可能会添加其他条件:查找连接数量、是否存在锁等等。
这样可以保证函数的执行频率不会超出需要。
完成时
INSERT INTO job_log
SELECT
(SELECT MAX(log_id) FROM job_log) + 1 # or use sequences/other autoincrements
,'TaskA'
,NOW()
Cronjob 计划
意思变了
现在是:“尝试开始执行任务”。
在所选时间段之间的每个小时甚至更频繁地安排它是安全的。
Cronjob 无法知道服务器是否处于负载状态,table 是否有锁,或者有人手动开始执行任务。
工作职能在这方面可能会更聪明。
任务 F
与上面相同,但在开始时检查是否已完成其他任务。
例如
IF NOT EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskA'
AND log_date::DATE = NOW()::DATE
) OR NOT EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskB'
AND log_date::DATE = NOW()::DATE
)
.... # checks for completions of other tasks
OR EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskF' # TaskB-TaskE for each functiont
AND log_date::DATE = NOW()::DATE # check that function already executed this night
) OR EXISTS(
SELECT 1 FROM pg_stat_activity
WHERE
query like 'SELECT * FROM jobF_function();' # check that job not executing right now
) THEN RETURN;
完成时
写入job_log与其他函数相同
更新。 Cronjob 计划
在 cronjob 中创建多个计划。
例如
假设任务 A-E 将 运行 大约 10-15 分钟。
并且其中一两个可以工作 30-45-60 分钟。
为任务 F 创建一个计划,每 5 分钟尝试启动一次。
如何运作:
- 尝试 1:任务 A 完成,其他仍在工作 -> 退出
- 尝试 2:任务 A-C 完成 -> 退出
- 尝试 3:任务 A-E 完成 -> 开始任务 F
- 尝试 4:任务 A-E 已完成,但在
pg_stat_activity
中有一个正在执行的任务 F -> 退出
- 尝试 5:任务 A-E 完成,
pg_stat_activity
为空但在日志中我们看到任务 F 已经执行 -> 无需工作 -> 退出
- ...所有其他尝试将在明晚之前相同
总结
很容易扩展此方法以满足任何要求:
- 另一个周期
- 或者完全不定期。例如。使用触发器创建 table 并在更改时开始执行
- 任何深度的依赖关系and/or“模糊”依赖关系
- ...字面上的一切
构想不变:
- cronjob 计划意味着“尝试 运行”
- 决定是否 运行 是数据驱动的
我很高兴听到任何形式的批评 - 谁知道我可能忽略了什么。
您可以使用 pg_stat_activity
view 来确保没有像您的职位 1-5 这样的活动查询。
注:
Superusers and members of the built-in role pg_read_all_stats
(see also Section 21.5) can see all the information about all sessions
...
while (
select count(*) > 0
from pg_stat_activity
where query in ('call my_stored_proc_1()', 'call my_stored_proc_2()', ...))
loop
perform pg_sleep(1);
perform pg_stat_clear_snapshot(); -- needs to retrieve the fresh data
end loop;
...
只需在 stored proc 6
的开头插入此代码,并在作业 1-5 之后调用它几秒钟。
注一:
可以使用正则表达式简化和概括条件:
when query ~ 'my_stored_proc_1|my_stored_proc_2|...'
注2:
您可以使用 clock_timestamp()
函数实现超时:
...
is_timedout := false;
timeout := '10 min'::interval; -- stop waiting after 10 minutes
start_time := clock_timestamp();
while (...)
loop
perform pg_sleep(1);
perform pg_stat_clear_snapshot(); -- needs to retrieve the fresh data
if clock_timestamp() - start_time > timeout then
is_timedout := true;
break;
end if;
end loop;
if is_timedout then
...
else
...
end if;
...
注3:
查看pg_stat_activity
的其他栏目。您可能也需要使用它们。
我 运行 晚上使用 pg_cron 扩展在我的 postgres 数据库上执行一些自动化任务。我正在将某些旧记录移动到存档数据库表中。我在 5 个不同的后台工作人员上 运行 同时 宁 5 个存储过程,所以他们都同时开始 运行 在不同的工作人员上(我假设这个类似于 运行 在 Java 中在不同的线程上设置不同的任务)。这5个Stored Procedures是独立的(移动记录到归档表),所以它们可以同时运行。我使用
这样的命令安排它们cron.schedule (myJob1,
'* * * * *',
'call my_stored_proc_1()'
);
cron.schedule (myJob2,
'* * * * *',
'call my_stored_proc_2()'
);
.
..
...
cron.schedule (myJob5,
'* * * * *',
'call my_stored_proc_5()'
);
现在,我还有一些 dependent 我想要的存储过程 运行。但是他们需要 运行 在这 5 个作业 finish/complete 之后,因为他们正在做一些 DELETE...sql 操作。
如何在我的前 5 个存储过程作业完成后获得第二个存储过程(执行 DELETE 查询的那个)作业运行?我不想为执行 DELETES 的第二个存储过程设置 CRON 表达式,因为我什至不知道前 5 个存储过程什么时候完成...
下面我提供了一个小示意图,说明当前如何触发作业以及我希望它如何工作(如果可能):
前言:我是如何理解问题的
希望我理解OP描述的问题。
如果我错了,那么下面的所有内容都会无效。
我想这是关于 CPU and/or IO 中繁重的周期性夜间任务。
例如:
- 有任务 A-C 用于归档数据
- 也许任务 D-E 用于重建聚合/刷新垫视图
- 最后是 运行s reindexing/analyze 在整个 DB 上的任务 F
所以只有在任务 A-E 完成后 运行 任务 F 才有意义。
每个任务都需要运行在一段时间内只需要一次:
- 一天或一小时或一周一次,或仅在周末晚上一次
- 最好不要运行服务器负载过大
是否符合 OP 要求 - IDK。
为了简单起见,我们假设每项任务 运行 每晚只有一次。很容易扩展到其他 periods/requirements.
数据驱动方法
1。添加日志table
例如
CREATE TABLE job_log (
log_id bigint,
job_name text,
log_date timestamptz
)
任务 A-E
开始时
对每个工作职能进行检查:
IF EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskA' # TaskB-TaskE for each functiont
AND log_date::DATE = NOW()::DATE # check that function already executed this night
) OR EXISTS(
SELECT 1 FROM pg_stat_activity
WHERE
query like 'SELECT * FROM jobA_function();' # check that job not executing right now
) THEN RETURN;
END IF;
可能会添加其他条件:查找连接数量、是否存在锁等等。
这样可以保证函数的执行频率不会超出需要。
完成时
INSERT INTO job_log
SELECT
(SELECT MAX(log_id) FROM job_log) + 1 # or use sequences/other autoincrements
,'TaskA'
,NOW()
Cronjob 计划
意思变了
现在是:“尝试开始执行任务”。
在所选时间段之间的每个小时甚至更频繁地安排它是安全的。
Cronjob 无法知道服务器是否处于负载状态,table 是否有锁,或者有人手动开始执行任务。
工作职能在这方面可能会更聪明。
任务 F
与上面相同,但在开始时检查是否已完成其他任务。
例如
IF NOT EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskA'
AND log_date::DATE = NOW()::DATE
) OR NOT EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskB'
AND log_date::DATE = NOW()::DATE
)
.... # checks for completions of other tasks
OR EXISTS(
SELECT 1 FROM job_log
WHERE
job_name = 'TaskF' # TaskB-TaskE for each functiont
AND log_date::DATE = NOW()::DATE # check that function already executed this night
) OR EXISTS(
SELECT 1 FROM pg_stat_activity
WHERE
query like 'SELECT * FROM jobF_function();' # check that job not executing right now
) THEN RETURN;
完成时
写入job_log与其他函数相同
更新。 Cronjob 计划
在 cronjob 中创建多个计划。
例如
假设任务 A-E 将 运行 大约 10-15 分钟。
并且其中一两个可以工作 30-45-60 分钟。
为任务 F 创建一个计划,每 5 分钟尝试启动一次。
如何运作:
- 尝试 1:任务 A 完成,其他仍在工作 -> 退出
- 尝试 2:任务 A-C 完成 -> 退出
- 尝试 3:任务 A-E 完成 -> 开始任务 F
- 尝试 4:任务 A-E 已完成,但在
pg_stat_activity
中有一个正在执行的任务 F -> 退出 - 尝试 5:任务 A-E 完成,
pg_stat_activity
为空但在日志中我们看到任务 F 已经执行 -> 无需工作 -> 退出 - ...所有其他尝试将在明晚之前相同
总结
很容易扩展此方法以满足任何要求:
- 另一个周期
- 或者完全不定期。例如。使用触发器创建 table 并在更改时开始执行
- 任何深度的依赖关系and/or“模糊”依赖关系
- ...字面上的一切
构想不变:
- cronjob 计划意味着“尝试 运行”
- 决定是否 运行 是数据驱动的
我很高兴听到任何形式的批评 - 谁知道我可能忽略了什么。
您可以使用 pg_stat_activity
view 来确保没有像您的职位 1-5 这样的活动查询。
注:
Superusers and members of the built-in role
pg_read_all_stats
(see also Section 21.5) can see all the information about all sessions
...
while (
select count(*) > 0
from pg_stat_activity
where query in ('call my_stored_proc_1()', 'call my_stored_proc_2()', ...))
loop
perform pg_sleep(1);
perform pg_stat_clear_snapshot(); -- needs to retrieve the fresh data
end loop;
...
只需在 stored proc 6
的开头插入此代码,并在作业 1-5 之后调用它几秒钟。
注一:
可以使用正则表达式简化和概括条件:
when query ~ 'my_stored_proc_1|my_stored_proc_2|...'
注2:
您可以使用 clock_timestamp()
函数实现超时:
...
is_timedout := false;
timeout := '10 min'::interval; -- stop waiting after 10 minutes
start_time := clock_timestamp();
while (...)
loop
perform pg_sleep(1);
perform pg_stat_clear_snapshot(); -- needs to retrieve the fresh data
if clock_timestamp() - start_time > timeout then
is_timedout := true;
break;
end if;
end loop;
if is_timedout then
...
else
...
end if;
...
注3:
查看pg_stat_activity
的其他栏目。您可能也需要使用它们。