Login | Register

Documentation

Documentation -> Manuals -> Manual devel -> Asynchronous Statements

This page has been visited 1224 times.


Pages for other versions: devel 2.3 2.2 Older versions: 2.1


Asynchronous Statements v2.3
PrevNext

1.  Description

Asynchronous statements are one of the key features of OpenSIPS 2.X. One of the main reasons to use them is that they allow the performance of the OpenSIPS script to scale with a high number of requests per second even when doing blocking I/O operations such as MySQL queries, exec commands or HTTP requests.


Using the asynchronous, "suspend-resume" logic instead of forking a large number of processes in order to scale also has the advantage of optimizing system resource usage, increasing its maximal throughput. By requiring less processes to complete the same amount of work in the same amount of time, process context switching is minimized and overall CPU usage is improved. Less processes will also eat up less system memory.

2.  Serial asynchronous operations

The async() statement of the OpenSIPS script can be used in situations where the script writer both needs to perform blocking I/O and also depends on the result of this operation. Some example scenarios:

  • fetch SIP authentication data from a database
  • perform an HTTP query and act upon its result
  • pause script execution for X seconds
  • exec an external script and use its result

2.1  Requirements

The async() statement depends on the transaction module (tm) - it must be loaded. The SIP transaction will be automatically and transparently created when an async operation is started, if necessary. This transaction contains all the necessary information to suspend script execution (e.g. it stores the updated SIP message, along with all $avp variables).

2.2  Script syntax and usage

Usage is straightforward: if your blocking function supports asynchronous mode (read the module documentation for this), then you can just throw it in the following function call:

async(blocking_function(...), resume_route);

Note that resume_route has to be a simple route.


Because the async() statement is serial with script execution (see below), the script will be immediately halted when calling it, so any code placed after the async() call will be ignored! The current OpenSIPS worker will launch the asynchronous operation, after which it will continue to process other pending tasks (queued SIP messages, timer jobs or possibly other async operations!). As soon as all data is available, it will run the resume route - thus resuming script execution with a minimum of idle time.


The return code of the function executed in async mode is available in the very beginning of the resume route in the $rc or $retcode variable. Also, all output parameters (variables in function parameters used to carry output values) will be available in resume route.


route
{
    /* preparation code */
    ...
    async(avp_db_query("SELECT credit FROM users WHERE uid='$avp(uid)'", "$avp(credit)"), resume_credit);
    /* script execution is paused right away! */
}

route [resume_credit]
{
    if ($rc < 0) {
        xlog("error $rc in avp_db_query()\n");
        exit;
    }

    xlog("Credit of user $avp(uid) is $avp(credit)\n");
    ...
    t_relay();
}


Data is copied over to the resume route as follows:


Preserved data (still available in resume route)

  • all $avp variables
  • all changes in current SIP message

Ignored data (not available anymore in resume route)

  • all $var variables

3.  Parallel asynchronous operations

The launch() statement of the OpenSIPS script can be used in situations where the script writer needs to perform blocking I/O, but does not depend on the result of this operation in order to continue the current SIP routing decision flow. Some example scenarios:

  • execute an external push notification trigger
  • push data into a custom database (e.g. call statistics, CDRs, etc.)
  • notify an HTTP service of the occurrence of an event (e.g. SIP traffic pattern, fraud detection, etc.)


The launch() statement comes with no additional module dependencies.

3.1  Script syntax and usage

Similarly to the async() statement, if your blocking function supports asynchronous mode (read the module documentation for this), then you can just throw it in the following function calls:

launch(blocking_function(...));
or
launch(blocking_function(...), report_route);

Note that report_route has to be a simple route.


The launch() statement is both asynchronous and parallel with the script execution that follows it (see below). Notice how the "report_route" can be omitted, as script execution does not depend on it. This route may be triggered at any of the following points in time:

  • right before the next code line following the launch() call
  • during the routing of the current SIP message
  • after the routing of the current SIP message


The return code of the function executed in async mode is available in the very beginning of the report route in the $rc or $retcode variable. Also, only the output parameters (variables in function parameters used to carry output values) will be available inside this route.


route
{
    /* preparation code */
    ...

    # send a push notification asynchronously, in parallel
    launch(exec("/usr/local/bin/send-google-pn.py"), pn_counter);
    t_relay();
}

route [pn_counter]
{
    if ($rc < 0) {
        xlog("error $rc in pn script!\n");
        update_stat("pn-failure", "1");
        exit;
    }

    update_stat("pn-success", "1");
}


Preserved data (still available in report route)

  • only $avp variables set by the async function

Ignored data (not available anymore in report route)

  • any other variable
  • the initial SIP message

4.  List of async functions

The following functions may also be called asynchronously:

The async implementation is not limited to the above functions, but these are the first ones migrated to async support. More I/O related functions will be ported to the async support.


Page last modified on August 23, 2017, at 04:18 PM