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]