vs网站开发教程,重庆市岗位证书查询,浅议网络营销论文,如何创建微信小程序商城1 总结 portal与事务有强绑定的关系#xff0c;由portal-createSubid变量记录关联关系。如果为1表示顶层事务#xff0c;关联的是子事务。 不论是顶层事务还是子事务#xff0c;提交、回滚时只会处理自己创建出来的portal。 顶层事务会清理非活跃状态的Portal#xff…1 总结 portal与事务有强绑定的关系由portal-createSubid变量记录关联关系。如果为1表示顶层事务关联的是子事务。 不论是顶层事务还是子事务提交、回滚时只会处理自己创建出来的portal。 顶层事务会清理非活跃状态的Portal如果Portal是活跃的会保留内存。子事务直接释放portal无论是否活跃。 PLpgSQL中的提交回滚有较大限制 PLpgSQL中的提交或回滚如果call proc在事务块中直接失败。PLpgSQL中的提交或回滚如果pl带exception直接失败。原因是pl中的提交或回滚不能再子事务、或事务块内要实现的话比较复杂需要对齐SPI与子事务、portal与子事务、exprcontext与子事务等等。 子事务ID只增不减可能有空隙存在参考PushTransaction的currentSubTransactionId。
2 提交
2.1 顶层事务提交PreCommit_Portals与子事务提交AtSubCommit_Portals
实例
CREATE OR REPLACE PROCEDURE tproc1()
AS $$
DECLAREcurs1 refcursor; curs2 CURSOR FOR SELECT c1 FROM tf1;curs3 CURSOR (key integer) FOR SELECT * FROM tf1 WHERE c1 key;x int;y tf1%ROWTYPE;
BEGINopen curs1 FOR SELECT * FROM tf1 WHERE c1 3;fetch curs1 into y; RAISE NOTICE curs1 : %, y.c3;fetch curs1 into y; RAISE NOTICE curs1 : %, y.c3;open curs2;fetch curs2 into x; RAISE NOTICE curs2 : %, x;fetch curs2 into x; RAISE NOTICE curs2 : %, x;OPEN curs3(4); -- OPEN curs3(key : 4);fetch curs3 into y; RAISE NOTICE curs3 : %, y.c4;fetch curs3 into y; RAISE NOTICE curs3 : %, y.c4;
EXCEPTION WHEN others THENRAISE NOTICE in caller exception;
END;
$$ LANGUAGE plpgsql;begin;
savepoint sp1;
savepoint sp2;
call tproc1();
commit;1第一次子事务提交EXCEPTION子事务提交AtSubCommit_Portalssubid4
发生在call tproc1();执行完毕。
exec_stmt_blockReleaseCurrentSubTransactionCommitSubTransactionAtSubCommit_Portals当前事务堆栈
CurrentTransactionState-subTransactionId
[1, 2, 3, 4]| | | |
top savepoint savepoint exception由于call进入SPI当前SPI堆栈只有一层对应到3号子事务上因为是在3号子事务的背景下执行的CALL。_SPI_stack[0] {connectSubid 3} AtSubCommit_Portals(mySubid4) 开始处理portal
portal1 { // 游标 mySubid 4 当前游标的portal被继承给parent portalname unnamed portal 17, createSubid 4, // AtSubCommit_Portals 4--3activeSubid 4,createLevel 4, sourceText SELECT c1 FROM tf1,commandTag CMDTAG_SELECT,strategy PORTAL_ONE_SELECT}… 三个游标的portal都是类似上面处理的。特殊的是顶层portal。不属于当前要释放的子事务4不处理。
portal1 { // 顶层 mySubid 3 当前不处理name , createSubid 3, activeSubid 3, createLevel 3, sourceText call tproc1();,commandTag CMDTAG_CALL,strategy PORTAL_MULTI_QUERY}2第二次子事务提交savepoint子事务提交AtSubCommit_Portalssubid3
发生在commit。
exec_simple_queryfinish_xact_commandCommitTransactionCommandCommitSubTransactionAtSubCommit_Portals当前事务堆栈
CurrentTransactionState-subTransactionId
[1, 2, 3]| | |
top savepoint savepointAtSubCommit_Portals(mySubid3)开始处理portal
portal1 { // 游标 mySubid 4 当前游标的portal被继承给parent portalname unnamed portal 17, createSubid 2, // AtSubCommit_Portals 3--2activeSubid 2, // AtSubCommit_Portals 3createLevel 2, // AtSubCommit_Portals 3--2sourceText SELECT c1 FROM tf1,commandTag CMDTAG_SELECT,strategy PORTAL_ONE_SELECT}当前堆栈Portal已经被drop了。现在PortalHashTable里面只有三个游标的Portal。
3第三次子事务提交savepoint子事务提交subid2
同上。
4顶层事务提交subid1PreCommit_Portals
发生在commit。
exec_simple_queryfinish_xact_commandCommitTransactionCommandCommitTransactionPreCommit_Portals提交时发现3个portal只剩游标的3个portal了。
调用PortalDrop全部释放掉。
PreCommit_Portals函数需要关注的就是普通portal都会被drop掉。特殊保留的是hold cursor即循环语句使用的内部自建游标还有一种就是vacuum等多事务语句。
3 回滚
3.1 顶层事务清理AtAbort_Portals
调用位置
AbortTransaction → AtAbort_Portals
调用一次即可用户清理顶层事务。
清理逻辑
遍历PortalHashTable拿到所有CreatePortal创建出来的Portal两种执行器的portal和游标的portal标记portal状态failed标记failed的内存会被释放掉。 情况一elog FATAL 主动标记failed情况二状态是PORTAL_READY的portal 跳过一些portal不处理。 情况一createSubid0 前一个事务的portal不属于自己!1。情况二portal-autoHeld true 专门用于循环的游标不是用户创建的PL自用的。
3.2 子事务清理AtSubAbort_Portals
调用位置
AbortSubTransaction → AtSubAbort_Portals
有两种调用场景
事务块内一次性rollback递归多次AtSubAbort_Portals提交所有子事务。回滚到某一个检查点递归指定次数AtSubAbort_Portals只提交指定的几个子事务。
清理逻辑
遍历PortalHashTable拿到所有CreatePortal创建出来的Portal两种执行器的portal和游标的portal判断创建归属 如果当前清理的子事务 与 portal的createSubid匹配直接清理PORTAL_READY和PORTAL_ACTIVE状态的包括删除内存。如果当前清理的子事务 与 portal的createSubid不匹配 判断使用归属如果当前清理的子事务 与 portal的activeSubid匹配说明不是当前子事务创建的但是被当前子事务使用了指标记failed但不做清理不删除内存。
4 PushTransaction与PopTransaction函数
启动子事务时需要将当前事务入栈CurrentTransactionState换成子事务的。子事务和父事务由parent连接。修改这部分代码需要注意一次弹出多个事务时currentSubTransactionId有没有正确维护。
static void
PushTransaction(void)
{TransactionState p CurrentTransactionState;TransactionState s;s (TransactionState)MemoryContextAllocZero(TopTransactionContext,sizeof(TransactionStateData));
注意currentSubTransactionId直增不减。pop时也不减少。 currentSubTransactionId 1;if (currentSubTransactionId InvalidSubTransactionId){currentSubTransactionId - 1;pfree(s);ereport(ERROR,(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),errmsg(cannot have more than 2^32-1 subtransactions in a transaction)));}/** We can now stack a minimally valid subtransaction without fear of* failure.*/s-fullTransactionId InvalidFullTransactionId; /* until assigned */s-subTransactionId currentSubTransactionId;s-parent p;s-nestingLevel p-nestingLevel 1;s-gucNestLevel NewGUCNestLevel();s-savepointLevel p-savepointLevel;s-state TRANS_DEFAULT;s-blockState TBLOCK_SUBBEGIN;GetUserIdAndSecContext(s-prevUser, s-prevSecContext);s-prevXactReadOnly XactReadOnly;s-parallelModeLevel 0;s-topXidLogged false;CurrentTransactionState s;}