我发现
但是没有答案,并且与我的问题不完全相同(尽管非常相似)。
假设我执行以下操作:
- 创建函数
myfunc()
- 从客户 A 开始交易
- 从客户 B 开始交易
- 在事务B中,使用“create or replace function”修改定义
myfunc()
- 提交事务 B
myfunc()
从事务 A调用
第 6 步会发生什么? 我是在调用第 1 步中定义的原始函数吗?还是第 4 步中的修改形式(在第 5 步中提交)?
如果函数在第 4 步中被删除而不是被修改,第 6 步会失败还是成功?(这可能是同一个问题,但修改可能会有所不同。)
关于这个的文档在哪里?
有趣的问题。
从一个小测试来看,函数修改和删除似乎是事务性的。这意味着 - 在任何隔离级别 - 当事务 2 修改或删除该函数时,事务 1 不会注意到它并且仍然使用该函数的旧版本。
对函数的任何更改仅在事务提交后才可见,并且仅对在该提交后开始的事务可见。隔离级别无关紧要,因为测试的函数没有从任何表中读取任何数据。
在稍微不同的场景中,如果两个事务都尝试修改函数,那么只有一个成功,另一个被阻止,然后在第一个提交时失败。
事务 A 立即看到函数的更新定义
myfunc()
。(但请看下面缓存的效果。)它会失败。(但请看下面缓存的效果。)
Postgres DDL 命令是完全事务性的。虽然事务 B 未提交,但两个事务将继续看到该函数的不同版本。但是并发事务确实会看到系统目录中已提交的更改。在默认隔离级别中似乎很明显
READ COMMITTED
。但是您甚至无法通过隔离级别REPEATABLE READ
或SERIALIZABLE
.如果您应该在事务 B 提交更改之前调用事务 A 中的函数,则本地缓存可能会干扰。在我的测试中,在下一个调用意识到更改并相应地回答之前,另一个调用使用了缓存的(旧)函数。
我没有找到系统目录缓存如何为此准确运行的文档(仍然可能存在于某处)。我不相信最后一点(从缓存中应答的另一个呼叫)是最好的行为。
顺便说一句,您的步骤 3. - 5. 可以减少到 4.,没有任何区别。显式或隐式事务包装器的工作方式相同:
3.从客户端B开始一个事务4.在事务B中,使用“create or replace function”修改myfunc()的定义
5.提交事务B