pgpool2.git / summary / log / commit / refs
commit f5b904847165160848a855b641f9dc05ea098be5
Author: Tatsuo Ishii <ishii at sraoss.co.jp>
Date: Thu Jul 22 04:24:34 2010 +0000
Add failover_if_affected_tuples_mismatch which trigger when
number of affcted tuples by UPDATE/DELETE.
doc/pgpool-en.html | 24 +++++-
doc/pgpool-ja.html | 105 +++++++++++++++++++++++-
pgpool.conf.sample | 11 ++-
pgpool.conf.sample-master-slave | 6 ++
pgpool.conf.sample-replication | 6 ++
pgpool.conf.sample-stream | 6 ++
pool_config.c | 15 ++++
pool_config.h | 7 ++
pool_config.l | 15 ++++
pool_process_query.c | 64 ++++++++-------
pool_proto_modules.c | 174 +++++++++++++++++++++++++++++++++++++---
pool_proto_modules.h | 1 -
pool_session_context.c | 5 ++
pool_session_context.h | 14 ++++
14 files changed, 407 insertions(+), 46 deletions(-)
diff --git a/doc/pgpool-en.html b/doc/pgpool-en.html
index a649634fa..47c762032 100644
--- a/doc/pgpool-en.html
+++ b/doc/pgpool-en.html
@@ -7,7 +7,7 @@
</head>
<!-- hhmts start -->
-Last modified: Wed Jul 14 13:50:01 JST 2010
+Last modified: Thu Jul 22 12:55:49 JST 2010
<!-- hhmts end -->
<body bgcolor="#ffffff">
@@ -833,12 +833,28 @@ configuration parameters below must be set in addtion to everything above.</p>
false.</p>
</dd>
+ <dt>failover_if_affected_tuples_mismatch</dt>
+ <dd>
+ <p>When set to true, if a backend returns number of affected
+ tuples by UPDATE/DELETE
+ different between the backends, the backends that differ from most
+ frequent result set are degenerated.
+ If set to false, the session is terminated and the backends are not
+ degenerated. Default is false.</p>
+ </dd>
+
<dt>replication_stop_on_mismatch</dt>
<dd>
- <p>When set to true, if a SELECT statement returns a result set of records
+ <p>When set to true, if a backend returns packet kind
different between the backends, the backends that differ from most
- frequent result set are degenerated. This is only valid if the SELECT
- statement is part of a transaction and replicate_select is set to true.
+ frequent result set are degenerated. Typical use case is the SELECT
+ statement is part of a transaction and replicate_select is set to
+ true, and SELECT returns diffrent number of rows among backends.
+ Other than SELECT statement might trigger this though.
+ For example, a backend succeeded in an UPDATE, while others
+ failed.
+ Also please note that pgpool does NOT examine content of records
+ returned from SELECT.
If set to false, the session is terminated and the backends are not
degenerated. Default is false.</p>
</dd>
diff --git a/doc/pgpool-ja.html b/doc/pgpool-ja.html
index 8016a4d7c..83bbfde59 100644
--- a/doc/pgpool-ja.html
+++ b/doc/pgpool-ja.html
@@ -8,7 +8,7 @@
<body>
<!-- hhmts start -->
-Last modified: Wed Jul 14 13:50:48 JST 2010
+Last modified: Thu Jul 22 13:07:52 JST 2010
<!-- hhmts end -->
<body bgcolor="#ffffff">
@@ -829,10 +829,22 @@ raw$B%b!<%I!"%3%M%/%7%g%s%W!<%k%b!<%I$K2C$(!"0J2<$r@_Dj$7$^$9!#(B
<dt>replication_stop_on_mismatch
<dd>
<p>
- true$B$r;XDj$,;XDj$5$l$F$$$F!"$+$D%H%i%s%6%/%7%g%s$NCf$G<B9T$5$l$?(BSELECT$B7k2L$,(BDB$B%N!<%I$N4V$GIT0lCW$K$J$C$?>l9g$K!"$=$N(BDB$B%N!<%I$r@Z$jJ|$7$F=LB`1?E>$KF~$j$^$9!#(B
+ $B3F(BDB$B%N!<%I$+$iAw$i$l$F$/$k%Q%1%C%H$N<oN`$,IT0lCW$K$J$C$?>l9g$K!"(BDB$B%N!<%I$r@Z$jJ|$7$F=LB`1?E>$KF~$j$^$9!#(B
+$BNI$/$"$k%1!<%9$H$7$F$O!"(Breplicate_select$B$,;XDj$5$l$F$$$F(BSELECT$B$,3F(BDB$B%N!<%I$G<B9T$5$l$F$$$k$H$-$K!"8!:w7k2L9T?t$,0lCW$7$J$$$J$I!"$,$"$j$^$9(B($B$3$l$K8BDj$5$l$k$b$N$G$O$"$j$^$;$s!#$?$H$($P$"$k(BDB$B%N!<%I$G(BUPDATE$B$,@.8y$7$?$N$K!"B>$N(BDB$B%N!<%I$G$O<:GT$7$?>l9g$,0lNc$G$9(B)$B!#(B
+$B$?$@$7!"(Bpgpool$B$O%Q%1%C%H$NCf?H$^$G$O%A%'%C%/$7$F$$$J$$$N$G!"(BSELECT$B7k2L$N%G!<%?FbMF$,0[$J$C$F$$$F$b!"=LB`$O5/$-$J$$$3$H$KCm0U$7$F$/$@$5$$!#(B
$B=LB`BP>]$N(BDB$B%N!<%I$O!VB??t7h!W$G>/?tGI$K$J$C$?$b$N$,BP>]$K$J$j$^$9!#(B
$B$b$7B??t7h$GF1I<$K$J$C$?>l9g$O!"%^%9%?(BDB$B%N!<%I(B(DB$B%N!<%IHV9f$,$b$C$H$b$o$+$$$b$N(B)$B$r4^$`%0%k!<%W$,M%@h$5$l!"$=$l0J30$N%0%k!<%W$K=jB0$9$k(BDB$B%N!<%I$,@Z$jJ|$7$NBP>]$K$J$j$^$9!#(B
-$B$3$N%*%W%7%g%s$,(Bfalse$B$N>l9g$O!"3:Ev$NLd$$9g$o$;$r6/@)E*$K=*N;$9$k$@$1$KN1$a$^$9!#(B
+$B$3$N%*%W%7%g%s$,(Bfalse$B$N>l9g$O!"3:Ev$N%;%C%7%g%s$r6/@)E*$K=*N;$9$k$@$1$KN1$a$^$9!#(B
+$B%G%U%)%k%HCM$O(Bfalse$B$G$9!#(B
+</p>
+
+<dt>failover_if_affected_tuples_mismatch
+<dd>
+<p>
+ $B3F(BDB$B%N!<%I$G<B9T$5$l$?(BUPDATE/DELETE$B$N7k2L9T?t$,IT0lCW$K$J$C$?>l9g$K!"(BDB$B%N!<%I$r@Z$jJ|$7$F=LB`1?E>$KF~$j$^$9!#(B
+$B=LB`BP>]$N(BDB$B%N!<%I$O!VB??t7h!W$G>/?tGI$K$J$C$?$b$N$,BP>]$K$J$j$^$9!#(B
+$B$b$7B??t7h$GF1I<$K$J$C$?>l9g$O!"%^%9%?(BDB$B%N!<%I(B(DB$B%N!<%IHV9f$,$b$C$H$b$o$+$$$b$N(B)$B$r4^$`%0%k!<%W$,M%@h$5$l!"$=$l0J30$N%0%k!<%W$K=jB0$9$k(BDB$B%N!<%I$,@Z$jJ|$7$NBP>]$K$J$j$^$9!#(B
+$B$3$N%*%W%7%g%s$,(Bfalse$B$N>l9g$O!"3:Ev$N%;%C%7%g%s$r6/@)E*$K=*N;$9$k$@$1$KN1$a$^$9!#(B
$B%G%U%)%k%HCM$O(Bfalse$B$G$9!#(B
</p>
@@ -2899,6 +2911,93 @@ pgpool-II$B$N%A%e!<%H%j%"%k$O(B<a href="tutorial-ja.html">$B$3$3(B<a/>$B$K$
<h1>$B%j%j!<%9%N!<%H(B<a name="release"></a></h1>
+<h2>3.0(umiyameboshi) 2010/08/xx</h2>
+<p>
+$B$3$N%P!<%8%g%s$O(B3.0$B7ONs$N:G=i$NHG$G!"(B2.2$B7O$d(B2.3$B7O$+$i$N!V%a%8%c!<%P!<%8%g%s%"%C%W!W$K$"$?$j$^$9!#(B
+$BB?$/$N5!G=2~NI$,$J$5$l!"?75!G=$,DI2C$5$l$k$H6&$K!"FbIt9=B$$,@0M}$5$l$F8+DL$7$,NI$/$J$C$FJ]<i@-$,8~>e$7$F$$$^$9!#(B
+</p>
+
+<h3>$B?75!G=(B</h3>
+<p>
+<ul>
+ <li>pgpool$B$N5/F0;~$K!"%9%F!<%?%9%U%!%$%k$rL5;k$9$k%*%W%7%g%s$,DI2C$5$l$^$7$?!#(B
+
+ <li>failover_if_affected_tuples_mismatch $B$H$$$&@_Dj9`L\$,DI2C$5$l$^$7$?!#(B
+$B=>Mh!"%l%W%j%1!<%7%g%s%b!<%I$G(BUPDATE/DELETE$B$N7k2L9T?t$,0[$J$k$H!"%H%i%s%6%/%7%g%s$r%"%\!<%H$7$F%;%C%7%g%s$r6/@)@ZCG$7$F$$$^$7$?!#(B
+failover_if_affected_tuples_mismatch $B$r(B true$B$K@_Dj$9$k$H!"$3$N8=>]$,5/$-$?$H$-$K!"IT0lCW$N$"$C$?(BDB$B%N!<%I$r@Z$jJ|$7$F=LB`1?E>$KF~$k$h$&$K$J$j$^$9!#(B
+</ul>
+</p>
+
+<h2>2.3.3 (tomiteboshi) 2010/04/23</h2>
+<p>$B$3$N%P!<%8%g%s$G$O!"(B2.3.2.2 $B0JA0$N?'!9$J%P%0$,=$@5$5$l$F$$$^$9!#(B</p>
+
+<h3>$B8_49@-$N$J$$JQ99(B</h3>
+<ul>
+<li>$B$3$N%P!<%8%g%s$+$i!"(Bpgpool $B$,0JA0$h$jB?$/$N6&M-%a%b%j$r;H$&$h$&$K$J$C$?$N$GCm0U$7$F$/$@$5$$!#(B
+$B$3$l$K$h$kLdBj$,(B pgpool $B$N5/F0;~$KH/@8$7$?>l9g$O!"(Bpgpool $B$N%m%0$r8+$F$/$@$5$$!#(B
+"could not create shared memory segment: Cannot allocate memory" $B$H$$$C$?%a%C%;!<%8$,$"$l$P!"%7%9%F%`$N6&M-%a%b%j$rA}$d$7$F$/$@$5$$!#(B</li>
+<li>$B%Q%i%l%k%b!<%I$,!"%l%W%j%1!<%7%g%s%b!<%I$+%m!<%I%P%i%s%9%b!<%I$,M-8z$G$J$$$H;H$($J$$$h$&$K$J$j$^$7$?!#(B
+pgpool-II $B$G$O$:$C$H!"%l%W%j%1!<%7%g%s%b!<%I$+%m!<%I%P%i%s%9%b!<%I$,M-8z$G$J$$$H$-%Q%i%l%k%b!<%I$O@5$7$/F0:n$7$F$$$^$;$s$G$7$?!#(B(Kitagawa)</li>
+<li>insert_lock $B$N%G%U%)%k%HCM$r(B false $B$KJQ99$7$^$7$?!#$3$l$O!"%^%9%?!&%9%l!<%V%b!<%I$G$O(B true $B$K$7$F$bL50UL#$J$?$a$G$9!#(B
+Fujii Masao $B$5$s$N;XE&$K$h$j=$@5$7$^$7$?!#(B(Tatsuo)</li>
+</ul>
+
+<h3>$B?7$7$/DI2C$7$?%I%-%e%a%s%H(B</h3>
+<ul>
+<li>README.online-recovery $B$rDI2C$7$^$7$?!#$3$N%I%-%e%a%s%H$K$O!"%*%s%i%$%s%j%+%P%j$NFbItE*$J$3$H$,=q$$$F$"$j$^$9!#(B</li>
+</ul>
+
+<h3>$B%P%0=$@5(B</h3>
+<ul>
+<li>$B;R%W%m%;%9$,(B segfault $B$r5/$3$9(B pgpool-II 1.0 $B$+$iD9$$4VB8:_$7$?%P%0$r=$@5$7$^$7$?!#(B
+$B$3$l$O!"?F%W%m%;%9$,(B shmem $B%5%$%:$N7W;;$r4V0c$($F$$$?$3$H$K5/0x$7$^$9!#%P%02r@O(B(Kitagawa)$B!"%Q%C%A:n@.(B(Tatsuo)</li>
+
+<li>$B%I%-%e%a%s%H$K%Q%i%l%k%b!<%IMQ$N0J2<$N@)Ls$rDI5-$7$^$7$?!#(B(Kitagawa)
+ <ul>
+ <li>- NATURAL JOIN $B$O;H$($^$;$s!#(B</li>
+ <li>- USING $B6g$,!"%/%(%j=q$-49$(=hM}$K$h$C$F(B ON $B6g$KJQ49$5$l$^$9!#(B</li>
+ </ul>
+</li>
+
+<li>$B%Q%i%l%k%b!<%I$N$H$-!"(BUSING $B6g$r4^$`(B JOIN $B9=J8$N=q$-49$($GH/@8$9$k2DG=@-$,$"$C$?%/%i%C%7%e$r=$@5$7$^$7$?!#(B(Kitagawa)<br>
+ $B$3$N=$@5$K$h$j!"0J2<$N$h$&$J(B JOIN $B9=J8$,;H$($k$h$&$K$J$j$^$9!#(B<br>
+ $BNc!'(B<pre>
+SELECT * FROM a JOIN b USING (aid) JOIN c USING (cid);
+SELECT * FROM a JOIN b USING (aid) JOIN c USING (cid) JOIN d USING</pre>
+</li>
+
+<li>$B%Q%i%l%k%/%(%j$G!"J,;6%-!<Ns$NA0$K(B current_time $B$r4^$`(B INSERT $BJ8$N%Q!<%9$,$G$-$k$h$&$K=$@5$7$^$7$?!#(B</li>
+
+<li>SimpleForwardToBackend() $B$r=$@5$7$^$7$?!#(B
+$B$3$l$K$h$j!"3HD%%W%m%H%3%k$r;H$C$?%/%i%$%"%s%H$,(B bind $B%(%i!<$N$h$&$J%(%i!<$rH/@8$5$;$?$H$-$K!"(B
+pgpool $B$,%P%C%/%(%s%I$N1~Ez$rBT$AB3$1$J$/$J$j$^$9!#(B
+$B$3$N%P%0$O!"%^%9%?!&%9%l!<%V%b!<%I!"(Braw $B%b!<%I!"%3%M%/%7%g%s%W!<%k%b!<%I$GH/@8$7$F$$$^$7$?!#(B
+$B$3$l$r=$@5$7$?$3$H$K$h$C$F!"%3%^%s%I$,%(%i!<$K$J$C$?$"$H!"%(%i!<$r2sI|$5$;$k$?$a$K(B SYNC $B%a%C%;!<%8$rAw$k$h$&$K$J$j$^$9!#(B(Kitagawa)</li>
+
+<li>select() $B$r<B9TCf$N(B pgpool $B;R%W%m%;%9$,!"(BSIGINT/SIGQUIT $B%7%0%J%k$rL5;k$9$k$h$&$K=$@5$7$^$7$?!#(B
+$B%7%0%J%k$,Aw$i$l$F$b(B pgpool $B$O(B select() $B$r:F<B9T$7$h$&$H$9$k$N$G!"7k2LE*$K%7%0%J%k$,L5;k$5$l$^$9!#(B(Tatsuo)</li>
+
+<li>connect_inet_domain_socket_by_port/connect_unix_domain_socket_by_port $B$,(B SIGTERM/SIGINT/SIGQUIT $B%7%0%J%k$r(B
+$B<u$1<h$C$F$$$J$$$+%A%'%C%/$9$k$h$&$K=$@5$7$^$7$?!#(B Daniel Codina $B$5$s$+$i$N%P%0Js9p$K4p$E$-$^$9!#(B(Tatsuo)</li>
+
+<li>"kind mismatch" $B$H$$$&%(%i!<%a%C%;!<%8$r@8@.$9$k:]$K%/%i%C%7%e$9$k2DG=@-$,$"$C$?$N$G!"$3$l$r=$@5$7$^$7$?!#(B
+$B0JA0$OLdBj$J$+$C$?$N$G$9$,!"(B2.3.2$B$G%(%s%P%0$7$^$7$?!#(B(Tatsuo)</li>
+
+<li>$B%X%k%9%A%'%C%/$K$*$1$k%P%0$r=$@5$7$^$7$?!#(B
+$B%3!<%I$,H4$1$?$J$I$G%M%C%H%o!<%/>c32$,H/@8$7$?$H$-$K!"(Bconnect() $B$r8F$s$G$$$k4V$O%X%k%9%A%'%C%/$,9T$J$o$l$F$$$^$;$s$G$7$?!#(B
+$B$3$l$O!"(Bconnect() $B$,(B ALARM $B%7%0%J%k$K$h$C$F3d$j9~$^$l$?:]$K!"(Bconnect_unix_domain_socket()/connect_inet_domain_socket() $B$,:F;n9T$7$F$$$?$?$a$G$9!#(B
+$B$3$N=$@5$G$O!">e5-$N4X?t$KBP$7$F:F;n9T$r%3%s%H%m!<%k$9$k$h$&$J0z?t$rDI2C$7$F$$$^$9!#(B
+$B$3$l$O!"(BDaniel Codina $B$5$s$N%P%0Js9p$HJ,@O$K4p$E$/=$@5$G$9!#(B(Tatsuo)</li>
+
+<li>2.3.2.2 $B$G!"(BSimpleForwardToBackend $B$G$N%?%$%`%9%?%s%W$N=q$-49$($K$*$$$F%(%s%P%0$7$F$$(B
+$B$?$N$G!"$3$l$r=$@5$7$^$7$?!#$3$l$O!"%P%0%H%i%C%/(B #1010771 $B$K$"$k(B Peter Pramberge $B$5$s$i$NJs9p$K4p$E$-$^$9!#(B(Tatsuo)</li>
+
+<li>$B%Q%i%l%k%/%(%j$K$*$1$k(B "*" $B$N=q$-49$($r=$@5$7$^$7$?!#(Bsho $B$5$s$+$iDs6!$$$?$@$$$?%Q%C%A$r<h$j9~$_$^$7$?!#(B(Kitagawa)</li>
+
+<li>connect_inet_domain_socket_by_port() $B$G%(%i!<%a%C%;!<%8$r=PNO$9$k:]$K!"(B
+strerror() $B$G$O$J$/(B hstrerror() $B$r;H$&$h$&$K=$@5$7$^$7$?!#(B (Tatsuo)</li>
+</ul>
+
<h2>2.3.2.2 (tomiteboshi) 2010/02/22</h2>
<h3>$B35MW(B</h3>
<p>
diff --git a/pgpool.conf.sample b/pgpool.conf.sample
index 5ab176822..3d2dd54d0 100644
--- a/pgpool.conf.sample
+++ b/pgpool.conf.sample
@@ -66,10 +66,17 @@ replication_mode = false
# This is ignored if replication_mode is false.
load_balance_mode = false
-# if there's a data mismatch between master and secondary
-# start degeneration to stop replication mode
+# If there's a disagreement with the packet kind sent from backend,
+# then degenrate the node which is most likely "minority". If false,
+# just force to exit this session.
replication_stop_on_mismatch = false
+# If there's a disagreement with the number of affected tuples in
+# UPDATE/DELETE, then degenrate the node which is most likely
+# "minority".
+# If false, just abort the transaction to keep the consistency.
+failover_if_affected_tuples_mismatch = false
+
# If true, replicate SELECT statement when load balancing is disabled.
# If false, it is only sent to the master node.
replicate_select = false
diff --git a/pgpool.conf.sample-master-slave b/pgpool.conf.sample-master-slave
index ef163417c..341932a83 100644
--- a/pgpool.conf.sample-master-slave
+++ b/pgpool.conf.sample-master-slave
@@ -70,6 +70,12 @@ load_balance_mode = true
# start degeneration to stop replication mode
replication_stop_on_mismatch = false
+# If there's a disagreement with the number of affected tuples in
+# UPDATE/DELETE, then degenrate the node which is most likely
+# "minority".
+# If false, just abort the transaction to keep the consistency.
+failover_if_affected_tuples_mismatch = false
+
# If true, replicate SELECT statement when load balancing is disabled.
# If false, it is only sent to the master node.
replicate_select = false
diff --git a/pgpool.conf.sample-replication b/pgpool.conf.sample-replication
index 397131705..76ce1142f 100644
--- a/pgpool.conf.sample-replication
+++ b/pgpool.conf.sample-replication
@@ -70,6 +70,12 @@ load_balance_mode = true
# start degeneration to stop replication mode
replication_stop_on_mismatch = false
+# If there's a disagreement with the number of affected tuples in
+# UPDATE/DELETE, then degenrate the node which is most likely
+# "minority".
+# If false, just abort the transaction to keep the consistency.
+failover_if_affected_tuples_mismatch = false
+
# If true, replicate SELECT statement when load balancing is disabled.
# If false, it is only sent to the master node.
replicate_select = false
diff --git a/pgpool.conf.sample-stream b/pgpool.conf.sample-stream
index 56880ef8c..a1f964ba4 100644
--- a/pgpool.conf.sample-stream
+++ b/pgpool.conf.sample-stream
@@ -70,6 +70,12 @@ load_balance_mode = true
# start degeneration to stop replication mode
replication_stop_on_mismatch = false
+# If there's a disagreement with the number of affected tuples in
+# UPDATE/DELETE, then degenrate the node which is most likely
+# "minority".
+# If false, just abort the transaction to keep the consistency.
+failover_if_affected_tuples_mismatch = false
+
# If true, replicate SELECT statement when load balancing is disabled.
# If false, it is only sent to the master node.
replicate_select = false
diff --git a/pool_config.c b/pool_config.c
index b6c332fee..3bd1125c1 100644
--- a/pool_config.c
+++ b/pool_config.c
@@ -1881,6 +1881,7 @@ int pool_init_config(void)
pool_config->replication_mode = 0;
pool_config->load_balance_mode = 0;
pool_config->replication_stop_on_mismatch = 0;
+ pool_config->failover_if_affected_tuples_mismatch = 0;
pool_config->replicate_select = 0;
pool_config->reset_query_list = default_reset_query_list;
pool_config->num_reset_queries = sizeof(default_reset_query_list)/sizeof(char *);
@@ -2343,6 +2344,20 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context)
pool_debug("replication_stop_on_mismatch: %d", v);
pool_config->replication_stop_on_mismatch = v;
}
+ else if (!strcmp(key, "failover_if_affected_tuples_mismatch") &&
+ CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
+ {
+ int v = eval_logical(yytext);
+
+ if (v < 0)
+ {
+ pool_error("pool_config: invalid value %s for %s", yytext, key);
+ fclose(fd);
+ return(-1);
+ }
+ pool_debug("failover_if_affected_tuples_mismatch: %d", v);
+ pool_config->failover_if_affected_tuples_mismatch = v;
+ }
else if (!strcmp(key, "replicate_select") &&
CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
{
diff --git a/pool_config.h b/pool_config.h
index 3f0d65543..6d690c2f1 100644
--- a/pool_config.h
+++ b/pool_config.h
@@ -65,6 +65,13 @@ typedef struct {
int replication_stop_on_mismatch; /* if there's a data mismatch between master and secondary
* start degenration to stop replication mode
*/
+
+ /* If there's a disagreement with the number of affected tuples in
+ * UPDATE/DELETE, then degenrate the node which is most likely
+ * "minority". # If false, just abort the transaction to keep the
+ * consistency.*/
+ int failover_if_affected_tuples_mismatch;
+
int replicate_select; /* if non 0, replicate SELECT statement when load balancing is disabled. */
char **reset_query_list; /* comma separated list of quries to be issued at the end of session */
diff --git a/pool_config.l b/pool_config.l
index c1cca5fb7..646d13d15 100644
--- a/pool_config.l
+++ b/pool_config.l
@@ -156,6 +156,7 @@ int pool_init_config(void)
pool_config->replication_mode = 0;
pool_config->load_balance_mode = 0;
pool_config->replication_stop_on_mismatch = 0;
+ pool_config->failover_if_affected_tuples_mismatch = 0;
pool_config->replicate_select = 0;
pool_config->reset_query_list = default_reset_query_list;
pool_config->num_reset_queries = sizeof(default_reset_query_list)/sizeof(char *);
@@ -618,6 +619,20 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context)
pool_debug("replication_stop_on_mismatch: %d", v);
pool_config->replication_stop_on_mismatch = v;
}
+ else if (!strcmp(key, "failover_if_affected_tuples_mismatch") &&
+ CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
+ {
+ int v = eval_logical(yytext);
+
+ if (v < 0)
+ {
+ pool_error("pool_config: invalid value %s for %s", yytext, key);
+ fclose(fd);
+ return(-1);
+ }
+ pool_debug("failover_if_affected_tuples_mismatch: %d", v);
+ pool_config->failover_if_affected_tuples_mismatch = v;
+ }
else if (!strcmp(key, "replicate_select") &&
CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
{
diff --git a/pool_process_query.c b/pool_process_query.c
index ca89ae731..273a38bb3 100644
--- a/pool_process_query.c
+++ b/pool_process_query.c
@@ -1063,6 +1063,7 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
// char kind1;
// POOL_STATUS ret;
POOL_SESSION_CONTEXT *session_context;
+ bool mismatch_ntuples = false;
/* Get session context */
session_context = pool_get_session_context();
@@ -1145,6 +1146,11 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
{
command_ok_row_count = extract_ntuples(p);
+ /*
+ * Save number of affcted tuples of master node.
+ */
+ session_context->ntuples[MASTER_NODE_ID] = command_ok_row_count;
+
/*
* if we are in the parallel mode, we have to sum up the number
* of affected rows
@@ -1158,8 +1164,14 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
for (i=0;i<NUM_BACKENDS;i++)
{
- if (VALID_BACKEND(i) && !IS_MASTER_NODE_ID(i))
+ if (!IS_MASTER_NODE_ID(i))
{
+ if (!VALID_BACKEND(i))
+ {
+ session_context->ntuples[i] = -1;
+ continue;
+ }
+
status = pool_read(CONNECTION(backend, i), &len, sizeof(len));
if (status < 0)
{
@@ -1184,6 +1196,11 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
{
int n = extract_ntuples(p);
+ /*
+ * Save number of affcted tuples.
+ */
+ session_context->ntuples[i] = n;
+
/*
* if we are in the parallel mode, we have to sum up the number
* of affected rows
@@ -1194,7 +1211,7 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
}
else if (command_ok_row_count != n) /* mismatch update rows */
{
- mismatch_ntuples = 1;
+ mismatch_ntuples = true;
}
}
}
@@ -1202,6 +1219,8 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
if (mismatch_ntuples)
{
+ char msgbuf[128];
+
String *msg = init_string("pgpool detected difference of the number of inserted, updated or deleted tuples. Possible last query was: \"");
string_append_char(msg, query_string_buffer);
string_append_char(msg, "\"");
@@ -1210,6 +1229,22 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend,
"check data consistency between master and other db node", __FILE__, __LINE__);
pool_error(msg->data);
free_string(msg);
+
+ msg = init_string("SimpleForwardToFrontend: Number of affected tuples are:");
+
+ for (i=0;i<NUM_BACKENDS;i++)
+ {
+ snprintf(msgbuf, sizeof(msgbuf), " %d", session_context->ntuples[i]);
+ string_append_char(msg, msgbuf);
+ }
+ pool_log("%s", msg->data);
+ free_string(msg);
+
+ /*
+ * Remember that we have different number of UPDATE/DELETE
+ * affcted tuples in backends.
+ */
+ session_context->mismatch_ntuples = true;
}
else
{
@@ -1720,7 +1755,6 @@ void reset_variables(void)
pool_unset_query_in_progress();
internal_transaction_started = 0;
- mismatch_ntuples = 0;
select_in_transaction = 0;
execute_select = 0;
receive_extended_begin = 0;
@@ -3434,30 +3468,6 @@ POOL_STATUS read_kind_from_backend(POOL_CONNECTION *frontend, POOL_CONNECTION_PO
kind_list[i] = 0;
}
-#ifdef NOT_USED
- /* register kind map */
- for (i = 0; i < NUM_BACKENDS; i++)
- {
- /* initialize degenerate record */
- degenerate_node[i] = 0;
-
- /* kind is signed char.
- * We must check negative number.
- */
- int id = kind_list[i] + 128;
-
- if (kind_list[i] == -1)
- continue;
-
- kind_map[id]++;
- if (kind_map[id] > max_count)
- {
- max_kind = kind_list[i];
- max_count = kind_map[id];
- }
- }
-#endif
-
if (max_count != num_executed_nodes)
{
/*
diff --git a/pool_proto_modules.c b/pool_proto_modules.c
index e3517d399..893b5558d 100644
--- a/pool_proto_modules.c
+++ b/pool_proto_modules.c
@@ -61,7 +61,6 @@ int replication_was_enabled; /* replication mode was enabled */
int master_slave_was_enabled; /* master/slave mode was enabled */
int internal_transaction_started; /* to issue table lock command a transaction
has been started internally */
-int mismatch_ntuples; /* number of updated tuples */
char *copy_table = NULL; /* copy table name */
char *copy_schema = NULL; /* copy table name */
char copy_delimiter; /* copy delimiter char */
@@ -102,6 +101,7 @@ static POOL_STATUS parse_before_bind(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend,
PreparedStatement *ps);
static void overwrite_map_for_deallocate(POOL_QUERY_CONTEXT *query_context);
+static int* find_victim_nodes(int *ntuples, int nmembers, int master_node, int *number_of_nodes);
/*
* Process Query('Q') message
@@ -1178,8 +1178,8 @@ POOL_STATUS FunctionCall3(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backe
/*
* Process ReadyForQuery('Z') message.
*
- * - if the global error status "mismatch_ntuples" is set, send an error query
- * to all DB nodes to abort transaction.
+ * - if the error status "mismatch_ntuples" is set, send an error query
+ * to all DB nodes to abort transaction or do failover.
* - internal transaction is closed
*/
POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
@@ -1202,15 +1202,59 @@ POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
}
/*
- * If the numbers of update tuples are differ, we need to abort transaction
- * by using do_error_command. This only works with PROTO_MAJOR_V3.
+ * If the numbers of update tuples are differ and
+ * failover_if_affected_tuples_mismatch is false, we abort
+ * transactions by using do_error_command. If
+ * failover_if_affected_tuples_mismatch is true, trigger failover.
+ * This only works with PROTO_MAJOR_V3.
*/
- if (mismatch_ntuples && MAJOR(backend) == PROTO_MAJOR_V3)
+ if (session_context->mismatch_ntuples && MAJOR(backend) == PROTO_MAJOR_V3)
{
int i;
signed char state;
char kind;
+ /*
+ * If failover_if_affected_tuples_mismatch, is true, then
+ * decide victim nodes by using find_victim_nodes and
+ * degenerate them.
+ */
+ if (pool_config->failover_if_affected_tuples_mismatch)
+ {
+ int *victim_nodes;
+ int number_of_nodes;
+ char msgbuf[128];
+
+ victim_nodes = find_victim_nodes(session_context->ntuples, NUM_BACKENDS,
+ MASTER_NODE_ID, &number_of_nodes);
+ if (victim_nodes)
+ {
+ String *msg = init_string("ReadyForQuery: Degenerate backends:");
+ int i;
+
+ for (i=0;i<number_of_nodes;i++)
+ {
+ snprintf(msgbuf, sizeof(msgbuf), " %d", victim_nodes[i]);
+ string_append_char(msg, msgbuf);
+ }
+ pool_log("%s", msg->data);
+ free_string(msg);
+
+ msg = init_string("ReadyForQuery: Number of affected tuples are:");
+
+ for (i=0;i<NUM_BACKENDS;i++)
+ {
+ snprintf(msgbuf, sizeof(msgbuf), " %d", session_context->ntuples[i]);
+ string_append_char(msg, msgbuf);
+ }
+ pool_log("%s", msg->data);
+ free_string(msg);
+
+ degenerate_backend_set(victim_nodes, number_of_nodes);
+ child_exit(1);
+ }
+ }
+
/*
* XXX: discard rest of ReadyForQuery packet
*/
@@ -1221,8 +1265,6 @@ POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
if (state < 0)
return POOL_END;
- pool_debug("ReadyForQuery: transaction state: %c", state);
-
for (i = 0; i < NUM_BACKENDS; i++)
{
if (VALID_BACKEND(i))
@@ -1258,7 +1300,7 @@ POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
return POOL_END;
}
}
- mismatch_ntuples = 0;
+ session_context->mismatch_ntuples = false;
}
/*
@@ -2716,3 +2758,117 @@ static void overwrite_map_for_deallocate(POOL_QUERY_CONTEXT *query_context)
}
}
}
+
+/*
+ * Find victim nodes by "decide by majority" rule and returns array
+ * of victim node ids. If no victim is found, return NULL.
+ *
+ * Arguments:
+ * ntuples: Array of number of affected tuples. -1 represents down node.
+ * nmembers: Number of elements in ntuples.
+ * master_node: The master node id. Less than 0 means ignore this parameter.
+ * number_of_nodes: Number of elements in victim nodes array.
+ *
+ * Note: If no one wins and master_node > 0, winner would be the
+ * master and other nodes who has same number of tuples as the master.
+ *
+ * Caution: Returned victim node array is allocated in static memory
+ * of this function. Subsequent calls to this function will overwrite
+ * the memory.
+ */
+static int* find_victim_nodes(int *ntuples, int nmembers, int master_node, int *number_of_nodes)
+{
+ static int victim_nodes[MAX_NUM_BACKENDS];
+ static int votes[MAX_NUM_BACKENDS];
+ int maxvotes;
+ int majority_ntuples;
+ int me;
+ int cnt;
+ int healthy_nodes;
+ int i, j;
+
+ healthy_nodes = 0;
+ *number_of_nodes = 0;
+ maxvotes = 0;
+
+ for (i=0;i<nmembers;i++)
+ {
+ me = ntuples[i];
+
+ /* Health node? */
+ if (me < 0)
+ {
+ votes[i] = -1;
+ continue;
+ }
+
+ healthy_nodes++;
+ votes[i] = 1;
+
+ for (j=0;j<nmembers;j++)
+ {
+ if (i != j && me == ntuples[j])
+ {
+ votes[i]++;
+
+ if (votes[i] > maxvotes)
+ {
+ maxvotes = votes[i];
+ majority_ntuples = me;
+ }
+ }
+ }
+ }
+
+ /* Everyone is different */
+ if (maxvotes == 1)
+ {
+ /* Master node is specified? */
+ if (master_node < 0)
+ return NULL;
+
+ /*
+ * If master node is specified, let it and others who has same
+ * ntuples win.
+ */
+ majority_ntuples = ntuples[master_node];
+ }
+ else
+ {
+ /* Find number of majority */
+ cnt = 0;
+ for (i=0;i<nmembers;i++)
+ {
+ if (votes[i] == maxvotes)
+ {
+ cnt++;
+ }
+ }
+
+ if (cnt <= healthy_nodes / 2.0)
+ {
+ /* No one wins */
+
+ /* Master node is specified? */
+ if (master_node < 0)
+ return NULL;
+
+ /*
+ * If master node is specified, let it and others who has same
+ * ntuples win.
+ */
+ majority_ntuples = ntuples[master_node];
+ }
+ }
+
+ /* Make victim nodes list */
+ for (i=0;i<nmembers;i++)
+ {
+ if (ntuples[i] > 0 && ntuples[i] != majority_ntuples)
+ {
+ victim_nodes[(*number_of_nodes)++] = i;
+ }
+ }
+
+ return victim_nodes;
+}
diff --git a/pool_proto_modules.h b/pool_proto_modules.h
index 6f75d7022..5a30dae16 100644
--- a/pool_proto_modules.h
+++ b/pool_proto_modules.h
@@ -42,7 +42,6 @@ extern int replication_was_enabled; /* replication mode was enabled */
extern int master_slave_was_enabled; /* master/slave mode was enabled */
extern int internal_transaction_started; /* to issue table lock command a transaction
has been started internally */
-extern int mismatch_ntuples; /* number of updated tuples */
extern char *copy_table; /* copy table name */
extern char *copy_schema; /* copy table name */
extern char copy_delimiter; /* copy delimiter char */
diff --git a/pool_session_context.c b/pool_session_context.c
index 10df35f29..632bfc090 100644
--- a/pool_session_context.c
+++ b/pool_session_context.c
@@ -103,6 +103,11 @@ void pool_init_session_context(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *
/* Initialize where to send map for PREPARE statemets */
memset(&session_context->prep_where, 0, sizeof(session_context->prep_where));
session_context->prep_where.nelem = POOL_MAX_PREPARED_STATEMENTS;
+
+ /* Reset flag to indicate difference in number of affected tuples
+ * in UPDATE/DELETE.
+ */
+ session_context->mismatch_ntuples = false;
}
/*
diff --git a/pool_session_context.h b/pool_session_context.h
index a2e2fe52a..e82bd1926 100644
--- a/pool_session_context.h
+++ b/pool_session_context.h
@@ -144,8 +144,22 @@ typedef struct {
Portal *pending_portal; /* used until receive backend response */
PreparedStatementList pstmt_list; /* named statement list */
PortalList portal_list; /* named portal list */
+
int load_balance_node_id; /* selected load balance node id */
+ /*
+ * If true, UPDATE/DELETE caused difference in number of affected
+ * tuples in backends.
+ */
+ bool mismatch_ntuples;
+
+ /*
+ * If mismatch_ntuples true, this array holds the number of
+ * affected tuples of each node.
+ * -1 for down nodes.
+ */
+ int ntuples[MAX_NUM_BACKENDS];
+
#ifdef NOT_USED
/* We need to override these gotchas... */
int force_replication;
[parent: c00cdc3d8c1a]