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:
parent
ff60aae1c8
commit
f0df2eba9d
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue