Table of Contents:
- Background
- What are we building?
- Prerequisite
- Define Unleash’s custom activation strategy
- Apply custom strategy to a feature
- Implement gradual rollout activation
- Make the activation sticky
- Implement regional activation
- Conclusion
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
- percent - Percentage to roll out.
- 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
- Create a new custom strategy file “regionalRollout.js”.
- Create a class that implements Unleash’s Strategy class which implements the “RegionalRollout” strategy.
- 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.
- Register the custom strategy class in “index.js"’s “createApp” function.
const app = createApp({ ... customStrategies: [new RegionalRolloutStrategy()] }, undefined, expressApp);
- 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.
-
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.
-
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.
- Install required packages.
npm i request-ip node-iplocate
- 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(); }); }) ...
- 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.
- 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.