From: Nathan Bruning Date: Tue, 24 Jan 2023 13:50:39 +0000 (+0100) Subject: app_queue: Add force_longest_waiting_caller option. X-Git-Tag: 21.0.0-pre1~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=292834d1ba2dbac0a413049a800f4ae82ca91f36;p=thirdparty%2Fasterisk.git 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 --- diff --git a/apps/app_queue.c b/apps/app_queue.c index f138cd590c..97143d7ae9 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -1603,6 +1603,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; @@ -4549,6 +4552,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) { @@ -4613,6 +4666,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; @@ -9429,6 +9488,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 */ @@ -9454,6 +9514,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 diff --git a/configs/samples/queues.conf.sample b/configs/samples/queues.conf.sample index 0987236cd6..ee49b7ce6c 100644 --- a/configs/samples/queues.conf.sample +++ b/configs/samples/queues.conf.sample @@ -51,6 +51,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