向 RDS 上的 Postgres pg_cron 的 job_run_details 表添加自定义触发器

问题描述 投票:0回答:1

我正在使用

pg_cron
,并且想添加一个触发器以将
failed
结果复制到自定义
system_message
表中。我已经在本地模拟了这个,但它不适用于 RDS。并且,没有看到任何错误。在进一步深入研究并实施此策略之前,在
pg_cron
之上添加自定义触发器是否是一个好的或安全的想法?

另一种替代方法是编写一个标准函数来复制数据,在 pg_cron 任务中运行

that
,并存储最大
runid
以用作下一个副本上的“查找大于此”的搜索。然而,考虑到并发系统的变幻莫测,我很可能会以这种方式错过失败的作业。这就是为什么我首先想到行触发器。

感谢您的任何建议或替代想法。

在“迄今为止的努力”下:

在目标表上授予触发器

GRANT TRIGGER on cron.job_run_details TO rds_super;

定义触发函数

CREATE OR REPLACE FUNCTION dba.trigger_function_job_run_details_after_insert()
RETURNS trigger AS

$BODY$

DECLARE
   result_v int4 = 0;

BEGIN
   
   IF NEW.status = 'failed' THEN
        SELECT dba.system_message_add (
             'Error',
             'pg_cron job failed',
              to_jsonb(NEW)
            ) INTO result_v; -- Could alternatively use PERFORM and ignore the result....we don't care about it anyway.
   END IF;
   RETURN NEW;
   
END

$BODY$

  LANGUAGE plpgsql 
  VOLATILE;

COMMENT ON FUNCTION dba.trigger_function_job_run_details_after_insert() IS 
'Post an error to system_message when a pg_cron job fails.';

ALTER FUNCTION dba.trigger_function_job_run_details_after_insert
    OWNER TO user_bender;

定义触发器绑定

-- Could use a STATEMENT trigger here...figured I'd leave it more obvious what's happening.
CREATE OR REPLACE TRIGGER trigger_job_run_details_after_insert
  AFTER INSERT
            ON cron.job_run_details
           FOR EACH ROW
       EXECUTE PROCEDURE dba.trigger_function_job_run_details_after_insert();

system_message_kind 域

------------------------
-- system_message_kind
------------------------
DROP DOMAIN IF EXISTS domains.system_message_kind;

CREATE DOMAIN domains.system_message_kind AS
    citext
    NOT NULL
    CONSTRAINT system_message_kind_legal_values
        CHECK(
            VALUE IN (
            'Info','Advice','Notice','Error')
     );


COMMENT ON DOMAIN domains.system_message_kind IS
    'Constrains system_message.kind field and parameter values.';

系统消息表

BEGIN;

DROP TABLE IF EXISTS dba.system_message;

CREATE TABLE IF NOT EXISTS dba.system_message (

    id                      int8                  GENERATED ALWAYS AS IDENTITY,

    created_dts             timestamp             NOT NULL DEFAULT NOW(),
    
                                                  -- Tip: Giving the CHECK a specific name improves error results, you'll see database_name_is_known in the message.
    database_name           citext                NOT NULL DEFAULT  current_database()     
                                                        CONSTRAINT  database_name_is_known -- Name shows up in errors.
                                                             CHECK (database_name IN ('postgres','nautilus','squid')),

    kind                     system_message_kind  NOT NULL DEFAULT NULL, -- 'Info','Advice','Notice','Error' in custom 'domain'

    subject                  citext               NOT NULL DEFAULT NULL,  -- Makes no sense to have an empty message.
    payload_text             citext               NOT NULL DEFAULT '',    -- You can include text, json, neither, or both. So far.  
    payload_json             jsonb                NOT NULL DEFAULT '{}'          

);

ALTER TABLE dba.system_message
    OWNER TO user_change_structure;

------------------------------------
-- FILLFACTOR
------------------------------------
-- This is a high-thrash table, by nature. It's a queue with short-lived rows.
ALTER TABLE dba.system_message
       SET (FILLFACTOR = 85);

系统消息添加函数

/*

kind:    
required 
DOMAIN used to automate type-check on parameter, and constrain values to legal kinds.

subject:
required
Anything you like

payload: 
Optional
text, json, text & json, or neither


Simple script to exercise the different parameter lists:

truncate table system_message;

select * from system_message_add('info','subject only');
select * from system_message_add('info','subject and text','Payload text');
select * from system_message_add('info','subject and json','{ "json": "payload only"}'::jsonb);
select * from system_message_add('info','subject text and json','Payload text','{ "json": "text and jsonb payloads"}'::jsonb);

select * from system_message;

+----+----------------------------+---------------+------+-----------------------+--------------+-------------------------------------+
| id | created_dts                | database_name | kind | subject               | payload_text | payload_json                        |
+----+----------------------------+---------------+------+-----------------------+--------------+-------------------------------------+
| 18 | 2024-02-19 08:31:08.782403 | squid         | info | subject only          |              | {}                                  |
| 19 | 2024-02-19 08:31:08.797859 | squid         | info | subject and text      | Payload text | {}                                  |
| 20 | 2024-02-19 08:31:08.803001 | squid         | info | subject and json      |              | {"json": "payload only"}            |
| 21 | 2024-02-19 08:31:08.815063 | squid         | info | subject text and json | Payload text | {"json": "text and jsonb payloads"} |
+----+----------------------------+---------------+------+-----------------------+--------------+-------------------------------------+

Note: Given the parameter options and how data is passed, you'll end up with your JSON in the text field, if you don't cast it to JSONB.
I'm anticipating that we'll be passing through existing JSON results, in the real world. Time will tell.
*/


------------------------------------------------------------------
-- system_message_add (kind, subject)
------------------------------------------------------------------
-- Weird one, only useful for a basic signal...not sure that we want this.
CREATE OR REPLACE FUNCTION dba.system_message_add (
      kind_in           system_message_kind,
      subject_in        citext)
      
 RETURNS int4 AS
 
$BODY$
 
 INSERT INTO dba.system_message
            (kind,    subject)
     VALUES (kind_in, subject_in)
     
   RETURNING 1;     
 
$BODY$
LANGUAGE SQL;

COMMENT ON FUNCTION dba.system_message_add (system_message_kind, citext) IS 
'Add a system message.';

ALTER FUNCTION dba.system_message_add (system_message_kind, citext)
    OWNER TO user_bender;

------------------------------------------------------------------
-- system_message_add (kind, subject, payload text)
------------------------------------------------------------------
CREATE OR REPLACE FUNCTION dba.system_message_add (
      kind_in           system_message_kind,
      subject_in        citext,
      payload_text_in   citext)
      
 RETURNS int4 AS
 
$BODY$
 
 INSERT INTO dba.system_message
            (kind,    subject,    payload_text)
     VALUES (kind_in, subject_in, payload_text_in)
     
   RETURNING 1;     
 
$BODY$
LANGUAGE SQL;

COMMENT ON FUNCTION dba.system_message_add (system_message_kind, citext, citext) IS 
'Add a system message.';

ALTER FUNCTION dba.system_message_add (system_message_kind, citext, citext)
    OWNER TO user_bender;

------------------------------------------------------------------
-- system_message_add (kind, subject, payload json)
------------------------------------------------------------------
CREATE OR REPLACE FUNCTION dba.system_message_add (
      kind_in           system_message_kind,
      subject_in        citext,
      payload_json_in   jsonb)
      
 RETURNS int4 AS
 
$BODY$
 
 INSERT INTO dba.system_message
            (kind,    subject,    payload_json)
     VALUES (kind_in, subject_in, payload_json_in)
     
   RETURNING 1;     
 
$BODY$
LANGUAGE SQL;

COMMENT ON FUNCTION dba.system_message_add (system_message_kind, citext, jsonb) IS 
'Add a system message.';

ALTER FUNCTION dba.system_message_add (system_message_kind, citext, jsonb)
    OWNER TO user_bender;

------------------------------------------------------------------
-- system_message_add (kind, subject, payload_text, payload json)
------------------------------------------------------------------
CREATE OR REPLACE FUNCTION dba.system_message_add (
      kind_in           system_message_kind,
      subject_in        citext,
      payload_text_in   citext,
      payload_json_in   jsonb)
      
 RETURNS int4 AS
 
$BODY$
 
 INSERT INTO dba.system_message
            (kind,    subject,    payload_text,    payload_json)
     VALUES (kind_in, subject_in, payload_text_in, payload_json_in)
     
   RETURNING 1;     
 
$BODY$
LANGUAGE SQL;

COMMENT ON FUNCTION dba.system_message_add (system_message_kind, citext, citext, jsonb) IS 
'Add a system message.';

ALTER FUNCTION dba.system_message_add (system_message_kind, citext, citext, jsonb)
    OWNER TO user_bender;
amazon-web-services triggers amazon-rds pg-cron
1个回答
0
投票

它有效......

我终于找到了几个小时的时间来回到这个问题上,是的,您可以在

cron.job_run_details
上获得自定义触发器来执行具有
failed
结果的操作。就我而言,在另一个表中插入一条记录。有一些问题(pg_cron)和怪癖(RDS)。简短版本,对于其他尝试这样做的人:

  • 您需要一个

    AFTER UPDATE
    触发器,而不是
    AFTER INSERT
    触发器。

  • RDS 将

    pg_cron
    作业作为
    rds_superuser
    运行。不是一个你可以搞乱的角色,但你可以继承它,并且你可以
    GRANT
    模式
    USAGE
    和例程
    EXECUTE
    。你需要这样做。

pg_cron 插入和更新

当它开始一项工作时,

pg_cron
似乎创建了一个
job_run_details
记录,您可以在
AFTER INSERT
触发函数中看到:

{
    "jobid": 83,
    "runid": 140394,
    "status": "starting",
    "command": "SELECT foo FROM bar;",
    "job_pid": null,
    "database": "nautilus",
    "end_time": null,
    "username": "rds_super",
    "start_time": null,
    "return_message": null
}

status = 'starting'
。公平的。我毫无理由地认为,一旦工作完成,就会插入完整的记录。如果您想要最终记录,请使用
AFTER UPDATE
并检查
status = 'failed'
,如果您的需求和我一样。

权限

pg_cron
作为后台工作进程运行,日志不指示用户名。我写了
current_user
,结果是
rds_superuser
。一旦我授予了
rds_superuser
显式
USAGE
EXECUTE
权限,我的工作就可以接受了。在此之前,我在
citext_eq
和其他随机扩展函数上遇到错误。我当然忽略了
rds_superuser
......现在我知道了。我将查看更改了
postgres
数据库中模式的默认权限。

RDS 上的故障排除和日志

我很高兴我们部署在 RDS 之类的东西上,因为我不想花更多的时间在 DBA 工作上。但是......有时您真的想要一个文件系统。对于日志文件来说更是如此。我曾大量使用 Splunk 一段时间,现在我已经被宠坏了。现在没有:( 不管怎样,在 RDS 上找到很多 Postgres 日志是很难的,它们并不是全部暴露。但是,你可以得到错误日志,相对活跃。有一个

log_fdw 
用于此目的。不起作用。嗯,有一点作用,但不可靠。闻起来像一个废弃的功能。

相反,您可以在 AWS 控制台中查看和下载实例的实时日志。这里有一点滞后,但还不错。当日志滚动时情况会更糟,但即使如此,也只有几秒钟。尽管如此,还是有点尴尬。然而,当你需要时,它是你的救星。

我喜欢将描述性错误强制写入日志中以标记我的位置,例如:

select changed_the_permissions_for_rds_superuser

不是一个有意义的 SQL 语句,但它直接将其推送到日志中。然后我可以在该条目之后查找感兴趣的事件。

最终触发功能和绑定

这是代码的最终位置:

--------------------------------------------
-- Create AFTER UPDATE function
--------------------------------------------
CREATE OR REPLACE FUNCTION dba.trigger_function_job_run_details_after_update()
RETURNS trigger AS

$BODY$

BEGIN

   IF NEW.status = 'failed' THEN

         INSERT INTO  dba.system_message (kind, subject, payload_json)
              VALUES ('Error',
                      'pg_cron job failed',
                       to_json(NEW)
                  );
   END IF;

   RETURN NEW;

END

$BODY$

  LANGUAGE plpgsql 
  VOLATILE;

COMMENT ON FUNCTION dba.trigger_function_job_run_details_after_update() IS 
'Post an error to system_message when a pg_cron job fails.';

ALTER FUNCTION dba.trigger_function_job_run_details_after_update
    OWNER TO user_bender;

--------------------------------------------
-- Create trigger binding
--------------------------------------------
CREATE OR REPLACE TRIGGER trigger_job_run_details_after_update
         AFTER UPDATE
            ON cron.job_run_details
           FOR EACH ROW
       EXECUTE PROCEDURE dba.trigger_function_job_run_details_after_update();
© www.soinside.com 2019 - 2024. All rights reserved.