added a new persistent transaction test program
authorAndrew Tridgell <tridge@samba.org>
Wed, 30 Jul 2008 09:55:54 +0000 (19:55 +1000)
committerAndrew Tridgell <tridge@samba.org>
Wed, 30 Jul 2008 09:55:54 +0000 (19:55 +1000)
Makefile.in
tests/ctdb_transaction.c [new file with mode: 0644]
tests/transaction.sh [new file with mode: 0755]

index 9b5ebe0288955dd5efa558a3650e2c7a0065e050..cf1240b1f71af90ac27d5c75264e6485687827a4 100755 (executable)
@@ -56,7 +56,8 @@ CTDB_SERVER_OBJ = server/ctdbd.o server/ctdb_daemon.o server/ctdb_lockwait.o \
        server/ctdb_keepalive.o server/ctdb_logging.o server/ctdb_uptime.c \
        $(CTDB_CLIENT_OBJ) $(CTDB_TCP_OBJ) @INFINIBAND_WRAPPER_OBJ@
 
-TEST_BINS=bin/ctdb_bench bin/ctdb_fetch bin/ctdb_store bin/ctdb_randrec bin/ctdb_persistent bin/ctdb_traverse bin/rb_test \
+TEST_BINS=bin/ctdb_bench bin/ctdb_fetch bin/ctdb_store bin/ctdb_randrec bin/ctdb_persistent \
+       bin/ctdb_traverse bin/rb_test bin/ctdb_transaction \
        @INFINIBAND_BINS@
 
 BINS = bin/ctdb @CTDB_SCSI_IO@ bin/ctdb_ipmux bin/smnotify
@@ -141,6 +142,10 @@ bin/ctdb_persistent: $(CTDB_CLIENT_OBJ) tests/ctdb_persistent.o
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ tests/ctdb_persistent.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
 
+bin/ctdb_transaction: $(CTDB_CLIENT_OBJ) tests/ctdb_transaction.o 
+       @echo Linking $@
+       @$(CC) $(CFLAGS) -o $@ tests/ctdb_transaction.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
+
 bin/ibwrapper_test: $(CTDB_CLIENT_OBJ) ib/ibwrapper_test.o
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ ib/ibwrapper_test.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS)
diff --git a/tests/ctdb_transaction.c b/tests/ctdb_transaction.c
new file mode 100644 (file)
index 0000000..783fa12
--- /dev/null
@@ -0,0 +1,258 @@
+/* 
+   simple tool to test persistent databases
+
+   Copyright (C) Andrew Tridgell  2006-2007
+   Copyright (c) Ronnie sahlberg  2007
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "popt.h"
+#include "cmdline.h"
+
+#include <sys/time.h>
+#include <time.h>
+
+static struct timeval tp1,tp2;
+
+static void start_timer(void)
+{
+       gettimeofday(&tp1,NULL);
+}
+
+static double end_timer(void)
+{
+       gettimeofday(&tp2,NULL);
+       return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
+               (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
+}
+
+static int timelimit = 10;
+
+static unsigned int pnn;
+
+static TDB_DATA old_data;
+
+static int success = true;
+
+static void each_second(struct event_context *ev, struct timed_event *te, 
+                                        struct timeval t, void *private_data)
+{
+       struct ctdb_context *ctdb = talloc_get_type(private_data, struct ctdb_context);
+       int i;
+       uint32_t *old_counters;
+
+
+       printf("[%4u] Counters: ", getpid());
+       old_counters = (uint32_t *)old_data.dptr;
+       for (i=0;i<old_data.dsize/sizeof(uint32_t); i++) {
+               printf("%6u ", old_counters[i]);
+       }
+       printf("\n"); 
+
+       event_add_timed(ev, ctdb, timeval_current_ofs(1, 0), each_second, ctdb);
+}
+
+static void check_counters(struct ctdb_context *ctdb, TDB_DATA data)
+{
+       int i;
+       uint32_t *counters, *old_counters;
+
+       counters     = (uint32_t *)data.dptr;
+       old_counters = (uint32_t *)old_data.dptr;
+
+       /* check that all the counters are monotonic increasing */
+       for (i=0; i<old_data.dsize/sizeof(uint32_t); i++) {
+               if (counters[i]<old_counters[i]) {
+                       printf("[%4u] ERROR: counters has decreased for node %u  From %u to %u\n", 
+                              getpid(), i, old_counters[i], counters[i]);
+                       success = false;
+               }
+       }
+
+       if (old_data.dsize != data.dsize) {
+               old_data.dsize = data.dsize;
+               old_data.dptr = talloc_realloc_size(ctdb, old_data.dptr, old_data.dsize);
+       }
+
+       memcpy(old_data.dptr, data.dptr, data.dsize);
+}
+
+
+
+static void test_store_records(struct ctdb_context *ctdb, struct event_context *ev)
+{
+       TDB_DATA key;
+       struct ctdb_db_context *ctdb_db;
+       int ret;
+       uint32_t *counters;
+       ctdb_db = ctdb_db_handle(ctdb, "transaction.tdb");
+
+       key.dptr = discard_const("testkey");
+       key.dsize = strlen((const char *)key.dptr)+1;
+
+       start_timer();
+       while (end_timer() < timelimit) {
+               TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+               TDB_DATA data;
+
+               struct ctdb_transaction_handle *h;
+               h = ctdb_transaction_start(ctdb_db, tmp_ctx);
+               if (h == NULL) {
+                       printf("Failed to start transaction on node %d\n", 
+                              ctdb_get_pnn(ctdb));
+                       talloc_free(tmp_ctx);
+                       return;
+               }
+
+               ret = ctdb_transaction_fetch(h, tmp_ctx, key, &data);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR,("Failed to fetch record\n"));
+                       exit(1);                        
+               }
+
+               if (data.dsize < sizeof(uint32_t) * (pnn+1)) {
+                       unsigned char *ptr = data.dptr;
+
+                       data.dptr = talloc_zero_size(tmp_ctx, sizeof(uint32_t) * (pnn+1));
+                       memcpy(data.dptr, ptr, data.dsize);
+                       talloc_free(ptr);
+
+                       data.dsize = sizeof(uint32_t) * (pnn+1);
+               }
+
+               if (data.dptr == NULL) {
+                       printf("Failed to realloc array\n");
+                       talloc_free(tmp_ctx);
+                       return;
+               }
+
+               counters = (uint32_t *)data.dptr;
+
+               /* bump our counter */
+               counters[pnn]++;
+
+               ret = ctdb_transaction_store(h, key, data);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR,("Failed to store record\n"));
+                       exit(1);
+               }
+
+               ret = ctdb_transaction_commit(h);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR,("Failed to commit transaction\n"));
+                       exit(1);
+               }
+
+               /* store the counters and verify that they are sane */
+               if (pnn == 0) {
+                       check_counters(ctdb, data);
+               }
+
+               talloc_free(tmp_ctx);
+       }
+
+}
+
+/*
+  main program
+*/
+int main(int argc, const char *argv[])
+{
+       struct ctdb_context *ctdb;
+       struct ctdb_db_context *ctdb_db;
+       int unsafe_writes = 0;
+       struct poptOption popt_options[] = {
+               POPT_AUTOHELP
+               POPT_CTDB_CMDLINE
+               { "timelimit", 't', POPT_ARG_INT, &timelimit, 0, "timelimit", "integer" },
+               { "unsafe-writes", 'u', POPT_ARG_NONE, &unsafe_writes, 0, "do not use tdb transactions when writing", NULL },
+               POPT_TABLEEND
+       };
+       int opt;
+       const char **extra_argv;
+       int extra_argc = 0;
+       poptContext pc;
+       struct event_context *ev;
+
+       setlinebuf(stdout);
+
+       pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
+
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               default:
+                       fprintf(stderr, "Invalid option %s: %s\n", 
+                               poptBadOption(pc, 0), poptStrerror(opt));
+                       exit(1);
+               }
+       }
+
+       /* setup the remaining options for the main program to use */
+       extra_argv = poptGetArgs(pc);
+       if (extra_argv) {
+               extra_argv++;
+               while (extra_argv[extra_argc]) extra_argc++;
+       }
+
+       ev = event_context_init(NULL);
+
+       ctdb = ctdb_cmdline_client(ev);
+       if (ctdb == NULL) {
+               printf("Could not attach to daemon\n");
+               return 1;
+       }
+
+       /* attach to a specific database */
+       if (unsafe_writes == 1) {
+               ctdb_db = ctdb_attach(ctdb, "transaction.tdb", true, TDB_NOSYNC);
+       } else {
+               ctdb_db = ctdb_attach(ctdb, "transaction.tdb", true, 0);
+       }
+
+       if (!ctdb_db) {
+               printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
+               exit(1);
+       }
+
+       printf("Waiting for cluster\n");
+       while (1) {
+               uint32_t recmode=1;
+               ctdb_ctrl_getrecmode(ctdb, ctdb, timeval_zero(), CTDB_CURRENT_NODE, &recmode);
+               if (recmode == 0) break;
+               event_loop_once(ev);
+       }
+
+       pnn = ctdb_get_pnn(ctdb);
+       printf("Starting test on node %u. running for %u seconds\n", pnn, timelimit);
+
+       if (pnn == 0) {
+               event_add_timed(ev, ctdb, timeval_current_ofs(1, 0), each_second, ctdb);
+       }
+
+       test_store_records(ctdb, ev);
+
+       if (pnn == 0) {
+               if (success != true) {
+                       printf("The test FAILED\n");
+                       return 1;
+               } else {
+                       printf("SUCCESS!\n");
+               }
+       }
+       return 0;
+}
diff --git a/tests/transaction.sh b/tests/transaction.sh
new file mode 100755 (executable)
index 0000000..7b1f921
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+NUMNODES=4
+if [ $# -gt 0 ]; then
+    NUMNODES=$1
+fi
+
+killall -9 -q ctdb_transaction ctdbd
+
+rm -rf test.db/transaction
+
+echo "Starting $NUMNODES daemons for transaction writes"
+tests/start_daemons.sh $NUMNODES || exit 1
+
+trap 'echo "Killing test"; killall -9 -q ctdbd ctdb_transaction; exit 1' INT TERM
+
+VALGRIND="valgrind -q"
+for i in `seq 1 $NUMNODES`; do
+  $VALGRIND bin/ctdb_transaction --timelimit 30 --socket sock.$i $* &
+  $VALGRIND bin/ctdb_transaction --timelimit 30 --socket sock.$i $* &
+done
+wait
+
+echo "Shutting down"
+bin/ctdb shutdown -n all --socket=sock.1
+killall -9 ctdbd
+
+exit 0