How To Use Database “Partitions” in OpenSIPS

pie-chart-149727_1280

Intro

Inside the OpenSIPS bubble, the notion of a “partition” was first introduced in OpenSIPS 2.1, roughly around March 2015. But what exactly are they and how do we use them?

In short, they allow a single OpenSIPS module to be able to work with data from multiple database sources. In other words, in OpenSIPS terminology, a “partition” is synonymous to “one of two or more databases”.

The following modules currently include support for database partitions: dialplan, dispatcher and drouting.

Below are two example VoIP scenarios, one which is more elegantly solved using OpenSIPS partitions and another one which actually depends on them.

Example 1: multiple tables

Suppose we have the following prefix matching requirements for our VoIP platform:

  • lookup inbound CLDs (DID matching)
  • lookup outbound CLDs (aka LCR – service provider side)
  • another outbound CLD lookup, for billing purposes (customer side)

Choosing the module is easy. Heavily equipped with both memory layout and lookup speed optimizations, the “drouting” module is a no-brainer choice here. But how should we provision all the above data?

We could definitely do it using the already existing grouping mechanism behind the “groupid” column of the “dr_rules” table. For example, we can assign groups 10-19 for inbound, 20-29 for outbound, and 30-39 for customer billing. With the help of tools like “m4”, we can even make the opensips.cfg.m4 file quite readable:

(opensips.cfg.m4)

do_routing("DR_DIDS_US", "C", , "$avp(did_attr)");
do_routing("DR_DIDS_UK", "C", , "$avp(did_attr)");
...
do_routing("DR_LCR_NEW", "F", , "$avp(lcr_attr)");
do_routing("DR_LCR_LEGACY", "F", , "$avp(lcr_attr)");
...
do_routing("DR_BILLING", "CF", , "$avp(billing_attr)");

(local.m4)

define(`DR_DIDS_US', `10')
define(`DR_DIDS_UK', `11')
define(`DR_LCR_NEW', `20')
...

How would a partition-based solution look like? Well, this time we are actually splitting the data into three tables, one for each of our lookups. Here is how the module parameters for drouting would look like:

modparam("drouting", "db_partitions_url",
"mysql://admin:admin@localhost/opensips")

The “dr_partitions” table (several columns omitted for simplicity):

+----+----------------+----------------------------------------+------------+
| id | partition_name | db_url                                 | drr_table  |
+----+----------------+----------------------------------------+------------+
| 1  | dids           | mysql://admin:admin@localhost/opensips | did_rules  |
| 2  | lcr            | mysql://admin:admin@localhost/opensips | lcr_rules  |
| 3  | billing        | mysql://admin:admin@localhost/opensips | bill_rules |
+----+----------------+----------------------------------------+------------+

Script usage of the do_routing() function is only slightly changed:

(opensips.cfg.m4)

do_routing("dids:DR_DIDS_US", "C", , "$avp(did_attr)");
...
do_routing("dids:DR_DIDS_UK", "C", , "$avp(did_attr)");
...
do_routing("lcr:DR_LCR_NEW", "F", , "$avp(lcr_attr)");
...
do_routing("lcr:DR_LCR_LEGACY", "F", , "$avp(lcr_attr)");
...
do_routing("billing:DR_BILLING", "CF", , "$avp(billing_attr)");

(local.m4)

define(`DR_DIDS_US', `1')
define(`DR_DIDS_UK', `2')
define(`DR_LCR_NEW', `1')
define(`DR_LCR_LEGACY', `2')
...

Although switching to partitions in this case is pretty much irrelevant for the drouting module itself, since it will group the data based on the groupid and efficiently work with it anyway, we will make the life of the platform administrators much easier. Here are some of the benefits:

  • faster OpenSIPS reloads, by avoiding a full reload when only one data set requires a cache refresh (e.g. customer billing rates). This might not seem like much, but mind you that reloading 200M prefix rules will cost you several minutes’ worth of CPU cycles.
  • by working with separate tables rather than a single one, it’s harder to accidentally delete data
  • less groups to work with (we’re on separate partitions, so we can reuse them!)

Example 2: multiple databases

In this scenario, we’re dealing with a VoIP service provider which prefers to provide us with a MySQL URL pointing to an OpenSIPS-compatible dispatcher table, listing their SIP proxy servers. The list shall be resized by them daily, based on their traffic necessities – we only have reload the destinations at a given time. Fair enough.

Ok, so we just plug in the provider’s URL into the dispatcher module’s “db_url” module parameter and that should be enough, right? But wait – we would also want to use the dispatcher to route inbound traffic as well, maybe to our FreeSWITCH or Asterisk cluster of servers. These servers are provisioned inside our platform’s PostgreSQL database, so we’d also need the dispatcher to handle that URL as well. We might have been able to hack our way out of using partitions in example #1, but they are now mandatory – we cannot get this working without them!

Using a 2.1+ OpenSIPS (our recommendation: use 2.3 stable), we define our dispatcher partitions:

(opensips.cfg.m4)

...
modparam("dispatcher", "partition",
 "vprov:
 db_url = mysql://oss:oss@10.0.0.10/oss;
 table_name = dispatcher;
 attrs_avp = $avp(vprov_attr);")

modparam("dispatcher", "partition",
 "pbx:
 db_url = postgres://admin:admin@192.168.10.10/opensips;
 table_name = dispatcher;
 attrs_avp = $avp(ds_media_attr);")
...

And simply start using them. Problem solved:

(opensips.cfg.m4)

...
# route outbound calls to "VProv"
ds_select_dst("vprov:DS_SET_VPROV", "6");

...

# route inbound calls to our media servers
ds_select_dst("pbx:DS_SET_PBX_CALL", "6");
...

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s