The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
NAME
    Perlbal::Plugin::SessionAffinity - Sane session affinity (sticky
    sessions) for Perlbal

VERSION
    version 0.005

SYNOPSIS
        LOAD SessionAffinity

        CREATE POOL backends
          POOL backends ADD 10.20.20.100
          POOL backends ADD 10.20.20.101
          POOL backends ADD 10.20.20.102

        CREATE SERVICE balancer
          SET listen          = 0.0.0.0:80
          SET role            = reverse_proxy
          SET pool            = backends
          SET persist_client  = on
          SET persist_backend = on
          SET verify_backend  = on
          SET plugins         = sessionaffinity
        ENABLE balancer

DESCRIPTION
    Perlbal doesn't support session affinity (or otherwise known as "sticky
    sessions") out of the box. There is a plugin on CPAN called
    Perlbal::Plugin::StickySessions but there's a few problems with it.

    This plugin should be do a much better job. Go ahead and read why you
    should use this one and how it works.

WHY YOU SHOULD USE IT
    Here are things that are unique in this plugin. I am comparing this with
    the current available session affinity implementation available on CPAN
    (Perlbal::Plugin::StickySessions).

    *   It supports session affinity for all requests

        Unlike the other plugin, this one uses a proper hook that supports
        not just file fetching, but for each and every request.

    *   No patches required

        Unlike the other plugin, that comes with two patches (which were not
        integrated into Perlbal core), this one requires no patches
        whatsoever.

    *   It's up-to-date

        Unlike the other plugin, that still requires a patch that includes a
        hook that was already introduced (which shows it's clearly
        outdated), this plugin is very much up to speed with things.

    *   It's thin and sane

        Unlike the other plugin, which is basically copy-pasted from some
        handling code in Perlbal itself (seriously!), this module contains
        no copy-pasted code, is much smaller and leaner, and is much less
        likely to break between new versions of Perlbal.

    *   No breakage

        Unlike the other plugin, which - after close inspection - seemed
        breakable (to say the least, since connect-aheads don't seem to get
        cleaned up), this plugin uses a completely different method which
        emphasizes correctness and the least intervention with Perlbal
        itself, and keeps Perlbal in charge of the critical operations.

    *   Much less security risk

        Unlike the other plugin, which sets a cookie with the backend ID
        correlating to the backend order in the pool, this plugin uses SHA1
        checksum IDs (with an optionally randomly-created salt) for each
        server, and allows you to change the header name and add a checksum
        salt (whether randomly-created or your own) for the cookie.

        This makes it harder for an attacker to understand what the header
        represents and how many backends exist (since there is no counter).

    *   Features

        Unlike the other plugin, that simply has things hardcoded, this
        plugin allows to change both the header name and the salt used to
        create the ID. By default the salt is off but you can turn it on and
        then either use a randomly-created one or set your own.

HOW DOES IT WORK
  Basic stuff
    Basically, the module creates a SHA1 checksum for each backend node, and
    provides the user with a cookie request. If the user provides that
    cookie in return, it will try and find and provide the user with that
    specific node.

    If the node is no longer in the service's pool, or the cookie matches a
    node that doesn't exist, it will provide the user with a cookie again.

  Advanced stuff
    The plugin sets up dedicated pools and services for each service's node.
    This is required since Perlbal has no way of actually allowing you to
    specify the node a user will go to, only the service. Not to worry, this
    creation is done lazily so it saves as much memory as it can.

    When a user comes in with a cookie of a node that exist in the service's
    pool it will create a pool for it (if one doesn't exist), and a matching
    service for it (if one doesn't exist) and then direct to user to it.

    The check against nodes and pools is done live and not against the
    static configuration file. This means that if you're playing with the
    pools (changing them live, for example), it will still work just fine.

    A new service is created using configurations from the existing service.
    The more interesting details is that reuse is emphasized so no new
    sockets are created and instead this new service uses the already
    existing sockets (along with existing connections) instead of firing new
    ones. It doesn't open a new socket for listening or anything like that.
    This also means your SSL connections work seamlessly. Yes, it's insanely
    cool, I know! :)

ATTRIBUTES
  affinity_cookie_header
    The name of the cookie header for the session.

    Default: X-SERVERID.

  affinity_use_salt
    Whether to use a salt or not when calculating SHA1 IDs.

        # both are equal
        affinity_use_salt = 1
        affinity_use_salt = yes

        # opposite meaning
        affinity_use_salt = 0
        affinity_use_salt = no

    Default: no.

  affinity_salt
    The salt that is used to create the backend's SHA1 IDs.

    Default: the following code is run when you load
    Perlbal::Plugin::SessionAffinity to create the salt on start up:

        join q{}, map { $_ = rand 999; s/\.//; $_ } 1 .. 10;

    If you want predictability with salt, you can override it as such:

        affinity_salt = helloworld

        # now the calculation will be:
        my $sha1 = sha1hex( $salt . $ip . $port );

SUBROUTINES/METHODS
  register
    Registers our events.

  unregister
    Unregister our hooks and setters events.

  get_ip_port
    Parses a request's cookies and finds the specific cookie relating to
    session affinity and get the backend details via the ID in the cookie.

  find_backend_by_id
    Given a SHA1 ID, find the correct backend to which it belongs.

  create_id
    Creates a SHA1 checksum ID using Digest::SHA. The checksum is composed
    of the IP, port and salt. If you want to have more predictability, you
    can provide a salt of 0 or "string" and then the checksum would be
    predictable.

DEPENDENCIES
  Perlbal
    Obviously.

  CGI::Cookies
    To parse and create cookies.

  Digest::SHA
    To provide a SHA1 checksum.

SEE ALSO
  Perlbal::Plugin::StickySessions
AUTHOR
    Sawyer X <xsawyerx@cpan.org>

COPYRIGHT AND LICENSE
    This software is copyright (c) 2012 by Sawyer X.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.