pgpool-II3.1 里面,有一些比较奇怪的做法,至少目前在我看来,是画蛇添足。
如果你没有声明 begin transaction 和 end/commit/rollback 。
当你执行一个SQL文的时候,如果事前没有 begin transaction 之类的,
它会在你所执行的单一的 update/insert/delete SQL 执行前,后,分别追加 BEGIN 和COMMIT。
虽然我认为这个追加是没有必要的,只要交给后台数据库就好了,但是还是先来探讨一下其实现机理。
具体是如何实现的呢。看代码:
复制代码
start_internal_transaction 函数
/*
* Start an internal transaction if necessary.
*/
POOL_STATUS start_internal_transaction(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, Node *node)
{
int i;
/* If we are not in a transaction block,
* start a new transaction
*/
if (is_internal_transaction_needed(node)){
for (i=0;i<NUM_BACKENDS;i++){
if (VALID_BACKEND(i) &&
!INTERNAL_TRANSACTION_STARTED(backend, i) &&
TSTATE(backend, i) == 'I' )
{
per_node_statement_log(backend, i, "BEGIN");
if (do_command(frontend, CONNECTION(backend, i), "BEGIN",
MAJOR(backend),
MASTER_CONNECTION(backend)->pid,
MASTER_CONNECTION(backend)->key, 0) != POOL_CONTINUE)
return POOL_END;
/* Mark that we started new transaction */
INTERNAL_TRANSACTION_STARTED(backend, i) = true;
pool_unset_writing_transaction();
}
}
}
return POOL_CONTINUE;
}
end_internal_transaction 函数
/*
* End internal transaction.
*/
POOL_STATUS end_internal_transaction(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
{
……
/* We need to commit from secondary to master. */
for (i=0;i<NUM_BACKENDS;i++){
if (VALID_BACKEND(i) && !IS_MASTER_NODE_ID(i) &&
TSTATE(backend, i) != 'I' &&
INTERNAL_TRANSACTION_STARTED(backend, i))
{
……
per_node_statement_log(backend, i, "COMMIT");
/* COMMIT success? */
if (do_command(frontend, CONNECTION(backend, i), "COMMIT",
MAJOR(backend),
MASTER_CONNECTION(backend)->pid,MASTER_CONNECTION(backend)->key, 1) != POOL_CONTINUE)
{
INTERNAL_TRANSACTION_STARTED(backend, i) = false;
POOL_SETMASK(&oldmask);
return POOL_END;
}
INTERNAL_TRANSACTION_STARTED(backend, i) = false;
}
}
/* Commit on master */
if (TSTATE(backend, MASTER_NODE_ID) != 'I' &&
INTERNAL_TRANSACTION_STARTED(backend, MASTER_NODE_ID))
{
……
per_node_statement_log(backend, MASTER_NODE_ID, "COMMIT");
if (do_command(frontend, MASTER(backend), "COMMIT", MAJOR(backend),
MASTER_CONNECTION(backend)->pid,MASTER_CONNECTION(backend)->key, 1) !=
POOL_CONTINUE)
{
INTERNAL_TRANSACTION_STARTED(backend, MASTER_NODE_ID) = false;
POOL_SETMASK(&oldmask);
return POOL_END;
}
INTERNAL_TRANSACTION_STARTED(backend, MASTER_NODE_ID) = false;
}
POOL_SETMASK(&oldmask);
return POOL_CONTINUE;
}
复制代码
其中所使用的 is_internal_transaction_needed 函数,其篇幅比较长,代码省略。
其主要做法,就是把 delete/insert/update 之类的关键字放到一个list中,然后用二分法查找。
如果命中,就认为 internal_transaction_needded。
从更高的层次看,有如下的调用关系:
pool_process_query → ProcessFrontEndResponse 和 ProcessBackendResponse
ProcessFrontEndResponse →
SimpleQuery → start_internal_transaction → is_internal_transaction_needed
ProcessBackendResponse →
ReadyForQuery →end_internal_transaction
需要注意,在 start_internal_transaction 中追加 BEGIN需要以下几个条件:
i) Interal transaction 被认定是必须的
ii) INTERNAL_TRANSACTION_STARTED 标志位被置位
iii) 各DB节点的TSTATE 状态是初始状态 I
而在 end_internal_transaction 中追加 COMMIT需要以下几个条件:
i) Interal transaction 被认定是必须的
ii) INTERNAl_TRANSACTION_STARTED标志位被置位
iii) 各DB节点的TSTATE 状态时初始状态 I
普通的单一SQL文执行的时候,上述的条件均可得到满足。
但是,像begein transaction 这样的语句,一旦执行,会改变DB节点的 TSTATE状态为 T。
这样,即使后面再有 单一SQL文被执行,也没有可能满足上面的条件了。
直到遇到了 begin-transaction--commit/rollback 的 commit/rollback , TSTATE状态恢复为I 。
换句话说,其实pgpool-II中,根本就没有对 begin transaction 语句的识别部分的代码!诡异!
本文转自健哥的数据花园博客园博客,原文链接:http://www.cnblogs.com/gaojian/archive/2012/08/29/2661439.html,如需转载请自行联系原作者