邮政跌落型 XX000 "cache lookup failed for type"



我目前正在开发一个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.导致异常的情况:

一)一个客户端经常像这样使用 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() 的客户端会恶意抛出

ERROR:  cache lookup failed for type 386318
CONTEXT:  PL/pgSQL function tmp._my_testfunction() line 3 at CASE

我怎样才能防止这种情况发生?

这里的情况似乎是竞争条件。客户端调用tmp._my_test_function(),在通过解释函数的源代码进行设置时,删除函数和enum类型,然后重新创建它们。在内部,几乎所有对象都由其oid引用(对于您的案例中的类型,值为 386318),因此客户端函数中的函数和enum解析为 oid s。如果解释器将函数和enum解析为两个oid,然后删除函数和enum并重新创建它们,则旧的oid消失,新创建的函数和enum得到不同的oid。函数的新调用将起作用,因为解释器会找到函数和enum的新oid

在这种情况下,解决方案是REVOKE EXECUTE客户端函数,进行更改,然后再次GRANT EXECUTE权限。在客户端,您必须通过重试函数调用来处理权限错误,直到成功。

通常,如果要避免重试函数调用的开销,则可以在低流量期间和/或通过会话管理在生产服务器上部署新代码。

由于我们找不到解决此问题的另一种方法,因此我们实现了一个解决方案,该解决方案通过使用 LISTEN 和 NOTIFY 命令通知所有其他连接自行刷新。因此,所有客户端都在特定通道上侦听指示它们重新连接的命令。然后,命令 ist 发送删除该类型的同一事务。

一个非常不技术的解决方案...

我将我的应用程序部署在 AWS 上并远程登录到 PSQL,删除数据库,重新导入并显示上述内容。

重新部署应用程序解决了它,为什么我不确定。

相关内容

  • 没有找到相关文章