我目前正在开发一个PostgreSQL 9.2.x数据库,它包含许多客户端,表和函数。我们不断部署代码,有时甚至需要删除由于此部署而导致的类型或功能。
示例:
1.首先创建所需功能的脚本
CREATE TYPE tmp._myEnum AS ENUM ('OLD', 'NEW', 'BOTH');
CREATE OR REPLACE FUNCTION tmp._get_status()
RETURNS tmp._myEnum AS
$BODY$
BEGIN
RETURN 'OLD'::tmp._myEnum;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER COST 10;
CREATE OR REPLACE FUNCTION tmp._my_testfunction()
RETURNS VOID AS
$BODY$
BEGIN
CASE tmp._get_status()
WHEN 'OLD'::tmp._myEnum THEN
RAISE INFO 'myEnum is OLD';
WHEN 'NEW'::tmp._myEnum THEN
RAISE INFO 'myEnum is NEW';
WHEN 'BOTH'::tmp._myEnum THEN
RAISE INFO 'myEnum is BOTH';
ELSE
RAISE INFO 'myEnum has an unexpected value';
END CASE;
FOR i IN 1..10 LOOP
RAISE INFO 'Step [%]',i;
END LOOP;
RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER COST 10;
2.导致异常的场景:
a)一个客户端一直在使用tmp._my_testfunction(),如下所示
SELECT tmp._my_testfunction()
b)为了将更改部署到我在另一个会话中执行的复合类型
DROP FUNCTION IF EXISTS tmp._get_status();
DROP TYPE IF EXISTS tmp._myEnum;
CREATE TYPE tmp._myEnum AS ENUM ('OLD', 'NEW', 'BOTH','NOTHING');
CREATE OR REPLACE FUNCTION tmp._get_status()
RETURNS tmp._myEnum AS
$BODY$
BEGIN
RETURN 'OLD'::tmp._myEnum;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER COST 10;
c)持续使用tmp._my_testfunction()的客户端imidiatly throws
ERROR: cache lookup failed for type 386318
CONTEXT: PL/pgSQL function tmp._my_testfunction() line 3 at CASE
我该如何防止这种情况?
答案 0 :(得分:0)
这里的情况似乎是竞争条件。您的客户端调用tmp._my_test_function()
并在通过解释函数的源代码进行设置时,删除函数和enum
类型,然后重新创建它们。在内部,几乎所有对象都由oid
引用(对于您的案例类型,值为386318),因此函数和客户端函数中的enum
将解析为oid
。如果解释器解析了函数,并且enum
解析为oid
,然后删除函数和enum
并重新创建它们,则旧oid
消失,您新创建的函数和enum
获得不同的oid
。函数的新调用将起作用,因为解释器为函数和oid
找到新的enum
。
在这种情况下的解决方案是客户端函数上的REVOKE EXECUTE
,进行更改,然后再次GRANT EXECUTE
权限。在客户端,您必须通过重试函数调用来处理权限错误,直到成功为止。
通常,如果要避免重试函数调用的开销,可以在低流量时段和/或通过会话管理在生产服务器上部署新代码。