Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package dqlite for openSUSE:Factory checked 
in at 2022-08-11 18:32:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/dqlite (Old)
 and      /work/SRC/openSUSE:Factory/.dqlite.new.1521 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "dqlite"

Thu Aug 11 18:32:07 2022 rev:3 rq:994365 version:1.11.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/dqlite/dqlite.changes    2022-05-02 
16:25:53.612859822 +0200
+++ /work/SRC/openSUSE:Factory/.dqlite.new.1521/dqlite.changes  2022-08-11 
18:32:19.470262571 +0200
@@ -1,0 +2,10 @@
+Sat Jul 23 11:29:54 UTC 2022 - Andreas Stieger <andreas.stie...@gmx.de>
+
+- update to 1.11.1:
+  * Replace deprecated raft function raft_fixture_init with
+    raft_fixture_initialize
+- includes changes from 1.11.0:
+  * Fix a bug that causes a node to crash under certain conditions
+    after losing leadership in the middle of a transaction
+
+-------------------------------------------------------------------

Old:
----
  dqlite-1.10.0.tar.gz

New:
----
  dqlite-1.11.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ dqlite.spec ++++++
--- /var/tmp/diff_new_pack.Vk7fDq/_old  2022-08-11 18:32:19.914263610 +0200
+++ /var/tmp/diff_new_pack.Vk7fDq/_new  2022-08-11 18:32:19.918263620 +0200
@@ -18,7 +18,7 @@
 
 %define lname libdqlite0
 Name:           dqlite
-Version:        1.10.0
+Version:        1.11.1
 Release:        0
 Summary:        Distributed SQLite
 License:        LGPL-3.0-only WITH LGPL-3.0-linking-exception

++++++ dqlite-1.10.0.tar.gz -> dqlite-1.11.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/configure.ac 
new/dqlite-1.11.1/configure.ac
--- old/dqlite-1.10.0/configure.ac      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/configure.ac      2022-07-13 13:35:54.000000000 +0200
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT([libdqlite], [1.10.0], [https://github.com/canonical/dqlite])
+AC_INIT([libdqlite], [1.11.1], [https://github.com/canonical/dqlite])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_AUX_DIR([ac])
 
@@ -49,7 +49,7 @@
 # Checks for libraries
 PKG_CHECK_MODULES(SQLITE, [sqlite3 >= 3.22.0], [], [])
 PKG_CHECK_MODULES(UV, [libuv >= 1.8.0], [], [])
-PKG_CHECK_MODULES(RAFT, [raft >= 0.13.0], [], [])
+PKG_CHECK_MODULES(RAFT, [raft >= 0.14.0], [], [])
 
 CC_CHECK_FLAGS_APPEND([AM_CFLAGS],[CFLAGS],[ \
   -std=c11 \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/client.c 
new/dqlite-1.11.1/src/client.c
--- old/dqlite-1.10.0/src/client.c      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/client.c      2022-07-13 13:35:54.000000000 +0200
@@ -365,6 +365,15 @@
        return 0;
 }
 
+int clientSendTransfer(struct client *c, unsigned id)
+{
+       tracef("client send transfer fd %d id %u", c->fd, id);
+       struct request_transfer request;
+       request.id = id;
+       REQUEST(transfer, TRANSFER);
+       return 0;
+}
+
 int clientRecvEmpty(struct client *c)
 {
        tracef("client recv empty fd %d", c->fd);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/client.h 
new/dqlite-1.11.1/src/client.h
--- old/dqlite-1.10.0/src/client.h      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/client.h      2022-07-13 13:35:54.000000000 +0200
@@ -79,6 +79,9 @@
 /* Send a request to remove a server from the cluster. */
 int clientSendRemove(struct client *c, unsigned id);
 
+/* Send a request to transfer leadership to node with id `id`. */
+int clientSendTransfer(struct client *c, unsigned id);
+
 /* Receive an empty response. */
 int clientRecvEmpty(struct client *c);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/gateway.c 
new/dqlite-1.11.1/src/gateway.c
--- old/dqlite-1.10.0/src/gateway.c     2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/gateway.c     2022-07-13 13:35:54.000000000 +0200
@@ -31,11 +31,10 @@
        g->protocol = DQLITE_PROTOCOL_VERSION;
 }
 
-void gateway__close(struct gateway *g)
+void gateway__leader_close(struct gateway *g, int reason)
 {
-       tracef("gateway close");
-       if (g->leader == NULL) {
-               stmt__registry_close(&g->stmts);
+       if (g == NULL || g->leader == NULL) {
+               tracef("gateway:%p or gateway->leader are NULL", g);
                return;
        }
 
@@ -43,7 +42,7 @@
                if (g->leader->inflight != NULL) {
                        tracef("finish inflight apply request");
                        struct raft_apply *req = &g->leader->inflight->req;
-                       req->cb(req, RAFT_SHUTDOWN, NULL);
+                       req->cb(req, reason, NULL);
                        assert(g->req == NULL);
                        assert(g->stmt == NULL);
                } else if (g->barrier.cb != NULL) {
@@ -52,12 +51,12 @@
                         * around g->barrier.cb and when called, will set 
g->barrier.cb to NULL.
                         * */
                        struct raft_barrier *b = &g->barrier.req;
-                       b->cb(b, RAFT_SHUTDOWN);
+                       b->cb(b, reason);
                        assert(g->barrier.cb == NULL);
                } else if (g->leader->exec != NULL && 
g->leader->exec->barrier.cb != NULL) {
                        tracef("finish inflight exec barrier");
                        struct raft_barrier *b = &g->leader->exec->barrier.req;
-                       b->cb(b, RAFT_SHUTDOWN);
+                       b->cb(b, reason);
                        assert(g->leader->exec == NULL);
                } else if (g->req != NULL && g->req->type != 
DQLITE_REQUEST_QUERY
                                          && g->req->type != 
DQLITE_REQUEST_EXEC) {
@@ -71,6 +70,18 @@
        stmt__registry_close(&g->stmts);
        leader__close(g->leader);
        sqlite3_free(g->leader);
+       g->leader = NULL;
+}
+
+void gateway__close(struct gateway *g)
+{
+       tracef("gateway close");
+       if (g->leader == NULL) {
+               stmt__registry_close(&g->stmts);
+               return;
+       }
+
+       gateway__leader_close(g, RAFT_SHUTDOWN);
 }
 
 /* Declare a request struct and a response struct of the appropriate types and
@@ -293,6 +304,7 @@
  * affected. */
 static void fill_result(struct gateway *g, struct response_result *response)
 {
+       assert(g->leader != NULL);
        response->last_insert_id =
            (uint64_t)sqlite3_last_insert_rowid(g->leader->conn);
        response->rows_affected = (uint64_t)sqlite3_changes(g->leader->conn);
@@ -325,6 +337,7 @@
                fill_result(g, &response);
                SUCCESS(result, RESULT);
        } else {
+               assert(g->leader != NULL);
                failure(req, status, error_message(g->leader->conn, status));
                sqlite3_reset(stmt);
        }
@@ -371,6 +384,7 @@
        rc = query__batch(stmt, req->buffer);
        if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
                sqlite3_reset(stmt);
+               assert(g->leader != NULL);
                failure(req, rc, sqlite3_errmsg(g->leader->conn));
                goto done;
        }
@@ -480,6 +494,7 @@
        if (status == SQLITE_DONE) {
                handle_exec_sql_next(req, NULL);
        } else {
+               assert(g->leader != NULL);
                failure(req, status, error_message(g->leader->conn, status));
                sqlite3_reset(g->stmt);
                sqlite3_finalize(g->stmt);
@@ -507,6 +522,7 @@
        }
 
        /* g->stmt will be set to NULL by sqlite when an error occurs. */
+       assert(g->leader != NULL);
        rv = sqlite3_prepare_v2(g->leader->conn, g->sql, -1, &g->stmt, &tail);
        if (rv != SQLITE_OK) {
                tracef("exec sql prepare failed %d", rv);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/gateway.h 
new/dqlite-1.11.1/src/gateway.h
--- old/dqlite-1.10.0/src/gateway.h     2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/gateway.h     2022-07-13 13:35:54.000000000 +0200
@@ -47,6 +47,12 @@
 void gateway__close(struct gateway *g);
 
 /**
+ * Closes the leader connection to the database, reason should contain a raft
+ * error code.
+ */
+void gateway__leader_close(struct gateway *g, int reason);
+
+/**
  * Asynchronous request to handle a client command.
  */
 typedef void (*handle_cb)(struct handle *req, int status, int type);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/leader.c 
new/dqlite-1.11.1/src/leader.c
--- old/dqlite-1.10.0/src/leader.c      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/leader.c      2022-07-13 13:35:54.000000000 +0200
@@ -9,11 +9,10 @@
 #include "tracing.h"
 #include "vfs.h"
 
-static void maybeExecDone(struct exec *req)
+/* Called when a leader exec request terminates and the associated callback can
+ * be invoked. */
+static void leaderExecDone(struct exec *req)
 {
-       if (!req->done) {
-               return;
-       }
        req->leader->exec = NULL;
        if (req->cb != NULL) {
                req->cb(req, req->status);
@@ -134,9 +133,8 @@
        /* TODO: there shouldn't be any ongoing exec request. */
        if (l->exec != NULL) {
                assert(l->inflight == NULL);
-               l->exec->done = true;
                l->exec->status = SQLITE_ERROR;
-               maybeExecDone(l->exec);
+               leaderExecDone(l->exec);
        }
        rc = sqlite3_close(l->conn);
        assert(rc == 0);
@@ -273,8 +271,7 @@
 finish:
        l->inflight = NULL;
        l->db->tx_id = 0;
-       l->exec->done = true;
-       maybeExecDone(l->exec);
+       leaderExecDone(l->exec);
 }
 
 static int leaderApplyFrames(struct exec *req,
@@ -364,12 +361,11 @@
        return;
 
 finish:
-       l->exec->done = true;
        if (rv != 0) {
                 tracef("exec v2 failed %d", rv);
                l->exec->status = rv;
        }
-       maybeExecDone(l->exec);
+       leaderExecDone(l->exec);
 }
 
 static void execBarrierCb(struct barrier *barrier, int status)
@@ -378,9 +374,8 @@
        struct exec *req = barrier->data;
        struct leader *l = req->leader;
        if (status != 0) {
-               l->exec->done = true;
                l->exec->status = status;
-               maybeExecDone(l->exec);
+               leaderExecDone(l->exec);
                return;
        }
        leaderExecV2(req);
@@ -402,7 +397,6 @@
        req->leader = l;
        req->stmt = stmt;
        req->cb = cb;
-       req->done = false;
        req->barrier.data = req;
        req->barrier.cb = NULL;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/leader.h 
new/dqlite-1.11.1/src/leader.h
--- old/dqlite-1.10.0/src/leader.h      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/leader.h      2022-07-13 13:35:54.000000000 +0200
@@ -64,7 +64,6 @@
        struct leader *leader;
        struct barrier barrier;
        sqlite3_stmt *stmt;
-       bool done;
        int status;
        queue queue;
        exec_cb cb;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/lib/registry.h 
new/dqlite-1.11.1/src/lib/registry.h
--- old/dqlite-1.10.0/src/lib/registry.h        2022-04-13 16:59:33.000000000 
+0200
+++ new/dqlite-1.11.1/src/lib/registry.h        2022-07-13 13:35:54.000000000 
+0200
@@ -55,7 +55,7 @@
 /**
  * Define the methods of a registry
  */
-#define REGISTRY_METHODS(NAME, TYPE)                                        \
+#define REGISTRY_METHODS(NAME, TYPE)                                           
     \
        void NAME##_init(struct NAME *r) {                                      
    \
                assert(r != NULL);                                              
    \
                                                                                
     \
@@ -82,8 +82,11 @@
                        }                                                       
    \
                }                                                               
    \
                                                                                
     \
+               r->len = 0;                                                     
    \
+               r->cap = 0;                                                     
    \
                if (r->buf != NULL) {                                           
    \
                        sqlite3_free(r->buf);                                   
    \
+                       r->buf = NULL;                                          
    \
                }                                                               
    \
        }                                                                       
    \
                                                                                
     \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/lib/util.h 
new/dqlite-1.11.1/src/lib/util.h
--- old/dqlite-1.10.0/src/lib/util.h    1970-01-01 01:00:00.000000000 +0100
+++ new/dqlite-1.11.1/src/lib/util.h    2022-07-13 13:35:54.000000000 +0200
@@ -0,0 +1,7 @@
+#ifndef LIB_UTIL_H_
+#define LIB_UTIL_H_
+
+#define LIKELY(x)       __builtin_expect(!!(x),1)
+#define UNLIKELY(x)     __builtin_expect(!!(x),0)
+
+#endif /* LIB_UTIL_H_ */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/server.c 
new/dqlite-1.11.1/src/server.c
--- old/dqlite-1.10.0/src/server.c      2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/server.c      2022-07-13 13:35:54.000000000 +0200
@@ -572,27 +572,21 @@
 {
        struct dqlite_node *d = monitor->data;
        int state = raft_state(&d->raft);
-       /*
        queue *head;
        struct conn *conn;
-       */
 
        if (state == RAFT_UNAVAILABLE) {
                return;
        }
 
-       /* TODO: we should shutdown clients that are performing SQL requests,
-        * but not the ones which are doing management-requests, such as
-        * transfer leadership.  */
-       /*
        if (d->raft_state == RAFT_LEADER && state != RAFT_LEADER) {
+               tracef("node %llu@%s: leadership lost", d->raft.id, 
d->raft.address);
                QUEUE__FOREACH(head, &d->conns)
                {
                        conn = QUEUE__DATA(head, struct conn, queue);
-                       conn__stop(conn);
+                       gateway__leader_close(&conn->gateway, 
RAFT_LEADERSHIPLOST);
                }
        }
-       */
 
        d->raft_state = state;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/src/tracing.h 
new/dqlite-1.11.1/src/tracing.h
--- old/dqlite-1.10.0/src/tracing.h     2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/src/tracing.h     2022-07-13 13:35:54.000000000 +0200
@@ -8,12 +8,14 @@
 #include <stdint.h>
 #include <time.h>
 
+#include "lib/util.h"
+
 /* This global variable is only written once at startup and is only read
  * from there on. Users should not manipulate the value of this variable. */
 extern bool _dqliteTracingEnabled;
 
 #define tracef(...) do {                                                       
            \
-    if (_dqliteTracingEnabled) {                                               
            \
+    if (UNLIKELY(_dqliteTracingEnabled)) {                                     
            \
         static char _msg[1024];                                                
            \
         snprintf(_msg, sizeof(_msg), __VA_ARGS__);                             
            \
         struct timespec ts = {0};                                              
            \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/integration/test_fsm.c 
new/dqlite-1.11.1/test/integration/test_fsm.c
--- old/dqlite-1.10.0/test/integration/test_fsm.c       2022-04-13 
16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/test/integration/test_fsm.c       2022-07-13 
13:35:54.000000000 +0200
@@ -188,8 +188,7 @@
        EXEC(stmt_id, &last_insert_id, &rows_affected);
 
        /* Close and reopen the client and open a second database */
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
 
        HANDSHAKE;
        OPEN_NAME("test2");
@@ -253,8 +252,7 @@
 
        /* Close and reopen the client and open a second database,
         * and ensure finalize succeeds. */
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
 
        HANDSHAKE;
        OPEN_NAME("test2");
@@ -391,8 +389,7 @@
        munit_assert_int(rv, ==, 0);
 
        /* Table is there on fresh connection. */
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
        HANDSHAKE;
        OPEN;
        PREPARE("SELECT COUNT(*) from test", &stmt_id);
@@ -469,8 +466,7 @@
        PREPARE("INSERT INTO test(n) VALUES(1)", &stmt_id);
        EXEC(stmt_id, &last_insert_id, &rows_affected);
 
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
        HANDSHAKE;
        OPEN_NAME("test2");
        PREPARE("CREATE TABLE test2a (n INT)", &stmt_id);
@@ -498,8 +494,7 @@
        munit_assert_int(rv, ==, 0);
 
        /* Reopen connection */
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
        HANDSHAKE;
        OPEN_NAME("test2");
 
@@ -514,8 +509,7 @@
        munit_assert_string_equal(msg, "no such table: test2b");
 
        /* Table is there on first DB */
-       rv = test_server_client_reconnect(&f->servers[0]);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
        HANDSHAKE;
        OPEN_NAME("test");
        PREPARE("SELECT * from test", &stmt_id);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/integration/test_membership.c 
new/dqlite-1.11.1/test/integration/test_membership.c
--- old/dqlite-1.10.0/test/integration/test_membership.c        2022-04-13 
16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/test/integration/test_membership.c        2022-07-13 
13:35:54.000000000 +0200
@@ -17,7 +17,8 @@
 #define N_SERVERS 3
 #define FIXTURE                                \
        struct test_server servers[N_SERVERS]; \
-       struct client *client
+       struct client *client;                 \
+       struct rows rows;
 
 #define SETUP                                                 \
        unsigned i_;                                          \
@@ -51,6 +52,20 @@
 /* Use the client connected to the server with the given ID. */
 #define SELECT(ID) f->client = test_server_client(&f->servers[ID - 1])
 
+/* Wait a bounded time until server ID has applied at least the entry at INDEX 
*/
+#define AWAIT_REPLICATION(ID, INDEX)                                           
          \
+       do {                                                                    
          \
+               struct timespec _start = {0};                                   
          \
+               struct timespec _end = {0};                                     
          \
+               clock_gettime(CLOCK_MONOTONIC, &_start);                        
          \
+               clock_gettime(CLOCK_MONOTONIC, &_end);                          
          \
+               while((f->servers[ID].dqlite->raft.last_applied < INDEX)        
          \
+                     && ((_end.tv_sec - _start.tv_sec) < 2)  ) {               
          \
+                       clock_gettime(CLOCK_MONOTONIC, &_end);                  
          \
+               }                                                               
          \
+               munit_assert_ullong(f->servers[ID].dqlite->raft.last_applied, 
>=, INDEX); \
+       } while(0)
+
 /******************************************************************************
  *
  * join
@@ -107,3 +122,153 @@
        REMOVE(id);
        return MUNIT_OK;
 }
+
+TEST(membership, transfer, setUp, tearDown, 0, NULL)
+{
+       struct fixture *f = data;
+       unsigned id = 2;
+       const char *address = "@2";
+       unsigned stmt_id;
+       unsigned last_insert_id;
+       unsigned rows_affected;
+       raft_index last_applied;
+       struct client c_transfer; /* Client used for transfer requests */
+
+       HANDSHAKE;
+       ADD(id, address);
+       ASSIGN(id, 1 /* voter */);
+       OPEN;
+       PREPARE("CREATE TABLE test (n INT)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+       PREPARE("INSERT INTO test(n) VALUES(1)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       /* Transfer leadership and wait until first leader has applied a new
+        * entry replicated from the new leader.  */
+       test_server_client_connect(&f->servers[0], &c_transfer);
+       HANDSHAKE_C(&c_transfer);
+       TRANSFER(2, &c_transfer);
+       test_server_client_close(&f->servers[0], &c_transfer);
+       last_applied = f->servers[0].dqlite->raft.last_applied;
+
+       SELECT(2);
+       HANDSHAKE;
+       OPEN;
+       PREPARE("INSERT INTO test(n) VALUES(1)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       AWAIT_REPLICATION(0, last_applied + 1);
+
+       return MUNIT_OK;
+}
+
+/* Transfer leadership away from a member that has a pending transaction */
+TEST(membership, transferPendingTransaction, setUp, tearDown, 0, NULL)
+{
+       struct fixture *f = data;
+       unsigned id = 2;
+       const char *address = "@2";
+       unsigned stmt_id;
+       unsigned last_insert_id;
+       unsigned rows_affected;
+       raft_index last_applied;
+       struct client c_transfer; /* Client used for transfer requests */
+
+       HANDSHAKE;
+       ADD(id, address);
+       ASSIGN(id, 1 /* voter */);
+       OPEN;
+       PREPARE("CREATE TABLE test (n INT)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+       PREPARE("INSERT INTO test(n) VALUES(1)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       /* Pending transaction */
+       PREPARE("BEGIN", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+       PREPARE("SELECT * FROM test", &stmt_id);
+       QUERY(stmt_id, &f->rows);
+       clientCloseRows(&f->rows);
+
+       /* Transfer leadership and wait until first leader has applied a new
+        * entry replicated from the new leader.  */
+       test_server_client_connect(&f->servers[0], &c_transfer);
+       HANDSHAKE_C(&c_transfer);
+       TRANSFER(2, &c_transfer);
+       test_server_client_close(&f->servers[0], &c_transfer);
+       last_applied = f->servers[0].dqlite->raft.last_applied;
+
+       SELECT(2);
+       HANDSHAKE;
+       OPEN;
+       PREPARE("INSERT INTO test(n) VALUES(2)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       AWAIT_REPLICATION(0, last_applied + 1);
+
+       return MUNIT_OK;
+}
+
+/* Transfer leadership back and forth from a member that has a pending 
transaction */
+TEST(membership, transferTwicePendingTransaction, setUp, tearDown, 0, NULL)
+{
+       struct fixture *f = data;
+       unsigned id = 2;
+       const char *address = "@2";
+       unsigned stmt_id;
+       unsigned last_insert_id;
+       unsigned rows_affected;
+       raft_index last_applied;
+       struct client c_transfer; /* Client used for transfer requests */
+
+       HANDSHAKE;
+       ADD(id, address);
+       ASSIGN(id, 1 /* voter */);
+       OPEN;
+       PREPARE("CREATE TABLE test (n INT)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+       PREPARE("INSERT INTO test(n) VALUES(1)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       /* Pending transaction */
+       PREPARE("BEGIN", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+       PREPARE("SELECT * FROM test", &stmt_id);
+       QUERY(stmt_id, &f->rows);
+       clientCloseRows(&f->rows);
+
+       /* Transfer leadership and wait until first leader has applied a new
+        * entry replicated from the new leader.  */
+       test_server_client_connect(&f->servers[0], &c_transfer);
+       HANDSHAKE_C(&c_transfer);
+       TRANSFER(2, &c_transfer);
+       test_server_client_close(&f->servers[0], &c_transfer);
+       last_applied = f->servers[0].dqlite->raft.last_applied;
+
+       SELECT(2);
+       HANDSHAKE;
+       OPEN;
+       PREPARE("INSERT INTO test(n) VALUES(2)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       AWAIT_REPLICATION(0, last_applied + 1);
+
+       /* Transfer leadership back to original node, reconnect the client and
+        * ensure queries can be executed. */
+       test_server_client_connect(&f->servers[1], &c_transfer);
+       HANDSHAKE_C(&c_transfer);
+       TRANSFER(1, &c_transfer);
+       test_server_client_close(&f->servers[1], &c_transfer);
+
+       last_applied = f->servers[1].dqlite->raft.last_applied;
+       test_server_client_reconnect(&f->servers[0], &f->servers[0].client);
+       SELECT(1);
+       HANDSHAKE;
+       OPEN;
+       PREPARE("INSERT INTO test(n) VALUES(3)", &stmt_id);
+       EXEC(stmt_id, &last_insert_id, &rows_affected);
+
+       AWAIT_REPLICATION(1, last_applied + 1);
+
+       return MUNIT_OK;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/lib/client.h 
new/dqlite-1.11.1/test/lib/client.h
--- old/dqlite-1.10.0/test/lib/client.h 2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/test/lib/client.h 2022-07-13 13:35:54.000000000 +0200
@@ -39,6 +39,14 @@
                munit_assert_int(rv_, ==, 0);         \
        }
 
+/* Send the initial client handshake for a specific client. */
+#define HANDSHAKE_C(CLIENT)                           \
+       {                                             \
+               int rv_;                              \
+               rv_ = clientSendHandshake(CLIENT);    \
+               munit_assert_int(rv_, ==, 0);         \
+       }
+
 /* Send an add request. */
 #define ADD(ID, ADDRESS)                                     \
        {                                                    \
@@ -69,6 +77,16 @@
                munit_assert_int(rv_, ==, 0);          \
        }
 
+/* Send a transfer request. */
+#define TRANSFER(ID, CLIENT)                             \
+       {                                                \
+               int rv_;                                 \
+               rv_ = clientSendTransfer(CLIENT, ID);    \
+               munit_assert_int(rv_, ==, 0);            \
+               rv_ = clientRecvEmpty(CLIENT);           \
+               munit_assert_int(rv_, ==, 0);            \
+       }
+
 /* Open a test database. */
 #define OPEN                                             \
        {                                                \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/lib/cluster.h 
new/dqlite-1.11.1/test/lib/cluster.h
--- old/dqlite-1.10.0/test/lib/cluster.h        2022-04-13 16:59:33.000000000 
+0200
+++ new/dqlite-1.11.1/test/lib/cluster.h        2022-07-13 13:35:54.000000000 
+0200
@@ -52,10 +52,11 @@
                int _rv;                                                    \
                SETUP_HEAP;                                                 \
                SETUP_SQLITE;                                               \
-               _rv = raft_fixture_init(&f->cluster, N_SERVERS, f->fsms);   \
+               _rv = raft_fixture_initialize(&f->cluster);                 \
                munit_assert_int(_rv, ==, 0);                               \
                for (_i = 0; _i < N_SERVERS; _i++) {                        \
                        SETUP_SERVER(_i, VERSION);                          \
+                       raft_fixture_grow(&f->cluster, &f->fsms[_i]);       \
                }                                                           \
                _rv = raft_fixture_configuration(&f->cluster, N_SERVERS,    \
                                                 &_configuration);          \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/lib/server.c 
new/dqlite-1.11.1/test/lib/server.c
--- old/dqlite-1.10.0/test/lib/server.c 2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/test/lib/server.c 2022-07-13 13:35:54.000000000 +0200
@@ -17,8 +17,7 @@
        *fd = socket(AF_UNIX, SOCK_STREAM, 0);
        munit_assert_int(*fd, !=, -1);
        rv = connect(*fd, (struct sockaddr *)&addr, sizeof(sa_family_t) + 
strlen(address + 1) + 1);
-       munit_assert_int(rv, ==, 0);
-       return 0;
+       return rv;
 }
 
 void test_server_setup(struct test_server *s,
@@ -39,8 +38,7 @@
 {
        int rv;
 
-       clientClose(&s->client);
-       close(s->client.fd);
+       test_server_client_close(s, &s->client);
        rv = dqlite_node_stop(s->dqlite);
        munit_assert_int(rv, ==, 0);
        dqlite_node_destroy(s->dqlite);
@@ -54,7 +52,6 @@
 
 void test_server_start(struct test_server *s)
 {
-       int client;
        int rv;
 
        rv = dqlite_node_create(s->id, s->address, s->dir, &s->dqlite);
@@ -72,12 +69,7 @@
        rv = dqlite_node_start(s->dqlite);
        munit_assert_int(rv, ==, 0);
 
-       /* Connect a client. */
-       rv = endpointConnect(NULL, s->address, &client);
-       munit_assert_int(rv, ==, 0);
-
-       rv = clientInit(&s->client, client);
-       munit_assert_int(rv, ==, 0);
+       test_server_client_connect(s, &s->client);
 }
 
 struct client *test_server_client(struct test_server *s)
@@ -85,12 +77,29 @@
        return &s->client;
 }
 
-int test_server_client_reconnect(struct test_server *s)
+void test_server_client_reconnect(struct test_server *s, struct client *c)
+{
+       test_server_client_close(s, c);
+       test_server_client_connect(s, c);
+}
+
+void test_server_client_connect(struct test_server *s, struct client *c)
+{
+       int rv;
+       int fd;
+
+       rv = endpointConnect(NULL, s->address, &fd);
+       munit_assert_int(rv, ==, 0);
+
+       rv = clientInit(c, fd);
+       munit_assert_int(rv, ==, 0);
+}
+
+void test_server_client_close(struct test_server *s, struct client *c)
 {
-       clientClose(&s->client);
-       close(s->client.fd);
-       endpointConnect(NULL, s->address, &s->client.fd);
-       return clientInit(&s->client, s->client.fd);
+       (void) s;
+       clientClose(c);
+       close(c->fd);
 }
 
 static void setOther(struct test_server *s, struct test_server *other)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dqlite-1.10.0/test/lib/server.h 
new/dqlite-1.11.1/test/lib/server.h
--- old/dqlite-1.10.0/test/lib/server.h 2022-04-13 16:59:33.000000000 +0200
+++ new/dqlite-1.11.1/test/lib/server.h 2022-07-13 13:35:54.000000000 +0200
@@ -43,7 +43,13 @@
 /* Return a client connected to the server. */
 struct client *test_server_client(struct test_server *s);
 
-/* Closes and reopens the client connection to the server. */
-int test_server_client_reconnect(struct test_server *s);
+/* Closes and reopens a client connection to the server. */
+void test_server_client_reconnect(struct test_server *s, struct client *c);
+
+/* Opens a client connection to the server. */
+void test_server_client_connect(struct test_server *s, struct client *c);
+
+/* Closes a client connection to ther server. */
+void test_server_client_close(struct test_server *s, struct client *c);
 
 #endif /* TEST_SERVER_H */

Reply via email to