]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-7138 - new mod_callcenter param: reserve-agents
authorItalo Rossi <italorossib@gmail.com>
Sun, 3 May 2015 04:10:02 +0000 (01:10 -0300)
committerItalo Rossi <italorossib@gmail.com>
Mon, 8 Jun 2015 17:38:51 +0000 (14:38 -0300)
Sets the agent state to Reserved before calling him. In this case,
the Agent is first updated to state Reserved ONLY if his previous
state was Waiting and status was Available/Available (On Demand).
The purpose is to avoid possible race conditions when external
applications are manipulating `agents` table.

Disabled by default.

src/mod/applications/mod_callcenter/conf/autoload_configs/callcenter.conf.xml
src/mod/applications/mod_callcenter/mod_callcenter.c

index a069413ac6a072440a9faf8af522e08c8bc8eadc..51a472615ab2aa8b91de203eddcde45d58682821 100644 (file)
@@ -2,6 +2,7 @@
   <settings>
     <!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
     <!--<param name="dbname" value="/dev/shm/callcenter.db"/>-->
+    <!--<param name="reserve-agents" value="true"/>-->
   </settings>
 
   <queues>
index a04dd300c8e7f12e1a863b4247675cf6cb66d527..f1ce9651e57b602605532dcbf17fdc926416913c 100644 (file)
@@ -119,7 +119,8 @@ typedef enum {
        CC_AGENT_STATE_WAITING = 1,
        CC_AGENT_STATE_RECEIVING = 2,
        CC_AGENT_STATE_IN_A_QUEUE_CALL = 3,
-       CC_AGENT_STATE_IDLE = 4
+       CC_AGENT_STATE_IDLE = 4,
+       CC_AGENT_STATE_RESERVED = 5
 } cc_agent_state_t;
 
 static struct cc_state_table AGENT_STATE_CHART[] = {
@@ -128,6 +129,7 @@ static struct cc_state_table AGENT_STATE_CHART[] = {
        {"Receiving", CC_AGENT_STATE_RECEIVING},
        {"In a queue call", CC_AGENT_STATE_IN_A_QUEUE_CALL},
        {"Idle", CC_AGENT_STATE_IDLE},
+       {"Reserved", CC_AGENT_STATE_RESERVED},
        {NULL, 0}
 
 };
@@ -412,6 +414,7 @@ static struct {
        int debug;
        char *odbc_dsn;
        char *dbname;
+       switch_bool_t reserve_agents;
        int32_t threads;
        int32_t running;
        switch_mutex_t *mutex;
@@ -555,6 +558,15 @@ cc_queue_t *queue_set_config(cc_queue_t *queue)
 
 }
 
+static int cc_execute_sql_affected_rows(char *sql) {
+       switch_cache_db_handle_t *dbh = NULL;
+       if (!(dbh = cc_get_db_handle())) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
+               return -1;
+       }
+       switch_cache_db_execute_sql(dbh, sql, NULL);
+       return switch_cache_db_affected_rows(dbh);
+}
 
 char *cc_execute_sql2str(cc_queue_t *queue, switch_mutex_t *mutex, char *sql, char *resbuf, size_t len)
 {
@@ -1060,6 +1072,31 @@ cc_status_t cc_agent_update(const char *key, const char *value, const char *agen
 
                result = CC_STATUS_SUCCESS;
 
+       } else if (!strcasecmp(key, "state_if_waiting")) {
+               if (cc_agent_str2state(value) == CC_AGENT_STATE_UNKNOWN) {
+                       result = CC_STATUS_AGENT_INVALID_STATE;
+                       goto done;
+               } else {
+                       sql = switch_mprintf("UPDATE agents SET state = '%q' WHERE name = '%q' AND state = '%q' AND status IN ('%q', '%q')",
+                                       value, agent,
+                                       cc_agent_state2str(CC_AGENT_STATE_WAITING),
+                                       cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE),
+                                       cc_agent_status2str(CC_AGENT_STATUS_AVAILABLE_ON_DEMAND));
+
+                       if (cc_execute_sql_affected_rows(sql) > 0) {
+                               result = CC_STATUS_SUCCESS;
+                               if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
+                                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", agent);
+                                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "agent-state-change");
+                                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-State", value);
+                                       switch_event_fire(&event);
+                               }
+                       } else {
+                               result = CC_STATUS_AGENT_NOT_FOUND;
+                       }
+                       switch_safe_free(sql);
+               }
+
        } else {
                result = CC_STATUS_INVALID_KEY;
                goto done;
@@ -1367,13 +1404,19 @@ static switch_status_t load_config(void)
                                globals.dbname = strdup(val);
                        } else if (!strcasecmp(var, "odbc-dsn")) {
                                globals.odbc_dsn = strdup(val);
+                       } else if(!strcasecmp(var, "reserve-agents")) {
+                               globals.reserve_agents = switch_true(val);
                        }
                }
        }
        if (!globals.dbname) {
                globals.dbname = strdup(CC_SQLITE_DB_NAME);
        }
-
+       if (!globals.reserve_agents) {
+               globals.reserve_agents = SWITCH_FALSE;
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reserving Agents before offering calls.\n");
+       }
        /* Initialize database */
        if (!(dbh = cc_get_db_handle())) {
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot open DB!\n");
@@ -2010,6 +2053,18 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
                }
        }
 
+       if (globals.reserve_agents) {
+               /* Updating agent state to Reserved only if it was Waiting previously, this is done to avoid race conditions
+                  when updating agents table with external applications */
+               if (cc_agent_update("state_if_waiting", cc_agent_state2str(CC_AGENT_STATE_RESERVED), agent_name) == CC_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reserved Agent %s\n", agent_name);
+               } else {
+                       /* Agent changed state just before we tried to update his state to Reserved. */
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Failed to Reserve Agent: %s. Skipping...\n", agent_name);
+                       return 0;
+               }
+       }
+
        if (!strcasecmp(cbt->strategy,"ring-all")) {
                /* Check if member is a ring-all mode */
                sql = switch_mprintf("SELECT count(*) FROM members WHERE serving_agent = 'ring-all' AND uuid = '%q' AND system = 'single_box'", cbt->member_uuid);