Hi, Alex,
Thanks for all the details. Wow, such nice implementation. It requires a deep knowledge of Kamailio.
I see, this is the part where the transaction is saved.
mq_add("reinvite_q", "$T(id_index):$T(id_label)", "");
I didn't know much about pseudo-variables. I am reading now.
https://www.kamailio.org/wiki/cookbooks/5.3.x/pseudovariables#tmx_module_pse...
I guess, this approach could help me also in case of replies, not just requests, to add a delay to 200 OK to Invite.
By the way, I assigned a value to "async_workers", and now the Invite (I was delaying any Invite, not just reinvite, just to test the api) is propagated delayed, which is great, but at the same time a
SIP/2.0 500 I'm terribly sorry, server error occurred (1/TM)
is sent back to the origin, just killing the call.
I'll start playing with mqueue and rtimer.
Thanks again!
Luis
On 4/14/20 5:26 PM, Alex Balashov wrote:
Hi Luis,
Rather confusingly, there is an 'async_workers' parameter in the core as well, which needs to be set:
https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.kamail...
There is some relationship between 'async_workers' in the core and the 'workers' modparam in the 'async' module which is explained by Daniel somewhere in a past mailing list thread, but I do not remember it offhand. I really should dig it out and update the documentation with this nuance.
Having said that, I did not use the 'async' framework for my fix, but rather 'mqueue' and 'rtimer'. I have no real justification for that; just custom, habit and comfort with those mechanisms. I grew accustomed to them at a time when I had some issues with 100% CPU utilisation in a virtualised (Xen) environment when using early versions of the 'async' concepts.
- The first thing is to create a reinvite queue; a single one will do,
since it's specifically designed to be multiprocess-safe:
loadmodule "mqueue" modparam("mqueue", "mqueue", "name=reinvite_q");
- Then I create 12 'rtimer' processes to consume this queue, each
having a 10,000 usec re-invocation delay:
loadmodule "rtimer" modparam("rtimer", "timer", "name=reinvite_q1;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q1;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q2;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q2;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q3;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q3;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q4;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q4;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q5;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q5;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q6;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q6;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q7;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q7;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q8;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q8;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q9;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q9;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q10;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q10;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q11;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q11;route=REINVITE_DEQUEUE") modparam("rtimer", "timer", "name=reinvite_q12;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q12;route=REINVITE_DEQUEUE")
- I then add this handling for reinvites to the loose_route() block in
the main request route -- logging and other extraneous matter omitted:
if(has_totag()) { if(loose_route()) { if(is_method("INVITE")) { if(!t_suspend()) { sl_send_reply("500", "Internal Server Error"); exit; } mq_add("reinvite_q", "$T(id_index):$T(id_label)", ""); } else { # Normal in-dialog request handling, t_relay() and that. route(IN_DLG_REQ); } } }
And the handler on the other side, when the transaction is reanimated, as it were:
route[REINVITE_DEQUEUE] { while(mq_fetch("reinvite_q")) { xlog("L_INFO", "[R-REINVITE-DEQUEUE:$ci] -> Resuming re-invite handling ($TV(Sn)) in PID $pp\n");
$var(id) = $(mqk(reinvite_q){s.select,0,:}{s.int}); $var(label) = $(mqk(reinvite_q){s.select,1,:}{s.int}); xlog("L_INFO", "[R-REINVITE-DEQUEUE:$ci] -> Resuming re-invite handling ($TV(Sn)) in PID $pp\n"); # Call route[IN_DLG_REQ] to do what we otherwise would have done # immediately, were it any other kind of in-dialog request. t_continue("$var(id)", "$var(label)", "IN_DLG_REQ"); return; }
}
The idea behind 12 whole 'rtimer' processes is to massively overprovision the amount of reinvite handlers available relative to the actual number of reinvites passing through the system, even at high volumes.
The danger with too few processes is that the intended 10 ms delay may not apply, because while(mq_fetch(...)) will just spin, always dequeueing new reinvites and never allowing the execution route to return control to 'rtimer'.
As a practical matter, the system is not extremely high-volume and this is a very academic concern.
Overall, I would describe this method as crude but effective. It was developed some years ago. 'async' provides some simplification and removes some of the manual labour around this kind of management, from what I understand, and overall I think you'd find it easier to use that.
¡Suerte! I'd be happy to answer any additional questions.
-- Alex