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