eXtended Attribute Value Pairs - XAVP

Proposal and prototype implementation by:
   Daniel-Constantin Mierla <miconda [at] gmail (dot) com>

CODE IN GIT REPOSITORY BRANCH: daniel/xavp

Purpose

It is not a replacement for current AVP implementation, but a new feature (a new data structure available in code and config, similar to pseudo-variable like: sahred variable $shv(…), hash table variable $sht(…), private variable $var(…)). Developers and script writers can decide which one to use: AVP or XAVP.

Primary goals:

  • ability to store more value types, not only integer and string
  • simplify addressing by using only string name for attributes
  • possibility to optimise the search by creating lists of xavp lists
  • availability in config file for xavp with string or integer value (expression interpreter supports only these values) via PV module
  • availability in stateful processing, during the lifetime of transaction

Internal design

  • one master list stored in core
  • internal integer id to optimise the search in list (hash value over the string name)
  • name stored as str and null terminated (can use regexp over it)
  • string values stored as str and null terminated
  • other types of value that can be used internally (not in config)
    • long
    • long long
    • time
    • xavp (or list of xavp)
    • generic data structure

Data types definition

struct _sr_xavp;
 
typedef enum {
	SR_XTYPE_NULL=0,
	SR_XTYPE_INT,
	SR_XTYPE_STR,
	SR_XTYPE_TIME,
	SR_XTYPE_LONG,
	SR_XTYPE_LLONG,
	SR_XTYPE_XAVP,
	SR_XTYPE_DATA
} sr_xtype_t;
 
typedef void (*sr_xavp_sfree_f)(void *d);
typedef void (*sr_data_free_f)(void *d, sr_xavp_sfree_f sfree);
 
typedef struct _sr_data {
	void *p;
	sr_data_free_f pfree;
} sr_data_t;
 
typedef struct _sr_xval {
	sr_xtype_t type;
	union {
		int i;
		str s;                 /* replicated */
		time_t t;
		long l;
		long long ll;
		struct _sr_xavp *xavp; /* must be given in shm */
		sr_data_t *data;       /* must be given in shm */
	} v;
} sr_xval_t;
 
typedef struct _sr_xavp {
	unsigned int id;
	str name;
	sr_xval_t val;
	struct _sr_xavp *next;
} sr_xavp_t;

Generic data value

It allows to store any type of data built in shared memory. A structure of sr_data_t has to be allocated in shared memory and filled with two fields:

  • p - pointer to your data - the data must be also allocated in shared memory
  • pfree - function to free the data - it takes two functions as parameter - void pointer to data and a free function. The free function is provided by the core API, because depending on usage case it might be shm_free() or shm_free_unsafe()

Example of usage (c-like pseudo-code):

#include <xavp.h>
 
struct _test {
  int a;
  char *s;
} *d;
 
sr_data_t *xdata;
sr_xval_t xval;
str xname = {"my-xavp", 7};
 
void test_free(void *d, sr_xavp_sfree_f sfree)
{
  sfree(d->s);
  sfree(d);
}
 
data = shm_malloc(sizeof(sr_data_t));
 
d = shm_malloc(sizeof(struct _test));
d->s = shm_malloc(5);
 
d->a = 10;
strcpy(d->s, "test");
 
xdata->p = (void*)d;
xdata->pfree = test_free;
 
xval.type = SR_XTYPE_DATA;
xval.v.data = xdata;
xavp_add(&xname, &xval, NULL);

NOTE: this avp printed to xlog will return a string: «data:value of the pointer to data» (e.g., «data:0x973937393»), good for debug purposes.

This type of value is intended for internal usage, one of the modules that needs it immediately being dialog.

XAVP data value

This allow to store within an xavp another xavp or list of xavps.

Example of usage (c-like pseudo-code):

#include <xavp.h>
 
sr_avp_t *list;
sr_xval_t xval;
str xname;
 
// build the list of xavp
list = NULL;
xval.type = SR_XTYPE_INT;
xval.v.i = 10;
xname.s = "avp1"; xname.len = 4;
xavp_add(&xname, &xval, &list);
xval.type = SR_XTYPE_INT;
xval.v.i = 20;
xname.s = "avp2"; xname.len = 4;
xavp_add(&xname, &xval, &list);
 
// add to default list the xavp having the value the above list
xval.type = SR_XTYPE_XAVP;
xval.v.xavp = list;
xname.s = "avp3"; xname.len = 4;
xavp_add(&xname, &xval, NULL);
 
// add to default list another xavp
xval.type = SR_XTYPE_INT;
xval.v.i = 40;
xname.s = "avp4"; xname.len = 4;
xavp_add(&xname, &xval, NULL);

The content of xavp default list is now:

(avp4, 40), (avp3, ((avp2, 20), (avp1, 10)))

These types of xavp can be used directly in configuration file, via PV module, as long as inner xavp has integer or string value. To address them, the format is:

$xavp(avp3=>avp1)=10; # add new xavp 'avp3' with value being xavp 'avp1' with value 10
$xavp(avp3[0]=>avp2)=20; # add to first xavp 'avp3' a new xavp 'avp2' with value 20

IMPORTANT: in assignments, if index is missing, then a new xavp is added. If index is provided, like above [0], then an inner xavp is added to the xavp at respective position.

When getting the value, no index or [0] point to same xavp - first one with respective name.

Benefits

  • grouping of value based on purpose
    • for example, avps for serial forking:
$xavp(sf=>uri)="sip:10.10.10.10";
$xavp(sf[0]=>fr_timer)=10;
$xavp(sf[0]=>fr_inv_timer)=15;
$xavp(sf[0]=>headers)="X-CustomerID: 1234\r\n";
 
$xavp(sf=>uri)="sip:10.10.10.11";
$xavp(sf[0]=>fr_timer)=20;
$xavp(sf[0]=>fr_inv_timer)=35;
 
$xavp(sf=>uri)="sip:10.10.10.12";
$xavp(sf[0]=>fr_timer)=10;
$xavp(sf[0]=>fr_inv_timer)=15;
$xavp(sf[0]=>headers)="X-CustomerID: pw45\r\n";

For second destination, there is no extra header to be added. With current avp model, a dummy value must be set, otherwise the value from third destination will be used.

  • faster lookup
    • with current avp, getting to last avp (as in above example) would require 11 steps in iteration. With xavp it takes 7 steps. When dealing with lot of avps, the improvement is more significant if the grouping is used.
    • from inside the code, the improvement is more visible, as the reference to inner xavp list can be kept at first lookup.
  • further improvements to speed and flexibility by storing generic data structures
    • example: serial forking function can build now the info in a shared memory structure
  • less exposure the name conflicts by grouping
    • modules can group their internal AVPs under a xavp, so other xavps with same name in global list won't affect the module functionality. Say dispatcher groups its avps under "dispatcher". Inside that list it can have xavp with names: dst, grp, cnt. In the global list or other sublists can be other avps with name 'dst', without any interference.
  • reduce number of parameters for some modules
    • a module can reserve now only one name for xavp in default list. Inner xavp can have predefined names, without the risk for overlapping with other modules or config
    • dispatcher module can reserve xavp name "dispatcher" via modparam, while the names for AVPs dst, cnt, grp can be predefined and documented in readme.

Use cases

Couple of use cases I have in mind now:

  • acc module for multi-leg accounting - grouping will improve speed and remove the wrong behaviour when a value is not set in one leg - null is set for the right leg, not for the last one.
  • dispatcher module - grouping
  • dialog module - store using generic data structure the dailog context pointer (get rid of current hack that adds a fake callback structure)
  • serial forking functions
  • possible in lcr or carrierroute
  • possible in utils, to store each line of http response in a grouped xavp list.

Observations

  • it is not a replacement to existing AVPs, but an addition, a new class of pseudo-variables

Navigation

Wiki

Other

Personal Tools