[asterisk-commits] tilghman: trunk r278957 - in /trunk: ./ configs/ include/asterisk/ main/ res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 23 11:19:26 CDT 2010


Author: tilghman
Date: Fri Jul 23 11:19:21 2010
New Revision: 278957

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=278957
Log:
Merge the realtime failover branch

Modified:
    trunk/CHANGES
    trunk/configs/extconfig.conf.sample
    trunk/configs/res_odbc.conf.sample
    trunk/include/asterisk/res_odbc.h
    trunk/main/config.c
    trunk/res/res_config_odbc.c
    trunk/res/res_odbc.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Jul 23 11:19:21 2010
@@ -524,6 +524,7 @@
    during device configuration.
  * The UNISTIM channel driver (chan_unistim) has been updated to support devices that
    have less than 3 lines on the LCD.
+ * Realtime now supports database failover.  See the sample extconfig.conf for details.
 
 CLI Changes
 -----------

Modified: trunk/configs/extconfig.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/extconfig.conf.sample?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/configs/extconfig.conf.sample (original)
+++ trunk/configs/extconfig.conf.sample Fri Jul 23 11:19:21 2010
@@ -9,7 +9,7 @@
 ;
 ; Static configuration files:
 ;
-; file.conf => driver,database[,table]
+; file.conf => driver,database[,table[,priority]]
 ;
 ; maps a particular configuration file to the given
 ; database driver, database and table (or uses the
@@ -40,14 +40,26 @@
 ; database and table (or uses the name of
 ; the family if the table is not specified
 ;
-;example => odbc,asterisk,alttable
+;example => odbc,asterisk,alttable,1
+;example => mysql,asterisk,alttable,2
 ;example2 => ldap,"dc=oxymium,dc=net",example2
+;
+; Additionally, priorities are now supported for use as failover methods
+; for retrieving realtime data.  If one connection fails to retrieve any
+; information, the next sequential priority will be tried next.  This
+; especially works well with ODBC connections, since res_odbc now caches
+; when connection failures occur and prevents immediately retrying those
+; connections until after a specified timeout.  Note:  priorities must
+; start at 1 and be sequential (i.e. if you have only priorities 1, 2,
+; and 4, then 4 will be ignored, because there is no 3).
 ;
 ; "odbc" is shown in the examples below, but is not the only valid realtime
 ; engine.  There is:
 ;    odbc ... res_config_odbc
 ;    sqlite ... res_config_sqlite
 ;    pgsql ... res_config_pgsql
+;    curl ... res_config_curl
+;    ldap ... res_config_ldap
 ;
 ;iaxusers => odbc,asterisk
 ;iaxpeers => odbc,asterisk

Modified: trunk/configs/res_odbc.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/res_odbc.conf.sample?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/configs/res_odbc.conf.sample (original)
+++ trunk/configs/res_odbc.conf.sample Fri Jul 23 11:19:21 2010
@@ -10,18 +10,69 @@
 
 ; All other sections are arbitrary names for database connections.
 
+;
+; The context name is what will be used in other configuration files, such
+; as extconfig.conf and func_odbc.conf, to reference this connection.
 [asterisk]
+;
+; Permit disabling sections without needing to comment them out.
+; If not specified, it is assumed the section is enabled.
 enabled => no
+;
+; This value should match an entry in /etc/odbc.ini
+; (or /usr/local/etc/odbc.ini, on FreeBSD and similar systems).
 dsn => asterisk
+;
+; Username for connecting to the database.  The default user is "root".
 ;username => myuser
+;
+; Password for authenticating the user to the database.  The default
+; password is blank.
 ;password => mypass
+;
+; Build a connection at startup?
 pre-connect => yes
 ;
 ; What should we execute to ensure that our connection is still alive?  The
 ; statement should return a non-zero value in the first field of its first
 ; record.  The default is "select 1".
 ;sanitysql => select 1
-
+;
+; On some databases, the connection times out and a reconnection will be
+; necessary.  This setting configures the amount of time a connection
+; may sit idle (in seconds) before a reconnection will be attempted.
+;idlecheck => 3600
+;
+; Should we use a single connection for all queries?  Most databases will
+; allow sharing the connection, though Sybase and MS SQL Server will not.
+;share_connections => yes
+;
+; If we aren't sharing connections, what is the maximum number of connections
+; that we should attempt?
+;limit => 5
+;
+; When the channel is destroyed, should any uncommitted open transactions
+; automatically be committed?
+;forcecommit => no
+;
+; How should we perceive data in other transactions within the database?
+; Possible values are read_uncommitted, read_committed, repeatable_read,
+; and serializable.  The default is read_committed.
+;isolation => repeatable_read
+;
+; Is the backslash a native escape character?  The default is yes, but for
+; MS SQL Server, the answer is no.
+;backslash_is_escape => yes
+;
+; How long (in seconds) should we attempt to connect before considering the
+; connection dead?  The default is 10 seconds, but you may wish to reduce it,
+; to increase responsiveness.
+;connect_timeout => 10
+;
+; When a connection fails, how long (in seconds) should we cache that
+; information before we attempt another connection?  This increases
+; responsiveness, when a database resource is not working.
+;negative_connection_cache => 300
 
 [mysql2]
 enabled => no
@@ -29,11 +80,6 @@
 username => myuser
 password => mypass
 pre-connect => yes
-;
-; On some databases, the connection times out and a reconnection will be
-; necessary.  This setting configures the amount of time a connection
-; may sit idle (in seconds) before a reconnection will be attempted.
-;idlecheck => 3600
 
 ; Certain servers, such as MS SQL Server and Sybase use the TDS protocol, which
 ; limits the number of active queries per connection to 1.  By telling res_odbc
@@ -64,5 +110,12 @@
 ; Server does not.
 backslash_is_escape => no
 
-
-
+;
+; If you are having problems with concurrency, please read this note from the
+; mailing lists, regarding UnixODBC:
+;
+; http://lists.digium.com/pipermail/asterisk-dev/2009-February/036539.html
+;
+; In summary, try setting "Threading=2" in the relevant section within your
+; odbcinst.ini.
+;

Modified: trunk/include/asterisk/res_odbc.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_odbc.h?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/include/asterisk/res_odbc.h (original)
+++ trunk/include/asterisk/res_odbc.h Fri Jul 23 11:19:21 2010
@@ -39,6 +39,7 @@
 enum {
 	RES_ODBC_SANITY_CHECK = (1 << 0),
 	RES_ODBC_INDEPENDENT_CONNECTION = (1 << 1),
+	RES_ODBC_CONNECTED = (1 << 2),
 };
 
 /*! \brief ODBC container */
@@ -59,6 +60,7 @@
 	AST_LIST_ENTRY(odbc_obj) list;
 };
 
+/*!\brief These structures are used for adaptive capabilities */
 struct odbc_cache_columns {
 	char *name;
 	SQLSMALLINT type;
@@ -99,17 +101,20 @@
  */
 int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) __attribute__((deprecated));
 
-/*! 
+/*!
  * \brief Retrieves a connected ODBC object
  * \param name The name of the ODBC class for which a connection is needed.
- * \param flags Set of flags used to control which connection is returned.
- * \retval ODBC object 
- * \retval  NULL if there is no connection available with the requested name.
+ * \param flags One or more of the following flags:
+ * \li RES_ODBC_SANITY_CHECK Whether to ensure that a connection is valid before returning the handle.  Usually unnecessary.
+ * \li RES_ODBC_INDEPENDENT_CONNECTION Return a handle which is independent from all others.  Usually used when starting a transaction.
+ * \li RES_ODBC_CONNECTED Only return a connected handle.  Intended for use with peers which use idlecheck, which are checked periodically for reachability.
+ * \return ODBC object
+ * \retval NULL if there is no connection available with the requested name.
  *
  * Connection classes may, in fact, contain multiple connection handles.  If
  * the connection is pooled, then each connection will be dedicated to the
  * thread which requests it.  Note that all connections should be released
- * when the thread is done by calling odbc_release_obj(), below.
+ * when the thread is done by calling ast_odbc_release_obj(), below.
  */
 struct odbc_obj *_ast_odbc_request_obj2(const char *name, struct ast_flags flags, const char *file, const char *function, int lineno);
 struct odbc_obj *_ast_odbc_request_obj(const char *name, int check, const char *file, const char *function, int lineno);
@@ -130,7 +135,7 @@
 struct odbc_obj *ast_odbc_retrieve_transaction_obj(struct ast_channel *chan, const char *objname);
 
 /*! 
- * \brief Releases an ODBC object previously allocated by odbc_request_obj()
+ * \brief Releases an ODBC object previously allocated by ast_odbc_request_obj()
  * \param obj The ODBC object
  */
 void ast_odbc_release_obj(struct odbc_obj *obj);
@@ -175,7 +180,9 @@
  * \param tablename Tablename to describe
  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
  * When a structure is returned, the contained columns list will be
- * rdlock'ed, to ensure that it will be retained in memory.
+ * rdlock'ed, to ensure that it will be retained in memory.  The information
+ * will be cached until a reload event or when ast_odbc_clear_cache() is called
+ * with the relevant parameters.
  * \since 1.6.1
  */
 struct odbc_cache_tables *ast_odbc_find_table(const char *database, const char *tablename);
@@ -191,6 +198,8 @@
 
 /*!
  * \brief Remove a cache entry from memory
+ * This function may be called to clear entries created and cached by the
+ * ast_odbc_find_table() API call.
  * \param database Name of an ODBC class (used to ensure like-named tables in different databases are not confused)
  * \param tablename Tablename for which a cached record should be removed
  * \retval 0 if the cache entry was removed, or -1 if no matching entry was found.

Modified: trunk/main/config.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/config.c?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/main/config.c (original)
+++ trunk/main/config.c Fri Jul 23 11:19:21 2010
@@ -163,6 +163,7 @@
 
 static struct ast_config_map {
 	struct ast_config_map *next;
+	int priority;
 	char *name;
 	char *driver;
 	char *database;
@@ -1858,7 +1859,7 @@
 	ast_mutex_unlock(&config_lock);
 }
 
-static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
+static int append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
 {
 	struct ast_config_map *map;
 	int length;
@@ -1883,6 +1884,7 @@
 		map->table = map->database + strlen(map->database) + 1;
 		strcpy(map->table, table);
 	}
+	map->priority = priority;
 	map->next = config_maps;
 
 	ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
@@ -1895,8 +1897,9 @@
 {
 	struct ast_config *config, *configtmp;
 	struct ast_variable *v;
-	char *driver, *table, *database, *stringp, *tmp;
+	char *driver, *table, *database, *textpri, *stringp, *tmp;
 	struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
+	int pri;
 
 	clear_config_maps();
 
@@ -1930,6 +1933,10 @@
 		}
 
 		table = strsep(&stringp, ",");
+		textpri = strsep(&stringp, ",");
+		if (!textpri || !(pri = atoi(textpri))) {
+			pri = 1;
+		}
 
 		if (!strcmp(v->name, extconfig_conf)) {
 			ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
@@ -1950,14 +1957,14 @@
 			continue;
 		if (!strcasecmp(v->name, "sipfriends")) {
 			ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
-			append_mapping("sipusers", driver, database, table ? table : "sipfriends");
-			append_mapping("sippeers", driver, database, table ? table : "sipfriends");
+			append_mapping("sipusers", driver, database, table ? table : "sipfriends", pri);
+			append_mapping("sippeers", driver, database, table ? table : "sipfriends", pri);
 		} else if (!strcasecmp(v->name, "iaxfriends")) {
 			ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
-			append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
-			append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
+			append_mapping("iaxusers", driver, database, table ? table : "iaxfriends", pri);
+			append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends", pri);
 		} else 
-			append_mapping(v->name, driver, database, table);
+			append_mapping(v->name, driver, database, table, pri);
 	}
 		
 	ast_config_destroy(config);
@@ -2006,7 +2013,7 @@
 }
 
 /*! \brief Find realtime engine for realtime family */
-static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
+static struct ast_config_engine *find_engine(const char *family, int priority, char *database, int dbsiz, char *table, int tabsiz) 
 {
 	struct ast_config_engine *eng, *ret = NULL;
 	struct ast_config_map *map;
@@ -2014,7 +2021,7 @@
 	ast_mutex_lock(&config_lock);
 
 	for (map = config_maps; map; map = map->next) {
-		if (!strcasecmp(family, map->name)) {
+		if (!strcasecmp(family, map->name) && (priority == map->priority)) {
 			if (database)
 				ast_copy_string(database, map->database, dbsiz);
 			if (table)
@@ -2063,13 +2070,13 @@
 	if (!ast_test_flag(&flags, CONFIG_FLAG_NOREALTIME) && config_engine_list) {
 		struct ast_config_engine *eng;
 
-		eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
+		eng = find_engine(filename, 1, db, sizeof(db), table, sizeof(table));
 
 
 		if (eng && eng->load_func) {
 			loader = eng;
 		} else {
-			eng = find_engine("global", db, sizeof(db), table, sizeof(table));
+			eng = find_engine("global", 1, db, sizeof(db), table, sizeof(table));
 			if (eng && eng->load_func)
 				loader = eng;
 		}
@@ -2107,10 +2114,17 @@
 	char db[256];
 	char table[256];
 	struct ast_variable *res=NULL;
-
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->realtime_func) 
-		res = eng->realtime_func(db, table, ap);
+	int i;
+
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			if (eng->realtime_func && (res = eng->realtime_func(db, table, ap))) {
+				return res;
+			}
+		} else {
+			return NULL;
+		}
+	}
 
 	return res;
 }
@@ -2168,7 +2182,7 @@
 		return 0;	/* There are no engines at all so fail early */
 	}
 
-	eng = find_engine(family, NULL, 0, NULL, 0);
+	eng = find_engine(family, 1, NULL, 0, NULL, 0);
 	if (eng)
 		return 1;
 	return 0;
@@ -2186,12 +2200,18 @@
 	char db[256];
 	char table[256];
 	va_list ap;
-	int res = -1;
+	int res = -1, i;
 
 	va_start(ap, family);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->require_func) {
-		res = eng->require_func(db, table, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			/* If the require succeeds, it returns 0. */
+			if (eng->require_func && !(res = eng->require_func(db, table, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
 	}
 	va_end(ap);
 
@@ -2203,11 +2223,17 @@
 	struct ast_config_engine *eng;
 	char db[256];
 	char table[256];
-	int res = -1;
-
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->unload_func) {
-		res = eng->unload_func(db, table);
+	int res = -1, i;
+
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			if (eng->unload_func) {
+				/* Do this for ALL engines */
+				res = eng->unload_func(db, table);
+			}
+		} else {
+			break;
+		}
 	}
 	return res;
 }
@@ -2219,11 +2245,18 @@
 	char table[256];
 	struct ast_config *res = NULL;
 	va_list ap;
+	int i;
 
 	va_start(ap, family);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->realtime_multi_func) 
-		res = eng->realtime_multi_func(db, table, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			if (eng->realtime_multi_func && (res = eng->realtime_multi_func(db, table, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
 	va_end(ap);
 
 	return res;
@@ -2232,15 +2265,22 @@
 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
 {
 	struct ast_config_engine *eng;
-	int res = -1;
+	int res = -1, i;
 	char db[256];
 	char table[256];
 	va_list ap;
 
 	va_start(ap, lookup);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->update_func) 
-		res = eng->update_func(db, table, keyfield, lookup, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			/* If the update succeeds, it returns 0. */
+			if (eng->update_func && !(res = eng->update_func(db, table, keyfield, lookup, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
 	va_end(ap);
 
 	return res;
@@ -2249,15 +2289,21 @@
 int ast_update2_realtime(const char *family, ...)
 {
 	struct ast_config_engine *eng;
-	int res = -1;
+	int res = -1, i;
 	char db[256];
 	char table[256];
 	va_list ap;
 
 	va_start(ap, family);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->update2_func) 
-		res = eng->update2_func(db, table, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			if (eng->update2_func && !(res = eng->update2_func(db, table, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
 	va_end(ap);
 
 	return res;
@@ -2266,15 +2312,22 @@
 int ast_store_realtime(const char *family, ...)
 {
 	struct ast_config_engine *eng;
-	int res = -1;
+	int res = -1, i;
 	char db[256];
 	char table[256];
 	va_list ap;
 
 	va_start(ap, family);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->store_func) 
-		res = eng->store_func(db, table, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			/* If the store succeeds, it returns 0. */
+			if (eng->store_func && !(res = eng->store_func(db, table, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
 	va_end(ap);
 
 	return res;
@@ -2283,15 +2336,21 @@
 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...)
 {
 	struct ast_config_engine *eng;
-	int res = -1;
+	int res = -1, i;
 	char db[256];
 	char table[256];
 	va_list ap;
 
 	va_start(ap, lookup);
-	eng = find_engine(family, db, sizeof(db), table, sizeof(table));
-	if (eng && eng->destroy_func) 
-		res = eng->destroy_func(db, table, keyfield, lookup, ap);
+	for (i = 1; ; i++) {
+		if ((eng = find_engine(family, i, db, sizeof(db), table, sizeof(table)))) {
+			if (eng->destroy_func && !(res = eng->destroy_func(db, table, keyfield, lookup, ap))) {
+				break;
+			}
+		} else {
+			break;
+		}
+	}
 	va_end(ap);
 
 	return res;

Modified: trunk/res/res_config_odbc.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_config_odbc.c?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/res/res_config_odbc.c (original)
+++ trunk/res/res_config_odbc.c Fri Jul 23 11:19:21 2010
@@ -167,6 +167,7 @@
 	SQLLEN indicator;
 	va_list aq;
 	struct custom_prepare_struct cps = { .sql = sql };
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
 	if (ast_string_field_init(&cps, 256)) {
 		return NULL;
@@ -179,7 +180,7 @@
 		return NULL;
 	}
 
-	obj = ast_odbc_request_obj(database, 0);
+	obj = ast_odbc_request_obj2(database, connected_flag);
 
 	if (!obj) {
 		ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
@@ -325,6 +326,7 @@
 	struct ast_variable *var=NULL;
 	struct ast_config *cfg=NULL;
 	struct ast_category *cat=NULL;
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 	SQLULEN colsize;
 	SQLSMALLINT colcount=0;
 	SQLSMALLINT datatype;
@@ -341,7 +343,7 @@
 	va_copy(aq, ap);
 
 
-	obj = ast_odbc_request_obj(database, 0);
+	obj = ast_odbc_request_obj2(database, connected_flag);
 	if (!obj) {
 		ast_string_field_free_memory(&cps);
 		return NULL;
@@ -479,6 +481,7 @@
 	struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
 	struct odbc_cache_tables *tableptr;
 	struct odbc_cache_columns *column;
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
 	if (!table) {
 		return -1;
@@ -492,7 +495,7 @@
 	}
 
 	tableptr = ast_odbc_find_table(database, table);
-	if (!(obj = ast_odbc_request_obj(database, 0))) {
+	if (!(obj = ast_odbc_request_obj2(database, connected_flag))) {
 		ast_odbc_release_table(tableptr);
 		ast_string_field_free_memory(&cps);
 		return -1;
@@ -716,6 +719,7 @@
 	int res;
 	va_list aq;
 	struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
 	va_copy(cps.ap, ap);
 	va_copy(aq, ap);
@@ -723,7 +727,7 @@
 	if (!table)
 		return -1;
 
-	obj = ast_odbc_request_obj(database, 0);
+	obj = ast_odbc_request_obj2(database, connected_flag);
 	if (!obj)
 		return -1;
 
@@ -790,6 +794,7 @@
 	int res;
 	va_list aq;
 	struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
 	va_copy(cps.ap, ap);
 	va_copy(aq, ap);
@@ -797,7 +802,7 @@
 	if (!table)
 		return -1;
 
-	obj = ast_odbc_request_obj(database, 0);
+	obj = ast_odbc_request_obj2(database, connected_flag);
 	if (!obj)
 		return -1;
 
@@ -883,13 +888,14 @@
 	char last[128] = "";
 	struct config_odbc_obj q;
 	struct ast_flags loader_flags = { 0 };
+	struct ast_flags connected_flag = { RES_ODBC_CONNECTED };
 
 	memset(&q, 0, sizeof(q));
 
 	if (!file || !strcmp (file, "res_config_odbc.conf"))
 		return NULL;		/* cant configure myself with myself ! */
 
-	obj = ast_odbc_request_obj(database, 0);
+	obj = ast_odbc_request_obj2(database, connected_flag);
 	if (!obj)
 		return NULL;
 

Modified: trunk/res/res_odbc.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_odbc.c?view=diff&rev=278957&r1=278956&r2=278957
==============================================================================
--- trunk/res/res_odbc.c (original)
+++ trunk/res/res_odbc.c Fri Jul 23 11:19:21 2010
@@ -131,6 +131,12 @@
 	unsigned int limit;                  /*!< Maximum number of database handles we will allow */
 	int count;                           /*!< Running count of pooled connections */
 	unsigned int idlecheck;              /*!< Recheck the connection if it is idle for this long (in seconds) */
+	unsigned int conntimeout;            /*!< Maximum time the connection process should take */
+	/*! When a connection fails, cache that failure for how long? */
+	struct timeval negative_connection_cache;
+	/*! When a connection fails, when did that last occur? */
+	struct timeval last_negative_connect;
+	/*! List of handles associated with this class */
 	struct ao2_container *obj_container;
 };
 
@@ -661,7 +667,7 @@
 	return stmt;
 }
 
-int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt) 
+int ast_odbc_smart_execute(struct odbc_obj *obj, SQLHSTMT stmt)
 {
 	int res = 0, i;
 	SQLINTEGER nativeerror=0, numfields=0;
@@ -681,9 +687,10 @@
 				}
 			}
 		}
-	} else
+	} else {
 		obj->last_used = ast_tvnow();
-	
+	}
+
 	return res;
 }
 
@@ -746,7 +753,8 @@
 	struct ast_variable *v;
 	char *cat;
 	const char *dsn, *username, *password, *sanitysql;
-	int enabled, pooling, limit, bse, forcecommit, isolation;
+	int enabled, pooling, limit, bse, conntimeout, forcecommit, isolation;
+	struct timeval ncache = { 0, 0 };
 	unsigned int idlecheck;
 	int preconnect = 0, res = 0;
 	struct ast_flags config_flags = { 0 };
@@ -808,6 +816,22 @@
 					sanitysql = v->value;
 				} else if (!strcasecmp(v->name, "backslash_is_escape")) {
 					bse = ast_true(v->value);
+				} else if (!strcasecmp(v->name, "connect_timeout")) {
+					if (sscanf(v->value, "%d", &conntimeout) != 1 || conntimeout < 1) {
+						ast_log(LOG_WARNING, "connect_timeout must be a positive integer\n");
+						conntimeout = 10;
+					}
+				} else if (!strcasecmp(v->name, "negative_connection_cache")) {
+					double dncache;
+					if (sscanf(v->value, "%lf", &dncache) != 1 || dncache < 0) {
+						ast_log(LOG_WARNING, "negative_connection_cache must be a non-negative integer\n");
+						/* 5 minutes sounds like a reasonable default */
+						ncache.tv_sec = 300;
+						ncache.tv_usec = 0;
+					} else {
+						ncache.tv_sec = (int)dncache;
+						ncache.tv_usec = (dncache - ncache.tv_sec) * 1000000;
+					}
 				} else if (!strcasecmp(v->name, "forcecommit")) {
 					forcecommit = ast_true(v->value);
 				} else if (!strcasecmp(v->name, "isolation")) {
@@ -851,6 +875,8 @@
 				new->forcecommit = forcecommit ? 1 : 0;
 				new->isolation = isolation;
 				new->idlecheck = idlecheck;
+				new->conntimeout = conntimeout;
+				new->negative_connection_cache = ncache;
 
 				if (cat)
 					ast_copy_string(new->name, cat, sizeof(new->name));
@@ -923,7 +949,13 @@
 	while ((class = ao2_iterator_next(&aoi))) {
 		if ((a->argc == 2) || (a->argc == 3 && !strcmp(a->argv[2], "all")) || (!strcmp(a->argv[2], class->name))) {
 			int count = 0;
+			char timestr[80];
+			struct ast_tm tm;
+
+			ast_localtime(&class->last_negative_connect, &tm, NULL);
+			ast_strftime(timestr, sizeof(timestr), "%Y-%m-%d %T", &tm);
 			ast_cli(a->fd, "  Name:   %s\n  DSN:    %s\n", class->name, class->dsn);
+			ast_cli(a->fd, "    Last connection attempt: %s\n", timestr);
 
 			if (class->haspool) {
 				struct ao2_iterator aoi2 = ao2_iterator_init(class->obj_container, 0);
@@ -1171,6 +1203,7 @@
 	unsigned char state[10], diagnostic[256];
 
 	if (!(class = ao2_callback(class_container, 0, aoro2_class_cb, (char *) name))) {
+		ast_debug(1, "Class not found!\n");
 		return NULL;
 	}
 
@@ -1183,11 +1216,13 @@
 		if (obj) {
 			ast_assert(ao2_ref(obj, 0) > 1);
 		}
-
-		if (!obj && (class->count < class->limit)) {
+		if (!obj && (class->count < class->limit) &&
+				(time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec)) {
 			obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
 			if (!obj) {
+				class->count--;
 				ao2_ref(class, -1);
+				ast_debug(3, "Unable to allocate object\n");
 				return NULL;
 			}
 			ast_assert(ao2_ref(obj, 0) == 1);
@@ -1228,9 +1263,11 @@
 	} else if (ast_test_flag(&flags, RES_ODBC_INDEPENDENT_CONNECTION)) {
 		/* Non-pooled connections -- but must use a separate connection handle */
 		if (!(obj = ao2_callback(class->obj_container, 0, aoro2_obj_cb, USE_TX))) {
+			ast_debug(1, "Object not found\n");
 			obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor);
 			if (!obj) {
 				ao2_ref(class, -1);
+				ast_debug(3, "Unable to allocate object\n");
 				return NULL;
 			}
 			ast_mutex_init(&obj->lock);
@@ -1271,6 +1308,7 @@
 			if (!(obj = ao2_alloc(sizeof(*obj), odbc_obj_destructor))) {
 				ast_assert(ao2_ref(class, 0) > 1);
 				ao2_ref(class, -1);
+				ast_debug(3, "Unable to allocate object\n");
 				return NULL;
 			}
 			ast_mutex_init(&obj->lock);
@@ -1313,10 +1351,16 @@
 		}
 	}
 
-	if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
+	if (obj && ast_test_flag(&flags, RES_ODBC_CONNECTED) && !obj->up) {
+		/* Check if this connection qualifies for reconnection, with negative connection cache time */
+		if (time(NULL) > class->last_negative_connect.tv_sec + class->negative_connection_cache.tv_sec) {
+			odbc_obj_connect(obj);
+		}
+	} else if (obj && ast_test_flag(&flags, RES_ODBC_SANITY_CHECK)) {
 		ast_odbc_sanity_check(obj);
-	} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck)
+	} else if (obj && obj->parent->idlecheck > 0 && ast_tvdiff_sec(ast_tvnow(), obj->last_used) > obj->parent->idlecheck) {
 		odbc_obj_connect(obj);
+	}
 
 #ifdef DEBUG_THREADS
 	if (obj) {
@@ -1431,11 +1475,12 @@
 
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 		ast_log(LOG_WARNING, "res_odbc: Error AllocHDB %d\n", res);
+		obj->parent->last_negative_connect = ast_tvnow();
 		ast_mutex_unlock(&obj->lock);
 		return ODBC_FAIL;
 	}
-	SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 10, 0);
-	SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) 10, 0);
+	SQLSetConnectAttr(obj->con, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0);
+	SQLSetConnectAttr(obj->con, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *) obj->parent->conntimeout, 0);
 #ifdef NEEDTRACE
 	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACE, &enable, SQL_IS_INTEGER);
 	SQLSetConnectAttr(obj->con, SQL_ATTR_TRACEFILE, tracefile, strlen(tracefile));
@@ -1448,6 +1493,7 @@
 
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 		SQLGetDiagRec(SQL_HANDLE_DBC, obj->con, 1, state, &err, msg, 100, &mlen);
+		obj->parent->last_negative_connect = ast_tvnow();
 		ast_mutex_unlock(&obj->lock);
 		ast_log(LOG_WARNING, "res_odbc: Error SQLConnect=%d errno=%d %s\n", res, (int)err, msg);
 		return ODBC_FAIL;




More information about the asterisk-commits mailing list