]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-dict: Support early rollback if dict_transaction_context.error is set
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 15 Jun 2022 08:08:41 +0000 (11:08 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Wed, 15 Jun 2022 12:28:59 +0000 (12:28 +0000)
src/lib-dict/dict-private.h
src/lib-dict/dict.c

index 83aeb854a6b5a8034e2ad10b767a1bcf3f40b421..6aeb767eb5d3a81394ba988e8747bfdc9f7de800 100644 (file)
@@ -70,6 +70,7 @@ struct dict {
        struct event *event;
        struct ioloop *ioloop, *prev_ioloop;
        struct dict_commit_callback_ctx *commits;
+       struct dict_transaction_context *rollbacks;
 };
 
 struct dict_iterate_context {
@@ -94,6 +95,9 @@ struct dict_transaction_context {
        struct event *event;
        struct timespec timestamp;
 
+       struct timeout *to_rollback;
+       const char *error;
+
        bool changed:1;
 };
 
index 352ff27cb403b90e551e2698815e93abcaa162ff..9eedbe73df1617daff82abc047ccec4aa2906539 100644 (file)
@@ -33,6 +33,7 @@ static ARRAY(struct dict *) dict_drivers;
 
 static void
 dict_commit_async_timeout(struct dict_commit_callback_ctx *ctx);
+static void dict_rollback_async_timeout(struct dict_transaction_context *ctx);
 
 static struct event_category event_category_dict = {
        .name = "dict",
@@ -165,6 +166,7 @@ void dict_deinit(struct dict **_dict)
 void dict_wait(struct dict *dict)
 {
        struct dict_commit_callback_ctx *commit, *next;
+       struct dict_transaction_context *rollback, *next_rollback;
 
        e_debug(dict->event, "Waiting for dict to finish pending operations");
        if (dict->v.wait != NULL)
@@ -173,6 +175,10 @@ void dict_wait(struct dict *dict)
                next = commit->next;
                dict_commit_async_timeout(commit);
        }
+       for (rollback = dict->rollbacks; rollback != NULL; rollback = next_rollback) {
+               next_rollback = rollback->next;
+               dict_rollback_async_timeout(rollback);
+       }
 }
 
 bool dict_have_async_operations(struct dict *dict)
@@ -285,6 +291,13 @@ static void dict_transaction_rollback_run(struct dict_transaction_context *ctx)
        event_unref(&event);
 }
 
+static void dict_rollback_async_timeout(struct dict_transaction_context *ctx)
+{
+       DLLIST_REMOVE(&ctx->dict->rollbacks, ctx);
+       timeout_remove(&ctx->to_rollback);
+       dict_transaction_rollback_run(ctx);
+}
+
 static void
 dict_commit_async_timeout(struct dict_commit_callback_ctx *ctx)
 {
@@ -575,13 +588,20 @@ dict_transaction_commit_sync_callback(const struct dict_commit_result *result,
 int dict_transaction_commit(struct dict_transaction_context **_ctx,
                            const char **error_r)
 {
-       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
-       struct dict_commit_callback_ctx *cctx =
-               p_new(pool, struct dict_commit_callback_ctx, 1);
        struct dict_transaction_context *ctx = *_ctx;
        struct dict_commit_sync_result result;
 
+       if (ctx->error != NULL) {
+               *error_r = t_strdup(ctx->error);
+               dict_transaction_rollback(_ctx);
+               return -1;
+       }
+
        *_ctx = NULL;
+
+       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
+       struct dict_commit_callback_ctx *cctx =
+               p_new(pool, struct dict_commit_callback_ctx, 1);
        cctx->pool = pool;
        i_zero(&result);
        i_assert(ctx->dict->transaction_count > 0);
@@ -606,15 +626,21 @@ void dict_transaction_commit_async(struct dict_transaction_context **_ctx,
                                   dict_transaction_commit_callback_t *callback,
                                   void *context)
 {
-       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
-       struct dict_commit_callback_ctx *cctx =
-               p_new(pool, struct dict_commit_callback_ctx, 1);
        struct dict_transaction_context *ctx = *_ctx;
 
        *_ctx = NULL;
        i_assert(ctx->dict->transaction_count > 0);
        ctx->dict->transaction_count--;
        DLLIST_REMOVE(&ctx->dict->transactions, ctx);
+
+       if (ctx->error != NULL) {
+               ctx->to_rollback = timeout_add_short(0,
+                       dict_rollback_async_timeout, ctx);
+               return;
+       }
+       pool_t pool = pool_alloconly_create("dict_commit_callback_ctx", 64);
+       struct dict_commit_callback_ctx *cctx =
+               p_new(pool, struct dict_commit_callback_ctx, 1);
        DLLIST_PREPEND(&ctx->dict->commits, cctx);
        if (callback == NULL)
                callback = dict_transaction_commit_async_noop_callback;