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;

Reply via email to