Table of Contents:

Background

Previous post, we learn about feature toggle and A/B testing using Unleash. It covers the basic setup of a feature toggle and how to use it in the ReactJs application.

In this post, we will use Unleash’s advanced feature to build our activation strategy using NodeJs. This custom activation strategy includes “Feature rollout”, “Regional rollout”, and “Session Stickiness”.

What are we building?

We’re going to build a custom activation strategy called “Regional Rollout”. This activation strategy can roll out a feature within specific regions (continents). For example, roll out “Feature.A” to 50% of the users from Asia.

Unleash’s custom activation strategy can be built into the backend application in case you query the toggle from the backend. Another option, which is the one we will implement, is to build it into Unleash’s proxy server using NodeJs.

Prerequisite

Following this post to set up Unleash API server, proxy server, ReactJs UI application, and a feature toggle.

Define Unleash’s custom activation strategy

On Unleash’s admin UI, click on the “Configure” menu, then select the “Strategies” menu.

Click the “New Strategy” button.

Fill in the custom strategy information.

Custom strategy information

Add custom strategy parameters. These are parameters that we can configure when we apply our custom strategy to a feature.

To add a new parameter, click the “Add parameter” button.

Custom strategy parameters

Parameters

  1. percent - Percentage to roll out.
  2. regions - Regions to roll out. Value could be “Africa | Antarctica | Asia | Europa | North America | Oceania | South America”

Click the “Create strategy” button.

Created custom strategy

Apply custom strategy to a feature

Select an existing feature.

Click the “Add strategy” button and select our custom strategy “RegionalRollout”.

Select RegionalRollout strategy

Fill in the “percent” and “regions” parameters.

RegionalRollout Parameters

Click the “Save strategy” button.

Remove the “Standard” strategy (if exists).

Remove standard strategy

RegionalRollout custom strategy and metrics

The feature is ready to be consumed. Next, we will need to implement the custom strategy code.

Implement gradual rollout activation

Let’s get to the coding part. First, we will implement the gradual rollout activation. You can start from the previous post or using this repository.

git clone https://github.com/pongsatt/unleash-proxy.git
  1. Create a new custom strategy file “regionalRollout.js”.
  2. Create a class that implements Unleash’s Strategy class which implements the “RegionalRollout” strategy.
  3. Override the “isEnabled” method. Use the “parameters.percent” to determine the percentage of the user to enable the feature.
const { Strategy } = require('unleash-client');

class RegionalRolloutStrategy extends Strategy {
  constructor() {
    super('RegionalRollout');
  }

  isEnabled(parameters, context) {
    const result = Math.random() < parameters.percent / 100;
    console.log('RegionalRollout - evaluate rollout: ', parameters.percent, ' result: ', result);
    return result;
  }
}

module.exports = RegionalRolloutStrategy

Code explanation

We random a number between 0 and 1 using Math.random function.

If the random number falls between 0 and the given rollout percentage, that user will get feature enabled.

This assumes that Math.random results in uniform distribution.

  1. Register the custom strategy class in “index.js"’s “createApp” function.
    const app = createApp({
       ...
       customStrategies: [new RegionalRolloutStrategy()]
     }, undefined, expressApp);
    
  2. Start the proxy using the command.
    npm start
    

Test the strategy using the demo app from the previous post or use this git repository.

git clone https://github.com/pongsatt/unleash-react-demo.git
cd unleash-react-demo
npm start

When you refresh the browser, you should see that the feature is enabled roughly 50% of the time.

50% feature enabled

See the complete code here.

Make the activation sticky

The problem with the current implementation is that when a user opens the application multiple times, they will randomly see the feature. This inconsistency could cause confusion. In the real world, we need to ensure that a user sees a feature consistently across a session. We call this “Stickiness”.

In this demo, we will make a sticky session using sessionId provided by Unleash’s client.

  1. Update “regionalRollout.js” and add “withStickiness” function.

    ...
     #CACHE = {};
    
     withStickiness(parameters, context, getValue) {
       const key = context['sessionId'];
    
       if (!key) {
         // when sessionId is missing from the context, evaluate the function
         return getValue();
       }
    
       // retrieve previous value from cache
       const stickyResult = this.#CACHE[key];
    
       if (stickyResult !== undefined) {
         // in the cache
         console.log('RegionRollout - evaluate stickiness: ', parameters.stickiness, ' stickyResult: ', stickyResult);
         return stickyResult;
       }
    
       // not in the cache, evaluate the function
       const result = getValue();
    
       // store value in cache
       this.#CACHE[key] = result;
    
       return result;
     }
     ...
    

    Note

    This function use a private object “CACHE” to store previous result using “sessionId” from “context” object as a key.

  2. Update “isEnabled” function to use “withStickiness” function.

    ...
    
    isEnabled(parameters, context) {
     return this.withStickiness(parameters, context, () => {
       const result = Math.random() < parameters.percent / 100;
       console.log('RegionalRollout - evaluate rollout: ', parameters.percent, ' result: ', result);
       return result;
     });
    
     ...
    
    }
    

Note

We send the evaluation function as the last parameter to “withStickiness” function. This function will be called once per session (when the cache missed).

Restart proxy server and go back to the demo app’s browser. When refresh the browser, this time, the result will not randomly changed as before.

See the complete code for this step here.

Implement regional activation

In addition to the gradual roll out with stickiness, we will implement the code to enable our features from client’s region. For example, only users from “Asia” would see the feature.

To implement this logic, we will get client’s IP address using package “request-ip” then use package “node-iplocate” to determine user’s region from the IP.

  1. Install required packages.
npm i request-ip node-iplocate
  1. Update “index.js” to add 2 middleware.
    ...
    
     const requestIp = require('request-ip');
     const iplocate = require("node-iplocate");
    
     // extract request's IP middleware
     expressApp.use(requestIp.mw())
    
     // retrieve region from IP address
     expressApp.use((req, res, next) => {
         var ip = req.clientIp; // ip from requestIp's middleware
    
         iplocate(ip, null, function (err, results) {
             if (err != null) {
                 console.error(err);
                 return next();
             }
             // add result to request's query so it get propagate to Unleash's context
             req.query = { ...req.query, ...results };
             next();
         });
     })
     ...
    
  2. Update “regionalRollout.js” and add “isRegionEnabled” function.
    ...
    
     isRegionEnabled(parameters, context) {
       const regionsParam = parameters.regions;
    
       if (!regionsParam || regionsParam.length === 0) {
         return true;
       }
    
       // Unleash client create context.properties based on request's query
       const result = regionsParam.includes(context.properties.continent);
       console.log('RegionRollout - evaluate region: ', regionsParam, ' result: ', result, ' from region: ', context.properties.continent);
       return result;
     }
    
    ...
    

Note

context.properties.continent is determined by “iplocate” function. If it is one of the regions configured, the feature will be enabled to the user.

  1. Update “isEnabled” function to use “isRegionEnabled” function.
    ...
    
    return this.withStickiness(parameters, context, () => {
    
       if (!this.isRegionEnabled(parameters, context)) {
         return false;
       }
    
       ...
     });
    

Restart the proxy server. See the complete code here.

To get the real IP address, our front end will need to call the proxy server through the public internet. We will use the Cloudflare tunnel service to expose our local proxy server to the internet.

Install the “cloudflared” cli using this instruction.

Run this command to tunnel our local proxy server.

cloudflared tunnel --url http://localhost:3000

Wait until it’s done and copy the URL you see in the result.

Example result URL from Cloudflare tunnel service

Open ReactJs Demo App, “index.tsx” and replace the local proxy URL (http://localhost:3000) with the URL you got from the Cloudflare service.

...

const config = {
  url: 'https://routine-clarke-modular-wooden.trycloudflare.com/proxy', // use your own url
  
  ...
};

...

In the proxy server log, you should see something like this.

RegionalRollout - evaluate region:  Asia,North America  result:  true  from region:  Asia

Note

My IP locates in “Asia” so this feature is enabled to me since it is in the list “Asia,North America”.


Conclusion

In this post, you learn Unleash’s advanced concept to create a custom activation strategy. How to write code to roll out a feature with a given percentage, then make the result sticky and lastly how to enable the feature based on the user’s region. This can be useful when you need to implement the strategy by yourself.

I hope you learn something or get something useful from this post.