在 postgresql/PL/pgSQL 中创建函数时同时更新元组

wol*_*pel 6 postgresql

初始化我的进程时,它运行下面的 PL/pgSQL 语句,创建两个函数。但是,每次我同时创建多个进程作为端到端测试的一部分时,此语句的并行执行会导致tuple concurrently updated我似乎无法绕过的错误。任何帮助将非常感激。

CREATE OR REPLACE FUNCTION
  count_rows(schema text, tablename text) returns integer
  AS
  $body$
  DECLARE
    result integer;
    query varchar;
  BEGIN
    query := 'SELECT count(1) FROM "' || schema || '"."' || tablename || '"';
    execute query into result;
    return result;
  END;
  $body$
  LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION
  delete_if_empty(schema text, tablename text) RETURNS INTEGER
  AS
  $$
  DECLARE
    result integer;
    query varchar;
  BEGIN
    query := 'SELECT COUNT(*) FROM "' || schema || '"."' || tablename || '"';
    execute query into result;
    IF result = 0 THEN
      EXECUTE 'DROP TABLE "' || schema || '"."' || tablename || '" CASCADE;';
      EXECUTE 'NOTIFY "' || schema || '", ''DESTROY_TABLE:' || tablename || ''';';
      RETURN 1;
    END IF;
    RETURN 0;
  END;
  $$
  LANGUAGE plpgsql;

  SELECT version()
Run Code Online (Sandbox Code Playgroud)

nh2*_*nh2 6

如此处所述,postgres 目前不允许您CREATE FUNCTION同时使用:

如果您想避免“元组同时更新”错误,则有必要添加某种锁定方案。这与两个事务都想要更新用户表中同一行的情况没有什么不同:除非应用程序采取额外的步骤来序列化更新,否则您将收到“元组同时更新”错误。

我们确实对表/索引上的 DDL 进行了此类锁定,但过去的理论认为,对于由单个目录行表示的对象(例如函数或角色)来说,不值得这么麻烦。

解决方案是确保没有两个事务尝试CREATE FUNCTION同时执行此操作。

您可以为此使用 posgres建议锁。

可以在这里找到有关咨询锁的详细介绍:https://vladmihalcea.com/how-do-postgresql-advisory-locks-work/

例如,您可以使用:

BEGIN; -- start of transaction

SELECT pg_advisory_xact_lock(2142616474639426746); -- random 64-bit signed ('bigint') lock number

CREATE OR REPLACE FUNCTION myfunction ...

COMMIT;
Run Code Online (Sandbox Code Playgroud)

这需要事务级独占咨询锁,因此没有两个并发事务可以同时运行创建函数。事务结束时,锁会自动释放。