Skip to content

Latest commit

 

History

History
231 lines (142 loc) · 14.4 KB

creating-client.md

File metadata and controls

231 lines (142 loc) · 14.4 KB

Superposition and her clients for CAC and Experimentation are written in rust. Since servers/programs implemented in a particular language need libraries written in their own language, we try to support as many languages as possible to make integrations easier. Some other reasons for providing language specific clients:

  • Many languages have a paradigm like OOP or Functional and forcing them to adopt another paradigm may make it confusing to programmers
  • Some engineers may get the implementation of the client wrong and may impact performance or memory usage
  • It makes it faster to work with a client in a particular language

If you want to add support for a programming language, read on.

Contributing Guidelines

Thank you for considering adding support for a new language in superposition clients! The following conditions must be met for your PR to be reviewed:

  • Your client code should be formatted (You can mention the formatter used in the PR)
  • Unit tests of your client code
  • Documentation of your client functions is necessary (See this for an example)
  • An example implementation of the client calling Superposition, loading the configurations and calling all exposed functions of your implemented client

Context Aware Config

Understanding the C structs and functions

All C structs and functions are generated automatically when the cac-client written in rust is compiled. Superposition uses the crate cbindgen to do this. The compiler generates a .h header file and an object file specific to the target platform, for example a .dll for windows or .so for linux. Some tips for writing an FFI/ABI to these files:

  • All memory operations should be done by the rust segment of the code. You should always free memory for strings and clients using the inbuilt free_* functions
  • You don't need to store the client on your implementation's end. Always use get_cac_client to get a pointer to a client to pass to other functions
  • Remember, a client is created for a particular tenant
  • Check the header files in the headers directory for further documentation
  • Your client implementation should expect the object file that it dynamically links to be present in the same directory
  • Try to use nix so that the process of dynamic linking dependencies becomes easier. You have to support non-nix setups as well

struct Arc_Client

This is a pointer to Arc<Client> used by rust to manage asynchronous access to the Client struct. Most functions expect Arc_Client to know what client to operate on

int cac_last_error_length(void)

Get the length of the error message that was produced during an operation.

Returns greater than 0 if an error has occurred Returns 0 if no error

const char *cac_last_error_message(void)

This function returns a character pointer to the last error message produced by an operation. Use cac_last_error_length to get its length to initialize a string if your language requires it.

returns null pointer if no error is present

void cac_free_string(char *s)

This function takes a character pointer as an arg and frees memory allocated to it. Use it whenever you want to free a string returned by function of cac-client so that the rust side can continue to handle memory management, preventing any unwanted memory leaks

int cac_new_client(const char *tenant, unsigned long update_frequency, const char *hostname)

A function that takes a tenant name as string, the update frequency and the hostname of the Superposition Server as arguments and creates a client that is internally managed by rust. Use cac_get_client to get a reference to this client

Returns 0 if client was successfully initialized Returns 1 if an error occurred, use cac_last_error_message to get the error

void cac_start_polling_update(const char *tenant)

Start polling the superposition server for updates for the given tenant

void cac_free_client(struct Arc_Client *ptr)

Free the memory allocated to the cac client. Always call this function instead of the implementing language's free memory function

struct Arc_Client *cac_get_client(const char *tenant)

A function to fetch a previously created client by using the tenant argument. Use cac_new_client to create a new client.

returns a null pointer if an error occurred. Use cac_last_error_message to get the error

returns a pointer to Arc_Client that can be used to perform other client operations

const char *cac_get_last_modified(struct Arc_Client *client)

A function to get the last modified time of a tenant's config.

returns a null pointer if an error occurred. Use cac_last_error_message to get the error

returns a string that represents the last modified time of your tenant's configs

const char *cac_get_config(struct Arc_Client *client, const char *filter_query, const char *filter_prefix)

A function that returns the config for your tenant. Takes the client, a context (filter_query) and a config prefix to filter on. All configs that have the prefix will be returned.

returns a null pointer if an error occurred. Use cac_last_error_message to get the error

returns a string that represents the config of your tenant based on your client and filters

const char *cac_get_resolved_config(struct Arc_Client *client, const char *query, const char *filter_keys, const char *merge_strategy)

Does the same thing as cac_get_config but does not return the entire config, rather the config filtered on the keys provided as arguments

returns a null pointer if an error occurred. Use cac_last_error_message to get the error

returns a string that represents the resolved config of your tenant based on your client and filters

const char *cac_get_default_config(struct Arc_Client *client, const char *filter_keys)

Does the same thing as cac_get_config but returns the entire default config, filtered on the config keys provided

returns a null pointer if an error occurred. Use cac_last_error_message to get the error

returns a string that represents the default config of your tenant based on your client and filters

Testing with an Example

Checkout the examples directory to understand how to create examples for cac_client_integration_example

Experimentation

Understanding the C structs and functions

All C structs and functions are generated automatically when the exp-client written in rust is compiled. Superposition uses the crate cbindgen to do this. The compiler generates a .h header file and an object file specific to the target platform, for example a .dll for windows or .so for linux. Some tips for writing an FFI/ABI to these files:

  • All memory operations should be done by the rust segment of the code. You should always free memory for strings and clients using the inbuilt free_* functions
  • You don't need to store the client on your implementation's end. Always use get_exp_client to get a pointer to a client to pass to other functions
  • Remember, a client is created for a particular tenant
  • Check the header files in the headers directory for further documentation
  • Your client implementation should expect the object file that it dynamically links to be present in the same directory
  • Try to use nix so that the process of dynamic linking dependencies becomes easier. You have to support non-nix setups as well

struct Arc_Client

This is a pointer to Arc<Client> used by rust to manage asynchronous access to the Client struct. Most functions expect Arc_Client to know what client to operate on

int expt_last_error_length(void)

Get the length of the error message that was produced during an operation.

Returns greater than 0 if an error has occurred Returns 0 if no error

const char *expt_last_error_message(void)

This function returns a character pointer to the last error message produced by an operation. Use cac_last_error_length to get its length to initialize a string if your language requires it.

returns null pointer if no error is present

void expt_free_string(char *s)

This function takes a character pointer as an arg and frees memory allocated to it. Use it whenever you want to free a string returned by function of exp-client so that the rust side can continue to handle memory management, preventing any unwanted memory leaks

int expt_new_client(const char *tenant, unsigned long update_frequency, const char *hostname)

A function that takes a tenant name as string, the update frequency and the hostname of the Superposition Server as arguments and creates a client that is internally managed by rust. Use expt_get_client to get a reference to this client

Returns 0 if client was successfully initialized Returns 1 if an error occurred, use expt_last_error_message to get the error

void expt_start_polling_update(const char *tenant)

Start polling the superposition server for updates for the given tenant

void expt_free_client(struct Arc_Client *ptr)

Free the memory allocated to the cac client. Always call this function instead of the implementing language's free memory function

struct Arc_Client *expt_get_client(const char *tenant)

A function to fetch a previously created client by using the tenant argument. Use expt_new_client to create a new client.

returns a null pointer if an error occurred. Use expt_last_error_message to get the error

returns a pointer to Arc_Client that can be used to perform other client operations

char *expt_get_applicable_variant(struct Arc_Client *client, const char *c_context, short toss)

get the experiments that apply to a given context c_context. It also takes a number toss between 0 - 100 that is used to assign a variant IDs

returns null pointer if no variant is found returns a string formatted array of variant IDs that match the parameters passed

char *expt_get_satisfied_experiments(struct Arc_Client *client, const char *c_context, const char *filter_prefix)

get the experiments that apply to a given context c_context. It also filters on config key prefix

returns null pointer if no variant is found returns a string formatted array of experiments that match the parameters passed

char *expt_get_filtered_satisfied_experiments(struct Arc_Client *client, const char *c_context, const char *filter_prefix)

get the experiments that apply to a given context c_context. It also filters on config key prefix

returns null pointer if no variant is found returns a string formatted array of experiments that match the parameters passed

char *expt_get_running_experiments(struct Arc_Client *client)

get all currently running experiments

returns null pointer if no variant is found returns a string formatted array of experiments that match the parameters passed

Testing with an Example

Checkout the examples directory to understand how to create examples for exp_client_integration_example