# HG changeset patch # User Timo Sirainen # Date 1290012944 0 # Node ID 990abbb6d3ddd318a5243dbfa305abe40ded68f9 # Parent 2456cd0917d3bf8348a40faae38a2d34593062d6 lib-sql: Connect earlier to fallback host when using multiple hosts and primary fails. diff -r 2456cd0917d3 -r 990abbb6d3dd src/lib-sql/driver-sqlpool.c --- a/src/lib-sql/driver-sqlpool.c Wed Nov 17 14:33:46 2010 +0000 +++ b/src/lib-sql/driver-sqlpool.c Wed Nov 17 16:55:44 2010 +0000 @@ -70,6 +70,9 @@ extern struct sql_db driver_sqlpool_db; +static struct sqlpool_connection * +sqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host, + unsigned int host_idx); static void driver_sqlpool_query_callback(struct sql_result *result, struct sqlpool_request *request); @@ -172,39 +175,6 @@ (void)sql_connect(conndb); } -static void -sqlpool_state_changed(struct sql_db *conndb, enum sql_db_state prev_state, - void *context) -{ - struct sqlpool_db *db = context; - - if (conndb->state == SQL_DB_STATE_IDLE) { - conndb->connect_failure_count = 0; - conndb->connect_delay = SQL_CONNECT_MIN_DELAY; - sqlpool_request_send_next(db, conndb); - } - - if (prev_state == SQL_DB_STATE_CONNECTING && - conndb->state == SQL_DB_STATE_DISCONNECTED && - !conndb->no_reconnect) { - /* connect failed */ - if (conndb->connect_failure_count > 0) { - /* increase delay between reconnections to this - server */ - conndb->connect_delay *= 5; - if (conndb->connect_delay > SQL_CONNECT_MAX_DELAY) - conndb->connect_delay = SQL_CONNECT_MAX_DELAY; - } - conndb->connect_failure_count++; - - /* reconnect after the delay */ - if (conndb->to_reconnect != NULL) - timeout_remove(&conndb->to_reconnect); - conndb->to_reconnect = timeout_add(conndb->connect_delay * 1000, - sqlpool_reconnect, conndb); - } -} - static struct sqlpool_host * sqlpool_find_host_with_least_connections(struct sqlpool_db *db, unsigned int *host_idx_r) @@ -227,16 +197,72 @@ return min; } -static struct sqlpool_connection *sqlpool_add_connection(struct sqlpool_db *db) +static bool sqlpool_have_successful_connections(struct sqlpool_db *db) +{ + const struct sqlpool_connection *conn; + + array_foreach(&db->all_connections, conn) { + if (conn->db->state >= SQL_DB_STATE_IDLE) + return TRUE; + } + return FALSE; +} + +static void +sqlpool_handle_connect_failed(struct sqlpool_db *db, struct sql_db *conndb) +{ + struct sqlpool_host *host; + unsigned int host_idx; + + if (conndb->connect_failure_count > 0) { + /* increase delay between reconnections to this + server */ + conndb->connect_delay *= 5; + if (conndb->connect_delay > SQL_CONNECT_MAX_DELAY) + conndb->connect_delay = SQL_CONNECT_MAX_DELAY; + } + conndb->connect_failure_count++; + + /* reconnect after the delay */ + if (conndb->to_reconnect != NULL) + timeout_remove(&conndb->to_reconnect); + conndb->to_reconnect = timeout_add(conndb->connect_delay * 1000, + sqlpool_reconnect, conndb); + + /* if we have zero successful hosts and there still are hosts + without connections, connect to one of them. */ + if (!sqlpool_have_successful_connections(db)) { + host = sqlpool_find_host_with_least_connections(db, &host_idx); + if (host->connection_count == 0) + (void)sqlpool_add_connection(db, host, host_idx); + } +} + +static void +sqlpool_state_changed(struct sql_db *conndb, enum sql_db_state prev_state, + void *context) +{ + struct sqlpool_db *db = context; + + if (conndb->state == SQL_DB_STATE_IDLE) { + conndb->connect_failure_count = 0; + conndb->connect_delay = SQL_CONNECT_MIN_DELAY; + sqlpool_request_send_next(db, conndb); + } + + if (prev_state == SQL_DB_STATE_CONNECTING && + conndb->state == SQL_DB_STATE_DISCONNECTED && + !conndb->no_reconnect) + sqlpool_handle_connect_failed(db, conndb); +} + +static struct sqlpool_connection * +sqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host, + unsigned int host_idx) { struct sql_db *conndb; - struct sqlpool_host *host; struct sqlpool_connection *conn; - unsigned int host_idx; - host = sqlpool_find_host_with_least_connections(db, &host_idx); - if (host->connection_count >= db->connection_limit) - return NULL; host->connection_count++; conndb = db->driver->v.init(host->connect_string); @@ -252,6 +278,19 @@ return conn; } +static struct sqlpool_connection * +sqlpool_add_new_connection(struct sqlpool_db *db) +{ + struct sqlpool_host *host; + unsigned int host_idx; + + host = sqlpool_find_host_with_least_connections(db, &host_idx); + if (host->connection_count >= db->connection_limit) + return NULL; + else + return sqlpool_add_connection(db, host, host_idx); +} + static const struct sqlpool_connection * sqlpool_find_available_connection(struct sqlpool_db *db, unsigned int unwanted_host_idx, @@ -316,7 +355,7 @@ } if (conn == NULL) { /* still nothing. try creating new connections */ - conn = sqlpool_add_connection(db); + conn = sqlpool_add_new_connection(db); if (conn == NULL || !SQL_DB_IS_READY(conn->db)) return FALSE; } @@ -425,7 +464,7 @@ i_array_init(&db->all_connections, 16); /* always have at least one backend connection initialized */ - (void)sqlpool_add_connection(db); + (void)sqlpool_add_new_connection(db); return &db->api; }