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.
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.kamai…
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.
1) 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");
2) 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")
3) 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
--
Luis Rojas
Software Architect
Sixbell
Los Leones 1200
Providencia
Santiago, Chile
Phone: (+56-2) 22001288
mailto:luis.rojas@sixbell.com