HAProxy and A/B Testing

Few weeks ago, I was given a task to create an A/B test using HAProxy. I need to make HAProxy to split traffic between two different applications ( Rails and NodeJS )

In this blog post, I’ll explain how to achieve this.

Create ACL for the page you want to A/B test

The first step you have to do is to create an ACL for the URL you want to A/B test. In this example, the URL path is /ab-test-path

acl ab_test_url path_beg /ab-test-path
# Send user to first backend if they have SITEID cookie with cookie_first_backend value
use_backend first-backend if { req.cook(SITEID) -m beg cookie_first_backend }

# Send user to second backend if they have SITEID cookie with cookie_second_backend value and the URL they request is ab_test_url
use_backend second-backend if { req.cook(SITEID) -m beg cookie_second_backend } ab_test_url

# If the doesn't have any cookie send them to ab-test backend
use_backend ab-test-backend if ab_test_url

# By default send all traffic to the first backend
default_backend first-backend

Create all the backends

backend first-backend
        appsession JSESSIONID len 52 timeout 3h
        cookie SERVERID insert

        balance leastconn
        server  backend02a-aws-syd 10.0.105.102:80 check maxconn 3000 inter 30s
        server  backend02b-aws-syd 10.0.106.102:80 check maxconn 3000 inter 30s

backend second-backend
        server  backend03a-aws-syd 10.0.105.103:80 check maxconn 3000 inter 30s

backend ab-test-backend
        balance roundrobin
        cookie SITEID insert indirect nocache maxlife 48h

        server backend02a-aws-syd 10.0.105.102:80 weight 25 cookie cookie_first_backend
        server backend02b-aws-syd 10.0.106.102:80 weight 25 cookie cookie_first_backend

        server backend03a-aws-syd 10.0.105.103:80 weight 50 cookie cookie_second_backend

The final HAProxy config should be something like this:

listen  http 127.0.0.1:8080
        maxconn     18000

        # A/B Test ACLs
        acl ab_test_url path_beg /ab-test-path

        use_backend first-backend if { req.cook(SITEID) -m beg cookie_first_backend }
        use_backend second-backend if { req.cook(SITEID) -m beg cookie_second_backend } ab_test_url
        use_backend ab-test-backend if ab_test_url

        default_backend first-backend

backend first-backend
        appsession JSESSIONID len 52 timeout 3h
        cookie SERVERID insert

        balance leastconn
        server  backend02a-aws-syd 10.0.105.102:80 check maxconn 3000 inter 30s
        server  backend02b-aws-syd 10.0.106.102:80 check maxconn 3000 inter 30s

backend second-backend
        server  backend03a-aws-syd 10.0.105.103:80 check maxconn 3000 inter 30s

backend ab-test-backend
        balance roundrobin
        cookie SITEID insert indirect nocache maxlife 48h

        server backend02a-aws-syd 10.0.105.102:80 weight 25 cookie cookie_first_backend
        server backend02b-aws-syd 10.0.106.102:80 weight 25 cookie cookie_first_backend

        server backend03a-aws-syd 10.0.105.103:80 weight 50 cookie cookie_second_backend
comments powered by Disqus