app_queue: Add force_longest_waiting_caller option.

This adds an option 'force_longest_waiting_caller' which changes the
global behavior of the queue engine to prevent queue callers from
'jumping ahead' when an agent is in multiple queues.

Resolves: #108

Also closes old asterisk issues:
- ASTERISK-17732
- ASTERISK-17570

Change-Id: I0f84e27903fefbe2018d0afa2d67b23aa0b321ce
(cherry picked from commit 6084bbfe0e)
This commit is contained in:
Nathan Bruning 2023-01-24 14:50:39 +01:00 committed by Asterisk Development Team
parent ff60aae1c8
commit f0df2eba9d
2 changed files with 71 additions and 0 deletions

View File

@ -1618,6 +1618,9 @@ static int negative_penalty_invalid;
/*! \brief queues.conf [general] option */
static int log_membername_as_agent;
/*! \brief queues.conf [general] option */
static int force_longest_waiting_caller;
/*! \brief name of the ringinuse field in the realtime database */
static char *realtime_ringinuse_field;
@ -4575,6 +4578,56 @@ static int compare_weight(struct call_queue *rq, struct member *member)
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(2, "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)
{
@ -4639,6 +4692,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
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;
@ -9520,6 +9579,7 @@ static void queue_reset_global_params(void)
shared_lastcall = 0;
negative_penalty_invalid = 0;
log_membername_as_agent = 0;
force_longest_waiting_caller = 0;
}
/*! Set the global queue parameters as defined in the "general" section of queues.conf */
@ -9545,6 +9605,9 @@ static void queue_set_global_params(struct ast_config *cfg)
if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
log_membername_as_agent = ast_true(general_val);
}
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

View File

@ -58,6 +58,14 @@ monitor-type = MixMonitor
;
;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. The default value is 'no'.
;
;force_longest_waiting_caller = no
;
;[markq]
;
; A sample call queue