Hello! We use HAProxy for several services and it's working great. However, for one specific load balancing need, we need to be able to put a server in backup mode without having to modify the configuration file followed by a reload.
Consider a single master MySQL deployment with multiple slaves, and two backends/frontends on HAProxy: master, and slave, where the master database is the single UP and operational server in the master pool, and the slave pool consists of the slave servers plus the master in backup mode: global stats socket /etc/haproxy/haproxy.sock level admin server-state-file /etc/haproxy/haproxy.state listen mysql-master bind :10000 option tcp-check server server1 10.0.0.1:3306 check weight 100 server server2 10.0.0.2:3306 check weight 100 server server3 10.0.0.3:3306 check weight 100 listen mysql-slave bind :10001 option tcp-check server server1 10.0.0.1:3306 check weight 100 backup server server2 10.0.0.2:3306 check weight 100 server server3 10.0.0.3:3306 check weight 100 In the mysql-master pool we have server1 (master), and server2 and server3 which are slaves. The state file makes sure that server2 and server3 will be loaded into HAProxy in MAINT mode at launch time so that only the master (server1) is served at port 10000. The master in the slave pool is in backup mode, and HAProxy will only add it to slave LB farm only when the slaves are not UP. We use MHA for automating MySQL master failover. MHA supports hook scripts that are launched at certain steps of a master switch over. For instance the script that acts as a master_ip_failover_script: https://code.google.com/p/mysql-master-ha/wiki/Parameters#master_ip_failover_script will be kicked off by the MHA before shutting down the master, AND after the newly promoted master is ready. The script that we implemented for this purpose communicates with the HAProxy socket via socat at both of these steps. At the first step, we want the HAProxy to put the master (server1) offline, but continue to serve current connections (DRAIN). We do this by either running "set server mysql-master/server1 weight 0" or "set server mysql-master/server1 state drain" command. At this stage, we don't initiate a command for master server in the slave pool as it isn't necessary. At the second step when the new master is ready (say server2 is the new master), the script will run "set server mysql-master/server2 state ready" and "set server mysql-master/server2 weight 100" commands to make the new master UP and available. It will also run "disable server mysql-master/server1" to disable the old master in the master pool - assume the time passes between the two steps is sufficient for the connections to be drained. Lastly, we need to be able to run "set server mysql-slave/server1 state active" to remove its backup flag and "set server mysql-slave/server2 state backup" to put the new master in backup mode. Unfortunately, "set server <server> state active/backup" commands do not exist and the only way to make this change is by modifying the haproxy.cfg file and reloading the daemon. For the sake of automation, we've modified the HAProxy 1.6.6 code that support active/backup commands. I've attached the patch against v1.6.6 for your verification. I didn't dive much into the HAProxy source code and I don't know at this point whether or not this patch would mess up the inner mechanics of HAProxy but I want to be able to run these commands on the HAProxy socket file AND I want to avoid any potential short circuits. Thanks in advance for your feedback. Murat
diff -urN a/src/dumpstats.c b/src/dumpstats.c --- a/src/dumpstats.c 2016-06-26 17:41:01.000000000 +0000 +++ b/src/dumpstats.c 2016-06-28 18:45:05.664074832 +0000 @@ -121,6 +121,8 @@ ST_ADM_ACTION_READY, ST_ADM_ACTION_DRAIN, ST_ADM_ACTION_MAINT, + ST_ADM_ACTION_BACKUP, + ST_ADM_ACTION_ACTIVE, ST_ADM_ACTION_SHUTDOWN, /* these are the ancient actions, still available for compatibility */ ST_ADM_ACTION_DISABLE, @@ -1529,8 +1531,20 @@ srv_adm_set_drain(sv); else if (strcmp(args[4], "maint") == 0) srv_adm_set_maint(sv); + else if (strcmp(args[4], "backup") == 0) { + struct proxy *p = sv->proxy; + sv->flags |= SRV_F_BACKUP; + p->srv_act--; + p->srv_bck++; + } + else if (strcmp(args[4], "active") == 0) { + struct proxy *p = sv->proxy; + sv->flags &= ~SRV_F_BACKUP; + p->srv_act++; + p->srv_bck--; + } else { - appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain' and 'maint'.\n"; + appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain', 'maint', 'active', and 'backup'.\n"; appctx->st0 = STAT_CLI_PRINT; } } @@ -4084,6 +4098,8 @@ "<option value=\"ready\">Set state to READY</option>" "<option value=\"drain\">Set state to DRAIN</option>" "<option value=\"maint\">Set state to MAINT</option>" + "<option value=\"backup\">Set state to BACKUP</option>" + "<option value=\"active\">Set state to ACTIVE</option>" "<option value=\"dhlth\">Health: disable checks</option>" "<option value=\"ehlth\">Health: enable checks</option>" "<option value=\"hrunn\">Health: force UP</option>" @@ -4891,6 +4907,12 @@ else if (strcmp(value, "maint") == 0) { action = ST_ADM_ACTION_MAINT; } + else if (strcmp(value, "backup") == 0) { + action = ST_ADM_ACTION_BACKUP; + } + else if (strcmp(value, "active") == 0) { + action = ST_ADM_ACTION_ACTIVE; + } else if (strcmp(value, "shutdown") == 0) { action = ST_ADM_ACTION_SHUTDOWN; } @@ -5064,6 +5086,20 @@ altered_servers++; total_servers++; break; + case ST_ADM_ACTION_BACKUP: + sv->flags |= SRV_F_BACKUP; + px->srv_act--; + px->srv_bck++; + altered_servers++; + total_servers++; + break; + case ST_ADM_ACTION_ACTIVE: + sv->flags &= ~SRV_F_BACKUP; + px->srv_act++; + px->srv_bck--; + altered_servers++; + total_servers++; + break; case ST_ADM_ACTION_SHUTDOWN: if (px->state != PR_STSTOPPED) { struct stream *sess, *sess_bck;