[PATCH] dbpool + dbpool support for mysql storage
Alexander Malysh <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Organization | Centrium GmbH |
| Message-ID | <[email protected]> |
Hi list, attached you can find 2 patches. 1) dbpool-1.diff: makes dbpool more generic in order to easy add libsdb or another db support later. For now is only mysql supported. Fixed varios rase conditions and adds more regress. test. Here is the diffstat for it: gwlib/dbpool.c | 334 235 + 99 - 0 ! gwlib/dbpool.h | 37 27 + 10 - 0 ! test/test_dbpool.c | 105 55 + 50 - 0 ! 3 files changed, 317 insertions(+), 159 deletions(-) 2) mysql-dlr-dbpool.diff: adds dbpool support for mysql dlr storage and makes some simplifications in insert/update/select queries. This patch adds also new config option "max-connections" into mysql-connection group. Here is the diffstat: doc/examples/dlr-mysql.conf | 5 4 + 1 - 0 ! doc/userguide/userguide.xml | 3 2 + 1 - 0 ! gw/dlr_mysql.c | 186 93 + 93 - 0 ! gwlib/cfg.def | 1 1 + 0 - 0 ! 4 files changed, 100 insertions(+), 95 deletions(-) Votes and comments are very very welcome... -- Best regards / Mit besten Grüßen aus Düsseldorf Dipl.-Ing. Alexander Malysh ___________________________________________ Centrium GmbH Vogelsanger Weg 80 40470 Düsseldorf Fon: +49 (0211) 74 84 51 80 Fax: +49 (0211) 277 49 109 email: a.malysh at centrium.de web: www.centrium.de msn: olek2002 at hotmail.com icq: 98063111 ___________________________________________ Please avoid sending me Word or PowerPoint attachments. See http://www.fsf.org/philosophy/no-word-attachments.html
mysql-dlr-dbpool.diff
(text/x-diff, 11.1 KB)
Index: gw/dlr_mysql.c
===================================================================
RCS file: /home/cvs/gateway/gw/dlr_mysql.c,v
retrieving revision 1.1
diff -a -u -r1.1 dlr_mysql.c
--- gw/dlr_mysql.c 19 Jun 2003 22:11:50 -0000 1.1
+++ gw/dlr_mysql.c 25 Jun 2003 14:46:10 -0000
@@ -10,36 +10,83 @@
*/
#include "gwlib/gwlib.h"
+#include "gwlib/dbpool.h"
#include "dlr_p.h"
#ifdef DLR_MYSQL
#include <mysql/mysql.h>
-static MYSQL *connection;
-static MYSQL mysql;
/*
- * Database fields, which we are use.
+ * Our connection pool to mysql.
*/
-static struct dlr_db_fields *fields = NULL;
+static DBPool *pool = NULL;
/*
- * Mutex to protec access to mysql.
+ * Database fields, which we are use.
*/
-static Mutex *dlr_mutex = NULL;
+static struct dlr_db_fields *fields = NULL;
+
+
+static void mysql_update(const Octstr *sql)
+{
+ int state;
+ DBPoolConn *pc;
+
+#if defined(DLR_TRACE)
+ debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
+#endif
+
+ pc = dbpool_conn_consume(pool);
+ if (pc == NULL) {
+ error(0, "MYSQL: Database pool got no connection! DB update failed!");
+ return;
+ }
+
+ state = mysql_query(pc->conn, octstr_get_cstr(sql));
+ if (state != 0)
+ error(0, "MYSQL: %s", mysql_error(pc->conn));
+
+ dbpool_conn_produce(pc);
+}
+
+static MYSQL_RES* mysql_select(const Octstr *sql)
+{
+ int state;
+ MYSQL_RES *result = NULL;
+ DBPoolConn *pc;
+
+#if defined(DLR_TRACE)
+ debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
+#endif
+
+ pc = dbpool_conn_consume(pool);
+ if (pc == NULL) {
+ error(0, "MYSQL: Database pool got no connection! DB update failed!");
+ return NULL;
+ }
+
+ state = mysql_query(pc->conn, octstr_get_cstr(sql));
+ if (state != 0) {
+ error(0, "MYSQL: %s", mysql_error(pc->conn));
+ } else {
+ result = mysql_store_result(pc->conn);
+ }
+ dbpool_conn_produce(pc);
+
+ return result;
+}
static void dlr_mysql_shutdown()
{
- mysql_close(connection);
+ dbpool_destroy(pool);
dlr_db_fields_destroy(fields);
- mutex_destroy(dlr_mutex);
}
static void dlr_mysql_add(struct dlr_entry *entry)
{
Octstr *sql;
- int state;
sql = octstr_format("INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES "
"('%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%d');",
@@ -53,17 +100,9 @@
octstr_get_cstr(entry->destination), octstr_get_cstr(entry->service), octstr_get_cstr(entry->url),
entry->mask, octstr_get_cstr(entry->boxc_id), 0);
-#if defined(DLR_TRACE)
- debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
-#endif
- mutex_lock(dlr_mutex);
-
- state = mysql_query(connection, octstr_get_cstr(sql));
- if (state != 0)
- error(0, "MYSQL: %s", mysql_error(connection));
+ mysql_update(sql);
- mutex_unlock(dlr_mutex);
octstr_destroy(sql);
dlr_entry_destroy(entry);
}
@@ -72,7 +111,6 @@
{
struct dlr_entry *res = NULL;
Octstr *sql;
- int state;
MYSQL_RES *result;
MYSQL_ROW row;
@@ -83,20 +121,13 @@
octstr_get_cstr(fields->table), octstr_get_cstr(fields->field_smsc),
octstr_get_cstr(smsc), octstr_get_cstr(fields->field_ts), octstr_get_cstr(ts));
-#if defined(DLR_TRACE)
- debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
-#endif
- mutex_lock(dlr_mutex);
- state = mysql_query(connection, octstr_get_cstr(sql));
+ result = mysql_select(sql);
octstr_destroy(sql);
- if (state != 0) {
- error(0, "MYSQL: %s", mysql_error(connection));
- mutex_unlock(dlr_mutex);
+
+ if (result == NULL) {
return NULL;
}
- result = mysql_store_result(connection);
- mutex_unlock(dlr_mutex);
if (mysql_num_rows(result) < 1) {
debug("dlr.mysql", 0, "no rows found");
mysql_free_result(result);
@@ -129,30 +160,21 @@
static void dlr_mysql_remove(const Octstr *smsc, const Octstr *ts, const Octstr *dst)
{
Octstr *sql;
- int state;
debug("dlr.mysql", 0, "removing DLR from database");
sql = octstr_format("DELETE FROM %s WHERE %s='%s' AND %s='%s' LIMIT 1;",
octstr_get_cstr(fields->table), octstr_get_cstr(fields->field_smsc),
octstr_get_cstr(smsc), octstr_get_cstr(fields->field_ts), octstr_get_cstr(ts));
-#if defined(DLR_TRACE)
- debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
-#endif
- mutex_lock(dlr_mutex);
- state = mysql_query(connection, octstr_get_cstr(sql));
+ mysql_update(sql);
+
octstr_destroy(sql);
- if (state != 0) {
- error(0, "MYSQL: %s", mysql_error(connection));
- }
- mutex_unlock(dlr_mutex);
}
static void dlr_mysql_update(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int status)
{
Octstr *sql;
- int state;
debug("dlr.mysql", 0, "updating DLR status in database");
sql = octstr_format("UPDATE %s SET %s=%d WHERE %s='%s' AND %s='%s' LIMIT 1;",
@@ -161,43 +183,27 @@
octstr_get_cstr(fields->field_smsc), octstr_get_cstr(smsc),
octstr_get_cstr(fields->field_ts), octstr_get_cstr(ts));
-#if defined(DLR_TRACE)
- debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
-#endif
+ mysql_update(sql);
- mutex_lock(dlr_mutex);
- state = mysql_query(connection, octstr_get_cstr(sql));
octstr_destroy(sql);
- if (state != 0) {
- error(0, "MYSQL: %s", mysql_error(connection));
- }
- mutex_unlock(dlr_mutex);
}
static long dlr_mysql_messages(void)
{
Octstr *sql;
- int state;
long res;
MYSQL_RES *result;
MYSQL_ROW row;
sql = octstr_format("SELECT count(*) FROM %s;", octstr_get_cstr(fields->table));
-#if defined(DLR_TRACE)
- debug("dlr.mysql", 0, "sql: %s", octstr_get_cstr(sql));
-#endif
- mutex_lock(dlr_mutex);
- state = mysql_query(connection, octstr_get_cstr(sql));
+ result = mysql_select(sql);
octstr_destroy(sql);
- if (state != 0) {
- error(0, "MYSQL: %s", mysql_error(connection));
- mutex_unlock(dlr_mutex);
+
+ if (result == NULL) {
return -1;
}
- result = mysql_store_result(connection);
- mutex_unlock(dlr_mutex);
if (mysql_num_rows(result) < 1) {
debug("dlr.mysql", 0, "Could not get count of DLR table");
mysql_free_result(result);
@@ -211,25 +217,18 @@
}
res = atol(row[0]);
mysql_free_result(result);
+
return res;
}
static void dlr_mysql_flush(void)
{
- Octstr *sql;
- int state;
- MYSQL_RES *result;
-
- sql = octstr_format("DELETE FROM %s;", octstr_get_cstr(fields->table));
- mutex_lock(dlr_mutex);
- state = mysql_query(connection, octstr_get_cstr(sql));
- octstr_destroy(sql);
- if (state != 0) {
- error(0, "MYSQL: %s", mysql_error(connection));
- }
- result = mysql_store_result(connection);
- mutex_unlock(dlr_mutex);
- mysql_free_result(result);
+ Octstr *sql;
+
+ sql = octstr_format("DELETE FROM %s;", octstr_get_cstr(fields->table));
+
+ mysql_update(sql);
+ octstr_destroy(sql);
}
static struct dlr_storage handles = {
@@ -249,6 +248,8 @@
List *grplist;
Octstr *mysql_host, *mysql_user, *mysql_pass, *mysql_db, *mysql_id;
Octstr *p = NULL;
+ long pool_size;
+ DBConf *db_conf = NULL;
/*
* check for all mandatory directives that specify the field names
@@ -286,6 +287,9 @@
octstr_destroy(p);
list_destroy(grplist, NULL);
+ if (cfg_get_integer(&pool_size, grp, octstr_imm("max-connections")) == -1 || pool_size == 0)
+ pool_size = 1;
+
if (!(mysql_host = cfg_get(grp, octstr_imm("host"))))
panic(0, "DLR: MySQL: directive 'host' is not specified!");
if (!(mysql_user = cfg_get(grp, octstr_imm("mysql-username"))))
@@ -298,30 +302,26 @@
/*
* ok, ready to connect to MySQL
*/
- mysql_init(&mysql);
- connection = mysql_real_connect(&mysql,
- octstr_get_cstr(mysql_host), octstr_get_cstr(mysql_user),
- octstr_get_cstr(mysql_pass), octstr_get_cstr(mysql_db),
- 0, NULL, 0);
+ db_conf = gw_malloc(sizeof(DBConf));
+ gw_assert(db_conf != NULL);
+
+ db_conf->mysql = gw_malloc(sizeof(MySQLConf));
+ gw_assert(db_conf->mysql != NULL);
+
+ db_conf->mysql->host = mysql_host;
+ db_conf->mysql->username = mysql_user;
+ db_conf->mysql->password = mysql_pass;
+ db_conf->mysql->database = mysql_db;
+
+ pool = dbpool_create(DBPOOL_MYSQL, db_conf, pool_size);
+ gw_assert(pool != NULL);
/*
* XXX should a failing connect throw panic?!
*/
- if (connection == NULL) {
- error(0,"DLR: MySQL: can not connect to database!");
- panic(0,"MYSQL: %s", mysql_error(&mysql));
- } else {
- info(0,"Connected to mysql server at %s.", octstr_get_cstr(mysql_host));
- info(0,"MYSQL: server version %s, client version %s.",
- mysql_get_server_info(&mysql), mysql_get_client_info());
- }
-
- dlr_mutex = mutex_create();
+ if (dbpool_conn_count(pool) == 0)
+ panic(0,"DLR: MySQL: database pool has no connections!");
- octstr_destroy(mysql_db);
- octstr_destroy(mysql_host);
- octstr_destroy(mysql_user);
- octstr_destroy(mysql_pass);
octstr_destroy(mysql_id);
return &handles;
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.89
diff -a -u -r1.89 cfg.def
--- gwlib/cfg.def 22 Jun 2003 22:23:00 -0000 1.89
+++ gwlib/cfg.def 25 Jun 2003 14:46:10 -0000
@@ -368,6 +368,7 @@
OCTSTR(mysql-username)
OCTSTR(mysql-password)
OCTSTR(database)
+ OCTSTR(max-connections)
)
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.225
diff -a -u -r1.225 userguide.xml
--- doc/userguide/userguide.xml 20 Jun 2003 13:31:15 -0000 1.225
+++ doc/userguide/userguide.xml 25 Jun 2003 14:46:12 -0000
@@ -1025,7 +1025,7 @@
breaks if either the included file can not be opened and
processed or the included file has been processed already in
the stack and a recursive cycling has been detected.</para>
-
+
</sect2>
<sect2>
@@ -5502,6 +5502,7 @@
mysql-username = foo
mysql-password = bar
database = dlr
+max-connections = 1
group = dlr-db
id = mydlr
Index: doc/examples/dlr-mysql.conf
===================================================================
RCS file: /home/cvs/gateway/doc/examples/dlr-mysql.conf,v
retrieving revision 1.2
diff -a -u -r1.2 dlr-mysql.conf
--- doc/examples/dlr-mysql.conf 5 Aug 2002 21:52:28 -0000 1.2
+++ doc/examples/dlr-mysql.conf 25 Jun 2003 14:46:12 -0000
@@ -1,4 +1,4 @@
-#
+#
# DLR with MySQL support configuration
#
# Example defining a MySQL database connection resource and
@@ -11,6 +11,9 @@
mysql-username = foo
mysql-password = bar
database = dlr
+# max count of connections that will be opened for dbpool
+# default is 1
+max-connections = 1
#
# Create the required table for the DLR support in the database
dbpool-1.diff
(text/x-diff, 22.4 KB)
Index: gwlib/dbpool.c =================================================================== RCS file: /home/cvs/gateway/gwlib/dbpool.c,v retrieving revision 1.4 diff -a -u -r1.4 dbpool.c --- gwlib/dbpool.c 18 Jun 2003 21:48:38 -0000 1.4 +++ gwlib/dbpool.c 25 Jun 2003 14:29:30 -0000 @@ -1,174 +1,281 @@ /* * dbpool.c - implement database connection pool + * + * Stipe Tolj <[email protected]> + * 2003 Initial version. + * Alexander Malysh <[email protected]> + * 2003 Made dbpool more generic. */ #include "gwlib.h" #include "dbpool.h" -#ifdef HAVE_MYSQL -#include <mysql.h> -#endif - #ifdef HAVE_DBPOOL -typedef struct { - Octstr *host; - Octstr *username; - Octstr *password; - Octstr *database; -} MySQLConf; - -typedef struct { - Octstr *url; -} SDBConf; +struct db_ops { + /* + * Open db connection with given config params. + * Config params are specificaly for each database type. + * return NULL if error occurs ; established connection's pointer otherwise + */ + void* (*open) (const DBConf *conf); + /* + * close given connection. + */ + void (*close) (void *conn); + /* + * check if given connection still alive, + * return -1 if not or error occurs ; 0 if all was fine + * NOTE: this function is optional + */ + int (*check) (void *conn); + /* + * Destroy specificaly configuration struct. + */ + void (*conf_destroy) (DBConf *conf); +}; struct DBPool { List *pool; /* queue representing the pool */ - enum dbpool_type type; - unsigned int max_size; - void *conf; /* the database type specific configuration block */ + unsigned int max_size; /* max #connections */ + unsigned int curr_size; /* current #connections */ + DBConf *conf; /* the database type specific configuration block */ + struct db_ops *db_ops; + enum db_type db_type; }; -/* Increase pool size by #conn connections. */ -unsigned int dbpool_increase_mysql(DBPool *p, unsigned int c) +/* start of mysql block */ +#ifdef HAVE_MYSQL +#include <mysql.h> + +static void* mysql_open_conn(const DBConf *db_conf) { - unsigned int i, n = 0; - long len; - MySQLConf *conf; + MYSQL *mysql = NULL; + MySQLConf *conf = db_conf->mysql; /* make compiler happy */ - gw_assert(p->conf != NULL); + /* sanity check */ + if (conf == NULL) + return NULL; + + /* pre-allocate */ + mysql = gw_malloc(sizeof(MYSQL)); + gw_assert(mysql != NULL); + + /* initialize mysql structures */ + if (!mysql_init(mysql)) { + error(0, "MYSQL: init failed!"); + error(0, "MYSQL: %s", mysql_error(mysql)); + goto failed; + } - conf = p->conf; + if (!mysql_real_connect(mysql, octstr_get_cstr(conf->host), + octstr_get_cstr(conf->username), + octstr_get_cstr(conf->password), + octstr_get_cstr(conf->database), 0, NULL, 0)) { + error(0, "MYSQL: can not connect to database!"); + error(0, "MYSQL: %s", mysql_error(mysql)); + goto failed; + } - list_lock(p->pool); + info(0,"MYSQL: Connected to server at %s.", octstr_get_cstr(conf->host)); + info(0, "MYSQL: server version %s, client version %s.", + mysql_get_server_info(mysql), mysql_get_client_info()); - /* ensure we don't increase more items than the max_size border */ - c = (len = list_len(p->pool)) + c > p->max_size ? p->max_size - len : c; + return mysql; - for (i = 0; i < c; i++) { - MYSQL *mysql; - DBPoolConn *pc; +failed: + if (mysql != NULL) gw_free(mysql); + return NULL; +} - /* pre-allocate */ - mysql = gw_malloc(sizeof(MYSQL)); - pc = gw_malloc(sizeof(DBPoolConn)); - - /* assign pool connection */ - pc->conn = mysql; - pc->pool = p; - - if (!mysql_init(pc->conn)) { - error(0, "MYSQL: init failed!"); - error(0, "MYSQL: %s", mysql_error(pc->conn)); - } +static void mysql_close_conn(void *conn) +{ + if (conn == NULL) + return; - if (!mysql_real_connect(mysql, octstr_get_cstr(conf->host), - octstr_get_cstr(conf->username), - octstr_get_cstr(conf->password), - octstr_get_cstr(conf->database), 0, NULL, 0)) { - error(0, "MYSQL: can not connect to database!"); - error(0, "MYSQL: %s", mysql_error(pc->conn)); - } else { - - /* drop the connection to the pool */ - list_produce(p->pool, pc); - n++; + mysql_close((MYSQL*) conn); + gw_free(conn); +} - info(0,"Connected to mysql server at %s.", octstr_get_cstr(conf->host)); - debug("gwlib.dbpool", 0, "MYSQL: server version %s, client version %s.", - mysql_get_server_info(pc->conn), mysql_get_client_info()); - } +static int mysql_check_conn(void *conn) +{ + if (conn == NULL) + return -1; + + if (mysql_ping((MYSQL*) conn)) { + error(0, "MYSQL: database check failed!"); + error(0, "MYSQL: %s", mysql_error(conn)); + return -1; } - list_unlock(p->pool); - return n; + return 0; } - -/* Initialize the mysql database config block */ -static void dbpool_startup_mysql(DBPool *p, void *data) +static void mysql_conf_destroy(DBConf *db_conf) { - list_add_producer(p->pool); - p->conf = data; + MySQLConf *conf = db_conf->mysql; + + octstr_destroy(conf->host); + octstr_destroy(conf->username); + octstr_destroy(conf->password); + octstr_destroy(conf->database); + + gw_free(conf); + gw_free(db_conf); } +static struct db_ops mysql_ops = { + .open = mysql_open_conn, + .close = mysql_close_conn, + .check = mysql_check_conn, + .conf_destroy = mysql_conf_destroy +}; +#endif /* HAVE_MYSQL */ +/* end of mysql block */ + static void dbpool_conn_destroy(DBPoolConn *conn) { - gw_assert(conn != NULL && conn->conn != NULL); - mysql_close(conn->conn); - gw_free(conn->conn); + gw_assert(conn != NULL); + + if (conn->conn != NULL) + conn->pool->db_ops->close(conn->conn); gw_free(conn); } - /************************************************************************* * public functions */ -DBPool *dbpool_create(enum dbpool_type type, void *data, unsigned int connections) +DBPool *dbpool_create(enum db_type db_type, DBConf *conf, unsigned int connections) { DBPool *p; + if (conf == NULL) + return NULL; + p = gw_malloc(sizeof(DBPool)); + gw_assert(p != NULL); p->pool = list_create(); - p->type = type; + list_add_producer(p->pool); p->max_size = connections; - p->conf = NULL; + p->curr_size = 0; + p->conf = conf; + p->db_type = db_type; + + switch(db_type) { +#ifdef HAVE_MYSQL + case DBPOOL_MYSQL: + p->db_ops = &mysql_ops; + break; +#endif + case DBPOOL_SDB: + panic(0, "DBPOOL for libsdb not yet implemented"); + default: + panic(0, "Unknown dbpool type defined."); + } - dbpool_startup_mysql(p, data); - dbpool_increase_mysql(p, connections); + /* + * XXX what is todo here if not all connections + * where established here ??? + */ + dbpool_increase(p, connections); return p; } void dbpool_destroy(DBPool *p) { - gw_assert(p != NULL && p->pool != NULL); + if (p == NULL) + return; /* nothing todo here */ + + gw_assert(p->pool != NULL && p->db_ops != NULL); + + list_remove_producer(p->pool); list_destroy(p->pool, (void*) dbpool_conn_destroy); - gw_free(p->conf); + switch (p->db_type) { + case DBPOOL_MYSQL: + p->db_ops->conf_destroy(p->conf); + break; + case DBPOOL_SDB: + panic(0, "DBPOOL for libsdb not yet implemented"); + default: + panic(0, "Unknown dbpool type defined."); + } + gw_free(p); } -unsigned int dbpool_increase(DBPool *p, unsigned int conn) +unsigned int dbpool_increase(DBPool *p, unsigned int count) { - return dbpool_increase_mysql(p, conn); + unsigned int i, opened = 0; + + gw_assert(p != NULL && p->conf != NULL && p->db_ops != NULL && p->db_ops->open != NULL); + + + /* lock dbpool for updates */ + list_lock(p->pool); + + /* ensure we don't increase more items than the max_size border */ + for (i=0; i < count && p->curr_size < p->max_size; i++) { + void *conn = p->db_ops->open(p->conf); + if (conn != NULL) { + DBPoolConn *pc = gw_malloc(sizeof(DBPoolConn)); + gw_assert(pc != NULL); + + pc->conn = conn; + pc->pool = p; + + p->curr_size++; + opened++; + list_produce(p->pool, pc); + } + } + + /* unlock dbpool for updates */ + list_unlock(p->pool); + + return opened; } - + unsigned int dbpool_decrease(DBPool *p, unsigned int c) { - long len; unsigned int i; - gw_assert(p != NULL && p->pool != NULL); + gw_assert(p != NULL && p->pool != NULL && p->db_ops != NULL && p->db_ops->close != NULL); + /* lock dbpool for updates */ list_lock(p->pool); - /* Ensure we don't decrease more items then ammount in the queue */ - c = (len = list_len(p->pool)) < c ? len : c; - - /* - * Ensure we don't try to decrease more then available in pool, - * because this would block while list_consume(). + /* + * Ensure we don't try to decrease more then available in pool. */ for (i = 0; i < c; i++) { DBPoolConn *pc; - - pc = list_consume(p->pool); + + /* list_extract_first doesn't block even if no conn here */ + pc = list_extract_first(p->pool); + + /* no conn availible anymore */ + if (pc == NULL) + break; /* close connections and destroy pool connection */ dbpool_conn_destroy(pc); + p->curr_size--; } + + /* unlock dbpool for updates */ list_unlock(p->pool); - return c; + return i; } @@ -180,10 +287,12 @@ } -void *dbpool_conn_consume(DBPool *p) +DBPoolConn *dbpool_conn_consume(DBPool *p) { DBPoolConn *pc; + gw_assert(p != NULL && p->pool != NULL); + /* garantee that you deliver a valid connection to the caller */ while ((pc = list_consume(p->pool)) != NULL) { @@ -191,9 +300,20 @@ * XXX check that the connection is still existing. * Is this a performance bottle-neck?! */ - if (!pc->conn || mysql_ping(pc->conn) != 0) { - /* something was wrong, drop the connection */ + if (!pc->conn || (p->db_ops->check && p->db_ops->check(pc->conn) != 0)) { + /* something was wrong, reinitialize the connection */ + /* lock dbpool for update */ + list_lock(p->pool); dbpool_conn_destroy(pc); + p->curr_size--; + /* unlock dbpool for update */ + list_unlock(p->pool); + /* + * maybe not needed, just try to get next connection, but it + * can be dangeros if all connections where broken, then we will + * block here for ever. + */ + dbpool_increase(p, 1); } else { break; } @@ -205,35 +325,51 @@ void dbpool_conn_produce(DBPoolConn *pc) { + gw_assert(pc != NULL && pc->conn != NULL && pc->pool != NULL && pc->pool->pool != NULL); + list_produce(pc->pool->pool, pc); } unsigned int dbpool_check(DBPool *p) { - long i, len, n = 0; + long i, len, n = 0, reinit = 0; - gw_assert(p != NULL && p->pool != NULL); + gw_assert(p != NULL && p->pool != NULL && p->db_ops != NULL); + + /* + * First check if db_ops->check function pointer is here. + * NOTE: db_ops->check is optional, so if it not here, the + * we have nothing todo and we return just list length. + */ + if (p->db_ops->check == NULL) + return list_len(p->pool); list_lock(p->pool); - len = list_len(p->pool); for (i = 0; i < len; i++) { DBPoolConn *pconn; - + pconn = list_get(p->pool, i); - if (mysql_ping(pconn->conn) != 0) { - /* something was wrong, drop the connection */ + if (p->db_ops->check(pconn->conn) != 0) { + /* something was wrong, reinitialize the connection */ list_delete(p->pool, i, 1); dbpool_conn_destroy(pconn); + p->curr_size--; + reinit++; } else { n++; } } list_unlock(p->pool); + /* reinitialize brocken connections */ + if (reinit > 0) + n += dbpool_increase(p, reinit); + + return n; } - + #endif /* HAVE_DBPOOL */ Index: gwlib/dbpool.h =================================================================== RCS file: /home/cvs/gateway/gwlib/dbpool.h,v retrieving revision 1.1 diff -a -u -r1.1 dbpool.h --- gwlib/dbpool.h 11 Mar 2003 15:30:22 -0000 1.1 +++ gwlib/dbpool.h 25 Jun 2003 14:29:31 -0000 @@ -10,7 +10,7 @@ #endif /* supported databases for connection pools */ -enum dbpool_type { +enum db_type { DBPOOL_MYSQL, DBPOOL_SDB }; @@ -27,19 +27,35 @@ * re-storage into the pool (also disallowing to insert the conn into an * other pool). */ -typedef struct { + typedef struct { void *conn; /* the pointer holding the database specific connection */ DBPool *pool; /* pointer of the pool where this connection belongs to */ } DBPoolConn; + +typedef struct { + Octstr *host; + Octstr *username; + Octstr *password; + Octstr *database; +} MySQLConf; + +typedef struct { + Octstr *url; +} SDBConf; + +typedef union { + MySQLConf *mysql; + SDBConf *sdb; +} DBConf; + /* * Create a database pool with #connections of connections. The pool - * is stored within a queue list. Threads that want to use the pool - * have to register before extracting connections. + * is stored within a queue list. * Returns a pointer to the pool object on success or NULL if the * creation fails. */ -DBPool *dbpool_create(enum dbpool_type type, void *conf, unsigned int connections); +DBPool *dbpool_create(enum db_type db_type, DBConf *conf, unsigned int connections); /* * Destroys the database pool. Includes also shutdowning all existing @@ -51,7 +67,7 @@ * Increase the connection size of the pool by #conn connections. * Beware that you can't increase a pool size to more then the initial * dbpool_create() call defined and opened the maximum pool connections. - * Returns how many connections have been additionally created and + * Returns how many connections have been additionally created and * inserted to the pool. */ unsigned int dbpool_increase(DBPool *p, unsigned int conn); @@ -73,18 +89,19 @@ /* * Gets and active connection from the pool and returns it. - * The caller can use it then for queuery oeprations and has to put it + * The caller can use it then for queuery operations and has to put it * back into the pool via dbpool_conn_produce(conn). - * If no connection is in pool or some error occures, returns NULL. + * If no connection is in pool and DBPool is not in destroying phase then + * will block until connection is available otherwise returns NULL. */ -void *dbpool_conn_consume(DBPool *p); +DBPoolConn *dbpool_conn_consume(DBPool *p); /* * Returns a used connection to the pool again. * The connection is returned to it's domestic pool for further extraction * using dbpool_conn_consume(). */ -void dbpool_conn_produce(DBPoolConn *pc); +void dbpool_conn_produce(DBPoolConn *conn); /* * Perfoms a check of all connections within the pool and tries to Index: test/test_dbpool.c =================================================================== RCS file: /home/cvs/gateway/test/test_dbpool.c,v retrieving revision 1.2 diff -a -u -r1.2 test_dbpool.c --- test/test_dbpool.c 19 Jun 2003 15:06:17 -0000 1.2 +++ test/test_dbpool.c 25 Jun 2003 14:29:31 -0000 @@ -15,14 +15,7 @@ #define MAX_THREADS 1024 -typedef struct { - Octstr *host; - Octstr *username; - Octstr *password; - Octstr *database; -} MySQLConf; - -static void help(void) +static void help(void) { info(0, "Usage: test_dbpool [options] ..."); info(0, "where options are:"); @@ -41,7 +34,7 @@ info(0, "-q number"); info(0, " run a set of queries on the database connection pool (default: 100)"); info(0, "-t number"); - info(0, " how many query cleint threads should be used (default: 1)"); + info(0, " how many query client threads should be used (default: 1)"); info(0, "-S string"); info(0, " the SQL string that is performed while the queries (default: SHOW STATUS)"); } @@ -49,9 +42,11 @@ /* global variables */ static unsigned long queries = 100; static Octstr *sql; +static unsigned int pool_size = 5; + -static void client_thread(void *arg) -{ +static void client_thread(void *arg) +{ unsigned long i, succeeded, failed; DBPool *pool = arg; @@ -67,7 +62,7 @@ /* provide us with a connection from the pool */ pconn = dbpool_conn_consume(pool); - debug("",0,"Query %ld/%ld: mysql thread id %ld obj at %p", + debug("",0,"Query %ld/%ld: mysql thread id %ld obj at %p", i, queries, mysql_thread_id(pconn->conn), (void*) pconn->conn); state = mysql_query(pconn->conn, octstr_get_cstr(sql)); @@ -86,44 +81,61 @@ info(0, "This thread: %ld succeeded, %ld failed.", succeeded, failed); } +static void inc_dec_thread(void *arg) +{ + DBPool *pool = arg; + int ret; + + /* decrease */ + info(0,"Decreasing pool by half of size, which is %d connections", abs(pool_size/2)); + ret = dbpool_decrease(pool, abs(pool_size/2)); + debug("",0,"Decreased by %d connections", ret); + debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool)); + + /* increase */ + info(0,"Increasing pool again by %d connections", pool_size); + ret = dbpool_increase(pool, pool_size); + debug("",0,"Increased by %d connections", ret); + debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool)); +} int main(int argc, char **argv) { DBPool *pool; - MySQLConf *conf; - unsigned int pool_size = 5; + DBConf *conf; unsigned int num_threads = 1; unsigned long i, threads[MAX_THREADS]; - int opt, ret; + int opt; time_t start, end; double run_time; gwlib_init(); - conf = gw_malloc(sizeof(MySQLConf)); - conf->host = conf->username = conf->password = conf->database = NULL; + conf = gw_malloc(sizeof(DBConf)); + conf->mysql = gw_malloc(sizeof(MySQLConf)); + conf->mysql->host = conf->mysql->username = conf->mysql->password = conf->mysql->database = NULL; sql = octstr_imm("SHOW STATUS"); while ((opt = getopt(argc, argv, "v:h:u:p:d:s:q:t:S:")) != EOF) { - switch (opt) { + switch (opt) { case 'v': log_set_output_level(atoi(optarg)); break; - + case 'h': - conf->host = octstr_create(optarg); + conf->mysql->host = octstr_create(optarg); break; case 'u': - conf->username = octstr_create(optarg); + conf->mysql->username = octstr_create(optarg); break; case 'p': - conf->password = octstr_create(optarg); + conf->mysql->password = octstr_create(optarg); break; case 'd': - conf->database = octstr_create(optarg); + conf->mysql->database = octstr_create(optarg); break; case 'S': @@ -150,53 +162,46 @@ panic(0, "Stopping."); } } - + if (!optind) { help(); exit(0); } /* check if we have the database connection details */ - if (!conf->host || !conf->username || - !conf->password || !conf->database) { + if (!conf->mysql->host || !conf->mysql->username || + !conf->mysql->password || !conf->mysql->database) { help(); panic(0, "Database connection details are not fully provided!"); } /* create */ info(0,"Creating database pool to `%s' with %d connections.", - octstr_get_cstr(conf->host), pool_size); - pool = dbpool_create(DBPOOL_MYSQL, conf, pool_size); - debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool)); - - /* decrease */ - info(0,"Decreasing pool by half of size, which is %d connections", abs(pool_size/2)); - ret = dbpool_decrease(pool, abs(pool_size/2)); - debug("",0,"Decreased by %d connections", ret); + octstr_get_cstr(conf->mysql->host), pool_size); + pool = dbpool_create(DBPOOL_MYSQL, conf, pool_size); debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool)); - /* increase */ - info(0,"Increasing pool again by %d connections", pool_size); - ret = dbpool_increase(pool, pool_size); - debug("",0,"Increased by %d connections", ret); - debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool)); + /* increase/decrease regress test */ + for (i = 0; i < num_threads; ++i) + threads[i] = gwthread_create(inc_dec_thread, pool); + for (i = 0; i < num_threads; ++i) + gwthread_join(threads[i]); + + info(0, "Connections within pool: %ld", dbpool_conn_count(pool)); + info(0,"Checked pool, %d connections still active and ok", dbpool_check(pool)); /* queries */ info(0,"SQL query is `%s'", octstr_get_cstr(sql)); time(&start); - if (num_threads == 1) { - client_thread(pool); - } else { - for (i = 0; i < num_threads; ++i) - threads[i] = gwthread_create(client_thread, pool); - for (i = 0; i < num_threads; ++i) - gwthread_join(threads[i]); - } + for (i = 0; i < num_threads; ++i) + threads[i] = gwthread_create(client_thread, pool); + for (i = 0; i < num_threads; ++i) + gwthread_join(threads[i]); time(&end); - + run_time = difftime(end, start); - info(0, "%ld requests in %f seconds, %f requests/s.", - (queries * num_threads), run_time, (queries * num_threads) / run_time); + info(0, "%ld requests in %.2f seconds, %.2f requests/s.", + (queries * num_threads), run_time, (float) (queries * num_threads) / (run_time==0?1:run_time)); /* check all active connections */ debug("",0,"Connections within pool: %ld", dbpool_conn_count(pool));