driver-sqlpool.c revision 2a70702978902669803cf8c7eed287cb006d2c48
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* all connections from all hosts */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ARRAY_DEFINE(all_connections, struct sqlpool_connection);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* index of last connection in all_connections that was used to
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen send a query. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* queued requests */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_request *requests_head, *requests_tail;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* requests are a) queries */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* b) transaction waiters */
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainensqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host,
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen unsigned int host_idx);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_query_callback(struct sql_result *result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_commit_callback(const char *error,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_request_new(struct sqlpool_db *db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_request_free(struct sqlpool_request **_request)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(request->prev == NULL && request->next == NULL);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_request_abort(struct sqlpool_request **_request)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen request->callback(&sql_not_connected_result, request->context);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_new_conn_trans(struct sqlpool_transaction_context *trans,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* backend will use our queries list (we might still append more
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen queries to the list) */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_request_handle_transaction(struct sql_db *conndb,
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen conn_trans = driver_sqlpool_new_conn_trans(trans, conndb);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_request_send_next(struct sqlpool_db *db, struct sql_db *conndb)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->requests_head == NULL || !SQL_DB_IS_READY(conndb))
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen DLLIST2_REMOVE(&db->requests_head, &db->requests_tail, request);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sqlpool_request_handle_transaction(conndb, request->trans);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void sqlpool_reconnect(struct sql_db *conndb)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_find_host_with_least_connections(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int *host_idx_r)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i, count;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen hosts = array_get_modifiable(&db->hosts, &count);
902acc26200957a1f04da3cc947211f0b9ffce05Timo Sirainen if (min->connection_count > hosts[i].connection_count) {
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainenstatic bool sqlpool_have_successful_connections(struct sqlpool_db *db)
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainensqlpool_handle_connect_failed(struct sqlpool_db *db, struct sql_db *conndb)
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen /* increase delay between reconnections to this
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen if (conndb->connect_delay > SQL_CONNECT_MAX_DELAY)
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen conndb->connect_delay = SQL_CONNECT_MAX_DELAY;
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen /* reconnect after the delay */
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen conndb->to_reconnect = timeout_add(conndb->connect_delay * 1000,
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen /* if we have zero successful hosts and there still are hosts
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen without connections, connect to one of them. */
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen if (!sqlpool_have_successful_connections(db)) {
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen host = sqlpool_find_host_with_least_connections(db, &host_idx);
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen (void)sqlpool_add_connection(db, host, host_idx);
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainensqlpool_state_changed(struct sql_db *conndb, enum sql_db_state prev_state,
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen conndb->connect_delay = SQL_CONNECT_MIN_DELAY;
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainensqlpool_add_connection(struct sqlpool_db *db, struct sqlpool_host *host,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conndb = db->driver->v.init(host->connect_string);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conndb->state_change_callback = sqlpool_state_changed;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conndb->connect_delay = SQL_CONNECT_MIN_DELAY;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conn = array_append_space(&db->all_connections);
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainensqlpool_add_new_connection(struct sqlpool_db *db)
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen host = sqlpool_find_host_with_least_connections(db, &host_idx);
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen if (host->connection_count >= db->connection_limit)
73bc59c2a56ff351ae7c4d9f52de76b1b0173995Timo Sirainen return sqlpool_add_connection(db, host, host_idx);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic const struct sqlpool_connection *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainensqlpool_find_available_connection(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i, count;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conns = array_get(&db->all_connections, &count);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < count; i++) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int idx = (i + db->last_query_conn_idx) % count;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* see if we could reconnect to it immediately */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (conndb->state != SQL_DB_STATE_DISCONNECTED)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_get_connection(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen const struct sqlpool_connection *conn, *conns;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i, count;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conn = sqlpool_find_available_connection(db, unwanted_host_idx,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (conn == NULL && unwanted_host_idx != -1U) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* maybe there are no wanted hosts. use any of them. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conn = sqlpool_find_available_connection(db, -1U,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* no connected connections. connect_delays may have gotten too
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen high, reset all of them to see if some are still alive. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conns = array_get(&db->all_connections, &count);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < count; i++) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (conndb->connect_delay > SQL_CONNECT_RESET_DELAY)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conndb->connect_delay = SQL_CONNECT_RESET_DELAY;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conn = sqlpool_find_available_connection(db, -1U,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* still nothing. try creating new connections */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (conn == NULL || !SQL_DB_IS_READY(conn->db))
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_get_sync_connection(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i, count;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (driver_sqlpool_get_connection(db, -1U, conn_r))
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* no idling connections, but maybe we can find one that's trying to
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen connect to server, and we can use it once it's finished */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen conns = array_get(&db->all_connections, &count);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < count; i++) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (conns[i].db->state == SQL_DB_STATE_CONNECTING) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_parse_hosts(struct sqlpool_db *db, const char *connect_string)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen const char *const *args, *key, *value, *const *hostnamep;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ARRAY_TYPE(const_string) hostnames, connect_args;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* connect string is a space separated list. it may contain
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen backend-specific strings which we'll pass as-is. we'll only care
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen about our own settings, plus the host settings. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen args = t_strsplit_spaces(connect_string, " ");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (str_to_uint(value, &db->connection_limit) < 0) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* build a new connect string without our settings or hosts */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen connect_string = t_strarray_join(array_idx(&connect_args, 0), " ");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* no hosts specified. create a default one. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen host->connect_string = i_strdup(connect_string);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->connection_limit = SQL_DEFAULT_CONNECTION_LIMIT;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_init(const char *connect_string, const struct sql_db *driver)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_sqlpool_parse_hosts(db, connect_string);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* always have at least one backend connection initialized */
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainenstatic void driver_sqlpool_abort_requests(struct sqlpool_db *db)
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainen struct sqlpool_request *request = db->requests_head;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_sqlpool_deinit(struct sql_db *_db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen array_foreach_modifiable(&db->all_connections, conn)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(array_count(&db->all_connections) == 0);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic int driver_sqlpool_connect(struct sql_db *_db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_sqlpool_disconnect(struct sql_db *_db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic const char *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_escape_string(struct sql_db *_db, const char *string)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* we always have at least one connection */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_sqlpool_timeout(struct sqlpool_db *db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_request *request = db->requests_head;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (request->created + SQL_QUERY_TIMEOUT_SECS > ioloop_time)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen "(no free connections for %u secs): %s",
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen (unsigned int)(ioloop_time - request->created),
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen "<transaction>");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_prepend_request(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen DLLIST2_PREPEND(&db->requests_head, &db->requests_tail, request);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->request_to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_append_request(struct sqlpool_db *db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen DLLIST2_APPEND(&db->requests_head, &db->requests_tail, request);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->request_to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_query_callback(struct sql_result *result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (result->failed_try_retry && !request->retried) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->driver->name, sql_result_get_error(result));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (driver_sqlpool_get_connection(request->db,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_sqlpool_query(struct sql_db *_db, const char *query,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query_callback_t *callback, void *context)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (!driver_sqlpool_get_connection(db, -1U, &conn))
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query(conn->db, query, driver_sqlpool_query_callback,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_sqlpool_exec(struct sql_db *_db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic struct sql_result *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_query_s(struct sql_db *_db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (!driver_sqlpool_get_sync_connection(db, &conn)) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (!driver_sqlpool_get_sync_connection(db, &conn))
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_transaction_begin(struct sql_db *_db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ctx = i_new(struct sqlpool_transaction_context, 1);
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen /* queue changes until commit. even if we did have a free connection
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen now, don't use it or multiple open transactions could tie up all
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen connections. */
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen ctx->query_pool = pool_alloconly_create("sqlpool transaction", 1024);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_transaction_free(struct sqlpool_transaction_context *ctx)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_commit_callback(const char *error,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_transaction_commit(struct sql_transaction_context *_ctx,
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_ctx->db;
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen ctx->commit_request = sqlpool_request_new(db, NULL);
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen if (driver_sqlpool_get_connection(db, -1U, &conn))
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen sqlpool_request_handle_transaction(conn->db, ctx);
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen driver_sqlpool_append_request(db, ctx->commit_request);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_transaction_commit_s(struct sql_transaction_context *_ctx,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen const char **error_r)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sqlpool_db *db = (struct sqlpool_db *)_ctx->db;
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen if (!driver_sqlpool_get_sync_connection(db, &conn)) {
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen conn_trans = driver_sqlpool_new_conn_trans(ctx, conn->db);
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen ret = sql_transaction_commit_s(&conn_trans, error_r);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_transaction_rollback(struct sql_transaction_context *_ctx)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_sqlpool_update(struct sql_transaction_context *_ctx, const char *query,
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen /* we didn't get a connection for transaction immediately.
75113e5fa7532ef628f273caac2feec6008992c6Timo Sirainen queue updates until commit transfers all of these */