An open, real-time, scalable and redis based A/B Testing Backend Service.
With flexible and very generic APIs it takes advantage of recent Redis Stack Graph, TimeSeries and JSON to provide a simple although functional alternative for massive scalable A/B testing services including weighted experiments, remote config, events, triggers, etc...
- Experiments API - Scalable A/B Testing Experiments Service (supports weights and trigger based enroll)
- Remote Config API - Scalable Remote Client Configuration Service with A/B Support (similar to Firebase)
- Events API - Scalable User Events Backend Service (integrated with A/B Experiments)
- Callback Schedule API - Flexible HTTP Request Scheduler
- Summary API - Allow Incremental Computation of Scores/Summaries
- Drawer API - Generic Key/Value Property Service
Future Steps:
- Machine Learning Based Experiment Result Analysis using RedisAI
docker run -p 8080:8080 -e BASE_URL -e PORT -e REDIS_URL com.xmppjingle/bjomeliga-docker-boot -ti
Swagger available at: http://localhost:8080/swagger-ui/#/
docker run \
-p 6379:6379 \
-v /home/user/redis_data:/data \
-it \
redislabs/redismod \
--loadmodule /usr/lib/redis/modules/rejson.so \
--loadmodule /usr/lib/redis/modules/redistimeseries.so \
--loadmodule /usr/lib/redis/modules/redisgraph.so \
--dir /data
Scalable A/B Testing Experiments API with trigger based weighted enrolment
curl --location --request POST 'localhost:8080/experiment' \
--header 'Content-Type: application/json' \
--data-raw '{
"goalIds": [
"purchase"
],
"id": "subscription1",
"triggerEventId": "user-plan-screen-view",
"variants": {
"variants": [
{
"id": "red",
"params": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"weight": 20
},
{
"id": "blue",
"params": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
},
"weight": 80
}
]
}
}'
curl --location --request GET 'localhost:8080/experiment/subscription1'
Response
{{
"id": "subscription1",
"variants": {
"variants": [
{
"id": "red",
"weight": 20,
"params": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
}
},
{
"id": "blue",
"weight": 80,
"params": {
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
}
}
]
},
"triggerEventId": "user-plan-screen-view",
"goalIds": [
"purchase"
]
}
Dynamic and Scalable Remote Client Configuration Service (Firebase Replacement)
curl --location --request POST 'localhost:8080/config' \
--header 'Content-Type: application/json' \
--data-raw '{
"params": {
"additionalProp1": "red",
"additionalProp2": "green",
"additionalProp3": "blue"
},
"userId": "pixel"
}'
curl --location --request GET 'localhost:8080/config/pixel'
Response
{
"userId": "pixel",
"params": {
"additionalProp1": "red",
"additionalProp2": "green",
"additionalProp3": "blue"
}
}
Timeseries Event Indexing
curl --location --request POST 'localhost:8080/events' \
--header 'Content-Type: application/json' \
--data-raw '{
"category": "generic",
"emitterId": "rickAstley",
"id": "Rickrolling",
"labels": {
"channel": "youtube",
"prankedBy": "steveTyler"
},
"retention": 900000,
"type": "prank",
"value": 100
}'
Simple and Flexible Summary Service, capable of keeping and maintaining summaries of multiple types of applications. Including: game score boards, product ratings, user ratings, incremental metrics, etc
curl --location --request POST 'localhost:8080/summary' \
--header 'Content-Type: application/json' \
--data-raw '{
"id": "abc",
"transactionId": "3rd",
"metrics": [
{
"id": "abc",
"value": 2
},
{
"id": "bcd",
"value": 4
},
{
"id": "fff",
"value": 1
}
]
}'
curl --location --request GET 'localhost:8080/summary/abc'
Response:
{
"id": "abc",
"metrics": [
{
"id": "abc",
"value": 6,
"count": 3,
"transactionIds": [
"3rd",
"2nd",
"1st"
]
},
{
"id": "bcd",
"value": 12,
"count": 3,
"transactionIds": [
"3rd",
"2nd",
"1st"
]
},
{
"id": "fff",
"value": 1,
"count": 1,
"transactionIds": [
"3rd"
]
}
]
}
Flexible Property Storage
curl --location --request POST 'localhost:8080/drawer/abc' \
--header 'Content-Type: application/json' \
--data-raw '{
"id": "abc",
"values": {
"google" : "123",
"fb": "abc"
}
}'
curl --location --request GET 'localhost:8080/drawer/abc'
Response
{
"id": "abc",
"values": {
"google": "123",
"fb": "abc",
"insta": "1222"
}
}
Most of the Commands are implemented using Lettuce Redis Command Annotation
- enrollEmitterOnExperiment
GRAPH.QUERY experiments :cmd
- fetchParticipantsOnExperiment
GRAPH.QUERY MATCH (u:User)-[:participants]->(:Exp {id: '$experimentId'}) RETURN COUNT(u.id)
- graphQuery
GRAPH.QUERY MERGE (:User {id: '$emitterId' })-[:participants]->(:Exp {id: '$experimentId' }
- pushEvent
TS.ADD :id * :value RETENTION :retention LABELS category :category type :type :labels
- setObject
JSON.SET :id . :json")
- getObject
JSON.GET :id .")
- setPathValue
JSON.SET :id :path :value
- getPathValue
JSON.GET :id :path
- HSET
- HGET / HGETALL
- HEXISTS
- ...