12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765 |
- #!/usr/bin/perl
- # This tool is "fat-packed": most of its dependent modules are embedded
- # in this file.
- # https://github.com/Tusamarco/proxy_sql_tools/blob/master/galera_check.pl
- #######################################
- #
- # ProxySQL galera check v1
- #
- # Author Marco Tusa
- # Copyright (C) (2016 - 2020)
- #
- #
- #THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
- #WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- #MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- #
- #This program is free software; you can redistribute it and/or modify it under
- #the terms of the GNU General Public License as published by the Free Software
- #Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
- #systems, you can issue `man perlgpl' or `man perlartistic' to read these
- #licenses.
- #
- #You should have received a copy of the GNU General Public License along with
- #this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- #Place, Suite 330, Boston, MA 02111-1307 USA.
- #######################################
- package galera_check ;
- use Time::HiRes qw(gettimeofday);
- use strict;
- use DBI;
- use Getopt::Long;
- use Pod::Usage;
- $Getopt::Long::ignorecase = 0;
- my $Param = {};
- my $user = "admin";
- my $pass = "admin";
- my $help = '';
- my $host = '' ;
- my $debug = 0 ;
- my %hostgroups;
- my $mysql_connect_timeout=6;
- my %processState;
- my %processCommand;
- my @HGIds;
- ######################################################################
- #Local functions
- ######################################################################
- sub URLDecode {
- my $theURL = $_[0];
- $theURL =~ tr/+/ /;
- $theURL =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg;
- $theURL =~ s/<!--(.|\n)*-->//g;
- return $theURL;
- }
- sub URLEncode {
- my $theURL = $_[0];
- $theURL =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
- return $theURL;
- }
- # return a proxy object
- sub get_proxy($$$$){
- my $dns = shift;
- my $user = shift;
- my $pass = shift;
- my $debug = shift;
- my $proxynode = ProxySqlNode->new();
- $proxynode->dns($dns);
- $proxynode->user($user);
- $proxynode->password($pass);
- $proxynode->debug($debug);
-
- return $proxynode;
-
- }
- sub main{
- # ============================================================================
- #+++++ INITIALIZATION
- # ============================================================================
- if($#ARGV < 0){
- pod2usage(-verbose => 2) ;
- exit 1;
- }
-
- if($#ARGV < 3){
- #given a ProxySQL scheduler
- #limitation we will pass the whole set of params as one
- # and will split after
- @ARGV = split('\ ',$ARGV[0]);
- }
-
- $Param->{user} = '';
- $Param->{log} = undef ;
- $Param->{password} = '';
- $Param->{host} = '';
- $Param->{port} = 3306;
- $Param->{debug} = 0;
- $Param->{processlist} = 0;
- $Param->{OS} = $^O;
- $Param->{main_segment} = 1;
- $Param->{retry_up} = 0;
- $Param->{retry_down} = 0;
- $Param->{print_execution} = 1;
- $Param->{development} = 0;
- $Param->{development_time} = 2;
- $Param->{active_failover} = 0;
- $Param->{single_writer} = 1;
- $Param->{writer_is_reader} = 1;
- $Param->{check_timeout} = 800;
- $Param->{ssl_certs_path} = undef;
-
- my $run_pid_dir = "/tmp" ;
-
- #if (
- GetOptions(
- 'user|u:s' => \$Param->{user},
- 'password|p:s' => \$Param->{password},
- 'host|h:s' => \$host,
- 'port|P:i' => \$Param->{port},
- 'debug|d:i' => \$Param->{debug},
- 'log:s' => \$Param->{log},
- 'hostgroups|H:s'=> \$Param->{hostgroups},
- 'main_segment|S:s'=> \$Param->{main_segment},
- 'retry_up:i' => \$Param->{retry_up},
- 'retry_down:i' => \$Param->{retry_down},
- 'execution_time:i' => \$Param->{print_execution},
- 'development:i' => \$Param->{development},
- 'development_time:i' => \$Param->{development_time},
- 'single_writer:i' => \$Param->{single_writer},
- 'writer_is_also_reader:i' => \$Param->{writer_is_reader},
- 'active_failover:i' => \$Param->{active_failover},
- 'check_timeout:i' => \$Param->{check_timeout},
- 'ssl_certs_path:s' => \$Param->{ssl_certs_path},
-
- 'help|?' => \$Param->{help}
-
- ) or pod2usage(2);
- pod2usage(-verbose => 2) if $Param->{help};
- die print Utils->print_log(1,"Option --hostgroups not specified.\n") unless defined($Param->{hostgroups});
- die print Utils->print_log(1,"Option --host not specified.\n") unless defined $Param->{host};
- die print Utils->print_log(1,"Option --user not specified.\n") unless defined $Param->{user};
- die print Utils->print_log(1,"Option --port not specified.\n") unless defined $Param->{port};
- die print Utils->print_log(1,"Option --active_failover has an invalid value ($Param->{active_failover}).\n"
- ."Valid values are:\n"
- ." 0 [default] do not make failover\n"
- ." 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers\n"
- ." 2 use PXC_CLUSTER_VIEW to identify a server in the same segment\n"
- ." 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW) ") unless $Param->{active_failover} < 4;
- #die "Option --log not specified. We need a place to log what is going on, don't we?\n" unless defined $Param->{log};
- print Utils->print_log(2,"Option --log not specified. We need a place to log what is going on, don't we?\n") unless defined $Param->{log};
-
- if($Param->{debug}){
- Utils::debugEnv();
- }
-
- $Param->{host} = URLDecode($host);
- my $dsn = "DBI:mysql:host=$Param->{host};port=$Param->{port}";
-
- if(defined $Param->{user}){
- $user = "$Param->{user}";
- }
- if(defined $Param->{password}){
- $pass = "$Param->{password}";
- }
- my $hg =$Param->{hostgroups};
- $hg =~ s/[\:,\,]/_/g;
- my $base_path = "${run_pid_dir}/proxysql_galera_check_${hg}.pid";
-
-
-
- #============================================================================
- # Execution
- #============================================================================
- if(defined $Param->{log}){
- open(FH, '>>', $Param->{log}."_".$hg.".log") or die Utils->print_log(1,"cannot open file");
- FH->autoflush if $Param->{development} < 2;
- select FH;
- }
- #checks for ssl cert path and identify if accessible.
- #If defined and not accessible exit with an error
- if(defined $Param->{ssl_certs_path}){
- my $ssl_path = $Param->{ssl_certs_path};
- if (-d $ssl_path) {
- # directory called cgi-bin exists
- if(-e $ssl_path."/client-key.pem"
- && -e $ssl_path."/client-cert.pem"
- && -e $ssl_path."/ca.pem"){
- print Utils->print_log(4," SSL Directory exists and all the files are there $ssl_path")
- }
- else{
- print Utils->print_log(1,"SSL Path (ssl_certs_path) declared and accessible [$ssl_path]. But certification files must have specific names:\n \t\t client-key.pem \n \t\t client-cert.pem \n \t\t ca.pem \n");
- exit 1
- }
- }
- else{
- # ssl path declared but not exists, exit with error
- print Utils->print_log(1,"SSL Path (ssl_certs_path) declared but not existing \n \t\t $ssl_path \n \t\t Please create directory and assign the right to access it to the ProxySQL user \n");
- exit 1;
- }
- }
-
- if($Param->{development} < 2){
- if(!-e $base_path){
- `echo "$$" > $base_path`
- }
- else{
- my $existing_pid=`cat $base_path`;
- my $exists = kill 0, $existing_pid;
- if($exists > 0){
- print STDOUT "Another process is running using the same HostGroup and settings,\n Or orphan pid file. check in $base_path \n";
- print Utils->print_log(1,"Another process is running using the same HostGroup and settings,\n Or orphan pid file. check in $base_path \n");
- exit 1;
- }
- else{
- `echo "$$" > $base_path`;
- }
- }
- }
-
- # for test only purpose comment for prod
- my $xx =1;
- my $y =0;
- $xx=2000000000 if($Param->{development} > 0);
-
- while($y < $xx){
- ++$y ;
-
- my $start = gettimeofday();
- if($Param->{debug} >= 1){
- print Utils->print_log(3,"START EXECUTION\n");
- }
-
-
-
- my $proxy_sql_node = get_proxy($dsn, $user, $pass ,$Param->{debug}) ;
- $proxy_sql_node->retry_up($Param->{retry_up});
- $proxy_sql_node->retry_down($Param->{retry_down});
- $proxy_sql_node->hostgroups($Param->{hostgroups}) ;
- $proxy_sql_node->require_failover($Param->{active_failover});
- $proxy_sql_node->check_timeout($Param->{check_timeout});
-
- $proxy_sql_node->connect();
-
- # create basic galera cluster object and fill info
- $proxy_sql_node->set_galera_cluster();
- my $galera_cluster = $proxy_sql_node->get_galera_cluster();
-
- if( defined $galera_cluster){
- $galera_cluster->main_segment($Param->{main_segment});
- $galera_cluster->cluster_identifier($hg);
- $galera_cluster->get_nodes();
- }
-
- # Retrive the nodes state
- if(defined $galera_cluster->nodes){
- $galera_cluster->process_nodes();
-
- }
-
- #Analyze nodes state from ProxySQL prospective;
- if(defined $galera_cluster->nodes){
- my %action_node = $proxy_sql_node->evaluate_nodes($galera_cluster);
-
- }
-
- if(defined $proxy_sql_node->action_nodes){
- $proxy_sql_node->push_changes;
- }
-
- my $end = gettimeofday();
- print Utils->print_log(3,"END EXECUTION Total Time(ms):".($end - $start) * 1000 ."\n") if $Param->{print_execution} >0;
- if($Param->{debug} >= 1){
- print Utils->print_log(3,"\n");
- }
- FH->flush();
-
- $proxy_sql_node->disconnect();
-
- #debug braket
- sleep $Param->{development_time} if($Param->{development} > 0);
- }
-
- if(defined $Param->{log}){
- close FH; # in the end
- }
-
- `rm -f $base_path`;
-
-
- exit(0);
-
-
- }
- # ############################################################################
- # Run the program.
- # ############################################################################
- exit main(@ARGV);
- {
- package Galeracluster;
- use threads;
- use threads::shared;
- use strict;
- use warnings;
- use Time::HiRes qw(gettimeofday usleep);
-
- sub new {
- my $class = shift;
- my $SQL_get_mysql_servers=" SELECT a.* FROM runtime_mysql_servers a join stats_mysql_connection_pool b on a.hostname=b.srv_host and a.port=b.srv_port and a.hostgroup_id=b.hostgroup WHERE b.status not in ('OFFLINE_HARD','SHUNNED') ";
-
- # Variable section for looping values
- #Generalize object for now I have conceptualize as:
- # Cluster (generic container)
- # Cluster->{name} This is the cluster name
- # Cluster->{nodes} the nodes in the cluster Map by node name
- # Cluster->{status} cluster status [Primary|not Primary]
- # Cluster->{size} cluster status [Primary|not Primary]
- # Cluster->{singlenode}=0; 0 if false 1 if true meaning only one ACTIVE node in the cluster
- # Cluster->{haswriter}=0; 0 if false 1 if true at least a node is fully active as writer
- # Cluster->{singlewriter}=1; 0 if false 1 if true this cluster can have ONLY one writer a time [true default]
-
- my $self = {
- _name => undef,
- _hosts => {},
- _status => undef,
- _size => {},
- _singlenode => 0,
- _haswriter => 0,
- _singlewriter => 1,
- _main_segment => 0,
- _SQL_get_mysql_servers => $SQL_get_mysql_servers,
- _hostgroups => undef,
- _dbh_proxy => undef,
- _debug => 0,
- _monitor_user => undef,
- _monitor_password => undef,
- _nodes => {},
- _nodes_maint => {},
- _check_timeout => 100, #timeout in ms
- _cluster_identifier => undef,
- _hg_writer_id => 0,
- _hg_reader_id => 0,
- _ssl_certificates_path => undef,
- _writer_is_reader => 0,
- _reader_nodes => [] ,
- _writer_nodes => [] ,
- _has_failover_node =>0,
- _writers =>0,
- #_hg => undef,
- };
- bless $self, $class;
- return $self;
-
- }
- sub ssl_certificates_path{
- my ( $self, $in ) = @_;
- $self->{_ssl_certificates_path} = $in if defined($in);
- return $self->{_ssl_certificates_path};
- }
-
- sub has_failover_node{
- my ( $self, $in ) = @_;
- $self->{_has_failover_node} = $in if defined($in);
- return $self->{_has_failover_node};
- }
-
- sub writer_nodes{
- my ( $self, $in ) = @_;
- $self->{_writer_nodes} = $in if defined($in);
- return $self->{_writer_nodes};
- }
- sub reader_nodes{
- my ( $self, $in ) = @_;
- $self->{_reader_nodes} = $in if defined($in);
- return $self->{_reader_nodes};
- }
- sub cluster_identifier{
- my ( $self, $in ) = @_;
- $self->{_cluster_identifier} = $in if defined($in);
- return $self->{_cluster_identifier};
- }
- sub main_segment{
- my ( $self, $main_segment ) = @_;
- $self->{_main_segment} = $main_segment if defined($main_segment);
- return $self->{_main_segment};
- }
- sub check_timeout{
- my ( $self, $check_timeout ) = @_;
- $self->{_check_timeout} = $check_timeout if defined($check_timeout);
- return $self->{_check_timeout};
- }
- sub debug{
- my ( $self, $debug ) = @_;
- $self->{_debug} = $debug if defined($debug);
- return $self->{_debug};
- }
-
- sub dbh_proxy{
- my ( $self, $dbh_proxy ) = @_;
- $self->{_dbh_proxy} = $dbh_proxy if defined($dbh_proxy);
- return $self->{_dbh_proxy};
- }
-
- sub name {
- my ( $self, $name ) = @_;
- $self->{_name} = $name if defined($name);
- return $self->{_name};
- }
- sub nodes {
- my ( $self, $nodes ) = @_;
- $self->{_nodes} = $nodes if defined($nodes);
- return $self->{_nodes};
- }
- sub nodes_maint {
- my ( $self, $nodes ) = @_;
- $self->{_nodes_maint} = $nodes if defined($nodes);
- return $self->{_nodes_maint};
- }
- sub status {
- my ( $self, $status ) = @_;
- $self->{_status} = $status if defined($status);
- return $self->{_status};
- }
- sub size {
- my ( $self, $size ) = @_;
- $self->{_size} = $size if defined($size);
- return $size->{_size};
- }
- sub singlenode {
- my ( $self, $singlenode ) = @_;
- $self->{_singlenode} = $singlenode if defined($singlenode);
- return $self->{_singlenode};
- }
- sub haswriter {
- my ( $self, $haswriter ) = @_;
- $self->{_haswriter} = $haswriter if defined($haswriter);
- return $self->{_haswriter};
- }
-
- sub singlewriter {
- my ( $self, $singlewriter ) = @_;
- $self->{_singlewriter} = $singlewriter if defined($singlewriter);
- return $self->{_singlewriter};
- }
- sub writer_is_reader {
- my ( $self, $writer_is_reader ) = @_;
- $self->{_writer_is_reader} = $writer_is_reader if defined($writer_is_reader);
- return $self->{_writer_is_reader};
- }
- sub writers {
- my ( $self, $in ) = @_;
- $self->{_writers} = $in if defined($in);
- return $self->{_writers};
- }
- sub hostgroups {
- my ( $self, $hostgroups ) = @_;
- $self->{_hostgroups} = $hostgroups if defined($hostgroups);
- return $self->{_hostgroups};
- }
- sub hg_writer_id {
- my ( $self, $hostgroups ) = @_;
- $self->{_hg_writer_id} = $hostgroups if defined($hostgroups);
- return $self->{_hg_writer_id};
- }
- sub hg_reader_id {
- my ( $self, $hostgroups ) = @_;
- $self->{_hg_reader_id} = $hostgroups if defined($hostgroups);
- return $self->{_hg_reader_id};
- }
-
-
- sub monitor_user{
- my ( $self, $monitor_user ) = @_;
- $self->{_monitor_user} = $monitor_user if defined($monitor_user);
- return $self->{_monitor_user};
- }
- sub monitor_password {
- my ( $self, $monitor_password ) = @_;
- $self->{_monitor_password} = $monitor_password if defined($monitor_password);
- return $self->{_monitor_password};
- }
- # this function is used to identify the nodes in the cluster
- # using the HG as reference
- sub get_nodes{
- my ( $self) = @_;
-
- my $dbh = $self->{_dbh_proxy};
- my $cmd =$self->{_SQL_get_mysql_servers}." AND hostgroup_id IN (".join(",",sort keys(%{$self->hostgroups})).") order by hostgroup_id, hostname";
- my $sth = $dbh->prepare($cmd);
- $sth->execute();
- my $i = 1;
- my $locHg = $self->{_hostgroups};
- my $ssl_certificates = "";
- #if a ssl certificate path is defined, will create the path for each certificate and add to the dns string
- if(defined $self->{_ssl_certificates_path}){
- $ssl_certificates = ";mysql_ssl_client_key=".$self->{_ssl_certificates_path}."/client-key.pem"
- .";mysql_ssl_client_cert=".$self->{_ssl_certificates_path}."/client-cert.pem"
- .";mysql_ssl_ca_file=".$self->{_ssl_certificates_path}."/ca.pem"
- }
-
- while (my $ref = $sth->fetchrow_hashref()) {
- my $ssl_options="" ;
- my $node = GaleraNode->new();
- $node->debug($self->debug);
- $node->use_ssl($ref->{use_ssl});
- $node->hostgroups($ref->{hostgroup_id});
- if($node->{_hostgroups} > 8000
- && exists $locHg->{$node->{_hostgroups}}){
- $self->{_has_failover_node} = 1;
-
- }
- $node->ip($ref->{hostname});
- $node->port($ref->{port});
- if($node->use_ssl gt 0 ){
- $ssl_options = ";mysql_ssl=1";
- if($self->debug){print Utils->print_log(4," Galera cluster node " . $node->ip.":". $node->port.":HG=".$node->hostgroups." Using SSL ($ssl_options)\n" ) }
- if(defined $self->{_ssl_certificates_path}){
- $ssl_options = $ssl_options . $ssl_certificates;
- if($self->debug){print Utils->print_log(4," Certificates also in use ($self->{_ssl_certificates_path})\n")}
- }
- }
-
- $node->dns("DBI:mysql:host=".$node->ip.";port=".$node->port.";mysql_connect_timeout=$mysql_connect_timeout".$ssl_options);
-
- $node->weight($ref->{weight});
- $node->connections($ref->{max_connections});
- $node->user($self->{_monitor_user});
- $node->password($self->{_monitor_password});
- $node->proxy_status($ref->{status});
- $node->comment($ref->{comment});
- $node->set_retry_up_down($self->{_cluster_identifier});
-
- $node->gtid_port($ref->{gtid_port});
- $node->compression($ref->{compression});
- $node->max_latency($ref->{max_latency_ms});
- $node->max_replication_lag($ref->{max_replication_lag});
-
-
- $self->{_nodes}->{$i++}=$node;
- $node->debug($self->debug);
-
- if($self->debug){print Utils->print_log(3," Galera cluster node " . $node->ip.":". $node->port.":HG=".$node->hostgroups."\n" ) }
- }
-
- if($self->debug){print Utils->print_log(3," Galera cluster nodes loaded \n") ; }
- }
- #Processing the nodes in the cluster and identify which node is active and which is to remove
-
- sub process_nodes{
- my ( $self ) = @_;
- my $nodes = $self->{_nodes} ;
- my $start = gettimeofday();
- my $run_milliseconds=0;
- my $init =0;
- my $irun = 1;
- my %Threads;
- my $new_nodes ={} ;
- my $processed_nodes ={} ;
-
- #using multiple threads to connect if a node is present in more than one HG it will have 2 threads
- while($irun){
- $irun = 0;
- foreach my $key (sort keys %{$self->{_nodes}}){
- if(!exists $Threads{$key}){
- if($self->debug){print Utils->print_log(3, " Creating new thread to manage server check:".
- $self->{_nodes}->{$key}->ip.":".
- $self->{_nodes}->{$key}->port.":HG".$self->{_nodes}->{$key}->hostgroups."\n" ) }
- $new_nodes->{$key} = $self->{_nodes}->{$key};
- $new_nodes->{$key}->{_process_status} = -1;
- $new_nodes->{$key}->{_ssl_certificates_path} = $self->ssl_certificates_path;
- # debug senza threads comment next line
- $Threads{$key}=threads->create(sub {return get_node_info($self,$key)});
-
- #DEBUG Without threads uncomment from here
- #next unless $new_nodes->{$key} = get_node_info($self,$key);
- #evaluate_joined_node($self, $key, $new_nodes, $processed_nodes) ;
-
- # to here
-
- }
- }
- ##DEBUG SENZA THREADS commenta da qui
- foreach my $thr (sort keys %Threads) {
- if($new_nodes->{$thr}->{_process_status} eq -100){
- next;
- }
-
- if ($Threads{$thr}->is_running()) {
- my $tid = $Threads{$thr}->tid;
- #print " - Thread $tid running\n";
-
- if($run_milliseconds > $self->{_check_timeout} ){
- if($self->debug >=0){
- my $timeout = ($run_milliseconds - $self->{_check_timeout});
- print print Utils->print_log(2,"Check timeout Node ip : $new_nodes->{$thr}->{_ip} , THID " . $tid." (taken: $run_milliseconds max_allowed: $self->{_check_timeout} over for ms: $timeout \n")
- }
- $irun = 0 ;
- }
- else{
- $irun = 1;
- }
- }
- elsif ( $Threads{$thr}->is_joinable()) {
-
- my $tid = $Threads{$thr}->tid;
- ( $new_nodes->{$thr} ) = $Threads{$thr}->join;
- #$processed_nodes =
- evaluate_joined_node($self, $thr, $new_nodes, $processed_nodes) ;
-
- if($self->debug){print Utils->print_log(3," Thread joined : " . $tid."\n" ) }
- #print " - Results for thread $tid:\n";
- #print " - Thread $tid has been joined\n";
- }
- #print ".";
- }
- ## a qui
- if($self->debug){$run_milliseconds = (gettimeofday() -$start ) *1000};
- #sleep for a time equal to the half of the timeout to save cpu cicle
- #usleep(($self->{_check_timeout} * 1000)/2);
- }
- $self->{_nodes} = $new_nodes;
- if($self->debug){$run_milliseconds = (gettimeofday() -$start ) *1000};
-
- if($debug>=3){
- foreach my $key (sort keys %{$new_nodes}){
- if($new_nodes->{$key}->{_process_status} == 1){
- print Utils->print_log(4,$new_nodes->{$key}->{_ip}.":".$new_nodes->{$key}->{_hostgroups}." Processed \n");
- }
- else{
- print Utils->print_log(4,$new_nodes->{$key}->{_ip}.":".$new_nodes->{$key}->{_hostgroups}." NOT Processed\n");
- }
- }
- }
- if($self->debug){print Utils->print_log(3," Multi Thread execution done in : " . $run_milliseconds. "(ms) \n" )}
-
- }
-
- sub evaluate_joined_node($$$$){
- my $self = shift;
- my $thr = shift;
- my $new_nodes = shift;
- my $processed_nodes = shift;
-
- #count the number of nodes by segment
- if($new_nodes->{$thr}->{_proxy_status} ne "OFFLINE_SOFT"
- && $new_nodes->{$thr}->{_proxy_status} ne "SHUNNED"
- && ($new_nodes->{$thr}->{_process_status} < 0 ||
- !exists $processed_nodes->{$new_nodes->{$thr}->{_ip}})
- && defined $new_nodes->{$thr}->{_wsrep_segment}
- ){
- $self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}} = (($self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}}|| 0) +1);
- $processed_nodes->{$new_nodes->{$thr}->{_ip}}=$self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}};
- }
-
- #assign size to HG
- if($new_nodes->{$thr}->{_proxy_status} ne "OFFLINE_SOFT"
- && defined $new_nodes->{$thr}->{_wsrep_segment}
- ){
- $self->{_hostgroups}->{$new_nodes->{$thr}->{_hostgroups}}->{_size} = ($self->{_hostgroups}->{$new_nodes->{$thr}->{_hostgroups}}->{_size}) + 1;
- }
-
- #checks for ONLINE writer(s)
- if(defined $new_nodes->{$thr}->{_read_only}
- && $new_nodes->{$thr}->{_read_only} eq "OFF"
- && ($new_nodes->{$thr}->{_proxy_status} eq "ONLINE" || $new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT")
- && ($new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id || $new_nodes->{$thr}->{_hostgroups} == ($self->hg_writer_id +9000))
- ){
- if($new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id
- && $new_nodes->{$thr}->{_proxy_status} eq "ONLINE"
- ){
- $self->{_haswriter} = 1 ;
- $self->{_writers} = $self->{_writers} +1;
- }
- push (@{$self->{_writer_nodes}}, "$new_nodes->{$thr}->{_ip}:$new_nodes->{$thr}->{_port}");
- }
- elsif(($new_nodes->{$thr}->{_proxy_status} eq "ONLINE" || $new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT")
- && ($new_nodes->{$thr}->{_hostgroups} == $self->hg_reader_id || $new_nodes->{$thr}->{_hostgroups} == ($self->hg_reader_id +9000))
- ){
- push (@{$self->{_reader_nodes}}, "$new_nodes->{$thr}->{_ip}:$new_nodes->{$thr}->{_port}");
- }
- else{
- if($self->debug
- && $new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id){
- print Utils->print_log(3," Not a writer :" .$new_nodes->{$thr}->{_ip} . " HG: $new_nodes->{$thr}->{_hostgroups} \n" )
- }
- }
- # check if under maintenance
- if($new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT"
- && $new_nodes->{$thr}->{_pxc_maint_mode} eq "MAINTENANCE"){
- $self->{_nodes_maint}->{$thr} = $new_nodes->{$thr};
- }
- #return $processed_nodes;
-
- }
-
- sub get_node_info($$){
- my $self = shift;
- my $key = shift;
- my $nodes =shift;
- my ( $node ) = $self->{_nodes}->{$key};
- if(!defined $node->get_node_info()){
- $node->{_process_status}=-100;
- }
-
- return $node;
-
- }
-
- }
- {
- package GaleraNode;
- #Node Proxy States
- sub new {
- my $class = shift;
- my $SQL_get_variables="SHOW GLOBAL VARIABLES LIKE 'wsrep%";
- my $SQL_get_status="SHOW GLOBAL STATUS LIKE 'wsrep%";
- my $SQL_get_read_only="SHOW GLOBAL VARIABLES LIKE 'read_only'";
- # Variable section for looping values
- #Generalize object for now I have conceptualize as:
- # Node (generic container)
- # Node->{name} This is the cluster name
- # Node->{IP}
- # Node->{hostgroups}
- # Node->{clustername} This is the cluster name
- # Node->{read_only} Read only node
- # Node->{wsrep_status} node status (OPEN 0,Primary 1,Joiner 2,Joined 3,Synced 4,Donor 5)
- # Node->{wsrep_rejectqueries} (NON, ALL,ALL_KILL)
- # Node->{wsrep_donorrejectqueries} If true the node when donor
- # Node->{wsrep_connected}=0; if false 1 if true meaning only one ACTIVE node in the cluster
- # Node->{wsrep_desinccount}=0; 0 if false 1 if true at least a node is fully active as writer
- # Node->{wsrep_ready} ON -OFF
-
- my $self = {
- _name => undef,
- _ip => undef,
- _port => 3306,
- _hostgroups => undef,
- _clustername => undef,
- _read_only => undef,
- _wsrep_status => -1,
- _wsrep_rejectqueries => undef,
- _wsrep_donorrejectqueries => undef,
- _wsrep_connected => undef,
- _wsrep_desinccount => undef,
- _wsrep_ready => undef,
- _wsrep_provider => [],
- _wsrep_segment => 1000,
- _wsrep_pc_weight => 1,
- _SQL_get_variables => $SQL_get_variables,
- _SQL_get_status=> $SQL_get_status,
- _SQL_get_read_only=> $SQL_get_read_only,
- _dns => undef,
- _user => undef,
- _password => undef,
- _debug => 0,
- _port => undef,
- _proxy_status => undef,
- _weight => 1,
- _connections => 2000,
- _cluster_status => undef,
- _cluster_size => 0,
- _process_status => -1,
- _MOVE_UP_OFFLINE => 1000, #move a node from OFFLINE_SOFT
- _MOVE_UP_HG_CHANGE => 1010, #move a node from HG 9000 (plus hg id) to reader HG
- _MOVE_DOWN_HG_CHANGE => 3001, #move a node from original HG to maintenance HG (HG 9000 (plus hg id) ) kill all existing connections
- _MOVE_DOWN_OFFLINE => 3010 , # move node to OFFLINE_soft keep existing connections, no new connections.
- _MOVE_TO_MAINTENANCE => 3020 , # move node to OFFLINE_soft keep existing connections, no new connections because maintenance.
- _MOVE_OUT_MAINTENANCE => 3030 , # move node to OFFLINE_soft keep existing connections, no new connections because maintenance.
- _INSERT_READ => 4010, # Insert a node in the reader host group
- _INSERT_WRITE => 4020, # Insert a node in the writer host group
- _DELETE_NODE => 5000, # this remove the node from the hostgroup
- _SAVE_RETRY => 9999, # this reset the retry counter in the comment
- #_MOVE_SWAP_READER_TO_WRITER => 5001, #Future use
- #_MOVE_SWAP_WRITER_TO_READER => 5010, #Future use
- _retry_down_saved => 0, # number of retry on a node before declaring it as failed.
- _retry_up_saved => 0, # number of retry on a node before declaring it OK.
- _comment => undef,
- _gtid_port => 0,
- _compression => 0,
- _use_ssl => 0,
- _ssl_certificates_path => undef,
- _max_latency => 0,
- _max_replication_lag => 0,
- _wsrep_gcomm_uuid => undef,
- _wsrep_local_index => 0,
- _pxc_maint_mode => undef,
- };
- bless $self, $class;
- return $self;
-
- }
- sub ssl_certificates_path{
- my ( $self, $in ) = @_;
- $self->{_ssl_certificates_path} = $in if defined($in);
- return $self->{_ssl_certificates_path};
- }
-
- sub max_replication_lag{
- my ( $self, $in ) = @_;
- $self->{_max_replication_lag} = $in if defined($in);
- return $self->{_max_replication_lag};
- }
- sub max_latency{
- my ( $self, $in ) = @_;
- $self->{_max_latency} = $in if defined($in);
- return $self->{_max_latency};
- }
-
- sub use_ssl{
- my ( $self, $in ) = @_;
- $self->{_use_ssl} = $in if defined($in);
- return $self->{_use_ssl};
- }
- sub compression{
- my ( $self, $in ) = @_;
- $self->{_compression} = $in if defined($in);
- return $self->{_compression};
- }
- sub gtid_port{
- my ( $self, $in ) = @_;
- $self->{_gtid_port} = $in if defined($in);
- return $self->{_gtid_port};
- }
- sub pxc_maint_mode{
- my ( $self, $in ) = @_;
- $self->{_pxc_maint_mode} = $in if defined($in);
- return $self->{_pxc_maint_mode};
- }
- sub wsrep_local_index{
- my ( $self, $in ) = @_;
- $self->{_wsrep_local_index} = $in if defined($in);
- return $self->{_wsrep_local_index};
- }
- sub comment{
- my ( $self, $in ) = @_;
- $self->{_comment} = $in if defined($in);
- return $self->{_comment};
- }
- sub wsrep_gcomm_uuid{
- my ( $self, $in ) = @_;
- $self->{_wsrep_gcomm_uuid} = $in if defined($in);
- return $self->{_wsrep_gcomm_uuid};
- }
-
- sub retry_down_saved{
- my ( $self, $in ) = @_;
- $self->{_retry_down_saved} = $in if defined($in);
- return $self->{_retry_down_saved};
- }
- sub retry_up_saved{
- my ( $self, $in ) = @_;
- $self->{_retry_up_saved} = $in if defined($in);
- return $self->{_retry_up_saved};
- }
-
- sub process_status {
- my ( $self, $process_status ) = @_;
- $self->{_process_status} = $process_status if defined($process_status);
- return $self->{_process_status};
- }
- sub debug{
- my ( $self, $debug ) = @_;
- $self->{_debug} = $debug if defined($debug);
- return $self->{_debug};
- }
- sub SAVE_RETRY {
- my ( $self) = @_;
- return $self->{_SAVE_RETRY};
- }
- sub MOVE_UP_OFFLINE {
- my ( $self) = @_;
- return $self->{_MOVE_UP_OFFLINE};
- }
- sub MOVE_UP_HG_CHANGE {
- my ( $self) = @_;
- return $self->{_MOVE_UP_HG_CHANGE};
- }
-
- sub MOVE_DOWN_OFFLINE {
- my ( $self) = @_;
- return $self->{_MOVE_DOWN_OFFLINE};
- }
-
-
- sub MOVE_TO_MAINTENANCE {
- my ( $self) = @_;
- return $self->{_MOVE_TO_MAINTENANCE};
- }
-
- sub MOVE_OUT_MAINTENANCE {
- my ( $self) = @_;
- return $self->{_MOVE_OUT_MAINTENANCE};
- }
- sub MOVE_DOWN_HG_CHANGE {
- my ( $self) = @_;
- return $self->{_MOVE_DOWN_HG_CHANGE};
- }
- sub DELETE_NODE {
- my ( $self) = @_;
- return $self->{_DELETE_NODE};
- }
- sub INSERT_READ {
- my ( $self) = @_;
- return $self->{_INSERT_READ};
- }
- sub INSERT_WRITE {
- my ( $self) = @_;
- return $self->{_INSERT_WRITE};
- }
- sub cluster_status {
- my ( $self, $status ) = @_;
- $self->{_cluster_status} = $status if defined($status);
- return $self->{_cluster_status};
- }
- sub cluster_size {
- my ( $self, $size ) = @_;
- $self->{_cluster_size} = $size if defined($size);
- return $size->{_cluster_size};
- }
-
- sub weight {
- my ( $self, $weight ) = @_;
- $self->{_weight} = $weight if defined($weight);
- return $self->{_weight};
- }
- sub connections {
- my ( $self, $connections ) = @_;
- $self->{_connections} = $connections if defined($connections);
- return $self->{_connections};
- }
-
- sub proxy_status {
- my ( $self, $status ) = @_;
- $self->{_proxy_status} = $status if defined($status);
- return $self->{_proxy_status};
- }
-
- sub dns {
- my ( $self, $dns ) = @_;
- $self->{_dns} = $dns if defined($dns);
- return $self->{_dns};
- }
-
- sub user{
- my ( $self, $user ) = @_;
- $self->{_user} = $user if defined($user);
- return $self->{_user};
- }
- sub password {
- my ( $self, $password ) = @_;
- $self->{_password} = $password if defined($password);
- return $self->{_password};
- }
- sub name {
- my ( $self, $name ) = @_;
- $self->{_name} = $name if defined($name);
- return $self->{_name};
- }
- sub ip {
- my ( $self, $ip ) = @_;
- $self->{_ip} = $ip if defined($ip);
- return $self->{_ip};
- }
- sub port {
- my ( $self, $port ) = @_;
- $self->{_port} = $port if defined($port);
- return $self->{_port};
- }
- sub hostgroups {
- my ( $self, $hostgroups ) = @_;
- $self->{_hostgroups} = $hostgroups if defined($hostgroups);
- return $self->{_hostgroups};
- }
-
- sub clustername {
- my ( $self, $clustername ) = @_;
- $self->{_clustername} = $clustername if defined($clustername);
- return $self->{_clustername};
- }
- sub read_only {
- my ( $self, $read_only ) = @_;
- $self->{_read_only} = $read_only if defined($read_only);
- return $self->{_read_only};
- }
- sub wsrep_status {
- my ( $self, $wsrep_status ) = @_;
- $self->{_wsrep_status} = $wsrep_status if defined($wsrep_status);
- return $self->{_wsrep_status};
- }
- sub wsrep_rejectqueries {
- my ( $self, $wsrep_rejectqueries ) = @_;
- $self->{_wsrep_rejectqueries} = $wsrep_rejectqueries if defined($wsrep_rejectqueries);
- return $self->{_wsrep_rejectqueries};
- }
- sub wsrep_donorrejectqueries {
- my ( $self, $wsrep_donorrejectqueries ) = @_;
- $self->{_wsrep_donorrejectqueries} = $wsrep_donorrejectqueries if defined($wsrep_donorrejectqueries);
- return $self->{_wsrep_donorrejectqueries};
- }
- sub wsrep_connected {
- my ( $self, $wsrep_connected ) = @_;
- $self->{_wsrep_connected} = $wsrep_connected if defined($wsrep_connected);
- return $self->{_wsrep_connected};
- }
- sub wsrep_desinccount {
- my ( $self, $wsrep_desinccount ) = @_;
- $self->{_wsrep_desinccount} = $wsrep_desinccount if defined($wsrep_desinccount);
- return $self->{_wsrep_desinccount};
- }
- sub wsrep_ready {
- my ( $self, $wsrep_ready ) = @_;
- $self->{_wsrep_ready} = $wsrep_ready if defined($wsrep_ready);
- return $self->{_wsrep_ready};
- }
- sub wsrep_segment {
- my ( $self, $wsrep_segment ) = @_;
- $self->{_wsrep_segment} = $wsrep_segment if defined($wsrep_segment);
- return $self->{_wsrep_segment};
- }
- sub wsrep_pc_weight {
- my ( $self, $wsrep_pc_weight ) = @_;
- $self->{_wsrep_pc_weight} = $wsrep_pc_weight if defined($wsrep_pc_weight);
- return $self->{_wsrep_pc_weight};
- }
- sub wsrep_provider {
- my ( $self, $wsrep_provider ) = @_;
- my ( @array)= @{$wsrep_provider} ;
- my %provider_map ;
- foreach my $item (@array){
- my @items = split('\=', $item);
- $provider_map{Utils::trim($items[0])}=$items[1];
- }
- ($self->{_wsrep_provider}) = {%provider_map} ;
- $self->wsrep_segment($provider_map{"gmcast.segment"});
- $self->wsrep_pc_weight($provider_map{"pc.weight"});
- return $self->{_wsrep_provider};
- }
- sub get_node_info($$){
- my ( $self ) = @_;
-
- if($self->debug >=1){
- print Utils->print_log(4," Node check START "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}
- ."\n" );
- }
- if($self->debug >=1){
- print Utils->print_log(4," Getting connection START "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}." \n" );
- }
- my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
- if(!defined $dbh){
- print Utils->print_log(1," Node is not responding setting it as SHUNNED (internally) (ProxySQL bug - #2658)"
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}." \n" );
- $self->{_proxy_status} = "SHUNNED";
- return $self ;
-
- }
- if($self->debug >=1){
- print Utils->print_log(4," Getting connection END "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}." \n" );
- }
- if($self->debug >=1){
- print Utils->print_log(4," Getting NODE info START "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}." \n" );
- }
- my $variables = Utils::get_variables($dbh,0);
- my $status = Utils::get_status_by_name($dbh,0,"wsrep_%");
- my $pxc_view = Utils::get_pxc_clusterview($dbh, $status->{wsrep_gcomm_uuid} );
- if($self->debug >=1){
- print Utils->print_log(4," Getting NODE info END "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}." \n" );
- }
-
- $self->{_name} = $variables->{wsrep_node_name};
- $self->{_clustername} = $variables->{wsrep_cluster_name};
- $self->{_read_only} = $variables->{read_only};
- $self->{_wsrep_rejectqueries} = $variables->{wsrep_reject_queries};
- #print "AAAAAAAAAAAAAAAAAAAAA $self->{_ip} $self->{_wsrep_rejectqueries} \n";
- $self->{_wsrep_donorrejectqueries} = $variables->{wsrep_sst_donor_rejects_queries};
- my ( @provider ) = split('\;', $variables->{wsrep_provider_options});
- $self->{_pxc_maint_mode} = $variables->{pxc_maint_mode};
-
- $self->wsrep_provider( [ @provider]) ;
- $self->{_wsrep_status} = $status->{wsrep_local_state};
- $self->{_wsrep_connected} = $status->{wsrep_connected};
- $self->{_wsrep_desinccount} = $status->{wsrep_desync_count};
- $self->{_wsrep_ready} = $status->{wsrep_ready};
- $self->{_cluster_status} = $status->{wsrep_cluster_status};
- $self->{_cluster_size} = $status->{wsrep_cluster_size};
- $self->{_wsrep_gcomm_uuid} = $status->{wsrep_gcomm_uuid};
- $self->{wsrep_segment} = ($self->{_wsrep_provider}->{"gmcast.segment"} );
- $self->{wsrep_segment} =~ s/^\s+|\s+$//g;
- $self->{_wsrep_local_index} = $pxc_view->{local_index};
- if($self->{wsrep_segment} == 0){
- $self->{_wsrep_segment} = $pxc_view->{segment};
- }
-
- $dbh->disconnect if (defined $dbh);
- #sleep 5;
-
- $self->{_process_status} = 1;
- if($self->debug>=1){
- print Utils->print_log(4," Node check END "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}
- ."\n" );}
- return $self;
-
- }
-
- sub set_retry_up_down(){
- my ( $self, $hg ) = @_;
- if($self->debug >=1){print Utils->print_log(4,"Calculate retry from comment Node:".$self->ip." port:".$self->port . " hg:".$self->hostgroups ." Time IN \n");}
-
- my %comments = split /[;=]/, $self->{_comment};
- if(exists $comments{$hg."_retry_up"}){
- $self->{_retry_up_saved} = $comments{$hg."_retry_up"};
- }
- else{
- $self->{_retry_up_saved} = 0;
- }
- if(exists $comments{$hg."_retry_down"}){
- $self->{_retry_down_saved} = $comments{$hg."_retry_down"};
- }
- else{
- $self->{_retry_down_saved} = 0;
- }
- my $removeUp=$hg."_retry_up=".$self->{_retry_up_saved}.";";
- my $removeDown=$hg."_retry_down=".$self->{_retry_down_saved}.";";
- $self->{_comment} =~ s/$removeDown//ig ;
- $self->{_comment} =~ s/$removeUp//ig ;
-
- if($self->debug >=1){print Utils->print_log(4,"Calculate retry from comment Node:".$self->ip." port:".$self->port . " hg:".$self->hostgroups ." Time OUT \n");}
- }
-
- sub get_retry_up(){
- my ( $self,$in) = @_;
- $self->{_retry_up_saved} = $in if defined($in);
- return $self->{_retry_up_saved};
- }
- sub get_retry_down(){
- my ( $self,$in) = @_;
- $self->{_retry_down_saved} = $in if defined($in);
- return $self->{_retry_down_saved};
- }
-
- sub promote_writer(){
- my ( $self,$proxynode,$Galera_cluster,$exclude_delete ) = @_;
- if($self->{_hostgroups} > 8000){
- print Utils->print_log(3,"Special Backup - Group found! I am electing a node to writer following the indications\n This Node Try to become the new"
- ." WRITER for HG $proxynode->{_hg_writer_id} Server details: "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}
- ."\n" );
-
- }
- print Utils->print_log(3,"This Node Try to become a WRITER promoting to HG $proxynode->{_hg_writer_id} "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG ".$self->{_hostgroups}
- ."\n" );
-
- #my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
- #if(!defined $dbh){
- # return undef;
- #}
- #(9000 + $proxynode->{_hg_writer_id})
-
- my $proxy_sql_command= "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_connections,use_ssl,compression,max_latency_ms) VALUES ('$self->{_ip}',$proxynode->{_hg_writer_id},$self->{_port},$self->{_weight},$self->{_connections},$self->{_use_ssl},$self->{_compression},$self->{_max_latency});";
- if($Galera_cluster->{_singlewriter} > 0){
- my $delete = "DELETE from mysql_servers where hostgroup_id in ($proxynode->{_hg_writer_id},".(9000 + $proxynode->{_hg_writer_id}).") AND STATUS = 'ONLINE'".$exclude_delete;
- print Utils->print_log(2," DELETE from writer group as: "
- ." SQL:" .$delete
- ."\n" );
-
- $proxynode->{_dbh_proxy}->do($delete) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- }
- #if the writer is NOT a reader by default remove it from reader groups also the
- if($Galera_cluster->{_writer_is_reader} < 1 ){
- my $delete = "DELETE from mysql_servers where hostgroup_id in ($proxynode->{_hg_reader_id},".(9000 + $proxynode->{_hg_reader_id}).") and hostname = '$self->{_ip}' and port=$self->{_port} ";
- $proxynode->{_dbh_proxy}->do($delete) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
-
- }
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("SAVE MYSQL SERVERS TO DISK") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:"
- .$self->{_ip}.":"
- .$self->{_port}
- .$self->{_weight}
- .$self->{_connections}
- .$proxynode->{hg_writer_id}
- ." SQL:" .$proxy_sql_command
- ."\n" );
-
-
- return 1;
- }
-
- sub move_to_writer_hg(){
- my ( $self ) = @_;
-
- print Utils->print_log(3,"This Node Try to become a WRITER set READ_ONLY to 0 "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}
- ."\n" );
-
- my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
- if(!defined $dbh){
- return undef;
- }
- if($dbh->do("SET GLOBAL READ_ONLY=0")){
- print Utils->print_log(3,"This Node NOW HAS READ_ONLY = 0 "
- .$self->{_ip}
- .":".$self->{_port}
- .":HG".$self->{_hostgroups}
- ."\n" );
- }
- else{
- die "Couldn't execute statement: " . $dbh->errstr;
- }
- $dbh->disconnect if (defined $dbh);
- #or die "Couldn't execute statement: " . $dbh->errstr;
- return 1;
- }
- }
- {
- package ProxySqlNode;
- sub new {
- my $class = shift;
- my $SQL_get_monitor = "select variable_name name,variable_value value from global_variables where variable_name in( 'mysql-monitor_username','mysql-monitor_password','mysql-monitor_read_only_timeout' ) order by 1";
- my $SQL_get_hostgroups = "select distinct hostgroup_id hg_isd from runtime_mysql_servers order by 1;";
- my $SQL_get_rep_hg = "select writer_hostgroup,reader_hostgroup from mysql_replication_hostgroups order by 1;";
- my $SQL_get_pxc_cluster_view = "select * from performance_schema.pxc_cluster_view order by SEGMENT, LOCAL_INDEX;";
- # Variable section for looping values
- #Generalize object for now I have conceptualize as:
- # Proxy (generic container)
- # Proxy->{DNS} conenction reference
- # Proxy->{PID} processes pid (angel and real)
- # Proxy->{hostgroups}
- # Proxy->{user} This is the user name
- # Proxy->{password}
- # Proxy->{port} node status (OPEN 0,Primary 1,Joiner 2,Joined 3,Synced 4,Donor 5)
-
- my $self = {
- _dns => undef,
- _pid => undef,
- _hostgroups => undef,
- _hg_writer_id => 0,
- _hg_reader_id => 0,
- _user => undef,
- _password => undef,
- _port => undef,
- _monitor_user => undef,
- _monitor_password => undef,
- _SQL_get_monitor => $SQL_get_monitor,
- _SQL_get_hg=> $SQL_get_hostgroups,
- _SQL_get_replication_hg=> $SQL_get_rep_hg,
- _dbh_proxy => undef,
- _check_timeout => 800, #timeout in ms
- _action_nodes => {},
- _retry_down => 0, # number of retry on a node before declaring it as failed.
- _retry_up => 0, # number of retry on a node before declaring it OK.
- _status_changed => 0, #if 1 something had happen and a node had be modify
- _require_failover => 0, # Valid values are:
- # 0 [default] do not make failover
- # 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
- # 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
- # 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
-
- };
- bless $self, $class;
- return $self;
-
- }
- sub require_failover{
- my ( $self, $in ) = @_;
- $self->{_require_failover} = $in if defined($in);
- return $self->{_require_failover};
- }
- sub hg_reader_id{
- my ( $self, $in ) = @_;
- $self->{_hg_reader_id} = $in if defined($in);
- return $self->{_hg_reader_id};
- }
-
- sub status_changed{
- my ( $self, $in ) = @_;
- $self->{_status_changed} = $in if defined($in);
- return $self->{_status_changed};
- }
- sub retry_down{
- my ( $self, $in ) = @_;
- $self->{_retry_down} = $in if defined($in);
- return $self->{_retry_down};
- }
- sub retry_up{
- my ( $self, $in ) = @_;
- $self->{_retry_up} = $in if defined($in);
- return $self->{_retry_up};
- }
-
- sub debug{
- my ( $self, $debug ) = @_;
- $self->{_debug} = $debug if defined($debug);
- return $self->{_debug};
- }
- sub action_nodes {
- my ( $self, $action_nodes ) = @_;
- $self->{_action_nodes} = $action_nodes if defined($action_nodes);
- return $self->{_action_nodes};
- }
-
- sub dns {
- my ( $self, $dns ) = @_;
- $self->{_dns} = $dns if defined($dns);
- return $self->{_dns};
- }
- sub dbh_proxy{
- my ( $self, $dbh_proxy ) = @_;
- $self->{_dbh_proxy} = $dbh_proxy if defined($dbh_proxy);
- return $self->{_dbh_proxy};
- }
- sub pid {
- my ( $self, $pid ) = @_;
- $self->{_pid} = $pid if defined($pid);
- return $self->{_pid};
- }
- sub hg_writer_id {
- my ( $self, $pid ) = @_;
- $self->{_hg_writer_id} = $pid if defined($pid);
- return $self->{_hg_writer_id};
- }
-
- sub hostgroups {
- my ( $self, $hostgroups ) = @_;
- if (defined $hostgroups){
- my @HGIds=split('\,', $Param->{hostgroups});
-
- foreach my $hg (@HGIds){
- my $proxy_hg = ProxySqlHG->new();
- my $proxy_hgM = ProxySqlHG->new();
- my $proxy_hgB = ProxySqlHG->new();
- my ($id,$type) = split /:/, $hg;
- $proxy_hg->id($id);
- $proxy_hg->type(lc($type));
- if(lc($type) eq 'w'){
- $self->hg_writer_id($id);
- }
- if(lc($type) eq 'r'){
- $self->hg_reader_id($id);
- }
- $self->{_hostgroups}->{$id}=($proxy_hg);
- $proxy_hgM->id(($id + 9000));
- $proxy_hgM->type("m".lc($type));
- $self->{_hostgroups}->{$proxy_hgM->id(($id + 9000))}=($proxy_hgM);
- #add a special group in case of back server for failover
- if(lc($type) eq "w"){
- $proxy_hgM->id(($id + 8000));
- $proxy_hgM->type("b".lc($type));
- $self->{_hostgroups}->{$proxy_hgM->id(($id + 8000))}=($proxy_hgM);
- }
- if(lc($type) eq "r"){
- $proxy_hgM->id(($id + 8000));
- $proxy_hgM->type("b".lc($type));
- $self->{_hostgroups}->{$proxy_hgM->id(($id + 8000))}=($proxy_hgM);
- }
- if($self->debug >=1){print Utils->print_log(3," Inizializing hostgroup " . $proxy_hg->id ." ".$proxy_hg->type . "with maintenance HG ". $proxy_hgM->id ." ".$proxy_hgM->type."\n") ; }
- }
- }
- return $self->{_hostgroups};
- }
-
- sub user{
- my ( $self, $user ) = @_;
- $self->{_user} = $user if defined($user);
- return $self->{_user};
- }
- sub password {
- my ( $self, $password ) = @_;
- $self->{_password} = $password if defined($password);
- return $self->{_password};
- }
-
- sub monitor_user{
- my ( $self, $monitor_user ) = @_;
- $self->{_monitor_user} = $monitor_user if defined($monitor_user);
- return $self->{_monitor_user};
- }
- sub monitor_password {
- my ( $self, $monitor_password ) = @_;
- $self->{_monitor_password} = $monitor_password if defined($monitor_password);
- return $self->{_monitor_password};
- }
- sub port {
- my ( $self, $port ) = @_;
- $self->{_port} = $port if defined($port);
- return $self->{_port};
- }
- sub check_timeout{
- my ( $self, $check_timeout ) = @_;
- $self->{_check_timeout} = $check_timeout if defined($check_timeout);
- return $self->{_check_timeout};
- }
-
- #Connect method connect an populate the cluster returns the Galera cluster
- sub connect{
- my ( $self, $port ) = @_;
- my $dbh = Utils::get_connection($self->{_dns}, $self->{_user}, $self->{_password},' ');
- $self->{_dbh_proxy} = $dbh;
-
- # get monitor user/pw
- my $cmd = $self->{_SQL_get_monitor};
- my $sth = $dbh->prepare($cmd);
- $sth->execute();
- while (my $ref = $sth->fetchrow_hashref()) {
- if($ref->{'name'} eq 'mysql-monitor_password' ){$self->{_monitor_password} = $ref->{'value'};}
- if($ref->{'name'} eq 'mysql-monitor_username' ) {$self->{_monitor_user} = $ref->{'value'};}
- #This is for now comment out.
- # this is related to issue #10, where the node is not answering in time to the check.
- # The timeout cannot be the same of the the ProxySQL read_only check
- #if($ref->{'name'} eq 'mysql-monitor_read_only_timeout' ) {$self->{_check_timeout} = $ref->{'value'};}
-
- }
- if($self->debug >=1){print Utils->print_log(3," Connecting to ProxySQL " . $self->{_dns}. "\n" ); }
-
- }
-
- sub disconnect{
- my ( $self, $port ) = @_;
- $self->{_dbh_proxy}->disconnect;
-
-
- }
- sub get_galera_cluster{
- my ( $self, $in ) = @_;
- $self->{_galera_cluster} = $in if defined($in);
- return $self->{_galera_cluster};
- }
- sub set_galera_cluster(){
- my ( $self, $port ) = @_;
- my $galera_cluster = Galeracluster->new();
-
- $galera_cluster->hostgroups($self->hostgroups);
- $galera_cluster->dbh_proxy($self->dbh_proxy);
- $galera_cluster->check_timeout($self->check_timeout);
- $galera_cluster->monitor_user($self->monitor_user);
- $galera_cluster->monitor_password($self->monitor_password);
- $galera_cluster->debug($self->debug);
- $galera_cluster->hg_writer_id($self->hg_writer_id);
- $galera_cluster->hg_reader_id($self->hg_reader_id);
- $galera_cluster->singlewriter($Param->{single_writer});
- $galera_cluster->writer_is_reader($Param->{writer_is_reader});
- $galera_cluster->ssl_certificates_path($Param->{ssl_certs_path});
-
- $self->get_galera_cluster($galera_cluster);
- if($self->debug >=1){print Utils->print_log(3," Galera cluster object created " . caller(3). "\n" ); }
- }
-
- sub evaluate_nodes{
- my ($proxynode,$GGalera_cluster) = @_ ;
- my ( $nodes ) = $GGalera_cluster->{_nodes};
- my $action_nodes = undef;
- #Rules:
- #see rules in the doc
-
- #do the checks
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state \n" ) }
- foreach my $key (sort keys %{$nodes}){
- if(defined $nodes->{$key} ){
-
- #only if node has HG that is not maintenance it can evaluate to be put down in some way
- if($nodes->{$key}->{_hostgroups} < 8000
- && $nodes->{$key}->{_process_status} > 0){
- #Check major exclusions
- # 1) wsrep state
- # 2) Node is not read only
- # 3) at least another node in the HG
-
- if( $nodes->{$key}->wsrep_status == 2
- && $nodes->{$key}->read_only eq "OFF"
- #&& $GGalera_cluster->{_main_segment} != $nodes->{$key}->wsrep_segment
- && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"
- ){
- if($GGalera_cluster->{_hostgroups}->{$nodes->{$key}->{_hostgroups}}->{_size} <= 1){
- print Utils->print_log(3," Node ".$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups."; Is in state ".$nodes->{$key}->wsrep_status
- .". But I will not move to OFFLINE_SOFT given last node left in the Host group \n");
- next;
- }
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
- next;
- }
-
- if( $nodes->{$key}->wsrep_status ne 4
- && $nodes->{$key}->wsrep_status ne 2){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
-
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
-
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
-
- next;
- }
-
- #3) Node/cluster in non primary
- if($nodes->{$key}->cluster_status ne "Primary"){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
-
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
-
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
- next;
- }
-
- # 4) wsrep_reject_queries=NONE
- if($nodes->{$key}->wsrep_rejectqueries ne "NONE" && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"){
- my $inc =0;
- if($nodes->{$key}->wsrep_rejectqueries eq "ALL"){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
- $inc=1;
- }else{
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
- $inc=1;
- }
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0 && $inc > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
-
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
- next;
- }
-
- #5) Donor, node reject queries =1 size of cluster > 2 of nodes in the same segments
- if($nodes->{$key}->wsrep_status eq 2
- && $nodes->{$key}->wsrep_donorrejectqueries eq "ON"){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
-
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
-
- next;
- }
-
- #Set OFFLINE_SOFT a writer:
- #1) donor node reject queries - 0
- #2)size of cluster > 2 of nodes in the same segments
- #3) more then one writer in the same HG
- #4) Node had pxc_maint_mode set to anything except DISABLED, not matter what it will go in OFFLINE_SOFT
-
- if(
- $nodes->{$key}->read_only eq "ON"
- && $nodes->{$key}->{_hostgroups} == $GGalera_cluster->{_hg_writer_id}
- && $nodes->{$key}->wsrep_donorrejectqueries eq "OFF"
- && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"
- ){
- ## In case READ_ONLY is OFF and we have only a node left but desync do not put it down
- #if( $GGalera_cluster->{_size}->{$nodes->{$key}->{_wsrep_segment}} == 1
- # &&$nodes->{$key}->read_only eq "OFF"){
- # next;
- #}
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
- }
-
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
-
- next;
- }
-
- #4) Node had pxc_maint_mode set to anything except DISABLED, not matter what it will go in OFFLINE_SOFT
- if( defined $nodes->{$key}->pxc_maint_mode
- && $nodes->{$key}->pxc_maint_mode ne "DISABLED"
- && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_TO_MAINTENANCE}}= $nodes->{$key};
-
- #if retry is > 0 then it's managed
- if($proxynode->retry_down > 0){
- $nodes->{$key}->get_retry_down($proxynode->retry_down ); # this is a known state and we do not want any delay set the retry to his max
- }
-
- if(
- $nodes->{$key}->{_hostgroups} == $GGalera_cluster->{_hg_writer_id}
- && $GGalera_cluster->{_singlewriter} > 0
- ){
- $GGalera_cluster->{_haswriter} = $GGalera_cluster->{_haswriter} -1;
- }
-
-
- if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_TO_MAINTENANCE}
- ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
-
- next;
- }
-
- # Node must be removed if writer is reader is disable and node is in writer group
- if($nodes->{$key}->wsrep_status eq 4
- && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
- && $nodes->{$key}->read_only eq "OFF"
- && $nodes->{$key}->cluster_status eq "Primary"
- && $nodes->{$key}->hostgroups == $proxynode->{_hg_reader_id}
- && $GGalera_cluster->{_writer_is_reader} < 1
- && $nodes->{$key}->proxy_status eq "ONLINE"
- ){
- #my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
- my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
-
- my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
-
- if($nodes_write_ips =~ m/$ip/ ){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- print Utils->print_log(3," Writer is also reader disabled removing node from reader Hostgroup "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" );
-
- next;
- }
- }
-
-
- }
- #Node comes back from offline_soft when (all of them):
- # 1) Node state is 4
- # 3) wsrep_reject_queries = none
- # 4) Primary state
- # 5) pxc_maint_mode is DISABLED or undef
-
- if($nodes->{$key}->wsrep_status eq 4
- && $nodes->{$key}->proxy_status eq "OFFLINE_SOFT"
- && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
- && $nodes->{$key}->read_only eq "OFF"
- &&$nodes->{$key}->cluster_status eq "Primary"
- &&(!defined $nodes->{$key}->pxc_maint_mode || $nodes->{$key}->pxc_maint_mode eq "DISABLED")
- && $nodes->{$key}->hostgroups < 8000
- ){
- if($GGalera_cluster->haswriter > 0
- && $GGalera_cluster->singlewriter > 0
- && $nodes->{$key}->hostgroups == $GGalera_cluster->hg_writer_id
- ){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- if($proxynode->debug <=1){
- print Utils->print_log(3, " Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
- next;
- }
- else{
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_OFFLINE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- if($proxynode->debug <=1){
- print Utils->print_log(3, " Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_OFFLINE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
- next;
- }
- }
-
- # Node comes back from maintenance HG when (all of them):
- # 1) node state is 4
- # 3) wsrep_reject_queries = none
- # 4) Primary state
- if($nodes->{$key}->wsrep_status eq 4
- && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
- && $nodes->{$key}->cluster_status eq "Primary"
- && $nodes->{$key}->hostgroups >= 9000
- ){
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
- next;
- }
-
- #Special case when a node goes down it goes through several state and the check disable it moving form original group
- #This is to remove it to his original HG when is not reachable
- if($nodes->{$key}->{_process_status} < 0
- && $nodes->{$key}->hostgroups >= 9000
- ){
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
-
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}}= $nodes->{$key};
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
- next;
- }
-
- #Check if any node that is in the read backup host group is not present in the readhostgroup while it should.
- #If identify it will add to the read HG
-
- if($nodes->{$key}->wsrep_status eq 4
- && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
- && $nodes->{$key}->cluster_status eq "Primary"
- && $nodes->{$key}->hostgroups == (8000 + $proxynode->{_hg_reader_id})
- ){
- my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
- my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
-
- my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
-
- if($nodes_read_ips =~ m/$ip/
- || ( $nodes_write_ips =~ m/$ip/
- && $GGalera_cluster->{_writer_is_reader} < 1)){
- if($proxynode->debug >=1){
- print Utils->print_log(3," Node already ONLINE in read hg "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";\n" ) }
- }else{
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_READ}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_READ}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" )
- }
- }
- next;
- }
- #Check if any node that is in the write backup host group is not present in the WRITE hostgroup while it should WHEN MULTIPLE WRITERS.
- #If identify it will add to the read HG
-
- if($nodes->{$key}->wsrep_status eq 4
- && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
- && $nodes->{$key}->read_only eq "OFF"
- && $nodes->{$key}->cluster_status eq "Primary"
- && $nodes->{$key}->hostgroups == (8000 + $proxynode->{_hg_writer_id})
- && $GGalera_cluster->{_singlewriter} < 1
- ){
- #my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
- my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
-
- my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
-
- if($nodes_write_ips =~ m/$ip/
- && $GGalera_cluster->{_single_writer} < 1){
- if($proxynode->debug >=1){
- print Utils->print_log(3," Node already ONLINE in write hg "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";\n" ) }
- }else{
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_WRITE}}= $nodes->{$key};
- #if retry is > 0 then it's managed
- if($proxynode->retry_up > 0){
- $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
- }
- if($proxynode->debug >=1){
- print Utils->print_log(3," Evaluate nodes state "
- .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_WRITE}
- ." Retry #".$nodes->{$key}->get_retry_up."\n" )
- }
- }
- next;
- }
-
-
- # in the case node is not in one of the declared state
- # BUT it has the counter retry set THEN I reset it to 0 whatever it was because
- # I assume it is ok now
- if($proxynode->retry_up > 0
- && $nodes->{$key}->get_retry_up > 0){
- $nodes->{$key}->get_retry_up(0);
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_SAVE_RETRY}}= $nodes->{$key};
- }
- if($proxynode->retry_down > 0
- && $nodes->{$key}->get_retry_down > 0){
- $nodes->{$key}->get_retry_down(0);
- $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_SAVE_RETRY}}= $nodes->{$key};
- }
-
- }
- }
- $proxynode->action_nodes($action_nodes);
-
- #failover has higher priority BUT if it happens before other action will interfere with recovery and move nodes that may prevent the failover to happen
- # and it will ends the script work (if successful)
- #if($GGalera_cluster->{_writers} > 1
- # && $GGalera_cluster->{_singlewriter} > 0)
- #{
- # $proxynode->{_require_failover} = 1;
- #}
-
-
-
- if($proxynode->require_failover > 0
- && !defined $action_nodes
- ){
- if($GGalera_cluster->haswriter < 1
- || ( $GGalera_cluster->{_writers} > 1 && $GGalera_cluster->{_singlewriter} > 0)
- ){
- print Utils->print_log(2,"Fail-over in action Using Method = $proxynode->{_require_failover}\n" );
- if($proxynode->initiate_failover($GGalera_cluster) >0){
- #if($proxynode->debug >=1){
- print Utils->print_log(2,"!!!! FAILOVER !!!!! \n Cluster was without WRITER I have try to restore service promoting a node\n" );
- #exit 0;
- #}
- }
- }
- }
- elsif($proxynode->require_failover > 0
- && $GGalera_cluster->haswriter < 1){
- print Utils->print_log(2,"PXC maintenance on single writer, is asking for failover. Fail-over in action Using Method = $proxynode->{_require_failover}\n" );
- $proxynode->push_changes;
- if($proxynode->initiate_failover($GGalera_cluster) >0){
- #if($proxynode->debug >=1){
- print Utils->print_log(2,"!!!! FAILOVER !!!!! \n Cluster was without WRITER I have try to restore service promoting a node\n" );
- #exit 0;
- #}
- }
- }
- }
-
- sub push_changes{
- my ($proxynode) = @_ ;
- my $node = GaleraNode->new();
- my $SQL_command="";
-
-
- foreach my $key (sort keys %{$proxynode->{_action_nodes}}){
- my ($host, $port, $hg, $action) = split /s*;\s*/, $key;
-
- SWITCH: {
- if ($action == $node->MOVE_DOWN_OFFLINE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){$proxynode->move_node_offline($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if ($action == $node->MOVE_DOWN_HG_CHANGE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){ $proxynode->move_node_down_hg_change($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if ($action == $node->MOVE_UP_OFFLINE) { if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){ $proxynode->move_node_up_from_offline($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if ($action == $node->MOVE_UP_HG_CHANGE) { if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){$proxynode->move_node_up_from_hg_change($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if ($action == $node->MOVE_TO_MAINTENANCE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){$proxynode->move_node_to_maintenance($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if ($action == $node->DELETE_NODE) {
- if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
- $proxynode->delete_node_from_hostgroup($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
- if($action == $node->INSERT_READ){if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
- $proxynode->insert_reader($key,$proxynode->{_action_nodes}->{$key})
- }; last SWITCH;
- }
-
- if($action == $node->INSERT_WRITE){if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
- $proxynode->insert_writer($key,$proxynode->{_action_nodes}->{$key})
- }; last SWITCH;
- }
- }
- if($proxynode->retry_up > 0 || $proxynode->retry_down > 0){
- save_retry($proxynode,$key,$proxynode->{_action_nodes}->{$key});
- }
- }
- $proxynode->{_action_nodes} = undef;
- }
-
- sub save_retry{
- #this action will take place only if retry is active
- my ($self,$key,$node) = @_;
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
-
- if($self->debug >=1){print Utils->print_log(4,"Check retry Node:".$host." port:".$port . " hg:".$hg ." Time IN \n");}
-
- my $sql_string = "UPDATE mysql_servers SET comment='"
- .$node->{_comment}
- .$self->get_galera_cluster->cluster_identifier."_retry_up=".$node->get_retry_up
- .";".$self->get_galera_cluster->cluster_identifier."_retry_down=".$node->get_retry_down
- .";' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
-
- $self->{_dbh_proxy}->do($sql_string) or die "Couldn't execute statement: " . $self->{_dbh_proxy}->errstr;
- $self->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $self->{_dbh_proxy}->errstr;
-
- if($self->debug >=1){print Utils->print_log(2," Reset retry to UP:".$node->get_retry_up." Down:".$node->get_retry_down."for node:" .$key
- ." SQL:" .$sql_string
- ."\n")} ;
- if($self->debug >=1){print Utils->print_log(4,"Check retry Node:".$host." port:".$port . " hg:".$hg ." Time OUT \n");}
- }
-
- sub move_node_offline{
- #this action involve only the proxy so we will
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command= " UPDATE mysql_servers SET status='OFFLINE_SOFT' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
-
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n") ;
- }
- sub move_node_to_maintenance{
- #this action involve only the proxy so we will
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command= " UPDATE mysql_servers SET status='OFFLINE_SOFT' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
-
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n") ;
- }
- #remove a node from an hostgroup
- sub delete_node_from_hostgroup{
- #this action involve only the proxy so we will
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command= " DELETE from mysql_servers WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
-
- print Utils->print_log(2," DELETE node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n") ;
- }
- #move a node to a maintenance HG ((9000 + HG id))
- sub move_node_down_hg_change{
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- if($hg > 9000) {return 1;}
-
- my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
- my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".(9000 + $hg)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" );
- }
- #Bring back a node that is just offline
- sub move_node_up_from_offline{
- my ($proxynode, $key,$node) = @_;
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command= " UPDATE mysql_servers SET status='ONLINE' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- my $error_code = $proxynode->{_dbh_proxy}->err();
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" );
- }
- #move a node back to his original HG ((HG id - 9000))
- sub move_node_up_from_hg_change{
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- #my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
- my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- my $error_code = $proxynode->{_dbh_proxy}->err();
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" ) ;
- }
-
- #move a node back to his original HG ((HG id - 9000))
- sub add_node_to_readers{
- my ($proxynode, $key,$node) = @_;
-
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
- my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- my $error_code = $proxynode->{_dbh_proxy}->err();
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" ) ;
- }
- sub insert_reader{
- my ($proxynode, $key,$node) = @_;
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command ="INSERT INTO mysql_servers (hostgroup_id, hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment) ".
- " VALUES($proxynode->{_hg_reader_id}" .
- ",'$node->{_ip}'" .
- ",$node->{_port} " .
- ",$node->{_gtid_port} " .
- ",'$node->{_proxy_status}' " .
- ",$node->{_weight}" .
- ",$node->{_compression}" .
- ",$node->{_connections}" .
- ",$node->{_max_replication_lag}" .
- ",$node->{_use_ssl}" .
- ",$node->{_max_latency}" .
- ",'$node->{_comments}')" ;
-
- #my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: $proxy_sql_command" . $proxynode->{_dbh_proxy}->errstr;
- my $error_code = $proxynode->{_dbh_proxy}->err();
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" ) ;
- }
-
- sub insert_writer{
- my ($proxynode, $key,$node) = @_;
- my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
- my $proxy_sql_command ="INSERT INTO mysql_servers (hostgroup_id, hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment) ".
- " VALUES($proxynode->{_hg_writer_id}" .
- ",'$node->{_ip}'" .
- ",$node->{_port} " .
- ",$node->{_gtid_port} " .
- ",'$node->{_proxy_status}' " .
- ",$node->{_weight}" .
- ",$node->{_compression}" .
- ",$node->{_connections}" .
- ",$node->{_max_replication_lag}" .
- ",$node->{_use_ssl}" .
- ",$node->{_max_latency}" .
- ",'$node->{_comments}')" ;
-
- #my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
- $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: $proxy_sql_command" . $proxynode->{_dbh_proxy}->errstr;
- my $error_code = $proxynode->{_dbh_proxy}->err();
- $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
- print Utils->print_log(2," Move node:" .$key
- ." SQL:" .$proxy_sql_command
- ."\n" ) ;
- }
- sub initiate_failover{
- my ($proxynode,$Galera_cluster) = @_ ;
- my ( $nodes ) = $Galera_cluster->{_nodes};
- my ( $nodes_maint ) = $Galera_cluster->{_nodes_maint};
- my $failover_node;
- my $candidate_failover_node;
- my $min_index = 100;
- my $max_weight=0;
- my $cand_min_index = 100;
- my $cand_max_weight=0;
- my $local_node;
- my $hg_writer_id=0;
- my $exclude_delete="";
-
- #Valid values are:
- # 0 [default] do not make failover
- # 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
- # 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
- # 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
- #
- foreach my $key (sort keys %{$nodes}){
- if(defined $nodes->{$key} ){
-
- #only if node has HG that is not maintenance it can be evaluated to be put down in some way
- #Look for the node with the lowest weight in the same segment
- if($nodes->{$key}->{_hostgroups} < 9000
- && $nodes->{$key}->{_proxy_status} eq "ONLINE"
- && $nodes->{$key}->{_process_status} > 0
- && $nodes->{$key}->{_wsrep_status} == 4
- && $nodes->{$key}->{_wsrep_rejectqueries} eq "NONE"
- && $nodes->{$key}->{_wsrep_donorrejectqueries} eq "OFF"
- && $nodes->{$key}->{_pxc_maint_mode} eq "DISABLED"
- && $nodes->{$key}->{_read_only} eq "OFF"
- ){
-
- #IN case failover option is 1 we need to have:
- #The failover group defined in Proxysql (8xxx + id of the HG)
- #Node must be of HG 8XXXX id
- #And must be in the same segment of the writer
- if(
- $proxynode->{_require_failover} == 1
- # && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
- && $Galera_cluster->{_has_failover_node} >0
- && $nodes->{$key}->{_hostgroups} == (8000 + $Galera_cluster->{_hg_writer_id})
- ){
- if($nodes->{$key}->{_weight} > $max_weight
- && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
- ){
- $max_weight= $nodes->{$key}->{_weight};
- #$min_index = $nodes->{$key}->{_wsrep_local_index};
- $failover_node = $nodes->{$key};
- }
- elsif($nodes->{$key}->{_wsrep_segment} != $Galera_cluster->{_main_segment}
- && !defined $failover_node
- && $nodes->{$key}->{_weight} > $cand_max_weight){
- $cand_max_weight= $nodes->{$key}->{_weight};
- # $cand_min_index = $nodes->{$key}->{_wsrep_local_index};
- $candidate_failover_node = $nodes->{$key};
- }
- #if($nodes->{$key}->{_weight} > $max_weight){
- # $max_weight= $nodes->{$key}->{_weight};
- # $failover_node = $nodes->{$key};
- #}
- }
- #IN case failover option is 2 we need to have:
- # must be in the same segment of the writer
- #and be in the PXC_CLUSTER_VIEW
- elsif(
- $proxynode->{_require_failover} == 2
- && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
- ){
- if($nodes->{$key}->{_wsrep_local_index} < $min_index
- ){
- $min_index = $nodes->{$key}->{_wsrep_local_index};
- $failover_node = $nodes->{$key};
- }
- }
- elsif($proxynode->{_require_failover} == 3){
- if($nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
- && $nodes->{$key}->{_wsrep_local_index} < $min_index){
- $min_index = $nodes->{$key}->{_wsrep_local_index};
- $failover_node = $nodes->{$key};
- }
- elsif($nodes->{$key}->{_wsrep_segment} != $Galera_cluster->{_main_segment}
- && !defined $failover_node
- && $nodes->{$key}->{_wsrep_local_index} < $cand_min_index){
- $cand_min_index = $nodes->{$key}->{_wsrep_local_index};
- $candidate_failover_node = $nodes->{$key};
- }
- }
- }
- }
- }
- if(defined $nodes_maint ){
- my $exclude_id = "";
- my $exclude_port = "";
- foreach my $key (sort keys %{$nodes_maint}){
- if(defined $nodes_maint->{$key}){
- if(length($exclude_id) > 1){
- $exclude_id = $exclude_id . ",";
- $exclude_port = $exclude_port . ",";
- }
-
- $exclude_id = $exclude_id ."'". $nodes_maint->{$key}->{_ip} ."'";
- $exclude_port = $exclude_port.$nodes_maint->{$key}->{_port} ;
- }
- }
- if(length($exclude_id) > 1){
- $exclude_delete = $exclude_delete . " AND (hostname not in (".$exclude_id.") AND port not in (".$exclude_port."))" ;
- }
- }
-
- #if a node was found, try to do the failover removing the READ_ONLY
- if(defined $candidate_failover_node && $failover_node){
- return $failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
- }
- elsif(defined $candidate_failover_node && !defined $failover_node){
- return $candidate_failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
- }
- else{
- if(!defined $failover_node){
- SWITCH: {
- if ($proxynode->{_require_failover} == 1) { print Utils->print_log(1,"!!!! No node for failover found , try to use active_failover=2 OR add a valid node to the 8000 HG pool \n" ) ; last SWITCH; }
- if ($proxynode->{_require_failover} == 2) { print Utils->print_log(1,"!!!! No node for failover found , try to use active_failover=3 But that may move production to the other segment.\n" ); last SWITCH; }
- if ($proxynode->{_require_failover} == 3) { print Utils->print_log(1,"!!!! No node for failover found also in the other segments, I cannot continue you need to act manually \n" ); last SWITCH; }
- }
- }
-
- if(defined $failover_node){
- return $failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
- }
- }
-
-
- return 0;
- }
- }
- {
- package ProxySqlHG;
- sub new {
- my $class = shift;
-
- my $self = {
- _id => undef, #
- _type => undef, # available types: w writer; r reader ; mw maintance writer; mr maintenance reader
- _size => 0,
- };
- bless $self, $class;
- return $self;
- }
-
- sub id {
- my ( $self, $id ) = @_;
- $self->{_id} = $id if defined($id);
- return $self->{_id};
- }
- sub type {
- my ( $self, $type ) = @_;
- $self->{_type} = $type if defined($type);
- return $self->{_type};
- }
- sub size {
- my ( $self, $size ) = @_;
- $self->{_size} = $size if defined($size);
- return $self->{_size};
- }
- }
- {
- package Utils;
- use Time::HiRes qw(gettimeofday);
- #============================================================================
- ## get_connection -- return a valid database connection handle (or die)
- ## $dsn -- a perl DSN, e.g. "DBI:mysql:host=ltsdbwm1;port=3311"
- ## $user -- a valid username, e.g. "check"
- ## $pass -- a matching password, e.g. "g33k!"
-
- sub get_connection($$$$) {
- my $dsn = shift;
- my $user = shift;
- my $pass = shift;
- my $SPACER = shift;
- my $dbh = DBI->connect($dsn, $user, $pass , {
- PrintError => 0,
- PrintWarn => 0,
- RaiseError => 0});
-
- if (!defined($dbh)) {
- #die
- print Utils->print_log(1, "Cannot connect to $dsn as $user\n");
- # Should not die and instead return undef so we can handle this shit
- #die();
- return undef;
- }
-
- return $dbh;
- }
-
-
- ######################################################################
- ## collection functions -- fetch status data from db
- ## get_status -- return a hash ref to SHOW GLOBAL STATUS output
- ## $dbh -- a non-null database handle, as returned from get_connection()
- ##
-
-
- sub get_status($$) {
- my $dbh = shift;
- my $debug = shift;
- my %v;
- my $cmd = "show /*!50000 global */ status";
-
- my $sth = $dbh->prepare($cmd);
- $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
- while (my $ref = $sth->fetchrow_hashref()) {
- my $n = $ref->{'Variable_name'};
- $v{"\L$n\E"} = $ref->{'Value'};
- if ($debug>0){print "MySQL status = ".$n."\n";}
- }
-
- return \%v;
- }
- ######################################################################
- ## collection functions -- fetch status data from db
- ## get_status -- return a hash ref to SHOW GLOBAL STATUS output
- ## $dbh -- a non-null database handle, as returned from get_connection()
- ##
-
- sub get_status_by_name($$) {
- my $dbh = shift;
- my $debug = shift;
- my $name = shift ;
- my %v;
- my $cmd = "show /*!50000 global */ status like '$name'";
-
- my $sth = $dbh->prepare($cmd);
- $sth->execute();
- while (my $ref = $sth->fetchrow_hashref()) {
- my $n = $ref->{'Variable_name'};
- $v{"\L$n\E"} = $ref->{'Value'};
- if ($debug>0){print "MySQL status = ".$n."\n";}
- }
-
- return \%v;
- }
- ##
- ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
- ##
- ## $dbh -- a non-null database handle, as returned from get_connection()
- ##
- sub get_variables($$) {
- my $dbh = shift;
- my $debug = shift;
- my %v;
- my $cmd = "select * from performance_schema.global_variables";
- $dbh->{LongReadLen} = 0;
- $dbh->{LongTruncOk} = 0;
-
- my $sth = $dbh->prepare($cmd);
- $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
- while (my $ref = $sth->fetchrow_hashref()) {
- my $n = $ref->{'VARIABLE_NAME'};
- $v{"\L$n\E"} = $ref->{'VARIABLE_VALUE'};
- # print STDERR "$n : ".$v{$n}. " ZZZZZZZZZZZZZZZZZZ ". $ref->{'Value'} ."\n";
- }
-
-
- return \%v;
- }
- ##
- ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
- ##
- ## $dbh -- a non-null database handle, as returned from get_connection()
- ##
- sub get_variablesByName($$) {
- my $dbh = shift;
- my $variableName = shift;
- #my $debug = shift;
- my %v;
- my $cmd = "show variables like '$variableName'";
-
- my $sth = $dbh->prepare($cmd);
- $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
- while (my $ref = $sth->fetchrow_hashref()) {
- my $n = $ref->{'Variable_name'};
- $v{"\L$n\E"} = $ref->{'Value'};
- }
- return \%v;
- }
- ##
- ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
- ##
- ## $dbh -- a non-null database handle, as returned from get_connection()
- ##
- sub get_pxc_clusterview($$) {
- my $dbh = shift;
- my $variableName = shift;
- #my $debug = shift;
-
- my %v;
- my $cmd = "select * from performance_schema.pxc_cluster_view where UUID = '$variableName'";
-
- my $sth = $dbh->prepare($cmd);
- $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
- my $ref;
- while ( $ref = $sth->fetchrow_hashref()) {
- foreach my $name ('HOST_NAME', 'UUID','STATUS','LOCAL_INDEX','SEGMENT'){
- my $n = lc $name;
- $v{$n} = $ref->{$name};
- }
- }
- return \%v;
- }
- #Print time from invocation with milliseconds
- sub get_current_time{
- use POSIX qw(strftime);
- my $t = gettimeofday();
- my $date = strftime "%Y/%m/%d %H:%M:%S", localtime $t;
- $date .= sprintf ".%03d", ($t-int($t))*1000; # without rounding
-
- return $date;
- }
- #prit all environmnt variables
- sub debugEnv{
- my $key = keys %ENV;
- foreach $key (sort(keys %ENV)) {
- print $key, '=', $ENV{$key}, "\n";
- }
-
- }
-
-
- #Print a log entry
- sub print_log($$){
- my $log_level = $_[1];
- my $text = $_[2];
- my $log_text = "[ - ] ";
-
- SWITCH: {
- if ($log_level == 1) { $log_text= "[ERROR] "; last SWITCH; }
- if ($log_level == 2) { $log_text= "[WARN] "; last SWITCH; }
- if ($log_level == 3) { $log_text= "[INFO] "; last SWITCH; }
- if ($log_level == 4) { $log_text= "[DEBUG] "; last SWITCH; }
- }
- return Utils::get_current_time.":".$log_text.$text;
-
- }
-
-
- #trim a string
- sub trim {
- my $s = shift;
- $s =~ s/^\s+|\s+$//g;
- return $s
- };
- }
- # ############################################################################
- # Documentation
- # #################
- =pod
- =head1 NAME
- galera_check.pl
- =head1 OPTIONS
- =over
- galera_check.pl -u=admin -p=admin -h=192.168.1.50 -H=500:W,501:R -P=3310 --main_segment=1 --debug=0 --log <full_path_to_file> --help
- sample [options] [file ...]
- Options:
- -u|user user to connect to the proxy
- -p|password Password for the proxy
- -h|host Proxy host
- -H Hostgroups with role definition. List comma separated.
- Definition R = reader; W = writer [500:W,501:R]
- --main_segment If segments are in use which one is the leading at the moment
- --retry_up The number of loop/test the check has to do before moving a node up (default 0)
- --retry_down The number of loop/test the check has to do before moving a node Down (default 0)
- --log Full path to the log file ie (/var/log/proxysql/galera_check_) the check will add
- the identifier for the specific HG.
- --active_failover A value from 0 to 3, indicating what level/kind of fail-over the script must perform.
- active_failover
- Valid values are:
- 0 [default] do not make failover
- 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
- 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
- 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
- --single_writer Active by default [single_writer = 1 ] if disable will allow to have multiple writers
-
-
- Performance parameters
- --check_timeout This parameter set in ms then time the script can alow a thread connecting to a MySQL node to wait, before forcing a returnn.
- In short if a node will take longer then check_timeout its entry will be not filled and it will eventually ignored in the evaluation.
- Setting the debug option =1 and look for [WARN] Check timeout Node ip : Information will tell you how much your nodes are exceeding the allowed limit.
- You can use the difference to correctly set the check_timeout
- Default is 800 ms
-
- --help help message
- --debug When active the log will have a lot of information about the execution. Parse it for ERRORS if you have problems
- --print_execution Active by default, it will print the execution time the check is taking in the log. This can be used to tune properly the scheduler time, and also the --check_timeout
-
- --development When set to 1 you can run the script in a loop from bash directly and test what is going to happen
- --development_time Time in seconds that the loop wait to execute when in development mode (default 2 seconds)
-
- SSL support
- Now the script identify if the node in the ProxySQL table mysql_servers has use_ssl = 1 and will set SSL to be used for that specific entry.
- This means that SSL connection is by ProxySQL mysql_server entry NOT by IP:port combination.
-
- --ssl_certs_path This parameter allow you to specify a DIRECTORY to use to assign specific certificates.
- At the moment is NOT possible to change the files names and ALL these 3 files must be there and named as follow:
- - client-key.pem
- - client-cert.pem
- - ca.pem
- Script will exit with an error if ssl_certs_pathis declared but not filled properly
- OR if the user running the script doesn't have acces.
- !!NOTE!! SSL connection requires more time to be established. This script is a check that needs to run very fast and constantly.
- force it to use ssl WILL impact in the performance of the check. Tune properly the check_timeout parameter.
- =back
-
- =head1 DESCRIPTION
- Galera check is a script to manage integration between ProxySQL and Galera (from Codership).
- Galera and its implementations like Percona Cluster (PCX), use the data-centric concept, as such the status of a node is relvant in relation to a cluster.
- In ProxySQL is possible to represent a cluster and its segments using HostGroups.
- Galera check is design to manage a X number of nodes that belong to a given Hostgroup (HG).
- In Galera_check it is also important to qualify the HG in case of use of Replication HG.
- galera_check works by HG and as such it will perform isolated actions/checks by HG.
- It is not possible to have more than one check running on the same HG. The check will create a lock file {proxysql_galera_check_${hg}.pid} that will be used by the check to prevent duplicates.
- Galera_check will connect to the ProxySQL node and retrieve all the information regarding the Nodes/proxysql configuration.
- It will then check in parallel each node and will retrieve the status and configuration.
- At the moment galera_check analyze and manage the following:
- Node states:
- read_only
- wsrep_status
- wsrep_rejectqueries
- wsrep_donorrejectqueries
- wsrep_connected
- wsrep_desinccount
- wsrep_ready
- wsrep_provider
- wsrep_segment
- Number of nodes in by segment
- Retry loop
-
- - Number of nodes in by segment
- If a node is the only one in a segment, the check will behave accordingly.
- IE if a node is the only one in the MAIN segment, it will not put the node in OFFLINE_SOFT when the node become donor to prevent the cluster to become unavailable for the applications.
- As mention is possible to declare a segment as MAIN, quite useful when managing prod and DR site.
- -The check can be configure to perform retry in a X interval.
- Where X is the time define in the ProxySQL scheduler.
- As such if the check is set to have 2 retry for UP and 4 for down, it will loop that number before doing anything. Given that Galera does some action behind the hood.
- This feature is very useful in some not well known cases where Galera bhave weird.
- IE whenever a node is set to READ_ONLY=1, galera desync and resync the node.
- A check not taking this into account will cause a node to be set OFFLINE and back for no reason.
- Another important differentiation for this check is that it use special HGs for maintenance, all in range of 9000.
- So if a node belong to HG 10 and the check needs to put it in maintenance mode, the node will be moved to HG 9010.
- Once all is normal again, the Node will be put back on his original HG.
- This check does NOT modify any state of the Nodes.
- Meaning It will NOT modify any variables or settings in the original node. It will ONLY change states in ProxySQL.
-
- The check is still a prototype and is not suppose to go to production (yet).
- =over
- =item 1
- Note that galera_check is also Segment aware, as such the checks on the presence of Writer /reader is done by segment, respecting the MainSegment as primary.
- =back
- =head1 Configure in ProxySQL
- INSERT INTO scheduler (id,active,interval_ms,filename,arg1) values (10,0,2000,"/var/lib/proxysql/galera_check.pl","-u=remoteUser -p=remotePW -h=192.168.1.50 -H=500:W,501:R -P=6032 --retry_down=2 --retry_up=1 --main_segment=1 --debug=0 --active_failover=1 --single_writer=1 --log=/var/lib/proxysql/galeraLog");
- LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
- To activate it
- update scheduler set active=1 where id=10;
- LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
- To update the parameters you must pass all of them not only the ones you want to change(IE enabling debug)
- update scheduler set arg1="-u=remoteUser -p=remotePW -h=192.168.1.50 -H=500:W,501:R -P=6032 --retry_down=2 --retry_up=1 --main_segment=1 --debug=1 --active_failover=1 --single_writer=1 --log=/var/lib/proxysql/galeraLog" where id =10;
- LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
- delete from scheduler where id=10;
- LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
- =head1 Rules:
- =over
- =item 1
- Set to offline_soft :
-
- any non 4 or 2 state, read only =ON
- donor node reject queries - 0 size of cluster > 2 of nodes in the same segments more then one writer, node is NOT read_only.
-
- Changes to pxc_maint_mode to anything else DISABLED
- =item 2
- change HG t maintenance HG:
-
- Node/cluster in non primary
- wsrep_reject_queries different from NONE
- Donor, node reject queries =1 size of cluster
- =item 3
- Node comes back from offline_soft when (all of them):
- 1) Node state is 4
- 3) wsrep_reject_queries = none
- 4) Primary state
- =item 4
- Node comes back from maintenance HG when (all of them):
- 1) node state is 4
- 3) wsrep_reject_queries = none
- 4) Primary state
- =item 5
- active_failover
- Valid values are:
- 0 [default] do not make failover
- 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
- 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
- 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
- =item 6
- PXC_MAIN_MODE is fully supported.
- Any node in a state different from pxc_maint_mode=disabled will be set in OFFLINE_SOFT for all the HostGroup.
-
- =item 7
- internally shunning node.
- While I am trying to rely as much as possible on ProxySQL, given few inefficiencies there are cases when I have to set a node to SHUNNED because ProxySQL doesn't recognize it correctly.
-
- =back
- =cut
|