Singleton (separate) message handler context in Javascript

From Yate Documentation
(Difference between revisions)
Jump to: navigation, search
(Created page with " Singleton message handlers should be used when application is expected to process a high number of messages and want to process it in a 'multithreaded' way. A regular messa...")
 
Line 1: Line 1:
  
Singleton message handlers should be used when application is expected to process a high number of messages and want to process it in a 'multithreaded' way.
+
A regular message handler callback is called by javascript when script context is unlocked.<br/>
 +
Script context is locked during message processing.<br/>
 +
Script global data is available in message handler callback function.
  
 +
A singleton message handler may be used when application is expected to process a high number of messages and want to avoid contention due to javascript serialization.
 +
When a singleton message handler is installed the javascript module will build and initialize a separate context.<br/>
 +
Handle callback function is called in this context.<br/>
 +
The context is deleted after message is handled.<br/>
 +
 +
Message singleton related functions (e.g. Message.installsingleton()) are not available in:
 +
* When running in a singleton context.
 +
* When running a script configured with multiple instances
  
A regular message handler callback is called by javascript when script context is unlocked.<br/>
 
Script context is locked during message processing.
 
  
 +
=== Sample ===
 +
Engine.debugName("handler_singleton_sample");
 +
Message.trackName("handler_singleton_sample");
 +
 +
// Callback for call.route. 'context' value is the second parameter passed to Message.installSingleton()
 +
function onCallRoute(msg,context)
 +
{
 +
    if (debug)
 +
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context);
 +
    // Increment some shared variable to be reported in status
 +
    var perf = new Engine.SharedVars("handler_singleton_sample_perf");
 +
    var perf.inc("handled_" + context);
 +
    if (!msg.called)
 +
return false;
 +
    var shared_objects = new Engine.SharedObjects;
 +
    var data = shared_objects.get("handler_singleton_sample_route_data");
 +
    if (!data)
 +
return false;
 +
    if (data.local_route_called) {
 +
// Sample: route local using called party match
 +
var match = msg.called.match(data.local_route_called);
 +
if (match[1]) {
 +
    perf.inc("routed_local_route_called");
 +
    msg.retValue("sip/sip:" + match[1] + "@127.0.0.1");
 +
    return true;
 +
}
 +
    }
 +
    if ("object" == typeof data.database) {
 +
// Sample: route local using database query
 +
var m = new Message("database",false,data.database);
 +
m.module = Engine.debugName();
 +
if (m.dispatch(true) && !m.error) {
 +
    var row = m.getRow(0);
 +
    if (row.target) {
 +
perf.inc("routed_database");
 +
msg.retValue(row.target);
 +
return true;
 +
    }
 +
}
 +
else
 +
    perf.inc("database_failure");
 +
    }
 +
    return false;
 +
}
 +
 +
function onTest(msg,context)
 +
{
 +
    if (debug) {
 +
var shared_objects = new Engine.SharedObjects;
 +
var data = shared_objects.get("handler_singleton_sample_route_data");
 +
var s = "\r\n-----\r\n" + Engine.dump_var_r(data) + "\r\n-----";
 +
Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context,"route_data:" + s);
 +
    }
 +
    // Increment some shared variable to be reported in status
 +
    perf = new Engine.SharedVars("handler_singleton_sample_perf");
 +
    perf.inc("handled_" + context);
 +
    return true;
 +
}
 +
 
 +
if (!Engine.scriptType)
 +
    initialize(true);
 +
else if (Engine.ScriptTypeMsgHandler !== Engine.scriptType())
 +
    initialize(true);
 +
else {
 +
    // We are in singleton message handler context
 +
    // NOTE: debug (including level) is propagated from main script to singleton
 +
    //      context by javascript module
 +
    debug = Engine.debugEnabled();
 +
}
 +
 +
//
 +
// From this point the code is used only if script is loaded as static script
 +
//
 +
 +
function onStatus(msg)
 +
{
 +
    if (msg.module && ("handler_singleton_sample" != msg.module))
 +
return false;
 +
    var str = "";
 +
    var perf = new Engine.SharedVars("handler_singleton_sample_perf");
 +
    var perf = perf.getVars();
 +
    for (var name in perf)
 +
str += "," + name + "=" + perf[name];
 +
    msg.retValue(msg.retValue() + "name=handler_singleton_sample,type=misc;" + str.substr(1) + "\r\n");
 +
    return !!msg.module;
 +
}
 +
 +
function onDebug(msg)
 +
{
 +
    if (var dbg = msg.line) {
 +
Engine.setDebug(dbg);
 +
debug = Engine.debugEnabled();
 +
    }
 +
    msg.retValue("Sample Singleton Message Handler debug " + Engine.debugEnabled() + " level " +
 +
Engine.debugLevel() + "\r\n");
 +
    return true;
 +
}
 +
 +
function onReload(msg)
 +
{
 +
    if (msg.plugin && ("handler_singleton_sample" != msg.plugin))
 +
return false;
 +
    initialize();
 +
    return !!msg.plugin;
 +
}
 +
 +
function initialize(first)
 +
{
 +
    Engine.output("Initializing module Sample Singleton Message Handler");
 +
    if (first) {
 +
// If script is configured to use multiple instances it can only install singleton for the first one
 +
// It makes no sense to configure multiple instances for it!
 +
if (!Message.installSingleton) {
 +
    Engine.debug(Engine.DebugConf,"Refusing to start Sample Singleton Message Handler",
 +
"singleton handler install not available");
 +
    return;
 +
}
 +
 +
// Initial debug
 +
Engine.setDebug("level 10");
 +
debug = Engine.debugEnabled();
 +
 +
// Message.installSingleton(func,context,name[,priority[,filterName[,filterValue[,params]]])
 +
// Do not load extensions (default: javascript.conf global setup)
 +
// Extensions are loaded using a script.init message. C++ modules may push javascript objects in script context
 +
var params = {load_extensions:false};
 +
// Sample call.route filter. Handle if not explicitly disabled. Use Engine.MatchingItem
 +
var mi = {name:"handler_singleton_sample",value:/^$(true|yes|)$/};
 +
var filter = new Engine.MatchingItem(mi);
 +
Message.installSingleton(onCallRoute,"call_route","call.route",30,filter,undefined,params);
 +
// Sample test filter. Handle if not explicitly disabled. Use basic
 +
Message.installSingleton(onTest,"test","test",70,"handler_singleton_sample",/^$(true|yes|)$/,params);
 +
 +
// Module messages
 +
Message.install(onStatus,"engine.status");
 +
Message.install(onDebug,"engine.debug",150,"module","handler_singleton_sample");
 +
Message.install(onReload,"engine.init");
 +
    }
 +
 +
    // Init and set shared object holding route data
 +
    var routeData = {
 +
local_route_called: /^(test|other)$/,
 +
database: {account:"some_db_account",query:"select target from route"},
 +
    };
 +
    var shared_objects = new Engine.SharedObjects;
 +
    shared_objects.set("handler_singleton_sample_route_data",routeData);
 +
}
  
 
'''See also'''
 
'''See also'''
  
 
* [[Javascript]]
 
* [[Javascript]]

Revision as of 16:47, 29 August 2024

A regular message handler callback is called by javascript when script context is unlocked.
Script context is locked during message processing.
Script global data is available in message handler callback function.

A singleton message handler may be used when application is expected to process a high number of messages and want to avoid contention due to javascript serialization. When a singleton message handler is installed the javascript module will build and initialize a separate context.
Handle callback function is called in this context.
The context is deleted after message is handled.

Message singleton related functions (e.g. Message.installsingleton()) are not available in:

  • When running in a singleton context.
  • When running a script configured with multiple instances


Sample

Engine.debugName("handler_singleton_sample");
Message.trackName("handler_singleton_sample");

// Callback for call.route. 'context' value is the second parameter passed to Message.installSingleton()
function onCallRoute(msg,context)
{
    if (debug)
	Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context);
    // Increment some shared variable to be reported in status
    var perf = new Engine.SharedVars("handler_singleton_sample_perf");
    var perf.inc("handled_" + context);
    if (!msg.called)
	return false;
    var shared_objects = new Engine.SharedObjects;
    var data = shared_objects.get("handler_singleton_sample_route_data");
    if (!data)
	return false;
    if (data.local_route_called) {
	// Sample: route local using called party match
	var match = msg.called.match(data.local_route_called);
	if (match[1]) {
	    perf.inc("routed_local_route_called");
	    msg.retValue("sip/sip:" + match[1] + "@127.0.0.1");
	    return true;
	}
    }
    if ("object" == typeof data.database) {
	// Sample: route local using database query
	var m = new Message("database",false,data.database);
	m.module = Engine.debugName();
	if (m.dispatch(true) && !m.error) {
	    var row = m.getRow(0);
	    if (row.target) {
		perf.inc("routed_database");
		msg.retValue(row.target);
		return true;
	    }
	}
	else
	    perf.inc("database_failure");
    }
    return false;
}

function onTest(msg,context)
{
    if (debug) {
	var shared_objects = new Engine.SharedObjects;
	var data = shared_objects.get("handler_singleton_sample_route_data");
	var s = "\r\n-----\r\n" + Engine.dump_var_r(data) + "\r\n-----";
	Engine.debug(Engine.DebugCall,"Singleton",msg.name(),"context=" + context,"route_data:" + s);
    }
    // Increment some shared variable to be reported in status
    perf = new Engine.SharedVars("handler_singleton_sample_perf");
    perf.inc("handled_" + context);
    return true;
}
 
if (!Engine.scriptType)
    initialize(true);
else if (Engine.ScriptTypeMsgHandler !== Engine.scriptType())
    initialize(true);
else {
    // We are in singleton message handler context
    // NOTE: debug (including level) is propagated from main script to singleton
    //       context by javascript module
    debug = Engine.debugEnabled();
}

//
// From this point the code is used only if script is loaded as static script
//

function onStatus(msg)
{
    if (msg.module && ("handler_singleton_sample" != msg.module))
	return false;
    var str = "";
    var perf = new Engine.SharedVars("handler_singleton_sample_perf");
    var perf = perf.getVars();
    for (var name in perf)
	str += "," + name + "=" + perf[name];
    msg.retValue(msg.retValue() + "name=handler_singleton_sample,type=misc;" + str.substr(1) + "\r\n");
    return !!msg.module;
}

function onDebug(msg)
{
    if (var dbg = msg.line) {
	Engine.setDebug(dbg);
	debug = Engine.debugEnabled();
    }
    msg.retValue("Sample Singleton Message Handler debug " + Engine.debugEnabled() + " level " +
	Engine.debugLevel() + "\r\n");
    return true;
}

function onReload(msg)
{
    if (msg.plugin && ("handler_singleton_sample" != msg.plugin))
	return false;
    initialize();
    return !!msg.plugin;
}

function initialize(first)
{
    Engine.output("Initializing module Sample Singleton Message Handler");
    if (first) {
	// If script is configured to use multiple instances it can only install singleton for the first one
	// It makes no sense to configure multiple instances for it!
	if (!Message.installSingleton) {
	    Engine.debug(Engine.DebugConf,"Refusing to start Sample Singleton Message Handler",
		"singleton handler install not available");
	    return;
	}

	// Initial debug
	Engine.setDebug("level 10");
	debug = Engine.debugEnabled();

	// Message.installSingleton(func,context,name[,priority[,filterName[,filterValue[,params]]])
	// Do not load extensions (default: javascript.conf global setup)
	// Extensions are loaded using a script.init message. C++ modules may push javascript objects in script context
	var params = {load_extensions:false};
	// Sample call.route filter. Handle if not explicitly disabled. Use Engine.MatchingItem
	var mi = {name:"handler_singleton_sample",value:/^$(true|yes|)$/};
	var filter = new Engine.MatchingItem(mi);
	Message.installSingleton(onCallRoute,"call_route","call.route",30,filter,undefined,params);
	// Sample test filter. Handle if not explicitly disabled. Use basic
	Message.installSingleton(onTest,"test","test",70,"handler_singleton_sample",/^$(true|yes|)$/,params);

	// Module messages
	Message.install(onStatus,"engine.status");
	Message.install(onDebug,"engine.debug",150,"module","handler_singleton_sample");
	Message.install(onReload,"engine.init");
    }

    // Init and set shared object holding route data
    var routeData = {
	local_route_called: /^(test|other)$/,
	database: {account:"some_db_account",query:"select target from route"},
    };
    var shared_objects = new Engine.SharedObjects;
    shared_objects.set("handler_singleton_sample_route_data",routeData);
}

See also

Personal tools
Namespaces

Variants
Actions
Preface
Configuration
Administrators
Developers