Superposition, a Context Aware Configuration Management System
Facilitate safe & flexible rollout of config changes with granular configuration based on contextual data, A/B testing, controlled rollout and rollback mechanisms
[default-config]
per_km_rate = { "value" = 20.0, "schema" = { "type" = "number" } }
surge_factor = { "value" = 0.0, "schema" = { "type" = "number" } }
[dimensions]
city = { schema = { "type" = "string", "enum" = ["Bangalore", "Delhi"] }}
vehicle_type = { schema = { "type" = "string", "enum" = ["auto", "cab", "bike"] }}
hour_of_day = { schema = { "type" = "integer", "minimum" = 0, "maximum" = 23 }}
[context."$vehicle_type == 'cab'"]
per_km_rate = 25.0
[context."$vehicle_type == 'bike'"]
per_km_rate = 15.0
[context."$city == 'Bangalore' && $vehicle_type == 'auto'"]
per_km_rate = 22.0
[context."$city == 'Delhi' && $hour_of_day >= 12 && $hour_of_day <= 18"]
surge_factor = 5.0
Typed
Enforces strict type safety and custom validation functions, preventing incorrect configuration values from being stored.
Experiments
Supports multi-variate experiments for data driven decision making and safe config releases.
Multi-tenant
Multi-tenant ready, allowing multiple teams within an organization to manage configurations with isolation in a single instance.
UI-Support
Out of the box powerful UI to perform all configuration management activities
Rich API
All Superposition actions are backed by API end-points, enabling teams programmatic configuration changes
Performant Client
Completely in memory lookups for config with a performant async client. Clients available in Java, Python, Javascript, Go, Rust, Haskell and more...
use cac_client;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let client = cac_client::CLIENT_FACTORY
.create_client(
"dev".to_string(),
std::time::Duration::new(10, 0),
"http://localhost:8080".into(),
)
.await
.unwrap();
actix_web::rt::spawn(client.clone().run_polling_updates());
let resolved_config = client
.get_resolved_config(
serde_json::json!({"country": "india"})
.as_object()
.unwrap()
.clone(),
None,
cac_client::MergeStrategy::MERGE,
)
.await
.unwrap();
println!("{resolved_config:?}");
Ok(())
}
module Main (main) where
import Client as CAC
import Control.Concurrent
import Prelude
main :: IO ()
main = do
CAC.createCacClient "dev" 10 "http://localhost:8080" >>= case
Left err -> putStrLn err
Right _ -> pure ()
threadId <- forkOS (CAC.cacStartPolling "dev")
CAC.getCacClient "dev" >>= case
Left err -> putStrLn err
Right client -> do
resolvedConfig <- CAC.getResolvedConfig
client
"{"country": "India"}" $
Nothing
pure ()
package main
import (
"fmt"
"github.com/juspay/superposition/clients/go/cacclient"
)
func main() {
tenant := "dev"
pollingFrequency := 1
cacHostName :=
client, error := cacclient.NewCacClient(tenant, pollingFrequency, cacHostName)
if error != nil {
fmt.Println(error)
}
fmt.Println("Resolved Config => ", client.GetResolvedConfig(map[string]string{"country": "india"}, nil, cacclient.MERGE))
}
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import cac_client.CacClient;
import jnr.ffi.Pointer;
public class Application {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(1);
try {
CacClient client = new CacClient();
int newClient = client.cacNewClient(tenant, 1, "http://localhost:8080");
Thread pollingThread = new Thread(() -> {
client.startPollingUpdate(tenant);
});
Pointer clientPtr = client.getCacClient(tenant);
String resolvedConfig = client.getResolvedConfig(clientPtr, "{"clientId": "abcd"}", null, "MERGE");
System.out.println("Resolved Config: " + resolvedConfig);
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
import {CacReader} from "superposition/clients/js/index.js";
import config from "./config.json";
const Cac = new CacReader(config);
const resolvedConfig = Cac.evaluateConfig({"country": "india"});
import {CacClient, MergeStrategy} from 'cac_client';
let tenantName: string = "dev";
let superpositionHost: string = "http://localhost:8080";
let pollingFrequency: number = 1;
let CACClient = new CacClient(tenantName, pollingFrequency, superpositionHost);
CACClient.startPollingUpdate();
let resolvedConfig = CACClient.getResolvedConfig({"country" : "india"}, undefined, MergeStrategy.MERGE);
console.log(resolvedConfig);
from cacclient import CacClient , MergeStrategy
def main():
tenant_name = "dev"
polling_frequency = 1
cac_host_name = "http://localhost:8080"
cac_client = CacClient(tenant_name, polling_frequency, cac_host_name)
cac_client.start_cac_polling_update()
resolved_config = cac_client.get_resolved_config(dict({'country': 'India'}), MergeStrategy.MERGE)
print(resolved_config)
if __name__=="__main__":
main()