<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:SimSun;
panose-1:2 1 6 0 3 1 1 1 1 1;}
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:"\@SimSun";
panose-1:2 1 6 0 3 1 1 1 1 1;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:#0563C1;
text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-priority:99;
color:#954F72;
text-decoration:underline;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-GB" link="#0563C1" vlink="#954F72">
<div class="WordSection1">
<p class="MsoNormal">Whilst looking at the performance of the search for an available queue member I’ve come across code that handles the ringinuse flag that seems to count busy an inuse as the same, that doesn’t make sense to me. I’ve also come across very
long standing code that means ringinuse disables state checks entirely, allowing the code ot fall through to the, very expensive, compare_weights code. Finally, the compare_weights code seems to waste a lot of time trying to match a member in queues that
don’t have higher weights.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Looking at the current trunk version, revision 325483 introduced additional “case” lines into, what is now, is_member_available, which treat busy and inuse the same:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">2310 case AST_DEVICE_INUSE: <o:p></o:p></p>
<p class="MsoNormal">2311 case AST_DEVICE_BUSY: <o:p></o:p></p>
<p class="MsoNormal">2312 case AST_DEVICE_RINGING: <o:p></o:p></p>
<p class="MsoNormal">2313 case AST_DEVICE_RINGINUSE: <o:p></o:p></p>
<p class="MsoNormal">2314 case AST_DEVICE_ONHOLD: <o:p></o:p></p>
<p class="MsoNormal">2315 if (!mem->ringinuse) {
<o:p></o:p></p>
<p class="MsoNormal">2316 break;
<o:p></o:p></p>
<p class="MsoNormal">2317 } <o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Whilst this postdates the code we are using, so doesn’t directly affect us, this seems wrong to me; I thought the whole subtlety behind calling it ringinuse, not ringbusy, is that there is a difference between busy and in use. Could someone
explain?<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Next, in can_ring_entry, ringinuse set completely bypasses the device state check:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">4169 if (!call->member->ringinuse && !member_status_available(call->member->status)) {
<o:p></o:p></p>
<p class="MsoNormal">4170 ast_debug(1, "%s not available, can't receive call\n", call->interface);
<o:p></o:p></p>
<p class="MsoNormal">4171 return 0; <o:p></o:p></p>
<p class="MsoNormal">4172 }<o:p></o:p></p>
<p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">The consequence of this is that, if ringinuse is set, even if the member is unavailable (e.g. agent logged out), or definitively busy, the code falls through to:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">4182 if (use_weight && compare_weight(qe->parent, call->member)) {
<o:p></o:p></p>
<p class="MsoNormal">4183 ast_debug(1, "Priority queue delaying call to %s:%s\n",
<o:p></o:p></p>
<p class="MsoNormal">4184 qe->parent->name, call->interface);
<o:p></o:p></p>
<p class="MsoNormal">4185 return 0; <o:p></o:p></p>
<p class="MsoNormal">4186 }<o:p></o:p></p>
<p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">compare_weight is very expensive, because it iterates over all queues and all members of them. Also, at least in the version we are using, although I haven’t checked this in trunk, there is a lock held on the queue of interest and locks
are taken, in enumeration sequence, on all the other queues. This looks to me that it will cause serialisation of access to that function.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">So, finally, looking at compare_weight, this line:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">4080 if (q->weight > rq->weight && q->count >= num_available_members(q)) {<o:p></o:p></p>
<p class="MsoNormal"><o:p></o:p></p>
<p class="MsoNormal">Is run after this line:<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">4078 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Whilst the second term is expensive, involving a scan of all the members, the first term is cheap. In our case, there are lots of members in common between lots of queues, a lot of which are not at high priority. It seems to me that doing
the cheap test before the search for the member, would quickly eliminate queues that could never pre-empt the agent.. Even if ao2_find hashes, it is still going to be expensive compared with a compare of two, one level indirect, scalars.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">One other observation, is that compare_weight doesn’t account for members that are unavailable to the conflicting queue because there is an even higher priority queue. NB the member in question need not be the same member as is being considered
for the call. (Without a more efficient algorithm, recursing the check would add additional multipliers to the computational complexity, so could well be unworkable.)<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
</div>
<br clear="both">
BTS Holdings PLC - Registered office: BTS House, Manor Road, Wallington, SM6 0DD - Registered in England: 1517630<BR>
</body>
</html>