Skip to content
Python library to toggle on/off features using a backend as data store
Python Makefile
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
hanoi
tests
.gitignore
.travis.yml
Makefile
README.md
benchmark.py
foo.py
setup.py

README.md

Hanoi

Version Downloads Build Status

Beta version.

Hanoi is a port of rollout gem from James Golick and Eric Rafaloff.

The idea behind it is to ease a simple way to enable/disable functionalities to a subset of users in a production (or any other) environment. This is in general handy upon deploying a new version of your product, in order to test the new functionalities in a subset of users. It could be useful as well as ACL mechanism.

Use cases

  • Enable a functionality globally (for every user using the system).
  • Enable a functionality to a percentage of users via Cyclic Redundancy Check(user identifier) % 100.
  • Enable a functionality to a percentage of users via a predefined rule using a Regular Expression.
  • Enable a functionality to specific user identifiers.
  • Variants support (new in 0.0.4): inspired in feature by Esty and sixpack, hanoi now supports variant for providing to users different options for an experiment.

Scenarios

  • Check if the functionality A is enabled for user B.
rollout.is_enabled('A', B)
  • Pre-check while executing a function (functionality A) that user B (received as parameter in the function call) is granted permissions.
@roll.check('A', 1)  # Check if user B (argument 1) is granted permissions to execute A
def execute_a_logic(user):
    pass  # Business logic here

execute_a_logic(B)
  • Pre-check to ensure user B (attached to the process/thread) can execute functionality A (ACL mechanism). Be aware if your environment requires thread-safe behavior.
rollout.set_current_id(B)

@roll.check('A')  # Check if the current user (B) is granted permissions to execute A
def execute_a_logic():
    pass  # Business logic here

execute_a_logic()
  • Retrieving a valid variant for an experiment A for user B
variant = rollout.variant('A', B)

Examples of usage

# Setting the configuration
# -------------------------

# bootstrap.py

import re

import hanoi

rollout = hanoi.Rollout(hanoi.RedisHighPerfBackEnd())

rollout.add_func(
    'cdc_on',               # Functionality name (CDC on)
    percentage=80,          # Percentage for toggle ON
    variants=('foo', 'bar') # Valid variants in case of toggle ON
)

rollout.register('cdc_on', '447568110000')  # Register a specific user

rollout.register('cdc_on', re.compile(r'01$')  # Register a subset of users

def get_rollout():
    return rollout


# Using Rollout
# -------------

# service.py

import bootstrap

roll = bootstrap.get_rollout()

# Define the current user (kind of ThreadLocal)
roll.set_current_id('444401')

@roll.check('cdc_on')  # Check if the current user is registerd to `cdc_on`
def execute_cdc_logic():
    pass

# Based on the rules defined in bootstrap.py,
# the decorator will not allow the function execution,
# as zlib.crc32('444401') % 100 = 89, and the predefined percentage is 80
execute_cdc_logic()


# Check if it's enabled `cdc_on` to the user `44488`
# Based on the rules defined in bootstrap.py, it will return False
print roll.is_enabled('cdc_on', '44488')


# Check if it's enabled `cdc_on` to the second parameter
@roll.check('cdc_on', 2)
def execute_again_cdc_logic(parameter, user):
    return "I'm in"

# Based on the rules defined in bootstrap.py,
# the decorator will allow the function execution, as 443301 matches the reg expr.
print execute_again_cdc_logic('foo', '443301')

# Get a valid variant for user `443301`
print roll.variant('cdc_on', '443301')

BackEnds

Currently there're three implemented BackEnds:

  • MemoryBackEnd: useful for development or where you have predefined rules and don't need to share information between different processes.

  • RedisBackEnd: useful for distributed environments, where you need to easily update functionalities, rules or users attached to a specific functionality.

  • RedisHighPerfBackEnd: useful for distributed environments and high performance. It uses SET to store users and reduce significantly the time to verify an user. Check and execute benchmark.py file for details.

TODO before BETA

  • Finish unit testing Rollout class
  • Finish unit testing Function class
  • Implement Redis BackEnd
  • Finish unit testing RedisBackEnd class
  • Document the different use cases and when to use both backends
  • Integrate travis.ci
  • Upload a beta version to pypi
  • Think about a cooler name 😉. We'll stay with hanoi
  • Write a blog post
You can’t perform that action at this time.