]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-sql: If mysql/pgsql commit fails due to server disconnection, reconnect and retry.
authorTimo Sirainen <tss@iki.fi>
Thu, 8 Dec 2011 05:02:03 +0000 (07:02 +0200)
committerTimo Sirainen <tss@iki.fi>
Thu, 8 Dec 2011 05:02:03 +0000 (07:02 +0200)
src/lib-sql/driver-mysql.c
src/lib-sql/driver-pgsql.c

index d3ecfb5c62e99ca1e8e476b9e8e60031ede2ea24..f5163e4b7966cd5778bf53bf89abff244e5f0485 100644 (file)
@@ -506,31 +506,54 @@ transaction_send_query(struct mysql_transaction_context *ctx, const char *query,
        return ret;
 }
 
+static int driver_mysql_try_commit_s(struct mysql_transaction_context *ctx)
+{
+       struct sql_transaction_context *_ctx = &ctx->ctx;
+
+       /* try to use a transaction in any case,
+          even if it's not actually functional. */
+       if (transaction_send_query(ctx, "BEGIN", NULL) < 0) {
+               if (_ctx->db->state != SQL_DB_STATE_DISCONNECTED)
+                       return -1;
+               /* we got disconnected, retry */
+               return 0;
+       }
+       while (_ctx->head != NULL) {
+               if (transaction_send_query(ctx, _ctx->head->query,
+                                          _ctx->head->affected_rows) < 0)
+                       return -1;
+               _ctx->head = _ctx->head->next;
+       }
+       if (transaction_send_query(ctx, "COMMIT", NULL) < 0)
+               return -1;
+       return 1;
+}
+
 static int
 driver_mysql_transaction_commit_s(struct sql_transaction_context *_ctx,
                                  const char **error_r)
 {
        struct mysql_transaction_context *ctx =
                (struct mysql_transaction_context *)_ctx;
-       int ret = 0;
+       struct mysql_db *db = (struct mysql_db *)_ctx->db;
+       int ret = 1;
 
        *error_r = NULL;
 
        if (_ctx->head != NULL) {
-               /* try to use a transaction in any case,
-                  even if it doesn't work. */
-               (void)transaction_send_query(ctx, "BEGIN", NULL);
-               while (_ctx->head != NULL) {
-                       if (transaction_send_query(ctx, _ctx->head->query,
-                                                  _ctx->head->affected_rows) < 0)
-                               break;
-                       _ctx->head = _ctx->head->next;
+               ret = driver_mysql_try_commit_s(ctx);
+               *error_r = t_strdup(ctx->error);
+               if (ret == 0) {
+                       i_info("%s: Disconnected from database, "
+                              "retrying commit", db->dbname);
+                       if (sql_connect(_ctx->db) >= 0) {
+                               ctx->failed = FALSE;
+                               ret = driver_mysql_try_commit_s(ctx);
+                       }
                }
-               ret = transaction_send_query(ctx, "COMMIT", NULL);
-               *error_r = ctx->error;
        }
        sql_transaction_rollback(&_ctx);
-       return ret;
+       return ret <= 0 ? -1 : 0;
 }
 
 static void
index fd76193d742ff575a1778125e5060e716591829d..b8a72fa5eb428dafc71780d8d12de4db609a3b07 100644 (file)
@@ -956,22 +956,16 @@ driver_pgsql_transaction_commit_multi(struct pgsql_transaction_context *ctx)
                                       "ROLLBACK" : "COMMIT");
 }
 
-static int
-driver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
-                                 const char **error_r)
+static void
+driver_pgsql_try_commit_s(struct pgsql_transaction_context *ctx,
+                         const char **error_r)
 {
-       struct pgsql_transaction_context *ctx =
-               (struct pgsql_transaction_context *)_ctx;
+       struct sql_transaction_context *_ctx = &ctx->ctx;
        struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
        struct sql_transaction_query *single_query = NULL;
        struct sql_result *result;
 
-       *error_r = NULL;
-
-       if (ctx->failed || _ctx->head == NULL) {
-               /* nothing to be done */
-               result = NULL;
-       } else if (_ctx->head->next == NULL) {
+       if (_ctx->head->next == NULL) {
                /* just a single query, send it */
                single_query = _ctx->head;
                result = sql_query_s(_ctx->db, single_query->query);
@@ -999,6 +993,31 @@ driver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
        }
        if (result != NULL)
                sql_result_unref(result);
+}
+
+static int
+driver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
+                                 const char **error_r)
+{
+       struct pgsql_transaction_context *ctx =
+               (struct pgsql_transaction_context *)_ctx;
+       struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
+
+       *error_r = NULL;
+
+       if (_ctx->head != NULL) {
+               driver_pgsql_try_commit_s(ctx, error_r);
+               if (_ctx->db->state == SQL_DB_STATE_DISCONNECTED) {
+                       *error_r = t_strdup(*error_r);
+                       i_info("%s: Disconnected from database, "
+                              "retrying commit", pgsql_prefix(db));
+                       if (sql_connect(_ctx->db) >= 0) {
+                               ctx->failed = FALSE;
+                               *error_r = NULL;
+                               driver_pgsql_try_commit_s(ctx, error_r);
+                       }
+               }
+       }
 
        i_assert(ctx->refcount == 1);
        driver_pgsql_transaction_unref(ctx);