<p>Nathan Bruning has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/19793">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_queue: add option to connect agents only to longest waiting queue caller<br><br>This adds an option 'force_longest_waiting_caller' which can be enabled per queue,<br>which changes the behavior of the queue engine to prevent queue callers from<br>'jumping ahead' when an agent is in multiple queues.<br><br>ASTERISK-17732 #close<br>ASTERISK-17570 #close<br><br>Change-Id: I4a8e4c21c60639c214114fc909226c175a8849f9<br>---<br>M apps/app_queue.c<br>M configs/samples/queues.conf.sample<br>A doc/CHANGES-staging/app_queue_force_longest_waiting_caller.txt<br>3 files changed, 94 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/93/19793/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_queue.c b/apps/app_queue.c</span><br><span>index c5b7d10..15f40a5 100644</span><br><span>--- a/apps/app_queue.c</span><br><span>+++ b/apps/app_queue.c</span><br><span>@@ -1606,6 +1606,9 @@</span><br><span> /*! \brief queues.conf [general] option */</span><br><span> static int log_membername_as_agent;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief queues.conf [general] option */</span><br><span style="color: hsl(120, 100%, 40%);">+static int force_longest_waiting_caller = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief name of the ringinuse field in the realtime database */</span><br><span> static char *realtime_ringinuse_field;</span><br><span> </span><br><span>@@ -4535,6 +4538,56 @@</span><br><span> return found;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct call_queue *q;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct member *mem;</span><br><span style="color: hsl(120, 100%, 40%);">+ int is_longest_waiting = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ao2_iterator queue_iter;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct queue_ent *ch;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ queue_iter = ao2_iterator_init(queues, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (q == caller->parent) { /* don't check myself, could deadlock */</span><br><span style="color: hsl(120, 100%, 40%);">+ queue_t_unref(q, "Done with iterator");</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(q);</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If the other queue has equal weight, see if we should let that handle</span><br><span style="color: hsl(120, 100%, 40%);">+ * their call first. If weights are not equal, compare_weights will step in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (q->weight == caller->parent->weight && q->count && q->members) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Does this queue have a caller that's been waiting longer? */</span><br><span style="color: hsl(120, 100%, 40%);">+ ch = q->head;</span><br><span style="color: hsl(120, 100%, 40%);">+ while (ch) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If ch->pending, the other call (which may be waiting for a longer period of time),</span><br><span style="color: hsl(120, 100%, 40%);">+ * is already ringing at another agent. Ignore such callers; otherwise, all agents</span><br><span style="color: hsl(120, 100%, 40%);">+ * will be unused until the first caller is picked up.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ch->start < caller->start && !ch->pending) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ q->name, ch->pos, ch->start, caller->start);</span><br><span style="color: hsl(120, 100%, 40%);">+ is_longest_waiting = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ch = ch->next;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(q);</span><br><span style="color: hsl(120, 100%, 40%);">+ queue_t_unref(q, "Done with iterator");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_longest_waiting) {</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_iterator_destroy(&queue_iter);</span><br><span style="color: hsl(120, 100%, 40%);">+ return is_longest_waiting;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief common hangup actions */</span><br><span> static void do_hang(struct callattempt *o)</span><br><span> {</span><br><span>@@ -4598,6 +4651,12 @@</span><br><span> qe->parent->name, call->interface);</span><br><span> return 0;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ if (force_longest_waiting_caller && !is_longest_waiting_caller(qe, memberp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ qe->parent->name, call->interface);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span> if (!memberp->ringinuse) {</span><br><span> struct member *mem;</span><br><span>@@ -9433,6 +9492,10 @@</span><br><span> if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {</span><br><span> log_membername_as_agent = ast_true(general_val);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ force_longest_waiting_caller = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ force_longest_waiting_caller = ast_true(general_val);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span> /*! \brief reload information pertaining to a single member</span><br><span>diff --git a/configs/samples/queues.conf.sample b/configs/samples/queues.conf.sample</span><br><span>index 3b2c18b..09fe534 100644</span><br><span>--- a/configs/samples/queues.conf.sample</span><br><span>+++ b/configs/samples/queues.conf.sample</span><br><span>@@ -51,6 +51,14 @@</span><br><span> ;</span><br><span> ;log_membername_as_agent = no</span><br><span> ;</span><br><span style="color: hsl(120, 100%, 40%);">+; force_longest_waiting_caller will cause app_queue to make sure callers are offered</span><br><span style="color: hsl(120, 100%, 40%);">+; in order (longest waiting first), even for callers across multiple queues.</span><br><span style="color: hsl(120, 100%, 40%);">+; Before a call is offered to an agent, an additional check is made to see if the agent</span><br><span style="color: hsl(120, 100%, 40%);">+; is a member of another queue with a call that's been waiting longer. If so, the current</span><br><span style="color: hsl(120, 100%, 40%);">+; call is not offered to the agent.</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span style="color: hsl(120, 100%, 40%);">+;force_longest_waiting_caller = no</span><br><span style="color: hsl(120, 100%, 40%);">+;</span><br><span> ;[markq]</span><br><span> ;</span><br><span> ; A sample call queue</span><br><span>diff --git a/doc/CHANGES-staging/app_queue_force_longest_waiting_caller.txt b/doc/CHANGES-staging/app_queue_force_longest_waiting_caller.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..f43c2a6</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/app_queue_force_longest_waiting_caller.txt</span><br><span>@@ -0,0 +1,7 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: app_queue</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A new option named "force_longest_waiting_caller" has been added to be configured</span><br><span style="color: hsl(120, 100%, 40%);">+per queue. When enabled, the queue engine performs additional checks before connecting</span><br><span style="color: hsl(120, 100%, 40%);">+a caller to an agent, making sure that the caller is indeed the 'longest waiting caller'.</span><br><span style="color: hsl(120, 100%, 40%);">+When an agent is a member of multiple queues, this ensures more 'fair' behavior from the</span><br><span style="color: hsl(120, 100%, 40%);">+caller's perspective.</span><br><span>\ No newline at end of file</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/19793">change 19793</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/19793"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I4a8e4c21c60639c214114fc909226c175a8849f9 </div>
<div style="display:none"> Gerrit-Change-Number: 19793 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Nathan Bruning <nathan@iperity.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>