PostgreSQL -在执行CREATE OR REPLACE时调用SQL函数

gfttwv5a  于 2023-05-06  发布在  PostgreSQL
关注(0)|答案(1)|浏览(240)

我有一个Postgres 14.4集群(AWS Aurora),我使用JDBC从我的应用程序连接到它。
我使用liquibase进行模式管理--不仅是表,还有一堆SQL存储过程和函数。基本上,有一个liquibasechangeSet最后运行并执行一组包含存储过程和函数的SQL文件。

CREATE OR REPLACE PROCEDURE foo(arg1 text, arg2 text) LANGUAGE "plpgsql" AS '
BEGIN
   // procedure body
END;
';

CREATE OR REPLACE FUNCTION bar(arg1 text, arg2 text) RETURNS record LANGUAGE "plpgsql" AS '
BEGIN
   // function body
END;
';

这些函数/过程仅在一个/多个SQL文件(属于此changeSet的一部分)发生更改时才被替换。(runOnChange: true)。
每当我对我的应用程序进行滚动部署时(比如,在bar()的函数体中进行更改),liquibase将执行CREATE OR REPLACE FUNCTION bar()作为事务的一部分。
bar()被替换的几毫秒内,还有其他正在进行的事务(来自我的应用程序的其他副本)不断尝试调用bar()
只有在这个微小的时间窗口中,很少有交易会失败,并出现以下错误:

ERROR: function bar(arg1 => text, arg2 => text) does not exist
  Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 4 : errorCode = 42883

我不希望这些事务中的任何一个失败(我们没有任何适当的方法从应用程序层重试它们)。这被视为影响可用性。但是,在替换SQL函数体时,这些事务可以BLOCK(几百毫秒),然后继续调用。
有没有一种方法可以安全地修改PostgreSQL中的存储函数/过程,同时该函数/过程被多个事务连续调用?

h43kikqp

h43kikqp1#

所以,我实际上在pgsql-general邮件列表中发布了这个问题,并收到了一个有用的见解,挽救了我的一天。
为了子孙后代追踪它。
CREATE OR REPLACE FUNCTION应该是原子的,不能更改函数签名。我不明白为什么在这种情况下函数不能存在。
您确定Liquibase在重新创建函数之前没有删除它吗?如果Liquibase在单独的事务中删除并重新创建函数,则尝试执行该函数的事务可能会发现它在使用已提交读隔离级别时被删除。
事实证明,它确实是liquibase,它是:

  • 删除一个事务中的函数(由于DROP TYPE CASCADE而无意中删除)
  • 紧接着下一个事务执行CREATE OR REPLACE FUNCTION,重新创建了这些函数

在第一个事务提交之后,第二个事务可以提交之前的时间上的微小差距是函数实际上停止存在的时间。因此,在此期间的调用失败。
见postgres邮件列表的讨论

相关问题