SDK Integration

๐Ÿ“˜

If you are using WSGI environment, please make sure to check the WSGI Configuration at the bottom of the guide.

This document provides steps for integrating your Python app via the Hackle Python SDK. SDK Integration is a must in order to use the functions we provide.

  • Step 1: Add Dependencies
  • Step 2: Initialize the SDK
  • Step 3 : A/B test, Feature Flag
  • Step 4 : Send customer event
  • Step5 : Termination

Step 1: Add Dependencies

Think of this step as importing our SDK into your codebase. To use our Hackle services add the following dependency code and import our SDK.

pip install hackle-sdk

Step 2: Initialize the SDK

Once you have imported the dependencies, in order to start using the Hackle SDK you must initialize the SDK. During this step, we get the information needed for SDK integration from the Hackle Server and store it in the SDK.

hackle_client is a class that provides methods for using the SDK functions.

Instantiation

from hackle import hackle

// Enter the SDK Key in the YOUR_SERVER_SDK_KEY. 
hackle_client = hackle.Client(sdk_key='YOUR_SERVER_SDK_KEY')

Instantiate hackle_client by passing the SDK key.
hackle_client periodically synchronizes with the Hackle server as a background task to constantly obtain the necessary information.

โ—๏ธ

hackle_client is a global variable and must be created only once.

hackle_client manages the status internally to evaluate results directly from threads without I/O. It uses additional resources for this. Instead of creating a new instance for every request, it uses an instance that has already been created.

Since hackle_client is created as a Singleton object, it is not recreated even if used with hackle.Client() for use in other functions.

The SDK key can be found in the SDK integration in the dashboard of Hackle service. Copy and use the Server SDK Key.

Step3. A/B test, Feature Flag

A/B test

Before running an A/B test, you must distribute users to the test group and create the logic corresponding to each test group.
User distribution can then be carried out through the hackle SDK.

๐Ÿ“˜

Test Group

The test group refers to the existing one (control group) and improvement one (treatment group) to be tested, and there may be more than one treatment group. You can configure in the dashboard and for information on how to manage test groups, visit see the document A/B setting.

variation

Input experiment key and user identifier to the 'variation()' method to distribute users and receive results. You can implement logic by test group afterwards.

The example code below inputs the experiment key 42.

from hackle.model import HackleUser
# Determine which test group to expose to the user "ae2182e0" in the A/B test with the test key of 42.
# Returns test group A if the test group can not be decided.
user = HackleUser(id='ae2182e0')
variation = hackle_client.variation(experiment_key=42, user=user)

# Logic for Assigned Group
if variation == 'A':
    # Logic for Group A
elif variation == 'B':
    # Logic for Group B

Feature Flag

๐Ÿ“˜

The Feature flag is available for SDK version 2.0.0 or later.

If you are using the feature flag, please apply SDK version 2.0.0 or higher when adding dependencies.

The feature flags are in the ON state and the OFF state. Different features will be set for each state.
When a user accesses a function with a feature flag applied, the user must be able to receive an on or off state. This status determination can be made via the hackle SDK.

isFeatureOn

Input feature key to the 'isFeatureOn()' method to receive status results for the user. Implement logic based on subsequent states.

The example code below input the feature key 42 for user "ae03e1adf".

from hackle.model import HackleUser

# Determine which status to expose to the user in the flag with the feature key of 42.
# Returns off statep if the state can not be decided. 

user = HackleUser(id='ae03e1adf')
feature_on = hackle_client.is_feature_on(feature_key=42, user=user)

# logic for assigned group
if feature_on == True:
    # logic for ON
elif feature_on == False:
		# logic for OFF

// Determine which status to expose to the user in the flag with the feature key of 42.
// Returns off statep if the state can not be decided.  

val isFeatureOn: Boolean = hackleClient.isFeatureOn(42, "ae03e1adf")

if (isFeatureOn) {
    // logic for ON
} else {
    // logic for OFF
}

Check the exposure results

On the [Dashboard Left Menu Bar] - [A/B Test] or [Feature Flag] page, browse to the detail page for the list of exposed A/B tests or feature flags, and click the Real-Time Exposure tab in the middle of the page to view the distribution results from integrated SDK.

Step 4. Send User Events

The Hackle SDK provides the ability to send user events to the hackle.
At each point where changes in user behavior occur, this feature provides meaningful data about user behavior and allows you to analyze user behavior from those collected data.

track

User events can be sent by passing event keys and user identifiers to the 'track()' method. If necessary, when sending user events, numeric values can be put in 'value' to be sent together.

  • You can only put the number type for 'value'

Example

Suppose you have defined an event key called 'purchase' to collect events when the user presses the buy button. At this time, you may want to collect the purchase price together. In this case, you can also receive the purchase amount in 'value'.

from hackle.model import HackleUser, Hackle


# Send "purchase" event from user "ae2182e0"

# Example 1: Send event key only 
event = Hackle.event(key='purchase')
user = HackleUser(id='ae2182e0')
hackle_client.track(event=event, user=user)

# Example 2: Send event keys and numeric values together
event = Hackle.event(key='purchase',
                     value=13200,	# Put the numeric value to send with the event key into the value
                    )
user = HackleUser(id='ae2182e0')
hackle_client.track(event=event, user=user)
// Send "purchase" event from user "ae2182e0"

/* Example 1: Send event key only */
hackleClient.track("purchase", "ae2182e0");

/* Example 2: Send event keys and numeric values together */
Event event = Event.builder("purchase")
    .value(13200) //Put the numeric value to send with the event key into the value
    .build();

hackleClient.track(event, "ae2182e0");
// Send "purchase" event from user "ae2182e0"

/* Example 1: Send event key only */
hackleClient.track("purchase", "ae2182e0")
  
/* Example 2: Send event keys and numeric values together */
val event: Event = Hackle.event("purchase") {
    value(13200) //Put the numeric value to send with the event key into the value
}

hackleClient.track(event, "ae2182e0")

Example 1 only sends event keys; Example 2 shows a case of putting the purchase amount in 'value' to collect the purchase amount together.

Validate the sending of user events

Verify that user events sent by SDK are being collected successfully.
You can check the real-time event collection status by finding events sent to the SDK in the [left menu bar of dashboards] - [Event Management].

Step5. Termination

The 'hackle_client' method must be used to terminate the 'hackle_client' when the application is terminated. This allows the client to return resources that are in use and send the remaining events. If the application is terminated without this process, the event may be missed.

hackle_client.close()

When the application is closed, you must close the hackle_client through the hackle_client.close() method. This allows the client to release the resources it is using and send the remaining tracked events. Tracked Events may be lost if the application is terminated without this process.

โ–ผ What if you using Flask or Django?

@atexit.register can automatically close hackle_client when the application is being closed.

import atexit

@atexit.register
def __exit__():
    hackle_client.close()

WSGI setting

If you are using WSGI, you must initialize 'hackle_client' after the process is forked.

uWSGI

# Step 1: uwsgi.ini setting
enable-threads = True


# Step 2: app.py setting
from hackle import hackle
from uwsgidecorators import postfork

class HackleClient:
    client = None

    def init(self):
        if self.client is None:
            self.client = hackle.Client(sdk_key='YOUR_SERVER_SDK_KEY')


hackle_client = HackleClient()


# Step 3: post_fork setting
@postfork
def post_fork(server, worker):
    hackle_client.init()


# Step 4: hackleClient Usecase
from app import hackle_client

hackle_client.client.variation(...)

gunicorn

# Step 1: app.py setting
from hackle import hackle

class HackleClient:
    client = None

    def init(self):
        if self.client is None:
            self.client = hackle.Client(sdk_key='YOUR_SERVER_SDK_KEY')


hackle_client = HackleClient()

def post_fork(server, worker):
    hackle_client.init()


# Step 2: gunicorn.conf.py setting
from app import post_fork

post_fork = post_fork


# Step 3: hackleClient setting
from app import hackle_client

hackle_client.client.variation(...)

gevent setting

If you change the internal communication method to non-blocking, such as the monkey patch in gvent, you should delay the creation time so that the instance is actually created at the time of the call.

# step 1: gevent monkey patch
import gevent.monkey
gevent.monkey.patch_all()

# step 2: app.py wrapper function
from hackle import hackle

def hackle_wrapper():
    if 'client' not in hackle_wrapper.__dict__:
        hackle_wrapper.client = hackle.Client(sdk_key='YOUR_SERVER_SDK_KEY')
    return hackle_wrapper.client

# step 3: hackleClient case
from app import hackle_wrapper

hackle_wrapper().variation(...)