From 6d72586364cee7a3393a794b00221087c56abb73 Mon Sep 17 00:00:00 2001 From: Robert Escriva Date: Mon, 16 Nov 2015 09:41:42 -0500 Subject: [PATCH] Consus commit squashed for preliminary release The code is a work in progress and not yet ready for production use. --- .gitignore | 65 + .tarballignore | 4 + BUGS | 10 + LICENSE | 5 + Makefile.am | 495 +++++ README | 0 TODO | 15 + bindings/c/testcompile.c | 17 + bindings/python/consus.pyx | 175 ++ client/c.cc | 312 ++++ client/client.cc | 756 ++++++++ client/client.h | 112 ++ client/configuration.cc | 75 + client/configuration.h | 62 + client/consus-internal.h | 30 + client/mapper.cc | 23 + client/mapper.h | 35 + client/pending.cc | 62 + client/pending.h | 88 + client/pending_begin_transaction.cc | 103 + client/pending_begin_transaction.h | 45 + client/pending_string.cc | 29 + client/pending_string.h | 38 + client/pending_transaction_abort.cc | 106 ++ client/pending_transaction_abort.h | 48 + client/pending_transaction_commit.cc | 106 ++ client/pending_transaction_commit.h | 48 + client/pending_transaction_read.cc | 165 ++ client/pending_transaction_read.h | 55 + client/pending_transaction_write.cc | 136 ++ client/pending_transaction_write.h | 54 + client/server_selector.cc | 35 + client/server_selector.h | 33 + client/transaction.cc | 147 ++ client/transaction.h | 57 + common/client_configuration.cc | 18 + common/client_configuration.h | 22 + common/constants.h | 29 + common/consus.cc | 54 + common/consus.h | 28 + common/coordinator_link.cc | 352 ++++ common/coordinator_link.h | 109 ++ common/coordinator_returncode.h | 33 + common/crc32c.cc | 610 ++++++ common/crc32c.h | 21 + common/data_center.cc | 54 + common/data_center.h | 38 + common/ids.cc | 36 + common/ids.h | 71 + common/kvs.cc | 50 + common/kvs.h | 42 + common/kvs_configuration.cc | 49 + common/kvs_configuration.h | 26 + common/kvs_state.cc | 108 ++ common/kvs_state.h | 67 + common/macros.h | 16 + common/network_msgtype.cc | 66 + common/network_msgtype.h | 66 + common/paxos_group.cc | 142 ++ common/paxos_group.h | 47 + common/transaction_group.cc | 75 + common/transaction_group.h | 64 + common/transaction_id.cc | 69 + common/transaction_id.h | 69 + common/txman.cc | 50 + common/txman.h | 42 + common/txman_configuration.cc | 109 ++ common/txman_configuration.h | 35 + common/txman_state.cc | 109 ++ common/txman_state.h | 67 + configure.ac | 55 + consus-debug.cc | 27 + consus.cc | 31 + consus.supp | 169 ++ coordinator/coordinator.cc | 988 ++++++++++ coordinator/coordinator.h | 105 ++ coordinator/symtable.c | 32 + coordinator/transitions.cc | 187 ++ coordinator/transitions.h | 49 + coordinator/util.h | 51 + doc/.gitignore | 15 + doc/Makefile | 29 + doc/consus.tex | 89 + doc/consus.xtx | 0 doc/latex.py | 235 +++ include/consus-admin.h | 36 + include/consus.h | 86 + kvs/configuration.cc | 73 + kvs/configuration.h | 56 + kvs/daemon.cc | 830 +++++++++ kvs/daemon.h | 90 + kvs/main.cc | 194 ++ kvs/mapper.cc | 31 + kvs/mapper.h | 35 + m4/anal_warnings.m4 | 118 ++ m4/ax_check_compile_flag.m4 | 72 + m4/ax_check_link_flag.m4 | 71 + m4/ax_check_preproc_flag.m4 | 72 + maint/generate-unit-test-gremlins | 48 + maint/valgrind-gremlins | 34 + man/consus-availability-check.1.md | 21 + man/consus-coordinator.1.md | 21 + man/consus-create-data-center.1.md | 21 + man/consus-debug-client-configuration.1.md | 21 + man/consus-debug-kvs-configuration.1.md | 21 + man/consus-debug-txman-configuration.1.md | 21 + man/consus-debug.1.md | 21 + man/consus-key-value-store.1.md | 21 + man/consus-set-default-data-center.1.md | 21 + man/consus-transaction-manager.1.md | 21 + man/consus.1.md | 21 + namespace.h | 12 + test/1-node-1-dc-cluster.gremlin | 31 + test/1-node-3-dc-cluster.gremlin | 59 + test/2-node-1-dc-cluster.gremlin | 34 + test/3-node-1-dc-cluster.gremlin | 37 + test/4-node-1-dc-cluster.gremlin | 40 + test/5-node-1-dc-cluster.gremlin | 43 + test/5-node-2-dc-cluster.gremlin | 65 + test/5-node-3-dc-cluster.gremlin | 87 + test/5-node-4-dc-cluster.gremlin | 109 ++ test/5-node-5-dc-cluster.gremlin | 131 ++ test/5-node-6-dc-cluster.gremlin | 153 ++ test/5-node-7-dc-cluster.gremlin | 175 ++ test/env.sh | 10 + test/paxos/generalized-brute-force.cc | 488 +++++ test/paxos/generalized.cc | 250 +++ test/th.cc | 217 +++ test/th.h | 144 ++ test/th_main.cc | 45 + test/unit/00.client.1n.1dc.gremlin | 4 + test/unit/00.client.2n.1dc.gremlin | 4 + test/unit/00.client.3n.1dc.gremlin | 4 + test/unit/00.client.4n.1dc.gremlin | 4 + test/unit/00.client.5n.1dc.gremlin | 4 + test/unit/00.client.py | 11 + test/unit/00.nop-abort.1n.1dc.gremlin | 4 + test/unit/00.nop-abort.2n.1dc.gremlin | 4 + test/unit/00.nop-abort.3n.1dc.gremlin | 4 + test/unit/00.nop-abort.4n.1dc.gremlin | 4 + test/unit/00.nop-abort.5n.1dc.gremlin | 4 + test/unit/00.nop-abort.py | 37 + test/unit/00.nop-commit.1n.1dc.gremlin | 4 + test/unit/00.nop-commit.2n.1dc.gremlin | 4 + test/unit/00.nop-commit.3n.1dc.gremlin | 4 + test/unit/00.nop-commit.4n.1dc.gremlin | 4 + test/unit/00.nop-commit.5n.1dc.gremlin | 4 + test/unit/00.nop-commit.py | 37 + test/unit/10.single-get.1n.1dc.gremlin | 4 + test/unit/10.single-get.2n.1dc.gremlin | 4 + test/unit/10.single-get.3n.1dc.gremlin | 4 + test/unit/10.single-get.4n.1dc.gremlin | 4 + test/unit/10.single-get.5n.1dc.gremlin | 4 + test/unit/10.single-get.py | 6 + test/unit/10.single-put.1n.1dc.gremlin | 4 + test/unit/10.single-put.2n.1dc.gremlin | 4 + test/unit/10.single-put.3n.1dc.gremlin | 4 + test/unit/10.single-put.4n.1dc.gremlin | 4 + test/unit/10.single-put.5n.1dc.gremlin | 4 + test/unit/10.single-put.py | 6 + ...11.put-get-separate-commits.1n.1dc.gremlin | 4 + ...11.put-get-separate-commits.2n.1dc.gremlin | 4 + ...11.put-get-separate-commits.3n.1dc.gremlin | 4 + ...11.put-get-separate-commits.4n.1dc.gremlin | 4 + ...11.put-get-separate-commits.5n.1dc.gremlin | 4 + test/unit/11.put-get-separate-commits.py | 15 + tools/availability-check.cc | 85 + tools/common.cc | 37 + tools/common.h | 24 + tools/connect_opts.cc | 102 + tools/connect_opts.h | 55 + tools/coordinator.cc | 155 ++ tools/create-data-center.cc | 58 + tools/debug-client-configuration.cc | 61 + tools/debug-kvs-configuration.cc | 60 + tools/debug-txman-configuration.cc | 60 + tools/locate-coordinator-lib.h | 43 + tools/set-default-data-center.cc | 58 + txman/configuration.cc | 186 ++ txman/configuration.h | 75 + txman/daemon.cc | 1214 ++++++++++++ txman/daemon.h | 134 ++ txman/durable_log.cc | 423 +++++ txman/durable_log.h | 73 + txman/generalized_paxos.cc | 1080 +++++++++++ txman/generalized_paxos.h | 220 +++ txman/global_voter.cc | 72 + txman/global_voter.h | 77 + txman/local_voter.cc | 384 ++++ txman/local_voter.h | 67 + txman/log_entry_t.cc | 73 + txman/log_entry_t.h | 48 + txman/main.cc | 194 ++ txman/mapper.cc | 31 + txman/mapper.h | 35 + txman/paxos_synod.cc | 393 ++++ txman/paxos_synod.h | 133 ++ txman/transaction.cc | 1650 +++++++++++++++++ txman/transaction.h | 187 ++ visibility.h | 10 + 200 files changed, 20447 insertions(+) create mode 100644 .gitignore create mode 100644 .tarballignore create mode 100644 BUGS create mode 100644 LICENSE create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 bindings/c/testcompile.c create mode 100644 bindings/python/consus.pyx create mode 100644 client/c.cc create mode 100644 client/client.cc create mode 100644 client/client.h create mode 100644 client/configuration.cc create mode 100644 client/configuration.h create mode 100644 client/consus-internal.h create mode 100644 client/mapper.cc create mode 100644 client/mapper.h create mode 100644 client/pending.cc create mode 100644 client/pending.h create mode 100644 client/pending_begin_transaction.cc create mode 100644 client/pending_begin_transaction.h create mode 100644 client/pending_string.cc create mode 100644 client/pending_string.h create mode 100644 client/pending_transaction_abort.cc create mode 100644 client/pending_transaction_abort.h create mode 100644 client/pending_transaction_commit.cc create mode 100644 client/pending_transaction_commit.h create mode 100644 client/pending_transaction_read.cc create mode 100644 client/pending_transaction_read.h create mode 100644 client/pending_transaction_write.cc create mode 100644 client/pending_transaction_write.h create mode 100644 client/server_selector.cc create mode 100644 client/server_selector.h create mode 100644 client/transaction.cc create mode 100644 client/transaction.h create mode 100644 common/client_configuration.cc create mode 100644 common/client_configuration.h create mode 100644 common/constants.h create mode 100644 common/consus.cc create mode 100644 common/consus.h create mode 100644 common/coordinator_link.cc create mode 100644 common/coordinator_link.h create mode 100644 common/coordinator_returncode.h create mode 100644 common/crc32c.cc create mode 100644 common/crc32c.h create mode 100644 common/data_center.cc create mode 100644 common/data_center.h create mode 100644 common/ids.cc create mode 100644 common/ids.h create mode 100644 common/kvs.cc create mode 100644 common/kvs.h create mode 100644 common/kvs_configuration.cc create mode 100644 common/kvs_configuration.h create mode 100644 common/kvs_state.cc create mode 100644 common/kvs_state.h create mode 100644 common/macros.h create mode 100644 common/network_msgtype.cc create mode 100644 common/network_msgtype.h create mode 100644 common/paxos_group.cc create mode 100644 common/paxos_group.h create mode 100644 common/transaction_group.cc create mode 100644 common/transaction_group.h create mode 100644 common/transaction_id.cc create mode 100644 common/transaction_id.h create mode 100644 common/txman.cc create mode 100644 common/txman.h create mode 100644 common/txman_configuration.cc create mode 100644 common/txman_configuration.h create mode 100644 common/txman_state.cc create mode 100644 common/txman_state.h create mode 100644 configure.ac create mode 100644 consus-debug.cc create mode 100644 consus.cc create mode 100644 consus.supp create mode 100644 coordinator/coordinator.cc create mode 100644 coordinator/coordinator.h create mode 100644 coordinator/symtable.c create mode 100644 coordinator/transitions.cc create mode 100644 coordinator/transitions.h create mode 100644 coordinator/util.h create mode 100644 doc/.gitignore create mode 100644 doc/Makefile create mode 100644 doc/consus.tex create mode 100644 doc/consus.xtx create mode 100644 doc/latex.py create mode 100644 include/consus-admin.h create mode 100644 include/consus.h create mode 100644 kvs/configuration.cc create mode 100644 kvs/configuration.h create mode 100644 kvs/daemon.cc create mode 100644 kvs/daemon.h create mode 100644 kvs/main.cc create mode 100644 kvs/mapper.cc create mode 100644 kvs/mapper.h create mode 100644 m4/anal_warnings.m4 create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 m4/ax_check_link_flag.m4 create mode 100644 m4/ax_check_preproc_flag.m4 create mode 100755 maint/generate-unit-test-gremlins create mode 100755 maint/valgrind-gremlins create mode 100644 man/consus-availability-check.1.md create mode 100644 man/consus-coordinator.1.md create mode 100644 man/consus-create-data-center.1.md create mode 100644 man/consus-debug-client-configuration.1.md create mode 100644 man/consus-debug-kvs-configuration.1.md create mode 100644 man/consus-debug-txman-configuration.1.md create mode 100644 man/consus-debug.1.md create mode 100644 man/consus-key-value-store.1.md create mode 100644 man/consus-set-default-data-center.1.md create mode 100644 man/consus-transaction-manager.1.md create mode 100644 man/consus.1.md create mode 100644 namespace.h create mode 100755 test/1-node-1-dc-cluster.gremlin create mode 100755 test/1-node-3-dc-cluster.gremlin create mode 100755 test/2-node-1-dc-cluster.gremlin create mode 100755 test/3-node-1-dc-cluster.gremlin create mode 100755 test/4-node-1-dc-cluster.gremlin create mode 100755 test/5-node-1-dc-cluster.gremlin create mode 100755 test/5-node-2-dc-cluster.gremlin create mode 100755 test/5-node-3-dc-cluster.gremlin create mode 100755 test/5-node-4-dc-cluster.gremlin create mode 100755 test/5-node-5-dc-cluster.gremlin create mode 100755 test/5-node-6-dc-cluster.gremlin create mode 100755 test/5-node-7-dc-cluster.gremlin create mode 100644 test/env.sh create mode 100644 test/paxos/generalized-brute-force.cc create mode 100644 test/paxos/generalized.cc create mode 100644 test/th.cc create mode 100644 test/th.h create mode 100644 test/th_main.cc create mode 100755 test/unit/00.client.1n.1dc.gremlin create mode 100755 test/unit/00.client.2n.1dc.gremlin create mode 100755 test/unit/00.client.3n.1dc.gremlin create mode 100755 test/unit/00.client.4n.1dc.gremlin create mode 100755 test/unit/00.client.5n.1dc.gremlin create mode 100644 test/unit/00.client.py create mode 100755 test/unit/00.nop-abort.1n.1dc.gremlin create mode 100755 test/unit/00.nop-abort.2n.1dc.gremlin create mode 100755 test/unit/00.nop-abort.3n.1dc.gremlin create mode 100755 test/unit/00.nop-abort.4n.1dc.gremlin create mode 100755 test/unit/00.nop-abort.5n.1dc.gremlin create mode 100644 test/unit/00.nop-abort.py create mode 100755 test/unit/00.nop-commit.1n.1dc.gremlin create mode 100755 test/unit/00.nop-commit.2n.1dc.gremlin create mode 100755 test/unit/00.nop-commit.3n.1dc.gremlin create mode 100755 test/unit/00.nop-commit.4n.1dc.gremlin create mode 100755 test/unit/00.nop-commit.5n.1dc.gremlin create mode 100644 test/unit/00.nop-commit.py create mode 100755 test/unit/10.single-get.1n.1dc.gremlin create mode 100755 test/unit/10.single-get.2n.1dc.gremlin create mode 100755 test/unit/10.single-get.3n.1dc.gremlin create mode 100755 test/unit/10.single-get.4n.1dc.gremlin create mode 100755 test/unit/10.single-get.5n.1dc.gremlin create mode 100644 test/unit/10.single-get.py create mode 100755 test/unit/10.single-put.1n.1dc.gremlin create mode 100755 test/unit/10.single-put.2n.1dc.gremlin create mode 100755 test/unit/10.single-put.3n.1dc.gremlin create mode 100755 test/unit/10.single-put.4n.1dc.gremlin create mode 100755 test/unit/10.single-put.5n.1dc.gremlin create mode 100644 test/unit/10.single-put.py create mode 100755 test/unit/11.put-get-separate-commits.1n.1dc.gremlin create mode 100755 test/unit/11.put-get-separate-commits.2n.1dc.gremlin create mode 100755 test/unit/11.put-get-separate-commits.3n.1dc.gremlin create mode 100755 test/unit/11.put-get-separate-commits.4n.1dc.gremlin create mode 100755 test/unit/11.put-get-separate-commits.5n.1dc.gremlin create mode 100644 test/unit/11.put-get-separate-commits.py create mode 100644 tools/availability-check.cc create mode 100644 tools/common.cc create mode 100644 tools/common.h create mode 100644 tools/connect_opts.cc create mode 100644 tools/connect_opts.h create mode 100644 tools/coordinator.cc create mode 100644 tools/create-data-center.cc create mode 100644 tools/debug-client-configuration.cc create mode 100644 tools/debug-kvs-configuration.cc create mode 100644 tools/debug-txman-configuration.cc create mode 100644 tools/locate-coordinator-lib.h create mode 100644 tools/set-default-data-center.cc create mode 100644 txman/configuration.cc create mode 100644 txman/configuration.h create mode 100644 txman/daemon.cc create mode 100644 txman/daemon.h create mode 100644 txman/durable_log.cc create mode 100644 txman/durable_log.h create mode 100644 txman/generalized_paxos.cc create mode 100644 txman/generalized_paxos.h create mode 100644 txman/global_voter.cc create mode 100644 txman/global_voter.h create mode 100644 txman/local_voter.cc create mode 100644 txman/local_voter.h create mode 100644 txman/log_entry_t.cc create mode 100644 txman/log_entry_t.h create mode 100644 txman/main.cc create mode 100644 txman/mapper.cc create mode 100644 txman/mapper.h create mode 100644 txman/paxos_synod.cc create mode 100644 txman/paxos_synod.h create mode 100644 txman/transaction.cc create mode 100644 txman/transaction.h create mode 100644 visibility.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62d2c2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# wildcards +.deps +.dirstamp +.libs +*.la +*.lo +*.o +# specific files +/aclocal.m4 +/autom4te.cache +/bindings/c/testcompile +/bindings/python/consus.c +/compile +/config.guess +/config.h +/config.h.in +/config.h.in~ +/config.log +/config.status +/config.sub +/configure +/consus +/consus-availability-check +/consus-coordinator +/consus-create-data-center +/consus-debug +/consus-debug-client-configuration +/consus-debug-kvs-configuration +/consus-debug-txman-configuration +/consus-key-value-store +/consus-set-default-data-center +/consus-transaction-manager +/depcomp +/install-sh +/libtool +/ltmain.sh +/m4 +/Makefile +/Makefile.in +/man/consus.1 +/man/consus.1.h2m +/man/consus-availability-check.1 +/man/consus-availability-check.1.h2m +/man/consus-coordinator.1 +/man/consus-coordinator.1.h2m +/man/consus-create-data-center.1 +/man/consus-create-data-center.1.h2m +/man/consus-debug.1 +/man/consus-debug.1.h2m +/man/consus-debug-client-configuration.1 +/man/consus-debug-client-configuration.1.h2m +/man/consus-debug-kvs-configuration.1 +/man/consus-debug-kvs-configuration.1.h2m +/man/consus-debug-txman-configuration.1 +/man/consus-debug-txman-configuration.1.h2m +/man/consus-key-value-store.1 +/man/consus-key-value-store.1.h2m +/man/consus-set-default-data-center.1 +/man/consus-set-default-data-center.1.h2m +/man/consus-transaction-manager.1 +/man/consus-transaction-manager.1.h2m +/missing +/stamp-h1 +/test/paxos/generalized +/test/paxos/generalized-brute-force diff --git a/.tarballignore b/.tarballignore new file mode 100644 index 0000000..6ddadbb --- /dev/null +++ b/.tarballignore @@ -0,0 +1,4 @@ +.gitignore +m4/ax_check_link_flag.m4 +m4/ax_check_preproc_flag.m4 +.tarballignore diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..92fb909 --- /dev/null +++ b/BUGS @@ -0,0 +1,10 @@ +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:_Znwm + fun:_ZN9replicant6daemon21enqueue_paxos_commandENS_9server_idEmNS_9slot_typeERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE + fun:_ZN9replicant6daemon12process_pokeENS_9server_idESt8auto_ptrIN1e6bufferEENS3_8unpackerE + fun:_ZN9replicant6daemon3runEbNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES6_S6_bbN3po63net8locationEbRKNS_9bootstrapEPKcSE_SE_SE_ + fun:main +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..70550e0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright (c) 2015, Robert Escriva +All rights reserved. + +Upon public release, this license text will be replaced with an open source +3-clause BSD license. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b2684b9 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,495 @@ +# Copyright (c) 2015, Robert Escriva +# All rights reserved. + +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} + +AM_CPPFLAGS = -DCONSUS_EXEC_DIR="\"$(consusexecdir)\"" -I${abs_top_srcdir}/include $(PO6_CFLAGS) $(E_CFLAGS) $(BUSYBEE_CFLAGS) $(REPLICANT_CFLAGS) +AM_CFLAGS = -fvisibility=hidden $(WANAL_CFLAGS) +AM_CXXFLAGS = -fvisibility=hidden -fvisibility-inlines-hidden $(PO6_CFLAGS) $(E_CFLAGS) $(BUSYBEE_CFLAGS) $(REPLICANT_CFLAGS) $(WANAL_CXXFLAGS) +AM_MAKEFLAGS = --no-print-directory +HELP2MAN_FLAGS = --no-discard-stderr --libtool --no-info --version-string=$(VERSION) --manual="Consus User Manual" +TESTS_ENVIRONMENT = . $(abs_top_srcdir)/test/env.sh "${abs_top_srcdir}" "${abs_top_builddir}" "${VERSION}"; + +consusexecdir = $(libexecdir)/$(PACKAGE)-$(VERSION) +pkgconfigdir = $(libdir)/pkgconfig + +BUILT_SOURCES = + +EXTRA_DIST = + +CLEANFILES = + +include_HEADERS = +noinst_HEADERS = + +lib_LTLIBRARIES = + +bin_PROGRAMS = +noinst_PROGRAMS = +check_PROGRAMS = +check_SCRIPTS = + +consusexec_PROGRAMS = +consusexec_LTLIBRARIES = + +dist_man_MANS = + +pyexec_LTLIBRARIES = + +TESTS = + +################################################################################ +#################################### Common #################################### +################################################################################ + +%.h2m: %.md + $(pandoc_verbose)pandoc -s -w man -o $@ $< + $(sed_verbose)sed -e 's/^\.SH \(.*\)$$/[\1]/' -e 's/^.PP$$//' $@ > $@.tmp + @mv $@.tmp $@ + +help2man_verbose = $(help2man_verbose_$(V)) +help2man_verbose_ = $(help2man_verbose_$(AM_DEFAULT_VERBOSITY)) +help2man_verbose_0 = @echo " MAN " $@; +pandoc_verbose = $(pandoc_verbose_$(V)) +pandoc_verbose_ = $(pandoc_verbose_$(AM_DEFAULT_VERBOSITY)) +pandoc_verbose_0 = @echo " PANDOC " $@; +pyx_verbose = $(pyx_verbose_$(V)) +pyx_verbose_ = $(pyx_verbose_$(AM_DEFAULT_VERBOSITY)) +pyx_verbose_0 = @echo " PYX " $@; +sed_verbose = $(sed_verbose_$(V)) +sed_verbose_ = $(sed_verbose_$(AM_DEFAULT_VERBOSITY)) +sed_verbose_0 = @echo " SED " $@; + +th_sources = test/th_main.cc test/th.cc test/th.h + +EXTRA_DIST += LICENSE + +noinst_HEADERS += namespace.h +noinst_HEADERS += visibility.h +noinst_HEADERS += common/client_configuration.h +noinst_HEADERS += common/constants.h +noinst_HEADERS += common/consus.h +noinst_HEADERS += common/coordinator_link.h +noinst_HEADERS += common/coordinator_returncode.h +noinst_HEADERS += common/crc32c.h +noinst_HEADERS += common/data_center.h +noinst_HEADERS += common/ids.h +noinst_HEADERS += common/kvs_configuration.h +noinst_HEADERS += common/kvs.h +noinst_HEADERS += common/kvs_state.h +noinst_HEADERS += common/macros.h +noinst_HEADERS += common/network_msgtype.h +noinst_HEADERS += common/paxos_group.h +noinst_HEADERS += common/transaction_id.h +noinst_HEADERS += common/transaction_group.h +noinst_HEADERS += common/txman_configuration.h +noinst_HEADERS += common/txman.h +noinst_HEADERS += common/txman_state.h + +################################################################################ +############################## Transaction Manager ############################# +################################################################################ + +consusexec_PROGRAMS += consus-transaction-manager +dist_man_MANS += man/consus-transaction-manager.1 + +noinst_HEADERS += txman/configuration.h +noinst_HEADERS += txman/daemon.h +noinst_HEADERS += txman/durable_log.h +noinst_HEADERS += txman/generalized_paxos.h +noinst_HEADERS += txman/local_voter.h +noinst_HEADERS += txman/log_entry_t.h +noinst_HEADERS += txman/mapper.h +noinst_HEADERS += txman/paxos_synod.h +noinst_HEADERS += txman/transaction.h + +consus_transaction_manager_SOURCES = +consus_transaction_manager_SOURCES += common/consus.cc +consus_transaction_manager_SOURCES += common/coordinator_link.cc +consus_transaction_manager_SOURCES += common/crc32c.cc +consus_transaction_manager_SOURCES += common/data_center.cc +consus_transaction_manager_SOURCES += common/ids.cc +consus_transaction_manager_SOURCES += common/kvs.cc +consus_transaction_manager_SOURCES += common/network_msgtype.cc +consus_transaction_manager_SOURCES += common/paxos_group.cc +consus_transaction_manager_SOURCES += common/transaction_id.cc +consus_transaction_manager_SOURCES += common/transaction_group.cc +consus_transaction_manager_SOURCES += common/txman.cc +consus_transaction_manager_SOURCES += common/txman_configuration.cc +consus_transaction_manager_SOURCES += common/txman_state.cc +consus_transaction_manager_SOURCES += txman/configuration.cc +consus_transaction_manager_SOURCES += txman/daemon.cc +consus_transaction_manager_SOURCES += txman/durable_log.cc +consus_transaction_manager_SOURCES += txman/generalized_paxos.cc +consus_transaction_manager_SOURCES += txman/global_voter.cc +consus_transaction_manager_SOURCES += txman/local_voter.cc +consus_transaction_manager_SOURCES += txman/log_entry_t.cc +consus_transaction_manager_SOURCES += txman/main.cc +consus_transaction_manager_SOURCES += txman/mapper.cc +consus_transaction_manager_SOURCES += txman/paxos_synod.cc +consus_transaction_manager_SOURCES += txman/transaction.cc +consus_transaction_manager_SOURCES += tools/connect_opts.cc +consus_transaction_manager_LDADD = +consus_transaction_manager_LDADD += $(REPLICANT_LIBS) +consus_transaction_manager_LDADD += $(BUSYBEE_LIBS) +consus_transaction_manager_LDADD += $(E_LIBS) +consus_transaction_manager_LDADD += $(PO6_LIBS) +consus_transaction_manager_LDADD += $(GLOG_LIBS) +consus_transaction_manager_LDADD += $(POPT_LIBS) + +EXTRA_DIST += man/consus-transaction-manager.1.md +EXTRA_DIST += man/consus-transaction-manager.1.h2m +man/consus-transaction-manager.1: man/consus-transaction-manager.1.h2m txman/main.cc | consus-transaction-manager${EXEEXT} + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-transaction-manager$(EXEEXT) + +################################################################################ +###################################### KVS ##################################### +################################################################################ + +consusexec_PROGRAMS += consus-key-value-store +dist_man_MANS += man/consus-key-value-store.1 + +noinst_HEADERS += kvs/configuration.h +noinst_HEADERS += kvs/daemon.h +noinst_HEADERS += kvs/mapper.h + +consus_key_value_store_SOURCES = +consus_key_value_store_SOURCES += common/consus.cc +consus_key_value_store_SOURCES += common/coordinator_link.cc +consus_key_value_store_SOURCES += common/ids.cc +consus_key_value_store_SOURCES += common/kvs.cc +consus_key_value_store_SOURCES += common/kvs_configuration.cc +consus_key_value_store_SOURCES += common/kvs_state.cc +consus_key_value_store_SOURCES += common/network_msgtype.cc +consus_key_value_store_SOURCES += common/transaction_id.cc +consus_key_value_store_SOURCES += common/transaction_group.cc +consus_key_value_store_SOURCES += kvs/configuration.cc +consus_key_value_store_SOURCES += kvs/daemon.cc +consus_key_value_store_SOURCES += kvs/main.cc +consus_key_value_store_SOURCES += kvs/mapper.cc +consus_key_value_store_SOURCES += tools/connect_opts.cc +consus_key_value_store_LDADD = +consus_key_value_store_LDADD += -lleveldb +consus_key_value_store_LDADD += $(REPLICANT_LIBS) +consus_key_value_store_LDADD += $(BUSYBEE_LIBS) +consus_key_value_store_LDADD += $(PO6_LIBS) +consus_key_value_store_LDADD += $(GLOG_LIBS) +consus_key_value_store_LDADD += $(POPT_LIBS) + +EXTRA_DIST += man/consus-key-value-store.1.md +EXTRA_DIST += man/consus-key-value-store.1.h2m +man/consus-key-value-store.1: man/consus-key-value-store.1.h2m kvs/main.cc | consus-key-value-store${EXEEXT} + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-key-value-store$(EXEEXT) + +################################################################################ +################################## Coordinator ################################# +################################################################################ + +consusexec_LTLIBRARIES += libconsus-coordinator.la +consusexec_PROGRAMS += consus-coordinator +dist_man_MANS += man/consus-coordinator.1 + +noinst_HEADERS += coordinator/coordinator.h +noinst_HEADERS += coordinator/transitions.h +noinst_HEADERS += coordinator/util.h + +libconsus_coordinator_la_SOURCES = +libconsus_coordinator_la_SOURCES += common/data_center.cc +libconsus_coordinator_la_SOURCES += common/ids.cc +libconsus_coordinator_la_SOURCES += common/kvs.cc +libconsus_coordinator_la_SOURCES += common/kvs_state.cc +libconsus_coordinator_la_SOURCES += common/network_msgtype.cc +libconsus_coordinator_la_SOURCES += common/paxos_group.cc +libconsus_coordinator_la_SOURCES += common/txman.cc +libconsus_coordinator_la_SOURCES += common/txman_state.cc +libconsus_coordinator_la_SOURCES += coordinator/coordinator.cc +libconsus_coordinator_la_SOURCES += coordinator/symtable.c +libconsus_coordinator_la_SOURCES += coordinator/transitions.cc +libconsus_coordinator_la_CXXFLAGS = $(AM_CXXFLAGS) $(CXXFLAGS) +libconsus_coordinator_la_LIBADD = +libconsus_coordinator_la_LIBADD += $(E_LIBS) + +consus_coordinator_SOURCES = tools/coordinator.cc +consus_coordinator_CPPFLAGS = -DCONSUS_EXEC_DIR=\""$(consusexecdir)\"" $(AM_CPPFLAGS) $(CPPFLAGS) +consus_coordinator_LDADD = +consus_coordinator_LDADD += $(BUSYBEE_LIBS) +consus_coordinator_LDADD += $(E_LIBS) +consus_coordinator_LDADD += $(POPT_LIBS) + +EXTRA_DIST += man/consus-coordinator.1.md +EXTRA_DIST += man/consus-coordinator.1.h2m +man/consus-coordinator.1: man/consus-coordinator.1.h2m | consus-coordinator${EXEEXT} + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-coordinator$(EXEEXT) + +################################################################################ +#################################### Client #################################### +################################################################################ + +include_HEADERS += include/consus.h +include_HEADERS += include/consus-admin.h + +lib_LTLIBRARIES += libconsus.la + +noinst_HEADERS += client/client.h +noinst_HEADERS += client/configuration.h +noinst_HEADERS += client/consus-internal.h +noinst_HEADERS += client/mapper.h +noinst_HEADERS += client/pending_begin_transaction.h +noinst_HEADERS += client/pending.h +noinst_HEADERS += client/pending_string.h +noinst_HEADERS += client/pending_transaction_abort.h +noinst_HEADERS += client/pending_transaction_commit.h +noinst_HEADERS += client/pending_transaction_read.h +noinst_HEADERS += client/pending_transaction_write.h +noinst_HEADERS += client/server_selector.h +noinst_HEADERS += client/transaction.h + +libconsus_la_SOURCES = +libconsus_la_SOURCES += common/client_configuration.cc +libconsus_la_SOURCES += common/consus.cc +libconsus_la_SOURCES += common/data_center.cc +libconsus_la_SOURCES += common/ids.cc +libconsus_la_SOURCES += common/kvs.cc +libconsus_la_SOURCES += common/kvs_configuration.cc +libconsus_la_SOURCES += common/kvs_state.cc +libconsus_la_SOURCES += common/network_msgtype.cc +libconsus_la_SOURCES += common/paxos_group.cc +libconsus_la_SOURCES += common/transaction_id.cc +libconsus_la_SOURCES += common/txman.cc +libconsus_la_SOURCES += common/txman_configuration.cc +libconsus_la_SOURCES += common/txman_state.cc +libconsus_la_SOURCES += client/c.cc +libconsus_la_SOURCES += client/client.cc +libconsus_la_SOURCES += client/configuration.cc +libconsus_la_SOURCES += client/mapper.cc +libconsus_la_SOURCES += client/pending_begin_transaction.cc +libconsus_la_SOURCES += client/pending.cc +libconsus_la_SOURCES += client/pending_string.cc +libconsus_la_SOURCES += client/pending_transaction_abort.cc +libconsus_la_SOURCES += client/pending_transaction_commit.cc +libconsus_la_SOURCES += client/pending_transaction_read.cc +libconsus_la_SOURCES += client/pending_transaction_write.cc +libconsus_la_SOURCES += client/server_selector.cc +libconsus_la_SOURCES += client/transaction.cc +libconsus_la_CXXFLAGS = $(AM_CXXFLAGS) $(CXXFLAGS) +libconsus_la_LIBADD = +libconsus_la_LIBADD += $(REPLICANT_LIBS) +libconsus_la_LIBADD += $(BUSYBEE_LIBS) +libconsus_la_LIBADD += $(TREADSTONE_LIBS) +libconsus_la_LIBADD += $(E_LIBS) +libconsus_la_LIBADD += $(PO6_LIBS) + +################################################################################ +################################### Bindings ################################### +################################################################################ + +####################################### C ###################################### + +noinst_PROGRAMS += bindings/c/testcompile + +bindings_c_testcompile_SOURCES = bindings/c/testcompile.c +bindings_c_testcompile_LDADD = libconsus.la $(TREADSTONE_LIBS) + +#################################### Python #################################### + +pyexec_LTLIBRARIES += bindings/python/consus.la + +CLEANFILES += bindings/python/consus.so + +EXTRA_DIST += bindings/python/consus.pyx +bindings_python_consus_la_SOURCES = bindings/python/consus.c +bindings_python_consus_la_CPPFLAGS = +bindings_python_consus_la_CPPFLAGS += $(PYTHON_CPPFLAGS) +bindings_python_consus_la_CPPFLAGS += $(AM_CPPFLAGS) +bindings_python_consus_la_CPPFLAGS += $(CPPFLAGS) +bindings_python_consus_la_CFLAGS = +bindings_python_consus_la_CFLAGS += -fvisibility=default +bindings_python_consus_la_CFLAGS += $(CFLAGS) +bindings_python_consus_la_LIBADD = +bindings_python_consus_la_LIBADD += libconsus.la +bindings_python_consus_la_LIBADD += $(PYTHON_LDFLAGS) +bindings_python_consus_la_LDFLAGS = -module -avoid-version -export-symbols-regex $(PYTHON_SYMBOL)consus $(AM_LDFLAGS) $(LDFLAGS) +bindings/python/consus.c: bindings/python/consus.pyx + $(pyx_verbose)cython bindings/python/consus.pyx + +################################################################################ +##################################### Tests #################################### +################################################################################ + +EXTRA_DIST += test/env.sh +EXTRA_DIST += consus.supp + +EXTRA_DIST += test/unit/00.client.py +EXTRA_DIST += test/unit/00.nop-abort.py +EXTRA_DIST += test/unit/00.nop-commit.py +EXTRA_DIST += test/unit/10.single-get.py +EXTRA_DIST += test/unit/10.single-put.py +EXTRA_DIST += test/unit/11.put-get-separate-commits.py + +gremlins = +### begin automatically generated gremlins +gremlins += test/1-node-1-dc-cluster.gremlin +gremlins += test/2-node-1-dc-cluster.gremlin +gremlins += test/3-node-1-dc-cluster.gremlin +gremlins += test/4-node-1-dc-cluster.gremlin +gremlins += test/5-node-1-dc-cluster.gremlin +gremlins += test/unit/00.client.1n.1dc.gremlin +gremlins += test/unit/00.client.2n.1dc.gremlin +gremlins += test/unit/00.client.3n.1dc.gremlin +gremlins += test/unit/00.client.4n.1dc.gremlin +gremlins += test/unit/00.client.5n.1dc.gremlin +gremlins += test/unit/00.nop-abort.1n.1dc.gremlin +gremlins += test/unit/00.nop-abort.2n.1dc.gremlin +gremlins += test/unit/00.nop-abort.3n.1dc.gremlin +gremlins += test/unit/00.nop-abort.4n.1dc.gremlin +gremlins += test/unit/00.nop-abort.5n.1dc.gremlin +gremlins += test/unit/00.nop-commit.1n.1dc.gremlin +gremlins += test/unit/00.nop-commit.2n.1dc.gremlin +gremlins += test/unit/00.nop-commit.3n.1dc.gremlin +gremlins += test/unit/00.nop-commit.4n.1dc.gremlin +gremlins += test/unit/00.nop-commit.5n.1dc.gremlin +gremlins += test/unit/10.single-get.1n.1dc.gremlin +gremlins += test/unit/10.single-get.2n.1dc.gremlin +gremlins += test/unit/10.single-get.3n.1dc.gremlin +gremlins += test/unit/10.single-get.4n.1dc.gremlin +gremlins += test/unit/10.single-get.5n.1dc.gremlin +gremlins += test/unit/10.single-put.1n.1dc.gremlin +gremlins += test/unit/10.single-put.2n.1dc.gremlin +gremlins += test/unit/10.single-put.3n.1dc.gremlin +gremlins += test/unit/10.single-put.4n.1dc.gremlin +gremlins += test/unit/10.single-put.5n.1dc.gremlin +gremlins += test/unit/11.put-get-separate-commits.1n.1dc.gremlin +gremlins += test/unit/11.put-get-separate-commits.2n.1dc.gremlin +gremlins += test/unit/11.put-get-separate-commits.3n.1dc.gremlin +gremlins += test/unit/11.put-get-separate-commits.4n.1dc.gremlin +gremlins += test/unit/11.put-get-separate-commits.5n.1dc.gremlin +### end automatically generated gremlins +EXTRA_DIST += ${gremlins} +TESTS += ${gremlins} + +check_PROGRAMS += test/paxos/generalized +TESTS += test/paxos/generalized +test_paxos_generalized_SOURCES = test/paxos/generalized.cc txman/generalized_paxos.cc common/ids.cc ${th_sources} +test_paxos_generalized_LDADD = ${E_LIBS} + +check_PROGRAMS += test/paxos/generalized-brute-force +test_paxos_generalized_brute_force_SOURCES = test/paxos/generalized-brute-force.cc txman/generalized_paxos.cc common/ids.cc +test_paxos_generalized_brute_force_LDADD = ${E_LIBS} $(POPT_LIBS) + +################################################################################ +##################################### Tools #################################### +################################################################################ + +noinst_HEADERS += tools/common.h +noinst_HEADERS += tools/connect_opts.h +noinst_HEADERS += tools/locate-coordinator-lib.h + +bin_PROGRAMS += consus +bin_PROGRAMS += consus-debug +consusexec_PROGRAMS += consus-create-data-center +consusexec_PROGRAMS += consus-set-default-data-center +consusexec_PROGRAMS += consus-availability-check +consusexec_PROGRAMS += consus-debug-client-configuration +consusexec_PROGRAMS += consus-debug-txman-configuration +consusexec_PROGRAMS += consus-debug-kvs-configuration +dist_man_MANS += man/consus.1 +dist_man_MANS += man/consus-create-data-center.1 +dist_man_MANS += man/consus-set-default-data-center.1 +dist_man_MANS += man/consus-availability-check.1 +dist_man_MANS += man/consus-debug.1 +dist_man_MANS += man/consus-debug-client-configuration.1 +dist_man_MANS += man/consus-debug-txman-configuration.1 +dist_man_MANS += man/consus-debug-kvs-configuration.1 + +# consus +EXTRA_DIST += man/consus.1.md +EXTRA_DIST += man/consus.1.h2m +consus_SOURCES = consus.cc +consus_LDADD = +consus_LDADD += $(PO6_LIBS) +consus_LDADD += $(POPT_LIBS) +man/consus.1: man/consus.1.h2m consus.cc | consus$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus$(EXEEXT) + +# consus-create-data-center +EXTRA_DIST += man/consus-create-data-center.1.md +EXTRA_DIST += man/consus-create-data-center.1.h2m +consus_create_data_center_SOURCES = tools/create-data-center.cc tools/common.cc tools/connect_opts.cc +consus_create_data_center_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-create-data-center.1: man/consus-create-data-center.1.h2m tools/create-data-center.cc | consus-create-data-center$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-create-data-center$(EXEEXT) + +# consus-set-default-data-center +EXTRA_DIST += man/consus-set-default-data-center.1.md +EXTRA_DIST += man/consus-set-default-data-center.1.h2m +consus_set_default_data_center_SOURCES = tools/set-default-data-center.cc tools/common.cc tools/connect_opts.cc +consus_set_default_data_center_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-set-default-data-center.1: man/consus-set-default-data-center.1.h2m tools/set-default-data-center.cc | consus-set-default-data-center$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-set-default-data-center$(EXEEXT) + +# consus-availability-check +EXTRA_DIST += man/consus-availability-check.1.md +EXTRA_DIST += man/consus-availability-check.1.h2m +consus_availability_check_SOURCES = tools/availability-check.cc tools/common.cc tools/connect_opts.cc +consus_availability_check_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-availability-check.1: man/consus-availability-check.1.h2m tools/availability-check.cc | consus-availability-check$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-availability-check$(EXEEXT) + +# consus-debug +EXTRA_DIST += man/consus-debug.1.md +EXTRA_DIST += man/consus-debug.1.h2m +consus_debug_SOURCES = consus-debug.cc +consus_debug_LDADD = +consus_debug_LDADD += $(PO6_LIBS) +consus_debug_LDADD += $(POPT_LIBS) +man/consus-debug.1: man/consus-debug.1.h2m consus-debug.cc | consus-debug$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-debug$(EXEEXT) + +# consus-debug-client-configuration +EXTRA_DIST += man/consus-debug-client-configuration.1.md +EXTRA_DIST += man/consus-debug-client-configuration.1.h2m +consus_debug_client_configuration_SOURCES = tools/debug-client-configuration.cc tools/common.cc tools/connect_opts.cc +consus_debug_client_configuration_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-debug-client-configuration.1: man/consus-debug-client-configuration.1.h2m tools/debug-client-configuration.cc | consus-debug-client-configuration$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-debug-client-configuration$(EXEEXT) + +# consus-debug-txman-configuration +EXTRA_DIST += man/consus-debug-txman-configuration.1.md +EXTRA_DIST += man/consus-debug-txman-configuration.1.h2m +consus_debug_txman_configuration_SOURCES = tools/debug-txman-configuration.cc tools/common.cc tools/connect_opts.cc +consus_debug_txman_configuration_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-debug-txman-configuration.1: man/consus-debug-txman-configuration.1.h2m tools/debug-txman-configuration.cc | consus-debug-txman-configuration$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-debug-txman-configuration$(EXEEXT) + +# consus-debug-kvs-configuration +EXTRA_DIST += man/consus-debug-kvs-configuration.1.md +EXTRA_DIST += man/consus-debug-kvs-configuration.1.h2m +consus_debug_kvs_configuration_SOURCES = tools/debug-kvs-configuration.cc tools/common.cc tools/connect_opts.cc +consus_debug_kvs_configuration_LDADD = libconsus.la $(TREADSTONE_LIBS) $(PO6_LIBS) $(POPT_LIBS) +man/consus-debug-kvs-configuration.1: man/consus-debug-kvs-configuration.1.h2m tools/debug-kvs-configuration.cc | consus-debug-kvs-configuration$(EXEEXT) + $(help2man_verbose)help2man $(HELP2MAN_FLAGS) --section 1 --output $@ --include $< ${abs_top_builddir}/consus-debug-kvs-configuration$(EXEEXT) + +################################################################################ +################################# Documentation ################################ +################################################################################ + +EXTRA_DIST += $(wildcard doc/*.tex) +EXTRA_DIST += $(wildcard doc/*.xtx) +EXTRA_DIST += $(wildcard doc/*/*.tex) +EXTRA_DIST += $(wildcard doc/*/*/*.tex) +EXTRA_DIST += $(wildcard doc/*/*/*/*.tex) +EXTRA_DIST += $(wildcard doc/*/*/*/*/*.tex) +EXTRA_DIST += doc/Makefile +EXTRA_DIST += doc/latex.py + +################################################################################ +################################## Maintenance ################################# +################################################################################ + +EXTRA_DIST += maint/generate-unit-test-gremlins +EXTRA_DIST += maint/valgrind-gremlins + +# run valgrind-gremlins after all other gremlin generators +# XXX maint/valgrind-gremlins +maintainer-generate: + maint/generate-unit-test-gremlins diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/TODO b/TODO new file mode 100644 index 0000000..e6c3923 --- /dev/null +++ b/TODO @@ -0,0 +1,15 @@ +List of major "TODO" items left: + - Transaction manager replay log on restart. Necessary for txman to come back + after a crash. + - Locking in the key-value store. Necessary to actually uphold serializability. + This will require implementing SCAN first, otherwise it will need to be + rewritten. + - Client-DC affiliation (currently just picks one group at random per + transaction) + - Garbage collection of in-memory structures + - Garbage collection of log + - SCAN(k, n) operation. Equivalent to selecting the next n keys >= k. + - Testing + - Optimization + - Durable log throughput/latency + - Remove early phases of paxos with implicit leader diff --git a/bindings/c/testcompile.c b/bindings/c/testcompile.c new file mode 100644 index 0000000..d0493e8 --- /dev/null +++ b/bindings/c/testcompile.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +#include + +#ifdef __cplusplus +#error please compile for c, not c++ +#endif + +int +main(int argc, const char* argv[]) +{ + argc = argc; + argv = argv; + return 0; +} diff --git a/bindings/python/consus.pyx b/bindings/python/consus.pyx new file mode 100644 index 0000000..942cef3 --- /dev/null +++ b/bindings/python/consus.pyx @@ -0,0 +1,175 @@ +# Copyright (c) 2015, Robert Escriva +# All rights reserved. + + +import datetime +import calendar +import json +import time + + +cdef extern from "stdint.h": + + ctypedef short int int16_t + ctypedef unsigned short int uint16_t + ctypedef int int32_t + ctypedef unsigned int uint32_t + ctypedef long int int64_t + ctypedef unsigned long int uint64_t + ctypedef long unsigned int size_t + + +cdef extern from "stdlib.h": + + void *malloc(size_t size); + void free(void *ptr); + + +cdef extern from "consus.h": + + cdef enum consus_returncode: + CONSUS_SUCCESS = 6656 + CONSUS_NOT_FOUND = 6657 + CONSUS_UNKNOWN_TABLE = 6720 + CONSUS_NONE_PENDING = 6721 + CONSUS_TIMEOUT = 6784 + CONSUS_INTERRUPTED = 6785 + CONSUS_SEE_ERRNO = 6786 + CONSUS_INTERNAL = 6910 + CONSUS_GARBAGE = 6911 + + cdef struct consus_client + cdef struct consus_transaction + consus_client* consus_create(const char* coordinator, uint16_t port); + consus_client* consus_create_conn_str(const char* conn_str); + void consus_destroy(consus_client* client); + int64_t consus_loop(consus_client* client, int timeout, + consus_returncode* status); + int64_t consus_wait(consus_client* client, int64_t x, int timeout, + consus_returncode* status); + int consus_poll_fd(consus_client* client); + int consus_block(consus_client* client, int timeout); + const char* consus_error_message(consus_client* client); + const char* consus_error_location(consus_client* client); + const char* consus_returncode_to_string(consus_returncode); + int64_t consus_begin_transaction(consus_client* client, consus_returncode* status, consus_transaction** xact); + int64_t consus_commit_transaction(consus_transaction* xact, consus_returncode* status); + int64_t consus_abort_transaction(consus_transaction* xact, consus_returncode* status); + int64_t consus_restart_transaction(consus_transaction* xact, consus_returncode* status); + void consus_destroy_transaction(consus_transaction* xact); + + int64_t consus_get(consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + consus_returncode* status, + const char** value, size_t* value_sz); + int64_t consus_put(consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + const char* value, size_t value_sz, + consus_returncode* status); + + +cdef class Client: + cdef consus_client* client + + def __cinit__(self, *args): + conn_str = '127.0.0.1:1982' + if len(args) == 2 and not isinstance(args[1], int): + raise ValueError('Expected an integer port number') + elif len(args) == 2: + conn_str = '%s:%d' % tuple(args) + elif len(args) == 0: + pass + elif len(args) == 1: + conn_str = args[0] + elif len(args) != 1: + raise TypeError('Client() at most 2 arguments (%d given)' % len(args)) + if not isinstance(conn_str, bytes): + conn_str = conn_str.encode('ascii') + self.client = consus_create_conn_str(conn_str) + + def __dealloc__(self): + if self.client: + consus_destroy(self.client) + + def begin_transaction(self): + return Transaction(self) + + +cdef class Transaction: + cdef Client client + cdef consus_transaction* xact + + def __cinit__(self, Client client): + cdef consus_returncode status + self.client = client + req = consus_begin_transaction(self.client.client, &status, &self.xact) + self.finish(req, &status) + assert self.xact + + def __dealloc__(self): + if self.xact: + consus_destroy_transaction(self.xact); + + def get(self, str table, key): + cdef bytes tmp = table.encode('ascii') + cdef bytes jkey = json.dumps(key).encode('utf8') + cdef consus_returncode status + cdef const char* t = tmp + cdef const char* k = jkey + cdef size_t k_sz = len(jkey) + cdef char* value + cdef size_t value_sz + req = consus_get(self.xact, t, k, k_sz, &status, &value, &value_sz) + self.finish(req, &status) + if status == CONSUS_SUCCESS: + x = json.loads(value[:value_sz].decode('utf8')) + free(value) + return x + else: + return None + + def put(self, str table, key, value): + cdef bytes tmp = table.encode('ascii') + cdef bytes jkey = json.dumps(key).encode('utf8') + cdef bytes jvalue = json.dumps(value).encode('utf8') + cdef consus_returncode status + cdef const char* t = tmp + cdef const char* k = jkey + cdef size_t k_sz = len(jkey) + cdef const char* v = jvalue + cdef size_t v_sz = len(jvalue) + req = consus_put(self.xact, t, k, k_sz, v, v_sz, &status) + self.finish(req, &status) + return True + + def commit(self): + cdef consus_returncode status + req = consus_commit_transaction(self.xact, &status) + self.finish(req, &status) + + def abort(self): + cdef consus_returncode status + req = consus_abort_transaction(self.xact, &status) + self.finish(req, &status) + + cdef finish(self, int64_t req, consus_returncode* rstatus): + cdef consus_returncode lstatus + if req < 0: + print consus_error_message(self.client.client).decode('ascii', 'ignore'), '@', \ + consus_error_location(self.client.client).decode('ascii', 'ignore'), ' ', \ + consus_returncode_to_string(rstatus[0]).decode('ascii', 'ignore') + assert False # XXX + lid = consus_wait(self.client.client, req, -1, &lstatus); + if lid < 0: + print consus_error_message(self.client.client).decode('ascii', 'ignore'), '@', \ + consus_error_location(self.client.client).decode('ascii', 'ignore'), ' ', \ + consus_returncode_to_string(lstatus).decode('ascii', 'ignore') + assert False # XXX + assert req == lid + if rstatus[0] != CONSUS_SUCCESS and rstatus[0] != CONSUS_NOT_FOUND: + print consus_error_message(self.client.client).decode('ascii', 'ignore'), '@', \ + consus_error_location(self.client.client).decode('ascii', 'ignore'), ' ', \ + consus_returncode_to_string(rstatus[0]).decode('ascii', 'ignore') + assert False # XXX diff --git a/client/c.cc b/client/c.cc new file mode 100644 index 0000000..8a12d57 --- /dev/null +++ b/client/c.cc @@ -0,0 +1,312 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#define __STDC_LIMIT_MACROS + +// POSIX +#include +#include + +// C++ +#include + +// e +#include + +// consus +#include +#include "visibility.h" +#include "common/macros.h" +#include "client/client.h" +#include "client/transaction.h" + +#define FAKE_STATUS consus_returncode _status; consus_returncode* status = &_status + +#define SIGNAL_PROTECT_ERR(X) \ + sigset_t old_sigs; \ + sigset_t all_sigs; \ + sigfillset(&all_sigs); \ + if (pthread_sigmask(SIG_BLOCK, &all_sigs, &old_sigs) < 0) \ + { \ + *status = CONSUS_INTERNAL; \ + return (X); \ + } \ + e::guard g = e::makeguard(pthread_sigmask, SIG_SETMASK, (sigset_t*)&old_sigs, (sigset_t*)NULL) + +#define SIGNAL_PROTECT SIGNAL_PROTECT_ERR(-1); +inline void return_void() {} +#define SIGNAL_PROTECT_VOID SIGNAL_PROTECT_ERR(return_void()); + +#define C_WRAP_EXCEPT(X) \ + consus::client* cl = reinterpret_cast(client); \ + SIGNAL_PROTECT; \ + try \ + { \ + X \ + } \ + catch (std::bad_alloc& ba) \ + { \ + errno = ENOMEM; \ + *status = CONSUS_SEE_ERRNO; \ + cl->set_error_message("out of memory"); \ + return -1; \ + } \ + catch (...) \ + { \ + *status = CONSUS_INTERNAL; \ + cl->set_error_message("internal state corrupted"); \ + return -1; \ + } + +#define C_WRAP_EXCEPT_XACT(X) \ + consus::transaction* tx = reinterpret_cast(xact); \ + consus::client* cl = tx->parent(); \ + SIGNAL_PROTECT; \ + try \ + { \ + X \ + } \ + catch (std::bad_alloc& ba) \ + { \ + errno = ENOMEM; \ + *status = CONSUS_SEE_ERRNO; \ + cl->set_error_message("out of memory"); \ + return -1; \ + } \ + catch (...) \ + { \ + *status = CONSUS_INTERNAL; \ + cl->set_error_message("internal state corrupted"); \ + return -1; \ + } + +extern "C" +{ + +CONSUS_API consus_client* +consus_create(const char* host, uint16_t port) +{ + FAKE_STATUS; + SIGNAL_PROTECT_ERR(NULL); + + try + { + return reinterpret_cast(new consus::client(host, port)); + } + catch (std::bad_alloc& ba) + { + errno = ENOMEM; + return NULL; + } + catch (...) + { + errno = EINVAL; + return NULL; + } +} + +CONSUS_API consus_client* +consus_create_conn_str(const char* conn_str) +{ + FAKE_STATUS; + SIGNAL_PROTECT_ERR(NULL); + + try + { + return reinterpret_cast(new consus::client(conn_str)); + } + catch (std::bad_alloc& ba) + { + errno = ENOMEM; + return NULL; + } + catch (...) + { + errno = EINVAL; + return NULL; + } +} + +CONSUS_API void +consus_destroy(consus_client* client) +{ + delete reinterpret_cast(client); +} + +CONSUS_API int64_t +consus_loop(consus_client* client, int timeout, consus_returncode* status) +{ + C_WRAP_EXCEPT( + return cl->loop(timeout, status); + ); +} + +CONSUS_API int64_t +consus_wait(consus_client* client, int64_t id, int timeout, consus_returncode* status) +{ + C_WRAP_EXCEPT( + return cl->wait(id, timeout, status); + ); +} + +CONSUS_API const char* +consus_error_message(consus_client* _cl) +{ + FAKE_STATUS; + SIGNAL_PROTECT_ERR(NULL); + consus::client* cl = reinterpret_cast(_cl); + return cl->error_message(); +} + +CONSUS_API const char* +consus_error_location(consus_client* _cl) +{ + FAKE_STATUS; + SIGNAL_PROTECT_ERR(NULL); + consus::client* cl = reinterpret_cast(_cl); + return cl->error_location(); +} + +CONSUS_API const char* +consus_returncode_to_string(consus_returncode stat) +{ + FAKE_STATUS; + SIGNAL_PROTECT_ERR(NULL); + switch (stat) + { + CSTRINGIFY(CONSUS_SUCCESS); + CSTRINGIFY(CONSUS_NOT_FOUND); + CSTRINGIFY(CONSUS_ABORTED); + CSTRINGIFY(CONSUS_UNKNOWN_TABLE); + CSTRINGIFY(CONSUS_NONE_PENDING); + CSTRINGIFY(CONSUS_INVALID); + CSTRINGIFY(CONSUS_TIMEOUT); + CSTRINGIFY(CONSUS_INTERRUPTED); + CSTRINGIFY(CONSUS_SEE_ERRNO); + CSTRINGIFY(CONSUS_COORD_FAIL); + CSTRINGIFY(CONSUS_UNAVAILABLE); + CSTRINGIFY(CONSUS_SERVER_ERROR); + CSTRINGIFY(CONSUS_INTERNAL); + CSTRINGIFY(CONSUS_GARBAGE); + default: + return "unknown consus_returncode"; + } +} + +CONSUS_API int64_t +consus_begin_transaction(consus_client* client, + consus_returncode* status, + consus_transaction** xact) +{ + C_WRAP_EXCEPT( + return cl->begin_transaction(status, xact); + ); +} + +CONSUS_API void +consus_destroy_transaction(consus_transaction* xact) +{ + delete reinterpret_cast(xact); +} + +CONSUS_API int64_t +consus_get(consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + consus_returncode* status, + char** value, size_t* value_sz) +{ + C_WRAP_EXCEPT_XACT( + return tx->get(table, key, key_sz, status, value, value_sz); + ); +} + +CONSUS_API int64_t +consus_put(consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + const char* value, size_t value_sz, + consus_returncode* status) +{ + C_WRAP_EXCEPT_XACT( + return tx->put(table, key, key_sz, value, value_sz, status); + ); +} + +CONSUS_API int64_t +consus_commit_transaction(consus_transaction* xact, + consus_returncode* status) +{ + C_WRAP_EXCEPT_XACT( + return tx->commit(status); + ); +} + +CONSUS_API int64_t +consus_abort_transaction(consus_transaction* xact, + consus_returncode* status) +{ + C_WRAP_EXCEPT_XACT( + return tx->abort(status); + ); +} + +CONSUS_API int +consus_debug_client_configuration(consus_client* client, + consus_returncode* status, + const char** str) +{ + C_WRAP_EXCEPT( + return cl->debug_client_configuration(status, str); + ); +} + +CONSUS_API int +consus_debug_txman_configuration(consus_client* client, + consus_returncode* status, + const char** str) +{ + C_WRAP_EXCEPT( + return cl->debug_txman_configuration(status, str); + ); +} + +CONSUS_API int +consus_debug_kvs_configuration(consus_client* client, + consus_returncode* status, + const char** str) +{ + C_WRAP_EXCEPT( + return cl->debug_kvs_configuration(status, str); + ); +} + +CONSUS_API int +consus_admin_create_data_center(consus_client* client, const char* name, + consus_returncode* status) +{ + C_WRAP_EXCEPT( + return cl->create_data_center(name, status); + ); +} + +CONSUS_API int +consus_admin_set_default_data_center(consus_client* client, const char* name, + consus_returncode* status) +{ + C_WRAP_EXCEPT( + return cl->set_default_data_center(name, status); + ); +} + +CONSUS_API int +consus_admin_availability_check(consus_client* client, + consus_availability_requirements* reqs, + int timeout, consus_returncode* status) +{ + C_WRAP_EXCEPT( + return cl->availability_check(reqs, timeout, status); + ); +} + +} // extern "C" diff --git a/client/client.cc b/client/client.cc new file mode 100644 index 0000000..f09a34a --- /dev/null +++ b/client/client.cc @@ -0,0 +1,756 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// BusyBee +#include + +// consus +#include "common/client_configuration.h" +#include "common/kvs_configuration.h" +#include "common/macros.h" +#include "common/paxos_group.h" +#include "common/txman_configuration.h" +#include "client/client.h" +#include "client/pending.h" +#include "client/pending_begin_transaction.h" +#include "client/pending_string.h" + +using consus::client; + +#define ERROR(CODE) \ + *status = CONSUS_ ## CODE; \ + m_last_error.set_loc(__FILE__, __LINE__); \ + m_last_error.set_msg() + +#define _BUSYBEE_ERROR(BBRC) \ + case BUSYBEE_ ## BBRC: \ + ERROR(INTERNAL) << "internal error: BusyBee unexpectedly returned " XSTR(BBRC) << ": please file a bug" + +#define BUSYBEE_ERROR_CASE(BBRC) \ + _BUSYBEE_ERROR(BBRC); \ + return -1; + +#define BUSYBEE_ERROR_CASE_FALSE(BBRC) \ + _BUSYBEE_ERROR(BBRC); \ + return false; + +client :: client(const char* host, uint16_t port) + : m_coord(replicant_client_create(host, port)) + , m_config() + , m_config_id(-1) + , m_config_status(REPLICANT_SUCCESS) + , m_config_state(0) + , m_config_data(NULL) + , m_config_data_sz(0) + , m_busybee_mapper(&m_config) + , m_busybee(&m_busybee_mapper, 0) + , m_next_client_id(1) + , m_next_server_nonce(1) + , m_pending() + , m_returnable() + , m_returned() + , m_flagfd() + , m_last_error() +{ + if (!m_coord) + { + throw std::bad_alloc(); + } + + busybee_returncode rc = m_busybee.set_external_fd(replicant_client_poll_fd(m_coord)); + assert(rc == BUSYBEE_SUCCESS); +} + +client :: client(const char* conn_str) + : m_coord(replicant_client_create_conn_str(conn_str)) + , m_config() + , m_config_id(-1) + , m_config_status(REPLICANT_SUCCESS) + , m_config_state(0) + , m_config_data(NULL) + , m_config_data_sz(0) + , m_busybee_mapper(&m_config) + , m_busybee(&m_busybee_mapper, 0) + , m_next_client_id(1) + , m_next_server_nonce(1) + , m_pending() + , m_returnable() + , m_returned() + , m_flagfd() + , m_last_error() +{ + if (!m_coord) + { + throw std::bad_alloc(); + } + + busybee_returncode rc = m_busybee.set_external_fd(replicant_client_poll_fd(m_coord)); + assert(rc == BUSYBEE_SUCCESS); +} + +client :: ~client() throw () +{ + replicant_client_destroy(m_coord); +} + +int64_t +client :: loop(int timeout, consus_returncode* status) +{ + *status = CONSUS_SUCCESS; + m_last_error = e::error(); + + while (!m_returnable.empty() || !m_pending.empty()) + { + if (!m_returnable.empty()) + { + m_returned = m_returnable.front(); + m_returnable.pop_front(); + m_last_error = m_returned->error(); + return m_returned->client_id(); + } + + if (inner_loop(timeout, status) < 0) + { + return -1; + } + } + + return post_loop(status); +} + +int64_t +client :: wait(int64_t id, int timeout, consus_returncode* status) +{ + *status = CONSUS_SUCCESS; + m_last_error = e::error(); + + while (true) + { + for (std::list >::iterator it = m_returnable.begin(); + it != m_returnable.end(); ++it) + { + pending* p = it->get(); + + if (p->client_id() == id) + { + m_returned = *it; + m_returnable.erase(it); + m_last_error = m_returned->error(); + return m_returned->client_id(); + } + } + + if (inner_loop(timeout, status) < 0) + { + return -1; + } + } + + return post_loop(status); +} + +int64_t +client :: begin_transaction(consus_returncode* status, + consus_transaction** xact) +{ + if (!maintain_coord_connection(status)) + { + return -1; + } + + int64_t client_id = generate_new_client_id(); + pending* p = new pending_begin_transaction(client_id, status, xact); + p->kickstart_state_machine(this); + return client_id; +} + +int +client :: create_data_center(const char* name, consus_returncode* status) +{ + std::string tmp; + e::packer(&tmp) << e::slice(name); + replicant_returncode rc; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_call(m_coord, "consus", "data_center_create", + tmp.data(), tmp.size(), REPLICANT_CALL_ROBUST, + &rc, &data, &data_sz); + + if (!replicant_finish(id, &rc, status)) + { + return -1; + } + + // XXX +#if 0 + coordinator_returncode crc; + e::unpacker up(data, data_sz); + up = up >> e::unpack_uint16(crc); + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: invalid return value"; + return -1; + } + + switch (crc) + { + case COORDINATOR_SUCCESS: + *status = REPLICANT_SUCCESS; + return 0; + } + + abort(); +#endif + return 0; +} + +int +client :: set_default_data_center(const char* name, consus_returncode* status) +{ + std::string tmp; + e::packer(&tmp) << e::slice(name); + replicant_returncode rc; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_call(m_coord, "consus", "data_center_default", + tmp.data(), tmp.size(), REPLICANT_CALL_ROBUST, + &rc, &data, &data_sz); + + if (!replicant_finish(id, &rc, status)) + { + return -1; + } + + // XXX +#if 0 + coordinator_returncode crc; + e::unpacker up(data, data_sz); + up = up >> e::unpack_uint16(crc); + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: invalid return value"; + return -1; + } + + switch (crc) + { + case COORDINATOR_SUCCESS: + *status = REPLICANT_SUCCESS; + return 0; + } + + abort(); +#endif + return 0; +} + +int +client :: availability_check(consus_availability_requirements* reqs, + int timeout, + consus_returncode* status) +{ + if (!maintain_coord_connection(status)) + { + return -1; + } + + const uint64_t start = po6::monotonic_time(); + uint64_t now = 0; + uint64_t version = 0; + + while (timeout < 0 || start + PO6_SECONDS * timeout >= (now = po6::monotonic_time())) + { + replicant_returncode rc = REPLICANT_GARBAGE; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_cond_wait(m_coord, "consus", "txmanconf", version, &rc, &data, &data_sz); + int to = -1; + + if (timeout > 0) + { + to = (start + PO6_SECONDS * timeout - now + PO6_MILLIS) / PO6_MILLIS; + to = std::min(to, int(PO6_MILLIS * 100)); + } + + if (!replicant_finish(id, to, &rc, status)) + { + replicant_client_kill(m_coord, id); + + if (rc == REPLICANT_TIMEOUT) + { + continue; + } + else + { + return -1; + } + } + + assert(data || data_sz == 0); + e::unpacker up(data, data_sz); + cluster_id cid; + version_id vid; + uint64_t flags; + std::vector dcs; + std::vector txmans; + std::vector txman_groups; + std::vector kvss; + up = txman_configuration(up, &cid, &vid, &flags, &dcs, &txmans, &txman_groups, &kvss); + + if (data) + { + free(data); + } + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: bad client configuration"; + return -1; + } + + version = vid.get(); + + unsigned txmans_avail = 0; + + for (size_t i = 0; i < txmans.size(); ++i) + { + if (txmans[i].state == txman_state::ONLINE) + { + ++txmans_avail; + } + } + + if (txmans_avail >= reqs->txmans && + txman_groups.size() >= reqs->txman_groups && + kvss.size() >= reqs->kvss) + { + *status = CONSUS_SUCCESS; + return 0; + } + } + + ERROR(TIMEOUT) << "operation timed out"; + return -1; +} + +bool +client :: replicant_finish(int64_t id, replicant_returncode* rc, consus_returncode* status) +{ + return replicant_finish(id, -1, rc, status); +} + +bool +client :: replicant_finish(int64_t id, int timeout, replicant_returncode* rc, consus_returncode* status) +{ + if (id < 0) + { + ERROR(COORD_FAIL) << "coordinator failure: " << replicant_client_error_message(m_coord); + return false; + } + + replicant_returncode lrc; + int64_t lid = replicant_client_wait(m_coord, id, timeout, &lrc); + + if (lid < 0) + { + *rc = lrc; + ERROR(COORD_FAIL) << "coordinator failure: " << replicant_client_error_message(m_coord); + return false; + } + + assert(id == lid); + + if (*rc != REPLICANT_SUCCESS) + { + ERROR(COORD_FAIL) << "coordinator failure: " << replicant_client_error_message(m_coord); + return false; + } + + return true; +} + +int +client :: debug_client_configuration(consus_returncode* status, const char** str) +{ + if (!maintain_coord_connection(status)) + { + return -1; + } + + replicant_returncode rc = REPLICANT_GARBAGE; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_cond_wait(m_coord, "consus", "clientconf", 0, &rc, &data, &data_sz); + + if (!replicant_finish(id, &rc, status) || (!data && data_sz != 0)) + { + return -1; + } + + e::unpacker up(data, data_sz); + cluster_id cid; + version_id vid; + uint64_t flags; + std::vector txmans; + up = client_configuration(up, &cid, &vid, &flags, &txmans); + free(data); + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: bad client configuration"; + return -1; + } + + std::ostringstream ostr; + ostr << cid << "\n" + << vid << "\n"; + + if (txmans.empty()) + { + ostr << "no transaction managers"; + } + else if (txmans.size() == 1) + { + ostr << "1 transaction manager:\n"; + } + else + { + ostr << txmans.size() << " transaction managers:\n"; + } + + for (unsigned i = 0; i < txmans.size(); ++i) + { + ostr << txmans[i] << "\n"; + } + + e::intrusive_ptr p = new pending_string(ostr.str()); + *str = p->string(); + m_returned = p.get(); + *status = CONSUS_SUCCESS; + m_last_error = e::error(); + return 0; +} + +int +client :: debug_txman_configuration(consus_returncode* status, const char** str) +{ + if (!maintain_coord_connection(status)) + { + return -1; + } + + replicant_returncode rc = REPLICANT_GARBAGE; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_cond_wait(m_coord, "consus", "txmanconf", 0, &rc, &data, &data_sz); + + if (!replicant_finish(id, &rc, status) || (!data && data_sz != 0)) + { + return -1; + } + + e::unpacker up(data, data_sz); + cluster_id cid; + version_id vid; + uint64_t flags; + std::vector dcs; + std::vector txmans; + std::vector txman_groups; + std::vector kvss; + up = txman_configuration(up, &cid, &vid, &flags, &dcs, &txmans, &txman_groups, &kvss); + free(data); + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: bad client configuration"; + return -1; + } + + std::string s = txman_configuration(cid, vid, flags, dcs, txmans, txman_groups, kvss); + e::intrusive_ptr p = new pending_string(s); + *str = p->string(); + m_returned = p.get(); + *status = CONSUS_SUCCESS; + m_last_error = e::error(); + return 0; +} + +int +client :: debug_kvs_configuration(consus_returncode* status, const char** str) +{ + if (!maintain_coord_connection(status)) + { + return -1; + } + + replicant_returncode rc = REPLICANT_GARBAGE; + char* data = NULL; + size_t data_sz = 0; + int64_t id = replicant_client_cond_wait(m_coord, "consus", "kvsconf", 0, &rc, &data, &data_sz); + + if (!replicant_finish(id, &rc, status) || (!data && data_sz != 0)) + { + return -1; + } + + e::unpacker up(data, data_sz); + cluster_id cid; + version_id vid; + uint64_t flags; + std::vector kvss; + up = kvs_configuration(up, &cid, &vid, &flags, &kvss); + free(data); + + if (up.error()) + { + ERROR(COORD_FAIL) << "coordinator failure: bad client configuration"; + return -1; + } + + std::string s = kvs_configuration(cid, vid, flags, kvss); + e::intrusive_ptr p = new pending_string(s); + *str = p->string(); + m_returned = p.get(); + *status = CONSUS_SUCCESS; + m_last_error = e::error(); + return 0; +} + +const char* +client :: error_message() +{ + return m_last_error.msg(); +} + +const char* +client :: error_location() +{ + return m_last_error.loc(); +} + +void +client :: set_error_message(const char* msg) +{ + m_last_error = e::error(); + m_last_error.set_loc(__FILE__, __LINE__); + m_last_error.set_msg() << msg; +} + +uint64_t +client :: generate_new_nonce() +{ + return m_next_server_nonce++; +} + +int64_t +client :: generate_new_client_id() +{ + return m_next_client_id++; +} + +void +client :: initialize(server_selector* ss) +{ + m_config.initialize(ss); +} + +void +client :: add_to_returnable(pending* p) +{ + m_returnable.push_back(p); +} + +bool +client :: send(uint64_t nonce, comm_id id, std::auto_ptr msg, pending* p) +{ + busybee_returncode rc = m_busybee.send(id.get(), msg); + + if (rc == BUSYBEE_DISRUPTED) + { + handle_disruption(id); + } + + if (rc == BUSYBEE_SUCCESS) + { + m_pending[std::make_pair(id, nonce)] = p; + } + + return rc == BUSYBEE_SUCCESS; +} + +void +client :: handle_disruption(const comm_id& id) +{ + for (std::map, e::intrusive_ptr >::iterator it = m_pending.begin(); + it != m_pending.end(); ) + { + if (it->first.first == id) + { + e::intrusive_ptr p = it->second; + m_pending.erase(it); + p->handle_server_disruption(this, id); + it = m_pending.begin(); + } + else + { + ++it; + } + } +} + +int64_t +client :: inner_loop(int timeout, consus_returncode* status) +{ + uint64_t cid_num; + std::auto_ptr msg; + m_busybee.set_timeout(timeout); + busybee_returncode rc = m_busybee.recv(&cid_num, &msg); + comm_id id(cid_num); + + switch (rc) + { + case BUSYBEE_SUCCESS: + break; + case BUSYBEE_INTERRUPTED: + ERROR(INTERRUPTED) << "signal received"; + return -1; + case BUSYBEE_TIMEOUT: + return 0; + case BUSYBEE_DISRUPTED: + handle_disruption(id); + return 0; + case BUSYBEE_EXTERNAL: + if (maintain_coord_connection(status) < 0) + { + return -1; + } + return 0; + BUSYBEE_ERROR_CASE(POLLFAILED); + BUSYBEE_ERROR_CASE(ADDFDFAIL); + BUSYBEE_ERROR_CASE(SHUTDOWN); + default: + ERROR(INTERNAL) << "internal error: BusyBee unexpectedly returned " + << (unsigned) rc << ": please file a bug"; + return -1; + } + + network_msgtype msg_type; + uint64_t nonce; + e::unpacker up = msg->unpack_from(BUSYBEE_HEADER_SIZE); + up = up >> msg_type >> nonce; + + if (up.error() || msg_type != CLIENT_RESPONSE) + { + ERROR(SERVER_ERROR) << "communication error: server " + << cid_num << " sent message=" + << msg->as_slice().hex() + << " with invalid header"; + return -1; + } + + std::map, e::intrusive_ptr >::iterator it; + it = m_pending.find(std::make_pair(id, nonce)); + + if (it != m_pending.end()) + { + e::intrusive_ptr p(it->second); + m_pending.erase(it); + p->handle_busybee_op(this, nonce, msg, up); + return p->client_id(); + } + + return 0; +} + +int64_t +client :: post_loop(consus_returncode* status) +{ + uint64_t cid_num; + m_busybee.set_timeout(0); + busybee_returncode rc = m_busybee.recv_no_msg(&cid_num); + + switch (rc) + { + case BUSYBEE_TIMEOUT: + break; + case BUSYBEE_INTERRUPTED: + ERROR(INTERRUPTED) << "signal received"; + return -1; + case BUSYBEE_DISRUPTED: + handle_disruption(comm_id(cid_num)); + break; + case BUSYBEE_EXTERNAL: + if (!maintain_coord_connection(status)) + { + return -1; + } + break; + BUSYBEE_ERROR_CASE(POLLFAILED); + BUSYBEE_ERROR_CASE(ADDFDFAIL); + BUSYBEE_ERROR_CASE(SHUTDOWN); + case BUSYBEE_SUCCESS: + default: + ERROR(INTERNAL) << "internal error: BusyBee unexpectedly returned " + << (unsigned) rc << ": please file a bug"; + return -1; + } + + ERROR(NONE_PENDING) << "no outstanding operations to process"; + return -1; +} + +bool +client :: maintain_coord_connection(consus_returncode* status) +{ + if (m_config_status != REPLICANT_SUCCESS) + { + replicant_client_kill(m_coord, m_config_id); + m_config_id = -1; + } + + if (m_config_id < 0) + { + m_config_id = replicant_client_cond_follow(m_coord, "consus", "clientconf", + &m_config_status, &m_config_state, + &m_config_data, &m_config_data_sz); + replicant_returncode rc; + + if (replicant_client_wait(m_coord, m_config_id, -1, &rc) < 0) + { + ERROR(COORD_FAIL) << "coordinator failure: " << replicant_client_error_message(m_coord); + return false; + } + } + + replicant_returncode rc; + + if (replicant_client_loop(m_coord, 0, &rc) < 0) + { + if (rc == REPLICANT_TIMEOUT || + rc == REPLICANT_INTERRUPTED || + rc == REPLICANT_NONE_PENDING) + { + } + else + { + ERROR(COORD_FAIL) << "coordinator failure: " << replicant_client_error_message(m_coord); + return false; + } + } + + if (m_config.version().get() < m_config_state) + { + configuration new_config; + e::unpacker up(m_config_data, m_config_data_sz); + up = up >> new_config; + + if (!up.error()) + { + m_config = new_config; + } + } + + return true; +} diff --git a/client/client.h b/client/client.h new file mode 100644 index 0000000..3937a57 --- /dev/null +++ b/client/client.h @@ -0,0 +1,112 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_client_h_ +#define consus_client_client_h_ + +// C +#include + +// STL +#include +#include + +// e +#include +#include + +// BusyBee +#include +#include + +// Replicant +#include + +// consus +#include +#include +#include "namespace.h" +#include "client/configuration.h" +#include "client/mapper.h" +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE + +class client +{ + public: + client(const char* host, uint16_t port); + client(const char* conn_str); + ~client() throw (); + + public: + // public API + int64_t loop(int timeout, consus_returncode* status); + int64_t wait(int64_t id, int timeout, consus_returncode* status); + int64_t begin_transaction(consus_returncode* status, + consus_transaction** xact); + // admin API + int create_data_center(const char* name, consus_returncode* status); + int set_default_data_center(const char* name, consus_returncode* status); + int availability_check(consus_availability_requirements* reqs, + int timeout, consus_returncode* status); + // internal semi-public API + int debug_client_configuration(consus_returncode* status, const char** str); + int debug_txman_configuration(consus_returncode* status, const char** str); + int debug_kvs_configuration(consus_returncode* status, const char** str); + // error handling + const char* error_message(); + const char* error_location(); + void set_error_message(const char* msg); + e::error* set_error_message() { return &m_last_error; } + + public: + uint64_t generate_new_nonce(); + int64_t generate_new_client_id(); + void initialize(server_selector* ss); + void add_to_returnable(pending* p); + bool send(uint64_t nonce, comm_id id, std::auto_ptr msg, pending* p); + void handle_disruption(const comm_id& id); + bool replicant_finish(int64_t id, replicant_returncode* rc, consus_returncode* status); + bool replicant_finish(int64_t id, int timeout, replicant_returncode* rc, consus_returncode* status); + + private: + friend class transaction; + // returns the ID of something that made progress; does not guarantee + // that it can return, so verify that at the callsite + int64_t inner_loop(int timeout, consus_returncode* status); + int64_t post_loop(consus_returncode* status); + bool maintain_coord_connection(consus_returncode* status); + + private: + // configuration + replicant_client* m_coord; + configuration m_config; + int64_t m_config_id; + replicant_returncode m_config_status; + uint64_t m_config_state; + char* m_config_data; + size_t m_config_data_sz; + // communication + mapper m_busybee_mapper; + busybee_st m_busybee; + // nonces + int64_t m_next_client_id; + uint64_t m_next_server_nonce; + // operations + std::map, e::intrusive_ptr > m_pending; + std::list > m_returnable; + e::intrusive_ptr m_returned; + // misc + e::flagfd m_flagfd; + e::error m_last_error; + + private: + client(const client&); + client& operator = (const client&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_client_h_ diff --git a/client/configuration.cc b/client/configuration.cc new file mode 100644 index 0000000..75be289 --- /dev/null +++ b/client/configuration.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/client_configuration.h" +#include "client/configuration.h" + +using consus::configuration; + +configuration :: configuration() + : m_cluster() + , m_version() + , m_flags(0) + , m_txmans() +{ +} + +configuration :: configuration(const configuration& other) + : m_cluster(other.m_cluster) + , m_version(other.m_version) + , m_flags(other.m_flags) + , m_txmans(other.m_txmans) +{ +} + +configuration :: ~configuration() throw () +{ +} + +po6::net::location +configuration :: get_address(const comm_id& id) const +{ + for (size_t i = 0; i < m_txmans.size(); ++i) + { + if (m_txmans[i].id == id) + { + return m_txmans[i].bind_to; + } + } + + return po6::net::location(); +} + +void +configuration :: initialize(server_selector* ss) +{ + std::vector ids; + + for (size_t i = 0; i < m_txmans.size(); ++i) + { + ids.push_back(m_txmans[i].id); + } + + ss->set(&ids[0], ids.size()); +} + +configuration& +configuration :: operator = (const configuration& rhs) +{ + if (this != &rhs) + { + m_cluster = rhs.m_cluster; + m_version = rhs.m_version; + m_flags = rhs.m_flags; + m_txmans = rhs.m_txmans; + } + + return *this; +} + +e::unpacker +consus :: operator >> (e::unpacker up, configuration& rhs) +{ + return client_configuration(up, &rhs.m_cluster, &rhs.m_version, &rhs.m_flags, &rhs.m_txmans); +} diff --git a/client/configuration.h b/client/configuration.h new file mode 100644 index 0000000..664d483 --- /dev/null +++ b/client/configuration.h @@ -0,0 +1,62 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_configuration_h_ +#define consus_client_configuration_h_ + +// C +#include + +// po6 +#include + +// e +#include + +// consus +#include "namespace.h" +#include "common/ids.h" +#include "common/txman.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE + +class configuration +{ + public: + configuration(); + configuration(const configuration& other); + ~configuration() throw (); + + // metadata + public: + cluster_id cluster() const { return m_cluster; } + version_id version() const { return m_version; } + + // transaction managers + public: + bool exists(const comm_id& id) const; + po6::net::location get_address(const comm_id& id) const; + void initialize(server_selector* ss); + + public: + configuration& operator = (const configuration& rhs); + + private: + friend e::unpacker operator >> (e::unpacker, configuration& s); + + private: + cluster_id m_cluster; + version_id m_version; + uint64_t m_flags; + std::vector m_txmans; +}; + +std::ostream& +operator << (std::ostream& lhs, const configuration& rhs); +e::unpacker +operator >> (e::unpacker lhs, configuration& rhs); + +END_CONSUS_NAMESPACE + +#endif // consus_client_configuration_h_ diff --git a/client/consus-internal.h b/client/consus-internal.h new file mode 100644 index 0000000..a03e428 --- /dev/null +++ b/client/consus-internal.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +#ifndef consus_internal_h_ +#define consus_internal_h_ + +/* C */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +int consus_debug_client_configuration(struct consus_client* client, + enum consus_returncode* status, + const char** str); +int consus_debug_txman_configuration(struct consus_client* client, + enum consus_returncode* status, + const char** str); +int consus_debug_kvs_configuration(struct consus_client* client, + enum consus_returncode* status, + const char** str); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ +#endif /* consus_internal_h_ */ diff --git a/client/mapper.cc b/client/mapper.cc new file mode 100644 index 0000000..9bdd3f3 --- /dev/null +++ b/client/mapper.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "client/mapper.h" + +using consus::mapper; + +mapper :: mapper(const configuration* config) + : m_config(config) +{ +} + +mapper :: ~mapper() throw () +{ +} + +bool +mapper :: lookup(uint64_t id, po6::net::location* addr) +{ + *addr = m_config->get_address(comm_id(id)); + return *addr != po6::net::location(); +} diff --git a/client/mapper.h b/client/mapper.h new file mode 100644 index 0000000..f1fa90e --- /dev/null +++ b/client/mapper.h @@ -0,0 +1,35 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_mapper_h_ +#define consus_client_mapper_h_ + +// BusyBee +#include + +// consus +#include "namespace.h" +#include "client/configuration.h" + +BEGIN_CONSUS_NAMESPACE + +class mapper : public ::busybee_mapper +{ + public: + mapper(const configuration* config); + ~mapper() throw (); + + public: + virtual bool lookup(uint64_t id, po6::net::location* addr); + + private: + mapper(const mapper&); + mapper& operator = (const mapper&); + + private: + const configuration* m_config; +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_mapper_h_ diff --git a/client/pending.cc b/client/pending.cc new file mode 100644 index 0000000..ff52998 --- /dev/null +++ b/client/pending.cc @@ -0,0 +1,62 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "client/pending.h" + +using consus::pending; + +pending :: pending(int64_t client_id, consus_returncode* status) + : m_ref(0) + , m_error() + , m_client_id(client_id) + , m_status(status) +{ +} + +pending :: ~pending() throw () +{ +} + +void +pending :: returning() +{ +} + +void +pending :: handle_server_failure(client*, comm_id) +{ +} + +void +pending :: handle_server_disruption(client*, comm_id) +{ +} + +void +pending :: handle_busybee_op(client*, + uint64_t, + std::auto_ptr, + e::unpacker) +{ +} + +std::ostream& +pending :: error(const char* file, size_t line) +{ + m_error.set_loc(file, line); + return m_error.set_msg(); +} + +void +pending :: set_error(const e::error& err) +{ + m_error = err; +} + +void +pending :: success() +{ + set_status(CONSUS_SUCCESS); + m_error = e::error(); +} diff --git a/client/pending.h b/client/pending.h new file mode 100644 index 0000000..a716367 --- /dev/null +++ b/client/pending.h @@ -0,0 +1,88 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_h_ +#define consus_client_pending_h_ + +// STL +#include + +// e +#include +#include + +// consus +#include +#include "namespace.h" +#include "common/ids.h" +#include "common/network_msgtype.h" + +BEGIN_CONSUS_NAMESPACE +class client; + +class pending +{ + public: + pending(int64_t client_id, consus_returncode* status); + virtual ~pending() throw (); + + public: + int64_t client_id() const { return m_client_id; } + void set_status(consus_returncode st) { *m_status = st; } + consus_returncode status() const { return *m_status; } + e::error error() const { return m_error; } + + // general stuff only useful for debugging, but then very useful + public: + virtual std::string describe() = 0; + + // called when this operation's id is returned to the client via loop/wait + // will be called once per time the pending returns from these calls + public: + virtual void returning(); + + // state machine + public: + virtual void kickstart_state_machine(client* cl) = 0; + // the entity has been failed according to the configuration + virtual void handle_server_failure(client* cl, comm_id si); + // the entity has been disrupted according to busybee + virtual void handle_server_disruption(client* cl, comm_id si); + // a busybee message + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + // refcount + protected: + friend class e::intrusive_ptr; + void inc() { ++m_ref; } + void dec() { if (--m_ref == 0) delete this; } + size_t m_ref; + + // errors + protected: + std::ostream& error(const char* file, size_t line); + void set_error(const e::error& err); + void success(); + e::error m_error; + + // operation state + private: + int64_t m_client_id; + consus_returncode* m_status; + + // noncopyable + private: + pending(const pending& other); + pending& operator = (const pending& rhs); +}; + +#define PENDING_ERROR(CODE) \ + this->set_status(CONSUS_ ## CODE); \ + this->error(__FILE__, __LINE__) + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_h_ diff --git a/client/pending_begin_transaction.cc b/client/pending_begin_transaction.cc new file mode 100644 index 0000000..2e74426 --- /dev/null +++ b/client/pending_begin_transaction.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// BusyBee +#include + +// consus +#include "common/transaction_id.h" +#include "client/client.h" +#include "client/pending_begin_transaction.h" +#include "client/transaction.h" + +using consus::pending_begin_transaction; + +pending_begin_transaction :: pending_begin_transaction(int64_t client_id, + consus_returncode* status, + consus_transaction** xact) + : pending(client_id, status) + , m_xact(xact) + , m_ss() +{ + *m_xact = NULL; +} + +pending_begin_transaction :: ~pending_begin_transaction() throw () +{ +} + +std::string +pending_begin_transaction :: describe() +{ + return "pending_begin_transaction()"; +} + +void +pending_begin_transaction :: kickstart_state_machine(client* cl) +{ + cl->initialize(&m_ss); + send_request(cl); +} + +void +pending_begin_transaction :: handle_server_failure(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_begin_transaction :: handle_server_disruption(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_begin_transaction :: handle_busybee_op(client* cl, + uint64_t, + std::auto_ptr, + e::unpacker up) +{ + transaction_id txid; + std::vector ids; + up = up >> txid >> ids; + + if (up.error()) + { + PENDING_ERROR(SERVER_ERROR) << "server sent a corrupt response to \"begin-transaction\""; + cl->add_to_returnable(this); + return; + } + + transaction* t = new transaction(cl, txid, &ids[0], ids.size()); + *m_xact = reinterpret_cast(t); + this->success(); + cl->add_to_returnable(this); +} + +void +pending_begin_transaction :: send_request(client* cl) +{ + while (true) + { + const uint64_t nonce = cl->generate_new_nonce(); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_BEGIN) + + VARINT_64_MAX_SIZE; + comm_id id = m_ss.next(); + + if (id == comm_id()) + { + PENDING_ERROR(UNAVAILABLE) << "insufficient number of servers to ensure durability"; + cl->add_to_returnable(this); + return; + } + + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << TXMAN_BEGIN << e::pack_varint(nonce); + + if (cl->send(nonce, id, msg, this)) + { + return; + } + } +} diff --git a/client/pending_begin_transaction.h b/client/pending_begin_transaction.h new file mode 100644 index 0000000..492f780 --- /dev/null +++ b/client/pending_begin_transaction.h @@ -0,0 +1,45 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_begin_transaction_h_ +#define consus_client_pending_begin_transaction_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE + +class pending_begin_transaction : public pending +{ + public: + pending_begin_transaction(int64_t client_id, + consus_returncode* status, + consus_transaction** xact); + virtual ~pending_begin_transaction() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client* cl); + virtual void handle_server_failure(client* cl, comm_id si); + virtual void handle_server_disruption(client* cl, comm_id si); + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + private: + void send_request(client* cl); + + private: + consus_transaction** m_xact; + server_selector m_ss; + + private: + pending_begin_transaction(const pending_begin_transaction&); + pending_begin_transaction& operator = (const pending_begin_transaction&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_begin_transaction_h_ diff --git a/client/pending_string.cc b/client/pending_string.cc new file mode 100644 index 0000000..51572f3 --- /dev/null +++ b/client/pending_string.cc @@ -0,0 +1,29 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "client/pending_string.h" + +using consus::pending_string; + +pending_string :: pending_string(const char* s) + : pending(-1, NULL) + , m_str(s) +{ +} + +pending_string :: pending_string(const std::string& s) + : pending(-1, NULL) + , m_str(s) +{ +} + +pending_string :: ~pending_string() throw () +{ +} + +std::string +pending_string :: describe() +{ + return "pending_string(" + m_str + ")"; +} diff --git a/client/pending_string.h b/client/pending_string.h new file mode 100644 index 0000000..91e168a --- /dev/null +++ b/client/pending_string.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_string_h_ +#define consus_client_pending_string_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE + +class pending_string : public pending +{ + public: + pending_string(const char* s); + pending_string(const std::string& s); + virtual ~pending_string() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client*) {} + const char* string() { return m_str.c_str(); } + + private: + friend class e::intrusive_ptr; + + private: + std::string m_str; + + private: + pending_string(const pending_string&); + pending_string& operator = (const pending_string&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_string_h_ diff --git a/client/pending_transaction_abort.cc b/client/pending_transaction_abort.cc new file mode 100644 index 0000000..6c991f9 --- /dev/null +++ b/client/pending_transaction_abort.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// BusyBee +#include + +// consus +#include "common/consus.h" +#include "client/client.h" +#include "client/pending_transaction_abort.h" +#include "client/transaction.h" + +using consus::pending_transaction_abort; + +pending_transaction_abort :: pending_transaction_abort(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot) + : pending(client_id, status) + , m_xact(xact) + , m_ss() + , m_slot(slot) +{ +} + +pending_transaction_abort :: ~pending_transaction_abort() throw () +{ +} + +std::string +pending_transaction_abort :: describe() +{ + std::ostringstream ostr; + ostr << "pending_transaction_abort(id=" << m_xact->txid() << "\")"; + return ostr.str(); +} + +void +pending_transaction_abort :: kickstart_state_machine(client* cl) +{ + m_xact->initialize(&m_ss); + send_request(cl); +} + +void +pending_transaction_abort :: handle_server_failure(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_abort :: handle_server_disruption(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_abort :: handle_busybee_op(client* cl, + uint64_t, + std::auto_ptr, + e::unpacker up) +{ + consus_returncode rc; + up = up >> rc; + + if (up.error()) + { + abort(); // XXX + } + + this->success(); // XXX + cl->add_to_returnable(this); +} + +void +pending_transaction_abort :: send_request(client* cl) +{ + while (true) + { + const uint64_t nonce = m_xact->parent()->generate_new_nonce(); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_ABORT) + + pack_size(m_xact->txid()) + + 2 * VARINT_64_MAX_SIZE; + comm_id id = m_ss.next(); + + if (id == comm_id()) + { + m_xact->mark_aborted(); + PENDING_ERROR(UNAVAILABLE) << "insufficient number of servers to ensure durability"; + cl->add_to_returnable(this); + return; + } + + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << TXMAN_ABORT << m_xact->txid() + << e::pack_varint(nonce) + << e::pack_varint(m_slot); + + if (cl->send(nonce, id, msg, this)) + { + return; + } + } +} diff --git a/client/pending_transaction_abort.h b/client/pending_transaction_abort.h new file mode 100644 index 0000000..1d73fd0 --- /dev/null +++ b/client/pending_transaction_abort.h @@ -0,0 +1,48 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_transaction_abort_h_ +#define consus_client_pending_transaction_abort_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE +class transaction; + +class pending_transaction_abort : public pending +{ + public: + pending_transaction_abort(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot); + virtual ~pending_transaction_abort() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client* cl); + virtual void handle_server_failure(client* cl, comm_id si); + virtual void handle_server_disruption(client* cl, comm_id si); + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + private: + void send_request(client* cl); + + private: + transaction* m_xact; + server_selector m_ss; + const uint64_t m_slot; + + private: + pending_transaction_abort(const pending_transaction_abort&); + pending_transaction_abort& operator = (const pending_transaction_abort&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_transaction_abort_h_ diff --git a/client/pending_transaction_commit.cc b/client/pending_transaction_commit.cc new file mode 100644 index 0000000..6b392db --- /dev/null +++ b/client/pending_transaction_commit.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// BusyBee +#include + +// consus +#include "common/consus.h" +#include "client/client.h" +#include "client/pending_transaction_commit.h" +#include "client/transaction.h" + +using consus::pending_transaction_commit; + +pending_transaction_commit :: pending_transaction_commit(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot) + : pending(client_id, status) + , m_xact(xact) + , m_ss() + , m_slot(slot) +{ +} + +pending_transaction_commit :: ~pending_transaction_commit() throw () +{ +} + +std::string +pending_transaction_commit :: describe() +{ + std::ostringstream ostr; + ostr << "pending_transaction_commit(id=" << m_xact->txid() << "\")"; + return ostr.str(); +} + +void +pending_transaction_commit :: kickstart_state_machine(client* cl) +{ + m_xact->initialize(&m_ss); + send_request(cl); +} + +void +pending_transaction_commit :: handle_server_failure(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_commit :: handle_server_disruption(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_commit :: handle_busybee_op(client* cl, + uint64_t, + std::auto_ptr, + e::unpacker up) +{ + consus_returncode rc; + up = up >> rc; + + if (up.error()) + { + abort(); // XXX + } + + this->success(); // XXX + cl->add_to_returnable(this); +} + +void +pending_transaction_commit :: send_request(client* cl) +{ + while (true) + { + const uint64_t nonce = m_xact->parent()->generate_new_nonce(); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_COMMIT) + + pack_size(m_xact->txid()) + + 2 * VARINT_64_MAX_SIZE; + comm_id id = m_ss.next(); + + if (id == comm_id()) + { + m_xact->mark_aborted(); + PENDING_ERROR(UNAVAILABLE) << "insufficient number of servers to ensure durability"; + cl->add_to_returnable(this); + return; + } + + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << TXMAN_COMMIT << m_xact->txid() + << e::pack_varint(nonce) + << e::pack_varint(m_slot); + + if (cl->send(nonce, id, msg, this)) + { + return; + } + } +} diff --git a/client/pending_transaction_commit.h b/client/pending_transaction_commit.h new file mode 100644 index 0000000..fd91080 --- /dev/null +++ b/client/pending_transaction_commit.h @@ -0,0 +1,48 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_transaction_commit_h_ +#define consus_client_pending_transaction_commit_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE +class transaction; + +class pending_transaction_commit : public pending +{ + public: + pending_transaction_commit(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot); + virtual ~pending_transaction_commit() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client* cl); + virtual void handle_server_failure(client* cl, comm_id si); + virtual void handle_server_disruption(client* cl, comm_id si); + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + private: + void send_request(client* cl); + + private: + transaction* m_xact; + server_selector m_ss; + const uint64_t m_slot; + + private: + pending_transaction_commit(const pending_transaction_commit&); + pending_transaction_commit& operator = (const pending_transaction_commit&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_transaction_commit_h_ diff --git a/client/pending_transaction_read.cc b/client/pending_transaction_read.cc new file mode 100644 index 0000000..c9892be --- /dev/null +++ b/client/pending_transaction_read.cc @@ -0,0 +1,165 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include + +// treadstone +#include + +// BusyBee +#include + +// consus +#include "common/consus.h" +#include "client/client.h" +#include "client/pending_transaction_read.h" +#include "client/transaction.h" + +using consus::pending_transaction_read; + +pending_transaction_read :: pending_transaction_read(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot, + const char* table, + const unsigned char* key, size_t key_sz, + char** value, size_t* value_sz) + : pending(client_id, status) + , m_xact(xact) + , m_ss() + , m_slot(slot) + , m_table(table) + , m_key(key, key + key_sz) + , m_value(value) + , m_value_sz(value_sz) +{ +} + +pending_transaction_read :: ~pending_transaction_read() throw () +{ +} + +std::string +pending_transaction_read :: describe() +{ + std::ostringstream ostr; + ostr << "pending_transaction_read(id=" << m_xact->txid() + << ", table=\"" << e::strescape(m_table) + << "\", key=\"" << e::strescape(m_key) << "\")"; + return ostr.str(); +} + +void +pending_transaction_read :: kickstart_state_machine(client* cl) +{ + m_xact->initialize(&m_ss); + send_request(cl); +} + +void +pending_transaction_read :: handle_server_failure(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_read :: handle_server_disruption(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_read :: handle_busybee_op(client* cl, + uint64_t, + std::auto_ptr, + e::unpacker up) +{ + consus_returncode rc; + uint64_t timestamp; + e::slice value; + up = up >> rc >> timestamp >> value; + + if (up.error()) + { + m_xact->mark_aborted(); + PENDING_ERROR(SERVER_ERROR) << "server sent a corrupt response to \"transaction-read\""; + cl->add_to_returnable(this); + return; + } + + if (rc != CONSUS_SUCCESS && rc != CONSUS_NOT_FOUND) + { + m_xact->mark_aborted(); + set_status(rc); + error(__FILE__, __LINE__) << "server sent failure code"; + cl->add_to_returnable(this); + return; + } + + if (rc == CONSUS_SUCCESS) + { + char* tmp = NULL; + + if (treadstone_binary_to_json(value.data(), value.size(), &tmp)) + { + PENDING_ERROR(SEE_ERRNO) << po6::strerror(errno); + cl->add_to_returnable(this); + return; + } + + *m_value = tmp; + *m_value_sz = strlen(tmp); + this->success(); + cl->add_to_returnable(this); + } + else if (rc == CONSUS_NOT_FOUND) + { + *m_value = NULL; + *m_value_sz = 0; + set_status(CONSUS_NOT_FOUND); + error(__FILE__, __LINE__) << "value not found"; + cl->add_to_returnable(this); + } + else + { + abort(); + } +} + +void +pending_transaction_read :: send_request(client* cl) +{ + while (true) + { + const uint64_t nonce = m_xact->parent()->generate_new_nonce(); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_READ) + + pack_size(m_xact->txid()) + + 2 * VARINT_64_MAX_SIZE + + pack_size(e::slice(m_table)) + + pack_size(e::slice(m_key)); + comm_id id = m_ss.next(); + + if (id == comm_id()) + { + m_xact->mark_aborted(); + PENDING_ERROR(UNAVAILABLE) << "insufficient number of servers to ensure durability"; + cl->add_to_returnable(this); + return; + } + + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << TXMAN_READ << m_xact->txid() + << e::pack_varint(nonce) + << e::pack_varint(m_slot) + << e::slice(m_table) + << e::slice(m_key); + + if (cl->send(nonce, id, msg, this)) + { + return; + } + } +} diff --git a/client/pending_transaction_read.h b/client/pending_transaction_read.h new file mode 100644 index 0000000..96e4a9a --- /dev/null +++ b/client/pending_transaction_read.h @@ -0,0 +1,55 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_transaction_read_h_ +#define consus_client_pending_transaction_read_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE +class transaction; + +class pending_transaction_read : public pending +{ + public: + pending_transaction_read(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot, + const char* table, + const unsigned char* key, size_t key_sz, + char** value, size_t* value_sz); + virtual ~pending_transaction_read() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client* cl); + virtual void handle_server_failure(client* cl, comm_id si); + virtual void handle_server_disruption(client* cl, comm_id si); + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + private: + void send_request(client* cl); + + private: + transaction* m_xact; + server_selector m_ss; + const uint64_t m_slot; + std::string m_table; + std::string m_key; + char** m_value; + size_t* m_value_sz; + + private: + pending_transaction_read(const pending_transaction_read&); + pending_transaction_read& operator = (const pending_transaction_read&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_transaction_read_h_ diff --git a/client/pending_transaction_write.cc b/client/pending_transaction_write.cc new file mode 100644 index 0000000..30a1e06 --- /dev/null +++ b/client/pending_transaction_write.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include + +// BusyBee +#include + +// consus +#include "common/consus.h" +#include "client/client.h" +#include "client/pending_transaction_write.h" +#include "client/transaction.h" + +using consus::pending_transaction_write; + +pending_transaction_write :: pending_transaction_write(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot, + const char* table, + const unsigned char* key, size_t key_sz, + const unsigned char* value, size_t value_sz) + : pending(client_id, status) + , m_xact(xact) + , m_ss() + , m_slot(slot) + , m_table(table) + , m_key(key, key + key_sz) + , m_value(value, value + value_sz) +{ +} + +pending_transaction_write :: ~pending_transaction_write() throw () +{ +} + +std::string +pending_transaction_write :: describe() +{ + std::ostringstream ostr; + ostr << "pending_transaction_write(id=" << m_xact->txid() + << ", table=\"" << e::strescape(m_table) + << "\", key=\"" << e::strescape(m_key) + << "\", value=\"" << e::strescape(m_value) << "\")"; + return ostr.str(); +} + +void +pending_transaction_write :: kickstart_state_machine(client* cl) +{ + m_xact->initialize(&m_ss); + send_request(cl); +} + +void +pending_transaction_write :: handle_server_failure(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_write :: handle_server_disruption(client* cl, comm_id) +{ + send_request(cl); +} + +void +pending_transaction_write :: handle_busybee_op(client* cl, + uint64_t, + std::auto_ptr, + e::unpacker up) +{ + consus_returncode rc; + up = up >> rc; + + if (up.error()) + { + m_xact->mark_aborted(); + PENDING_ERROR(SERVER_ERROR) << "server sent a corrupt response to \"transaction-write\""; + cl->add_to_returnable(this); + return; + } + + if (rc != CONSUS_SUCCESS) + { + m_xact->mark_aborted(); + set_status(rc); + error(__FILE__, __LINE__) << "server sent failure code"; + cl->add_to_returnable(this); + return; + } + + this->success(); + cl->add_to_returnable(this); +} + +void +pending_transaction_write :: send_request(client* cl) +{ + while (true) + { + const uint64_t nonce = m_xact->parent()->generate_new_nonce(); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_WRITE) + + pack_size(m_xact->txid()) + + 2 * VARINT_64_MAX_SIZE + + pack_size(e::slice(m_table)) + + pack_size(e::slice(m_key)) + + pack_size(e::slice(m_value)); + comm_id id = m_ss.next(); + + if (id == comm_id()) + { + m_xact->mark_aborted(); + PENDING_ERROR(UNAVAILABLE) << "insufficient number of servers to ensure durability"; + cl->add_to_returnable(this); + return; + } + + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << TXMAN_WRITE << m_xact->txid() + << e::pack_varint(nonce) + << e::pack_varint(m_slot) + << e::slice(m_table) + << e::slice(m_key) + << e::slice(m_value); + + if (cl->send(nonce, id, msg, this)) + { + return; + } + } +} diff --git a/client/pending_transaction_write.h b/client/pending_transaction_write.h new file mode 100644 index 0000000..b4daba1 --- /dev/null +++ b/client/pending_transaction_write.h @@ -0,0 +1,54 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_pending_transaction_write_h_ +#define consus_client_pending_transaction_write_h_ + +// consus +#include "client/pending.h" +#include "client/server_selector.h" + +BEGIN_CONSUS_NAMESPACE +class transaction; + +class pending_transaction_write : public pending +{ + public: + pending_transaction_write(int64_t client_id, + consus_returncode* status, + transaction* xact, + uint64_t slot, + const char* table, + const unsigned char* key, size_t key_sz, + const unsigned char* value, size_t value_sz); + virtual ~pending_transaction_write() throw (); + + public: + virtual std::string describe(); + virtual void kickstart_state_machine(client* cl); + virtual void handle_server_failure(client* cl, comm_id si); + virtual void handle_server_disruption(client* cl, comm_id si); + virtual void handle_busybee_op(client* cl, + uint64_t nonce, + std::auto_ptr msg, + e::unpacker up); + + private: + void send_request(client* cl); + + private: + transaction* m_xact; + server_selector m_ss; + const uint64_t m_slot; + std::string m_table; + std::string m_key; + std::string m_value; + + private: + pending_transaction_write(const pending_transaction_write&); + pending_transaction_write& operator = (const pending_transaction_write&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_pending_transaction_write_h_ diff --git a/client/server_selector.cc b/client/server_selector.cc new file mode 100644 index 0000000..7f60ef7 --- /dev/null +++ b/client/server_selector.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include "client/server_selector.h" + +using consus::server_selector; + +server_selector :: server_selector() + : m_ids() + , m_consumed_idx() +{ +} + +server_selector :: ~server_selector() throw () +{ +} + +void +server_selector :: set(const comm_id* ids, size_t ids_sz) +{ + m_ids = std::vector(ids, ids + ids_sz); + m_consumed_idx = 0; +} + +consus::comm_id +server_selector :: next() +{ + if (m_consumed_idx >= m_ids.size()) + { + return comm_id(); + } + + return m_ids[m_consumed_idx++]; +} diff --git a/client/server_selector.h b/client/server_selector.h new file mode 100644 index 0000000..5445904 --- /dev/null +++ b/client/server_selector.h @@ -0,0 +1,33 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_server_selector_h_ +#define consus_client_server_selector_h_ + +// STL +#include + +// consus +#include "namespace.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class server_selector +{ + public: + server_selector(); + ~server_selector() throw (); + + public: + void set(const comm_id* ids, size_t ids_sz); + comm_id next(); + + private: + std::vector m_ids; + size_t m_consumed_idx; +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_server_selector_h_ diff --git a/client/transaction.cc b/client/transaction.cc new file mode 100644 index 0000000..c8815d3 --- /dev/null +++ b/client/transaction.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// treadstone +#include + +// consus +#include "client/client.h" +#include "client/transaction.h" +#include "client/pending_transaction_read.h" +#include "client/pending_transaction_write.h" +#include "client/pending_transaction_commit.h" +#include "client/pending_transaction_abort.h" + +#define ERROR(CODE) \ + *status = CONSUS_ ## CODE; \ + m_cl->set_error_message()->set_loc(__FILE__, __LINE__); \ + m_cl->set_error_message()->set_msg() + +using consus::transaction; + +transaction :: transaction(client* cl, const transaction_id& txid, + const comm_id* ids, size_t ids_sz) + : m_cl(cl) + , m_txid(txid) + , m_ids(ids, ids + ids_sz) + , m_next_slot(1) +{ +} + +transaction :: ~transaction() throw () +{ +} + +int64_t +transaction :: get(const char* table, + const char* key, size_t key_sz, + consus_returncode* status, + char** value, size_t* value_sz) +{ + if (!m_cl->maintain_coord_connection(status)) + { + return -1; + } + + unsigned char* binkey = NULL; + size_t binkey_sz = 0; + + if (treadstone_json_sz_to_binary(key, key_sz, &binkey, &binkey_sz) < 0) + { + ERROR(INVALID) << "key contains invalid JSON"; + return -1; + } + + uint64_t slot = m_next_slot; + ++m_next_slot; + int64_t client_id = m_cl->generate_new_client_id(); + pending* p = new pending_transaction_read(client_id, status, this, slot, + table, binkey, binkey_sz, value, value_sz); + free(binkey); + p->kickstart_state_machine(m_cl); + return client_id; +} + +int64_t +transaction :: put(const char* table, + const char* key, size_t key_sz, + const char* value, size_t value_sz, + consus_returncode* status) +{ + if (!m_cl->maintain_coord_connection(status)) + { + return -1; + } + + unsigned char* binkey = NULL; + size_t binkey_sz = 0; + unsigned char* binval = NULL; + size_t binval_sz = 0; + + if (treadstone_json_sz_to_binary(key, key_sz, &binkey, &binkey_sz) < 0) + { + ERROR(INVALID) << "key contains invalid JSON"; + return -1; + } + + if (treadstone_json_sz_to_binary(value, value_sz, &binval, &binval_sz) < 0) + { + ERROR(INVALID) << "value contains invalid JSON"; + free(binkey); + return -1; + } + + uint64_t slot = m_next_slot; + ++m_next_slot; + int64_t client_id = m_cl->generate_new_client_id(); + pending* p = new pending_transaction_write(client_id, status, this, slot, + table, binkey, binkey_sz, binval, binval_sz); + free(binkey); + free(binval); + p->kickstart_state_machine(m_cl); + return client_id; +} + +int64_t +transaction :: commit(consus_returncode* status) +{ + if (!m_cl->maintain_coord_connection(status)) + { + return -1; + } + + uint64_t slot = m_next_slot; + ++m_next_slot; + int64_t client_id = m_cl->generate_new_client_id(); + pending* p = new pending_transaction_commit(client_id, status, this, slot); + p->kickstart_state_machine(m_cl); + return client_id; +} + +int64_t +transaction :: abort(consus_returncode* status) +{ + if (!m_cl->maintain_coord_connection(status)) + { + return -1; + } + + uint64_t slot = m_next_slot; + ++m_next_slot; + int64_t client_id = m_cl->generate_new_client_id(); + pending* p = new pending_transaction_abort(client_id, status, this, slot); + p->kickstart_state_machine(m_cl); + return client_id; +} + +void +transaction :: initialize(server_selector* ss) +{ + ss->set(&m_ids[0], m_ids.size()); +} + +void +transaction :: mark_aborted() +{ + ::abort(); // XXX +} diff --git a/client/transaction.h b/client/transaction.h new file mode 100644 index 0000000..65201fd --- /dev/null +++ b/client/transaction.h @@ -0,0 +1,57 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_client_transaction_h_ +#define consus_client_transaction_h_ + +// C +#include + +// e +#include + +// consus +#include +#include "namespace.h" +#include "common/transaction_id.h" + +BEGIN_CONSUS_NAMESPACE +class client; + +class transaction +{ + public: + transaction(client* cl, const transaction_id& txid, + const comm_id* ids, size_t ids_sz); + ~transaction() throw (); + + public: + transaction_id txid() { return m_txid; } + client* parent() { return m_cl; } + int64_t get(const char* table, + const char* key, size_t key_sz, + consus_returncode* status, + char** value, size_t* value_sz); + int64_t put(const char* table, + const char* key, size_t key_sz, + const char* value, size_t value_sz, + consus_returncode* status); + int64_t commit(consus_returncode* status); + int64_t abort(consus_returncode* status); + void initialize(server_selector* ss); + void mark_aborted(); + + private: + client* const m_cl; + const transaction_id m_txid; + const std::vector m_ids; + uint64_t m_next_slot; + + private: + transaction(const transaction&); + transaction& operator = (const transaction&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_client_transaction_h_ diff --git a/common/client_configuration.cc b/common/client_configuration.cc new file mode 100644 index 0000000..575ed39 --- /dev/null +++ b/common/client_configuration.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include + +// consus +#include "common/client_configuration.h" + +e::unpacker +consus :: client_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* txmans) +{ + return up >> *cid >> *vid >> *flags >> *txmans; +} diff --git a/common/client_configuration.h b/common/client_configuration.h new file mode 100644 index 0000000..68f0c4f --- /dev/null +++ b/common/client_configuration.h @@ -0,0 +1,22 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_client_configuration_h_ +#define consus_common_client_configuration_h_ + +// consus +#include "namespace.h" +#include "common/ids.h" +#include "common/txman.h" + +BEGIN_CONSUS_NAMESPACE + +e::unpacker client_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* txmans); + +END_CONSUS_NAMESPACE + +#endif // consus_common_client_configuration_h_ diff --git a/common/constants.h b/common/constants.h new file mode 100644 index 0000000..bf7bf62 --- /dev/null +++ b/common/constants.h @@ -0,0 +1,29 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_constants_h_ +#define consus_common_constants_h_ + +#define CONSUS_MAX_REPLICATION_FACTOR 9 + +#define CONSUS_PORT_TXMAN 22751 +#define CONSUS_PORT_KVS 22761 + +// This defines the maximum number of key-value stores that can be within a +// single data center. This can support a 10PB data set with just 160GB per +// partition. Because each schema spreads across the entire ring, that's 10PB +// per schema. If you have closer to 4TB/node, your cluster can support 256PB. +// All numbers are raw-data capacity. Replication obviously introduces a +// multiplier on the space required for a given data set. +// +// Changing this constant will not be supported by the initial developers, as it +// only makes sense to increase it. If you must increase it, you have a +// sufficiently large deployment that you should probably think about tasking a +// few developers with making the change, testing it, and giving any +// changes/fixes back to the project. +#define CONSUS_KVS_PARTITIONS 65536 + +#define CONSUS_VOTE_ABORT 0x61626f7274000000ULL +#define CONSUS_VOTE_COMMIT 0x636f6d6d69740000ULL + +#endif // consus_common_constants_h_ diff --git a/common/consus.cc b/common/consus.cc new file mode 100644 index 0000000..85a6b28 --- /dev/null +++ b/common/consus.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/consus.h" +#include "common/macros.h" + +std::ostream& +consus :: operator << (std::ostream& lhs, const consus_returncode& rc) +{ + switch (rc) + { + STRINGIFY(CONSUS_SUCCESS); + STRINGIFY(CONSUS_NOT_FOUND); + STRINGIFY(CONSUS_ABORTED); + STRINGIFY(CONSUS_UNKNOWN_TABLE); + STRINGIFY(CONSUS_NONE_PENDING); + STRINGIFY(CONSUS_INVALID); + STRINGIFY(CONSUS_TIMEOUT); + STRINGIFY(CONSUS_INTERRUPTED); + STRINGIFY(CONSUS_SEE_ERRNO); + STRINGIFY(CONSUS_COORD_FAIL); + STRINGIFY(CONSUS_UNAVAILABLE); + STRINGIFY(CONSUS_SERVER_ERROR); + STRINGIFY(CONSUS_INTERNAL); + STRINGIFY(CONSUS_GARBAGE); + default: + lhs << "unknown consus_returncode"; + } + + return lhs; +} + +e::packer +consus :: operator << (e::packer lhs, const consus_returncode& rhs) +{ + uint16_t mt = static_cast(rhs); + return lhs << mt; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, consus_returncode& rhs) +{ + uint16_t mt; + lhs = lhs >> mt; + rhs = static_cast(mt); + return lhs; +} + +size_t +consus :: pack_size(const consus_returncode&) +{ + return sizeof(uint16_t); +} diff --git a/common/consus.h b/common/consus.h new file mode 100644 index 0000000..b8275e6 --- /dev/null +++ b/common/consus.h @@ -0,0 +1,28 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_consus_h_ +#define consus_common_consus_h_ + +// e +#include + +// consus +#include +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +std::ostream& +operator << (std::ostream& lhs, const consus_returncode& rhs); + +e::packer +operator << (e::packer lhs, const consus_returncode& rhs); +e::unpacker +operator >> (e::unpacker lhs, consus_returncode& rhs); +size_t +pack_size(const consus_returncode& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_consus_h_ diff --git a/common/coordinator_link.cc b/common/coordinator_link.cc new file mode 100644 index 0000000..2bf278e --- /dev/null +++ b/common/coordinator_link.cc @@ -0,0 +1,352 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include + +// POSIX +#include + +// Google Log +#include + +// po6 +#include + +// e +#include +#include + +// consus +#include "common/coordinator_returncode.h" +#include "common/coordinator_link.h" + +using consus::coordinator_link; + +coordinator_link :: coordinator_link(const std::string& rendezvous, + comm_id id, po6::net::location bind_to, + callback* c/*ownership not transferred*/) + : m_mtx() + , m_repl(replicant_client_create_conn_str(rendezvous.c_str())) + , m_id(id) + , m_bind_to(bind_to) + , m_cb(c) + , m_config_id(-1) + , m_config_status(REPLICANT_SUCCESS) + , m_config_state(0) + , m_config_data(NULL) + , m_config_data_sz(0) + , m_last_config_state(0) + , m_last_config_valid(false) + , m_allow_rereg(false) + , m_error(false) + , m_orphaned(false) + , m_online_once(false) +{ + if (!m_repl) + { + throw std::bad_alloc(); + } + + po6::threads::mutex::hold hold(&m_mtx); + invariant_check(); +} + +coordinator_link :: ~coordinator_link() throw () +{ + replicant_client_destroy(m_repl); +} + +bool +coordinator_link :: initial_registration() +{ + { + po6::threads::mutex::hold hold(&m_mtx); + invariant_check(); + + if (m_error) + { + LOG(ERROR) << "coordinator link failed"; + return false; + } + + if (!registration()) + { + return false; + } + } + + return establish(); +} + +bool +coordinator_link :: establish() +{ + { + po6::threads::mutex::hold hold(&m_mtx); + invariant_check(); + + if (m_error) + { + LOG(ERROR) << "coordinator link failed"; + return false; + } + } + + maintain_connection(); + po6::threads::mutex::hold hold(&m_mtx); + return m_last_config_valid; +} + +void +coordinator_link :: allow_reregistration() +{ + po6::threads::mutex::hold hold(&m_mtx); + m_allow_rereg = true; +} + +void +coordinator_link :: maintain_connection() +{ + po6::threads::mutex::hold hold(&m_mtx); + invariant_check(); + + if (m_error) + { + LOG(ERROR) << "coordinator link failed"; + return; + } + + std::string cond = m_cb->prefix() + "conf"; + + if (m_config_status != REPLICANT_SUCCESS) + { + replicant_client_kill(m_repl, m_config_id); + m_config_id = -1; + } + + int timeout = 1000; + + if (m_config_id < 0) + { + m_config_id = replicant_client_cond_follow(m_repl, "consus", cond.c_str(), + &m_config_status, &m_config_state, + &m_config_data, &m_config_data_sz); + replicant_returncode rc; + + if (replicant_client_wait(m_repl, m_config_id, -1, &rc) < 0 || + m_config_status != REPLICANT_SUCCESS) + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return; + } + + timeout = 0; + } + + if (!m_online_once) + { + online(); + m_online_once = true; + } + + replicant_returncode rc; + m_mtx.unlock(); + replicant_client_block(m_repl, timeout); + m_mtx.lock(); + + if (replicant_client_loop(m_repl, 0, &rc) < 0) + { + if (rc == REPLICANT_TIMEOUT || + rc == REPLICANT_INTERRUPTED || + rc == REPLICANT_NONE_PENDING) + { + } + else + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return; + } + } + + if (m_last_config_state < m_config_state) + { + m_last_config_valid = m_cb->new_config(m_config_data, m_config_data_sz); + m_last_config_state = m_config_state; + + if (!m_cb->has_id(m_id) && m_allow_rereg) + { + registration(); + } + else if (!m_cb->has_id(m_id)) + { + m_orphaned = true; + } + } + + if (m_cb->has_id(m_id) && + (!m_cb->is_steady_state(m_id) || m_cb->address(m_id) != m_bind_to)) + { + online(); + } +} + +bool +coordinator_link :: error() +{ + po6::threads::mutex::hold hold(&m_mtx); + return m_error; +} + +bool +coordinator_link :: orphaned() +{ + po6::threads::mutex::hold hold(&m_mtx); + return m_orphaned; +} + +bool +coordinator_link :: call(const char* func, + const char* input, size_t input_sz, + coordinator_returncode* rc) +{ + po6::threads::mutex::hold hold(&m_mtx); + return call_no_lock(func, input, input_sz, rc); +} + +void +coordinator_link :: invariant_check() +{ + if (!m_repl) + { + m_error = true; + } + else if (!m_cb) + { + m_error = true; + } + else if (m_id == comm_id()) + { + m_error = true; + } +} + +bool +coordinator_link :: call_no_lock(const char* func, + const char* input, size_t input_sz, + coordinator_returncode* rc) +{ + replicant_returncode status; + char* output = NULL; + size_t output_sz = 0; + int64_t req = replicant_client_call(m_repl, "consus", func, input, input_sz, + REPLICANT_CALL_ROBUST, + &status, &output, &output_sz); + + if (req < 0) + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return false; + } + + int64_t loop = replicant_client_wait(m_repl, req, 10000, &status); + + if (loop < 0) + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return false; + } + + assert(loop == req); + + if (status == REPLICANT_TIMEOUT || status == REPLICANT_INTERRUPTED) + { + return false; + } + else if (status != REPLICANT_SUCCESS) + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return false; + } + + if (output_sz != 2 || !output) + { + LOG(ERROR) << "coordinator failure: bad response to " << func << " request"; + return false; + } + + uint16_t x; + e::unpack16be(output, &x); + *rc = static_cast(x); + free(output); + return true; +} + +bool +coordinator_link :: registration() +{ + std::string func = m_cb->prefix() + "_register"; + std::string input; + e::packer(&input) << m_id << m_bind_to; + coordinator_returncode rc; + + if (!call_no_lock(func.c_str(), input.data(), input.size(), &rc)) + { + return false; + } + + switch (rc) + { + case COORD_SUCCESS: + return true; + case COORD_DUPLICATE: + LOG(ERROR) << "cannot register: another server already registered this identity"; + return false; + case COORD_MALFORMED: + case COORD_NOT_FOUND: + case COORD_UNINITIALIZED: + case COORD_NO_CAN_DO: + default: + LOG(ERROR) << "coordinator failure: bad response to registration request"; + return false; + } +} + +bool +coordinator_link :: online() +{ + uint64_t x; + + if (!e::generate_token(&x)) + { + return false; + } + + std::string func1 = m_cb->prefix() + "_online"; + std::string func2 = m_cb->prefix() + "_offline"; + std::string input; + e::packer(&input) << m_id << m_bind_to << x; + replicant_returncode status = REPLICANT_GARBAGE; + replicant_returncode lstatus = REPLICANT_GARBAGE; + int64_t req = replicant_client_defended_call(m_repl, + "consus", func1.c_str(), input.data(), input.size(), + func2.c_str(), input.data(), input.size(), &status); + + if (req < 0 || + replicant_client_wait(m_repl, req, 10000, &lstatus) != req || + status != REPLICANT_SUCCESS) + { + LOG(ERROR) << "coordinator failure: " << replicant_client_error_message(m_repl); + return false; + } + + return true; +} + +coordinator_link :: callback :: callback() +{ +} + +coordinator_link :: callback :: ~callback() throw () +{ +} diff --git a/common/coordinator_link.h b/common/coordinator_link.h new file mode 100644 index 0000000..19c1c63 --- /dev/null +++ b/common/coordinator_link.h @@ -0,0 +1,109 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_coordinator_link_h_ +#define consus_common_coordinator_link_h_ + +// STL +#include + +// po6 +#include +#include + +// replicant +#include + +// consus +#include "namespace.h" +#include "common/coordinator_returncode.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class coordinator_link +{ + public: + class callback; + + public: + coordinator_link(const std::string& rendezvous, + comm_id id, po6::net::location bind_to, + callback* c/*ownership not transferred*/); + ~coordinator_link() throw (); + + // registration: call these before going into steady state + // call either "initial_registration" or "establish" + // other calls are options that affect behavior + public: + // claim the unique token and establish a connection + bool initial_registration(); + // establish a connection + bool establish(); + + // claim the token again in steady state if removed; default is to set + // "orphaned" and cease further activity (expecting the process to + // self-terminated). + void allow_reregistration(); + + // maintenance: steady-state operation + public: + // keep connected to the coordinator and pull new configs + // returns when a maintenance call will return a different value than + // prior to the call, or a callback has been made on "c" + void maintain_connection(); + // a permanent error has occurred and the connection will never recover + bool error(); + // has this instance's comm_id been removed from the configuration? + bool orphaned(); + + // piggy-back on the replicant connection + public: + bool call(const char* func, const char* input, size_t input_sz, coordinator_returncode* coord); + + private: + void invariant_check(); + bool call_no_lock(const char* func, const char* input, size_t input_sz, coordinator_returncode* coord); + bool registration(); + bool online(); + + private: + po6::threads::mutex m_mtx; + replicant_client* const m_repl; + const comm_id m_id; + const po6::net::location m_bind_to; + callback* const m_cb; + int64_t m_config_id; + replicant_returncode m_config_status; + uint64_t m_config_state; + char* m_config_data; + size_t m_config_data_sz; + uint64_t m_last_config_state; + bool m_last_config_valid; + bool m_allow_rereg; + bool m_error; + bool m_orphaned; + bool m_online_once; + + private: + coordinator_link(const coordinator_link&); + coordinator_link& operator = (const coordinator_link&); +}; + +class coordinator_link::callback +{ + public: + callback(); + virtual ~callback() throw (); + + public: + virtual std::string prefix() = 0; + virtual bool new_config(const char* data, size_t data_sz) = 0; + virtual bool has_id(comm_id id) = 0; + virtual po6::net::location address(comm_id id) = 0; + virtual bool is_steady_state(comm_id id) = 0; +}; + +END_CONSUS_NAMESPACE + +#endif // consus_common_coordinator_link_h_ diff --git a/common/coordinator_returncode.h b/common/coordinator_returncode.h new file mode 100644 index 0000000..e837938 --- /dev/null +++ b/common/coordinator_returncode.h @@ -0,0 +1,33 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_coordinator_returncode_h_ +#define consus_common_coordinator_returncode_h_ + +// C++ +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +// occupies [8832, 8960) +// these are hardcoded as byte strings in coordinator/coordinator.cc +// keep them in sync +enum coordinator_returncode +{ + COORD_SUCCESS = 8832, + COORD_MALFORMED = 8833, + COORD_DUPLICATE = 8834, + COORD_NOT_FOUND = 8835, + COORD_UNINITIALIZED = 8837, + COORD_NO_CAN_DO = 8839 +}; + +std::ostream& +operator << (std::ostream& lhs, coordinator_returncode rhs); + +END_CONSUS_NAMESPACE + +#endif // consus_common_coordinator_returncode_h_ diff --git a/common/crc32c.cc b/common/crc32c.cc new file mode 100644 index 0000000..6a20dde --- /dev/null +++ b/common/crc32c.cc @@ -0,0 +1,610 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/crc32c.h" + +#define CRC_FFs 0xFFFFFFFFU + +static uint32_t +crc32c_sb8_64_bit(uint32_t p_running_crc, + const uint8_t* p_buf, + const uint32_t length, + const uint32_t init_bytes); + + +static uint32_t +crc32_software(uint32_t init_crc, const uint8_t* data, size_t n) +{ + init_crc = init_crc ^ CRC_FFs; + uintptr_t x = reinterpret_cast(data); + uint32_t init = ((x + 3) & ~3ULL) - x; + init = init > n ? n : init; + return crc32c_sb8_64_bit(init_crc, data, n, init) ^ CRC_FFs; +} + +static uint32_t +crc32_sse42_quads(uint32_t init_crc, const uint8_t* data, size_t n) +{ + uint64_t crc = init_crc ^ CRC_FFs; + const uintptr_t x = reinterpret_cast(data); + const size_t align = ((x + 7) & ~7ULL) - x; + const size_t init = align > n ? n : align; + const size_t body = (n - init) >> 3; + const size_t tail = n - (body << 3) - init; + + for (size_t i = 0; i < init; ++i) + { + __asm__ __volatile__("crc32b %2, %0\n\t" : "=r"(crc) : "0"(crc), "r"(data[i]) : ); + } + + const uint64_t* body_ptr = reinterpret_cast(data + init); + + for (size_t i = 0; i < body; ++i) + { + __asm__ __volatile__("crc32q %2, %0" : "=r"(crc) : "0"(crc), "r"(body_ptr[i]) : ); + } + + const size_t offset = n - tail; + + for (size_t i = 0; i < tail; ++i) + { + __asm__ __volatile__("crc32b %2, %0\n\t" : "=r"(crc) : "0"(crc), "r"(data[offset + i]) : ); + } + + crc ^= CRC_FFs; + return crc; +} + +typedef uint32_t (*crc32c_func_t)(uint32_t init_crc, const uint8_t* data, size_t n); + +#if defined(__i386__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%ebx, %%edi\n" \ + "cpuid\n" \ + "xchg %%edi, %%ebx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#define cpuid_t uint32_t +#elif defined (__x86_64__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#define cpuid_t uint64_t +#endif + +static crc32c_func_t +choose_crc32c() +{ +#ifdef cpuid + cpuid_t xa; + cpuid_t xb; + cpuid_t xc; + cpuid_t xd; + cpuid(xa, xb, xc, xd, 1); + + if (xc & (1<<20)) + { + return crc32_sse42_quads; + } +#endif + + return crc32_software; +} + +static crc32c_func_t crc32c_func = choose_crc32c(); + +uint32_t +consus :: crc32c(uint32_t init, const unsigned char* data, size_t n) +{ + return crc32c_func(init, data, n); +} + +/* This is Intel's slicing-by-8 implementation of CRC32. + * + * Retrieved From: http://slicing-by-8.sourceforge.net/ + * License Text: + * + * Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved + * + * This software program is licensed subject to the BSD License, + * available at http://www.opensource.org/licenses/bsd-license.html + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o32[256] = +{ + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 +}; + +/* + * end of the CRC lookup table crc_tableil8_o32 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o40[256] = +{ + 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB, 0x69CF5132, 0x7A6DC945, + 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, 0xD39EA264, 0xC03C3A13, 0xF4DB928A, 0xE7790AFD, + 0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4, + 0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C, + 0x7F8BE302, 0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, + 0xE29F20BA, 0xF13DB8CD, 0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF, + 0x404E1283, 0x53EC8AF4, 0x670B226D, 0x74A9BA1A, 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6, + 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2, 0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, + 0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8, 0xA23F3FAF, 0x96D89736, 0x857A0F41, + 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17, 0x0BCC548E, 0x186ECCF9, + 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, 0x8E585659, 0x9DFACE2E, 0xA91D66B7, 0xBABFFEC0, + 0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78, + 0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43, + 0x1D88E6BE, 0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, + 0xBF59D487, 0xACFB4CF0, 0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2, + 0x224D173F, 0x31EF8F48, 0x050827D1, 0x16AABFA6, 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A, + 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260, 0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, + 0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, 0x285D589D, 0x3BFFC0EA, 0x0F186873, 0x1CBAF004, + 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3, 0xADC95A4A, 0xBE6BC23D, + 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, 0x1798A91C, 0x043A316B, 0x30DD99F2, 0x237F0185, + 0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE, + 0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306, + 0xBB8DE87A, 0xA82F700D, 0x9CC8D894, 0x8F6A40E3, 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, + 0x26992BC2, 0x353BB3B5, 0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287, + 0x04D43CFD, 0x1776A48A, 0x23910C13, 0x30339464, 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8, + 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC, 0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, + 0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, 0x759BACA0, 0x663934D7, 0x52DE9C4E, 0x417C0439, + 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F, 0xCFCA5FF6, 0xDC68C781, + 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, 0x35D5BE23, 0x26772654, 0x12908ECD, 0x013216BA, + 0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502, + 0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B, + 0xD98EEDC6, 0xCA2C75B1, 0xFECBDD28, 0xED69455F, 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483 +}; + +/* + * end of the CRC lookup table crc_tableil8_o40 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o48[256] = +{ + 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664, 0xD1B1F617, 0x74F06469, + 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2, 0x4CA15AAC, + 0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3, + 0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726, + 0xE144FB14, 0x4405696A, 0xAE2BA919, 0x0B6A3B67, 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, + 0xD915C5D1, 0x7C5457AF, 0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8, + 0x91E6869E, 0x34A714E0, 0xDE89D493, 0x7BC846ED, 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7, + 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828, 0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, + 0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3, 0xFCFAB6BD, 0x16D476CE, 0xB395E4B0, + 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878, 0x2E85480B, 0x8BC4DA75, + 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, 0x29195949, 0x8C58CB37, 0x66760B44, 0xC337993A, + 0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF, + 0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4, + 0x1E704508, 0xBB31D776, 0x511F1705, 0xF45E857B, 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, + 0x56830647, 0xF3C29439, 0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E, + 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F, 0x84FCF8F1, 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB, + 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730, 0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, + 0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, 0x2DA8ED9C, 0x88E97FE2, 0x62C7BF91, 0xC7862DEF, + 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD, 0x2A34FCDE, 0x8F756EA0, + 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, 0x5D0A9016, 0xF84B0268, 0x1265C21B, 0xB7245065, + 0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E, + 0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB, + 0x1AC1F1DD, 0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, + 0x2290CF18, 0x87D15D66, 0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71, + 0x4C42F79A, 0xE90365E4, 0x032DA597, 0xA66C37E9, 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3, + 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C, 0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, + 0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A, 0x077FBC74, 0xED517C07, 0x4810EE79, + 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1, 0xD50042C2, 0x7041D0BC, + 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, 0x33D8A894, 0x96993AEA, 0x7CB7FA99, 0xD9F668E7, + 0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622, + 0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D, + 0xE5F54FC1, 0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8 +}; + +/* + * end of the CRC lookup table crc_tableil8_o48 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o56[256] = +{ + 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B, 0xC4451272, 0x1900B8CA, + 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, 0x8D665215, 0x5023F8AD, 0x32017194, 0xEF44DB2C, + 0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7, + 0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11, + 0xD725148B, 0x0A60BE33, 0x6842370A, 0xB5079DB2, 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, + 0x2161776D, 0xFC24DDD5, 0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7, + 0x3E41A5B6, 0xE3040F0E, 0x81268637, 0x5C632C8F, 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C, + 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69, 0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, + 0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, 0xD0846E14, 0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D, + 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A, 0x99A72E73, 0x44E284CB, + 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, 0x39E0DF29, 0xE4A57591, 0x8687FCA8, 0x5BC25610, + 0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6, + 0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6, + 0x8AC7288A, 0x57828232, 0x35A00B0B, 0xE8E5A1B3, 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, + 0x95E7FA51, 0x48A250E9, 0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B, + 0x63A399B7, 0xBEE6330F, 0xDCC4BA36, 0x0181108E, 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D, + 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006, 0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, + 0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, 0xDFC69B2A, 0x02833192, 0x60A1B8AB, 0xBDE41213, + 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349, 0x7F816A70, 0xA2C4C0C8, + 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, 0x36A22A17, 0xEBE780AF, 0x89C50996, 0x5480A32E, + 0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E, + 0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698, + 0x6CE16C89, 0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, + 0x9AA50F6F, 0x47E0A5D7, 0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5, + 0xF90696D8, 0x24433C60, 0x4661B559, 0x9B241FE1, 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12, + 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07, 0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, + 0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, 0x6B401616, 0xB605BCAE, 0xD4273597, 0x09629F2F, + 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48, 0x22635671, 0xFF26FCC9, + 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, 0x5501B3A0, 0x88441918, 0xEA669021, 0x37233A99, + 0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F, + 0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4, + 0x31035088, 0xEC46FA30, 0x8E647309, 0x5321D9B1, 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842 +}; + +/* + * end of the CRC lookup table crc_tableil8_o56 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o64[256] = +{ + 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C, 0x906761E8, 0xA8760E44, + 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, 0x2522B521, 0x1D33DA8D, 0x55006A79, 0x6D1105D5, + 0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97, + 0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406, + 0x1BA8B557, 0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, + 0xDECFBEC6, 0xE6DED16A, 0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082, + 0x948AD484, 0xAC9BBB28, 0xE4A80BDC, 0xDCB96470, 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0, + 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1, 0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, + 0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, 0xD714D41E, 0xEF05BBB2, 0xA7360B46, 0x9F2764EA, + 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023, 0x625100D7, 0x5A406F7B, + 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, 0x5836B5CD, 0x6027DA61, 0x28146A95, 0x10050539, + 0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8, + 0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD, + 0xE99ED468, 0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, + 0xA3DBBE2A, 0x9BCAD186, 0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E, + 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3, 0x2E8F054F, 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF, + 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8, 0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, + 0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, 0x4B80607D, 0x73910FD1, 0x3BA2BF25, 0x03B3D089, + 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593, 0x71E7D567, 0x49F6BACB, + 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, 0xC4A201AE, 0xFCB36E02, 0xB480DEF6, 0x8C91B15A, + 0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F, + 0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE, + 0xFA2801D8, 0xC2396E74, 0x8A0ADE80, 0xB21BB12C, 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, + 0x3F4F0A49, 0x075E65E5, 0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D, + 0x59F3BFF2, 0x61E2D05E, 0x29D160AA, 0x11C00F06, 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6, + 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497, 0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, + 0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, 0x36946091, 0x0E850F3D, 0x46B6BFC9, 0x7EA7D065, + 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC, 0x83D1B458, 0xBBC0DBF4, + 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D, 0xEA2D04E1, + 0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70, + 0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532, + 0x081E60E7, 0x300F0F4B, 0x783CBFBF, 0x402DD013, 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3 +}; + +/* + * end of the CRC lookup table crc_tableil8_o64 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o72[256] = +{ + 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E, 0x697997B4, 0x8649FCAD, + 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, 0xD2F32F68, 0x3DC34471, 0x097F8FAB, 0xE64FE4B2, + 0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93, + 0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C, + 0x85F4168D, 0x6AC47D94, 0x5E78B64E, 0xB148DD57, 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, + 0xE5F20E92, 0x0AC2658B, 0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F, + 0x45F826B3, 0xAAC84DAA, 0x9E748670, 0x7144ED69, 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E, + 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576, 0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, + 0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, 0xBCF16C9C, 0x53C10785, 0x677DCC5F, 0x884DA746, + 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A, 0x077BD440, 0xE84BBF59, + 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61, 0x48419778, + 0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67, + 0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB, + 0xEBF65579, 0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, + 0x4BFC7D58, 0xA4CC1641, 0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5, + 0x2BFA6547, 0xC4CA0E5E, 0xF076C584, 0x1F46AE9D, 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA, + 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C, 0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, + 0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, 0xCEFB98BE, 0x21CBF3A7, 0x1577387D, 0xFA475364, + 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86, 0xB57D105C, 0x5A4D7B45, + 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, 0x0EF7A880, 0xE1C7C399, 0xD57B0843, 0x3A4B635A, + 0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6, + 0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9, + 0x59F09165, 0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, + 0x39F6897A, 0xD6C6E263, 0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7, + 0x120CEC3D, 0xFD3C8724, 0xC9804CFE, 0x26B027E7, 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090, + 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8, 0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, + 0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74, 0x8FC5806D, 0xBB794BB7, 0x544920AE, + 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872, 0xDB7F53A8, 0x344F38B1, + 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04, 0x11B1061D, + 0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02, + 0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623, + 0x37F2D291, 0xD8C2B988, 0xEC7E7252, 0x034E194B, 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C +}; + +/* + * end of the CRC lookup table crc_tableil8_o72 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o80[256] = +{ + 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919, 0x75E69C41, 0x1DE5B089, + 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, 0xEBCD3882, 0x83CE144A, 0x3BCB6112, 0x53C84DDA, + 0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F, + 0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C, + 0x3D5B83BD, 0x5558AF75, 0xED5DDA2D, 0x855EF6E5, 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, + 0x73767EEE, 0x1B755226, 0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67, + 0xA100791B, 0xC90355D3, 0x7106208B, 0x19050C43, 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992, + 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110, 0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, + 0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB, 0xB754EE63, 0x0F519B3B, 0x6752B7F3, + 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330, 0x417C6668, 0x297F4AA0, + 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, 0x430C380D, 0x2B0F14C5, 0x930A619D, 0xFB094D55, + 0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006, + 0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E, + 0x09C17994, 0x61C2555C, 0xD9C72004, 0xB1C40CCC, 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, + 0xDBB77E61, 0xB3B452A9, 0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8, + 0x959A8332, 0xFD99AFFA, 0x459CDAA2, 0x2D9FF66A, 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB, + 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC, 0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, + 0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, 0x1EA33676, 0x76A01ABE, 0xCEA56FE6, 0xA6A6432E, + 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B, 0x1CD36813, 0x74D044DB, + 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, 0x82F8CCD0, 0xEAFBE018, 0x52FE9540, 0x3AFDB988, + 0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0, + 0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093, + 0x546E77EF, 0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, + 0x1A438ABC, 0x7240A674, 0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35, + 0x8FD9098E, 0xE7DA2546, 0x5FDF501E, 0x37DC7CD6, 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907, + 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185, 0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, + 0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, 0xB66236F9, 0xDE611A31, 0x66646F69, 0x0E6743A1, + 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762, 0x2849923A, 0x404ABEF2, + 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, 0x17624FE2, 0x7F61632A, 0xC7641672, 0xAF673ABA, + 0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9, + 0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C, + 0x60F48DC6, 0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F +}; + +/* + * end of the CRC lookup table crc_tableil8_o80 + */ + +/* + * The following CRC lookup table was generated automagically + * using the following model parameters: + * + * Generator Polynomial = ................. 0x1EDC6F41 + * Generator Polynomial Length = .......... 32 bits + * Reflected Bits = ....................... TRUE + * Table Generation Offset = .............. 32 bits + * Number of Slices = ..................... 8 slices + * Slice Lengths = ........................ 8 8 8 8 8 8 8 8 + * Directory Name = ....................... .\ + * File Name = ............................ 8x256_tables.c + */ + +static uint32_t crc_tableil8_o88[256] = +{ + 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A, 0xB3657823, 0xFA590504, + 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, 0x632686B7, 0x2A1AFB90, 0xF15E7CF9, 0xB86201DE, + 0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0, + 0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A, + 0x0D006599, 0x443C18BE, 0x9F789FD7, 0xD644E2F0, 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, + 0x4F3B6143, 0x06071C64, 0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447, + 0x89766C2D, 0xC04A110A, 0x1B0E9663, 0x5232EB44, 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929, + 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E, 0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, + 0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, 0x3B1D495F, 0x72213478, 0xA965B311, 0xE059CE36, + 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2, 0xEB5EB7CB, 0xA262CAEC, + 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5, 0x642FC782, + 0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358, + 0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF, + 0x553BAA71, 0x1C07D756, 0xC743503F, 0x8E7F2D18, 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, + 0x9376A71F, 0xDA4ADA38, 0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B, + 0xD14DA3C5, 0x9871DEE2, 0x4335598B, 0x0A0924AC, 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1, + 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D, 0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, + 0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, 0x572710D3, 0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA, + 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A, 0x0312E7F3, 0x4A2E9AD4, + 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, 0xD3511967, 0x9A6D6440, 0x4129E329, 0x08159E0E, + 0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9, + 0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223, + 0xBD77FA49, 0xF44B876E, 0x2F0F0007, 0x66337D20, 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, + 0xFF4CFE93, 0xB67083B4, 0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97, + 0x2E015D56, 0x673D2071, 0xBC79A718, 0xF545DA3F, 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852, + 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5, 0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, + 0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, 0x8B6AD68F, 0xC256ABA8, 0x19122CC1, 0x502E51E6, + 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72, 0x5B29281B, 0x1215553C, + 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, 0x021CBAA2, 0x4B20C785, 0x906440EC, 0xD9583DCB, + 0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911, + 0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F, + 0xE54C35A1, 0xAC704886, 0x7734CFEF, 0x3E08B2C8, 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5 +}; + +/* + * end of the CRC lookup table crc_tableil8_o88 + */ + +/** + * + * Routine Description: + * + * Computes the CRC32c checksum for the specified buffer using the slicing by 8 + * algorithm over 64 bit quantities. + * + * Arguments: + * + * p_running_crc - pointer to the initial or final remainder value + * used in CRC computations. It should be set to + * non-NULL if the mode argument is equal to CONT or END + * p_buf - the packet buffer where crc computations are being performed + * length - the length of p_buf in bytes + * init_bytes - the number of initial bytes that need to be procesed before + * aligning p_buf to multiples of 4 bytes + * mode - can be any of the following: BEGIN, CONT, END, BODY, ALIGN + * + * Return value: + * + * The computed CRC32c value + */ + +static uint32_t +crc32c_sb8_64_bit( + uint32_t p_running_crc, + const uint8_t* p_buf, + const uint32_t length, + const uint32_t init_bytes) +{ + uint32_t li; + uint32_t crc, term1, term2; + uint32_t running_length; + uint32_t end_bytes; + crc = p_running_crc; + running_length = ((length - init_bytes)/8)*8; + end_bytes = length - init_bytes - running_length; + + for(li=0; li < init_bytes; li++) + crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8); + for(li=0; li < running_length/8; li++) + { + crc ^= *(const uint32_t *)p_buf; + p_buf += 4; + term1 = crc_tableil8_o88[crc & 0x000000FF] ^ + crc_tableil8_o80[(crc >> 8) & 0x000000FF]; + term2 = crc >> 16; + crc = term1 ^ + crc_tableil8_o72[term2 & 0x000000FF] ^ + crc_tableil8_o64[(term2 >> 8) & 0x000000FF]; + term1 = crc_tableil8_o56[(*(const uint32_t *)p_buf) & 0x000000FF] ^ + crc_tableil8_o48[((*(const uint32_t *)p_buf) >> 8) & 0x000000FF]; + + term2 = (*(const uint32_t *)p_buf) >> 16; + crc = crc ^ + term1 ^ + crc_tableil8_o40[term2 & 0x000000FF] ^ + crc_tableil8_o32[(term2 >> 8) & 0x000000FF]; + p_buf += 4; + } + for(li=0; li < end_bytes; li++) + crc = crc_tableil8_o32[(crc ^ *p_buf++) & 0x000000FF] ^ (crc >> 8); + return crc; +} diff --git a/common/crc32c.h b/common/crc32c.h new file mode 100644 index 0000000..2d6bfa4 --- /dev/null +++ b/common/crc32c.h @@ -0,0 +1,21 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_crc32c_h_ +#define consus_common_crc32c_h_ + +// C +#include +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +uint32_t +crc32c(uint32_t init, const unsigned char* data, size_t n); + +END_CONSUS_NAMESPACE + +#endif // consus_common_crc32c_h_ diff --git a/common/data_center.cc b/common/data_center.cc new file mode 100644 index 0000000..a06343d --- /dev/null +++ b/common/data_center.cc @@ -0,0 +1,54 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include + +// consus +#include "common/data_center.h" + +using consus::data_center; + +data_center :: data_center() + : id() + , name() +{ +} + +data_center :: data_center(data_center_id i, const std::string& n) + : id(i) + , name(n) +{ +} + +data_center :: data_center(const data_center& other) + : id(other.id) + , name(other.name) +{ +} + +data_center :: ~data_center() throw () +{ +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const data_center& rhs) +{ + return lhs << "data_center(id=" << rhs.id.get() + << ", name=\"" << e::strescape(rhs.name) << "\")"; +} + +e::packer +consus :: operator << (e::packer lhs, const data_center& rhs) +{ + return lhs << rhs.id << e::slice(rhs.name); +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, data_center& rhs) +{ + e::slice name; + lhs = lhs >> rhs.id >> name; + rhs.name = name.str(); + return lhs; +} diff --git a/common/data_center.h b/common/data_center.h new file mode 100644 index 0000000..828246d --- /dev/null +++ b/common/data_center.h @@ -0,0 +1,38 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_data_center_h_ +#define consus_common_data_center_h_ + +// consus +#include "namespace.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class data_center +{ + public: + data_center(); + data_center(data_center_id id, const std::string& name); + data_center(const data_center& other); + ~data_center() throw (); + + public: + data_center_id id; + std::string name; +}; + +std::ostream& +operator << (std::ostream& lhs, const data_center& rhs); + +e::packer +operator << (e::packer lhs, const data_center& rhs); +e::unpacker +operator >> (e::unpacker lhs, data_center& rhs); +size_t +pack_size(const data_center& dc); + +END_CONSUS_NAMESPACE + +#endif // consus_common_data_center_h_ diff --git a/common/ids.cc b/common/ids.cc new file mode 100644 index 0000000..7db2ee9 --- /dev/null +++ b/common/ids.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/ids.h" + +#define CREATE_ID(TYPE) \ + std::ostream& \ + operator << (std::ostream& lhs, const TYPE ## _id& rhs) \ + { \ + return lhs << #TYPE "(" << rhs.get() << ")"; \ + } \ + e::packer \ + operator << (e::packer pa, const TYPE ## _id& rhs) \ + { \ + return pa << rhs.get(); \ + } \ + e::unpacker \ + operator >> (e::unpacker up, TYPE ## _id& rhs) \ + { \ + uint64_t id; \ + up = up >> id; \ + rhs = TYPE ## _id(id); \ + return up; \ + } + +BEGIN_CONSUS_NAMESPACE + +CREATE_ID(abstract) +CREATE_ID(cluster) +CREATE_ID(version) +CREATE_ID(comm) +CREATE_ID(paxos_group) +CREATE_ID(data_center) + +END_CONSUS_NAMESPACE diff --git a/common/ids.h b/common/ids.h new file mode 100644 index 0000000..d7acd82 --- /dev/null +++ b/common/ids.h @@ -0,0 +1,71 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_ids_h_ +#define consus_common_ids_h_ + +// C +#include + +// C++ +#include + +// e +#include + +// consus +#include "namespace.h" + +// An ID is a simple wrapper around uint64_t in order to prevent devs from +// accidently using one type of ID as another. + +#define OPERATOR(TYPE, OP) \ + inline bool \ + operator OP (const TYPE ## _id& lhs, const TYPE ## _id& rhs) \ + { \ + return lhs.get() OP rhs.get(); \ + } +#define CREATE_ID(TYPE) \ + class TYPE ## _id \ + { \ + public: \ + static uint64_t hash(const TYPE ## _id& x) { return x.get(); } \ + TYPE ## _id() : m_id(0) {} \ + explicit TYPE ## _id(uint64_t id) : m_id(id) {} \ + public: \ + uint64_t get() const { return m_id; } \ + private: \ + uint64_t m_id; \ + }; \ + std::ostream& \ + operator << (std::ostream& lhs, const TYPE ## _id& rhs); \ + inline size_t \ + pack_size(const TYPE ## _id&) \ + { \ + return sizeof(uint64_t); \ + } \ + e::packer \ + operator << (e::packer pa, const TYPE ## _id& rhs); \ + e::unpacker \ + operator >> (e::unpacker up, TYPE ## _id& rhs); \ + OPERATOR(TYPE, <) \ + OPERATOR(TYPE, <=) \ + OPERATOR(TYPE, ==) \ + OPERATOR(TYPE, !=) \ + OPERATOR(TYPE, >=) \ + OPERATOR(TYPE, >) + +BEGIN_CONSUS_NAMESPACE + +CREATE_ID(abstract) +CREATE_ID(cluster) +CREATE_ID(version) +CREATE_ID(comm) +CREATE_ID(paxos_group) +CREATE_ID(data_center) + +END_CONSUS_NAMESPACE + +#undef OPERATOR +#undef CREATE_ID +#endif // consus_common_ids_h_ diff --git a/common/kvs.cc b/common/kvs.cc new file mode 100644 index 0000000..591a9ce --- /dev/null +++ b/common/kvs.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/kvs.h" + +using consus::kvs; + +kvs :: kvs() + : id() + , bind_to() + , dc() +{ +} + +kvs :: kvs(comm_id i, const po6::net::location& b) + : id(i) + , bind_to(b) + , dc() +{ +} + +kvs :: kvs(const kvs& other) + : id(other.id) + , bind_to(other.bind_to) + , dc(other.dc) +{ +} + +kvs :: ~kvs() throw () +{ +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const kvs& rhs) +{ + return lhs << "kvs(id=" << rhs.id.get() << ", bind_to=" << rhs.bind_to << ", dc=" << rhs.dc.get() << ")"; +} + +e::packer +consus :: operator << (e::packer lhs, const kvs& rhs) +{ + return lhs << rhs.id << rhs.bind_to << rhs.dc; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, kvs& rhs) +{ + return lhs >> rhs.id >> rhs.bind_to >> rhs.dc; +} diff --git a/common/kvs.h b/common/kvs.h new file mode 100644 index 0000000..1fe090e --- /dev/null +++ b/common/kvs.h @@ -0,0 +1,42 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_kvs_h_ +#define consus_common_kvs_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class kvs +{ + public: + kvs(); + kvs(comm_id id, const po6::net::location& bind_to); + kvs(const kvs& other); + ~kvs() throw (); + + public: + comm_id id; + po6::net::location bind_to; + data_center_id dc; +}; + +std::ostream& +operator << (std::ostream& lhs, const kvs& rhs); + +e::packer +operator << (e::packer lhs, const kvs& rhs); +e::unpacker +operator >> (e::unpacker lhs, kvs& rhs); +size_t +pack_size(const kvs& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_kvs_h_ diff --git a/common/kvs_configuration.cc b/common/kvs_configuration.cc new file mode 100644 index 0000000..ce13399 --- /dev/null +++ b/common/kvs_configuration.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include + +// consus +#include "common/kvs_configuration.h" + +e::unpacker +consus :: kvs_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* kvss) +{ + return up >> *cid >> *vid >> *flags >> *kvss; +} + +std::string +consus :: kvs_configuration(const cluster_id& cid, + const version_id& vid, + uint64_t, + const std::vector& kvss) +{ + std::ostringstream ostr; + ostr << cid << "\n" + << vid << "\n"; + + if (kvss.empty()) + { + ostr << "no key-value stores\n"; + } + else if (kvss.size() == 1) + { + ostr << "1 key-value store:\n"; + } + else + { + ostr << kvss.size() << " key-value stores:\n"; + } + + for (size_t i = 0; i < kvss.size(); ++i) + { + ostr << kvss[i] << "\n"; + } + + return ostr.str(); +} diff --git a/common/kvs_configuration.h b/common/kvs_configuration.h new file mode 100644 index 0000000..b2a2c4b --- /dev/null +++ b/common/kvs_configuration.h @@ -0,0 +1,26 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_kvs_configuration_h_ +#define consus_common_kvs_configuration_h_ + +// consus +#include "namespace.h" +#include "common/ids.h" +#include "common/kvs_state.h" + +BEGIN_CONSUS_NAMESPACE + +e::unpacker kvs_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* kvss); +std::string kvs_configuration(const cluster_id& cid, + const version_id& vid, + uint64_t flags, + const std::vector& kvss); + +END_CONSUS_NAMESPACE + +#endif // consus_common_kvs_configuration_h_ diff --git a/common/kvs_state.cc b/common/kvs_state.cc new file mode 100644 index 0000000..a78f034 --- /dev/null +++ b/common/kvs_state.cc @@ -0,0 +1,108 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/macros.h" +#include "common/kvs_state.h" + +using consus::kvs_state; + +const char* +kvs_state :: to_string(state_t state) +{ + switch (state) + { + CSTRINGIFY(REGISTERED); + CSTRINGIFY(ONLINE); + CSTRINGIFY(OFFLINE); + default: + return "UNKNOWN"; + } +} + +kvs_state :: kvs_state() + : kv() + , state() + , nonce() +{ +} + +kvs_state :: kvs_state(const kvs& k) + : kv(k) + , state(REGISTERED) + , nonce() +{ +} + +kvs_state :: kvs_state(const kvs_state& other) + : kv(other.kv) + , state(other.state) + , nonce(other.nonce) +{ +} + +kvs_state :: ~kvs_state() throw () +{ +} + +kvs_state& +kvs_state :: operator = (const kvs_state& rhs) +{ + if (this != &rhs) + { + kv = rhs.kv; + state = rhs.state; + nonce = rhs.nonce; + } + + return *this; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const kvs_state& rhs) +{ + return lhs << "kvs(id=" << rhs.kv.id.get() + << ", bind_to=" << rhs.kv.bind_to + << ", state=" << rhs.state + << ", nonce=" << rhs.nonce + << ")"; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const kvs_state::state_t& rhs) +{ + switch (rhs) + { + STRINGIFYNS(kvs_state, REGISTERED); + STRINGIFYNS(kvs_state, ONLINE); + STRINGIFYNS(kvs_state, OFFLINE); + default: + lhs << "unknown"; + } + + return lhs; +} + +e::packer +consus :: operator << (e::packer lhs, const kvs_state& rhs) +{ + return lhs << rhs.kv << rhs.state << rhs.nonce; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, kvs_state& rhs) +{ + return lhs >> rhs.kv >> rhs.state >> rhs.nonce; +} + +e::packer +consus :: operator << (e::packer lhs, const kvs_state::state_t& rhs) +{ + return lhs << e::pack_uint8(rhs); +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, kvs_state::state_t& rhs) +{ + return lhs >> e::unpack_uint8(rhs); +} diff --git a/common/kvs_state.h b/common/kvs_state.h new file mode 100644 index 0000000..6bd490f --- /dev/null +++ b/common/kvs_state.h @@ -0,0 +1,67 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_kvs_state_h_ +#define consus_common_kvs_state_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/kvs.h" + +BEGIN_CONSUS_NAMESPACE + +class kvs_state +{ + public: + enum state_t + { + REGISTERED = 1, + ONLINE = 2, + OFFLINE = 3 + }; + static const char* to_string(state_t state); + + public: + kvs_state(); + kvs_state(const kvs& tx); + kvs_state(const kvs_state& other); + ~kvs_state() throw (); + + public: + comm_id id() const { return kv.id; } + po6::net::location bind_to() const { return kv.bind_to; } + + public: + kvs_state& operator = (const kvs_state& rhs); + + public: + kvs kv; + state_t state; + uint64_t nonce; +}; + +std::ostream& +operator << (std::ostream& lhs, const kvs_state& rhs); +std::ostream& +operator << (std::ostream& lhs, const kvs_state::state_t& rhs); + +e::packer +operator << (e::packer lhs, const kvs_state& rhs); +e::unpacker +operator >> (e::unpacker lhs, kvs_state& rhs); +size_t +pack_size(const kvs_state& p); + +e::packer +operator << (e::packer lhs, const kvs_state::state_t& rhs); +e::unpacker +operator >> (e::unpacker lhs, kvs_state::state_t& rhs); +size_t +pack_size(const kvs_state::state_t& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_txman_state_h_ diff --git a/common/macros.h b/common/macros.h new file mode 100644 index 0000000..e62ef8d --- /dev/null +++ b/common/macros.h @@ -0,0 +1,16 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef kvs_common_macros_h_ +#define kvs_common_macros_h_ + +#define XSTR(x) #x +#define STR(x) XSTR(x) +#define STRINGIFY(x) case (x): lhs << XSTR(x); break +#define STRINGIFYNS(ns, x) case (ns::x): lhs << XSTR(x); break +#define CSTRINGIFY(x) case (x): return XSTR(x); + +#define _CONCAT(x, y) x ## y +#define CONCAT(x, y) _CONCAT(x, y) + +#endif // kvs_common_macros_h_ diff --git a/common/network_msgtype.cc b/common/network_msgtype.cc new file mode 100644 index 0000000..31ceabc --- /dev/null +++ b/common/network_msgtype.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C++ +#include + +// e +#include + +// consus +#include "common/macros.h" +#include "common/network_msgtype.h" + +std::ostream& +consus :: operator << (std::ostream& lhs, network_msgtype rhs) +{ + switch (rhs) + { + STRINGIFY(CLIENT_RESPONSE); + STRINGIFY(TXMAN_BEGIN); + STRINGIFY(TXMAN_READ); + STRINGIFY(TXMAN_WRITE); + STRINGIFY(TXMAN_COMMIT); + STRINGIFY(TXMAN_ABORT); + STRINGIFY(TXMAN_PAXOS_2A); + STRINGIFY(TXMAN_PAXOS_2B); + STRINGIFY(LV_VOTE_1A); + STRINGIFY(LV_VOTE_1B); + STRINGIFY(LV_VOTE_2A); + STRINGIFY(LV_VOTE_2B); + STRINGIFY(LV_VOTE_LEARN); + STRINGIFY(COMMIT_RECORD); + STRINGIFY(KVS_RD_LOCK); + STRINGIFY(KVS_RD_LOCKED); + STRINGIFY(KVS_RD_UNLOCK); + STRINGIFY(KVS_RD_UNLOCKED); + STRINGIFY(KVS_WR_BEGIN); + STRINGIFY(KVS_WR_BEGUN); + STRINGIFY(KVS_WR_FINISH); + STRINGIFY(KVS_WR_CANCEL); + STRINGIFY(KVS_WR_FINISHED); + STRINGIFY(CONSUS_NOP); + default: + lhs << "unknown msgtype"; + } + + return lhs; +} + +e::packer +consus :: operator << (e::packer lhs, const network_msgtype& rhs) +{ + return lhs << e::pack_uint16(rhs); +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, network_msgtype& rhs) +{ + return lhs >> e::unpack_uint16(rhs); +} + +size_t +consus :: pack_size(const network_msgtype&) +{ + return sizeof(uint16_t); +} diff --git a/common/network_msgtype.h b/common/network_msgtype.h new file mode 100644 index 0000000..4e61a83 --- /dev/null +++ b/common/network_msgtype.h @@ -0,0 +1,66 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_network_msgtype_h_ +#define consus_common_network_msgtype_h_ + +// C++ +#include + +// e +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +/* network_returncode occupies [7168, 7936) */ +enum network_msgtype +{ + CLIENT_RESPONSE = 7423, + + TXMAN_BEGIN = 7424, + TXMAN_READ = 7425, + TXMAN_WRITE = 7426, + TXMAN_COMMIT = 7427, + TXMAN_ABORT = 7428, + + TXMAN_PAXOS_2A = 7439, + TXMAN_PAXOS_2B = 7433, + + LV_VOTE_1A = 7900, + LV_VOTE_1B = 7901, + LV_VOTE_2A = 7902, + LV_VOTE_2B = 7903, + LV_VOTE_LEARN = 7904, + + COMMIT_RECORD = 7905, + + KVS_RD_LOCK = 7800, + KVS_RD_LOCKED = 7802, + KVS_RD_UNLOCK = 7803, + KVS_RD_UNLOCKED = 7804, + + KVS_WR_BEGIN = 7805, + KVS_WR_BEGUN = 7807, + KVS_WR_FINISH = 7808, + KVS_WR_CANCEL = 7809, + KVS_WR_FINISHED = 7810, + + CONSUS_NOP = 7935 +}; + +std::ostream& +operator << (std::ostream& lhs, network_msgtype rhs); + +e::packer +operator << (e::packer lhs, const network_msgtype& rhs); +e::unpacker +operator >> (e::unpacker lhs, network_msgtype& rhs); +size_t +pack_size(const network_msgtype& rhs); + +END_CONSUS_NAMESPACE + +#endif // consus_common_network_msgtype_h_ diff --git a/common/paxos_group.cc b/common/paxos_group.cc new file mode 100644 index 0000000..5d94715 --- /dev/null +++ b/common/paxos_group.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/macros.h" +#include "common/paxos_group.h" + +using consus::paxos_group; + +paxos_group :: paxos_group() + : id() + , dc() + , members_sz(0) +{ + for (unsigned i = 0; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + members[i] = comm_id(); + } +} + +paxos_group :: paxos_group(const paxos_group& other) + : id(other.id) + , dc(other.dc) + , members_sz(other.members_sz) +{ + for (unsigned i = 0; i < members_sz; ++i) + { + members[i] = other.members[i]; + } + + for (unsigned i = members_sz; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + members[i] = comm_id(); + } +} + +paxos_group :: ~paxos_group() throw () +{ +} + +unsigned +paxos_group :: quorum() const +{ + return members_sz / 2 + 1; +} + +unsigned +paxos_group :: index(comm_id c) const +{ + unsigned i = 0; + + for (; i < members_sz; ++i) + { + if (members[i] == c) + { + break; + } + } + + return i; +} + +paxos_group& +paxos_group :: operator = (const paxos_group& rhs) +{ + if (this != &rhs) + { + id = rhs.id; + dc = rhs.dc; + members_sz = rhs.members_sz; + + for (unsigned i = 0; i < members_sz; ++i) + { + members[i] = rhs.members[i]; + } + + for (unsigned i = members_sz; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + members[i] = comm_id(); + } + } + + return *this; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const paxos_group& rhs) +{ + lhs << "paxos_group(id=" << rhs.id.get() << ", dc=" << rhs.dc.get() << ", ["; + + for (unsigned i = 0; i < rhs.members_sz; ++i) + { + if (i > 0) + { + lhs << ", "; + } + + lhs << rhs.members[i].get(); + } + + lhs << "])"; + return lhs; +} + +e::packer +consus :: operator << (e::packer pa, const paxos_group& rhs) +{ + pa = pa << rhs.id << rhs.dc << e::pack_varint(rhs.members_sz); + + for (unsigned i = 0; i < rhs.members_sz; ++i) + { + pa = pa << rhs.members[i]; + } + + return pa; +} + +e::unpacker +consus :: operator >> (e::unpacker up, paxos_group& rhs) +{ + uint64_t sz = 0; + up = up >> rhs.id >> rhs.dc >> e::unpack_varint(sz); + + if (sz > CONSUS_MAX_REPLICATION_FACTOR) + { + return e::unpacker::error_out(); + } + + rhs.members_sz = sz; + + for (unsigned i = 0; i < rhs.members_sz; ++i) + { + up = up >> rhs.members[i]; + } + + for (unsigned i = rhs.members_sz; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + rhs.members[i] = comm_id(); + } + + return up; +} diff --git a/common/paxos_group.h b/common/paxos_group.h new file mode 100644 index 0000000..3ff4727 --- /dev/null +++ b/common/paxos_group.h @@ -0,0 +1,47 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_paxos_group_h_ +#define consus_common_paxos_group_h_ + +// consus +#include "namespace.h" +#include "common/constants.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class paxos_group +{ + public: + paxos_group(); + paxos_group(const paxos_group& other); + ~paxos_group() throw (); + + public: + unsigned quorum() const; + unsigned index(comm_id id) const; + + public: + paxos_group& operator = (const paxos_group& rhs); + + public: + paxos_group_id id; + data_center_id dc; + unsigned members_sz; + comm_id members[CONSUS_MAX_REPLICATION_FACTOR]; +}; + +std::ostream& +operator << (std::ostream& lhs, const paxos_group& rhs); + +e::packer +operator << (e::packer lhs, const paxos_group& rhs); +e::unpacker +operator >> (e::unpacker lhs, paxos_group& rhs); +size_t +pack_size(const paxos_group& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_paxos_group_h_ diff --git a/common/transaction_group.cc b/common/transaction_group.cc new file mode 100644 index 0000000..19cca3a --- /dev/null +++ b/common/transaction_group.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/transaction_group.h" + +using consus::transaction_group; + +transaction_group :: transaction_group() + : group() + , txid() +{ +} + +transaction_group :: transaction_group(transaction_id t) + : group(t.group) + , txid(t) +{ +} + +transaction_group :: transaction_group(paxos_group_id g, transaction_id t) + : group(g) + , txid(t) +{ +} + +transaction_group :: transaction_group(const transaction_group& other) + : group(other.group) + , txid(other.txid) +{ +} + +transaction_group :: ~transaction_group() throw () +{ +} + +size_t +transaction_group :: hash() const +{ + e::compat::hash h; + return h(group.get()) ^ txid.hash(); +} + +bool +consus :: operator == (const transaction_group& lhs, const transaction_group& rhs) +{ + return lhs.group == rhs.group && lhs.txid == rhs.txid; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const transaction_group& rhs) +{ + return lhs << "transaction_group(executing=" + << rhs.group << ", originating=" + << rhs.txid.group << ", number=" + << rhs.txid.number << ")"; +} + +e::packer +consus :: operator << (e::packer pa, const transaction_group& rhs) +{ + return pa << rhs.group << rhs.txid; +} + +e::unpacker +consus :: operator >> (e::unpacker up, transaction_group& rhs) +{ + return up >> rhs.group >> rhs.txid; +} + +size_t +consus :: pack_size(const transaction_group& tg) +{ + return pack_size(tg.group) + pack_size(tg.txid); +} diff --git a/common/transaction_group.h b/common/transaction_group.h new file mode 100644 index 0000000..f3b4875 --- /dev/null +++ b/common/transaction_group.h @@ -0,0 +1,64 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_transaction_group_h_ +#define consus_common_transaction_group_h_ + +// consus +#include "namespace.h" +#include "common/transaction_id.h" + +BEGIN_CONSUS_NAMESPACE + +class transaction_group +{ + public: + static uint64_t hash(const transaction_group& tg) { return tg.hash(); } + + public: + transaction_group(); + explicit transaction_group(transaction_id t); + transaction_group(paxos_group_id g, transaction_id t); + transaction_group(const transaction_group& other); + ~transaction_group() throw (); + + public: + size_t hash() const; + + public: + paxos_group_id group; + transaction_id txid; +}; + +bool +operator == (const transaction_group& lhs, const transaction_group& rhs); +inline bool +operator != (const transaction_group& lhs, const transaction_group& rhs) +{ return !(lhs == rhs); } + +std::ostream& +operator << (std::ostream& lhs, const transaction_group& rhs); + +e::packer +operator << (e::packer pa, const transaction_group& rhs); +e::unpacker +operator >> (e::unpacker up, transaction_group& rhs); +size_t +pack_size(const transaction_group& tg); + +END_CONSUS_NAMESPACE + +BEGIN_E_COMPAT_NAMESPACE + +template <> +struct hash +{ + size_t operator()(const consus::transaction_group& kr) const + { + return kr.hash(); + } +}; + +END_E_COMPAT_NAMESPACE + +#endif // consus_common_transaction_group_h_ diff --git a/common/transaction_id.cc b/common/transaction_id.cc new file mode 100644 index 0000000..55f424a --- /dev/null +++ b/common/transaction_id.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/transaction_id.h" + +using consus::transaction_id; + +transaction_id :: transaction_id() + : group() + , number(0) +{ +} + +transaction_id :: transaction_id(paxos_group_id g, uint64_t n) + : group(g) + , number(n) +{ +} + +transaction_id :: transaction_id(const transaction_id& other) + : group(other.group) + , number(other.number) +{ +} + +transaction_id :: ~transaction_id() throw () +{ +} + +size_t +transaction_id :: hash() const +{ + e::compat::hash h; + return h(number) ^ h(group.get()); +} + +bool +consus :: operator == (const transaction_id& lhs, const transaction_id& rhs) +{ + return lhs.group == rhs.group && + lhs.number == rhs.number; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const transaction_id& rhs) +{ + return lhs << "transaction_id(group=" + << rhs.group.get() << ", number=" + << rhs.number << ")"; +} + +e::packer +consus :: operator << (e::packer pa, const transaction_id& rhs) +{ + return pa << rhs.group << rhs.number; +} + +e::unpacker +consus :: operator >> (e::unpacker up, transaction_id& rhs) +{ + return up >> rhs.group >> rhs.number; +} + +size_t +consus :: pack_size(const transaction_id& x) +{ + return pack_size(x.group) + sizeof(uint64_t); +} diff --git a/common/transaction_id.h b/common/transaction_id.h new file mode 100644 index 0000000..88a32cd --- /dev/null +++ b/common/transaction_id.h @@ -0,0 +1,69 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_transaction_id_h_ +#define consus_common_transaction_id_h_ + +// C +#include + +// C++ +#include + +// e +#include + +// consus +#include "namespace.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class transaction_id +{ + public: + transaction_id(); + transaction_id(paxos_group_id g, uint64_t number); + transaction_id(const transaction_id& other); + ~transaction_id() throw (); + + public: + size_t hash() const; + + public: + paxos_group_id group; + uint64_t number; +}; + +bool +operator == (const transaction_id& lhs, const transaction_id& rhs); +inline bool +operator != (const transaction_id& lhs, const transaction_id& rhs) +{ return !(lhs == rhs); } + +std::ostream& +operator << (std::ostream& lhs, const transaction_id& rhs); + +e::packer +operator << (e::packer pa, const transaction_id& rhs); +e::unpacker +operator >> (e::unpacker up, transaction_id& rhs); +size_t +pack_size(const transaction_id& txid); + +END_CONSUS_NAMESPACE + +BEGIN_E_COMPAT_NAMESPACE + +template <> +struct hash +{ + size_t operator()(const consus::transaction_id& kr) const + { + return kr.hash(); + } +}; + +END_E_COMPAT_NAMESPACE + +#endif // consus_common_transaction_id_h_ diff --git a/common/txman.cc b/common/txman.cc new file mode 100644 index 0000000..b2393e2 --- /dev/null +++ b/common/txman.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/txman.h" + +using consus::txman; + +txman :: txman() + : id() + , bind_to() + , dc() +{ +} + +txman :: txman(comm_id i, const po6::net::location& b) + : id(i) + , bind_to(b) + , dc() +{ +} + +txman :: txman(const txman& other) + : id(other.id) + , bind_to(other.bind_to) + , dc(other.dc) +{ +} + +txman :: ~txman() throw () +{ +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const txman& rhs) +{ + return lhs << "txman(id=" << rhs.id.get() << ", bind_to=" << rhs.bind_to << ", dc=" << rhs.dc.get() << ")"; +} + +e::packer +consus :: operator << (e::packer lhs, const txman& rhs) +{ + return lhs << rhs.id << rhs.bind_to << rhs.dc; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, txman& rhs) +{ + return lhs >> rhs.id >> rhs.bind_to >> rhs.dc; +} diff --git a/common/txman.h b/common/txman.h new file mode 100644 index 0000000..e06d0e3 --- /dev/null +++ b/common/txman.h @@ -0,0 +1,42 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_txman_h_ +#define consus_common_txman_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/ids.h" + +BEGIN_CONSUS_NAMESPACE + +class txman +{ + public: + txman(); + txman(comm_id id, const po6::net::location& bind_to); + txman(const txman& other); + ~txman() throw (); + + public: + comm_id id; + po6::net::location bind_to; + data_center_id dc; +}; + +std::ostream& +operator << (std::ostream& lhs, const txman& rhs); + +e::packer +operator << (e::packer lhs, const txman& rhs); +e::unpacker +operator >> (e::unpacker lhs, txman& rhs); +size_t +pack_size(const txman& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_txman_h_ diff --git a/common/txman_configuration.cc b/common/txman_configuration.cc new file mode 100644 index 0000000..c5643bf --- /dev/null +++ b/common/txman_configuration.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include + +// consus +#include "common/txman_configuration.h" + +e::unpacker +consus :: txman_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* dcs, + std::vector* txmans, + std::vector* txman_groups, + std::vector* kvss) +{ + return up >> *cid >> *vid >> *flags >> *dcs >> *txmans >> *txman_groups >> *kvss; +} + +std::string +consus :: txman_configuration(const cluster_id& cid, + const version_id& vid, + uint64_t, + const std::vector& dcs, + const std::vector& txmans, + const std::vector& txman_groups, + const std::vector& kvss) +{ + std::ostringstream ostr; + ostr << cid << "\n" + << vid << "\n"; + + if (dcs.empty()) + { + ostr << "default data center only\n"; + } + else if (dcs.size() == 1) + { + ostr << "1 configured data center:\n"; + } + else + { + ostr << dcs.size() << " configured data centers:\n"; + } + + for (size_t i = 0; i < dcs.size(); ++i) + { + ostr << dcs[i] << "\n"; + } + + if (txmans.empty()) + { + ostr << "no transaction managers\n"; + } + else if (txmans.size() == 1) + { + ostr << "1 transaction manager:\n"; + } + else + { + ostr << txmans.size() << " transaction managers:\n"; + } + + for (size_t i = 0; i < txmans.size(); ++i) + { + ostr << txmans[i] << "\n"; + } + + if (txman_groups.empty()) + { + ostr << "no paxos groups\n"; + } + else if (txman_groups.size() == 1) + { + ostr << "1 paxos group:\n"; + } + else + { + ostr << txman_groups.size() << " paxos groups:\n"; + } + + for (size_t i = 0; i < txman_groups.size(); ++i) + { + ostr << txman_groups[i] << "\n"; + } + + if (kvss.empty()) + { + ostr << "no key value stores\n"; + } + else if (kvss.size() == 1) + { + ostr << "1 key value store\n"; + } + else + { + ostr << kvss.size() << " key value stores:\n"; + } + + for (size_t i = 0; i < kvss.size(); ++i) + { + ostr << kvss[i] << "\n"; + } + + return ostr.str(); +} diff --git a/common/txman_configuration.h b/common/txman_configuration.h new file mode 100644 index 0000000..2295c3f --- /dev/null +++ b/common/txman_configuration.h @@ -0,0 +1,35 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_txman_configuration_h_ +#define consus_common_txman_configuration_h_ + +// consus +#include "namespace.h" +#include "common/data_center.h" +#include "common/ids.h" +#include "common/kvs.h" +#include "common/paxos_group.h" +#include "common/txman_state.h" + +BEGIN_CONSUS_NAMESPACE + +e::unpacker txman_configuration(e::unpacker up, + cluster_id* cid, + version_id* vid, + uint64_t* flags, + std::vector* dcs, + std::vector* txmans, + std::vector* txman_groups, + std::vector* kvss); +std::string txman_configuration(const cluster_id& cid, + const version_id& vid, + uint64_t flags, + const std::vector& dcs, + const std::vector& txmans, + const std::vector& txman_groups, + const std::vector& kvss); + +END_CONSUS_NAMESPACE + +#endif // consus_common_txman_configuration_h_ diff --git a/common/txman_state.cc b/common/txman_state.cc new file mode 100644 index 0000000..2683043 --- /dev/null +++ b/common/txman_state.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/macros.h" +#include "common/txman_state.h" + +using consus::txman_state; + +const char* +txman_state :: to_string(state_t state) +{ + switch (state) + { + CSTRINGIFY(REGISTERED); + CSTRINGIFY(ONLINE); + CSTRINGIFY(OFFLINE); + default: + return "UNKNOWN"; + } +} + +txman_state :: txman_state() + : tx() + , state() + , nonce() +{ +} + +txman_state :: txman_state(const txman& t) + : tx(t) + , state(REGISTERED) + , nonce() +{ +} + +txman_state :: txman_state(const txman_state& other) + : tx(other.tx) + , state(other.state) + , nonce(other.nonce) +{ +} + +txman_state :: ~txman_state() throw () +{ +} + +txman_state& +txman_state :: operator = (const txman_state& rhs) +{ + if (this != &rhs) + { + tx = rhs.tx; + state = rhs.state; + nonce = rhs.nonce; + } + + return *this; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const txman_state& rhs) +{ + return lhs << "txman(id=" << rhs.tx.id.get() + << ", bind_to=" << rhs.tx.bind_to + << ", dc=" << rhs.tx.dc.get() + << ", state=" << rhs.state + << ", nonce=" << rhs.nonce + << ")"; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const txman_state::state_t& rhs) +{ + switch (rhs) + { + STRINGIFYNS(txman_state, REGISTERED); + STRINGIFYNS(txman_state, ONLINE); + STRINGIFYNS(txman_state, OFFLINE); + default: + lhs << "unknown"; + } + + return lhs; +} + +e::packer +consus :: operator << (e::packer lhs, const txman_state& rhs) +{ + return lhs << rhs.tx << rhs.state << rhs.nonce; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, txman_state& rhs) +{ + return lhs >> rhs.tx >> rhs.state >> rhs.nonce; +} + +e::packer +consus :: operator << (e::packer lhs, const txman_state::state_t& rhs) +{ + return lhs << e::pack_uint8(rhs); +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, txman_state::state_t& rhs) +{ + return lhs >> e::unpack_uint8(rhs); +} diff --git a/common/txman_state.h b/common/txman_state.h new file mode 100644 index 0000000..e384004 --- /dev/null +++ b/common/txman_state.h @@ -0,0 +1,67 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_common_txman_state_h_ +#define consus_common_txman_state_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/txman.h" + +BEGIN_CONSUS_NAMESPACE + +class txman_state +{ + public: + enum state_t + { + REGISTERED = 1, + ONLINE = 2, + OFFLINE = 3 + }; + static const char* to_string(state_t state); + + public: + txman_state(); + txman_state(const txman& tx); + txman_state(const txman_state& other); + ~txman_state() throw (); + + public: + comm_id id() const { return tx.id; } + po6::net::location bind_to() const { return tx.bind_to; } + + public: + txman_state& operator = (const txman_state& rhs); + + public: + txman tx; + state_t state; + uint64_t nonce; +}; + +std::ostream& +operator << (std::ostream& lhs, const txman_state& rhs); +std::ostream& +operator << (std::ostream& lhs, const txman_state::state_t& rhs); + +e::packer +operator << (e::packer lhs, const txman_state& rhs); +e::unpacker +operator >> (e::unpacker lhs, txman_state& rhs); +size_t +pack_size(const txman_state& p); + +e::packer +operator << (e::packer lhs, const txman_state::state_t& rhs); +e::unpacker +operator >> (e::unpacker lhs, txman_state::state_t& rhs); +size_t +pack_size(const txman_state::state_t& p); + +END_CONSUS_NAMESPACE + +#endif // consus_common_txman_state_h_ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d08fb17 --- /dev/null +++ b/configure.ac @@ -0,0 +1,55 @@ +# Copyright (c) 2015, Robert Escriva +# All rights reserved. + +AC_PREREQ([2.61]) +AC_INIT([consus], [0.0.dev], [robert@rescrv.net]) +m4_define([serial_tests], [ + m4_esyscmd([case `automake --version | head -n 1` in + *1.11*);; + *) echo serial-tests;; + esac]) +]) +AM_INIT_AUTOMAKE(foreign serial_tests subdir-objects dist-bzip2) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_PATH_PYTHON([2.6]) +LT_PREREQ([2.2]) +LT_INIT +AC_CONFIG_SRCDIR([include/consus.h]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_LANG(C++) + +ANAL_WARNINGS + +AC_CHECK_HEADER([popt.h],,[AC_MSG_ERROR([ +------------------------------------------------- +Consus relies upon the popt library. +Please install popt to continue. +-------------------------------------------------])]) +AC_ARG_VAR(POPT_LIBS, [linker flags for popt]) +AS_IF([test "x$POPT_LIBS" = x], [POPT_LIBS="-lpopt"]) + +AC_CHECK_HEADER([glog/logging.h],,[AC_MSG_ERROR([ +------------------------------------------------- +Consus relies upon the glog library. +Please install glog to continue. +-------------------------------------------------])]) +AC_ARG_VAR(GLOG_LIBS, [linker flags for glog]) +AS_IF([test "x$GLOG_LIBS" = x], [GLOG_LIBS="-lglog"]) + +PKG_CHECK_MODULES([PO6], [libpo6 >= 0.8]) +PKG_CHECK_MODULES([E], [libe >= 0.11]) +PKG_CHECK_MODULES([TREADSTONE], [libtreadstone >= 0.0]) +PKG_CHECK_MODULES([BUSYBEE], [busybee >= 0.7]) +PKG_CHECK_MODULES([REPLICANT], [replicant >= 0.8]) + +AC_PYTHON_DEVEL([>= '2.6']) +AS_CASE([$PYTHON_VERSION], [3*], [pythonsym=PyInit_], [2*], [pythonsym=init], []) +AC_SUBST([PYTHON_SYMBOL], [${pythonsym}]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/consus-debug.cc b/consus-debug.cc new file mode 100644 index 0000000..6306a6b --- /dev/null +++ b/consus-debug.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// STL +#include + +// e +#include + +int +main(int argc, const char* argv[]) +{ + std::vector cmds; + cmds.push_back(e::subcommand("client-configuration", "Show the client configuration")); + cmds.push_back(e::subcommand("txman-configuration", "Show the transaction manager configuration")); + cmds.push_back(e::subcommand("kvs-configuration", "Show the key value store configuration")); + return dispatch_to_subcommands(argc, argv, + "consus debug", "Consus", + PACKAGE_VERSION, + "consus-debug-", + "CONSUS_EXEC_PATH", CONSUS_EXEC_DIR, + &cmds.front(), cmds.size()); +} diff --git a/consus.cc b/consus.cc new file mode 100644 index 0000000..36b4bbb --- /dev/null +++ b/consus.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// STL +#include + +// e +#include + +int +main(int argc, const char* argv[]) +{ + std::vector cmds; + cmds.push_back(e::subcommand("transaction-manager", "Start a new transaction manager")); + cmds.push_back(e::subcommand("key-value-store", "Start a new key value store")); + cmds.push_back(e::subcommand("coordinator", "Start a new coordinator")); + cmds.push_back(e::subcommand("create-data-center", "Create a new data center")); + cmds.push_back(e::subcommand("set-default-data-center", "Set the default data center for new servers")); + cmds.push_back(e::subcommand("availability-check", "Check that the cluster has sufficient availability")); + cmds.push_back(e::subcommand("debug", "Debug tools for Consus developers")); + return dispatch_to_subcommands(argc, argv, + "consus", "Consus", + PACKAGE_VERSION, + "consus-", + "CONSUS_EXEC_PATH", CONSUS_EXEC_DIR, + &cmds.front(), cmds.size()); +} diff --git a/consus.supp b/consus.supp new file mode 100644 index 0000000..7189f72 --- /dev/null +++ b/consus.supp @@ -0,0 +1,169 @@ +{ + + Memcheck:Leak + ... + obj:*/bash + ... +} +{ + + Memcheck:Leak + ... + obj:*/sed + ... +} +{ + + Memcheck:Leak + ... + fun:aio_fsync + ... +} +{ + + Memcheck:Leak + ... + fun:poptGetNextOpt + ... +} +{ + + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + fun:pthread_create@@* + ... +} +{ + + Memcheck:Addr1 + ... + fun:_ZN6google24glog_internal_namespace_26InitGoogleLoggingUtilitiesEPKc + ... +} +{ + + Memcheck:Addr1 + ... + fun:_ZN6google11LogToStderrEv + ... +} +{ + + Memcheck:Addr2 + ... + fun:_ZN6google11LogToStderrEv + ... +} +{ + + Memcheck:Addr4 + ... + fun:_ZN6google11LogToStderrEv + ... +} +{ + + Memcheck:Addr8 + ... + fun:_ZN6google11LogToStderrEv + ... +} +{ + + Memcheck:Leak + ... + fun:_ZN6google11LogToStderrEv + ... +} +{ + + Memcheck:Leak + ... + obj:/usr/*lib*/libglog*so* + ... +} +{ + + Memcheck:Addr4 + obj:/usr/*lib*/libpython*so* + ... +} +{ + + Memcheck:Cond + obj:/usr/*lib*/libpython*so* + ... +} +{ + + Memcheck:Leak + fun:malloc + obj:/usr/*lib*/libpython*so* + ... +} +{ + + Memcheck:Value8 + obj:/usr/*lib*/libpython*so* + ... +} +{ + + Memcheck:Addr4 + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Cond + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Leak + fun:calloc + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Leak + fun:malloc + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Leak + fun:realloc + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Value8 + obj:/usr/*bin*/python* + ... +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:_ZnwmRKSt9nothrow_t + fun:consus_coordinator_create + fun:action_ctor + fun:main +} +{ + + Memcheck:Leak + match-leak-kinds: definite + fun:_Znwm + fun:_ZN6consus11coordinator8recreateEP11rsm_contextPKcm + fun:consus_coordinator_recreate + fun:action_rtor + fun:main +} diff --git a/coordinator/coordinator.cc b/coordinator/coordinator.cc new file mode 100644 index 0000000..197758b --- /dev/null +++ b/coordinator/coordinator.cc @@ -0,0 +1,988 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#define __STDC_FORMAT_MACROS + +// C +#include + +// STL +#include +#include +#include +#include + +// e +#include + +// consus +#include "common/coordinator_returncode.h" +#include "common/macros.h" +#include "coordinator/coordinator.h" +#include "coordinator/util.h" + +#pragma GCC diagnostic ignored "-Wlarger-than=" + +using consus::coordinator; + +namespace +{ + +template +std::string +to_string(const T& t) +{ + std::ostringstream ostr; + ostr << t; + return ostr.str(); +} + +template +T* +get_by_id(std::vector& TS, TID id) +{ + for (size_t i = 0; i < TS.size(); ++i) + { + if (TS[i].id() == id) + { + return &TS[i]; + } + } + + return NULL; +} + +template +bool +address_is_free(std::vector& TS, TID id, const po6::net::location& bind_to) +{ + for (size_t i = 0; i < TS.size(); ++i) + { + if (TS[i].id() != id && + TS[i].bind_to() == bind_to) + { + return false; + } + } + + return true; +} + +} // namespace + +coordinator :: coordinator() + : m_cluster() + , m_version() + , m_flags(0) + , m_counter(1) + , m_dc_default() + , m_dcs() + , m_txmans() + , m_txman_groups() + , m_txman_quiescence_counter(0) + , m_txmans_changed(false) + , m_kvss() + , m_kvs_quiescence_counter(0) + , m_kvss_changed(false) +{ +} + +coordinator :: ~coordinator() throw () +{ +} + +void +coordinator :: init(rsm_context* ctx, uint64_t token) +{ + if (m_cluster != cluster_id()) + { + rsm_log(ctx, "cannot initialize consus cluster(%" PRIu64 ") " + "because it is already initialized to cluster(%" PRIu64 ")\n", token, m_cluster.get()); + // we lie to the client and pretend all is well + return generate_response(ctx, COORD_SUCCESS); + } + + rsm_tick_interval(ctx, "tick", 1 /*PER SECOND*/); + rsm_log(ctx, "initializing consus cluster(%" PRIu64 ")\n", token); + m_cluster = cluster_id(token); + generate_next_configuration(ctx); + return generate_response(ctx, COORD_SUCCESS); +} + +#define INVARIANT(X) \ + do { \ + if (!(X)) { \ + rsm_log(ctx, "invariant violated: %s\n", STR(X)); \ + rsm_log(ctx, " at %s:%d\n", __FILE__, __LINE__); \ + abort(); \ + } \ + } while (0) + +void +coordinator :: invariant_check(rsm_context* ctx) +{ + INVARIANT(m_dc_default == data_center_id() || get_data_center(m_dc_default)); + + for (size_t i = 0; i < m_dcs.size(); ++i) + { + INVARIANT(m_dcs[i].id.get() < m_counter); + } + + for (size_t i = 0; i < m_txmans.size(); ++i) + { + INVARIANT(get_data_center(m_txmans[i].tx.dc)); + } + + for (size_t i = 0; i < m_txman_groups.size(); ++i) + { + INVARIANT(m_txman_groups[i].id.get() < m_counter); + INVARIANT(get_data_center(m_txman_groups[i].dc)); + + for (unsigned m = 0; m < m_txman_groups[i].members_sz; ++m) + { + txman_state* ts = get_txman(m_txman_groups[i].members[m]); + INVARIANT(ts); + INVARIANT(ts->tx.id == m_txman_groups[i].members[m]); + INVARIANT(ts->tx.dc == m_txman_groups[i].dc); + } + } + + for (size_t i = 0; i < m_kvss.size(); ++i) + { + INVARIANT(get_data_center(m_kvss[i].kv.dc)); + } +} + +consus::data_center* +coordinator :: get_data_center(data_center_id id) +{ + for (size_t i = 0; i < m_dcs.size(); ++i) + { + if (m_dcs[i].id == id) + { + return &m_dcs[i]; + } + } + + return NULL; +} + +consus::data_center* +coordinator :: get_data_center(const std::string& name) +{ + for (size_t i = 0; i < m_dcs.size(); ++i) + { + if (m_dcs[i].name == name) + { + return &m_dcs[i]; + } + } + + return NULL; +} + +consus::data_center* +coordinator :: new_data_center(const std::string& name) +{ + assert(!get_data_center(name)); + data_center_id id(m_counter); + ++m_counter; + m_dcs.push_back(data_center(id, name)); + return &m_dcs.back(); +} + +void +coordinator :: data_center_create(rsm_context* ctx, const std::string& name) +{ + data_center* dc = get_data_center(name); + + if (dc) + { + rsm_log(ctx, "data center \"%s\" already exists", e::strescape(name).c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + + dc = new_data_center(name); + rsm_log(ctx, "created data center %s", e::strescape(name).c_str()); + generate_next_configuration(ctx); + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: data_center_default(rsm_context* ctx, const std::string& name) +{ + data_center* dc = get_data_center(name); + + if (!dc) + { + rsm_log(ctx, "cannot make \"%s\" the default data center because it doesn't exist", e::strescape(name).c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + + m_dc_default = dc->id; + return generate_response(ctx, COORD_SUCCESS); +} + +consus::txman_state* +coordinator :: get_txman(comm_id tx) +{ + return get_by_id(m_txmans, tx); +} + +consus::txman_state* +coordinator :: new_txman(const txman& t) +{ + assert(!get_txman(t.id)); + m_txmans.push_back(txman_state(t)); + return &m_txmans.back(); +} + +void +coordinator :: txman_register(rsm_context* ctx, const txman& t) +{ + txman_state* ts = get_txman(t.id); + + if (ts) + { + rsm_log(ctx, "register %s failed: already registered", to_string(t).c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + + ts = new_txman(t); + ts->tx.dc = m_dc_default; + rsm_log(ctx, "registered %s", to_string(t).c_str()); + generate_next_configuration(ctx); + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: txman_online(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce) +{ + txman_state* ts = get_txman(id); + + if (!ts) + { + rsm_log(ctx, "cannot bring transaction manager %" PRIu64 + " online because it is not registered", id.get()); + return generate_response(ctx, COORD_NOT_FOUND); + } + + if (ts->state != txman_state::REGISTERED && + ts->state != txman_state::ONLINE && + ts->state != txman_state::OFFLINE) + { + rsm_log(ctx, "cannot bring transaction manager %" PRIu64 + " online because the server is " + "%s\n", id.get(), txman_state::to_string(ts->state)); + return generate_response(ctx, COORD_NO_CAN_DO); + } + + bool changed = false; + + if (ts->state != txman_state::ONLINE) + { + rsm_log(ctx, "changing transaction manager %" PRIu64 + " from %s to %s\n", + id.get(), txman_state::to_string(ts->state), + txman_state::to_string(txman_state::ONLINE)); + ts->state = txman_state::ONLINE; + ts->nonce = nonce; + changed = true; + txman_availability_changed(); + } + else if (ts->nonce != nonce) + { + rsm_log(ctx, "changing transaction manager %" PRIu64 + "'s nonce from %" PRIu64 " to %" PRIu64 "\n", + id.get(), ts->nonce, nonce); + ts->nonce = nonce; + changed = true; + } + + if (ts->tx.bind_to != bind_to) + { + std::string from(to_string(ts->tx.bind_to)); + std::string to(to_string(bind_to)); + + if (address_is_free(m_txmans, id, bind_to)) + { + rsm_log(ctx, "changing transaction manager %" PRIu64 "'s address from %s to %s\n", + id.get(), from.c_str(), to.c_str()); + ts->tx.bind_to = bind_to; + changed = true; + } + else + { + rsm_log(ctx, "cannot change transaction manager %" PRIu64 " to %s " + "because that address is already in use\n", id.get(), to.c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + } + + if (changed) + { + generate_next_configuration(ctx); + } + + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: txman_offline(rsm_context* ctx, comm_id id, const po6::net::location&, uint64_t nonce) +{ + txman_state* ts = get_txman(id); + + if (!ts) + { + rsm_log(ctx, "cannot bring transaction manager %" PRIu64 + " offline because it is not registered", id.get()); + return generate_response(ctx, COORD_NOT_FOUND); + } + + if (ts->nonce == nonce && ts->state == txman_state::ONLINE) + { + rsm_log(ctx, "changing transaction manager %" PRIu64 " from %s to %s\n", + id.get(), txman_state::to_string(ts->state), + txman_state::to_string(txman_state::OFFLINE)); + ts->state = txman_state::OFFLINE; + txman_availability_changed(); + generate_next_configuration(ctx); + } + + return generate_response(ctx, COORD_SUCCESS); +} + +consus::kvs_state* +coordinator :: get_kvs(comm_id lk) +{ + return get_by_id(m_kvss, lk); +} + +consus::kvs_state* +coordinator :: new_kvs(const kvs& k) +{ + assert(!get_kvs(k.id)); + m_kvss.push_back(kvs_state(k)); + return &m_kvss.back(); +} + +#if 0 +consus::ring* +coordinator :: get_or_create_ring(data_center_id id) +{ + for (size_t i = 0; i < m_rings.size(); ++i) + { + if (m_rings[i].dc == id) + { + return &m_rings[i]; + } + } + + m_rings.push_back(ring(id)); + return &m_rings.back(); +} +#endif + +void +coordinator :: kvs_register(rsm_context* ctx, const kvs& k) +{ + kvs_state* kv = get_kvs(k.id); + + if (kv) + { + rsm_log(ctx, "register %s failed: already registered", to_string(k).c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + + kv = new_kvs(k); + kv->kv.dc = m_dc_default; + rsm_log(ctx, "registered %s", to_string(k).c_str()); + generate_next_configuration(ctx); + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: kvs_online(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce) +{ + kvs_state* kv = get_kvs(id); + + if (!kv) + { + rsm_log(ctx, "cannot bring key value store %" PRIu64 + " online because it is not registered", id.get()); + return generate_response(ctx, COORD_NOT_FOUND); + } + + if (kv->state != kvs_state::REGISTERED && + kv->state != kvs_state::ONLINE && + kv->state != kvs_state::OFFLINE) + { + rsm_log(ctx, "cannot bring key value store %" PRIu64 + " online because the server is " + "%s\n", id.get(), kvs_state::to_string(kv->state)); + return generate_response(ctx, COORD_NO_CAN_DO); + } + + bool changed = false; + + if (kv->state != kvs_state::ONLINE) + { + rsm_log(ctx, "changing key value store %" PRIu64 + " from %s to %s\n", + id.get(), kvs_state::to_string(kv->state), + kvs_state::to_string(kvs_state::ONLINE)); + kv->state = kvs_state::ONLINE; + kv->nonce = nonce; + changed = true; + kvs_availability_changed(); + } + else if (kv->nonce != nonce) + { + rsm_log(ctx, "changing key value store %" PRIu64 + "'s nonce from %" PRIu64 " to %" PRIu64 "\n", + id.get(), kv->nonce, nonce); + kv->nonce = nonce; + changed = true; + } + + if (kv->kv.bind_to != bind_to) + { + std::string from(to_string(kv->kv.bind_to)); + std::string to(to_string(bind_to)); + + if (address_is_free(m_kvss, id, bind_to)) + { + rsm_log(ctx, "changing key value store %" PRIu64 "'s address from %s to %s\n", + id.get(), from.c_str(), to.c_str()); + kv->kv.bind_to = bind_to; + changed = true; + } + else + { + rsm_log(ctx, "cannot change key value store %" PRIu64 " to %s " + "because that address is in use\n", id.get(), to.c_str()); + return generate_response(ctx, consus::COORD_DUPLICATE); + } + } + + if (changed) + { + generate_next_configuration(ctx); + } + + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: kvs_offline(rsm_context* ctx, comm_id id, const po6::net::location&, uint64_t nonce) +{ + kvs_state* kv = get_kvs(id); + + if (!kv) + { + rsm_log(ctx, "cannot bring key value store %" PRIu64 + " offline because it is not registered", id.get()); + return generate_response(ctx, COORD_NOT_FOUND); + } + + if (kv->nonce == nonce && kv->state == kvs_state::ONLINE) + { + rsm_log(ctx, "changing key value store %" PRIu64 " from %s to %s\n", + id.get(), kvs_state::to_string(kv->state), + kvs_state::to_string(kvs_state::OFFLINE)); + kv->state = kvs_state::OFFLINE; + kvs_availability_changed(); + generate_next_configuration(ctx); + } + + return generate_response(ctx, COORD_SUCCESS); +} + +void +coordinator :: tick(rsm_context* ctx) +{ + ++m_txman_quiescence_counter; + + if (m_txman_quiescence_counter > 5 && m_txmans_changed) + { + rsm_log(ctx, "regenerating paxos groups because of recent changes to transaction manager availability"); + regenerate_paxos_groups(ctx); + generate_next_configuration(ctx); + m_txmans_changed = false; + } + else if (m_txmans_changed) + { + rsm_log(ctx, "delaying regeneration of paxos groups because transaction managers' availability has not quiesced"); + } + + ++m_kvs_quiescence_counter; + + if (m_kvs_quiescence_counter > 5 && m_kvss_changed) + { + rsm_log(ctx, "restructuring rings because of recent changes to key-value store availability"); + // XXX + m_kvss_changed = false; + } + else if (m_kvss_changed) + { + rsm_log(ctx, "delaying ring restructuring because key-value stores' availability has not quiesced"); + } +} + +coordinator* +coordinator :: recreate(rsm_context* ctx, + const char* data, size_t data_sz) +{ + // XXX + std::auto_ptr c(new coordinator()); + + if (!c.get()) + { + return NULL; + } + + e::unpacker(data, data_sz) + >> c->m_cluster >> c->m_version + >> c->m_flags >> c->m_counter + >> c->m_txmans; + c->generate_next_configuration(ctx); + return c.release(); +} + +int +coordinator :: snapshot(rsm_context* /*ctx*/, + char** data, size_t* data_sz) +{ + // XXX + std::string buf; + e::packer(&buf) + << m_cluster << m_version + << m_flags << m_counter + << m_txmans; + char* ptr = static_cast(malloc(buf.size())); + *data = ptr; + *data_sz = buf.size(); + + if (*data) + { + memmove(ptr, buf.data(), buf.size()); + } + + return 0; +} + +void +coordinator :: generate_next_configuration(rsm_context* ctx) +{ + m_version = version_id(m_version.get() + 1); + rsm_log(ctx, "issuing new configuration version %" PRIu64 "\n", m_version); + std::vector txmans; + + for (size_t i = 0; i < m_txmans.size(); ++i) + { + if (m_txmans[i].state == txman_state::ONLINE) + { + txmans.push_back(m_txmans[i].tx); + } + } + + std::vector kvss; + + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].state == kvs_state::ONLINE) + { + kvss.push_back(m_kvss[i].kv); + } + } + + // client configuration + std::string clientconf; + e::packer(&clientconf) << m_cluster << m_version << m_flags << txmans; + rsm_cond_broadcast_data(ctx, "clientconf", clientconf.data(), clientconf.size()); + + // txman configuration + std::string txmanconf; + e::packer(&txmanconf) + << m_cluster << m_version << m_flags + << m_dcs << m_txmans << m_txman_groups << kvss; + rsm_cond_broadcast_data(ctx, "txmanconf", txmanconf.data(), txmanconf.size()); + + // kvs configuration + std::string kvsconf; + e::packer(&kvsconf) + << m_cluster << m_version << m_flags << m_kvss; + rsm_cond_broadcast_data(ctx, "kvsconf", kvsconf.data(), kvsconf.size()); +} + +void +coordinator :: txman_availability_changed() +{ + m_txman_quiescence_counter = 0; + m_txmans_changed = true; +} + +void +coordinator :: kvs_availability_changed() +{ + m_kvs_quiescence_counter = 0; + m_kvss_changed = true; +} + +namespace +{ +using namespace consus; + +void +update_scatters_and_pairs(const paxos_group& g, + std::map* scatters, + std::set >* pairs) +{ + for (size_t i = 0; i < g.members_sz; ++i) + { + (*scatters)[g.members[i]] += g.members_sz - 1; + + for (size_t j = 0; j < g.members_sz; ++j) + { + pairs->insert(std::make_pair(g.members[i], g.members[j])); + pairs->insert(std::make_pair(g.members[j], g.members[i])); + } + } +} + +void +sort_nodes_by_width(const std::vector& txmans, + std::map* scatters, + std::vector* sorted) +{ + std::vector > widths; + + for (size_t i = 0; i < txmans.size(); ++i) + { + if (txmans[i].state != txman_state::ONLINE) + { + continue; + } + + comm_id id(txmans[i].tx.id); + widths.push_back(std::make_pair((*scatters)[id], id)); + } + + std::stable_sort(widths.begin(), widths.end()); + sorted->clear(); + + for (size_t i = 0; i < widths.size(); ++i) + { + sorted->push_back(widths[i].second); + } +} + +} // namespace + +void +coordinator :: regenerate_paxos_groups(rsm_context*) +{ + const unsigned SCATTER = 3; + const unsigned REPLICATION = 3; + std::map scatters; + std::set > pairs; + + for (size_t i = 0; i < m_txman_groups.size(); ) + { + bool remove = false; + + for (size_t j = 0; j < m_txman_groups[i].members_sz; ++j) + { + txman_state* ts = get_txman(m_txman_groups[i].members[j]); + + if (!ts || ts->state != txman_state::ONLINE) + { + remove = true; + } + } + + if (remove) + { + std::swap(m_txman_groups[i], m_txman_groups.back()); + m_txman_groups.pop_back(); + } + else + { + update_scatters_and_pairs(m_txman_groups[i], &scatters, &pairs); + ++i; + } + } + + size_t txmans_sz = m_txman_groups.size() + 1; + + while (m_txman_groups.size() != txmans_sz) + { + txmans_sz = m_txman_groups.size(); + + for (size_t node = 0; node < m_txmans.size(); ++node) + { + const txman_state& ts(m_txmans[node]); + + if (ts.state != txman_state::ONLINE || scatters[ts.tx.id] >= SCATTER) + { + continue; + } + + paxos_group g; + g.dc = ts.tx.dc; + g.members_sz = 1; + g.members[0] = ts.tx.id; + std::vector candidates; + sort_nodes_by_width(m_txmans, &scatters, &candidates); + + for (size_t i = 0; i < candidates.size(); ++i) + { + if (ts.tx.id == candidates[i]) + { + continue; + } + + bool valid = true; + comm_id a = candidates[i]; + txman_state* ats = get_txman(a); + + for (unsigned p = 0; valid && p < g.members_sz; ++p) + { + comm_id b = g.members[p]; + assert(a != b); + txman_state* bts = get_txman(b); + + // nodes must not already be in a replica set together + if (pairs.find(std::make_pair(a, b)) != pairs.end()) + { + valid = false; + } + + // nodes must be in the same data center + if (ats->tx.dc != bts->tx.dc) + { + valid = false; + } + + // additional constraints should set valid=false if not met + } + + if (valid) + { + g.members[g.members_sz] = a; + ++g.members_sz; + } + + if (g.members_sz == REPLICATION) + { + break; + } + } + + if (g.members_sz == 1 && + pairs.find(std::make_pair(ts.tx.id, ts.tx.id)) != pairs.end()) + { + continue; + } + + g.id = paxos_group_id(m_counter); + ++m_counter; + update_scatters_and_pairs(g, &scatters, &pairs); + m_txman_groups.push_back(g); + } + } +} + +#if 0 +namespace +{ +using namespace consus; + +void +select_by_data_center(const std::vector& kvss, + data_center_id id, + std::vector* ids) +{ + for (size_t i = 0; i < kvss.size(); ++i) + { + if (kvss[i].kv.dc == id) + { + ids->push_back(kvss[i].kv.id); + } + } +} + +void +expand_to_constant(std::vector& ids, + comm_id* out, size_t out_sz) +{ + if (ids.size() >= out_sz) + { + std::copy(ids.begin(), ids.begin() + out_sz, out); + return; + } + + const size_t per = out_sz / ids.size(); + const size_t lim = out_sz % ids.size(); + size_t start = 0; + + for (size_t i = 0; i < ids.size(); ++i) + { + const size_t limit = start + per + (i < lim ? 1 : 0); + + for (size_t j = start; j < limit; ++j) + { + out[j] = ids[i]; + } + + start = limit; + } +} + +struct assignment +{ + assignment() : p(), t() {} + assignment(unsigned _p, comm_id _t) : p(_p), t(_t) {} + + bool operator < (const assignment& rhs) { return p < rhs.p; } + + unsigned p; + comm_id t; +}; + +struct reassignment +{ + reassignment() : p(), f(), t() {} + reassignment(unsigned _p, comm_id _f, comm_id _t) : p(_p), f(_f), t(_t) {} + + bool operator < (const reassignment& rhs) { return p < rhs.p; } + + unsigned p; + comm_id f; + comm_id t; +}; + +void +log_assignments(rsm_context* ctx, const std::string& name, std::vector* assignments) +{ + std::sort(assignments->begin(), assignments->end()); + assignment* ptr = &(*assignments)[0]; + assignment* const end = ptr + assignments->size(); + + while (ptr < end) + { + assignment* eor = ptr; + + while (eor < end && + ptr->t == eor->t && + eor->p - ptr->p == eor - ptr) + { + ++eor; + } + + if (ptr + 1 == eor) + { + rsm_log(ctx, "assigning partition %u to kvs(%" PRIu64 ") in data center \"%s\"\n", + ptr->p, ptr->t.get(), e::strescape(name).c_str()); + } + else + { + assert(ptr != eor); + rsm_log(ctx, "assigning partitions %u-%u to kvs(%" PRIu64 ") in data center \"%s\"\n", + ptr->p, (eor - 1)->p, ptr->t.get(), e::strescape(name).c_str()); + } + + ptr = eor; + } +} + +void +log_reassignments(rsm_context* ctx, const std::string& name, std::vector* reassignments) +{ + std::sort(reassignments->begin(), reassignments->end()); + reassignment* ptr = &(*reassignments)[0]; + reassignment* const end = ptr + reassignments->size(); + + while (ptr < end) + { + reassignment* eor = ptr; + + while (eor < end && + ptr->f == eor->f && + ptr->t == eor->t && + eor->p - ptr->p == eor - ptr) + { + ++eor; + } + + if (ptr + 1 == eor) + { + rsm_log(ctx, "reassigning partition %u from kvs(%" PRIu64 ") to kvs(%" PRIu64 ") in data center \"%s\"\n", + ptr->p, ptr->f.get(), ptr->t.get(), e::strescape(name).c_str()); + } + else + { + assert(ptr != eor); + rsm_log(ctx, "reassigning partitions %u-%u from kvs(%" PRIu64 ") to kvs(%" PRIu64 ") in data center \"%s\"\n", + ptr->p, (eor - 1)->p, ptr->f.get(), ptr->t.get(), e::strescape(name).c_str()); + } + + ptr = eor; + } +} + +} // namespace + +void +coordinator :: maintain_kvs_rings(rsm_context* ctx) +{ + for (size_t i = 0; i < m_dcs.size(); ++i) + { + std::vector kvss; + select_by_data_center(m_kvss, m_dcs[i].id, &kvss); + comm_id partitions[CONSUS_KVS_PARTITIONS]; + expand_to_constant(kvss, partitions, CONSUS_KVS_PARTITIONS); + ring* r = get_or_create_ring(m_dcs[i].id); + std::vector assignments; + std::vector reassignments; + + for (size_t p = 0; p < CONSUS_KVS_PARTITIONS; ++p) + { + if (r->partitions[p].assigned == comm_id()) + { + assignments.push_back(assignment(p, partitions[p])); + r->partitions[p].assigned = partitions[p]; + ++r->partitions[p].version; + } + else if (r->partitions[p].assigned != partitions[p]) + { + reassignments.push_back(reassignment(p, r->partitions[p].assigned, partitions[p])); + } + } + + log_assignments(ctx, m_dcs[i].name, &assignments); + log_reassignments(ctx, m_dcs[i].name, &reassignments); + +#if 0 +#if 0 + ring_changed = true; + +#endif +#if 0 + ring_changed = true; + + rsm_log(ctx, "assigning partition(%u) to kvs(%" PRIu64 ") in data center \"%s\"\n", + p, partitions[p].get(), e::strescape(m_dcs[i].name).c_str()); + rsm_log(ctx, "assigning kvs(%" PRIu64 ") to partition(%u) in data center \"%s\"\n", + partitions[p].get(), p, e::strescape(m_dcs[i].name).c_str()); + rsm_log(ctx, "re-assigning partition(%u) from kvs(%" PRIu64 " to kvs(%" PRIu64 ") in data center \"%s\"\n", + p, r->partitions[p].assigned.get(), partitions[p].get(), e::strescape(m_dcs[i].name).c_str()); +#endif + if (ring_changed) + { + ++r->version; + } +#endif + } +} +#endif diff --git a/coordinator/coordinator.h b/coordinator/coordinator.h new file mode 100644 index 0000000..a1e7779 --- /dev/null +++ b/coordinator/coordinator.h @@ -0,0 +1,105 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_coordinator_coordinator_h_ +#define consus_coordinator_coordinator_h_ + +// STL +#include + +// Replicant +#include + +// consus +#include "namespace.h" +#include "common/data_center.h" +#include "common/ids.h" +#include "common/kvs.h" +#include "common/kvs_state.h" +#include "common/paxos_group.h" +#include "common/txman.h" +#include "common/txman_state.h" + +BEGIN_CONSUS_NAMESPACE + +class coordinator +{ + public: + coordinator(); + ~coordinator() throw (); + + public: + void init(rsm_context* ctx, uint64_t token); + cluster_id cluster() const { return m_cluster; } + void invariant_check(rsm_context* ctx); + + // data centers + public: + data_center* get_data_center(data_center_id id); + data_center* get_data_center(const std::string& name); + data_center* new_data_center(const std::string& name); + void data_center_create(rsm_context* ctx, const std::string& name); + void data_center_default(rsm_context* ctx, const std::string& name); + + // transaction managers + public: + txman_state* get_txman(comm_id tx); + txman_state* new_txman(const txman& t); + void txman_register(rsm_context* ctx, const txman& t); + void txman_online(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce); + void txman_offline(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce); + + // key value stores + public: + kvs_state* get_kvs(comm_id tx); + kvs_state* new_kvs(const kvs& t); + void kvs_register(rsm_context* ctx, const kvs& t); + void kvs_online(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce); + void kvs_offline(rsm_context* ctx, comm_id id, const po6::net::location& bind_to, uint64_t nonce); + + // maintenance + public: + void tick(rsm_context* ctx); + + // backup/restore + public: + static coordinator* recreate(rsm_context* ctx, + const char* data, size_t data_sz); + int snapshot(rsm_context* ctx, + char** data, size_t* data_sz); + + // utilities + private: + void generate_next_configuration(rsm_context* ctx); + void txman_availability_changed(); + void kvs_availability_changed(); + void regenerate_paxos_groups(rsm_context* ctx); + + private: + // meta state + cluster_id m_cluster; + version_id m_version; + uint64_t m_flags; + uint64_t m_counter; + // data centers + data_center_id m_dc_default; + std::vector m_dcs; + // transaction managers + std::vector m_txmans; + // transaction manager groups + std::vector m_txman_groups; + unsigned m_txman_quiescence_counter; + bool m_txmans_changed; + // key value stores + std::vector m_kvss; + unsigned m_kvs_quiescence_counter; + bool m_kvss_changed; + + private: + coordinator(const coordinator&); + coordinator& operator = (const coordinator&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_coordinator_coordinator_h_ diff --git a/coordinator/symtable.c b/coordinator/symtable.c new file mode 100644 index 0000000..82b2b7b --- /dev/null +++ b/coordinator/symtable.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +/* Replicant */ +#include + +/* consus */ +#include "visibility.h" +#include "coordinator/transitions.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-pedantic" + +struct state_machine CONSUS_API rsm = { + consus_coordinator_create, + consus_coordinator_recreate, + consus_coordinator_snapshot, + {{"init", consus_coordinator_init}, + {"data_center_create", consus_coordinator_data_center_create}, + {"data_center_default", consus_coordinator_data_center_default}, + {"txman_register", consus_coordinator_txman_register}, + {"txman_online", consus_coordinator_txman_online}, + {"txman_offline", consus_coordinator_txman_offline}, + {"kvs_register", consus_coordinator_kvs_register}, + {"kvs_online", consus_coordinator_kvs_online}, + {"kvs_offline", consus_coordinator_kvs_offline}, + {"tick", consus_coordinator_tick}, + {NULL, NULL}} +}; + +#pragma GCC diagnostic pop diff --git a/coordinator/transitions.cc b/coordinator/transitions.cc new file mode 100644 index 0000000..487231e --- /dev/null +++ b/coordinator/transitions.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C++ +#include + +// e +#include + +// consus +#include "visibility.h" +#include "common/txman.h" +#include "coordinator/coordinator.h" +#include "coordinator/transitions.h" +#include "coordinator/util.h" + +using namespace consus; + +#define PROTECT_UNINITIALIZED \ + coordinator* c = static_cast(obj); \ + e::guard g_c = e::makeobjguard(*c, &coordinator::invariant_check, ctx); \ + do \ + { \ + c->invariant_check(ctx); \ + if (c->cluster() == cluster_id()) \ + { \ + rsm_log(ctx, "cluster not initialized\n"); \ + return generate_response(ctx, consus::COORD_UNINITIALIZED); \ + } \ + } \ + while (0) + +#define CHECK_UNPACK(MSGTYPE) \ + do \ + { \ + if (up.error() || up.remain()) \ + { \ + rsm_log(ctx, "received malformed \"" #MSGTYPE "\" message\n"); \ + return generate_response(ctx, consus::COORD_MALFORMED); \ + } \ + } while (0) + +extern "C" +{ + +CONSUS_API void* +consus_coordinator_create(rsm_context* ctx) +{ + rsm_cond_create(ctx, "clientconf"); + rsm_cond_create(ctx, "txmanconf"); + rsm_cond_create(ctx, "kvsconf"); + return new (std::nothrow) coordinator(); +} + +CONSUS_API void* +consus_coordinator_recreate(rsm_context* ctx, + const char* data, size_t data_sz) +{ + return coordinator::recreate(ctx, data, data_sz); +} + +CONSUS_API int +consus_coordinator_snapshot(rsm_context* ctx, + void* obj, char** data, size_t* data_sz) +{ + coordinator* c = static_cast(obj); + return c->snapshot(ctx, data, data_sz); +} + +CONSUS_API void +consus_coordinator_init(rsm_context* ctx, + void* obj, const char* data, size_t data_sz) +{ + coordinator* c = static_cast(obj); + std::string id(data, data_sz); + uint64_t cluster = strtoull(id.c_str(), NULL, 0); + c->init(ctx, cluster); +} + +CONSUS_API void +consus_coordinator_data_center_create(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + e::slice name; + e::unpacker up(data, data_sz); + up = up >> name; + CHECK_UNPACK(data_center_create); + c->data_center_create(ctx, name.str()); +} + +CONSUS_API void +consus_coordinator_data_center_default(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + e::slice name; + e::unpacker up(data, data_sz); + up = up >> name; + CHECK_UNPACK(data_center_default); + c->data_center_default(ctx, name.str()); +} + +CONSUS_API void +consus_coordinator_txman_register(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to; + CHECK_UNPACK(txman_register); + txman t(id, bind_to); + c->txman_register(ctx, t); +} + +CONSUS_API void +consus_coordinator_txman_online(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + uint64_t nonce; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to >> nonce; + CHECK_UNPACK(txman_online); + c->txman_online(ctx, id, bind_to, nonce); +} + +CONSUS_API void +consus_coordinator_txman_offline(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + uint64_t nonce; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to >> nonce; + CHECK_UNPACK(txman_offline); + c->txman_offline(ctx, id, bind_to, nonce); +} + +CONSUS_API void +consus_coordinator_kvs_register(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to; + CHECK_UNPACK(kvs_register); + kvs k(id, bind_to); + c->kvs_register(ctx, k); +} + +CONSUS_API void +consus_coordinator_kvs_online(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + uint64_t nonce; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to >> nonce; + CHECK_UNPACK(kvs_online); + c->kvs_online(ctx, id, bind_to, nonce); +} + +CONSUS_API void +consus_coordinator_kvs_offline(rsm_context* ctx, void* obj, const char* data, size_t data_sz) +{ + PROTECT_UNINITIALIZED; + comm_id id; + po6::net::location bind_to; + uint64_t nonce; + e::unpacker up(data, data_sz); + up = up >> id >> bind_to >> nonce; + CHECK_UNPACK(kvs_offline); + c->kvs_offline(ctx, id, bind_to, nonce); +} + +CONSUS_API void +consus_coordinator_tick(rsm_context* ctx, void* obj, const char*, size_t) +{ + PROTECT_UNINITIALIZED; + c->tick(ctx); +} + +} // extern "C" diff --git a/coordinator/transitions.h b/coordinator/transitions.h new file mode 100644 index 0000000..a33aa64 --- /dev/null +++ b/coordinator/transitions.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +#ifndef consus_coordinator_transitions_h_ +#define consus_coordinator_transitions_h_ + +/* Replicant */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +void* +consus_coordinator_create(struct rsm_context* ctx); + +void* +consus_coordinator_recreate(struct rsm_context* ctx, + const char* data, size_t data_sz); + +int +consus_coordinator_snapshot(struct rsm_context* ctx, + void* obj, char** data, size_t* sz); + +#define TRANSITION(X) void \ + consus_coordinator_ ## X(struct rsm_context* ctx, \ + void* obj, const char* data, size_t data_sz) + +TRANSITION(init); + +TRANSITION(data_center_create); +TRANSITION(data_center_default); + +TRANSITION(txman_register); +TRANSITION(txman_online); +TRANSITION(txman_offline); + +TRANSITION(kvs_register); +TRANSITION(kvs_online); +TRANSITION(kvs_offline); + +TRANSITION(tick); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ +#endif /* consus_coordinator_transitions_h_ */ diff --git a/coordinator/util.h b/coordinator/util.h new file mode 100644 index 0000000..e6f0718 --- /dev/null +++ b/coordinator/util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_coordinator_util_h_ +#define consus_coordinator_util_h_ + +// consus +#include "namespace.h" +#include "common/coordinator_returncode.h" + +BEGIN_CONSUS_NAMESPACE + +static inline void +generate_response(rsm_context* ctx, coordinator_returncode x) +{ + const char* ptr = NULL; + + switch (x) + { + case COORD_SUCCESS: + ptr = "\x22\x80"; + break; + case COORD_MALFORMED: + ptr = "\x22\x81"; + break; + case COORD_DUPLICATE: + ptr = "\x22\x82"; + break; + case COORD_NOT_FOUND: + ptr = "\x22\x83"; + break; + case COORD_UNINITIALIZED: + ptr = "\x22\x85"; + break; + case COORD_NO_CAN_DO: + ptr = "\x22\x87"; + break; + default: + ptr = "\xff\xff"; + break; + } + + rsm_set_output(ctx, ptr, 2); +} + +#define INVARIANT_BROKEN(X) \ + fprintf(log, "invariant broken at " __FILE__ ":%d: %s\n", __LINE__, X "\n") + +END_CONSUS_NAMESPACE + +#endif // consus_coordinator_util_h_ diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..ee21d09 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,15 @@ +/consus.aux +/consus.bbl +/consus.dvi +/consus.idx +/consus.ilg +/consus.ind +/consus.log +/consus.out +/consus.P +/consus.pdf +/consus.ps +/consus.stdout.log +/consus.toc +/.consus.xtx.cache +/_minted-consus/ diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..12093dd --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,29 @@ +.PHONY: all clean clobber data + +# Configuration variables + +TARGET = consus + +export CROSSTEX_FLAGS:= --add-in --add-proceedings --titlecase=title + +# Rules that derive from configuration variables + +all: $(TARGET).pdf $(TARGET).ps + @echo "SUCCESS: dvi, ps, pdf all up-to-date" + +$(TARGET).dvi: $(TARGET).tex $(TARGET).xtx latex.py Makefile + python2 latex.py $(TARGET) + +$(TARGET).ps: $(TARGET).dvi + dvips $(TARGET).dvi + +$(TARGET).pdf: $(TARGET).ps + ps2pdf -dPDFSETTINGS=/prepress -dEmbedAllFonts=true $(TARGET).ps $(TARGET).pdf + +-include $(TARGET).P + +clean: + rm -f $(TARGET).aux $(TARGET).bbl $(TARGET).log $(TARGET).stdout.log $(TARGET).pgf .$(TARGET).xtx.cache $(TARGET).P + +clobber: clean + rm -f $(TARGET).dvi $(TARGET).pdf $(TARGET).ps diff --git a/doc/consus.tex b/doc/consus.tex new file mode 100644 index 0000000..a79648d --- /dev/null +++ b/doc/consus.tex @@ -0,0 +1,89 @@ +\documentclass[10pt,oneside]{book} + +\ifx \HCode\Undef +\newcommand*{\topdir}{.}% +\else +\def\pgfsysdriver{pgfsys-tex4ht.def} +\newcommand*{\topdir}{..}% +\fi + +\usepackage{hyperref} +\usepackage{nameref} +\usepackage{minted} +\usepackage{graphicx} +\usepackage{enumitem} +\usepackage{calc} +\usepackage{ulem} +\usepackage{tikz} +\usepackage{comment} +\usepackage[margin=1in]{geometry} +\usepackage[small,compact]{titlesec} +\usepackage[binary,squaren]{SIunits} +\usetikzlibrary{arrows} +\usetikzlibrary{decorations.markings} +\usepackage{makeidx} + +\makeindex +\AtBeginDocument{\renewcommand{\bibname}{References}} + +\newcommand{\ConsusVersion}{0.1.dev} +\newcommand{\code}[1]{\texttt{#1}} + +% http://texblog.org/2012/03/21/cross-referencing-list-items/ +\makeatletter +\def\namedlabel#1#2{\begingroup + #2% + \def\@currentlabel{#2}% + \phantomsection\label{#1}\endgroup +} +\makeatother + +\setcounter{secnumdepth}{3} +\setcounter{tocdepth}{2} + +\title{Consus Reference Manual v\ConsusVersion} +\author{Consus Team} + +\newminted{c}{samepage} +\newminted{console}{samepage} +\newminted{go}{samepage} +\newminted{java}{samepage} +\newminted{javascript}{samepage} +\newminted{json}{samepage} +\newminted{pycon}{samepage} +\newminted{python}{samepage} +\newminted{ruby}{samepage} + +\begin{document} + +\frontmatter +\maketitle +\tableofcontents + +\mainmatter + +\part{For Developers} +\label{part:for-developers} + +\part{For Administrators} +\label{part:for-admins} + +\part{API Reference} +\label{part:api-ref} + +\ifx \HCode\Undef % >>>>>>>>>>>>>>>>>>> +\backmatter + +% Index +\clearpage +\addcontentsline{toc}{chapter}{Index} +\printindex + +% References +\clearpage +\addcontentsline{toc}{chapter}{References} +\bibliographystyle{plain} +\bibliography{consus} +\fi % <<<<<<<<<<<<<<<<<<< + +\end{document} diff --git a/doc/consus.xtx b/doc/consus.xtx new file mode 100644 index 0000000..e69de29 diff --git a/doc/latex.py b/doc/latex.py new file mode 100644 index 0000000..36fd0d7 --- /dev/null +++ b/doc/latex.py @@ -0,0 +1,235 @@ +# Copyright (c) 2013-2015, Robert Escriva +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of this project nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import argparse +import hashlib +import logging +import os.path +import re +import shlex +import subprocess +import sys + + +# A list of extensions that latex.py acknowledges. +# +# Each entry is a tuple (extension, tex, xtx). "extension" is a string +# indicating the exension to be watched (e.g., '.tex'). "tex" (and "xtx") are +# booleans that indicate that a rerun of LaTeX (resp., CrossTeX) are necessary +# to obtain a correct build. +# +# If you're adding a new file type, and you're not sure whether it impacts +# LaTeX, CrossTeX, or both, it is safe (but inefficient) to mark both as True. +EXTENSIONS = (('.tex', True, False, False), + ('.xtx', False, True, False), + ('.aux', True, True, False), + ('.bbl', True, False, False), + ('.idx', False, False, True), + ('.ind', True, False, False), + ('.log', True, False, False)) + +BAD_STARTS = ['This is pdfTeX, Version', + r'\write18', + 'entering extended mode', + 'LaTeX2e', + 'Babel', + 'Style option: `fancyvrb\' v2.7a', + 'Document Class: article 2014/09/29 v1.4h Standard LaTeX document class', + 'Option `squaren\' provided!', + 'Package pgf Warning: Your graphic driver pgfsys-dvips.def does not support fadings', + 'Command \\squaren defined by SIunits package!', + 'ABD: EveryShipout initializing macros', + 'see the transcript file for additional information', + 'Output written on ', + 'Transcript written on ', + ] + +pathsoft_re = re.compile(r'\([-/a-zA-Z0-9_.]+[ )]', flags=re.M) +pathhard_re = re.compile(r'<[-/a-zA-Z0-9_.]+[ >]', flags=re.M) +pathsoftnl_re = re.compile(r'\([-/a-zA-Z0-9_.]+$', flags=re.M) +pathhardnl_re = re.compile(r'<[-/a-zA-Z0-9_.]+$', flags=re.M) +pagenum_re = re.compile(r'\[\d+\]') + +def eliminate_paths(path_re, log, deps): + xs = reversed(list(path_re.finditer(log))) + for x in xs: + path = log[x.start(0):x.end(0)] + char = ' ' + if '\n' in path: + char = '\n' + path = path.strip('()<> \n') + if os.path.exists(path): + deps.add(path) + log = log[:x.start(0)] + char + log[x.end(0):] + return log + +def sanitize_lines(lines): + for line in lines: + xs = pagenum_re.finditer(line) + breaks = set() + for x in xs: + breaks.add(x.start(0)) + breaks.add(x.end(0)) + prev = 0 + for x in sorted(breaks): + s = line[prev:x] + s = s.strip('()<> ') + if s: + yield s + prev = x + if breaks: + line = line[max(breaks):] + line = line.strip('()<> ') + if line: + yield line + +def process_tex_log(name): + deps = set([]) + log = open(name + '.stdout.log').read() + log.replace('\t', ' ') + next_page = 1 + log = eliminate_paths(pathsoft_re, log, deps) + log = eliminate_paths(pathhard_re, log, deps) + log = eliminate_paths(pathsoftnl_re, log, deps) + log = eliminate_paths(pathhardnl_re, log, deps) + lines = sanitize_lines(log.split('\n')) + for line in lines: + skip = False + for bs in BAD_STARTS: + if line.startswith(bs): + skip = True + if skip: + continue + print line + return deps + +def cat_tex_log(name): + with open(name + '.log') as f: + data = f.read() + sys.stderr.write(data) + +def compute_sha1s(name): + sha1s = {} + for ext in [x[0] for x in EXTENSIONS]: + if os.path.exists(name + ext): + h = hashlib.new('sha1') + h.update(open(name + ext).read()) + sha1s[name + ext] = h.hexdigest() + else: + sha1s[name + ext] = '' + return sha1s + + +def write_deps(name, deps): + f = open(name + '.P', 'w') + f.write('{0}.dvi: latex.py {1}\n'.format(name, ' '.join(deps))) + for d in deps: + d = d[2:] if d.startswith('./') else d + f.write('{0}:\n'.format(d)) + f.flush() + f.close() + + +def older_than(name, target, dep): + t = name + target + d = name + dep + if not os.path.exists(t) or not os.path.exists(d): + return True + return os.stat(t).st_mtime < os.stat(d).st_mtime + + +def build(name, run_xtx=False, run_idx=False): + need_tex = True + need_xtx = run_xtx and older_than(name, '.bbl', '.xtx') + need_idx = run_idx and older_than(name, '.ind', '.idx') + if not need_tex and not need_xtx and not need_idx: + need_tex = True + sha1s = compute_sha1s(name) + if not need_tex and not need_xtx and not need_idx: + subprocess.check_call(['touch', name + '.dvi']) + subprocess.check_call(['touch', name + '.P']) + return 0 + iters = 0 + deps = set([]) + while need_tex or need_xtx or need_idx: + if iters > 16: + logging.error("error, iterated 16 times; you'd think tex would converge by now; bailing") + return -1 + logging.error("=======================") + logging.error("beginning iteration %d" % iters) + logging.error("=======================") + if need_tex or (need_xtx and not os.path.exists(name + '.aux')): + env = os.environ.copy() + env['max_print_line'] = '4096' + rc = subprocess.call(['latex', '-interaction=nonstopmode', + '-shell-escape', '-halt-on-error', name], + stdin = open('/dev/null', 'r'), + stdout = open(name + '.stdout.log', 'w'), + stderr = subprocess.STDOUT, + env = env) + if rc != 0: + cat_tex_log(name) + logging.error("LaTeX failed") + return rc + deps |= process_tex_log(name) + need_tex = False + if need_xtx: + crosstex_flags = shlex.split(os.environ.get('CROSSTEX_FLAGS', ''), posix=True) + rc = subprocess.call(['crosstex', name] + crosstex_flags) + if rc != 0: + logging.error("CrossTeX failed") + return rc + need_xtx = False + if need_idx: + rc = subprocess.call(['makeindex', name]) + if rc != 0: + logging.error("makeindex failed") + return rc + need_idx = False + new_sha1s = compute_sha1s(name) + for ext, tex, xtx, idx in EXTENSIONS: + if sha1s[name + ext] != new_sha1s[name + ext]: + need_tex = need_tex or tex + need_xtx = run_xtx and (need_xtx or xtx) + need_idx = run_idx and (need_idx or idx) + sha1s = new_sha1s + iters += 1 + write_deps(name, deps) + return 0 + +logging.basicConfig(format='%(message)s', level=logging.INFO) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--crosstex', default=False, action='store_true') + parser.add_argument('--index', default=False, action='store_true') + parser.add_argument('document') + args = parser.parse_args() + rc = build(args.document, run_xtx=args.crosstex, run_idx=args.index) + if rc != 0: + os.unlink(args.document + '.dvi') + sys.exit(rc) diff --git a/include/consus-admin.h b/include/consus-admin.h new file mode 100644 index 0000000..1d4ee77 --- /dev/null +++ b/include/consus-admin.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +#ifndef consus_admin_h_ +#define consus_admin_h_ + +/* consus */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +int consus_admin_create_data_center(struct consus_client* client, const char* name, + enum consus_returncode* status); +int consus_admin_set_default_data_center(struct consus_client* client, const char* name, + enum consus_returncode* status); + +struct consus_availability_requirements +{ + unsigned txmans; + unsigned txman_groups; + unsigned kvss; +}; + +int consus_admin_availability_check(struct consus_client* client, + struct consus_availability_requirements* reqs, + int timeout, + enum consus_returncode* status); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ +#endif /* consus_admin_h_ */ diff --git a/include/consus.h b/include/consus.h new file mode 100644 index 0000000..3f28ddc --- /dev/null +++ b/include/consus.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2015, Robert Escriva + * All rights reserved. + */ + +#ifndef consus_h_ +#define consus_h_ + +/* C */ +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* consus_returncode occupies [6656, 6912) */ +enum consus_returncode +{ + CONSUS_SUCCESS = 6656, + CONSUS_NOT_FOUND = 6657, + CONSUS_ABORTED = 6658, + + /* persistent/programmatic errors */ + CONSUS_UNKNOWN_TABLE = 6720, + CONSUS_NONE_PENDING = 6721, + CONSUS_INVALID = 6722, + + /* transient/operational errors */ + CONSUS_TIMEOUT = 6784, + CONSUS_INTERRUPTED = 6785, + CONSUS_SEE_ERRNO = 6786, + CONSUS_COORD_FAIL = 6787, + CONSUS_UNAVAILABLE = 6788, + CONSUS_SERVER_ERROR = 6789, + + /* this should never happen */ + CONSUS_INTERNAL = 6910, + CONSUS_GARBAGE = 6911 +}; + +struct consus_client; +struct consus_transaction; + +struct consus_client* consus_create(const char* coordinator, uint16_t port); +struct consus_client* consus_create_conn_str(const char* conn_str); +void consus_destroy(struct consus_client* client); + +int64_t consus_loop(struct consus_client* client, int timeout, + enum consus_returncode* status); +int64_t consus_wait(struct consus_client* client, int64_t id, int timeout, + enum consus_returncode* status); + +int consus_poll_fd(struct consus_client* client); +int consus_block(struct consus_client* client, int timeout); + +const char* consus_error_message(struct consus_client* client); +const char* consus_error_location(struct consus_client* client); +const char* consus_returncode_to_string(enum consus_returncode); + +int64_t consus_begin_transaction(struct consus_client* client, + enum consus_returncode* status, + struct consus_transaction** xact); +int64_t consus_commit_transaction(struct consus_transaction* xact, + enum consus_returncode* status); +int64_t consus_abort_transaction(struct consus_transaction* xact, + enum consus_returncode* status); +int64_t consus_restart_transaction(struct consus_transaction* xact, + enum consus_returncode* status); +void consus_destroy_transaction(struct consus_transaction* xact); + +int64_t consus_get(struct consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + enum consus_returncode* status, + char** value, size_t* value_sz); +int64_t consus_put(struct consus_transaction* xact, + const char* table, + const char* key, size_t key_sz, + const char* value, size_t value_sz, + enum consus_returncode* status); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ +#endif /* consus_h_ */ diff --git a/kvs/configuration.cc b/kvs/configuration.cc new file mode 100644 index 0000000..10a222f --- /dev/null +++ b/kvs/configuration.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#include "common/kvs_configuration.h" +#include "kvs/configuration.h" + +using consus::configuration; + +configuration :: configuration() + : m_cluster() + , m_version() + , m_flags(0) + , m_kvss() +{ +} + +configuration :: ~configuration() throw () +{ +} + +bool +configuration :: exists(comm_id id) const +{ + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].kv.id == id) + { + return true; + } + } + + return false; +} + +po6::net::location +configuration :: get_address(comm_id id) const +{ + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].kv.id == id) + { + return m_kvss[i].kv.bind_to; + } + } + + return po6::net::location(); +} + +consus::kvs_state::state_t +configuration :: get_state(comm_id id) const +{ + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].kv.id == id) + { + return m_kvss[i].state; + } + } + + return kvs_state::state_t(); +} + +std::string +configuration :: dump() const +{ + return kvs_configuration(m_cluster, m_version, m_flags, m_kvss); +} + +e::unpacker +consus :: operator >> (e::unpacker up, configuration& c) +{ + return kvs_configuration(up, &c.m_cluster, &c.m_version, &c.m_flags, &c.m_kvss); +} diff --git a/kvs/configuration.h b/kvs/configuration.h new file mode 100644 index 0000000..23fe02a --- /dev/null +++ b/kvs/configuration.h @@ -0,0 +1,56 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_kvs_configuration_h_ +#define consus_kvs_configuration_h_ + +// consus +#include "namespace.h" +#include "common/ids.h" +#include "common/kvs_state.h" + +BEGIN_CONSUS_NAMESPACE + +class configuration +{ + public: + configuration(); + ~configuration() throw (); + + // metadata + public: + cluster_id cluster() const { return m_cluster; } + version_id version() const { return m_version; } + + // kvs daemons + public: + bool exists(comm_id id) const; + po6::net::location get_address(comm_id id) const; + kvs_state::state_t get_state(comm_id id) const; + + // debug/internal + public: + std::string dump() const; + + private: + friend e::unpacker operator >> (e::unpacker, configuration& s); + + private: + cluster_id m_cluster; + version_id m_version; + uint64_t m_flags; + std::vector m_kvss; + + private: + configuration(const configuration& other); + configuration& operator = (const configuration& rhs); +}; + +std::ostream& +operator << (std::ostream& lhs, const configuration& rhs); +e::unpacker +operator >> (e::unpacker lhs, configuration& rhs); + +END_CONSUS_NAMESPACE + +#endif // consus_kvs_configuration_h_ diff --git a/kvs/daemon.cc b/kvs/daemon.cc new file mode 100644 index 0000000..b5a9b43 --- /dev/null +++ b/kvs/daemon.cc @@ -0,0 +1,830 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include +#include +#include + +// POSIX +#include +#include + +// STL +#include + +// Google Log +#include +#include + +// LevelDB +#include + +// po6 +#include +#include + +// e +#include +#include +#include +#include +#include +#include +#include +#include + +// BusyBee +#include + +// consus +#include +#include "common/consus.h" +#include "common/macros.h" +#include "common/network_msgtype.h" +#include "common/transaction_group.h" +#include "kvs/daemon.h" + +using consus::daemon; + +#define CHECK_UNPACK(MSGTYPE, UNPACKER) \ + do \ + { \ + if (UNPACKER.error()) \ + { \ + network_msgtype CONCAT(_anon, __LINE__)(MSGTYPE); \ + LOG(WARNING) << "received corrupt \"" \ + << CONCAT(_anon, __LINE__) << "\" message"; \ + return; \ + } \ + } while (0) + +uint32_t s_interrupts = 0; +bool s_debug_dump = false; +bool s_debug_mode = false; + +static void +exit_on_signal(int /*signum*/) +{ + RAW_LOG(ERROR, "interrupted: exiting"); + e::atomic::increment_32_nobarrier(&s_interrupts, 1); +} + +static void +handle_debug_dump(int /*signum*/) +{ + s_debug_dump = true; +} + +static void +handle_debug_mode(int /*signum*/) +{ + s_debug_mode = !s_debug_mode; +} + +struct daemon::coordinator_callback : public coordinator_link::callback +{ + coordinator_callback(daemon* d); + virtual ~coordinator_callback() throw (); + virtual std::string prefix() { return "kvs"; } + virtual bool new_config(const char* data, size_t data_sz); + virtual bool has_id(comm_id id); + virtual po6::net::location address(comm_id id); + virtual bool is_steady_state(comm_id id); + + private: + daemon* d; + coordinator_callback(const coordinator_callback&); + coordinator_callback& operator = (const coordinator_callback&); +}; + +daemon :: coordinator_callback :: coordinator_callback(daemon* _d) + : d(_d) +{ +} + +daemon :: coordinator_callback :: ~coordinator_callback() throw () +{ +} + +static std::vector +split_by_newlines(std::string s) +{ + std::vector v; + + while (!s.empty()) + { + size_t idx = s.find_first_of('\n'); + + if (idx == std::string::npos) + { + v.push_back(s); + s = ""; + } + else + { + v.push_back(s.substr(0, idx)); + s = s.substr(idx + 1, s.size()); + } + } + + return v; +} + +bool +daemon :: coordinator_callback :: new_config(const char* data, size_t data_sz) +{ + std::auto_ptr c(new configuration()); + e::unpacker up(data, data_sz); + up = up >> *c; + + if (up.error() || up.remain()) + { + LOG(ERROR) << "received a bad configuration"; + return false; + } + + configuration* old_config = d->get_config(); + e::atomic::store_ptr_release(&d->m_config, c.release()); + d->m_gc.collect(old_config, e::garbage_collector::free_ptr); + LOG(INFO) << "updating to configuration " << d->get_config()->version(); + + if (s_debug_mode) + { + std::string debug = d->get_config()->dump(); + std::vector lines = split_by_newlines(debug); + LOG(INFO) << "=== begin debug dump of configuration ==="; + + for (size_t i = 0; i < lines.size(); ++i) + { + LOG(INFO) << lines[i]; + } + + LOG(INFO) << "=== end debug dump of configuration ==="; + } + + return true; +} + +bool +daemon :: coordinator_callback :: has_id(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->exists(id); + } + + return false; +} + +po6::net::location +daemon :: coordinator_callback :: address(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->get_address(id); + } + + return po6::net::location(); +} + +bool +daemon :: coordinator_callback :: is_steady_state(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->get_state(id) == kvs_state::ONLINE; + } + + return false; +} + +struct daemon::comparator : public leveldb::Comparator +{ + comparator(); + virtual ~comparator() throw (); + virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const; + virtual const char* Name() const { return "ConsusComparator"; } + virtual void FindShortestSeparator(std::string*, + const leveldb::Slice&) const {} + virtual void FindShortSuccessor(std::string*) const {} +}; + +daemon :: comparator :: comparator() +{ +} + +daemon :: comparator :: ~comparator() throw () +{ +} + +int +daemon :: comparator :: Compare(const leveldb::Slice& a, const leveldb::Slice& b) const +{ + if (a.size() < 8 || b.size() < 8) + { + return -1; + } + + e::slice x(a.data(), a.size() - 8); + e::slice y(b.data(), b.size() - 8); + size_t sz = std::min(x.size(), y.size()); + int cmp = memcmp(x.data(), y.data(), sz); + + if (cmp < 0) + { + return -1; + } + if (cmp > 0) + { + return 1; + } + + uint64_t at; + uint64_t bt; + e::unpack64be(x.data() + x.size() - 8, &at); + e::unpack64be(y.data() + y.size() - 8, &bt); + + if (at > bt) + { + return -1; + } + if (at < bt) + { + return 1; + } + + return 0; +} + +daemon :: daemon() + : m_us() + , m_gc() + , m_busybee_mapper(this) + , m_busybee() + , m_coord_cb() + , m_coord() + , m_config(NULL) + , m_threads() + , m_cmp(new comparator()) + , m_bf() + , m_db() +{ +} + +daemon :: ~daemon() throw () +{ + m_gc.collect(get_config(), e::garbage_collector::free_ptr); + + if (m_db) + { + delete m_db; + } + + if (m_bf) + { + delete m_bf; + } +} + +int +daemon :: run(bool background, + std::string data, + std::string log, + std::string pidfile, + bool has_pidfile, + bool set_bind_to, + po6::net::location bind_to, + bool set_coordinator, + const char* coordinator, + unsigned threads) +{ + if (!e::block_all_signals()) + { + std::cerr << "could not block signals; exiting" << std::endl; + return EXIT_FAILURE; + } + + if (!e::daemonize(background, log, "consus-txman-", pidfile, has_pidfile)) + { + return EXIT_FAILURE; + } + + if (!e::install_signal_handler(SIGHUP, exit_on_signal) || + !e::install_signal_handler(SIGINT, exit_on_signal) || + !e::install_signal_handler(SIGTERM, exit_on_signal) || + !e::install_signal_handler(SIGQUIT, exit_on_signal) || + !e::install_signal_handler(SIGUSR1, handle_debug_dump) || + !e::install_signal_handler(SIGUSR2, handle_debug_mode)) + { + PLOG(ERROR) << "could not install signal handlers"; + return EXIT_FAILURE; + } + + leveldb::Options opts; + opts.create_if_missing = true; + opts.filter_policy = m_bf = leveldb::NewBloomFilterPolicy(10); + opts.max_open_files = std::max(sysconf(_SC_OPEN_MAX) >> 1, 1024L); + opts.comparator = m_cmp.get(); + leveldb::Status st = leveldb::DB::Open(opts, data, &m_db); + + if (!st.ok()) + { + LOG(ERROR) << "could not open LevelDB: " << st.ToString(); + return EXIT_FAILURE; + } + + bool saved; + uint64_t id; + std::string rendezvous(coordinator); + + if (!e::load_identity(po6::path::join(data, "KVS").c_str(), &saved, &id, + set_bind_to, &bind_to, set_coordinator, &rendezvous)) + { + LOG(ERROR) << "could not load prior identity; exiting"; + return EXIT_FAILURE; + } + + m_us.id = comm_id(id); + m_us.bind_to = bind_to; + bool (coordinator_link::*coordfunc)(); + + if (saved) + { + coordfunc = &coordinator_link::establish; + } + else + { + if (!e::generate_token(&id)) + { + PLOG(ERROR) << "could not read random token from /dev/urandom"; + return EXIT_FAILURE; + } + + m_us.id = comm_id(id); + coordfunc = &coordinator_link::initial_registration; + } + + m_coord_cb.reset(new coordinator_callback(this)); + m_coord.reset(new coordinator_link(rendezvous, m_us.id, m_us.bind_to, m_coord_cb.get())); + m_coord->allow_reregistration(); + LOG(INFO) << "starting consus kvs-daemon " << m_us.id + << " on address " << m_us.bind_to; + LOG(INFO) << "connecting to " << rendezvous; + + if (!(((*m_coord).*coordfunc)())) + { + return EXIT_FAILURE; + } + + assert(get_config()); + + if (!e::save_identity(po6::path::join(data, "KVS").c_str(), id, bind_to, rendezvous)) + { + LOG(ERROR) << "could not save identity; exiting"; + return EXIT_FAILURE; + } + + m_busybee.reset(new busybee_mta(&m_gc, &m_busybee_mapper, bind_to, id, threads)); + + for (size_t i = 0; i < threads; ++i) + { + using namespace po6::threads; + e::compat::shared_ptr t(new thread(make_thread_wrapper(&daemon::loop, this, i))); + m_threads.push_back(t); + t->start(); + } + + while (e::atomic::increment_32_nobarrier(&s_interrupts, 0) == 0) + { + bool debug_mode = s_debug_mode; + m_coord->maintain_connection(); + + if (m_coord->error()) + { + break; + } + + if (m_coord->orphaned()) + { + LOG(ERROR) << "server removed from cluster; exiting"; + break; + } + + if (s_debug_mode != debug_mode) + { + if (s_debug_mode) + { + debug_dump(); + LOG(INFO) << "enabling debug mode; will log all state transitions"; + s_debug_dump = false; + } + else + { + LOG(INFO) << "disabling debug mode; will go back to normal operation"; + } + } + + if (s_debug_dump) + { + debug_dump(); + s_debug_dump = false; + } + } + + e::atomic::increment_32_nobarrier(&s_interrupts, 1); + m_busybee->shutdown(); + + for (size_t i = 0; i < m_threads.size(); ++i) + { + m_threads[i]->join(); + } + + LOG(INFO) << "consus is gracefully shutting down"; + return EXIT_SUCCESS; +} + +void +daemon :: loop(size_t thread) +{ + size_t core = thread % sysconf(_SC_NPROCESSORS_ONLN); +#ifdef __LINUX__ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core, &cpuset); + pthread_t cur = pthread_self(); + int x = pthread_setaffinity_np(cur, sizeof(cpu_set_t), &cpuset); + assert(x == 0); +#elif defined(__APPLE__) + thread_affinity_policy_data_t policy; + policy.affinity_tag = 0; + thread_policy_set(mach_thread_self(), + THREAD_AFFINITY_POLICY, + (thread_policy_t)&policy, + THREAD_AFFINITY_POLICY_COUNT); +#endif + + LOG(INFO) << "network thread " << thread << " started on core " << core; + + sigset_t ss; + + if (sigfillset(&ss) < 0 || + pthread_sigmask(SIG_SETMASK, &ss, NULL) < 0) + { + std::cerr << "could not block signals" << std::endl; + return; + } + + e::garbage_collector::thread_state ts; + m_gc.register_thread(&ts); + bool done = false; + + while (!done) + { + uint64_t _id; + std::auto_ptr msg; + busybee_returncode rc = m_busybee->recv(&ts, &_id, &msg); + + switch (rc) + { + case BUSYBEE_SUCCESS: + break; + case BUSYBEE_SHUTDOWN: + done = true; + continue; + case BUSYBEE_DISRUPTED: + case BUSYBEE_INTERRUPTED: + continue; + case BUSYBEE_POLLFAILED: + case BUSYBEE_ADDFDFAIL: + case BUSYBEE_TIMEOUT: + case BUSYBEE_EXTERNAL: + default: + LOG(ERROR) << "internal invariants broken; crashing"; + abort(); + } + + comm_id id(_id); + network_msgtype mt; + e::unpacker up = msg->unpack_from(BUSYBEE_HEADER_SIZE); + up = up >> mt; + + if (up.error()) + { + LOG(WARNING) << "dropping message that has a malformed header"; + + if (s_debug_mode) + { + LOG(WARNING) << "here's some hex: " << msg->hex(); + } + + continue; + } + + switch (mt) + { + case KVS_RD_LOCK: + process_read_lock(id, msg, up); + break; + case KVS_RD_UNLOCK: + process_read_unlock(id, msg, up); + break; + case KVS_WR_BEGIN: + process_write_begin(id, msg, up); + break; + case KVS_WR_FINISH: + process_write_finish(id, msg, up); + break; + case KVS_WR_CANCEL: + process_write_cancel(id, msg, up); + break; + case CONSUS_NOP: + break; + case CLIENT_RESPONSE: + case TXMAN_BEGIN: + case TXMAN_READ: + case TXMAN_WRITE: + case TXMAN_COMMIT: + case TXMAN_ABORT: + case TXMAN_PAXOS_2A: + case TXMAN_PAXOS_2B: + case LV_VOTE_1A: + case LV_VOTE_1B: + case LV_VOTE_2A: + case LV_VOTE_2B: + case LV_VOTE_LEARN: + case KVS_RD_LOCKED: + case KVS_RD_UNLOCKED: + case KVS_WR_BEGUN: + case KVS_WR_FINISHED: + default: + LOG(INFO) << "received " << mt << " message which key-value-stores do not process"; + break; + } + + m_gc.quiescent_state(&ts); + } + + m_gc.deregister_thread(&ts); + LOG(INFO) << "network thread shutting down"; +} + +void +daemon :: process_read_lock(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + e::slice table; + e::slice key; + up = up >> tg >> seqno >> table >> key; + CHECK_UNPACK(KVS_RD_LOCK, up); + + if (s_debug_mode) + { + LOG(INFO) << "get(\"" << e::strescape(table.str()) << "\", \"" + << e::strescape(key.str()) << "\") by " + << tg << "[" << seqno << "]"; + } + + LOG(INFO) << tg << " request read lock(table=\"" << e::strescape(table.str()) + << "\", key=\"" << e::strescape(key.str()) << "\"); not implemented (yet)"; + + std::string tmp; + e::packer(&tmp) << table << key << uint64_t(UINT64_MAX); + leveldb::ReadOptions opts; + leveldb::Iterator* it = m_db->NewIterator(opts); + + if (!it) + { + LOG(ERROR) << "fatal LevelDB failure: " << it->status().ToString(); + e::atomic::increment_32_nobarrier(&s_interrupts, 1); + return; + } + + e::guard g_it = e::makeguard(e::garbage_collector::free_ptr, it); + it->Seek(tmp); + consus_returncode rc; + uint64_t timestamp = 0; + e::slice value; + + if (!it->status().ok()) + { + LOG(ERROR) << "fatal LevelDB failure: " << it->status().ToString(); + e::atomic::increment_32_nobarrier(&s_interrupts, 1); + return; + } + + leveldb::Slice k; + + if (!it->Valid() || (k = it->key()).size() < 8 || + memcmp(k.data(), tmp.data(), k.size() - 8) != 0) + { + rc = CONSUS_NOT_FOUND; + } + else + { + rc = CONSUS_SUCCESS; + e::unpack64be(k.data() + k.size() - 8, ×tamp); + leveldb::Slice v = it->value(); + value = e::slice(v.data(), v.size()); + } + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_RD_LOCKED) + + pack_size(tg) + + sizeof(uint64_t) + + pack_size(rc) + + sizeof(uint64_t) + + pack_size(value); + msg.reset(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_RD_LOCKED << tg << seqno << rc << timestamp << value; + send(id, msg); + + if (s_debug_mode) + { + if (rc == CONSUS_SUCCESS) + { + LOG(INFO) << "timestamp=" << timestamp + << " value=\"" << e::strescape(value.str()) << "\""; + } + else + { + LOG(INFO) << "value not found"; + } + } +} + +void +daemon :: process_read_unlock(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + e::slice table; + e::slice key; + up = up >> tg >> seqno >> table >> key; + CHECK_UNPACK(KVS_RD_UNLOCK, up); + + //if (s_debug_mode) + //{ + LOG(ERROR) << tg << " request read unlock(table=\"" << e::strescape(table.str()) + << "\", key=\"" << e::strescape(key.str()) << "\"); not implemented (yet)"; + //} + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_RD_UNLOCKED) + + pack_size(tg) + + sizeof(uint64_t); + msg.reset(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << KVS_RD_UNLOCKED << tg << seqno; + send(id, msg); +} + +void +daemon :: process_write_begin(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + e::slice table; + e::slice key; + up = up >> tg >> seqno >> table >> key; + CHECK_UNPACK(KVS_WR_BEGIN, up); + + //if (s_debug_mode) + //{ + LOG(ERROR) << tg << " request write lock(table=\"" << e::strescape(table.str()) + << "\", key=\"" << e::strescape(key.str()) << "\"); not implemented (yet)"; + //} + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_BEGUN) + + pack_size(tg) + + sizeof(uint64_t); + msg.reset(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << KVS_WR_BEGUN << tg << seqno; + send(id, msg); +} + +void +daemon :: process_write_finish(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + e::slice table; + e::slice key; + uint64_t timestamp; + e::slice value; + up = up >> tg >> seqno >> table >> key >> timestamp >> value; + CHECK_UNPACK(KVS_WR_BEGIN, up); + + if (s_debug_mode) + { + LOG(INFO) << "put(\"" << e::strescape(table.str()) << "\", \"" + << e::strescape(key.str()) << "\"@" << timestamp << ", \"" + << e::strescape(value.str()) << "\") by " + << tg << "[" << seqno << "]"; + } + + LOG(INFO) << tg << " request write unlock(table=\"" << e::strescape(table.str()) + << "\", key=\"" << e::strescape(key.str()) << "\"); not implemented (yet)"; + + std::string tmp; + e::packer(&tmp) << table << key << timestamp; + leveldb::WriteOptions opts; + opts.sync = true; + leveldb::Status st = m_db->Put(opts, tmp, leveldb::Slice(value.cdata(), value.size())); + consus_returncode rc; + + if (st.ok()) + { + rc = CONSUS_SUCCESS; + } + else + { + rc = CONSUS_SERVER_ERROR; + } + + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_FINISHED) + + pack_size(tg) + + sizeof(uint64_t) + + pack_size(rc) + + sizeof(uint64_t) + + pack_size(e::slice()); + msg.reset(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_WR_FINISHED << tg << seqno << rc; + send(id, msg); + + if (!st.ok()) + { + LOG(ERROR) << "fatal LevelDB failure: " << st.ToString(); + } +} + +void +daemon :: process_write_cancel(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + e::slice table; + e::slice key; + up = up >> tg >> seqno >> table >> key; + CHECK_UNPACK(KVS_WR_CANCEL, up); + + //if (s_debug_mode) + //{ + LOG(ERROR) << tg << " request write unlock(table=\"" << e::strescape(table.str()) + << "\", key=\"" << e::strescape(key.str()) << "\"); not implemented (yet)"; + //} + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_FINISHED) + + pack_size(tg) + + sizeof(uint64_t); + msg.reset(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << KVS_WR_FINISHED << tg << seqno << CONSUS_SUCCESS; + send(id, msg); +} + +consus::configuration* +daemon :: get_config() +{ + return e::atomic::load_ptr_acquire(&m_config); +} + +void +daemon :: debug_dump() +{ + LOG(ERROR) << "DEBUG DUMP"; // XXX +} + +bool +daemon :: send(comm_id id, std::auto_ptr msg) +{ + busybee_returncode rc = m_busybee->send(id.get(), msg); + + switch (rc) + { + case BUSYBEE_SUCCESS: + return true; + case BUSYBEE_DISRUPTED: + return false; + case BUSYBEE_SHUTDOWN: + case BUSYBEE_INTERRUPTED: + case BUSYBEE_POLLFAILED: + case BUSYBEE_ADDFDFAIL: + case BUSYBEE_TIMEOUT: + case BUSYBEE_EXTERNAL: + default: + LOG(ERROR) << "internal invariants broken; crashing"; + abort(); + } +} diff --git a/kvs/daemon.h b/kvs/daemon.h new file mode 100644 index 0000000..4520da7 --- /dev/null +++ b/kvs/daemon.h @@ -0,0 +1,90 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_kvs_daemon_h_ +#define consus_kvs_daemon_h_ + +// STL +#include + +// LevelDB +#include +#include + +// po6 +#include +#include + +// e +#include +#include + +// BusyBee +#include + +// consus +#include "namespace.h" +#include "common/coordinator_link.h" +#include "common/kvs.h" +#include "kvs/configuration.h" +#include "kvs/mapper.h" + +BEGIN_CONSUS_NAMESPACE + +class daemon +{ + public: + daemon(); + ~daemon() throw (); + + public: + int run(bool daemonize, + std::string data, + std::string log, + std::string pidfile, + bool has_pidfile, + bool set_bind_to, + po6::net::location bind_to, + bool set_coordinator, + const char* coordinator, + unsigned threads); + + private: + struct coordinator_callback; + struct comparator; + friend class mapper; + + private: + void loop(size_t thread); + void process_read_lock(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_read_unlock(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_write_begin(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_write_finish(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_write_cancel(comm_id id, std::auto_ptr msg, e::unpacker up); + + private: + configuration* get_config(); + void debug_dump(); + bool send(comm_id id, std::auto_ptr msg); + + private: + kvs m_us; + e::garbage_collector m_gc; + mapper m_busybee_mapper; + std::auto_ptr m_busybee; + std::auto_ptr m_coord_cb; + std::auto_ptr m_coord; + configuration* m_config; + std::vector > m_threads; + std::auto_ptr m_cmp; + const leveldb::FilterPolicy* m_bf; + leveldb::DB* m_db; + + private: + daemon(const daemon&); + daemon& operator = (const daemon&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_kvs_daemon_h_ diff --git a/kvs/main.cc b/kvs/main.cc new file mode 100644 index 0000000..9098c9c --- /dev/null +++ b/kvs/main.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// POSIX +#include + +// C++ +#include + +// Google Log +#include + +// po6 +#include +#include + +// e +#include + +// BusyBee +#include + +// replicant +#include + +// consus +#include "common/constants.h" +#include "common/macros.h" +#include "kvs/daemon.h" +#include "tools/connect_opts.h" + +extern bool s_debug_mode; + +int +main(int argc, const char* argv[]) +{ + bool daemonize = true; + const char* data = "."; + const char* log = NULL; + bool listen = false; + const char* listen_host = "auto"; + long listen_port = CONSUS_PORT_KVS; + const char* pidfile = ""; + bool has_pidfile = false; + long threads = 0; + bool log_immediate = false; + sigset_t ss; + + if (sigfillset(&ss) < 0 || + sigprocmask(SIG_BLOCK, &ss, NULL) < 0) + { + std::cerr << "could not block signals"; + return EXIT_FAILURE; + } + + consus::connect_opts conn('c', "connect", 'P', "connect-port", 'C', "connect-string"); + e::argparser ap; + ap.autohelp(); + ap.arg().name('d', "daemon") + .description("run in the background") + .set_true(&daemonize); + ap.arg().name('f', "foreground") + .description("run in the foreground") + .set_false(&daemonize); + ap.arg().name('D', "data") + .description("store persistent state in this directory (default: .)") + .metavar("dir").as_string(&data); + ap.arg().name('L', "log") + .description("store logs in this directory (default: --data)") + .metavar("dir").as_string(&log); + ap.arg().name('l', "listen") + .description("listen on a specific IP address (default: auto)") + .metavar("IP").as_string(&listen_host).set_true(&listen); + ap.arg().name('p', "listen-port") + .description("listen on an alternative port (default: " STR(CONSUS_PORT_KVS) ")") + .metavar("port").as_long(&listen_port).set_true(&listen); + ap.arg().long_name("pidfile") + .description("write the PID to a file (default: don't)") + .metavar("file").as_string(&pidfile).set_true(&has_pidfile); + ap.arg().name('t', "threads") + .description("the number of threads which will handle network traffic") + .metavar("N").as_long(&threads); + ap.arg().long_name("log-immediate") + .description("immediately flush all log output") + .set_true(&log_immediate).hidden(); + ap.arg().long_name("debug") + .description("start in debug mode") + .set_true(&s_debug_mode).hidden(); + ap.add("Connect to the cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "command takes no positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (listen_port >= (1 << 16) || listen_port <= 0) + { + std::cerr << "listen-port is out of range" << std::endl; + return EXIT_FAILURE; + } + + po6::net::ipaddr listen_ip; + po6::net::location bind_to; + + if (strcmp(listen_host, "auto") == 0) + { + if (!busybee_discover(&listen_ip)) + { + std::cerr << "cannot automatically discover local address; specify one manually" << std::endl; + return EXIT_FAILURE; + } + + bind_to = po6::net::location(listen_ip, listen_port); + } + else + { + if (listen_ip.set(listen_host)) + { + bind_to = po6::net::location(listen_ip, listen_port); + } + + if (bind_to == po6::net::location()) + { + bind_to = po6::net::hostname(listen_host, 0).lookup(AF_UNSPEC, IPPROTO_TCP); + bind_to.port = listen_port; + } + } + + if (bind_to == po6::net::location()) + { + std::cerr << "cannot interpret listen address as hostname or IP address" << std::endl; + return EXIT_FAILURE; + } + + if (bind_to.address == po6::net::ipaddr::ANY()) + { + std::cerr << "cannot bind to " << bind_to << " because it is not routable" << std::endl; + return EXIT_FAILURE; + } + + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); + + if (log_immediate) + { + FLAGS_logbufsecs = 0; + } + + if (threads <= 0) + { + threads += sysconf(_SC_NPROCESSORS_ONLN); + + if (threads <= 0) + { + std::cerr << "cannot create a non-positive number of threads" << std::endl; + return EXIT_FAILURE; + } + } + else if (threads > 512) + { + std::cerr << "refusing to create more than 512 threads" << std::endl; + return EXIT_FAILURE; + } + + try + { + consus::daemon d; + return d.run(daemonize, + std::string(data), + std::string(log ? log : data), + std::string(pidfile), has_pidfile, + listen, bind_to, + conn.isset(), conn.conn_str(), threads); + } + catch (std::exception& e) + { + std::cerr << "error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/kvs/mapper.cc b/kvs/mapper.cc new file mode 100644 index 0000000..efe91d0 --- /dev/null +++ b/kvs/mapper.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "kvs/daemon.h" +#include "kvs/mapper.h" + +using consus::mapper; + +mapper :: mapper(daemon* d) + : m_d(d) +{ +} + +mapper :: ~mapper() throw () +{ +} + +bool +mapper :: lookup(uint64_t server_id, po6::net::location* bound_to) +{ + po6::net::location loc; + + if (m_d->m_config->exists(comm_id(server_id))) + { + *bound_to = m_d->m_config->get_address(comm_id(server_id)); + return true; + } + + return false; +} diff --git a/kvs/mapper.h b/kvs/mapper.h new file mode 100644 index 0000000..3803332 --- /dev/null +++ b/kvs/mapper.h @@ -0,0 +1,35 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_kvs_mapper_h_ +#define consus_kvs_mapper_h_ + +// BusyBee +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class mapper : public busybee_mapper +{ + public: + mapper(daemon* d); + ~mapper() throw (); + + public: + virtual bool lookup(uint64_t server_id, po6::net::location* bound_to); + + private: + daemon* m_d; + + private: + mapper(const mapper&); + mapper& operator = (const mapper&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_kvs_mapper_h_ diff --git a/m4/anal_warnings.m4 b/m4/anal_warnings.m4 new file mode 100644 index 0000000..ef56854 --- /dev/null +++ b/m4/anal_warnings.m4 @@ -0,0 +1,118 @@ +# Copyright (c) 2012-2013, Robert Escriva +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of this project nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# This macro enables many compiler warnings for C++ that generally catch bugs in +# code. It offers the "--enable-wanal-flags" option which defaults to "no". + +AC_DEFUN([ANAL_WARNINGS], + [WANAL_CFLAGS="" + WANAL_CXXFLAGS="" + WANAL_CFLAGS_ONLY="" + AC_ARG_ENABLE([wanal-flags], + [AS_HELP_STRING([--enable-wanal-flags], [enable many warnings @<:@default: no@:>@])], + [wanal_flags=${enableval}], [wanal_flags=no]) + if test x"${wanal_flags}" = xyes; then + AX_CHECK_COMPILE_FLAG([-pedantic],[WANAL_CFLAGS="${WANAL_CFLAGS} -pedantic"],,) + AX_CHECK_COMPILE_FLAG([-Wabi],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wabi"],,) + AX_CHECK_COMPILE_FLAG([-Waddress],[WANAL_CFLAGS="${WANAL_CFLAGS} -Waddress"],,) + AX_CHECK_COMPILE_FLAG([-Wall],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wall"],,) + AX_CHECK_COMPILE_FLAG([-Warray-bounds],[WANAL_CFLAGS="${WANAL_CFLAGS} -Warray-bounds"],,) + AX_CHECK_COMPILE_FLAG([-Wc++0x-compat],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wc++0x-compat"],,) + AX_CHECK_COMPILE_FLAG([-Wcast-align],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcast-align"],,) + AX_CHECK_COMPILE_FLAG([-Wcast-qual],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcast-qual"],,) + AX_CHECK_COMPILE_FLAG([-Wchar-subscripts],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wchar-subscripts"],,) + AX_CHECK_COMPILE_FLAG([-Wclobbered],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wclobbered"],,) + AX_CHECK_COMPILE_FLAG([-Wcomment],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcomment"],,) + #AX_CHECK_COMPILE_FLAG([-Wconversion],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wconversion"],,) + AX_CHECK_COMPILE_FLAG([-Wctor-dtor-privacy],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wctor-dtor-privacy"],,) + AX_CHECK_COMPILE_FLAG([-Wdisabled-optimization],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wdisabled-optimization"],,) + AX_CHECK_COMPILE_FLAG([-Weffc++],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Weffc++"],,) + AX_CHECK_COMPILE_FLAG([-Wempty-body],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wempty-body"],,) + AX_CHECK_COMPILE_FLAG([-Wenum-compare],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wenum-compare"],,) + AX_CHECK_COMPILE_FLAG([-Wextra],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wextra"],,) + AX_CHECK_COMPILE_FLAG([-Wfloat-equal],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wfloat-equal"],,) + AX_CHECK_COMPILE_FLAG([-Wformat=2],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat=2"],,) + AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-nonliteral"],,) + AX_CHECK_COMPILE_FLAG([-Wformat-security],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-security"],,) + AX_CHECK_COMPILE_FLAG([-Wformat],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat"],,) + AX_CHECK_COMPILE_FLAG([-Wformat-y2k],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-y2k"],,) + AX_CHECK_COMPILE_FLAG([-Wignored-qualifiers],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wignored-qualifiers"],,) + AX_CHECK_COMPILE_FLAG([-Wimplicit],[WANAL_CFLAGS_ONLY="${WANAL_CFLAGS} -Wimplicit"],,) + AX_CHECK_COMPILE_FLAG([-Winit-self],[WANAL_CFLAGS="${WANAL_CFLAGS} -Winit-self"],,) + AX_CHECK_COMPILE_FLAG([-Winline],[WANAL_CFLAGS="${WANAL_CFLAGS} -Winline"],,) + AX_CHECK_COMPILE_FLAG([-Wlarger-than=4096],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wlarger-than=4096"],,) + AX_CHECK_COMPILE_FLAG([-Wlogical-op],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wlogical-op"],,) + AX_CHECK_COMPILE_FLAG([-Wmain],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmain"],,) + AX_CHECK_COMPILE_FLAG([-Wmissing-braces],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-braces"],,) + AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-field-initializers"],,) + AX_CHECK_COMPILE_FLAG([-Wmissing-format-attribute],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-format-attribute"],,) + AX_CHECK_COMPILE_FLAG([-Wmissing-include-dirs],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-include-dirs"],,) + AX_CHECK_COMPILE_FLAG([-Wno-long-long],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wno-long-long"],,) + AX_CHECK_COMPILE_FLAG([-Wnon-virtual-dtor],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wnon-virtual-dtor"],,) + AX_CHECK_COMPILE_FLAG([-Woverlength-strings],[WANAL_CFLAGS="${WANAL_CFLAGS} -Woverlength-strings"],,) + AX_CHECK_COMPILE_FLAG([-Woverloaded-virtual],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Woverloaded-virtual"],,) + AX_CHECK_COMPILE_FLAG([-Wpacked-bitfield-compat],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpacked-bitfield-compat"],,) + AX_CHECK_COMPILE_FLAG([-Wpacked],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpacked"],,) + #AX_CHECK_COMPILE_FLAG([-Wpadded],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpadded"],,) + AX_CHECK_COMPILE_FLAG([-Wparentheses],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wparentheses"],,) + AX_CHECK_COMPILE_FLAG([-Wpointer-arith],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpointer-arith"],,) + AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wredundant-decls"],,) + AX_CHECK_COMPILE_FLAG([-Wreorder],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wreorder"],,) + AX_CHECK_COMPILE_FLAG([-Wreturn-type],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wreturn-type"],,) + AX_CHECK_COMPILE_FLAG([-Wsequence-point],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsequence-point"],,) + AX_CHECK_COMPILE_FLAG([-Wshadow],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wshadow"],,) + AX_CHECK_COMPILE_FLAG([-Wsign-compare],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsign-compare"],,) + #AX_CHECK_COMPILE_FLAG([-Wsign-conversion],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsign-conversion"],,) + AX_CHECK_COMPILE_FLAG([-Wsign-promo],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wsign-promo"],,) + AX_CHECK_COMPILE_FLAG([-Wstack-protector],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstack-protector"],,) + AX_CHECK_COMPILE_FLAG([-Wstrict-aliasing=3],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-aliasing=3"],,) + AX_CHECK_COMPILE_FLAG([-Wstrict-aliasing],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-aliasing"],,) + AX_CHECK_COMPILE_FLAG([-Wstrict-null-sentinel],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wstrict-null-sentinel"],,) + #AX_CHECK_COMPILE_FLAG([-Wstrict-overflow=4],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-overflow=4"],,) + #AX_CHECK_COMPILE_FLAG([-Wstrict-overflow],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-overflow"],,) + AX_CHECK_COMPILE_FLAG([-Wswitch-default],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch-default"],,) + AX_CHECK_COMPILE_FLAG([-Wswitch-enum],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch-enum"],,) + AX_CHECK_COMPILE_FLAG([-Wswitch],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch"],,) + AX_CHECK_COMPILE_FLAG([-Wtrigraphs],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wtrigraphs"],,) + AX_CHECK_COMPILE_FLAG([-Wtype-limits],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wtype-limits"],,) + AX_CHECK_COMPILE_FLAG([-Wundef],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wundef"],,) + AX_CHECK_COMPILE_FLAG([-Wuninitialized],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wuninitialized"],,) + AX_CHECK_COMPILE_FLAG([-Wunsafe-loop-optimizations],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunsafe-loop-optimizations"],,) + AX_CHECK_COMPILE_FLAG([-Wunused-function],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-function"],,) + AX_CHECK_COMPILE_FLAG([-Wunused-label],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-label"],,) + AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-parameter"],,) + AX_CHECK_COMPILE_FLAG([-Wunused-value],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-value"],,) + AX_CHECK_COMPILE_FLAG([-Wunused-variable],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-variable"],,) + AX_CHECK_COMPILE_FLAG([-Wunused],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused"],,) + AX_CHECK_COMPILE_FLAG([-Wvolatile-register-var],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wvolatile-register-var"],,) + AX_CHECK_COMPILE_FLAG([-Wwrite-strings],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wwrite-strings"],,) + AX_CHECK_COMPILE_FLAG([-Qunused-arguments],[WANAL_CFLAGS="${WANAL_CFLAGS} -Qunused-arguments"],,) + fi + WANAL_CXXFLAGS="${WANAL_CFLAGS} ${WANAL_CXXFLAGS}" + WANAL_CFLAGS="${WANAL_CFLAGS} ${WANAL_CFLAGS_ONLY}" + AC_SUBST([WANAL_CFLAGS], [${WANAL_CFLAGS}]) + AC_SUBST([WANAL_CXXFLAGS], [${WANAL_CXXFLAGS}]) +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..c3a8d69 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,72 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 2 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 0000000..e2d0d36 --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 2 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([AC_LANG_PROGRAM()], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/m4/ax_check_preproc_flag.m4 b/m4/ax_check_preproc_flag.m4 new file mode 100644 index 0000000..b1cfef6 --- /dev/null +++ b/m4/ax_check_preproc_flag.m4 @@ -0,0 +1,72 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_preproc_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's +# preprocessor or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the preprocessor's default +# flags when the check is done. The check is thus made with the flags: +# "CPPFLAGS EXTRA-FLAGS FLAG". This can for example be used to force the +# preprocessor to issue an error when a bad flag is given. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{COMPILE,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 2 + +AC_DEFUN([AX_CHECK_PREPROC_FLAG], +[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]cppflags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG preprocessor accepts $1], CACHEVAR, [ + ax_check_save_flags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $4 $1" + AC_PREPROC_IFELSE([AC_LANG_PROGRAM()], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + CPPFLAGS=$ax_check_save_flags]) +AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_PREPROC_FLAGS diff --git a/maint/generate-unit-test-gremlins b/maint/generate-unit-test-gremlins new file mode 100755 index 0000000..fdd6d4d --- /dev/null +++ b/maint/generate-unit-test-gremlins @@ -0,0 +1,48 @@ +#!/usr/bin/env python2 + +import glob +import os +import os.path + +# (file relative to test/, # nodes, # data centers) +GREMLINS = [ + ('1-node-1-dc-cluster.gremlin', 1, 1), + ('2-node-1-dc-cluster.gremlin', 2, 1), + ('3-node-1-dc-cluster.gremlin', 3, 1), + ('4-node-1-dc-cluster.gremlin', 4, 1), + ('5-node-1-dc-cluster.gremlin', 5, 1), + + #('1-node-3-dc-cluster.gremlin', 1, 3), + + #('5-node-2-dc-cluster.gremlin', 5, 2), + #('5-node-3-dc-cluster.gremlin', 5, 3), + #('5-node-4-dc-cluster.gremlin', 5, 4), + #('5-node-5-dc-cluster.gremlin', 5, 5), +] + +files = set() + +for gremlin, nodes, dcs in GREMLINS: + for x in glob.glob('test/unit/*.py'): + path = os.path.splitext(x)[0] + path += '.%dn.%ddc.gremlin' % (nodes, dcs) + f = open(path, 'w') + f.write('''#!/usr/bin/env gremlin +include ../{gremlin} +timeout 30 +run python ${{CONSUS_SRCDIR}}/{python} +'''.format(gremlin=gremlin, python=x)) + f.flush() + f.close() + os.chmod(path, 0755) + files.add(path) + +gremlins = '' +for path in ['test/' + g[0] for g in GREMLINS] + sorted(files): + gremlins += 'gremlins += %s\n' % path +START = '### begin automatically generated gremlins\n' +END = '### end automatically generated gremlins\n' +text = open('Makefile.am').read() +head, tail = text.split(START) +body, tail = tail.split(END) +open('Makefile.am', 'w').write(head + START + gremlins + END + tail) diff --git a/maint/valgrind-gremlins b/maint/valgrind-gremlins new file mode 100755 index 0000000..4c9b101 --- /dev/null +++ b/maint/valgrind-gremlins @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import os +import os.path +import stat + +def lines(): + for line in open('Makefile.am'): + yield 'file', line + x = line.split('+=') + if len(x) != 2: continue + g = x[1].strip() + if not g.endswith('.gremlin'): continue + if g.endswith('.valgrind.gremlin'): continue + if not os.path.exists(g): continue + v = g[:-len('.gremlin')] + '.valgrind.gremlin' + f = open(v, 'w') + f.write('#!/usr/bin/env gremlin\n') + f.write("env GREMLIN_PREFIX 'libtool --mode=execute valgrind --tool=memcheck --trace-children=yes --error-exitcode=127 --leak-check=full --gen-suppressions=all --suppressions=\"${CONSUS_SRCDIR}/consus.supp\"'\n") + f.write('include ' + os.path.basename(g) + '\n') + f.flush() + f.close() + os.chmod(v, stat.S_IRWXU) + yield 'auto', (x[0] + '+= ' + v + '\n') + +prev = None +fout = open('Makefile.am.tmp', 'w') +for line in lines(): + if prev is None or prev[0] != 'auto' or prev[1] != line[1]: + fout.write(line[1]) + prev = line +fout.flush() +fout.close +os.rename('Makefile.am.tmp', 'Makefile.am') diff --git a/man/consus-availability-check.1.md b/man/consus-availability-check.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-availability-check.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-coordinator.1.md b/man/consus-coordinator.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-coordinator.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-create-data-center.1.md b/man/consus-create-data-center.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-create-data-center.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-debug-client-configuration.1.md b/man/consus-debug-client-configuration.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-debug-client-configuration.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-debug-kvs-configuration.1.md b/man/consus-debug-kvs-configuration.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-debug-kvs-configuration.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-debug-txman-configuration.1.md b/man/consus-debug-txman-configuration.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-debug-txman-configuration.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-debug.1.md b/man/consus-debug.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-debug.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-key-value-store.1.md b/man/consus-key-value-store.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-key-value-store.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-set-default-data-center.1.md b/man/consus-set-default-data-center.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-set-default-data-center.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus-transaction-manager.1.md b/man/consus-transaction-manager.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus-transaction-manager.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/man/consus.1.md b/man/consus.1.md new file mode 100644 index 0000000..6507ae4 --- /dev/null +++ b/man/consus.1.md @@ -0,0 +1,21 @@ +# NAME + +# SYNOPSIS + +# DESCRIPTION + +# OPTIONS + +# ENVIRONMENT + +# FILES + +# EXAMPLES + +# AUTHORS + +# REPORTING BUGS + +# COPYRIGHT + +# SEE ALSO diff --git a/namespace.h b/namespace.h new file mode 100644 index 0000000..e2dea2c --- /dev/null +++ b/namespace.h @@ -0,0 +1,12 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_namespace_h_ +#define consus_namespace_h_ + +#define BEGIN_CONSUS_NAMESPACE \ + namespace consus __attribute__ ((visibility ("hidden"))) { + +#define END_CONSUS_NAMESPACE } + +#endif // consus_namespace_h_ diff --git a/test/1-node-1-dc-cluster.gremlin b/test/1-node-1-dc-cluster.gremlin new file mode 100755 index 0000000..6d85491 --- /dev/null +++ b/test/1-node-1-dc-cluster.gremlin @@ -0,0 +1,31 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 \ + 22751 \ + 22761 \ + 22771 + +run mkdir coord1 + +run mkdir txman1.dc1 + +run mkdir kvs1.dc1 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +run replicant availability-check --servers 1 --timeout 30 --host 127.0.0.1 --port 1982 + +run consus create-data-center --cluster 127.0.0.1:1982 dc1 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 22751 +run consus availability-check --transaction-managers 1 --transaction-manager-groups 1 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 22771 +run consus availability-check --key-value-stores 1 --timeout 30 + +run replicant availability-check --servers 1 --timeout 30 --host 127.0.0.1 --port 1982 diff --git a/test/1-node-3-dc-cluster.gremlin b/test/1-node-3-dc-cluster.gremlin new file mode 100755 index 0000000..706a31b --- /dev/null +++ b/test/1-node-3-dc-cluster.gremlin @@ -0,0 +1,59 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 \ + 22751 \ + 22761 \ + 22771 \ + 23751 \ + 23761 \ + 23771 \ + 24751 \ + 24761 \ + 24771 + +run mkdir coord1 coord3 coord4 coord5 + +run mkdir txman1.dc1 +run mkdir txman1.dc2 +run mkdir txman1.dc3 + +run mkdir kvs1.dc1 +run mkdir kvs1.dc2 +run mkdir kvs1.dc3 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +run replicant availability-check --servers 1 --timeout 30 --host 127.0.0.1 --port 1982 + +run consus create-data-center --cluster 127.0.0.1:1982 dc1 +run consus create-data-center --cluster 127.0.0.1:1982 dc2 +run consus create-data-center --cluster 127.0.0.1:1982 dc3 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 22751 +run consus availability-check --transaction-managers 1 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 23751 +run consus availability-check --transaction-managers 2 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 24751 +run consus availability-check --transaction-managers 3 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 22771 +run consus availability-check --key-value-stores 1 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 23771 +run consus availability-check --key-value-stores 2 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982 --listen 127.0.0.1 --listen-port 24771 +run consus availability-check --key-value-stores 3 --timeout 30 + +run replicant availability-check --servers 1 --timeout 30 --host 127.0.0.1 --port 1982 diff --git a/test/2-node-1-dc-cluster.gremlin b/test/2-node-1-dc-cluster.gremlin new file mode 100755 index 0000000..e937bbc --- /dev/null +++ b/test/2-node-1-dc-cluster.gremlin @@ -0,0 +1,34 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 \ + 22751 22752 \ + 22761 22762 \ + 22771 22772 + +run mkdir coord1 coord2 + +run mkdir txman1.dc1 txman2.dc1 + +run mkdir kvs1.dc1 kvs2.dc1 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983 +run replicant availability-check --servers 2 --timeout 30 --host 127.0.0.1 --port 1983 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983 dc1 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983 --listen 127.0.0.1 --listen-port 22752 +run consus availability-check --transaction-managers 2 --transaction-manager-groups 1 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983 --listen 127.0.0.1 --listen-port 22772 +run consus availability-check --key-value-stores 2 --timeout 30 + +run replicant availability-check --servers 2 --timeout 30 --host 127.0.0.1 --port 1983 diff --git a/test/3-node-1-dc-cluster.gremlin b/test/3-node-1-dc-cluster.gremlin new file mode 100755 index 0000000..f1f993b --- /dev/null +++ b/test/3-node-1-dc-cluster.gremlin @@ -0,0 +1,37 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 \ + 22751 22752 22753 \ + 22761 22762 22763 \ + 22771 22772 22773 + +run mkdir coord1 coord2 coord3 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 +run replicant availability-check --servers 3 --timeout 30 --host 127.0.0.1 --port 1984 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 dc1 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22753 +run consus availability-check --transaction-managers 3 --transaction-manager-groups 1 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984 --listen 127.0.0.1 --listen-port 22773 +run consus availability-check --key-value-stores 3 --timeout 30 + +run replicant availability-check --servers 3 --timeout 30 --host 127.0.0.1 --port 1984 diff --git a/test/4-node-1-dc-cluster.gremlin b/test/4-node-1-dc-cluster.gremlin new file mode 100755 index 0000000..498109d --- /dev/null +++ b/test/4-node-1-dc-cluster.gremlin @@ -0,0 +1,40 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 \ + 22751 22752 22753 22754 \ + 22761 22762 22763 22764 \ + 22771 22772 22773 22774 + +run mkdir coord1 coord2 coord3 coord4 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 +run replicant availability-check --servers 4 --timeout 30 --host 127.0.0.1 --port 1985 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 dc1 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22754 +run consus availability-check --transaction-managers 4 --transaction-manager-groups 1 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985 --listen 127.0.0.1 --listen-port 22774 +run consus availability-check --key-value-stores 4 --timeout 30 + +run replicant availability-check --servers 4 --timeout 30 --host 127.0.0.1 --port 1985 diff --git a/test/5-node-1-dc-cluster.gremlin b/test/5-node-1-dc-cluster.gremlin new file mode 100755 index 0000000..6709e8d --- /dev/null +++ b/test/5-node-1-dc-cluster.gremlin @@ -0,0 +1,43 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --transaction-manager-groups 1 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-2-dc-cluster.gremlin b/test/5-node-2-dc-cluster.gremlin new file mode 100755 index 0000000..468e121 --- /dev/null +++ b/test/5-node-2-dc-cluster.gremlin @@ -0,0 +1,65 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-3-dc-cluster.gremlin b/test/5-node-3-dc-cluster.gremlin new file mode 100755 index 0000000..e51c76a --- /dev/null +++ b/test/5-node-3-dc-cluster.gremlin @@ -0,0 +1,87 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 \ + 24751 24752 24753 24754 24755 \ + 24761 24762 24763 24764 24765 \ + 24771 24772 24773 24774 24775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 +run mkdir txman1.dc3 txman2.dc3 txman3.dc3 txman4.dc3 txman5.dc3 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 +run mkdir kvs1.dc3 kvs2.dc3 kvs3.dc3 kvs4.dc3 kvs5.dc3 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc3 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24755 +run consus availability-check --transaction-managers 15 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24775 +run consus availability-check --key-value-stores 15 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-4-dc-cluster.gremlin b/test/5-node-4-dc-cluster.gremlin new file mode 100755 index 0000000..af7245d --- /dev/null +++ b/test/5-node-4-dc-cluster.gremlin @@ -0,0 +1,109 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 \ + 24751 24752 24753 24754 24755 \ + 24761 24762 24763 24764 24765 \ + 24771 24772 24773 24774 24775 \ + 25751 25752 25753 25754 25755 \ + 25761 25762 25763 25764 25765 \ + 25771 25772 25773 25774 25775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 +run mkdir txman1.dc3 txman2.dc3 txman3.dc3 txman4.dc3 txman5.dc3 +run mkdir txman1.dc4 txman2.dc4 txman3.dc4 txman4.dc4 txman5.dc4 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 +run mkdir kvs1.dc3 kvs2.dc3 kvs3.dc3 kvs4.dc3 kvs5.dc3 +run mkdir kvs1.dc4 kvs2.dc4 kvs3.dc4 kvs4.dc4 kvs5.dc4 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc3 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc4 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24755 +run consus availability-check --transaction-managers 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus transaction-manager --debug --foreground --data=txman1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25755 +run consus availability-check --transaction-managers 20 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24775 +run consus availability-check --key-value-stores 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus key-value-store --debug --foreground --data=kvs1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25775 +run consus availability-check --key-value-stores 20 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-5-dc-cluster.gremlin b/test/5-node-5-dc-cluster.gremlin new file mode 100755 index 0000000..8a01c8f --- /dev/null +++ b/test/5-node-5-dc-cluster.gremlin @@ -0,0 +1,131 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 \ + 24751 24752 24753 24754 24755 \ + 24761 24762 24763 24764 24765 \ + 24771 24772 24773 24774 24775 \ + 25751 25752 25753 25754 25755 \ + 25761 25762 25763 25764 25765 \ + 25771 25772 25773 25774 25775 \ + 26751 26752 26753 26754 26755 \ + 26761 26762 26763 26764 26765 \ + 26771 26772 26773 26774 26775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 +run mkdir txman1.dc3 txman2.dc3 txman3.dc3 txman4.dc3 txman5.dc3 +run mkdir txman1.dc4 txman2.dc4 txman3.dc4 txman4.dc4 txman5.dc4 +run mkdir txman1.dc5 txman2.dc5 txman3.dc5 txman4.dc5 txman5.dc5 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 +run mkdir kvs1.dc3 kvs2.dc3 kvs3.dc3 kvs4.dc3 kvs5.dc3 +run mkdir kvs1.dc4 kvs2.dc4 kvs3.dc4 kvs4.dc4 kvs5.dc4 +run mkdir kvs1.dc5 kvs2.dc5 kvs3.dc5 kvs4.dc5 kvs5.dc5 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc3 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc4 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc5 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24755 +run consus availability-check --transaction-managers 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus transaction-manager --debug --foreground --data=txman1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25755 +run consus availability-check --transaction-managers 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus transaction-manager --debug --foreground --data=txman1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26755 +run consus availability-check --transaction-managers 25 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24775 +run consus availability-check --key-value-stores 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus key-value-store --debug --foreground --data=kvs1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25775 +run consus availability-check --key-value-stores 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus key-value-store --debug --foreground --data=kvs1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26775 +run consus availability-check --key-value-stores 25 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-6-dc-cluster.gremlin b/test/5-node-6-dc-cluster.gremlin new file mode 100755 index 0000000..8214afb --- /dev/null +++ b/test/5-node-6-dc-cluster.gremlin @@ -0,0 +1,153 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 \ + 24751 24752 24753 24754 24755 \ + 24761 24762 24763 24764 24765 \ + 24771 24772 24773 24774 24775 \ + 25751 25752 25753 25754 25755 \ + 25761 25762 25763 25764 25765 \ + 25771 25772 25773 25774 25775 \ + 26751 26752 26753 26754 26755 \ + 26761 26762 26763 26764 26765 \ + 26771 26772 26773 26774 26775 \ + 27751 27752 27753 27754 27755 \ + 27761 27762 27763 27764 27765 \ + 27771 27772 27773 27774 27775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 +run mkdir txman1.dc3 txman2.dc3 txman3.dc3 txman4.dc3 txman5.dc3 +run mkdir txman1.dc4 txman2.dc4 txman3.dc4 txman4.dc4 txman5.dc4 +run mkdir txman1.dc5 txman2.dc5 txman3.dc5 txman4.dc5 txman5.dc5 +run mkdir txman1.dc6 txman2.dc6 txman3.dc6 txman4.dc6 txman5.dc6 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 +run mkdir kvs1.dc3 kvs2.dc3 kvs3.dc3 kvs4.dc3 kvs5.dc3 +run mkdir kvs1.dc4 kvs2.dc4 kvs3.dc4 kvs4.dc4 kvs5.dc4 +run mkdir kvs1.dc5 kvs2.dc5 kvs3.dc5 kvs4.dc5 kvs5.dc5 +run mkdir kvs1.dc6 kvs2.dc6 kvs3.dc6 kvs4.dc6 kvs5.dc6 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc3 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc4 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc5 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc6 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24755 +run consus availability-check --transaction-managers 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus transaction-manager --debug --foreground --data=txman1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25755 +run consus availability-check --transaction-managers 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus transaction-manager --debug --foreground --data=txman1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26755 +run consus availability-check --transaction-managers 25 --timeout 30 + +run consus set-default-data-center dc6 +daemon consus transaction-manager --debug --foreground --data=txman1.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27755 +run consus availability-check --transaction-managers 30 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24775 +run consus availability-check --key-value-stores 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus key-value-store --debug --foreground --data=kvs1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25775 +run consus availability-check --key-value-stores 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus key-value-store --debug --foreground --data=kvs1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26775 +run consus availability-check --key-value-stores 25 --timeout 30 + +run consus set-default-data-center dc6 +daemon consus key-value-store --debug --foreground --data=kvs1.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27775 +run consus availability-check --key-value-stores 30 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/5-node-7-dc-cluster.gremlin b/test/5-node-7-dc-cluster.gremlin new file mode 100755 index 0000000..75128e5 --- /dev/null +++ b/test/5-node-7-dc-cluster.gremlin @@ -0,0 +1,175 @@ +#!/usr/bin/env gremlin + +env GLOG_logtostderr +env GLOG_minloglevel 0 +env GLOG_logbufsecs 0 + +tcp-port 1982 1983 1984 1985 1986 \ + 22751 22752 22753 22754 22755 \ + 22761 22762 22763 22764 22765 \ + 22771 22772 22773 22774 22775 \ + 23751 23752 23753 23754 23755 \ + 23761 23762 23763 23764 23765 \ + 23771 23772 23773 23774 23775 \ + 24751 24752 24753 24754 24755 \ + 24761 24762 24763 24764 24765 \ + 24771 24772 24773 24774 24775 \ + 25751 25752 25753 25754 25755 \ + 25761 25762 25763 25764 25765 \ + 25771 25772 25773 25774 25775 \ + 26751 26752 26753 26754 26755 \ + 26761 26762 26763 26764 26765 \ + 26771 26772 26773 26774 26775 \ + 27751 27752 27753 27754 27755 \ + 27761 27762 27763 27764 27765 \ + 27771 27772 27773 27774 27775 \ + 28751 28752 28753 28754 28755 \ + 28761 28762 28763 28764 28765 \ + 28771 28772 28773 28774 28775 + +run mkdir coord1 coord2 coord3 coord4 coord5 + +run mkdir txman1.dc1 txman2.dc1 txman3.dc1 txman4.dc1 txman5.dc1 +run mkdir txman1.dc2 txman2.dc2 txman3.dc2 txman4.dc2 txman5.dc2 +run mkdir txman1.dc3 txman2.dc3 txman3.dc3 txman4.dc3 txman5.dc3 +run mkdir txman1.dc4 txman2.dc4 txman3.dc4 txman4.dc4 txman5.dc4 +run mkdir txman1.dc5 txman2.dc5 txman3.dc5 txman4.dc5 txman5.dc5 +run mkdir txman1.dc6 txman2.dc6 txman3.dc6 txman4.dc6 txman5.dc6 +run mkdir txman1.dc7 txman2.dc7 txman3.dc7 txman4.dc7 txman5.dc7 + +run mkdir kvs1.dc1 kvs2.dc1 kvs3.dc1 kvs4.dc1 kvs5.dc1 +run mkdir kvs1.dc2 kvs2.dc2 kvs3.dc2 kvs4.dc2 kvs5.dc2 +run mkdir kvs1.dc3 kvs2.dc3 kvs3.dc3 kvs4.dc3 kvs5.dc3 +run mkdir kvs1.dc4 kvs2.dc4 kvs3.dc4 kvs4.dc4 kvs5.dc4 +run mkdir kvs1.dc5 kvs2.dc5 kvs3.dc5 kvs4.dc5 kvs5.dc5 +run mkdir kvs1.dc6 kvs2.dc6 kvs3.dc6 kvs4.dc6 kvs5.dc6 +run mkdir kvs1.dc7 kvs2.dc7 kvs3.dc7 kvs4.dc7 kvs5.dc7 + +daemon consus coordinator --foreground --data=coord1 --listen 127.0.0.1 --listen-port 1982 +daemon consus coordinator --foreground --data=coord2 --listen 127.0.0.1 --listen-port 1983 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord3 --listen 127.0.0.1 --listen-port 1984 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord4 --listen 127.0.0.1 --listen-port 1985 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +daemon consus coordinator --foreground --data=coord5 --listen 127.0.0.1 --listen-port 1986 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 + +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc1 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc2 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc3 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc4 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc5 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc6 +run consus create-data-center --cluster 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 dc7 + +run consus set-default-data-center dc1 +daemon consus transaction-manager --debug --foreground --data=txman1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22755 +run consus availability-check --transaction-managers 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus transaction-manager --debug --foreground --data=txman1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23755 +run consus availability-check --transaction-managers 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus transaction-manager --debug --foreground --data=txman1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24755 +run consus availability-check --transaction-managers 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus transaction-manager --debug --foreground --data=txman1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25755 +run consus availability-check --transaction-managers 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus transaction-manager --debug --foreground --data=txman1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26755 +run consus availability-check --transaction-managers 25 --timeout 30 + +run consus set-default-data-center dc6 +daemon consus transaction-manager --debug --foreground --data=txman1.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27755 +run consus availability-check --transaction-managers 30 --timeout 30 + +run consus set-default-data-center dc7 +daemon consus transaction-manager --debug --foreground --data=txman1.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28751 +daemon consus transaction-manager --debug --foreground --data=txman2.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28752 +daemon consus transaction-manager --debug --foreground --data=txman3.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28753 +daemon consus transaction-manager --debug --foreground --data=txman4.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28754 +daemon consus transaction-manager --debug --foreground --data=txman5.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28755 +run consus availability-check --transaction-managers 35 --timeout 30 + +run consus set-default-data-center dc1 +daemon consus key-value-store --debug --foreground --data=kvs1.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc1 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 22775 +run consus availability-check --key-value-stores 5 --timeout 30 + +run consus set-default-data-center dc2 +daemon consus key-value-store --debug --foreground --data=kvs1.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc2 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 23775 +run consus availability-check --key-value-stores 10 --timeout 30 + +run consus set-default-data-center dc3 +daemon consus key-value-store --debug --foreground --data=kvs1.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc3 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 24775 +run consus availability-check --key-value-stores 15 --timeout 30 + +run consus set-default-data-center dc4 +daemon consus key-value-store --debug --foreground --data=kvs1.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc4 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 25775 +run consus availability-check --key-value-stores 20 --timeout 30 + +run consus set-default-data-center dc5 +daemon consus key-value-store --debug --foreground --data=kvs1.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc5 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 26775 +run consus availability-check --key-value-stores 25 --timeout 30 + +run consus set-default-data-center dc6 +daemon consus key-value-store --debug --foreground --data=kvs1.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc6 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 27775 +run consus availability-check --key-value-stores 30 --timeout 30 + +run consus set-default-data-center dc7 +daemon consus key-value-store --debug --foreground --data=kvs1.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28771 +daemon consus key-value-store --debug --foreground --data=kvs2.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28772 +daemon consus key-value-store --debug --foreground --data=kvs3.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28773 +daemon consus key-value-store --debug --foreground --data=kvs4.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28774 +daemon consus key-value-store --debug --foreground --data=kvs5.dc7 --connect-string 127.0.0.1:1982,127.0.0.1:1983,127.0.0.1:1984,127.0.0.1:1985,127.0.0.1:1986 --listen 127.0.0.1 --listen-port 28775 +run consus availability-check --key-value-stores 35 --timeout 30 + +run replicant availability-check --servers 5 --timeout 30 --host 127.0.0.1 --port 1986 diff --git a/test/env.sh b/test/env.sh new file mode 100644 index 0000000..e896fb8 --- /dev/null +++ b/test/env.sh @@ -0,0 +1,10 @@ +export CONSUS_SRCDIR="$1" +export CONSUS_BUILDDIR="$2" +export CONSUS_VERSION="$3" + +export CONSUS_EXEC_PATH="${CONSUS_BUILDDIR}" +export CONSUS_COORD_LIB="${CONSUS_BUILDDIR}"/.libs/libconsus-coordinator + +export PATH=${CONSUS_BUILDDIR}:${CONSUS_SRCDIR}:${PATH} + +export PYTHONPATH="${CONSUS_BUILDDIR}"/bindings/python:"${CONSUS_BUILDDIR}"/bindings/python/.libs:${PYTHONPATH} diff --git a/test/paxos/generalized-brute-force.cc b/test/paxos/generalized-brute-force.cc new file mode 100644 index 0000000..28942bb --- /dev/null +++ b/test/paxos/generalized-brute-force.cc @@ -0,0 +1,488 @@ +// Copyright (c) 2016, Robert Escriva +// All rights reserved. + +#define __STDC_LIMIT_MACROS + +// STL +#include +#include + +// po6 +#include +#include +#include + +// e +#include +#include +#include +#include + +// consus +#include "common/ids.h" +#include "txman/generalized_paxos.h" + +using namespace consus; + +class state_machine +{ + public: + state_machine(unsigned num_lists); + ~state_machine() throw (); + + public: + void execute(const generalized_paxos::command* cmds, size_t cmds_sz); + uint64_t complete() { return e::atomic::increment_64_nobarrier(&m_elements, 0); } + const std::vector& list(unsigned idx) { assert(idx < m_lists_sz); return m_lists[idx]; } + + protected: + void execute(const generalized_paxos::command& cmd); + + private: + std::set m_executed; + std::vector* const m_lists; + const size_t m_lists_sz; + uint64_t m_elements; + + private: + state_machine(const state_machine&); + state_machine& operator = (const state_machine&); +}; + +state_machine :: state_machine(unsigned num_lists) + : m_executed() + , m_lists(new std::vector[num_lists]) + , m_lists_sz(num_lists) + , m_elements(0) +{ +} + +state_machine :: ~state_machine() throw () +{ + delete[] m_lists; +} + +void +state_machine :: execute(const generalized_paxos::command* cmds, size_t cmds_sz) +{ + for (size_t i = 0; i < cmds_sz; ++i) + { + const generalized_paxos::command& c(cmds[i]); + + if (m_executed.find(c) == m_executed.end()) + { + this->execute(c); + m_executed.insert(c); + } + } +} + +void +state_machine :: execute(const generalized_paxos::command& cmd) +{ + assert(cmd.value.size() == sizeof(uint64_t)); + uint64_t v = 0; + e::unpacker up(cmd.value); + up = up >> v; + + if (up.error()) + { + abort(); + } + + assert(cmd.type < m_lists_sz); + m_lists[cmd.type].push_back(v); + e::atomic::increment_64_nobarrier(&m_elements, 1); +} + +struct message +{ + message() + : has_c(false), c() + , has_p1a(false), p1a() + , has_p1b(false), p1b() + , has_p2a(false), p2a() + , has_p2b(false), p2b() + { + } + + bool has_c; + generalized_paxos::command c; + bool has_p1a; + generalized_paxos::message_p1a p1a; + bool has_p1b; + generalized_paxos::message_p1b p1b; + bool has_p2a; + generalized_paxos::message_p2a p2a; + bool has_p2b; + generalized_paxos::message_p2b p2b; +}; + +class generalized_paxos_state_machine +{ + public: + generalized_paxos_state_machine(state_machine** sm, unsigned acceptors); + ~generalized_paxos_state_machine() throw (); + + public: + void propose(const generalized_paxos::command& cmd); + void wait() { po6::threads::mutex::hold hold(&m_mtx); m_cond.wait(); } + void finish(); + + private: + void acceptor(state_machine* sm, abstract_id id); + void send_to_all(const generalized_paxos::message_p1a& m); + void send_to_all(const generalized_paxos::message_p1b& m); + void send_to_all(const generalized_paxos::message_p2a& m); + void send_to_all(const generalized_paxos::message_p2b& m); + void send_to_all(const message& m); + + private: + unsigned m_num_acceptors; + e::compat::shared_ptr* m_acceptors; + po6::threads::mutex m_mtx; + po6::threads::cond m_cond; + bool m_done; + unsigned short m_randbuf[3]; + std::queue* m_msgs; + + private: + generalized_paxos_state_machine(const generalized_paxos_state_machine&); + generalized_paxos_state_machine& operator = (const generalized_paxos_state_machine&); +}; + +struct comparator : public generalized_paxos::comparator +{ + comparator() {} + virtual ~comparator() throw () {} + virtual bool conflict(const generalized_paxos::command& a, + const generalized_paxos::command& b) const { return a.type == b.type; } +}; + +comparator cmp; + +generalized_paxos_state_machine :: generalized_paxos_state_machine(state_machine** sm, unsigned acceptors) + : m_num_acceptors(acceptors) + , m_acceptors(NULL) + , m_mtx() + , m_cond(&m_mtx) + , m_done(false) + , m_msgs(NULL) +{ + m_acceptors = new e::compat::shared_ptr[acceptors]; + m_msgs = new std::queue[acceptors]; + memset(m_randbuf, 0, sizeof(m_randbuf)); + + for (unsigned i = 0; i < acceptors; ++i) + { + m_acceptors[i].reset(new po6::threads::thread(po6::threads::make_thread_wrapper(&generalized_paxos_state_machine::acceptor, this, sm[i], abstract_id(i + 1)))); + m_acceptors[i]->start(); + } +} + +generalized_paxos_state_machine :: ~generalized_paxos_state_machine() throw () +{ + { + po6::threads::mutex::hold hold(&m_mtx); + assert(m_done); + } + + delete[] m_acceptors; + delete[] m_msgs; +} + +void +generalized_paxos_state_machine :: propose(const generalized_paxos::command& cmd) +{ + po6::threads::mutex::hold hold(&m_mtx); + uint64_t idx = nrand48(m_randbuf) % m_num_acceptors; + assert(idx < m_num_acceptors); + message m; + m.has_c = true; + m.c = cmd; + m_msgs[idx].push(m); + m_cond.broadcast(); +} + +void +generalized_paxos_state_machine :: finish() +{ + { + po6::threads::mutex::hold hold(&m_mtx); + m_cond.broadcast(); + m_done = true; + } + + for (size_t i = 0; i < m_num_acceptors; ++i) + { + m_acceptors[i]->join(); + } +} + +void +generalized_paxos_state_machine :: acceptor(state_machine* sm, abstract_id id) +{ + assert(id > abstract_id()); + const size_t us = id.get() - 1; + generalized_paxos gp; + std::vector ids; + + { + po6::threads::mutex::hold hold(&m_mtx); + ids.resize(m_num_acceptors); + } + + for (size_t i = 0; i < ids.size(); ++i) + { + ids[i] = abstract_id(i + 1); + } + + gp.init(&cmp, id, &ids[0], ids.size()); + uint16_t randbuf[3]; + randbuf[0] = us; + + while (true) + { + std::queue messages; + + { + po6::threads::mutex::hold hold(&m_mtx); + + while (!m_done && m_msgs[us].empty()) + { + m_cond.wait(); + } + + if (m_done) + { + break; + } + + while (!m_msgs[us].empty()) + { + messages.push(m_msgs[us].front()); + m_msgs[us].pop(); + } + } + + while (!messages.empty()) + { + message m = messages.front(); + messages.pop(); + + if (m.has_c) + { + gp.propose(m.c); + } + + if (m.has_p1a) + { + bool send = false; + generalized_paxos::message_p1b r; + gp.process_p1a(m.p1a, &send, &r); + + if (send) + { + send_to_all(r); + } + } + + if (m.has_p1b) + { + gp.process_p1b(m.p1b); + } + + if (m.has_p2a) + { + bool send = false; + generalized_paxos::message_p2b r; + gp.process_p2a(m.p2a, &send, &r); + + if (send) + { + send_to_all(r); + } + } + + if (m.has_p2b) + { + gp.process_p2b(m.p2b); + gp.propose_from_p2b(m.p2b); + } + } + + bool may_attempt_leadership = id == abstract_id(1); + + if (nrand48(randbuf) < (1LL << 22)) + { + may_attempt_leadership = true; + } + + bool send_m1 = false; + bool send_m2 = false; + bool send_m3 = false; + generalized_paxos::message_p1a m1; + generalized_paxos::message_p2a m2; + generalized_paxos::message_p2b m3; + gp.advance(may_attempt_leadership, + &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + + if (send_m1) + { + send_to_all(m1); + } + + if (send_m2) + { + send_to_all(m2); + } + + if (send_m3) + { + send_to_all(m3); + } + + generalized_paxos::cstruct L = gp.learned(); + sm->execute(&L.commands[0], L.commands.size()); + } +} + +void +generalized_paxos_state_machine :: send_to_all(const generalized_paxos::message_p1a& m) +{ + message n; + n.has_p1a = true; + n.p1a = m; + send_to_all(n); +} + +void +generalized_paxos_state_machine :: send_to_all(const generalized_paxos::message_p1b& m) +{ + message n; + n.has_p1b = true; + n.p1b = m; + send_to_all(n); +} + +void +generalized_paxos_state_machine :: send_to_all(const generalized_paxos::message_p2a& m) +{ + message n; + n.has_p2a = true; + n.p2a = m; + send_to_all(n); +} + +void +generalized_paxos_state_machine :: send_to_all(const generalized_paxos::message_p2b& m) +{ + message n; + n.has_p2b = true; + n.p2b = m; + send_to_all(n); +} + +void +generalized_paxos_state_machine :: send_to_all(const message& m) +{ + po6::threads::mutex::hold hold(&m_mtx); + + for (size_t i = 0; i < m_num_acceptors; ++i) + { + m_msgs[i].push(m); + } + + m_cond.broadcast(); +} + +int +main(int argc, const char* argv[]) +{ + long lists = 8; + long acceptors = 5; + long iterations = 1000000; + e::argparser ap; + ap.autohelp(); + ap.arg().name('l', "lists") + .description("how many lists to create (default: 8)") + .as_long(&lists); + ap.arg().name('a', "acceptors") + .description("how many acceptors to use (default: 5)") + .as_long(&acceptors); + ap.arg().name('e', "elements") + .description("how many elements to add (default: 1,000,000)") + .as_long(&iterations); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (lists <= 0) + { + std::cerr << "must specify a positive number of lists\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (acceptors <= 0) + { + std::cerr << "must specify a positive number of acceptors\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + std::vector sm; + + for (long i = 0; i < acceptors; ++i) + { + sm.push_back(new state_machine(lists)); + } + + generalized_paxos_state_machine gp(&sm[0], sm.size()); + unsigned short randbuf[3]; + memset(randbuf, 0, sizeof(randbuf)); + + for (uint64_t i = 1; i <= uint64_t(iterations); ++i) + { + generalized_paxos::command c; + c.type = nrand48(randbuf) % lists; + e::packer(&c.value) << i; + gp.propose(c); + } + + for (size_t i = 0; i < sm.size(); ++i) + { + while (sm[i]->complete() < uint64_t(iterations)) + { + gp.wait(); + } + } + + gp.finish(); + + for (long i = 0; i < lists; ++i) + { + const std::vector& ref(sm[0]->list(i)); + + for (size_t j = 1; j < sm.size(); ++j) + { + const std::vector& x(sm[j]->list(i)); + + if (ref.size() != x.size()) + { + std::cerr << "incomplete" << std::endl; + return EXIT_FAILURE; + } + + if (!std::equal(ref.begin(), ref.end(), x.begin())) + { + std::cerr << "inconsistent" << std::endl; + return EXIT_FAILURE; + } + } + } + + return EXIT_SUCCESS; +} diff --git a/test/paxos/generalized.cc b/test/paxos/generalized.cc new file mode 100644 index 0000000..f8fe32a --- /dev/null +++ b/test/paxos/generalized.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2016, Robert Escriva +// All rights reserved. + +#define __STDC_LIMIT_MACROS + +// consus +#include "test/th.h" +#include "txman/generalized_paxos.h" + +using namespace consus; + +struct no_conflict_comparator : public generalized_paxos::comparator +{ + no_conflict_comparator() {} + virtual ~no_conflict_comparator() throw () {} + + virtual bool conflict(const generalized_paxos::command&, const generalized_paxos::command&) const + { + return false; + } +}; +no_conflict_comparator ncc; + +struct all_conflict_comparator : public generalized_paxos::comparator +{ + all_conflict_comparator() {} + virtual ~all_conflict_comparator() throw () {} + + virtual bool conflict(const generalized_paxos::command&, const generalized_paxos::command&) const + { + return true; + } +}; +all_conflict_comparator acc; + +abstract_id _ids[] = {abstract_id(1), + abstract_id(2), + abstract_id(3), + abstract_id(4), + abstract_id(5), + abstract_id(6), + abstract_id(7), + abstract_id(8), + abstract_id(9)}; + +TEST(GeneralizedPaxos, FiveAcceptors) +{ + // ignore gp[0] to make 1-indexed for easy reading + generalized_paxos gp[6]; + + gp[1].init(&ncc, abstract_id(1), _ids, 5); + gp[2].init(&ncc, abstract_id(2), _ids, 5); + gp[3].init(&ncc, abstract_id(3), _ids, 5); + gp[4].init(&ncc, abstract_id(4), _ids, 5); + gp[5].init(&ncc, abstract_id(5), _ids, 5); + + bool send_m1 = false; + bool send_m2 = false; + bool send_m3 = false; + generalized_paxos::message_p1a m1; + generalized_paxos::message_p2a m2; + generalized_paxos::message_p2b m3; + generalized_paxos::message_p1b r1; + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_TRUE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_FALSE(send_m3); + + for (int i = 1; i <= 5; ++i) + { + bool send = false; + gp[i].process_p1a(m1, &send, &r1); + + if (send) + { + gp[1].process_p1b(r1); + } + } + + gp[1].propose(generalized_paxos::command(1, "hello world from 1")); + gp[2].propose(generalized_paxos::command(2, "hello world from 2")); + gp[3].propose(generalized_paxos::command(3, "hello world from 3")); + gp[4].propose(generalized_paxos::command(4, "hello world from 4")); + gp[5].propose(generalized_paxos::command(5, "hello world from 5")); + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_FALSE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_TRUE(send_m3); + + for (int i = 1; i <= 5; ++i) + { + gp[i].process_p2b(m3); + gp[i].propose_from_p2b(m3); + } + + for (int i = 0; i < 10; ++i) + { + const unsigned idx = (i % 5) + 1; + gp[idx].advance(false, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_FALSE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_TRUE(send_m3); + + for (int j = 1; j <= 5; ++j) + { + gp[j].process_p2b(m3); + gp[j].propose_from_p2b(m3); + } + } + + for (int i = 1; i <= 5; ++i) + { + generalized_paxos::cstruct v = gp[i].learned(); + ASSERT_EQ(5U, v.commands.size()); + } +} + +TEST(GeneralizedPaxos, Conflict) +{ + // ignore gp[0] to make 1-indexed for easy reading + generalized_paxos gp[4]; + + gp[1].init(&acc, abstract_id(1), _ids, 3); + gp[2].init(&acc, abstract_id(2), _ids, 3); + gp[3].init(&acc, abstract_id(3), _ids, 3); + + bool send_m1 = false; + bool send_m2 = false; + bool send_m3 = false; + generalized_paxos::message_p1a m1; + generalized_paxos::message_p2a m2; + generalized_paxos::message_p2b m3; + generalized_paxos::message_p1b r1; + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_TRUE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_FALSE(send_m3); + + for (int i = 1; i <= 3; ++i) + { + bool send = false; + gp[i].process_p1a(m1, &send, &r1); + + if (send) + { + gp[1].process_p1b(r1); + } + } + + gp[1].propose(generalized_paxos::command(1, "operation 1")); + gp[1].propose(generalized_paxos::command(2, "operation 2")); + gp[1].propose(generalized_paxos::command(3, "operation 3")); + + gp[2].propose(generalized_paxos::command(2, "operation 2")); + gp[2].propose(generalized_paxos::command(3, "operation 3")); + gp[2].propose(generalized_paxos::command(1, "operation 1")); + + gp[3].propose(generalized_paxos::command(3, "operation 3")); + gp[3].propose(generalized_paxos::command(1, "operation 1")); + gp[3].propose(generalized_paxos::command(2, "operation 2")); + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_FALSE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_TRUE(send_m3); + + for (int i = 1; i <= 3; ++i) + { + gp[i].process_p2b(m3); + } + + for (int i = 0; i < 6; ++i) + { + const unsigned idx = (i % 3) + 1; + gp[idx].advance(false, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_FALSE(send_m1); + ASSERT_FALSE(send_m1); + ASSERT_FALSE(send_m2); + ASSERT_TRUE(send_m3); + + for (int j = 1; j <= 3; ++j) + { + gp[j].process_p2b(m3); + } + } + + generalized_paxos::cstruct v; + + for (int i = 1; i <= 3; ++i) + { + v = gp[i].learned(); + ASSERT_EQ(0U, v.commands.size()); + } + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_TRUE(send_m1); + ASSERT_EQ(generalized_paxos::ballot::CLASSIC, m1.b.type); + ASSERT_FALSE(send_m2); + ASSERT_TRUE(send_m3); + + for (int i = 1; i <= 3; ++i) + { + bool send = false; + gp[i].process_p1a(m1, &send, &r1); + + if (send) + { + gp[1].process_p1b(r1); + } + } + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_FALSE(send_m1); + ASSERT_TRUE(send_m2); + ASSERT_FALSE(send_m3); + + for (int i = 1; i <= 3; ++i) + { + bool send = false; + gp[i].process_p2a(m2, &send, &m3); + + if (send) + { + for (int j = 1; j <= 3; ++j) + { + gp[j].process_p2b(m3); + } + } + } + + for (int i = 1; i <= 3; ++i) + { + v = gp[i].learned(); + ASSERT_EQ(3U, v.commands.size()); + generalized_paxos::cstruct u; + u.commands.push_back(generalized_paxos::command(1, "operation 1")); + u.commands.push_back(generalized_paxos::command(2, "operation 2")); + u.commands.push_back(generalized_paxos::command(3, "operation 3")); + ASSERT_EQ(v, u); + } + + gp[1].advance(true, &send_m1, &m1, &send_m2, &m2, &send_m3, &m3); + ASSERT_TRUE(send_m1); + ASSERT_EQ(generalized_paxos::ballot::FAST, m1.b.type); + ASSERT_FALSE(send_m2); + ASSERT_FALSE(send_m3); +} diff --git a/test/th.cc b/test/th.cc new file mode 100644 index 0000000..3f4c20e --- /dev/null +++ b/test/th.cc @@ -0,0 +1,217 @@ +// Copyright (c) 2013, Robert Escriva +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of th nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// C +#include + +// C++ +#include + +// STL +#include +#include + +// th +#include "th.h" + +static std::vector* _th_tests = NULL; + +class escape_from_test_failure +{ + public: + escape_from_test_failure() {} +}; + +th :: test_base :: test_base(const char* group, + const char* name, + const char* file, + size_t line) + : m_group(group) + , m_name(name) + , m_file(file) + , m_line(line) +{ + if (_th_tests == NULL) + { + _th_tests = new std::vector(); + } + + _th_tests->push_back(this); +} + +void +th :: test_base :: run(bool quiet, bool* failed) +{ + if (!quiet) std::cerr << "===== Test " << m_group << "::" << m_name << " @ " << m_file << ":" << m_line << std::endl; + + try + { + this->_run(); + *failed = false; + } + catch (escape_from_test_failure& ttotf) + { + *failed = true; + } +} + +bool +th :: test_base :: operator < (const test_base& rhs) const +{ + return compare(rhs) < 0; +} + +int +th :: test_base :: compare(const test_base& rhs) const +{ + const test_base& lhs(*this); + int cmp; + + // Compare file + cmp = strcmp(lhs.m_file, rhs.m_file); + + if (cmp < 0) + { + return -1; + } + if (cmp > 0) + { + return 1; + } + + // Compare line + if (lhs.m_line < rhs.m_line) + { + return -1; + } + if (lhs.m_line > rhs.m_line) + { + return 1; + } + + // Compare group + cmp = strcmp(lhs.m_group, rhs.m_group); + + if (cmp < 0) + { + return -1; + } + if (cmp > 0) + { + return 1; + } + + // Compare name + cmp = strcmp(lhs.m_name, rhs.m_name); + + if (cmp < 0) + { + return -1; + } + if (cmp > 0) + { + return 1; + } + + return 0; +} + +th :: predicate :: predicate(const char* file, size_t line, const char* a, const char* b) + : m_file(file) + , m_line(line) + , m_a(a) + , m_b(b) +{ +} + +void +th :: predicate :: assert_true(bool T) +{ + if (!T) + { + std::cerr << "FAIL @ " << m_file << ":" << m_line << ": tested " << m_a << "; expected true, but got false" << std::endl; + th::fail(); + } +} + +void +th :: predicate :: assert_false(bool F) +{ + if (F) + { + std::cerr << "FAIL @ " << m_file << ":" << m_line << ": tested " << m_a << "; expected false, but got true" << std::endl; + th::fail(); + } +} + +void +th :: predicate :: fail() +{ + std::cerr << "FAIL @ " << m_file << ":" << m_line << ": forced fail" << std::endl; + th::fail(); +} + +static bool +compare_test_base_ptrs(const th::test_base* lhs, const th::test_base* rhs) +{ + return *lhs < *rhs; +} + +int +th :: run_tests(bool quiet) +{ + if (!_th_tests) + { + return 0; + } + + std::sort(_th_tests->begin(), _th_tests->end(), compare_test_base_ptrs); + const std::vector& th_tests(*_th_tests); + int failures = 0; + + for (size_t i = 0; i < th_tests.size(); ++i) + { + bool fail = false; + th_tests[i]->run(quiet, &fail); + + if (fail) + { + ++failures; + } + } + + return failures; +} + +#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" + +void +th :: fail() +{ + escape_from_test_failure eftf; + throw eftf; +} diff --git a/test/th.h b/test/th.h new file mode 100644 index 0000000..dca6ef7 --- /dev/null +++ b/test/th.h @@ -0,0 +1,144 @@ +// Copyright (c) 2013, Robert Escriva +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of th nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef th_h_ +#define th_h_ + +// C +#include + +// C++ +#include + +#define TH_XSTR(x) #x +#define TH_STR(x) TH_XSTR(x) +#define TH_XCONCAT(x, y) x ## y +#define TH_CONCAT(x, y) TH_XCONCAT(x, y) + +namespace th +{ + +int +run_tests(bool quiet); + +void +fail(); + +class test_base +{ + public: + test_base(const char* group, + const char* name, + const char* file, + size_t line); + virtual ~test_base() throw () {} + + public: + void run(bool quiet, bool* failed); + + public: + bool operator < (const test_base& rhs) const; + + private: + virtual void _run() = 0; + int compare(const test_base& rhs) const; + + private: + test_base(const test_base&); + test_base& operator = (const test_base&); + + private: + const char* m_group; + const char* m_name; + const char* m_file; + size_t m_line; +}; + +#define BINARY_PREDICATE(english, compiler) \ + template \ + void \ + assert_ ## english(const A& a, const B& b) \ + { \ + if (!(a compiler b)) \ + { \ + std::cerr << "FAIL @ " << m_file << ":" << m_line << ": tested " << m_a << " " TH_XSTR(compiler) " " << m_b << "; got " << a << " " TH_XSTR(compiler) " " << b << std::endl; \ + th::fail(); \ + } \ + } + +class predicate +{ + public: + predicate(const char* file, + size_t line, + const char* a, + const char* b); + void assert_true(bool T); + void assert_false(bool F); + BINARY_PREDICATE(lt, <) + BINARY_PREDICATE(le, <=) + BINARY_PREDICATE(eq, ==) + BINARY_PREDICATE(ne, !=) + BINARY_PREDICATE(ge, >=) + BINARY_PREDICATE(gt, >) + void fail(); + + private: + const char* m_file; + size_t m_line; + const char* m_a; + const char* m_b; +}; + +#undef BINARY_PREDICATE + +} // namespace th + +#define TEST(GROUP, NAME) \ + class TH_CONCAT(GROUP, TH_CONCAT(_, TH_CONCAT(NAME, TH_CONCAT(_, __LINE__)))) : public th::test_base \ + { \ + public: \ + TH_CONCAT(GROUP, TH_CONCAT(_, TH_CONCAT(NAME, TH_CONCAT(_, __LINE__))))() \ + : test_base(TH_STR(GROUP), TH_STR(NAME), __FILE__, __LINE__) {} \ + protected: \ + virtual void _run(); \ + }; \ + TH_CONCAT(GROUP, TH_CONCAT(_, TH_CONCAT(NAME, TH_CONCAT(_, __LINE__)))) \ + TH_CONCAT(_test_instance_, TH_CONCAT(GROUP, TH_CONCAT(_, TH_CONCAT(NAME, TH_CONCAT(_, __LINE__))))); \ + void TH_CONCAT(GROUP, TH_CONCAT(_, TH_CONCAT(NAME, TH_CONCAT(_, __LINE__)))) :: _run() + +#define ASSERT_TRUE(P) th::predicate(__FILE__, __LINE__, TH_STR(P), NULL).assert_true(P) +#define ASSERT_FALSE(P) th::predicate(__FILE__, __LINE__, TH_STR(P), NULL).assert_false(P) +#define ASSERT_LT(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_lt(a, b) +#define ASSERT_LE(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_le(a, b) +#define ASSERT_EQ(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_eq(a, b) +#define ASSERT_NE(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_ne(a, b) +#define ASSERT_GE(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_ge(a, b) +#define ASSERT_GT(a, b) th::predicate(__FILE__, __LINE__, TH_STR(a), TH_STR(b)).assert_gt(a, b) +#define FAIL() th::predicate(__FILE__, __LINE__, NULL, NULL).fail() + +#endif // th_h_ diff --git a/test/th_main.cc b/test/th_main.cc new file mode 100644 index 0000000..df15ad2 --- /dev/null +++ b/test/th_main.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2011, Robert Escriva +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of po6 nor the names of its contributors may be used +// to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// C +#include + +// th +#include "th.h" + +int +main(int argc, char* argv[]) +{ + bool quiet = false; + + for (int i = 1; i < argc; ++i) + { + quiet = quiet || strcmp(argv[i], "--quiet") == 0; + } + + return th::run_tests(quiet); +} diff --git a/test/unit/00.client.1n.1dc.gremlin b/test/unit/00.client.1n.1dc.gremlin new file mode 100755 index 0000000..ebc61da --- /dev/null +++ b/test/unit/00.client.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.client.py diff --git a/test/unit/00.client.2n.1dc.gremlin b/test/unit/00.client.2n.1dc.gremlin new file mode 100755 index 0000000..b91669f --- /dev/null +++ b/test/unit/00.client.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.client.py diff --git a/test/unit/00.client.3n.1dc.gremlin b/test/unit/00.client.3n.1dc.gremlin new file mode 100755 index 0000000..5fa3d41 --- /dev/null +++ b/test/unit/00.client.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.client.py diff --git a/test/unit/00.client.4n.1dc.gremlin b/test/unit/00.client.4n.1dc.gremlin new file mode 100755 index 0000000..f713461 --- /dev/null +++ b/test/unit/00.client.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.client.py diff --git a/test/unit/00.client.5n.1dc.gremlin b/test/unit/00.client.5n.1dc.gremlin new file mode 100755 index 0000000..64e903e --- /dev/null +++ b/test/unit/00.client.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.client.py diff --git a/test/unit/00.client.py b/test/unit/00.client.py new file mode 100644 index 0000000..956affc --- /dev/null +++ b/test/unit/00.client.py @@ -0,0 +1,11 @@ +import consus + +c1 = consus.Client() +c2 = consus.Client(b'127.0.0.1') +c3 = consus.Client('127.0.0.1') +c4 = consus.Client(b'127.0.0.1', 1982) +c5 = consus.Client('127.0.0.1', 1982) +c6 = consus.Client(b'127.0.0.1:1982') +c7 = consus.Client('127.0.0.1:1982') +c8 = consus.Client(b'[::]:1982,127.0.0.1:1982') +c9 = consus.Client('[::]:1982,127.0.0.1:1982') diff --git a/test/unit/00.nop-abort.1n.1dc.gremlin b/test/unit/00.nop-abort.1n.1dc.gremlin new file mode 100755 index 0000000..81721b8 --- /dev/null +++ b/test/unit/00.nop-abort.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-abort.py diff --git a/test/unit/00.nop-abort.2n.1dc.gremlin b/test/unit/00.nop-abort.2n.1dc.gremlin new file mode 100755 index 0000000..44195fc --- /dev/null +++ b/test/unit/00.nop-abort.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-abort.py diff --git a/test/unit/00.nop-abort.3n.1dc.gremlin b/test/unit/00.nop-abort.3n.1dc.gremlin new file mode 100755 index 0000000..78558bb --- /dev/null +++ b/test/unit/00.nop-abort.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-abort.py diff --git a/test/unit/00.nop-abort.4n.1dc.gremlin b/test/unit/00.nop-abort.4n.1dc.gremlin new file mode 100755 index 0000000..6676db3 --- /dev/null +++ b/test/unit/00.nop-abort.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-abort.py diff --git a/test/unit/00.nop-abort.5n.1dc.gremlin b/test/unit/00.nop-abort.5n.1dc.gremlin new file mode 100755 index 0000000..9012aa4 --- /dev/null +++ b/test/unit/00.nop-abort.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-abort.py diff --git a/test/unit/00.nop-abort.py b/test/unit/00.nop-abort.py new file mode 100644 index 0000000..4b42396 --- /dev/null +++ b/test/unit/00.nop-abort.py @@ -0,0 +1,37 @@ +import consus + +c1 = consus.Client() +t1 = c1.begin_transaction() +t1.abort() + +c2 = consus.Client(b'127.0.0.1') +t2 = c1.begin_transaction() +t2.abort() + +c3 = consus.Client('127.0.0.1') +t3 = c1.begin_transaction() +t3.abort() + +c4 = consus.Client(b'127.0.0.1', 1982) +t4 = c1.begin_transaction() +t4.abort() + +c5 = consus.Client('127.0.0.1', 1982) +t5 = c1.begin_transaction() +t5.abort() + +c6 = consus.Client(b'127.0.0.1:1982') +t6 = c1.begin_transaction() +t6.abort() + +c7 = consus.Client('127.0.0.1:1982') +t7 = c1.begin_transaction() +t7.abort() + +c8 = consus.Client(b'[::]:1982,127.0.0.1:1982') +t8 = c1.begin_transaction() +t8.abort() + +c9 = consus.Client('[::]:1982,127.0.0.1:1982') +t9 = c1.begin_transaction() +t9.abort() diff --git a/test/unit/00.nop-commit.1n.1dc.gremlin b/test/unit/00.nop-commit.1n.1dc.gremlin new file mode 100755 index 0000000..0a8cf53 --- /dev/null +++ b/test/unit/00.nop-commit.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-commit.py diff --git a/test/unit/00.nop-commit.2n.1dc.gremlin b/test/unit/00.nop-commit.2n.1dc.gremlin new file mode 100755 index 0000000..089c19b --- /dev/null +++ b/test/unit/00.nop-commit.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-commit.py diff --git a/test/unit/00.nop-commit.3n.1dc.gremlin b/test/unit/00.nop-commit.3n.1dc.gremlin new file mode 100755 index 0000000..1514bca --- /dev/null +++ b/test/unit/00.nop-commit.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-commit.py diff --git a/test/unit/00.nop-commit.4n.1dc.gremlin b/test/unit/00.nop-commit.4n.1dc.gremlin new file mode 100755 index 0000000..e4dbff3 --- /dev/null +++ b/test/unit/00.nop-commit.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-commit.py diff --git a/test/unit/00.nop-commit.5n.1dc.gremlin b/test/unit/00.nop-commit.5n.1dc.gremlin new file mode 100755 index 0000000..deb1de4 --- /dev/null +++ b/test/unit/00.nop-commit.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/00.nop-commit.py diff --git a/test/unit/00.nop-commit.py b/test/unit/00.nop-commit.py new file mode 100644 index 0000000..1765e4f --- /dev/null +++ b/test/unit/00.nop-commit.py @@ -0,0 +1,37 @@ +import consus + +c1 = consus.Client() +t1 = c1.begin_transaction() +t1.commit() + +c2 = consus.Client(b'127.0.0.1') +t2 = c1.begin_transaction() +t2.commit() + +c3 = consus.Client('127.0.0.1') +t3 = c1.begin_transaction() +t3.commit() + +c4 = consus.Client(b'127.0.0.1', 1982) +t4 = c1.begin_transaction() +t4.commit() + +c5 = consus.Client('127.0.0.1', 1982) +t5 = c1.begin_transaction() +t5.commit() + +c6 = consus.Client(b'127.0.0.1:1982') +t6 = c1.begin_transaction() +t6.commit() + +c7 = consus.Client('127.0.0.1:1982') +t7 = c1.begin_transaction() +t7.commit() + +c8 = consus.Client(b'[::]:1982,127.0.0.1:1982') +t8 = c1.begin_transaction() +t8.commit() + +c9 = consus.Client('[::]:1982,127.0.0.1:1982') +t9 = c1.begin_transaction() +t9.commit() diff --git a/test/unit/10.single-get.1n.1dc.gremlin b/test/unit/10.single-get.1n.1dc.gremlin new file mode 100755 index 0000000..36d64d0 --- /dev/null +++ b/test/unit/10.single-get.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-get.py diff --git a/test/unit/10.single-get.2n.1dc.gremlin b/test/unit/10.single-get.2n.1dc.gremlin new file mode 100755 index 0000000..fe02024 --- /dev/null +++ b/test/unit/10.single-get.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-get.py diff --git a/test/unit/10.single-get.3n.1dc.gremlin b/test/unit/10.single-get.3n.1dc.gremlin new file mode 100755 index 0000000..20457c9 --- /dev/null +++ b/test/unit/10.single-get.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-get.py diff --git a/test/unit/10.single-get.4n.1dc.gremlin b/test/unit/10.single-get.4n.1dc.gremlin new file mode 100755 index 0000000..739ee5f --- /dev/null +++ b/test/unit/10.single-get.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-get.py diff --git a/test/unit/10.single-get.5n.1dc.gremlin b/test/unit/10.single-get.5n.1dc.gremlin new file mode 100755 index 0000000..a9897bd --- /dev/null +++ b/test/unit/10.single-get.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-get.py diff --git a/test/unit/10.single-get.py b/test/unit/10.single-get.py new file mode 100644 index 0000000..1662c32 --- /dev/null +++ b/test/unit/10.single-get.py @@ -0,0 +1,6 @@ +import consus + +c = consus.Client() +t = c.begin_transaction() +assert t.get('the table', 'the key') is None +t.commit() diff --git a/test/unit/10.single-put.1n.1dc.gremlin b/test/unit/10.single-put.1n.1dc.gremlin new file mode 100755 index 0000000..d0eb2bb --- /dev/null +++ b/test/unit/10.single-put.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-put.py diff --git a/test/unit/10.single-put.2n.1dc.gremlin b/test/unit/10.single-put.2n.1dc.gremlin new file mode 100755 index 0000000..dbc6233 --- /dev/null +++ b/test/unit/10.single-put.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-put.py diff --git a/test/unit/10.single-put.3n.1dc.gremlin b/test/unit/10.single-put.3n.1dc.gremlin new file mode 100755 index 0000000..bbd7102 --- /dev/null +++ b/test/unit/10.single-put.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-put.py diff --git a/test/unit/10.single-put.4n.1dc.gremlin b/test/unit/10.single-put.4n.1dc.gremlin new file mode 100755 index 0000000..d315ee4 --- /dev/null +++ b/test/unit/10.single-put.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-put.py diff --git a/test/unit/10.single-put.5n.1dc.gremlin b/test/unit/10.single-put.5n.1dc.gremlin new file mode 100755 index 0000000..090c428 --- /dev/null +++ b/test/unit/10.single-put.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/10.single-put.py diff --git a/test/unit/10.single-put.py b/test/unit/10.single-put.py new file mode 100644 index 0000000..748a3a8 --- /dev/null +++ b/test/unit/10.single-put.py @@ -0,0 +1,6 @@ +import consus + +c = consus.Client() +t = c.begin_transaction() +assert t.put('the table', 'the key', 'the value') +t.commit() diff --git a/test/unit/11.put-get-separate-commits.1n.1dc.gremlin b/test/unit/11.put-get-separate-commits.1n.1dc.gremlin new file mode 100755 index 0000000..5e72c56 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.1n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../1-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/11.put-get-separate-commits.py diff --git a/test/unit/11.put-get-separate-commits.2n.1dc.gremlin b/test/unit/11.put-get-separate-commits.2n.1dc.gremlin new file mode 100755 index 0000000..c13fc98 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.2n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../2-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/11.put-get-separate-commits.py diff --git a/test/unit/11.put-get-separate-commits.3n.1dc.gremlin b/test/unit/11.put-get-separate-commits.3n.1dc.gremlin new file mode 100755 index 0000000..0316dd8 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.3n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../3-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/11.put-get-separate-commits.py diff --git a/test/unit/11.put-get-separate-commits.4n.1dc.gremlin b/test/unit/11.put-get-separate-commits.4n.1dc.gremlin new file mode 100755 index 0000000..7fea9b6 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.4n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../4-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/11.put-get-separate-commits.py diff --git a/test/unit/11.put-get-separate-commits.5n.1dc.gremlin b/test/unit/11.put-get-separate-commits.5n.1dc.gremlin new file mode 100755 index 0000000..cf217e2 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.5n.1dc.gremlin @@ -0,0 +1,4 @@ +#!/usr/bin/env gremlin +include ../5-node-1-dc-cluster.gremlin +timeout 30 +run python ${CONSUS_SRCDIR}/test/unit/11.put-get-separate-commits.py diff --git a/test/unit/11.put-get-separate-commits.py b/test/unit/11.put-get-separate-commits.py new file mode 100644 index 0000000..7f95309 --- /dev/null +++ b/test/unit/11.put-get-separate-commits.py @@ -0,0 +1,15 @@ +import consus + +c = consus.Client() + +t = c.begin_transaction() +assert t.get('the table', 'the key') is None +t.commit() + +t = c.begin_transaction() +assert t.put('the table', 'the key', 'the value') +t.commit() + +t = c.begin_transaction() +assert t.get('the table', 'the key') == 'the value' +t.commit() diff --git a/tools/availability-check.cc b/tools/availability-check.cc new file mode 100644 index 0000000..dce7d1d --- /dev/null +++ b/tools/availability-check.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include +#include + +// consus +#include +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + long txmans = 0; + long txman_groups = 0; + long kvss = 0; + long timeout = 10; + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS]"); + ap.arg().long_name("transaction-managers") + .description("wait for N transaction-managers to join the cluster (default: 0)") + .metavar("N").as_long(&txmans); + ap.arg().long_name("transaction-manager-groups") + .description("wait for N transaction-manager groups to form (default: 0)") + .metavar("N").as_long(&txman_groups); + ap.arg().long_name("key-value-stores") + .description("wait for N key-value-stores to join the cluster (default: 0)") + .metavar("N").as_long(&kvss); + ap.arg().name('t', "timeout") + .description("wait at most S seconds (default: 10)") + .metavar("S").as_long(&timeout); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-availability-check: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "consus-availability-check takes zero positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (txmans < 0 || kvss < 0) + { + std::cerr << "consus-availability-check: negative values make no sense\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-availability-check: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + e::guard g_cl = e::makeguard(consus_destroy, cl); + consus_returncode rc; + consus_availability_requirements reqs; + reqs.txmans = txmans; + reqs.txman_groups = txman_groups; + reqs.kvss = kvss; + + if (consus_admin_availability_check(cl, &reqs, timeout, &rc) < 0) + { + std::cerr << "consus-availability-check: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tools/common.cc b/tools/common.cc new file mode 100644 index 0000000..97b3baa --- /dev/null +++ b/tools/common.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// replicant +#include + +// consus +#include "tools/common.h" + +bool +consus :: finish(consus_client* cl, const char* prog, int64_t id, consus_returncode* status) +{ + if (id < 0) + { + std::cerr << prog << ": " << consus_error_message(cl) << std::endl; + return false; + } + + consus_returncode lrc; + int64_t lid = consus_wait(cl, id, -1, &lrc); + + if (lid < 0) + { + std::cerr << prog << ": " << consus_error_message(cl) << std::endl; + return false; + } + + assert(id == lid); + + if (*status != CONSUS_SUCCESS) + { + std::cerr << prog << ": " << consus_error_message(cl) << std::endl; + return false; + } + + return true; +} diff --git a/tools/common.h b/tools/common.h new file mode 100644 index 0000000..89504f7 --- /dev/null +++ b/tools/common.h @@ -0,0 +1,24 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_tools_common_h_ +#define consus_tools_common_h_ + +// STL +#include + +// e +#include + +// consus +#include +#include "namespace.h" +#include "tools/connect_opts.h" + +BEGIN_CONSUS_NAMESPACE + +bool finish(consus_client* cl, const char* prog, int64_t id, consus_returncode* status); + +END_CONSUS_NAMESPACE + +#endif // consus_tools_common_h_ diff --git a/tools/connect_opts.cc b/tools/connect_opts.cc new file mode 100644 index 0000000..5b7cc29 --- /dev/null +++ b/tools/connect_opts.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// replicant +#include + +// consus +#include "tools/connect_opts.h" + +using consus::connect_opts; + +connect_opts :: connect_opts() + : m_ap() + , m_connect1(false) + , m_connect_host("127.0.0.1") + , m_connect_port(1982) + , m_connect2(false) + , m_connect_string(NULL) + , m_valid_conn_str() +{ + create_parser('h', "host", 'p', "port", 'c', "cluster"); +} + +connect_opts :: connect_opts(char hn, const char* host_name, + char pn, const char* port_name, + char sn, const char* str_name) + : m_ap() + , m_connect1(false) + , m_connect_host("127.0.0.1") + , m_connect_port(1982) + , m_connect2(false) + , m_connect_string(NULL) + , m_valid_conn_str() +{ + create_parser(hn, host_name, pn, port_name, sn, str_name); +} + +connect_opts :: ~connect_opts() throw () +{ +} + +bool +connect_opts :: validate() +{ + char* conn_str = NULL; + + if (m_connect1 && m_connect2) + { + conn_str = replicant_client_add_to_conn_str(m_connect_string, m_connect_host, m_connect_port); + } + else if (m_connect1) + { + conn_str = replicant_client_host_to_conn_str(m_connect_host, m_connect_port); + } + else if (m_connect2) + { + conn_str = replicant_client_validate_conn_str(m_connect_string); + } + else + { + conn_str = strdup("127.0.0.1:1982"); + } + + if (!conn_str) + { + return false; + } + + m_valid_conn_str = conn_str; + free(conn_str); + return true; +} + +void +connect_opts :: create_parser(char hn, const char* host_name, + char pn, const char* port_name, + char sn, const char* str_name) +{ + m_ap.arg().name(hn, host_name) + .description("connect to an IP address or hostname (default: 127.0.0.1)") + .metavar("addr").as_string(&m_connect_host).set_true(&m_connect1); + m_ap.arg().name(pn, port_name) + .description("connect to an alternative port (default: 1982)") + .metavar("port").as_long(&m_connect_port).set_true(&m_connect1); + m_ap.arg().name(sn, str_name) + .description("connect to a list of hosts (default: none)") + .metavar("hosts").as_string(&m_connect_string).set_true(&m_connect2); +} + +bool +connect_opts :: isset() +{ + return m_connect1 || m_connect2; +} + +const char* +connect_opts :: conn_str() +{ + bool valid = validate(); + assert(valid); + return m_valid_conn_str.c_str(); +} diff --git a/tools/connect_opts.h b/tools/connect_opts.h new file mode 100644 index 0000000..31a6220 --- /dev/null +++ b/tools/connect_opts.h @@ -0,0 +1,55 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_tools_connect_opts_h_ +#define consus_tools_connect_opts_h_ + +// STL +#include + +// e +#include + +// consus +#include +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +class connect_opts +{ + public: + connect_opts(); + connect_opts(char hn, const char* host_name, + char pn, const char* port_name, + char sn, const char* str_name); + ~connect_opts() throw (); + + public: + const e::argparser& parser() { return m_ap; } + bool validate(); + bool isset(); + const char* conn_str(); + + private: + void create_parser(char hn, const char* host_name, + char pn, const char* port_name, + char sn, const char* str_name); + + private: + e::argparser m_ap; + bool m_connect1; + const char* m_connect_host; + long m_connect_port; + bool m_connect2; + const char* m_connect_string; + std::string m_valid_conn_str; + + private: + connect_opts(const connect_opts&); + connect_opts& operator = (const connect_opts&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_tools_connect_opts_h_ diff --git a/tools/coordinator.cc b/tools/coordinator.cc new file mode 100644 index 0000000..6c270d4 --- /dev/null +++ b/tools/coordinator.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include +#include +#include + +// POSIX +#include + +// C++ +#include + +// STL +#include + +// po6 +#include +#include + +// consus +#include "tools/common.h" + +#ifdef CONSUS_EXEC_DIR +#define CONSUS_LIB_NAME "libconsus-coordinator" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wlarger-than=" +static bool +locate_coordinator_lib(const char* argv0, std::string* path) +{ + // find the right library + std::vector paths; + const char* env = getenv("CONSUS_COORD_LIB"); + static const char* exts[] = { "", ".so.0.0.0", ".so.0", ".so", ".dylib", 0 }; + + for (size_t i = 0; exts[i]; ++i) + { + std::string base(CONSUS_LIB_NAME); + base += exts[i]; + paths.push_back(po6::path::join(CONSUS_EXEC_DIR, base)); + paths.push_back(po6::path::join(po6::path::dirname(argv0), ".libs", base)); + + if (env) + { + std::string envlib(env); + envlib += exts[i]; + paths.push_back(envlib); + } + } + + // maybe we're running out of Git. make it "just work" + char selfbuf[PATH_MAX + 1]; + memset(selfbuf, 0, sizeof(selfbuf)); + + if (readlink("/proc/self/exe", selfbuf, PATH_MAX) >= 0) + { + std::string workdir(selfbuf); + workdir = po6::path::dirname(workdir); + std::string gitdir(po6::path::join(workdir, ".git")); + struct stat buf; + + if (stat(gitdir.c_str(), &buf) == 0 && + S_ISDIR(buf.st_mode)) + { + std::string libdir(po6::path::join(workdir, ".libs")); + + for (size_t i = 0; exts[i]; ++i) + { + std::string libname(CONSUS_LIB_NAME); + libname += exts[i]; + paths.push_back(po6::path::join(libdir, libname)); + } + } + } + + size_t idx = 0; + + while (idx < paths.size()) + { + struct stat buf; + + if (stat(paths[idx].c_str(), &buf) == 0) + { + *path = paths[idx]; + return true; + } + + ++idx; + } + + return false; +} +#pragma GCC diagnostic pop +#undef CONSUS_LIB_NAME +#endif // CONSUS_EXEC_DIR + +int +main(int argc, const char* argv[]) +{ + std::string libpath; + + if (!locate_coordinator_lib(argv[0], &libpath)) + { + std::cerr << "cannot locate the consus coordinator library" << std::endl; + return EXIT_FAILURE; + } + + // setup the environment + if (setenv("REPLICANT_WRAP", "consus-coordinator", 1) < 0) + { + std::cerr << "could not setup the environment: " << po6::strerror(errno) << std::endl; + return EXIT_FAILURE; + } + + // generate a random token + uint64_t token; + po6::io::fd sysrand(open("/dev/urandom", O_RDONLY)); + + if (sysrand.get() < 0 || + sysrand.read(&token, sizeof(token)) != sizeof(token)) + { + std::cerr << "could not generate random token for cluster" << std::endl; + return EXIT_FAILURE; + } + + char token_buf[21]; + snprintf(token_buf, 21, "%lu", (unsigned long) token); + + // exec replicant daemon + std::vector args; + args.push_back("replicant"); + args.push_back("daemon"); + + for (int i = 1; i < argc; ++i) + { + args.push_back(argv[i]); + } + + args.push_back("--object"); + args.push_back("consus"); + args.push_back("--library"); + args.push_back(libpath.c_str()); + args.push_back("--init-string"); + args.push_back(token_buf); + args.push_back(NULL); + + if (execvp("replicant", const_cast(&args[0])) < 0) + { + perror("could not exec replicant"); + return EXIT_FAILURE; + } + + abort(); +} diff --git a/tools/create-data-center.cc b/tools/create-data-center.cc new file mode 100644 index 0000000..3e9f6d4 --- /dev/null +++ b/tools/create-data-center.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include +#include + +// consus +#include +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS] "); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-create-data-center: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 1) + { + std::cerr << "consus-create-data-center takes one positional argument\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-create-data-center: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + e::guard g_cl = e::makeguard(consus_destroy, cl); + consus_returncode rc; + + if (consus_admin_create_data_center(cl, ap.args()[0], &rc) < 0) + { + std::cerr << "consus-create-data-center: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/tools/debug-client-configuration.cc b/tools/debug-client-configuration.cc new file mode 100644 index 0000000..15d9197 --- /dev/null +++ b/tools/debug-client-configuration.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include +#include + +// consus +#include +#include "client/consus-internal.h" +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS]"); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-debug-client-configuration: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "consus-debug-client-configuration takes zero positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-debug-client-configuration: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + e::guard g_cl = e::makeguard(consus_destroy, cl); + consus_returncode rc; + const char* str = NULL; + + if (consus_debug_client_configuration(cl, &rc, &str) < 0) + { + std::cerr << "consus-debug-client-configuration: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + std::cout << str << std::flush; + return EXIT_SUCCESS; +} diff --git a/tools/debug-kvs-configuration.cc b/tools/debug-kvs-configuration.cc new file mode 100644 index 0000000..a6446e5 --- /dev/null +++ b/tools/debug-kvs-configuration.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include + +// consus +#include +#include "client/consus-internal.h" +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS]"); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-debug-kvs-configuration: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "consus-debug-kvs-configuration takes zero positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-debug-kvs-configuration: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + consus_returncode rc; + const char* str = NULL; + + if (consus_debug_kvs_configuration(cl, &rc, &str) < 0) + { + std::cerr << "consus-debug-kvs-configuration: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + std::cout << str << std::flush; + consus_destroy(cl); + return EXIT_SUCCESS; +} diff --git a/tools/debug-txman-configuration.cc b/tools/debug-txman-configuration.cc new file mode 100644 index 0000000..4cc973f --- /dev/null +++ b/tools/debug-txman-configuration.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include + +// consus +#include +#include "client/consus-internal.h" +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS]"); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-debug-txman-configuration: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "consus-debug-txman-configuration takes zero positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-debug-txman-configuration: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + consus_returncode rc; + const char* str = NULL; + + if (consus_debug_txman_configuration(cl, &rc, &str) < 0) + { + std::cerr << "consus-debug-txman-configuration: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + std::cout << str << std::flush; + consus_destroy(cl); + return EXIT_SUCCESS; +} diff --git a/tools/locate-coordinator-lib.h b/tools/locate-coordinator-lib.h new file mode 100644 index 0000000..3eacd24 --- /dev/null +++ b/tools/locate-coordinator-lib.h @@ -0,0 +1,43 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_tools_common_h_ +#define consus_tools_common_h_ + +// STL +#include + +// e +#include + +// consus +#include +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +class connect_opts +{ + public: + connect_opts(); + ~connect_opts() throw (); + + public: + const e::argparser& parser() { return m_ap; } + bool validate(); + consus_client* create(); + + private: + e::argparser m_ap; + + private: + connect_opts(const connect_opts&); + connect_opts& operator = (const connect_opts&); +}; + +bool finish(consus_client* cl, const char* prog, int64_t id, consus_returncode* status); +bool locate_coordinator_lib(const char* argv0, std::string* path); + +END_CONSUS_NAMESPACE + +#endif // consus_tools_common_h_ diff --git a/tools/set-default-data-center.cc b/tools/set-default-data-center.cc new file mode 100644 index 0000000..49ed0f4 --- /dev/null +++ b/tools/set-default-data-center.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// e +#include +#include + +// consus +#include +#include "tools/common.h" + +int +main(int argc, const char* argv[]) +{ + consus::connect_opts conn; + e::argparser ap; + ap.autohelp(); + ap.option_string("[OPTIONS] "); + ap.add("Connect to a cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "consus-set-default-data-center: invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 1) + { + std::cerr << "consus-set-default-data-center takes one positional argument\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + consus_client* cl = consus_create_conn_str(conn.conn_str()); + + if (!cl) + { + std::cerr << "consus-set-default-data-center: memory allocation failed" << std::endl; + return EXIT_FAILURE; + } + + e::guard g_cl = e::makeguard(consus_destroy, cl); + consus_returncode rc; + + if (consus_admin_set_default_data_center(cl, ap.args()[0], &rc) < 0) + { + std::cerr << "consus-set-default-data-center: " << consus_error_message(cl) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/txman/configuration.cc b/txman/configuration.cc new file mode 100644 index 0000000..50e737d --- /dev/null +++ b/txman/configuration.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include + +// consus +#include "common/txman_configuration.h" +#include "txman/configuration.h" + +using consus::configuration; + +configuration :: configuration() + : m_cluster() + , m_version() + , m_flags(0) + , m_dcs() + , m_txmans() + , m_paxos_groups() + , m_kvss() +{ +} + +configuration :: ~configuration() throw () +{ +} + +bool +configuration :: exists(comm_id id) const +{ + for (size_t i = 0; i < m_txmans.size(); ++i) + { + if (m_txmans[i].tx.id == id) + { + return true; + } + } + + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].id == id) + { + return true; + } + } + + return false; +} + +po6::net::location +configuration :: get_address(comm_id id) const +{ + for (size_t i = 0; i < m_txmans.size(); ++i) + { + if (m_txmans[i].tx.id == id) + { + return m_txmans[i].tx.bind_to; + } + } + + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].id == id) + { + return m_kvss[i].bind_to; + } + } + + return po6::net::location(); +} + +consus::txman_state::state_t +configuration :: get_state(comm_id id) const +{ + for (size_t i = 0; i < m_txmans.size(); ++i) + { + if (m_txmans[i].tx.id == id) + { + return m_txmans[i].state; + } + } + + return txman_state::state_t(); +} + +std::vector +configuration :: groups_for(comm_id id) const +{ + std::vector gs; + + for (size_t i = 0; i < m_paxos_groups.size(); ++i) + { + for (size_t p = 0; p < m_paxos_groups[i].members_sz; ++p) + { + if (m_paxos_groups[i].members[p] == id) + { + gs.push_back(m_paxos_groups[i].id); + break; + } + } + } + + return gs; +} + +const consus::paxos_group* +configuration :: get_group(paxos_group_id id) const +{ + for (size_t i = 0; i < m_paxos_groups.size(); ++i) + { + if (m_paxos_groups[i].id == id) + { + return &m_paxos_groups[i]; + } + } + + return NULL; +} + +bool +configuration :: is_member(paxos_group_id gid, comm_id id) const +{ + const paxos_group* g = get_group(gid); + + if (!g) + { + return false; + } + + for (unsigned i = 0; i < g->members_sz; ++i) + { + if (id == g->members[i]) + { + return true; + } + } + + return false; +} + +bool +configuration :: choose_groups(paxos_group_id g, std::vector* others) const +{ + others->push_back(g); + std::set dcs; + const paxos_group* gptr = get_group(g); + assert(gptr); + dcs.insert(gptr->dc); + + for (size_t i = 0; i < m_paxos_groups.size(); ++i) + { + if (dcs.find(m_paxos_groups[i].dc) == dcs.end()) + { + others->push_back(m_paxos_groups[i].id); + dcs.insert(m_paxos_groups[i].dc); + } + } + + return others->size() >= (m_dcs.size() / 2 + 1); +} + +consus::comm_id +configuration :: choose_kvs(data_center_id dc) const +{ + for (size_t i = 0; i < m_kvss.size(); ++i) + { + if (m_kvss[i].dc == dc) + { + return m_kvss[i].id; + } + } + + return comm_id(); +} + +std::string +configuration :: dump() const +{ + return txman_configuration(m_cluster, m_version, m_flags, m_dcs, m_txmans, m_paxos_groups, m_kvss); +} + +e::unpacker +consus :: operator >> (e::unpacker up, configuration& c) +{ + return txman_configuration(up, &c.m_cluster, &c.m_version, &c.m_flags, &c.m_dcs, &c.m_txmans, &c.m_paxos_groups, &c.m_kvss); +} diff --git a/txman/configuration.h b/txman/configuration.h new file mode 100644 index 0000000..e0a955e --- /dev/null +++ b/txman/configuration.h @@ -0,0 +1,75 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_configuration_h_ +#define consus_txman_configuration_h_ + +// consus +#include "namespace.h" +#include "common/data_center.h" +#include "common/ids.h" +#include "common/kvs.h" +#include "common/paxos_group.h" +#include "common/txman.h" +#include "common/txman_state.h" + +BEGIN_CONSUS_NAMESPACE + +class configuration +{ + public: + configuration(); + ~configuration() throw (); + + // metadata + public: + cluster_id cluster() const { return m_cluster; } + version_id version() const { return m_version; } + uint64_t timestamp_bottom() const { return 0; /* XXX */ } + + // transaction managers + public: + bool exists(comm_id id) const; + po6::net::location get_address(comm_id id) const; + txman_state::state_t get_state(comm_id id) const; + + // transaction manager paxos groups + public: + std::vector groups_for(comm_id id) const; + const paxos_group* get_group(paxos_group_id id) const; + bool is_member(paxos_group_id g, comm_id id) const; + bool choose_groups(paxos_group_id g, std::vector* others) const; + + // key-value stores + public: + comm_id choose_kvs(data_center_id dc) const; + + // debug/internal + public: + std::string dump() const; + + private: + friend e::unpacker operator >> (e::unpacker, configuration& s); + + private: + cluster_id m_cluster; + version_id m_version; + uint64_t m_flags; + std::vector m_dcs; + std::vector m_txmans; + std::vector m_paxos_groups; + std::vector m_kvss; + + private: + configuration(const configuration& other); + configuration& operator = (const configuration& rhs); +}; + +std::ostream& +operator << (std::ostream& lhs, const configuration& rhs); +e::unpacker +operator >> (e::unpacker lhs, configuration& rhs); + +END_CONSUS_NAMESPACE + +#endif // consus_txman_configuration_h_ diff --git a/txman/daemon.cc b/txman/daemon.cc new file mode 100644 index 0000000..80f7997 --- /dev/null +++ b/txman/daemon.cc @@ -0,0 +1,1214 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include +#include +#include + +// POSIX +#include +#include + +// STL +#include + +// Google Log +#include +#include + +// po6 +#include +#include +#include + +// e +#include +#include +#include +#include +#include + +// BusyBee +#include + +// consus +#include "common/coordinator_returncode.h" +#include "common/macros.h" +#include "txman/daemon.h" +#include "txman/log_entry_t.h" + +using consus::daemon; + +#define CHECK_UNPACK(MSGTYPE, UNPACKER) \ + do \ + { \ + if (UNPACKER.error()) \ + { \ + network_msgtype CONCAT(_anon, __LINE__)(MSGTYPE); \ + LOG(WARNING) << "received corrupt \"" \ + << CONCAT(_anon, __LINE__) << "\" message"; \ + return; \ + } \ + } while (0) + +uint32_t s_interrupts = 0; +bool s_debug_dump = false; +bool s_debug_mode = false; + +static void +exit_on_signal(int /*signum*/) +{ + RAW_LOG(ERROR, "interrupted: exiting"); + e::atomic::increment_32_nobarrier(&s_interrupts, 1); +} + +static void +handle_debug_dump(int /*signum*/) +{ + s_debug_dump = true; +} + +static void +handle_debug_mode(int /*signum*/) +{ + s_debug_mode = !s_debug_mode; +} + +struct daemon::coordinator_callback : public coordinator_link::callback +{ + coordinator_callback(daemon* d); + virtual ~coordinator_callback() throw (); + virtual std::string prefix() { return "txman"; } + virtual bool new_config(const char* data, size_t data_sz); + virtual bool has_id(comm_id id); + virtual po6::net::location address(comm_id id); + virtual bool is_steady_state(comm_id id); + + private: + daemon* d; + coordinator_callback(const coordinator_callback&); + coordinator_callback& operator = (const coordinator_callback&); +}; + +daemon :: coordinator_callback :: coordinator_callback(daemon* _d) + : d(_d) +{ +} + +daemon :: coordinator_callback :: ~coordinator_callback() throw () +{ +} + +static std::vector +split_by_newlines(std::string s) +{ + std::vector v; + + while (!s.empty()) + { + size_t idx = s.find_first_of('\n'); + + if (idx == std::string::npos) + { + v.push_back(s); + s = ""; + } + else + { + v.push_back(s.substr(0, idx)); + s = s.substr(idx + 1, s.size()); + } + } + + return v; +} + +bool +daemon :: coordinator_callback :: new_config(const char* data, size_t data_sz) +{ + std::auto_ptr c(new configuration()); + e::unpacker up(data, data_sz); + up = up >> *c; + + if (up.error() || up.remain()) + { + LOG(ERROR) << "received a bad configuration"; + return false; + } + + configuration* old_config = d->get_config(); + e::atomic::store_ptr_release(&d->m_config, c.release()); + d->m_gc.collect(old_config, e::garbage_collector::free_ptr); + LOG(INFO) << "updating to configuration " << d->get_config()->version(); + std::vector gs = d->get_config()->groups_for(d->m_us.id); + + if (s_debug_mode) + { + std::string debug = d->get_config()->dump(); + std::vector lines = split_by_newlines(debug); + LOG(INFO) << "=== begin debug dump of configuration ==="; + + for (size_t i = 0; i < lines.size(); ++i) + { + LOG(INFO) << lines[i]; + } + + LOG(INFO) << "active paxos groups for this transaction-manager:"; + + for (size_t i = 0; i < gs.size(); ++i) + { + LOG(INFO) << gs[i]; + } + + LOG(INFO) << "=== end debug dump of configuration ==="; + } + + return true; +} + +bool +daemon :: coordinator_callback :: has_id(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->exists(id); + } + + return false; +} + +po6::net::location +daemon :: coordinator_callback :: address(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->get_address(id); + } + + return po6::net::location(); +} + +bool +daemon :: coordinator_callback :: is_steady_state(comm_id id) +{ + configuration* c = d->get_config(); + + if (c) + { + return c->get_state(id) == txman_state::ONLINE; + } + + return false; +} + +daemon :: daemon() + : m_us() + , m_gc() + , m_busybee_mapper(this) + , m_busybee() + , m_coord_cb() + , m_coord() + , m_config(NULL) + , m_threads() + , m_transactions(&m_gc) + , m_local_voters(&m_gc) + , m_dispositions(&m_gc) + , m_log() + , m_durable_thread(po6::threads::make_thread_wrapper(&daemon::durable, this)) + , m_durable_mtx() + , m_durable_up_to(-1) + , m_durable_msgs() + , m_durable_cbs() +{ +} + +daemon :: ~daemon() throw () +{ + m_gc.collect(get_config(), e::garbage_collector::free_ptr); +} + +int +daemon :: run(bool background, + std::string data, + std::string log, + std::string pidfile, + bool has_pidfile, + bool set_bind_to, + po6::net::location bind_to, + bool set_coordinator, + const char* coordinator, + unsigned threads) +{ + if (!e::block_all_signals()) + { + std::cerr << "could not block signals; exiting" << std::endl; + return EXIT_FAILURE; + } + + if (!e::daemonize(background, log, "consus-txman-", pidfile, has_pidfile)) + { + return EXIT_FAILURE; + } + + if (!e::install_signal_handler(SIGHUP, exit_on_signal) || + !e::install_signal_handler(SIGINT, exit_on_signal) || + !e::install_signal_handler(SIGTERM, exit_on_signal) || + !e::install_signal_handler(SIGQUIT, exit_on_signal) || + !e::install_signal_handler(SIGUSR1, handle_debug_dump) || + !e::install_signal_handler(SIGUSR2, handle_debug_mode)) + { + PLOG(ERROR) << "could not install signal handlers"; + return EXIT_FAILURE; + } + + if (!m_log.open(data)) + { + LOG(ERROR) << "could not open log: " << po6::strerror(m_log.error()); + return EXIT_FAILURE; + } + + bool saved; + uint64_t id; + std::string rendezvous(coordinator); + + if (!e::load_identity(po6::path::join(data, "TXMAN").c_str(), &saved, &id, + set_bind_to, &bind_to, set_coordinator, &rendezvous)) + { + LOG(ERROR) << "could not load prior identity; exiting"; + return EXIT_FAILURE; + } + + m_us.id = comm_id(id); + m_us.bind_to = bind_to; + bool (coordinator_link::*coordfunc)(); + + if (saved) + { + coordfunc = &coordinator_link::establish; + } + else + { + if (!e::generate_token(&id)) + { + PLOG(ERROR) << "could not read random token from /dev/urandom"; + return EXIT_FAILURE; + } + + m_us.id = comm_id(id); + coordfunc = &coordinator_link::initial_registration; + } + + m_coord_cb.reset(new coordinator_callback(this)); + m_coord.reset(new coordinator_link(rendezvous, m_us.id, m_us.bind_to, m_coord_cb.get())); + m_coord->allow_reregistration(); + LOG(INFO) << "starting consus transaction-manager " << m_us.id + << " on address " << m_us.bind_to; + LOG(INFO) << "connecting to " << rendezvous; + + if (!(((*m_coord).*coordfunc)())) + { + return EXIT_FAILURE; + } + + assert(get_config()); + + if (!e::save_identity(po6::path::join(data, "TXMAN").c_str(), id, bind_to, rendezvous)) + { + LOG(ERROR) << "could not save identity; exiting"; + return EXIT_FAILURE; + } + + m_busybee.reset(new busybee_mta(&m_gc, &m_busybee_mapper, bind_to, id, threads)); + m_durable_thread.start(); + + for (size_t i = 0; i < threads; ++i) + { + using namespace po6::threads; + e::compat::shared_ptr t(new thread(make_thread_wrapper(&daemon::loop, this, i))); + m_threads.push_back(t); + t->start(); + } + + while (e::atomic::increment_32_nobarrier(&s_interrupts, 0) == 0) + { + bool debug_mode = s_debug_mode; + m_coord->maintain_connection(); + + if (m_coord->error()) + { + break; + } + + if (m_coord->orphaned()) + { + LOG(ERROR) << "server removed from cluster; exiting"; + break; + } + + if (s_debug_mode != debug_mode) + { + if (s_debug_mode) + { + debug_dump(); + LOG(INFO) << "enabling debug mode; will log all state transitions"; + s_debug_dump = false; + } + else + { + LOG(INFO) << "disabling debug mode; will go back to normal operation"; + } + } + + if (s_debug_dump) + { + debug_dump(); + s_debug_dump = false; + } + } + + e::atomic::increment_32_nobarrier(&s_interrupts, 1); + m_busybee->shutdown(); + + for (size_t i = 0; i < m_threads.size(); ++i) + { + m_threads[i]->join(); + } + + m_log.close(); + m_durable_thread.join(); + LOG(ERROR) << "consus is gracefully shutting down"; + return EXIT_SUCCESS; +} + +void +daemon :: loop(size_t thread) +{ + size_t core = thread % sysconf(_SC_NPROCESSORS_ONLN); +#ifdef __LINUX__ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(core, &cpuset); + pthread_t cur = pthread_self(); + int x = pthread_setaffinity_np(cur, sizeof(cpu_set_t), &cpuset); + assert(x == 0); +#elif defined(__APPLE__) + thread_affinity_policy_data_t policy; + policy.affinity_tag = 0; + thread_policy_set(mach_thread_self(), + THREAD_AFFINITY_POLICY, + (thread_policy_t)&policy, + THREAD_AFFINITY_POLICY_COUNT); +#endif + + LOG(INFO) << "network thread " << thread << " started on core " << core; + + sigset_t ss; + + if (sigfillset(&ss) < 0 || + pthread_sigmask(SIG_SETMASK, &ss, NULL) < 0) + { + std::cerr << "could not block signals" << std::endl; + return; + } + + e::garbage_collector::thread_state ts; + m_gc.register_thread(&ts); + bool done = false; + + while (!done) + { + uint64_t _id; + std::auto_ptr msg; + busybee_returncode rc = m_busybee->recv(&ts, &_id, &msg); + + switch (rc) + { + case BUSYBEE_SUCCESS: + break; + case BUSYBEE_SHUTDOWN: + done = true; + continue; + case BUSYBEE_DISRUPTED: + case BUSYBEE_INTERRUPTED: + continue; + case BUSYBEE_POLLFAILED: + case BUSYBEE_ADDFDFAIL: + case BUSYBEE_TIMEOUT: + case BUSYBEE_EXTERNAL: + default: + LOG(ERROR) << "internal invariants broken; crashing"; + abort(); + } + + comm_id id(_id); + network_msgtype mt; + e::unpacker up = msg->unpack_from(BUSYBEE_HEADER_SIZE); + up = up >> mt; + + if (up.error()) + { + LOG(WARNING) << "dropping message that has a malformed header"; + + if (s_debug_mode) + { + LOG(WARNING) << "here's some hex: " << msg->hex(); + } + + continue; + } + + switch (mt) + { + case TXMAN_BEGIN: + process_begin(id, msg, up); + break; + case TXMAN_READ: + process_read(id, msg, up); + break; + case TXMAN_WRITE: + process_write(id, msg, up); + break; + case TXMAN_COMMIT: + process_commit(id, msg, up); + break; + case TXMAN_ABORT: + process_abort(id, msg, up); + break; + case TXMAN_PAXOS_2A: + process_paxos_2a(id, msg, up); + break; + case TXMAN_PAXOS_2B: + process_paxos_2b(id, msg, up); + break; + case LV_VOTE_1A: + process_lv_vote_1a(id, msg, up); + break; + case LV_VOTE_1B: + process_lv_vote_1b(id, msg, up); + break; + case LV_VOTE_2A: + process_lv_vote_2a(id, msg, up); + break; + case LV_VOTE_2B: + process_lv_vote_2b(id, msg, up); + break; + case LV_VOTE_LEARN: + process_lv_vote_learn(id, msg, up); + break; + case COMMIT_RECORD: + process_commit_record(id, msg, up); + break; + case KVS_RD_LOCKED: + process_kvs_rd_locked(id, msg, up); + break; + case KVS_RD_UNLOCKED: + process_kvs_rd_unlocked(id, msg, up); + break; + case KVS_WR_BEGUN: + process_kvs_wr_begun(id, msg, up); + break; + case KVS_WR_FINISHED: + process_kvs_wr_finished(id, msg, up); + break; + case CONSUS_NOP: + break; + case KVS_RD_LOCK: + case KVS_RD_UNLOCK: + case KVS_WR_BEGIN: + case KVS_WR_FINISH: + case KVS_WR_CANCEL: + case CLIENT_RESPONSE: + default: + LOG(INFO) << "received " << mt << " message which transaction-managers do not process"; + break; + } + + m_gc.quiescent_state(&ts); + } + + m_gc.deregister_thread(&ts); + LOG(INFO) << "network thread shutting down"; +} + +void +daemon :: process_begin(comm_id id, std::auto_ptr, e::unpacker up) +{ + uint64_t nonce; + up = up >> e::unpack_varint(nonce); + CHECK_UNPACK(TXMAN_BEGIN, up); + configuration* c = get_config(); + + while (true) + { + transaction_id txid = generate_txid(); + const paxos_group* group = c->get_group(txid.group); + + if (!group) + { + LOG(ERROR) << "generated txid with invalid paxos group"; + // XXX reply with an error + return; + } + + transaction_group tg(txid); + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.create_state(tg, &tsr); + + if (!xact) + { + continue; + } + + std::vector dcs; + + if (!c->choose_groups(txid.group, &dcs)) + { + LOG(ERROR) << "not enough dcs online"; + // XXX reply with an error + return; + } + + uint64_t ts = po6::wallclock_time(); + xact->begin(id, nonce, ts, *group, dcs, this); + break; + } +} + +void +daemon :: process_read(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_id txid; + uint64_t nonce; + uint64_t seqno; + e::slice table; + e::slice key; + up = up >> txid + >> e::unpack_varint(nonce) + >> e::unpack_varint(seqno) + >> table >> key; + CHECK_UNPACK(TXMAN_READ, up); + + configuration* c = get_config(); + + if (!c->get_group(txid.group)) + { + LOG_IF(INFO, s_debug_mode) << "dropping read for " << txid + << " because the group is not in the configuration"; + return; + } + + if (!c->is_member(txid.group, m_us.id)) + { + LOG_IF(INFO, s_debug_mode) << "dropping read for " << txid + << " this server is not part of the group"; + return; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(transaction_group(txid), &tsr); + assert(xact); + xact->read(id, nonce, seqno, table, key, msg, this); +} + +void +daemon :: process_write(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_id txid; + uint64_t nonce; + uint64_t seqno; + e::slice table; + e::slice key; + e::slice value; + up = up >> txid + >> e::unpack_varint(nonce) + >> e::unpack_varint(seqno) + >> table >> key >> value; + CHECK_UNPACK(TXMAN_WRITE, up); + + if (!get_config()->get_group(txid.group)) + { + LOG_IF(INFO, s_debug_mode) << "dropping it because " << txid.group + << " not in configuration"; + return; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(transaction_group(txid), &tsr); + assert(xact); + xact->write(id, nonce, seqno, table, key, value, msg, this); +} + +void +daemon :: process_commit(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_id txid; + uint64_t nonce; + uint64_t seqno; + up = up >> txid + >> e::unpack_varint(nonce) + >> e::unpack_varint(seqno); + CHECK_UNPACK(TXMAN_COMMIT, up); + + if (!get_config()->get_group(txid.group)) + { + LOG_IF(INFO, s_debug_mode) << "dropping it because " << txid.group + << " not in configuration"; + return; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(transaction_group(txid), &tsr); + assert(xact); + xact->commit(id, nonce, seqno, this); +} + +void +daemon :: process_abort(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_id txid; + uint64_t nonce; + uint64_t seqno; + up = up >> txid + >> e::unpack_varint(nonce) + >> e::unpack_varint(seqno); + CHECK_UNPACK(TXMAN_ABORT, up); + + if (!get_config()->get_group(txid.group)) + { + LOG_IF(INFO, s_debug_mode) << "dropping it because " << txid.group + << " not in configuration"; + return; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(transaction_group(txid), &tsr); + assert(xact); + xact->abort(id, nonce, seqno, this); +} + +void +daemon :: process_paxos_2a(comm_id, std::auto_ptr msg, e::unpacker up) +{ + e::slice log_entry; + up = up >> log_entry; + CHECK_UNPACK(TXMAN_PAXOS_2A, up); + log_entry_t t = LOG_ENTRY_NOP; + transaction_group tg; + uint64_t seqno = 0; + up = e::unpacker(log_entry) >> t >> tg >> seqno; + + if (up.error() || !is_paxos_2a_log_entry(t)) + { + LOG(ERROR) << "dropping corrupt paxos 2A log entry"; + + if (s_debug_mode) + { + LOG(ERROR) << "here's some hex: " << msg->hex(); + } + + return; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(tg, &tsr); + assert(xact); + xact->paxos_2a(seqno, t, up, msg, this); +} + +void +daemon :: process_paxos_2b(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + up = up >> tg >> seqno; + CHECK_UNPACK(TXMAN_PAXOS_2B, up); + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(tg, &tsr); + assert(xact); + xact->paxos_2b(id, seqno, this); +} + +void +daemon :: process_lv_vote_1a(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint8_t idx; + paxos_synod::ballot b; + up = up >> tg >> idx >> b; + CHECK_UNPACK(LV_VOTE_1A, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << ".synod_commit[" << unsigned(idx) << "] vote 1A " << b; + } + + local_voter_map_t::state_reference lvsr; + local_voter* lv = m_local_voters.get_or_create_state(tg, &lvsr); + assert(lv); + lv->vote_1a(id, idx, b, this); +} + +void +daemon :: process_lv_vote_1b(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint8_t idx; + paxos_synod::ballot b; + paxos_synod::pvalue p; + up = up >> tg >> idx >> b >> p; + CHECK_UNPACK(LV_VOTE_1B, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << ".synod_commit[" << unsigned(idx) << "] vote 1B " << b << " " << p; + } + + local_voter_map_t::state_reference lvsr; + local_voter* lv = m_local_voters.get_or_create_state(tg, &lvsr); + assert(lv); + lv->vote_1b(id, idx, b, p, this); +} + +void +daemon :: process_lv_vote_2a(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint8_t idx; + paxos_synod::pvalue p; + up = up >> tg >> idx >> p; + CHECK_UNPACK(LV_VOTE_2A, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << ".synod_commit[" << unsigned(idx) << "] vote 2A " << p; + } + + local_voter_map_t::state_reference lvsr; + local_voter* lv = m_local_voters.get_or_create_state(tg, &lvsr); + assert(lv); + lv->vote_2a(id, idx, p, this); +} + +void +daemon :: process_lv_vote_2b(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint8_t idx; + paxos_synod::pvalue p; + up = up >> tg >> idx >> p; + CHECK_UNPACK(LV_VOTE_2B, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << ".synod_commit[" << unsigned(idx) << "] vote 2B " << p << " from " << id.get(); + } + + local_voter_map_t::state_reference lvsr; + local_voter* lv = m_local_voters.get_or_create_state(tg, &lvsr); + assert(lv); + lv->vote_2b(id, idx, p, this); +} + +void +daemon :: process_lv_vote_learn(comm_id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint8_t idx; + uint64_t v; + up = up >> tg >> idx >> v; + CHECK_UNPACK(LV_VOTE_LEARN, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << ".synod_commit[" << unsigned(idx) << "] vote learn " << v; + } + + local_voter_map_t::state_reference lvsr; + local_voter* lv = m_local_voters.get_or_create_state(tg, &lvsr); + assert(lv); + lv->vote_learn(idx, v, this); + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(tg, &tsr); + + if (xact) + { + xact->externally_work_state_machine(this); + } +} + +void +daemon :: process_commit_record(comm_id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + e::slice commit_record; + up = up >> tg >> commit_record; + CHECK_UNPACK(COMMIT_RECORD, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << " commit record"; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_or_create_state(tg, &tsr); + assert(xact); + xact->commit_record(commit_record, msg, this); +} + +void +daemon :: process_kvs_rd_locked(comm_id id, std::auto_ptr msg, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + consus_returncode rc; + uint64_t timestamp; + e::slice value; + up = up >> tg >> seqno >> rc >> timestamp >> value; + CHECK_UNPACK(KVS_RD_LOCKED, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << "[" << seqno << "] kvs returned " << rc << " from " << id; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(tg, &tsr); + + if (xact) + { + xact->kvs_rd_locked(seqno, rc, timestamp, value, msg, this); + } +} + +void +daemon :: process_kvs_rd_unlocked(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + up = up >> tg >> seqno; + CHECK_UNPACK(KVS_RD_UNLOCKED, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << "[" << seqno << "] lock released from " << id; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(tg, &tsr); + + if (xact) + { + xact->kvs_rd_unlocked(seqno, this); + } +} + +void +daemon :: process_kvs_wr_begun(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + up = up >> tg >> seqno; + CHECK_UNPACK(KVS_WR_BEGUN, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << "[" << seqno << "] write started by " << id; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(tg, &tsr); + + if (xact) + { + xact->kvs_wr_begun(seqno, this); + } +} + +void +daemon :: process_kvs_wr_finished(comm_id id, std::auto_ptr, e::unpacker up) +{ + transaction_group tg; + uint64_t seqno; + up = up >> tg >> seqno; + CHECK_UNPACK(KVS_RD_UNLOCKED, up); + + if (s_debug_mode) + { + LOG(INFO) << tg << "[" << seqno << "] write finished by " << id; + } + + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(tg, &tsr); + + if (xact) + { + xact->kvs_wr_finished(seqno, this); + } +} + +consus::configuration* +daemon :: get_config() +{ + return e::atomic::load_ptr_acquire(&m_config); +} + +void +daemon :: debug_dump() +{ + LOG(ERROR) << "DEBUG DUMP"; // XXX +} + +consus::transaction_id +daemon :: generate_txid() +{ + // XXX don't go out of process + po6::io::fd fd(open("/dev/urandom", O_RDONLY)); + uint64_t x; + int ret = fd.xread(&x, sizeof(x)); + assert(ret == 8); + paxos_group_id id; + std::vector groups(get_config()->groups_for(m_us.id)); + // XXX groups.size() == 0? + size_t idx = x % groups.size(); + id = groups[idx]; + return transaction_id(id, x); +} + +bool +daemon :: send(comm_id id, std::auto_ptr msg) +{ + if (id == comm_id()) + { + return false; + } + + busybee_returncode rc = m_busybee->send(id.get(), msg); + + switch (rc) + { + case BUSYBEE_SUCCESS: + return true; + case BUSYBEE_DISRUPTED: + return false; + case BUSYBEE_SHUTDOWN: + case BUSYBEE_INTERRUPTED: + case BUSYBEE_POLLFAILED: + case BUSYBEE_ADDFDFAIL: + case BUSYBEE_TIMEOUT: + case BUSYBEE_EXTERNAL: + default: + LOG(ERROR) << "internal invariants broken; crashing"; + abort(); + } +} + +unsigned +daemon :: send(const paxos_group& g, std::auto_ptr msg) +{ + unsigned count = 0; + + for (unsigned i = 0; i < g.members_sz; ++i) + { + std::auto_ptr m(msg->copy()); + busybee_returncode rc = m_busybee->send(g.members[i].get(), m); + + switch (rc) + { + case BUSYBEE_SUCCESS: + ++count; + break; + case BUSYBEE_DISRUPTED: + break; + case BUSYBEE_SHUTDOWN: + case BUSYBEE_INTERRUPTED: + case BUSYBEE_POLLFAILED: + case BUSYBEE_ADDFDFAIL: + case BUSYBEE_TIMEOUT: + case BUSYBEE_EXTERNAL: + default: + LOG(ERROR) << "internal invariants broken; crashing"; + abort(); + } + } + + return count; +} + +struct daemon::durable_msg +{ + durable_msg() : recno(), client(), msg(NULL) {} + durable_msg(int64_t r, comm_id c, e::buffer* m) : recno(r), client(c), msg(m) {} + durable_msg(const durable_msg& other) + : recno(other.recno), client(other.client), msg(other.msg) {} + ~durable_msg() throw () {} + durable_msg& operator = (const durable_msg& rhs) + { + // no self-assign check needed + recno = rhs.recno; + client = rhs.client; + msg = rhs.msg; + return *this; + } + bool operator < (const durable_msg& rhs) { return rhs.recno > recno; } + int64_t recno; + comm_id client; + e::buffer* msg; +}; + +void +daemon :: send_when_durable(const std::string& entry, comm_id id, std::auto_ptr msg) +{ + e::buffer* m = msg.release(); + send_when_durable(entry, &id, &m, 1); +} + +void +daemon :: send_when_durable(const std::string& entry, comm_id* ids, e::buffer** msgs, size_t sz) +{ + int64_t x = m_log.append(entry.data(), entry.size()); + + if (x < 0) + { + return; + } + + bool wake = false; + + { + po6::threads::mutex::hold hold(&m_durable_mtx); + wake = x <= m_durable_up_to; + + for (size_t i = 0; i < sz; ++i) + { + durable_msg d(x, ids[i], msgs[i]); + m_durable_msgs.push_back(d); + std::push_heap(m_durable_msgs.begin(), m_durable_msgs.end()); + } + } + + if (wake) + { + m_log.wake(); + } +} + +struct daemon::durable_cb +{ + durable_cb() : recno(), tg(), seqno() {} + durable_cb(int64_t r, transaction_group t, uint64_t s) : recno(r), tg(t), seqno(s) {} + durable_cb(const durable_cb& other) + : recno(other.recno), tg(other.tg), seqno(other.seqno) {} + ~durable_cb() throw () {} + durable_cb& operator = (const durable_cb& rhs) + { + // no self-assign check needed + recno = rhs.recno; + tg = rhs.tg; + seqno = rhs.seqno; + return *this; + } + bool operator < (const durable_cb& rhs) { return rhs.recno > recno; } + int64_t recno; + transaction_group tg; + uint64_t seqno; +}; + +void +daemon :: callback_when_durable(const std::string& entry, const transaction_group& tg, uint64_t seqno) +{ + int64_t x = m_log.append(entry.data(), entry.size()); + + if (x < 0) + { + return; + } + + bool wake = false; + + { + po6::threads::mutex::hold hold(&m_durable_mtx); + durable_cb d(x, tg, seqno); + m_durable_cbs.push_back(d); + std::push_heap(m_durable_cbs.begin(), m_durable_cbs.end()); + wake = x <= m_durable_up_to; + } + + if (wake) + { + m_log.wake(); + } +} + +void +daemon :: durable() +{ + sigset_t ss; + + if (sigfillset(&ss) < 0 || + pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) + { + LOG(ERROR) << "could not successfully block signals; this could result in undefined behavior"; + return; + } + + LOG(INFO) << "durability monitor started"; + int64_t x = -1; + + while (true) + { + x = m_log.wait(x); + + if (m_log.error() != 0) + { + break; + } + + std::vector msgs; + std::vector cbs; + + { + po6::threads::mutex::hold hold(&m_durable_mtx); + m_durable_up_to = x; + + while (!m_durable_msgs.empty() && + m_durable_msgs[0].recno < x) + { + msgs.push_back(m_durable_msgs[0]); + std::pop_heap(m_durable_msgs.begin(), m_durable_msgs.end()); + m_durable_msgs.pop_back(); + } + + while (!m_durable_cbs.empty() && + m_durable_cbs[0].recno < x) + { + cbs.push_back(m_durable_cbs[0]); + std::pop_heap(m_durable_cbs.begin(), m_durable_cbs.end()); + m_durable_cbs.pop_back(); + } + } + + for (size_t i = 0; i < msgs.size(); ++i) + { + std::auto_ptr msg(msgs[i].msg); + send(msgs[i].client, msg); + } + + for (size_t i = 0; i < cbs.size(); ++i) + { + transaction_map_t::state_reference tsr; + transaction* xact = m_transactions.get_state(cbs[i].tg, &tsr); + + if (xact) + { + xact->callback(cbs[i].seqno, this); + } + } + } + + LOG(INFO) << "durability monitor shutting down"; +} diff --git a/txman/daemon.h b/txman/daemon.h new file mode 100644 index 0000000..6da883e --- /dev/null +++ b/txman/daemon.h @@ -0,0 +1,134 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_daemon_h_ +#define consus_txman_daemon_h_ + +// STL +#include +#include + +// po6 +#include +#include + +// e +#include +#include +#include +#include + +// BusyBee +#include + +// Replicant +#include + +// consus +#include "namespace.h" +#include "common/coordinator_link.h" +#include "common/ids.h" +#include "common/network_msgtype.h" +#include "common/transaction_id.h" +#include "common/transaction_group.h" +#include "common/txman.h" +#include "txman/configuration.h" +#include "txman/durable_log.h" +#include "txman/local_voter.h" +#include "txman/mapper.h" +#include "txman/transaction.h" + +BEGIN_CONSUS_NAMESPACE + +class daemon +{ + public: + daemon(); + ~daemon() throw (); + + public: + int run(bool daemonize, + std::string data, + std::string log, + std::string pidfile, + bool has_pidfile, + bool set_bind_to, + po6::net::location bind_to, + bool set_coordinator, + const char* coordinator, + unsigned threads); + + private: + struct coordinator_callback; + struct durable_msg; + struct durable_cb; + typedef e::state_hash_table transaction_map_t; + typedef e::state_hash_table local_voter_map_t; + typedef e::nwf_hash_map disposition_map_t; + typedef std::vector durable_msg_heap_t; + typedef std::vector durable_cb_heap_t; + friend class mapper; + friend class transaction; + friend class local_voter; + + private: + void loop(size_t thread); + void process_begin(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_read(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_write(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_commit(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_abort(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_paxos_2a(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_paxos_2b(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_lv_vote_1a(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_lv_vote_1b(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_lv_vote_2a(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_lv_vote_2b(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_lv_vote_learn(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_commit_record(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_kvs_rd_locked(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_kvs_rd_unlocked(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_kvs_wr_begun(comm_id id, std::auto_ptr msg, e::unpacker up); + void process_kvs_wr_finished(comm_id id, std::auto_ptr msg, e::unpacker up); + + private: + configuration* get_config(); + void debug_dump(); + transaction_id generate_txid(); + uint64_t resend_interval() { return PO6_SECONDS; } + bool send(comm_id id, std::auto_ptr msg); + unsigned send(const paxos_group& g, std::auto_ptr msg); + void send_when_durable(const std::string& entry, comm_id id, std::auto_ptr msg); + void send_when_durable(const std::string& entry, comm_id* id, e::buffer** msg, size_t sz); + void callback_when_durable(const std::string& entry, const transaction_group& tg, uint64_t seqno); + void durable(); + + private: + txman m_us; + e::garbage_collector m_gc; + mapper m_busybee_mapper; + std::auto_ptr m_busybee; + std::auto_ptr m_coord_cb; + std::auto_ptr m_coord; + configuration* m_config; + std::vector > m_threads; + transaction_map_t m_transactions; + local_voter_map_t m_local_voters; + disposition_map_t m_dispositions; + durable_log m_log; + + // awaiting durability + po6::threads::thread m_durable_thread; + po6::threads::mutex m_durable_mtx; + int64_t m_durable_up_to; + durable_msg_heap_t m_durable_msgs; + durable_cb_heap_t m_durable_cbs; + + private: + daemon(const daemon&); + daemon& operator = (const daemon&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_daemon_h_ diff --git a/txman/durable_log.cc b/txman/durable_log.cc new file mode 100644 index 0000000..024ec6d --- /dev/null +++ b/txman/durable_log.cc @@ -0,0 +1,423 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// C +#include +#include + +// POSIX +#include +#include +#include +#include + +// STL +#include +#include + +// e +#include +#include +#include +#include + +// consus +#include "common/crc32c.h" +#include "txman/durable_log.h" + +using consus::durable_log; + +#define RECORD_HEADER_SIZE (2 * sizeof(uint64_t)) + +static void +encode_header(uint64_t recno, uint64_t size, unsigned char* header) +{ + e::pack64be(recno, header); + e::pack64be(size, header + sizeof(uint64_t)); +} + +struct durable_log :: segment +{ + segment(po6::threads::mutex* mtx, int x) + : fd(x) + , offset_next_write(0) + , offset_last_fsync(0) + , recno_last_write(0) + , recno_last_fsync(0) + , ongoing_writes(0) + , done_writing(mtx) + , syncing(false) + { + } + po6::io::fd fd; + uint64_t offset_next_write; + uint64_t offset_last_fsync; + uint64_t recno_last_write; + uint64_t recno_last_fsync; + int32_t ongoing_writes; + po6::threads::cond done_writing; + bool syncing; +}; + +durable_log :: durable_log() + : m_path() + , m_dir() + , m_lockfile() + , m_mtx() + , m_cond(&m_mtx) + , m_flush(po6::threads::make_thread_wrapper(&durable_log::flush, this)) + , m_error(0) + , m_wakeup(false) + , m_next_entry(1) + , m_segment_a(NULL) + , m_segment_b(NULL) +{ + m_flush.start(); +} + +durable_log :: ~durable_log() throw () +{ + close(); + m_flush.join(); + + if (m_segment_a) + { + delete m_segment_a; + } + + if (m_segment_b) + { + delete m_segment_b; + } +} + +bool +durable_log :: open(const std::string& dir) +{ + po6::threads::mutex::hold hold(&m_mtx); + m_path = dir; + struct stat st; + int ret = stat(m_path.c_str(), &st); + + if (ret < 0 && errno == ENOENT) + { + if (mkdir(m_path.c_str(), S_IRWXU) < 0) + { + m_error = errno; + return false; + } + + ret = stat(m_path.c_str(), &st); + } + + if (ret < 0) + { + m_error = errno; + return false; + } + else if (!S_ISDIR(st.st_mode)) + { + m_error = errno = ENOTDIR; + return false; + } + + m_dir = ::open(m_path.c_str(), O_RDONLY); + + if (m_dir.get() < 0) + { + m_error = errno; + return false; + } + + int fd = openat(m_dir.get(), "LOCK", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); + + if (!m_lockfile.lock(fd)) + { + m_error = errno; + return false; + } + + // XXX do rotation on these and don't overwrite the old + int file_a = openat(m_dir.get(), "file_a", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + int file_b = openat(m_dir.get(), "file_b", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + + if (file_a < 0 || file_b < 0) + { + ::close(file_a); + ::close(file_b); + return false; + } + + m_segment_a = new segment(&m_mtx, file_a); + m_segment_b = new segment(&m_mtx, file_b); + return true; +} + +void +durable_log :: close() +{ + po6::threads::mutex::hold hold(&m_mtx); + m_error = -1; + m_cond.broadcast(); +} + +int64_t +durable_log :: append(const char* entry, size_t entry_sz) +{ + return append(reinterpret_cast(entry), entry_sz); +} + +int64_t +durable_log :: append(const unsigned char* entry, size_t entry_sz) +{ + unsigned char header[RECORD_HEADER_SIZE]; + segment* seg; + uint64_t offset; + uint64_t recno; + + { + po6::threads::mutex::hold hold(&m_mtx); + + if (m_error) + { + errno = m_error; + return -1; + } + + recno = m_next_entry; + ++m_next_entry; + seg = select_segment_write(); + assert(seg); + assert(!seg->syncing); + offset = seg->offset_next_write; + seg->offset_next_write += RECORD_HEADER_SIZE + entry_sz + sizeof(uint32_t); + seg->recno_last_write = recno; + ++seg->ongoing_writes; + } + + encode_header(recno, entry_sz, header); + uint32_t crc = 0; + crc = crc32c(crc, header, RECORD_HEADER_SIZE); + crc = crc32c(crc, entry, entry_sz); + unsigned char crcbuf[sizeof(uint32_t)]; + e::pack32be(crc, crcbuf); + + if (pwrite(seg->fd.get(), header, RECORD_HEADER_SIZE, offset) < 0 || + pwrite(seg->fd.get(), entry, entry_sz, offset + RECORD_HEADER_SIZE) < 0 || + pwrite(seg->fd.get(), crcbuf, sizeof(uint32_t), offset + RECORD_HEADER_SIZE + entry_sz) < 0) + { + int e = errno; + po6::threads::mutex::hold hold(&m_mtx); + m_error = e; + return -1; + } + + po6::threads::mutex::hold hold(&m_mtx); + --seg->ongoing_writes; + + if (seg->ongoing_writes == 0) + { + seg->done_writing.broadcast(); + m_cond.broadcast(); + } + + return recno; +} + +int64_t +durable_log :: durable() +{ + po6::threads::mutex::hold hold(&m_mtx); + return durable_lock_held_elsewhere(); +} + +int64_t +durable_log :: wait(int64_t prev_ub) +{ + po6::threads::mutex::hold hold(&m_mtx); + + while (true) + { + int64_t x = durable_lock_held_elsewhere(); + + if (m_error == 0 && x <= prev_ub && !m_wakeup) + { + m_cond.wait(); + } + else + { + m_wakeup = false; + return x; + } + } +} + +void +durable_log :: wake() +{ + po6::threads::mutex::hold hold(&m_mtx); + m_wakeup = true; + m_cond.broadcast(); +} + +int +durable_log :: error() +{ + po6::threads::mutex::hold hold(&m_mtx); + return m_error; +} + +void +durable_log :: flush() +{ + sigset_t ss; + + if (sigfillset(&ss) < 0 || + pthread_sigmask(SIG_BLOCK, &ss, NULL) < 0) + { + int err = errno; + po6::threads::mutex::hold hold(&m_mtx); + m_error = err; + return; + } + + while (true) + { + uint64_t offset_saved; + uint64_t recno_saved; + segment* seg; + + { + po6::threads::mutex::hold hold(&m_mtx); + + while (m_error == 0 && + !(seg = select_segment_fsync())) + { + m_cond.wait(); + } + + if (m_error != 0) + { + break; + } + + seg->syncing = true; + + while (seg->ongoing_writes > 0) + { + seg->done_writing.wait(); + } + + offset_saved = seg->offset_next_write; + recno_saved = seg->recno_last_write; + } + + if (fsync(seg->fd.get()) < 0) + { + int e = errno; + po6::threads::mutex::hold hold(&m_mtx); + m_error = e; + } + + { + po6::threads::mutex::hold hold(&m_mtx); + seg->syncing = false; + seg->offset_last_fsync = offset_saved; + seg->recno_last_fsync = recno_saved; + m_cond.broadcast(); + } + } +} + +durable_log::segment* +durable_log :: select_segment_write() +{ + segment* a = m_segment_a; + segment* b = m_segment_b; + assert(a->offset_next_write >= a->offset_last_fsync); + assert(b->offset_next_write >= b->offset_last_fsync); + const uint64_t a_unflushed = a->offset_next_write - a->offset_last_fsync; + const uint64_t b_unflushed = b->offset_next_write - b->offset_last_fsync; + + if (a_unflushed < b_unflushed && !a->syncing) + { + return a; + } + else if (a_unflushed > b_unflushed && !b->syncing) + { + return b; + } + else if (!a->syncing) + { + return a; + } + else if (!b->syncing) + { + return b; + } + else + { + return NULL; + } +} + +durable_log::segment* +durable_log :: select_segment_fsync() +{ + segment* a = m_segment_a; + segment* b = m_segment_b; + + if (!a || !b) + { + return NULL; + } + + assert(!a->syncing); + assert(!b->syncing); + assert(a->offset_next_write >= a->offset_last_fsync); + assert(b->offset_next_write >= b->offset_last_fsync); + const uint64_t a_unflushed = a->offset_next_write - a->offset_last_fsync; + const uint64_t b_unflushed = b->offset_next_write - b->offset_last_fsync; + + if (a_unflushed < b_unflushed) + { + return b; + } + else if (a_unflushed > b_unflushed) + { + return a; + } + else if (a_unflushed > 0) + { + return a; + } + else if (b_unflushed > 0) + { + return b; + } + else + { + return NULL; + } +} + +int64_t +durable_log :: durable_lock_held_elsewhere() +{ + segment* a = m_segment_a; + segment* b = m_segment_b; + assert(a->offset_next_write >= a->offset_last_fsync); + assert(b->offset_next_write >= b->offset_last_fsync); + + if (a->recno_last_fsync > b->recno_last_fsync) + { + std::swap(a, b); + } + + if (a->offset_next_write - a->offset_last_fsync > 0) + { + return a->recno_last_fsync + 1; + } + + return b->recno_last_fsync + 1; +} diff --git a/txman/durable_log.h b/txman/durable_log.h new file mode 100644 index 0000000..721ef67 --- /dev/null +++ b/txman/durable_log.h @@ -0,0 +1,73 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_log_h_ +#define consus_txman_log_h_ + +// C +#include + +// STL +#include +#include +#include + +// po6 +#include +#include +#include +#include + +// e +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +class durable_log +{ + public: + durable_log(); + ~durable_log() throw (); + + public: + bool open(const std::string& dir); + void close(); + int64_t append(const char* entry, size_t entry_sz); + int64_t append(const unsigned char* entry, size_t entry_sz); + int64_t replay(void (*f)(void*, const unsigned char*, size_t), void* p); + int64_t durable(); + int64_t wait(int64_t prev_ub); + void wake(); + int error(); + + private: + class segment; + void flush(); + segment* select_segment_write(); + segment* select_segment_fsync(); + int64_t durable_lock_held_elsewhere(); + + private: + std::string m_path; + po6::io::fd m_dir; + e::lockfile m_lockfile; + po6::threads::mutex m_mtx; + po6::threads::cond m_cond; + po6::threads::thread m_flush; + int m_error; + bool m_wakeup; + uint64_t m_next_entry; + segment* m_segment_a; + segment* m_segment_b; + + private: + durable_log(const durable_log&); + durable_log& operator = (const durable_log&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_log_h_ diff --git a/txman/generalized_paxos.cc b/txman/generalized_paxos.cc new file mode 100644 index 0000000..43294e7 --- /dev/null +++ b/txman/generalized_paxos.cc @@ -0,0 +1,1080 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include +#include +#include + +// e +#include + +// consus +#include "txman/generalized_paxos.h" + +#ifndef GENERALIZED_PAXOS_DEBUG +#define GENERALIZED_PAXOS_DEBUG 1 +#endif + +#if GENERALIZED_PAXOS_DEBUG +#define GP_ABORT() do { abort(); } while (false) +#else +#define GP_ABORT() do { } while (false) +#endif + +using consus::generalized_paxos; + +generalized_paxos :: generalized_paxos() + : m_init(false) + , m_interfere(NULL) + , m_state(PARTICIPATING) + , m_us() + , m_acceptors() + , m_proposed() + , m_acceptor_ballot() + , m_acceptor_value() + , m_acceptor_value_src() + , m_leader_ballot() + , m_leader_value() + , m_promises() + , m_learned() + , m_learned_cached() +{ +} + +generalized_paxos :: ~generalized_paxos() throw () +{ +} + +void +generalized_paxos :: init(const comparator* cmp, abstract_id us, const abstract_id* acceptors, size_t acceptors_sz) +{ + assert(!m_init); + assert(acceptors_sz <= 63); + m_init = true; + m_interfere = cmp; + m_us = us; + m_acceptors = std::vector(acceptors, acceptors + acceptors_sz); + m_promises.resize(acceptors_sz); + m_learned.resize(acceptors_sz); +} + +void +generalized_paxos :: propose(const command& c) +{ + if (std::find(m_proposed.begin(), m_proposed.end(), c) == m_proposed.end()) + { + m_proposed.push_back(c); + } +} + +void +generalized_paxos :: propose_from_p2b(const message_p2b& m) +{ + for (size_t i = 0; i < m.v.commands.size(); ++i) + { + propose(m.v.commands[i]); + } +} + +void +generalized_paxos :: advance(bool may_attempt_leadership, + bool* send_m1, message_p1a* m1, + bool* send_m2, message_p2a* m2, + bool* send_m3, message_p2b* m3) +{ + *send_m1 = false; + *send_m2 = false; + *send_m3 = false; + cstruct learn; + bool conflict = false; + learned(&learn, &conflict); + + if (m_state >= LEADING_PHASE2 && + m_leader_ballot.type == ballot::CLASSIC && + cstruct_eq(learn, m_leader_value)) + { + may_attempt_leadership = true; + m_state = PARTICIPATING; + } + + if (may_attempt_leadership && + (m_acceptor_ballot.leader != m_us || + m_state == PARTICIPATING || + (m_leader_ballot.type == ballot::FAST && conflict))) + { + ballot::type_t t = conflict ? ballot::CLASSIC : ballot::FAST; + uint64_t number = std::max(m_acceptor_ballot, m_leader_ballot).number + 1; + m_leader_ballot = ballot(t, number, m_us); + m_state = LEADING_PHASE1; + } + + if (m_state >= LEADING_PHASE1) + { + size_t promised = 0; + + for (size_t i = 0; i < m_promises.size(); ++i) + { + if (m_promises[i].b == m_leader_ballot) + { + ++promised; + } + } + + if (promised < m_promises.size()) + { + *send_m1 = true; + *m1 = message_p1a(m_leader_ballot); + } + + if (promised >= quorum() && m_state < LEADING_PHASE2) + { + m_state = LEADING_PHASE2; + m_leader_value = proven_safe(); + } + } + + if (m_state >= LEADING_PHASE2 && m_leader_ballot.type == ballot::CLASSIC) + { + for (size_t i = 0; i < m_proposed.size(); ++i) + { + if (std::find(m_leader_value.commands.begin(), + m_leader_value.commands.end(), + m_proposed[i]) == m_leader_value.commands.end()) + { + m_leader_value.commands.push_back(m_proposed[i]); + } + } + + *send_m2 = true; + *m2 = message_p2a(m_leader_ballot, m_leader_value); + } + + if (m_acceptor_ballot.type == ballot::FAST) + { + for (size_t i = 0; i < m_proposed.size(); ++i) + { + if (std::find(m_acceptor_value.commands.begin(), + m_acceptor_value.commands.end(), + m_proposed[i]) == m_acceptor_value.commands.end()) + { + m_acceptor_value_src = m_acceptor_ballot; + m_acceptor_value.commands.push_back(m_proposed[i]); + } + } + + *send_m3 = true; + *m3 = message_p2b(m_acceptor_ballot, m_us, m_acceptor_value); + } +} + +// this implements Phase1b of the distributed abstract algorithm +void +generalized_paxos :: process_p1a(const message_p1a& m, bool* send, message_p1b* r) +{ + assert(m_init); + *send = false; + size_t idx = index_of(m.b.leader); + + if (idx >= m_acceptors.size()) + { + return; + } + + if (m.b > m_leader_ballot) + { + m_state = PARTICIPATING; + } + + if (m.b >= m_acceptor_ballot) + { + m_acceptor_ballot = m.b; + *send = true; + *r = message_p1b(m_acceptor_ballot, m_us, m_acceptor_value_src, m_acceptor_value); + } +} + +void +generalized_paxos :: process_p1b(const message_p1b& m) +{ + assert(m_init); + size_t idx = index_of(m.acceptor); + + if (idx >= m_acceptors.size()) + { + return; + } + + if (m.b > m_leader_ballot) + { + m_state = PARTICIPATING; + } + else if (m.b == m_leader_ballot && + m_promises[idx].b != m_leader_ballot && + m_state >= LEADING_PHASE1) + { + m_promises[idx] = m; + } +} + +// this implements Phase2bClassic of the distributed abstract algorithm +void +generalized_paxos :: process_p2a(const message_p2a& m, bool* send, message_p2b* r) +{ + assert(m_init); + *send = false; + + if (m.b != m_acceptor_ballot || + m.b.type != ballot::CLASSIC) + { + return; + } + + if (m_acceptor_value_src == m_acceptor_ballot && + !cstruct_le(m_acceptor_value, m.v)) + { + return; + } + + m_acceptor_value_src = m_acceptor_ballot; + m_acceptor_value = m.v; + *send = true; + *r = message_p2b(m_acceptor_ballot, m_us, m_acceptor_value); +} + +void +generalized_paxos :: process_p2b(const message_p2b& m) +{ + assert(m_init); + + if (m.b != m_acceptor_ballot) + { + return; + } + + size_t idx = index_of(m.acceptor); + + if (idx >= m_acceptors.size()) + { + return; + } + + if (m_learned[idx].b < m.b) + { + m_learned[idx] = m; + } + + if (m_learned[idx].b == m.b && + cstruct_le(m_learned[idx].v, m.v)) + { + m_learned[idx] = m; + } +} + +generalized_paxos::cstruct +generalized_paxos :: learned() +{ + cstruct ret; + bool conflict; + learned(&ret, &conflict); + return ret; +} + +size_t +generalized_paxos :: index_of(abstract_id a) +{ + for (size_t i = 0; i < m_acceptors.size(); ++i) + { + if (a == m_acceptors[i]) + { + return i; + } + } + + return m_acceptors.size(); +} + +size_t +generalized_paxos :: quorum() +{ + return 2 * m_acceptors.size() / 3 + 1; +} + +void +generalized_paxos :: learned(cstruct* ret, bool* conflict) +{ + *conflict = false; + typedef std::map ballot_map_t; + ballot_map_t ballots; + + for (size_t i = 0; i < m_learned.size(); ++i) + { + ++ballots[m_learned[i].b]; + } + + std::vector learned_values; + + for (ballot_map_t::iterator it = ballots.begin(); it != ballots.end(); ++it) + { + if (it->second >= quorum()) + { + learned(it->first, &learned_values, conflict); + } + } + +#if GENERALIZED_PAXOS_DEBUG + for (size_t i = 0; i < learned_values.size(); ++i) + { + assert(cstruct_compatible(m_learned_cached, learned_values[i])); + assert(cstruct_compatible(learned_values[i], m_learned_cached)); + + for (size_t j = i + 1; j < learned_values.size(); ++j) + { + assert(cstruct_compatible(learned_values[i], learned_values[j])); + assert(cstruct_compatible(learned_values[j], learned_values[i])); + } + } +#endif + + *ret = cstruct(); + + if (learned_values.empty()) + { + return; + } + + for (size_t i = 0; i < learned_values.size(); ++i) + { + *ret = cstruct_lub(*ret, learned_values[i]); + } + +#if GENERALIZED_PAXOS_DEBUG + assert(cstruct_compatible(*ret, m_learned_cached)); + m_learned_cached = *ret; +#endif +} + +void +generalized_paxos :: learned(const ballot& b, std::vector* lv, bool* conflict) +{ + std::vector vs; + + for (size_t i = 0; i < m_learned.size(); ++i) + { + if (m_learned[i].b == b) + { + vs.push_back(&m_learned[i].v); + } + } + + assert(vs.size() > 0); + learned(&vs[0], vs.size(), quorum(), lv, conflict); +} + +void +generalized_paxos :: learned(cstruct** vs, size_t vs_sz, size_t max_sz, + std::vector* lv, bool* conflict) +{ + assert(vs_sz > 0); + + if (vs_sz == 1) + { + lv->push_back(*vs[0]); + } + else if (vs_sz <= max_sz) + { + cstruct tmp; + learned(vs, vs_sz, &tmp, conflict); + lv->push_back(tmp); + } + else + { + // enumerate every subset of vs of size max_sz + uint64_t limit = 1ULL << vs_sz; + uint64_t v = (1ULL << max_sz) - 1ULL; + assert(v < limit); + + while (v < limit) + { + std::vector nvs; + + for (size_t i = 0; i < vs_sz; ++i) + { + if ((v & (1ULL << i))) + { + nvs.push_back(vs[i]); + } + } + + assert(nvs.size() == max_sz); + cstruct tmp; + learned(&nvs[0], nvs.size(), &tmp, conflict); + lv->push_back(tmp); + + // lexicographically next bit permutation + // http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation + uint64_t t = (v | (v - 1)) + 1; + v = t | ((((t & -t) / (v & -v)) >> 1) - 1); + } + } +} + +void +generalized_paxos :: learned(cstruct** vs, size_t vs_sz, + cstruct* v, bool* conflict) +{ + assert(vs_sz > 0); + *v = *vs[0]; + + for (size_t i = 0; i < vs_sz; ++i) + { + *v = cstruct_glb(*v, *vs[i], conflict); + } + + assert(cstruct_compatible(m_learned_cached, *v)); +} + +generalized_paxos::cstruct +generalized_paxos :: proven_safe() +{ + ballot k; + + for (size_t i = 0; i < m_promises.size(); ++i) + { + if (m_promises[i].b == m_leader_ballot) + { + k = std::max(k, m_promises[i].vb); + } + } + + // enumerate every R \in Quorum(k) + const uint64_t limit = 1ULL << m_promises.size(); + uint64_t v = (1ULL << quorum()) - 1ULL; + assert(v < limit); + std::vector lv; + bool conflict = false; + + while (v < limit) + { + std::vector vs; + bool consider = true; + + for (size_t i = 0; i < m_promises.size(); ++i) + { + // if in R && in Q + if ((v & (1ULL << i)) && m_promises[i].b == m_leader_ballot) + { + if (m_promises[i].vb == k) + { + vs.push_back(&m_promises[i].v); + } + else + { + consider = false; + } + } + } + + if (consider) + { + cstruct tmp; + learned(&vs[0], vs.size(), &tmp, &conflict); + lv.push_back(tmp); + } + + // lexicographically next bit permutation + // http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation + uint64_t t = (v | (v - 1)) + 1; + v = t | ((((t & -t) / (v & -v)) >> 1) - 1); + } + + if (lv.empty()) + { + for (size_t i = 0; i < m_promises.size(); ++i) + { + if (m_promises[i].b == m_leader_ballot && m_promises[i].vb == k) + { + return m_promises[i].v; + } + } + } + + cstruct ret; + + for (size_t i = 0; i < lv.size(); ++i) + { + ret = cstruct_lub(ret, lv[i]); + } + + return ret; +} + +bool +generalized_paxos :: cstruct_le(const cstruct& lhs, const cstruct& rhs) +{ + std::vector lhs_elem; + std::vector rhs_elem; + partial_order_t lhs_order; + partial_order_t rhs_order; + cstruct_pieces(lhs, &lhs_elem, &lhs_order); + cstruct_pieces(rhs, &rhs_elem, &rhs_order); + + // if rhs doesn't include every element included by lhs + if (!std::includes(rhs_elem.begin(), rhs_elem.end(), + lhs_elem.begin(), lhs_elem.end())) + { + return false; + } + + // if rhs doesn't order every element that was ordered by lhs + // + // the above check and symmetry of interference means that rhs must have an + // order between every pair of elements ordered by lhs, so this will really + // fail when elements in rhs are ordered differently than those in lhs + if (!std::includes(rhs_order.begin(), rhs_order.end(), + lhs_order.begin(), lhs_order.end())) + { + return false; + } + + // a sequence of commands that were added to [lhs] to get [rhs] + // these are only in rhs; + std::vector seq; + std::set_difference(rhs_elem.begin(), rhs_elem.end(), + lhs_elem.begin(), lhs_elem.end(), + std::inserter(seq, seq.begin())); + // sort for fast binary search + std::sort(seq.begin(), seq.end()); + + // check that for every pairwise ordering v, w in rhs: + // if v in seq => w in seq + // if this is not true, then w is in lhs, and rhs cannot add an element + // earlier in the relation than lhs has and be considered >= + for (partial_order_t::iterator it = rhs_order.begin(); + it != rhs_order.end(); ++it) + { + if (std::binary_search(seq.begin(), seq.end(), it->first) && + !std::binary_search(seq.begin(), seq.end(), it->second)) + { + return false; + } + } + + return true; +} + +bool +generalized_paxos :: cstruct_eq(const cstruct& lhs, const cstruct& rhs) +{ + std::vector lhs_elem; + std::vector rhs_elem; + partial_order_t lhs_order; + partial_order_t rhs_order; + cstruct_pieces(lhs, &lhs_elem, &lhs_order); + cstruct_pieces(rhs, &rhs_elem, &rhs_order); + return lhs_elem.size() == rhs_elem.size() && lhs_order.size() == rhs_order.size() && + std::equal(lhs_elem.begin(), lhs_elem.end(), rhs_elem.begin()) && + std::equal(lhs_order.begin(), lhs_order.end(), rhs_order.begin()); +} + +bool +generalized_paxos :: cstruct_compatible(const cstruct& lhs, const cstruct& rhs) +{ + std::vector lhs_elem; + std::vector rhs_elem; + partial_order_t lhs_order; + partial_order_t rhs_order; + cstruct_pieces(lhs, &lhs_elem, &lhs_order); + cstruct_pieces(rhs, &rhs_elem, &rhs_order); + + // From the GP Tech Report: + // [σ] and [Ï„] are compatible iff the subgraphs of G(σ) and G(Ï„) consisting + // of the nodes they have in common are identical, and C does not conflict + // with D for every node C in G(σ) that is not in G(Ï„) and every node D in + // G(Ï„) that is not in G(σ). + // + // We will say σ=lhs and Ï„=rhs + + std::vector C; + std::set_difference(lhs_elem.begin(), lhs_elem.end(), + rhs_elem.begin(), rhs_elem.end(), + std::back_inserter(C)); + std::sort(C.begin(), C.end()); + std::vector D; + std::set_difference(rhs_elem.begin(), rhs_elem.end(), + lhs_elem.begin(), lhs_elem.end(), + std::back_inserter(D)); + std::sort(D.begin(), D.end()); + + for (size_t c = 0; c < C.size(); ++c) + { + for (size_t d = 0; d < D.size(); ++d) + { + if (m_interfere->conflict(C[c], D[d])) + { + return false; + } + } + } + + std::vector commands_in_common; + std::set_intersection(lhs_elem.begin(), lhs_elem.end(), + rhs_elem.begin(), rhs_elem.end(), + std::back_inserter(commands_in_common)); + std::sort(commands_in_common.begin(), commands_in_common.end()); + + partial_order_t edges_not_in_common; + std::set_symmetric_difference(lhs_order.begin(), lhs_order.end(), + rhs_order.begin(), rhs_order.end(), + std::inserter(edges_not_in_common, + edges_not_in_common.begin())); + + for (partial_order_t::iterator it = edges_not_in_common.begin(); + it != edges_not_in_common.end(); ++it) + { + if (std::binary_search(commands_in_common.begin(), + commands_in_common.end(), + it->first) && + std::binary_search(commands_in_common.begin(), + commands_in_common.end(), + it->second)) + { + return false; + } + } + + return true; +} + +generalized_paxos::cstruct +generalized_paxos :: cstruct_glb(const cstruct& lhs, const cstruct& rhs, bool* conflict) +{ + std::vector lhs_cmds; + std::vector rhs_cmds; + partial_order_t edge_list; + cstruct_pieces(lhs, &lhs_cmds, &edge_list); + cstruct_pieces(rhs, &rhs_cmds, &edge_list); + std::vector all_cmds; + std::set_union(lhs_cmds.begin(), lhs_cmds.end(), + rhs_cmds.begin(), rhs_cmds.end(), + std::back_inserter(all_cmds)); + std::sort(all_cmds.begin(), all_cmds.end()); + + // determine commands to exclude + std::vector exclude; + exclude.reserve(lhs_cmds.size() + rhs_cmds.size()); + std::set_symmetric_difference(lhs_cmds.begin(), lhs_cmds.end(), + rhs_cmds.begin(), rhs_cmds.end(), + std::back_inserter(exclude)); + + for (size_t i = 0; i < all_cmds.size(); ++i) + { + const command& e(all_cmds[i]); + + if (directed_path_exists(e, e, edge_list)) + { + *conflict = true; + exclude.push_back(e); + } + } + + for (size_t i = 0; i < exclude.size(); ++i) + { + const command& u(exclude[i]); + + for (size_t j = 0; j < all_cmds.size(); ++j) + { + const command& v(all_cmds[j]); + + if (directed_path_exists(u, v, edge_list) && + std::find(exclude.begin(), exclude.end(), v) == exclude.end()) + { + exclude.push_back(v); + } + } + } + + std::sort(exclude.begin(), exclude.end()); + std::vector::iterator it; + it = std::unique(exclude.begin(), exclude.end()); + exclude.resize(it - exclude.begin()); + + // create a new cstruct including only those commands that are not excluded + cstruct tmp; + + for (size_t i = 0; i < lhs.commands.size(); ++i) + { + const command& c(lhs.commands[i]); + + if (!std::binary_search(exclude.begin(), exclude.end(), c)) + { + tmp.commands.push_back(c); + } + } + + return tmp; +} + +generalized_paxos::cstruct +generalized_paxos :: cstruct_lub(const cstruct& lhs, const cstruct& rhs) +{ + assert(cstruct_compatible(lhs, rhs)); + std::vector lhs_elem(lhs.commands); + std::vector rhs_elem(rhs.commands); + std::sort(lhs_elem.begin(), lhs_elem.end()); + std::sort(rhs_elem.begin(), rhs_elem.end()); + + std::vector D; + std::set_difference(rhs_elem.begin(), rhs_elem.end(), + lhs_elem.begin(), lhs_elem.end(), + std::back_inserter(D)); + std::sort(D.begin(), D.end()); + cstruct tmp(lhs); + + for (size_t i = 0; i < rhs.commands.size(); ++i) + { + if (std::binary_search(D.begin(), D.end(), rhs.commands[i])) + { + tmp.commands.push_back(rhs.commands[i]); + } + } + + return tmp; +} + +void +generalized_paxos :: cstruct_pieces(const cstruct& c, + std::vector* commands, + partial_order_t* order) +{ + if (commands) + { + *commands = c.commands; + std::sort(commands->begin(), commands->end()); + } + + for (size_t i = 0; i < c.commands.size(); ++i) + { + const command& v(c.commands[i]); + + for (size_t j = i + 1; j < c.commands.size(); ++j) + { + const command& w(c.commands[j]); + + if (m_interfere->conflict(v, w)) + { + order->insert(std::make_pair(v, w)); + } + } + } +} + +bool +generalized_paxos :: directed_path_exists(const command& from, + const command& to, + const partial_order_t& edge_list) +{ + std::set seen; + return directed_path_exists(from, to, edge_list, &seen); +} + +bool +generalized_paxos :: directed_path_exists(const command& from, + const command& to, + const partial_order_t& edge_list, + std::set* seen) +{ + for (partial_order_t::iterator it = edge_list.lower_bound(std::make_pair(from, command())); + it != edge_list.end() && it->first == from; ++it) + { + if (it->second == to) + { + return true; + } + + if (seen->find(it->second) == seen->end()) + { + seen->insert(it->second); + + if (directed_path_exists(it->second, to, edge_list, seen)) + { + return true; + } + } + } + + return false; +} + +generalized_paxos :: command :: command() + : type(0) + , value() +{ +} + +generalized_paxos :: command :: command(uint16_t _type, const std::string& _value) + : type(_type) + , value(_value) +{ +} + +generalized_paxos :: command :: command(const command& other) + : type(other.type) + , value(other.value) +{ +} + +generalized_paxos :: command :: ~command() throw () +{ +} + +int +generalized_paxos :: command :: compare(const command& rhs) const +{ + if (type < rhs.type) + { + return -1; + } + if (type > rhs.type) + { + return 1; + } + + if (value < rhs.value) + { + return -1; + } + if (value > rhs.value) + { + return 1; + } + + return 0; +} + +generalized_paxos::command& +generalized_paxos :: command :: operator = (const command& rhs) +{ + type = rhs.type; + value = rhs.value; + return *this; +} + +bool generalized_paxos :: command :: operator < (const command& rhs) const { return compare(rhs) < 0; } +bool generalized_paxos :: command :: operator <= (const command& rhs) const { return compare(rhs) <= 0; } +bool generalized_paxos :: command :: operator == (const command& rhs) const { return compare(rhs) == 0; } +bool generalized_paxos :: command :: operator != (const command& rhs) const { return compare(rhs) != 0; } +bool generalized_paxos :: command :: operator >= (const command& rhs) const { return compare(rhs) >= 0; } +bool generalized_paxos :: command :: operator > (const command& rhs) const { return compare(rhs) > 0; } + +generalized_paxos :: comparator :: comparator() +{ +} + +generalized_paxos :: comparator :: ~comparator() throw () +{ +} + +generalized_paxos :: cstruct :: cstruct() + : commands() +{ +} + +generalized_paxos :: cstruct :: ~cstruct() throw () +{ +} + +bool +generalized_paxos :: cstruct :: operator == (const cstruct& rhs) const +{ + if (commands.size() != rhs.commands.size()) + { + return false; + } + + for (size_t i = 0; i < commands.size(); ++i) + { + if (commands[i] != rhs.commands[i]) + { + return false; + } + } + + return true; +} + +generalized_paxos :: ballot :: ballot() + : type(CLASSIC) + , number() + , leader() +{ +} + +generalized_paxos :: ballot :: ballot(type_t _type, uint64_t _number, abstract_id _leader) + : type(_type) + , number(_number) + , leader(_leader) +{ +} + +generalized_paxos :: ballot :: ballot(const ballot& other) + : type(other.type) + , number(other.number) + , leader(other.leader) +{ +} + +generalized_paxos :: ballot :: ~ballot() throw () +{ +} + +int +generalized_paxos :: ballot :: compare(const ballot& rhs) const +{ + if (number < rhs.number) + { + return -1; + } + if (number > rhs.number) + { + return 1; + } + + if (leader < rhs.leader) + { + return -1; + } + if (leader > rhs.leader) + { + return 1; + } + + return 0; +} + +generalized_paxos::ballot& +generalized_paxos :: ballot :: operator = (const ballot& rhs) +{ + if (this != &rhs) + { + type = rhs.type; + number = rhs.number; + leader = rhs.leader; + } + + return *this; +} + +bool generalized_paxos :: ballot :: operator < (const ballot& rhs) const { return compare(rhs) < 0; } +bool generalized_paxos :: ballot :: operator <= (const ballot& rhs) const { return compare(rhs) <= 0; } +bool generalized_paxos :: ballot :: operator == (const ballot& rhs) const { return compare(rhs) == 0; } +bool generalized_paxos :: ballot :: operator != (const ballot& rhs) const { return compare(rhs) != 0; } +bool generalized_paxos :: ballot :: operator >= (const ballot& rhs) const { return compare(rhs) >= 0; } +bool generalized_paxos :: ballot :: operator > (const ballot& rhs) const { return compare(rhs) > 0; } + +generalized_paxos :: message_p1a :: message_p1a() + : b() +{ +} + +generalized_paxos :: message_p1a :: message_p1a(const ballot& _b) + : b(_b) +{ +} + +generalized_paxos :: message_p1a :: ~message_p1a() throw () +{ +} + +generalized_paxos :: message_p1b :: message_p1b() + : b() + , acceptor() + , vb() + , v() +{ +} + +generalized_paxos :: message_p1b :: message_p1b(const ballot& _b, abstract_id _acceptor, const ballot& _vb, const cstruct& _v) + : b(_b) + , acceptor(_acceptor) + , vb(_vb) + , v(_v) +{ +} + +generalized_paxos :: message_p1b :: ~message_p1b() throw () +{ +} + +generalized_paxos :: message_p2a :: message_p2a() + : b() + , v() +{ +} + +generalized_paxos :: message_p2a :: message_p2a(const ballot& _b, const cstruct& _v) + : b(_b) + , v(_v) +{ +} + +generalized_paxos :: message_p2a :: ~message_p2a() throw () +{ +} + +generalized_paxos :: message_p2b :: message_p2b() + : b() + , acceptor() + , v() +{ +} + +generalized_paxos :: message_p2b :: message_p2b(const ballot& _b, abstract_id _acceptor, const cstruct& _v) + : b(_b) + , acceptor(_acceptor) + , v(_v) +{ +} + +generalized_paxos :: message_p2b :: ~message_p2b() throw () +{ +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const generalized_paxos::command& rhs) +{ + return lhs << "command(type=" << rhs.type << ", value=\"" << e::strescape(rhs.value) << "\")"; +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const generalized_paxos::cstruct& rhs) +{ + lhs << "cstruct(["; + + for (size_t i = 0; i < rhs.commands.size(); ++i) + { + if (i > 0) + { + lhs << ", "; + } + + lhs << rhs.commands[i]; + } + + lhs << "])"; + return lhs; +} + +std::ostream& +consus :: operator << (std::ostream& out, const generalized_paxos::ballot& b) +{ + return out << "ballot(type=" << b.type + << ", number=" << b.number + << ", leader=" << b.leader.get() << ")"; +} + +std::ostream& +consus :: operator << (std::ostream& out, const generalized_paxos::ballot::type_t& t) +{ + return out << (t == generalized_paxos::ballot::FAST ? "FAST" : "CLASSIC"); +} diff --git a/txman/generalized_paxos.h b/txman/generalized_paxos.h new file mode 100644 index 0000000..e38b916 --- /dev/null +++ b/txman/generalized_paxos.h @@ -0,0 +1,220 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_generalized_paxos_h_ +#define consus_txman_generalized_paxos_h_ + +// STL +#include +#include + +// consus +#include "namespace.h" +#include "common/paxos_group.h" + +BEGIN_CONSUS_NAMESPACE + +class generalized_paxos +{ + public: + struct command + { + command(); + command(uint16_t tag, const std::string& value); + command(const command& other); + ~command() throw (); + int compare(const command& rhs) const; + command& operator = (const command& rhs); + bool operator < (const command& rhs) const; + bool operator <= (const command& rhs) const; + bool operator == (const command& rhs) const; + bool operator != (const command& rhs) const; + bool operator >= (const command& rhs) const; + bool operator > (const command& rhs) const; + + uint16_t type; + std::string value; + }; + struct comparator + { + comparator(); + virtual bool conflict(const command& a, const command& b) const = 0; + protected: + ~comparator() throw (); + }; + struct cstruct + { + cstruct(); + ~cstruct() throw (); + bool operator == (const cstruct& rhs) const; + + std::vector commands; + }; + struct ballot + { + enum type_t + { + CLASSIC, + FAST + }; + + ballot(); + ballot(type_t type, uint64_t number, abstract_id leader); + ballot(const ballot& other); + ~ballot() throw (); + int compare(const ballot& rhs) const; + ballot& operator = (const ballot& rhs); + bool operator < (const ballot& rhs) const; + bool operator <= (const ballot& rhs) const; + bool operator == (const ballot& rhs) const; + bool operator != (const ballot& rhs) const; + bool operator >= (const ballot& rhs) const; + bool operator > (const ballot& rhs) const; + + type_t type; + uint64_t number; + abstract_id leader; + }; + struct message_p1a + { + message_p1a(); + message_p1a(const ballot& b); + ~message_p1a() throw (); + + ballot b; // called "m" in the paper + }; + struct message_p1b + { + message_p1b(); + message_p1b(const ballot& b, abstract_id acceptor, const ballot& vb, const cstruct& v); + ~message_p1b() throw (); + + ballot b; // called "m" in the paper + abstract_id acceptor; // called "a" in the paper + ballot vb; // called "bA_a" in the paper; vbal in the TLA+ + cstruct v; // called "bA_a" in the paper; vote in the TLA+ + }; + struct message_p2a + { + message_p2a(); + message_p2a(const ballot& b, const cstruct& v); + ~message_p2a() throw (); + + ballot b; // called "m" in the paper + cstruct v; // called "maxTried[m]•C" or "v" in the paper + }; + struct message_p2b + { + message_p2b(); + message_p2b(const ballot& b, abstract_id acceptor, const cstruct& v); + ~message_p2b() throw (); + + ballot b; // called "m" in the paper + abstract_id acceptor; // called "a" in the paper + cstruct v; // called "bA_a[m]•C" or "v" in the paper + }; + + public: + generalized_paxos(); + ~generalized_paxos() throw (); + + public: + void init(const comparator* cmp, abstract_id us, const abstract_id* acceptors, size_t acceptors_sz); + + void propose(const command& c); + void propose_from_p2b(const message_p2b& m); + void advance(bool may_attempt_leadership, + bool* send_m1, message_p1a* m1, + bool* send_m2, message_p2a* m2, + bool* send_m3, message_p2b* m3); + + // react to messages + void process_p1a(const message_p1a& m, bool* send, message_p1b* r); + void process_p1b(const message_p1b& m); + void process_p2a(const message_p2a& m, bool* send, message_p2b* r); + void process_p2b(const message_p2b& m); + + // what has been cumulatively learned throughout the system, from the + // limited amount that this instance can observe + cstruct learned(); + + private: + enum state_t + { + PARTICIPATING, + LEADING_PHASE1, + LEADING_PHASE2 + }; + + private: + size_t index_of(abstract_id a); + size_t quorum(); + void learned(cstruct* l, bool* conflict); + void learned(const ballot& b, std::vector* lv, bool* conflict); + void learned(cstruct** vs, size_t vs_sz, size_t max_sz, + std::vector* lv, bool* conflict); + void learned(cstruct** vs, size_t vs_sz, cstruct* v, bool* conflict); + cstruct proven_safe(); + + // cstructs are command histories as described in the paper, + // not sequences + typedef std::set > partial_order_t; + bool cstruct_le(const cstruct& lhs, const cstruct& rhs); + bool cstruct_eq(const cstruct& lhs, const cstruct& rhs); + bool cstruct_compatible(const cstruct& lhs, const cstruct& rhs); + cstruct cstruct_glb(const cstruct& lhs, const cstruct& rhs, bool* conflict); + cstruct cstruct_lub(const cstruct& lhs, const cstruct& rhs); + + // from the cstruct, returns a sorted list of commands, a list of edges + // defined according to m_interfere + // + // will not clear commands or order + void cstruct_pieces(const cstruct& c, + std::vector* commands, + partial_order_t* order); + + // Graph operations on the partial order over commands + static bool directed_path_exists(const command& from, + const command& to, + const partial_order_t& edge_list); + static bool directed_path_exists(const command& from, + const command& to, + const partial_order_t& edge_list, + std::set* seen); + + private: + bool m_init; + const comparator* m_interfere; + state_t m_state; + abstract_id m_us; + std::vector m_acceptors; + std::vector m_proposed; + + ballot m_acceptor_ballot; + cstruct m_acceptor_value; + ballot m_acceptor_value_src; + + ballot m_leader_ballot; + cstruct m_leader_value; + std::vector m_promises; + + std::vector m_learned; + cstruct m_learned_cached; + + private: + generalized_paxos(const generalized_paxos&); + generalized_paxos& operator = (const generalized_paxos&); +}; + +std::ostream& +operator << (std::ostream& lhs, const generalized_paxos::command& rhs); +std::ostream& +operator << (std::ostream& lhs, const generalized_paxos::cstruct& rhs); +std::ostream& +operator << (std::ostream& out, const generalized_paxos::ballot& b); +std::ostream& +operator << (std::ostream& out, const generalized_paxos::ballot::type_t& t); + +END_CONSUS_NAMESPACE + +#endif // consus_txman_generalized_paxos_h_ diff --git a/txman/global_voter.cc b/txman/global_voter.cc new file mode 100644 index 0000000..60e06a8 --- /dev/null +++ b/txman/global_voter.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2016, Robert Escriva +// All rights reserved. + +#if 0 +#define consus_txman_global_voter_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/transaction_group.h" +#include "txman/paxos_synod.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class global_voter +{ + public: + global_voter(const transaction_group& tg); + ~global_voter() throw (); + + public: + const transaction_group& state_key() const; + bool finished(); + + public: + void set_preferred_vote(uint64_t v); + void init(paxos_group_id* us, const paxos_group_id* dcs, size_t dcs_sz, daemon* d); +#if 0 + void vote_1a(comm_id id, unsigned idx, const paxos_synod::ballot& b, daemon* d); + void vote_1b(comm_id id, unsigned idx, + const paxos_synod::ballot& b, + const paxos_synod::pvalue& p, + daemon* d); + void vote_2a(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_2b(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_learn(unsigned idx, uint64_t v, daemon* d); +#endif + void externally_work_state_machine(daemon* d); + bool outcome(uint64_t* v); + +#if 0 + private: + bool preconditions_for_paxos(daemon* d); + void work_state_machine(daemon* d); + void work_paxos_vote(unsigned idx, daemon* d, uint64_t preferred); + + private: + const transaction_group m_tg; + po6::threads::mutex m_mtx; + bool m_initialized; + paxos_group m_group; + paxos_synod m_votes[CONSUS_MAX_REPLICATION_FACTOR]; + paxos_synod::phase_t m_phases[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t m_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + bool m_has_preferred_vote; + uint64_t m_preferred_vote; + bool m_has_outcome; + uint64_t m_outcome; + bool m_outcome_in_dispositions; +#endif + + private: + global_voter(const global_voter&); + global_voter& operator = (const global_voter&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_global_voter_h_ diff --git a/txman/global_voter.h b/txman/global_voter.h new file mode 100644 index 0000000..ffeb2a4 --- /dev/null +++ b/txman/global_voter.h @@ -0,0 +1,77 @@ +// Copyright (c) 2016, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_global_voter_h_ +#define consus_txman_global_voter_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/transaction_group.h" +#include "txman/generalized_paxos.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class global_voter +{ + public: + global_voter(const transaction_group& tg); + ~global_voter() throw (); + + public: + const transaction_group& state_key() const; + bool finished(); + + public: + void set_preferred_vote(uint64_t v); + void init(paxos_group_id* us, const paxos_group_id* dcs, size_t dcs_sz, daemon* d); +#if 0 + void vote_1a(comm_id id, unsigned idx, const paxos_synod::ballot& b, daemon* d); + void vote_1b(comm_id id, unsigned idx, + const paxos_synod::ballot& b, + const paxos_synod::pvalue& p, + daemon* d); + void vote_2a(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_2b(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_learn(unsigned idx, uint64_t v, daemon* d); +#endif + void externally_work_state_machine(daemon* d); + bool outcome(uint64_t* v); + + private: + bool preconditions(daemon* d); + void work_state_machine(daemon* d); + + private: + const transaction_group m_tg; + po6::threads::mutex m_mtx; + bool m_initialized; + paxos_group_id m_us; + paxos_group_id m_dcs[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t m_dcs_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + size_t m_dcs_sz; + generalized_paxos m_data_center_gp; + generalized_paxos m_global_gp; +#if 0 + paxos_group m_group; + paxos_synod m_votes[CONSUS_MAX_REPLICATION_FACTOR]; + paxos_synod::phase_t m_phases[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t m_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + bool m_has_preferred_vote; + uint64_t m_preferred_vote; + bool m_has_outcome; + uint64_t m_outcome; + bool m_outcome_in_dispositions; +#endif + + private: + global_voter(const global_voter&); + global_voter& operator = (const global_voter&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_global_voter_h_ diff --git a/txman/local_voter.cc b/txman/local_voter.cc new file mode 100644 index 0000000..aa09dcc --- /dev/null +++ b/txman/local_voter.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// Google Log +#include + +// BusyBee +#include + +// consus +#include "common/network_msgtype.h" +#include "txman/daemon.h" +#include "txman/local_voter.h" +#include "txman/log_entry_t.h" + +#pragma GCC diagnostic ignored "-Wlarger-than=" + +using consus::local_voter; + +local_voter :: local_voter(const transaction_group& tg) + : m_tg(tg) + , m_mtx() + , m_initialized(false) + , m_group() + , m_votes() + , m_timestamps() + , m_has_preferred_vote(false) + , m_preferred_vote(0) + , m_has_outcome(false) + , m_outcome(0) + , m_outcome_in_dispositions(false) +{ + po6::threads::mutex::hold hold(&m_mtx); + + for (unsigned i = 0; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + m_phases[i] = paxos_synod::PHASE1; + m_timestamps[i] = 0; + } +} + +local_voter :: ~local_voter() throw () +{ +} + +const consus::transaction_group& +local_voter :: state_key() const +{ + return m_tg; +} + +bool +local_voter :: finished() +{ + po6::threads::mutex::hold hold(&m_mtx); + return !m_initialized || m_outcome_in_dispositions; +} + +void +local_voter :: set_preferred_vote(uint64_t v) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (!m_has_preferred_vote) + { + m_has_preferred_vote = true; + m_preferred_vote = v; + } +} + +void +local_voter :: vote_1a(comm_id id, unsigned idx, const paxos_synod::ballot& b, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (!preconditions_for_paxos(d)) + { + return; + } + + if (idx >= m_group.members_sz || + id != b.leader) + { + return; + } + + paxos_synod::ballot a; + paxos_synod::pvalue p; + m_votes[idx].phase1a(b, &a, &p); + + std::string entry; + e::packer(&entry) + << LOG_ENTRY_LOCAL_VOTE_1A << m_tg << uint8_t(idx) << b; + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(LV_VOTE_1B) + + pack_size(m_tg) + + sizeof(uint8_t) + + pack_size(a) + + pack_size(p); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << LV_VOTE_1B << m_tg << uint8_t(idx) << a << p; + d->send_when_durable(entry, b.leader, msg); +} + +void +local_voter :: vote_1b(comm_id id, unsigned idx, + const paxos_synod::ballot& b, + const paxos_synod::pvalue& p, + daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (idx >= m_group.members_sz) + { + return; + } + + m_votes[idx].phase1b(id, b, p); + work_state_machine(d); +} + +void +local_voter :: vote_2a(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (idx >= m_group.members_sz || + id != p.b.leader) + { + return; + } + + bool send = false; + m_votes[idx].phase2a(p, &send); + + if (send) + { + std::string entry; + e::packer(&entry) + << LOG_ENTRY_LOCAL_VOTE_2A << m_tg << uint8_t(idx) << p; + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(LV_VOTE_2B) + + pack_size(m_tg) + + sizeof(uint8_t) + + pack_size(p); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << LV_VOTE_2B << m_tg << uint8_t(idx) << p; + d->send_when_durable(entry, p.b.leader, msg); + } +} + +void +local_voter :: vote_2b(comm_id id, unsigned idx, + const paxos_synod::pvalue& p, + daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (idx >= m_group.members_sz) + { + return; + } + + paxos_synod::phase_t x = m_votes[idx].phase(); + m_votes[idx].phase2b(id, p); + + if (x != paxos_synod::LEARNED && + m_votes[idx].phase() == paxos_synod::LEARNED) + { + std::string entry; + e::packer(&entry) + << LOG_ENTRY_LOCAL_LEARN << m_tg << uint8_t(idx) << m_votes[idx].learned(); + d->m_log.append(entry.data(), entry.size()); + } + + work_state_machine(d); +} + +void +local_voter :: vote_learn(unsigned idx, uint64_t v, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (idx >= m_group.members_sz) + { + return; + } + + if (m_votes[idx].phase() == paxos_synod::LEARNED && + m_votes[idx].learned() != v) + { + // this should never happen; let's catch if it does so we can make sure + // it doesn't happen in the future + LOG(ERROR) << "synod_commit learned inconsistent values: " << m_votes[idx].learned() << " vs " << v; + } + else if (m_votes[idx].phase() != paxos_synod::LEARNED) + { + std::string entry; + e::packer(&entry) + << LOG_ENTRY_LOCAL_LEARN << m_tg << uint8_t(idx) << v; + d->m_log.append(entry.data(), entry.size()); + } + + m_votes[idx].force_learn(v); + work_state_machine(d); +} + +void +local_voter :: externally_work_state_machine(daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + work_state_machine(d); +} + +bool +local_voter :: outcome(uint64_t* v) +{ + po6::threads::mutex::hold hold(&m_mtx); + *v = m_outcome; + return m_has_outcome; +} + +bool +local_voter :: preconditions_for_paxos(daemon* d) +{ + uint64_t outcome; + + if (d->m_dispositions.get(m_tg, &outcome)) + { + m_outcome_in_dispositions = true; + return false; + } + + if (!m_initialized) + { + const paxos_group* group = d->get_config()->get_group(m_tg.group); + + if (!group) + { + return false; + } + + m_group = *group; + + for (size_t i = 0; i < m_group.members_sz; ++i) + { + m_votes[i].init(d->m_us.id, m_group); + m_timestamps[i] = 0; + } + + m_initialized = true; + } + + return true; +} + +void +local_voter :: work_state_machine(daemon* d) +{ + if (!preconditions_for_paxos(d)) + { + return; + } + + unsigned our_idx = m_group.index(d->m_us.id); + assert(our_idx < m_group.members_sz); + + if (m_has_preferred_vote) + { + work_paxos_vote(our_idx, d, m_preferred_vote); + } + + for (unsigned i = 1; i < m_group.members_sz; ++i) + { + unsigned idx = (our_idx + i) % m_group.members_sz; + + // XXX this is not robust if the coordinator totally goes missing + if (d->get_config()->get_state(m_group.members[idx]) == txman_state::ONLINE) + { + break; + } + + work_paxos_vote(idx, d, CONSUS_VOTE_ABORT); + } + + unsigned voted = 0; + unsigned committed = 0; + + for (size_t i = 0; i < m_group.members_sz; ++i) + { + if (m_votes[i].phase() == paxos_synod::LEARNED) + { + ++voted; + + if (m_votes[i].learned() == CONSUS_VOTE_COMMIT) + { + ++committed; + } + else if (m_votes[i].learned() != CONSUS_VOTE_ABORT) + { + LOG(ERROR) << m_tg << ".synod_commit[" << i << "] learned invalid value " << m_votes[i].learned(); + } + } + } + + assert(voted <= m_group.members_sz); + unsigned aborted = voted - committed; + assert(aborted < m_group.quorum() || committed < m_group.quorum()); + + if (aborted >= m_group.quorum()) + { + m_has_outcome = true; + m_outcome = CONSUS_VOTE_ABORT; + } + + if (committed >= m_group.quorum()) + { + m_has_outcome = true; + m_outcome = CONSUS_VOTE_COMMIT; + } + + if (d->m_dispositions.has(m_tg)) + { + m_outcome_in_dispositions = true; + } +} + +void +local_voter :: work_paxos_vote(unsigned idx, daemon* d, uint64_t preference) +{ + paxos_synod::ballot b; + paxos_synod::pvalue p; + paxos_synod* ps = &m_votes[idx]; + std::auto_ptr msg(e::buffer::create(pack_size(m_tg) + 64)); + const uint64_t now = po6::monotonic_time(); + + switch (ps->phase()) + { + case paxos_synod::PHASE1: + ps->phase1(&b); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << LV_VOTE_1A << m_tg << uint8_t(idx) << b; + + if (m_phases[idx] != paxos_synod::PHASE1 || + m_timestamps[idx] + d->resend_interval() < now) + { + d->send(m_group, msg); + m_phases[idx] = paxos_synod::PHASE1; + m_timestamps[idx] = now; + } + + break; + case paxos_synod::PHASE2: + ps->phase2(&p, preference); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << LV_VOTE_2A << m_tg << uint8_t(idx) << p; + + if (m_phases[idx] != paxos_synod::PHASE2 || + m_timestamps[idx] + d->resend_interval() < now) + { + d->send(m_group, msg); + m_phases[idx] = paxos_synod::PHASE2; + m_timestamps[idx] = now; + } + + break; + case paxos_synod::LEARNED: + if (m_phases[idx] != paxos_synod::LEARNED || + m_timestamps[idx] + d->resend_interval() < now) + { + msg->pack_at(BUSYBEE_HEADER_SIZE) + << LV_VOTE_LEARN << m_tg << uint8_t(idx) << m_votes[idx].learned(); + d->send(m_group, msg); + m_phases[idx] = paxos_synod::LEARNED; + m_timestamps[idx] = now; + } + + break; + default: + ::abort(); + } +} diff --git a/txman/local_voter.h b/txman/local_voter.h new file mode 100644 index 0000000..007313b --- /dev/null +++ b/txman/local_voter.h @@ -0,0 +1,67 @@ +// Copyright (c) 2016, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_local_voter_h_ +#define consus_txman_local_voter_h_ + +// po6 +#include + +// consus +#include "namespace.h" +#include "common/transaction_group.h" +#include "txman/paxos_synod.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class local_voter +{ + public: + local_voter(const transaction_group& tg); + ~local_voter() throw (); + + public: + const transaction_group& state_key() const; + bool finished(); + + public: + void set_preferred_vote(uint64_t v); + void vote_1a(comm_id id, unsigned idx, const paxos_synod::ballot& b, daemon* d); + void vote_1b(comm_id id, unsigned idx, + const paxos_synod::ballot& b, + const paxos_synod::pvalue& p, + daemon* d); + void vote_2a(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_2b(comm_id id, unsigned idx, const paxos_synod::pvalue& p, daemon* d); + void vote_learn(unsigned idx, uint64_t v, daemon* d); + void externally_work_state_machine(daemon* d); + bool outcome(uint64_t* v); + + private: + bool preconditions_for_paxos(daemon* d); + void work_state_machine(daemon* d); + void work_paxos_vote(unsigned idx, daemon* d, uint64_t preferred); + + private: + const transaction_group m_tg; + po6::threads::mutex m_mtx; + bool m_initialized; + paxos_group m_group; + paxos_synod m_votes[CONSUS_MAX_REPLICATION_FACTOR]; + paxos_synod::phase_t m_phases[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t m_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + bool m_has_preferred_vote; + uint64_t m_preferred_vote; + bool m_has_outcome; + uint64_t m_outcome; + bool m_outcome_in_dispositions; + + private: + local_voter(const local_voter&); + local_voter& operator = (const local_voter&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_local_voter_h_ diff --git a/txman/log_entry_t.cc b/txman/log_entry_t.cc new file mode 100644 index 0000000..c9097f8 --- /dev/null +++ b/txman/log_entry_t.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "common/macros.h" +#include "txman/log_entry_t.h" + +using consus::log_entry_t; + +bool +consus :: is_paxos_2a_log_entry(log_entry_t t) +{ + switch (t) + { + case LOG_ENTRY_TX_BEGIN: + case LOG_ENTRY_TX_READ: + case LOG_ENTRY_TX_WRITE: + case LOG_ENTRY_TX_PREPARE: + case LOG_ENTRY_TX_ABORT: + return true; + case LOG_ENTRY_LOCAL_VOTE_1A: + case LOG_ENTRY_LOCAL_VOTE_2A: + case LOG_ENTRY_LOCAL_LEARN: + case LOG_ENTRY_CONFIG: + case LOG_ENTRY_NOP: + default: + return false; + } +} + +std::ostream& +consus :: operator << (std::ostream& lhs, const log_entry_t& rhs) +{ + switch (rhs) + { + STRINGIFY(LOG_ENTRY_CONFIG); + STRINGIFY(LOG_ENTRY_TX_BEGIN); + STRINGIFY(LOG_ENTRY_TX_READ); + STRINGIFY(LOG_ENTRY_TX_WRITE); + STRINGIFY(LOG_ENTRY_TX_PREPARE); + STRINGIFY(LOG_ENTRY_TX_ABORT); + STRINGIFY(LOG_ENTRY_LOCAL_VOTE_1A); + STRINGIFY(LOG_ENTRY_LOCAL_VOTE_2A); + STRINGIFY(LOG_ENTRY_LOCAL_LEARN); + STRINGIFY(LOG_ENTRY_NOP); + default: + lhs << "unknown LOG_ENTRY"; + } + + return lhs; +} + +e::packer +consus :: operator << (e::packer lhs, const log_entry_t& rhs) +{ + uint16_t mt = static_cast(rhs); + return lhs << mt; +} + +e::unpacker +consus :: operator >> (e::unpacker lhs, log_entry_t& rhs) +{ + uint16_t mt; + lhs = lhs >> mt; + rhs = static_cast(mt); + return lhs; +} + +size_t +consus :: pack_size(const log_entry_t&) +{ + return sizeof(uint16_t); +} diff --git a/txman/log_entry_t.h b/txman/log_entry_t.h new file mode 100644 index 0000000..2b4a860 --- /dev/null +++ b/txman/log_entry_t.h @@ -0,0 +1,48 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_log_entry_t_h_ +#define consus_txman_log_entry_t_h_ + +// C++ +#include + +// e +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE + +/* log_entry_t occupies [7936, 8192) */ +enum log_entry_t +{ + LOG_ENTRY_CONFIG = 7936, + LOG_ENTRY_TX_BEGIN = 7937, + LOG_ENTRY_TX_READ = 7938, + LOG_ENTRY_TX_WRITE = 7939, + LOG_ENTRY_TX_PREPARE = 7940, + LOG_ENTRY_TX_ABORT = 7943, + LOG_ENTRY_LOCAL_VOTE_1A = 7944, + LOG_ENTRY_LOCAL_VOTE_2A = 7946, + LOG_ENTRY_LOCAL_LEARN = 7947, + LOG_ENTRY_NOP = 8191 +}; + +bool +is_paxos_2a_log_entry(log_entry_t t); + +std::ostream& +operator << (std::ostream& lhs, const log_entry_t& rhs); + +e::packer +operator << (e::packer pa, const log_entry_t& rhs); +e::unpacker +operator >> (e::unpacker up, log_entry_t& rhs); +size_t +pack_size(const log_entry_t& tid); + +END_CONSUS_NAMESPACE + +#endif // consus_txman_log_entry_t_h_ diff --git a/txman/main.cc b/txman/main.cc new file mode 100644 index 0000000..d4dc5a3 --- /dev/null +++ b/txman/main.cc @@ -0,0 +1,194 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// POSIX +#include + +// C++ +#include + +// Google Log +#include + +// po6 +#include +#include + +// e +#include + +// BusyBee +#include + +// replicant +#include + +// consus +#include "common/constants.h" +#include "common/macros.h" +#include "txman/daemon.h" +#include "tools/connect_opts.h" + +extern bool s_debug_mode; + +int +main(int argc, const char* argv[]) +{ + bool daemonize = true; + const char* data = "."; + const char* log = NULL; + bool listen = false; + const char* listen_host = "auto"; + long listen_port = CONSUS_PORT_TXMAN; + const char* pidfile = ""; + bool has_pidfile = false; + long threads = 0; + bool log_immediate = false; + sigset_t ss; + + if (sigfillset(&ss) < 0 || + sigprocmask(SIG_BLOCK, &ss, NULL) < 0) + { + std::cerr << "could not block signals"; + return EXIT_FAILURE; + } + + consus::connect_opts conn('c', "connect", 'P', "connect-port", 'C', "connect-string"); + e::argparser ap; + ap.autohelp(); + ap.arg().name('d', "daemon") + .description("run in the background") + .set_true(&daemonize); + ap.arg().name('f', "foreground") + .description("run in the foreground") + .set_false(&daemonize); + ap.arg().name('D', "data") + .description("store persistent state in this directory (default: .)") + .metavar("dir").as_string(&data); + ap.arg().name('L', "log") + .description("store logs in this directory (default: --data)") + .metavar("dir").as_string(&log); + ap.arg().name('l', "listen") + .description("listen on a specific IP address (default: auto)") + .metavar("IP").as_string(&listen_host).set_true(&listen); + ap.arg().name('p', "listen-port") + .description("listen on an alternative port (default: " STR(CONSUS_PORT_TXMAN) ")") + .metavar("port").as_long(&listen_port).set_true(&listen); + ap.arg().long_name("pidfile") + .description("write the PID to a file (default: don't)") + .metavar("file").as_string(&pidfile).set_true(&has_pidfile); + ap.arg().name('t', "threads") + .description("the number of threads which will handle network traffic") + .metavar("N").as_long(&threads); + ap.arg().long_name("log-immediate") + .description("immediately flush all log output") + .set_true(&log_immediate).hidden(); + ap.arg().long_name("debug") + .description("start in debug mode") + .set_true(&s_debug_mode).hidden(); + ap.add("Connect to the cluster:", conn.parser()); + + if (!ap.parse(argc, argv)) + { + return EXIT_FAILURE; + } + + if (!conn.validate()) + { + std::cerr << "invalid host:port specification\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (ap.args_sz() != 0) + { + std::cerr << "command takes no positional arguments\n" << std::endl; + ap.usage(); + return EXIT_FAILURE; + } + + if (listen_port >= (1 << 16) || listen_port <= 0) + { + std::cerr << "listen-port is out of range" << std::endl; + return EXIT_FAILURE; + } + + po6::net::ipaddr listen_ip; + po6::net::location bind_to; + + if (strcmp(listen_host, "auto") == 0) + { + if (!busybee_discover(&listen_ip)) + { + std::cerr << "cannot automatically discover local address; specify one manually" << std::endl; + return EXIT_FAILURE; + } + + bind_to = po6::net::location(listen_ip, listen_port); + } + else + { + if (listen_ip.set(listen_host)) + { + bind_to = po6::net::location(listen_ip, listen_port); + } + + if (bind_to == po6::net::location()) + { + bind_to = po6::net::hostname(listen_host, 0).lookup(AF_UNSPEC, IPPROTO_TCP); + bind_to.port = listen_port; + } + } + + if (bind_to == po6::net::location()) + { + std::cerr << "cannot interpret listen address as hostname or IP address" << std::endl; + return EXIT_FAILURE; + } + + if (bind_to.address == po6::net::ipaddr::ANY()) + { + std::cerr << "cannot bind to " << bind_to << " because it is not routable" << std::endl; + return EXIT_FAILURE; + } + + google::InitGoogleLogging(argv[0]); + google::InstallFailureSignalHandler(); + + if (log_immediate) + { + FLAGS_logbufsecs = 0; + } + + if (threads <= 0) + { + threads += sysconf(_SC_NPROCESSORS_ONLN); + + if (threads <= 0) + { + std::cerr << "cannot create a non-positive number of threads" << std::endl; + return EXIT_FAILURE; + } + } + else if (threads > 512) + { + std::cerr << "refusing to create more than 512 threads" << std::endl; + return EXIT_FAILURE; + } + + try + { + consus::daemon d; + return d.run(daemonize, + std::string(data), + std::string(log ? log : data), + std::string(pidfile), has_pidfile, + listen, bind_to, + conn.isset(), conn.conn_str(), threads); + } + catch (std::exception& e) + { + std::cerr << "error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/txman/mapper.cc b/txman/mapper.cc new file mode 100644 index 0000000..0cbac6a --- /dev/null +++ b/txman/mapper.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "txman/daemon.h" +#include "txman/mapper.h" + +using consus::mapper; + +mapper :: mapper(daemon* d) + : m_d(d) +{ +} + +mapper :: ~mapper() throw () +{ +} + +bool +mapper :: lookup(uint64_t server_id, po6::net::location* bound_to) +{ + po6::net::location loc; + + if (m_d->m_config->exists(comm_id(server_id))) + { + *bound_to = m_d->m_config->get_address(comm_id(server_id)); + return true; + } + + return false; +} diff --git a/txman/mapper.h b/txman/mapper.h new file mode 100644 index 0000000..96eb2b7 --- /dev/null +++ b/txman/mapper.h @@ -0,0 +1,35 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_mapper_h_ +#define consus_txman_mapper_h_ + +// BusyBee +#include + +// consus +#include "namespace.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class mapper : public busybee_mapper +{ + public: + mapper(daemon* d); + ~mapper() throw (); + + public: + virtual bool lookup(uint64_t server_id, po6::net::location* bound_to); + + private: + daemon* m_d; + + private: + mapper(const mapper&); + mapper& operator = (const mapper&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_mapper_h_ diff --git a/txman/paxos_synod.cc b/txman/paxos_synod.cc new file mode 100644 index 0000000..dd87236 --- /dev/null +++ b/txman/paxos_synod.cc @@ -0,0 +1,393 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// consus +#include "txman/paxos_synod.h" + +using consus::paxos_synod; + +paxos_synod :: paxos_synod() + : m_init(false) + , m_us() + , m_group() + , m_acceptor_ballot() + , m_acceptor_pvalue() + , m_leader_phase() + , m_leader_ballot() + , m_leader_pvalue() + , m_value() +{ +} + +paxos_synod :: ~paxos_synod() throw () +{ +} + +void +paxos_synod :: init(comm_id us, const paxos_group& pg) +{ + assert(!m_init); + m_init = true; + m_us = us; + m_group = pg; +} + +paxos_synod::phase_t +paxos_synod :: phase() +{ + assert(m_init); + return m_leader_phase; +} + +void +paxos_synod :: phase1(ballot* b) +{ + assert(m_init); + assert(m_leader_phase == PHASE1); + uint64_t number = m_leader_ballot.number; + + for (unsigned i = 0; i < m_group.members_sz; ++i) + { + number = std::max(number, m_promises[i].current_ballot.number); + } + + if (m_leader_ballot.leader != m_us) + { + ++number; + } + + *b = m_leader_ballot = ballot(number, m_us); +} + +void +paxos_synod :: phase1a(const ballot& b, ballot* a, pvalue* p) +{ + assert(m_init); + + if (b > m_acceptor_ballot) + { + m_acceptor_ballot = b; + } + + *a = m_acceptor_ballot; + *p = m_acceptor_pvalue; + set_phase(); +} + +void +paxos_synod :: phase1b(comm_id m, const ballot& a, const pvalue& p) +{ + assert(m_init); + unsigned idx = m_group.index(m); + + if (idx < m_group.members_sz && + m_leader_phase >= PHASE1 && + m_leader_ballot == a) + { + m_promises[idx].current_ballot = a; + m_promises[idx].current_pvalue = p; + } + + set_phase(); +} + +void +paxos_synod :: phase2(pvalue* p, uint64_t preferred_value) +{ + assert(m_init); + assert(m_leader_phase == PHASE2); + *p = pvalue(); + + for (unsigned i = 0; i < m_group.members_sz; ++i) + { + *p = std::max(*p, m_promises[i].current_pvalue); + } + + if (*p == pvalue()) + { + *p = pvalue(m_leader_ballot, preferred_value); + } + else + { + *p = pvalue(m_leader_ballot, p->v); + } + + m_leader_pvalue = *p; + set_phase(); +} + +void +paxos_synod :: phase2a(const pvalue& p, bool* a) +{ + assert(m_init); + + if (p.b == m_acceptor_ballot) + { + m_acceptor_pvalue = p; + *a = true; + } + else + { + *a = false; + } + + set_phase(); +} + +void +paxos_synod :: phase2b(comm_id m, const pvalue& p) +{ + assert(m_init); + unsigned idx = m_group.index(m); + + if (idx < m_group.members_sz && + m_leader_phase == PHASE2 && + m_leader_ballot == p.b) + { + m_promises[idx].current_pvalue = p; + } + + set_phase(); +} + +void +paxos_synod :: force_learn(uint64_t value) +{ + assert(m_init); + m_leader_phase = LEARNED; + m_value = value; +} + +uint64_t +paxos_synod :: learned() +{ + assert(m_init); + assert(m_leader_phase == LEARNED); + return m_value; +} + +void +paxos_synod :: set_phase() +{ + for (size_t i = 0; i < m_group.members_sz; ++i) + { + if (m_promises[i].current_ballot > m_leader_ballot) + { + m_leader_phase = PHASE1; + return; + } + } + + if (m_leader_phase == PHASE1) + { + unsigned accepted = 0; + + for (size_t i = 0; i < m_group.members_sz; ++i) + { + if (m_leader_ballot != ballot() && + m_promises[i].current_ballot == m_leader_ballot) + { + ++accepted; + } + } + + if (accepted >= m_group.quorum()) + { + m_leader_phase = PHASE2; + } + } + + if (m_leader_phase == PHASE2) + { + unsigned accepted = 0; + + for (size_t i = 0; i < m_group.members_sz; ++i) + { + if (m_promises[i].current_ballot == m_leader_ballot && + m_promises[i].current_pvalue == m_leader_pvalue && + m_leader_pvalue.b == m_leader_ballot) + { + ++accepted; + } + } + + if (accepted >= m_group.quorum()) + { + m_leader_phase = LEARNED; + m_value = m_leader_pvalue.v; + } + } +} + +paxos_synod :: ballot :: ballot() + : number(0) + , leader() +{ +} + +paxos_synod :: ballot :: ballot(uint64_t n, comm_id l) + : number(n) + , leader(l) +{ +} + +paxos_synod :: ballot :: ballot(const ballot& other) + : number(other.number) + , leader(other.leader) +{ +} + +paxos_synod :: ballot :: ~ballot() throw () +{ +} + +int +paxos_synod :: ballot :: compare(const ballot& rhs) const +{ + if (number < rhs.number) + { + return -1; + } + if (number > rhs.number) + { + return 1; + } + + if (leader < rhs.leader) + { + return -1; + } + if (leader > rhs.leader) + { + return 1; + } + + return 0; +} + +paxos_synod::ballot& +paxos_synod :: ballot :: operator = (const ballot& rhs) +{ + if (this != &rhs) + { + number = rhs.number; + leader = rhs.leader; + } + + return *this; +} + +bool paxos_synod :: ballot :: operator < (const ballot& rhs) const { return compare(rhs) < 0; } +bool paxos_synod :: ballot :: operator <= (const ballot& rhs) const { return compare(rhs) <= 0; } +bool paxos_synod :: ballot :: operator == (const ballot& rhs) const { return compare(rhs) == 0; } +bool paxos_synod :: ballot :: operator != (const ballot& rhs) const { return compare(rhs) != 0; } +bool paxos_synod :: ballot :: operator >= (const ballot& rhs) const { return compare(rhs) >= 0; } +bool paxos_synod :: ballot :: operator > (const ballot& rhs) const { return compare(rhs) > 0; } + +paxos_synod :: pvalue :: pvalue() + : b() + , v() +{ +} + +paxos_synod :: pvalue :: pvalue(const ballot& _b, uint64_t _v) + : b(_b) + , v(_v) +{ +} + +paxos_synod :: pvalue :: pvalue(const pvalue& other) + : b(other.b) + , v(other.v) +{ +} + +paxos_synod :: pvalue :: ~pvalue() throw () +{ +} + +int +paxos_synod :: pvalue :: compare(const pvalue& rhs) const +{ + return b.compare(rhs.b); +} + +paxos_synod::pvalue& +paxos_synod :: pvalue :: operator = (const pvalue& rhs) +{ + if (this != &rhs) + { + b = rhs.b; + v = rhs.v; + } + + return *this; +} + +bool paxos_synod :: pvalue :: operator < (const pvalue& rhs) const { return compare(rhs) < 0; } +bool paxos_synod :: pvalue :: operator <= (const pvalue& rhs) const { return compare(rhs) <= 0; } +bool paxos_synod :: pvalue :: operator == (const pvalue& rhs) const { return compare(rhs) == 0; } +bool paxos_synod :: pvalue :: operator != (const pvalue& rhs) const { return compare(rhs) != 0; } +bool paxos_synod :: pvalue :: operator >= (const pvalue& rhs) const { return compare(rhs) >= 0; } +bool paxos_synod :: pvalue :: operator > (const pvalue& rhs) const { return compare(rhs) > 0; } + +paxos_synod :: promise :: promise() + : current_ballot() + , current_pvalue() +{ +} + +paxos_synod :: promise :: ~promise() throw () +{ +} + +std::ostream& +consus :: operator << (std::ostream& out, const paxos_synod::ballot& b) +{ + return out << "ballot(number=" << b.number + << ", leader=" << b.leader.get() << ")"; +} + +std::ostream& +consus :: operator << (std::ostream& out, const paxos_synod::pvalue& p) +{ + return out << "pvalue(number=" << p.b.number + << ", leader=" << p.b.leader.get() + << ", value=" << p.v << ")"; +} + +e::packer +consus :: operator << (e::packer pa, const paxos_synod::ballot& rhs) +{ + return pa << rhs.number << rhs.leader; +} + +e::unpacker +consus :: operator >> (e::unpacker up, paxos_synod::ballot& rhs) +{ + return up >> rhs.number >> rhs.leader; +} + +size_t +consus :: pack_size(const paxos_synod::ballot& b) +{ + return sizeof(uint64_t) + pack_size(b.leader); +} + +e::packer +consus :: operator << (e::packer pa, const paxos_synod::pvalue& rhs) +{ + return pa << rhs.b << rhs.v; +} + +e::unpacker +consus :: operator >> (e::unpacker up, paxos_synod::pvalue& rhs) +{ + return up >> rhs.b >> rhs.v; +} + +size_t +consus :: pack_size(const paxos_synod::pvalue& p) +{ + return sizeof(uint64_t) + pack_size(p.b); +} diff --git a/txman/paxos_synod.h b/txman/paxos_synod.h new file mode 100644 index 0000000..1f674a8 --- /dev/null +++ b/txman/paxos_synod.h @@ -0,0 +1,133 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_paxos_synod_h_ +#define consus_txman_paxos_synod_h_ + +// consus +#include "namespace.h" +#include "common/paxos_group.h" + +BEGIN_CONSUS_NAMESPACE + +class paxos_synod +{ + public: + enum phase_t + { + PHASE1, + PHASE2, + LEARNED + }; + + struct ballot + { + ballot(); + ballot(uint64_t number, comm_id leader); + ballot(const ballot& other); + ~ballot() throw (); + int compare(const ballot& rhs) const; + ballot& operator = (const ballot& rhs); + bool operator < (const ballot& rhs) const; + bool operator <= (const ballot& rhs) const; + bool operator == (const ballot& rhs) const; + bool operator != (const ballot& rhs) const; + bool operator >= (const ballot& rhs) const; + bool operator > (const ballot& rhs) const; + + uint64_t number; + comm_id leader; + }; + struct pvalue + { + pvalue(); + pvalue(const ballot& b, uint64_t v); + pvalue(const pvalue& other); + ~pvalue() throw (); + int compare(const pvalue& rhs) const; + pvalue& operator = (const pvalue& rhs); + bool operator < (const pvalue& rhs) const; + bool operator <= (const pvalue& rhs) const; + bool operator == (const pvalue& rhs) const; + bool operator != (const pvalue& rhs) const; + bool operator >= (const pvalue& rhs) const; + bool operator > (const pvalue& rhs) const; + + ballot b; + uint64_t v; + }; + + public: + paxos_synod(); + ~paxos_synod() throw (); + + public: + void init(comm_id us, const paxos_group& pg); + phase_t phase(); + void phase1(ballot* b); + void phase1a(const ballot& b, ballot* a, pvalue* p); + void phase1b(comm_id m, const ballot& a, const pvalue& p); + void phase2(pvalue* p, uint64_t preferred_value); + void phase2a(const pvalue& p, bool* a); + void phase2b(comm_id m, const pvalue& p); + void force_learn(uint64_t value); + uint64_t learned(); + + private: + struct promise + { + promise(); + ~promise() throw (); + + ballot current_ballot; + pvalue current_pvalue; + + private: + promise(const promise&); + promise& operator = (const promise&); + }; + void set_phase(); + + private: + bool m_init; + comm_id m_us; + paxos_group m_group; + + ballot m_acceptor_ballot; + pvalue m_acceptor_pvalue; + + phase_t m_leader_phase; + ballot m_leader_ballot; + pvalue m_leader_pvalue; + + promise m_promises[CONSUS_MAX_REPLICATION_FACTOR]; + + uint64_t m_value; + + private: + paxos_synod(const paxos_synod&); + paxos_synod& operator = (const paxos_synod&); +}; + +std::ostream& +operator << (std::ostream& out, const paxos_synod::ballot& b); +std::ostream& +operator << (std::ostream& out, const paxos_synod::pvalue& p); + +e::packer +operator << (e::packer pa, const paxos_synod::ballot& rhs); +e::unpacker +operator >> (e::unpacker up, paxos_synod::ballot& rhs); +size_t +pack_size(const paxos_synod::ballot& b); + +e::packer +operator << (e::packer pa, const paxos_synod::pvalue& rhs); +e::unpacker +operator >> (e::unpacker up, paxos_synod::pvalue& rhs); +size_t +pack_size(const paxos_synod::pvalue& p); + +END_CONSUS_NAMESPACE + +#endif // consus_txman_paxos_synod_h_ diff --git a/txman/transaction.cc b/txman/transaction.cc new file mode 100644 index 0000000..891b481 --- /dev/null +++ b/txman/transaction.cc @@ -0,0 +1,1650 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +// STL +#include + +// Google Log +#include + +// e +#include +#include + +// BusyBee +#include + +// consus +#include "common/consus.h" +#include "common/ids.h" +#include "txman/daemon.h" +#include "txman/log_entry_t.h" +#include "txman/transaction.h" + +#pragma GCC diagnostic ignored "-Wlarger-than=" + +using consus::transaction; + +extern bool s_debug_mode; + +struct transaction :: comparison +{ + comparison() : type(false), table(false), key(false), value(false) {} + ~comparison() throw () {} + + bool type; + bool table; + bool key; + bool value; + + private: + comparison(const comparison& other); + comparison& operator = (const comparison& rhs); +}; + +struct transaction :: operation +{ + operation(); + ~operation() throw (); + + // utils + void set_client(comm_id client, uint64_t nonce); + bool merge(const operation& op, const comparison& cmp); + + // log entry + log_entry_t type; + e::slice table; + e::slice key; + uint64_t timestamp; + e::slice value; + consus_returncode rc; + e::compat::shared_ptr backing; + + // reading + bool require_read_lock; + bool read_lock_acquired; + bool read_lock_released; + e::compat::shared_ptr read_backing; + uint64_t read_timestamp; + + // writing + bool require_write; + bool write_started; + bool write_finished; + uint64_t write_timestamp; + + // durability + bool log_write_issued; + bool log_write_durable; + bool durable[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t paxos_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t paxos_2b_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + + // client response + comm_id client; + uint64_t nonce; +}; + +transaction :: operation :: operation() + : type(LOG_ENTRY_NOP) + , table() + , key() + , timestamp(0) + , value() + , rc(CONSUS_GARBAGE) + , backing() + , require_read_lock(false) + , read_lock_acquired(false) + , read_lock_released(false) + , read_backing() + , read_timestamp(0) + , require_write(false) + , write_started(false) + , write_finished(false) + , write_timestamp(0) + , log_write_issued(false) + , log_write_durable(false) + , client() + , nonce() +{ + for (unsigned i = 0; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + durable[i] = false; + paxos_timestamps[i] = 0; + paxos_2b_timestamps[i] = 0; + } +} + +transaction :: operation :: ~operation() throw () +{ +} + +void +transaction :: operation :: set_client(comm_id c, uint64_t n) +{ + if (client == comm_id() || c != comm_id()) + { + client = c; + nonce = n; + } +} + +bool +transaction :: operation :: merge(const operation& op, const comparison& cmp) +{ + if (type == LOG_ENTRY_NOP) + { + type = op.type; + table = op.table; + key = op.key; + value = op.value; + backing = op.backing; + } + else + { + if ((cmp.type && type != op.type) || + (cmp.table && table != op.table) || + (cmp.key && key != op.key) || + (cmp.value && value != op.value)) + { + return false; + } + } + + return true; +} + +transaction :: transaction(const transaction_group& tg) + : m_tg(tg) + , m_mtx() + , m_init_timestamp() + , m_group() + , m_dcs() + , m_dcs_sz() + , m_state(INITIALIZED) + , m_timestamp(0) + , m_prefer_to_commit(true) + , m_ops() + , m_deferred_2b() +{ + po6::threads::mutex::hold hold(&m_mtx); + + for (unsigned i = 0; i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + m_dcs_timestamps[i] = 0; + } +} + +transaction :: ~transaction() throw () +{ +} + +const consus::transaction_group& +transaction :: state_key() const +{ + return m_tg; +} + +bool +transaction :: finished() +{ + po6::threads::mutex::hold hold(&m_mtx); + return m_state == INITIALIZED || m_state == COLLECTED; +} + +void +transaction :: begin(comm_id id, uint64_t nonce, uint64_t timestamp, + const paxos_group& group, + const std::vector& dcs, + daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + internal_begin("client", timestamp, group, dcs, d); + m_ops[0].set_client(id, nonce); + work_state_machine(d); +} + +void +transaction :: paxos_2a_begin(uint64_t seqno, + e::unpacker up, + std::auto_ptr, + daemon* d) +{ + uint64_t timestamp; + std::vector dcs; + up = up >> timestamp >> dcs; + const paxos_group* group = d->get_config()->get_group(m_tg.group); + + if (seqno != 0 || up.error() || up.remain() || !group) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " paxos 2a begin failed; invariants violated"; + po6::threads::mutex::hold hold(&m_mtx); + avoid_commit_if_possible(d); + return; + } + + po6::threads::mutex::hold hold(&m_mtx); + internal_begin("paxos 2a", timestamp, *group, dcs, d); + work_state_machine(d); +} + +void +transaction :: commit_record_begin(uint64_t seqno, + e::unpacker up, + e::compat::shared_ptr, + daemon* d) +{ + uint64_t timestamp; + std::vector dcs(m_dcs, m_dcs + m_dcs_sz); + up = up >> timestamp >> dcs; + const paxos_group* group = d->get_config()->get_group(m_tg.group); + + if (seqno != 0 || up.error() || up.remain() || !group) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " commit record begin failed; invariants violated"; + avoid_commit_if_possible(d); + return; + } + + internal_begin("commit record", timestamp, *group, dcs, d); +} + +void +transaction :: internal_begin(const char* source, uint64_t timestamp, + const paxos_group& group, + const std::vector& dcs, + daemon* d) +{ + ensure_initialized(); + + if (s_debug_mode) + { + LOG(INFO) << m_tg << "[0] = " << source << " initiated begin() in " << dcs.size() << " datacenters @ " << timestamp; + + for (size_t i = 0; i < dcs.size(); ++i) + { + LOG(INFO) << m_tg << ".dc[" << i << "] " << dcs[i]; + } + + LOG(INFO) << m_tg << ".group " << group; + } + + if (m_state > EXECUTING) + { + LOG_IF(INFO, s_debug_mode) << "NOP: state > EXECUTING"; + return; + } + + operation op; + comparison cmp; + op.type = LOG_ENTRY_TX_BEGIN; + cmp.type = true; + + if (!resize_to_hold(0) || + !m_ops[0].merge(op, cmp) || + (m_init_timestamp != 0 && m_init_timestamp != timestamp)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " begin failed: invariants violated"; + avoid_commit_if_possible(d); + return; + } + + if (m_init_timestamp == 0) + { + m_init_timestamp = timestamp; + m_timestamp = std::max(m_timestamp, timestamp); // XXX replay + m_group = group; + + for (unsigned i = 0; i < dcs.size() && i < CONSUS_MAX_REPLICATION_FACTOR; ++i) + { + m_dcs[i] = dcs[i]; + } + + m_dcs_sz = dcs.size(); + + for (size_t i = 0; i < m_deferred_2b.size(); ++i) + { + internal_paxos_2b(m_deferred_2b[i].first, m_deferred_2b[i].second, d); + } + } + else if (s_debug_mode) + { + LOG(INFO) << "NOP: already initiated"; + } +} + +void +transaction :: read(comm_id id, uint64_t nonce, uint64_t seqno, + const e::slice& table, + const e::slice& key, + std::auto_ptr _backing, + daemon* d) +{ + e::compat::shared_ptr backing(_backing.release()); + po6::threads::mutex::hold hold(&m_mtx); + internal_read("client", seqno, table, key, backing, d); + m_ops[seqno].require_read_lock = true; + m_ops[seqno].set_client(id, nonce); + work_state_machine(d); +} + +void +transaction :: paxos_2a_read(uint64_t seqno, + e::unpacker up, + std::auto_ptr _backing, + daemon* d) +{ + e::slice table; + e::slice key; + uint64_t timestamp; + up = up >> table >> key >> timestamp; + + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " paxos 2a read failed; invariants violated"; + po6::threads::mutex::hold hold(&m_mtx); + avoid_commit_if_possible(d); + return; + } + + e::compat::shared_ptr backing(_backing.release()); + po6::threads::mutex::hold hold(&m_mtx); + internal_read("paxos 2a", seqno, table, key, backing, d); + m_ops[seqno].require_read_lock = true; + m_ops[seqno].read_lock_acquired = true; + // XXX copy value + work_state_machine(d); +} + +#if 0 +void +transaction :: commit_record_read(uint64_t seqno, + e::unpacker up, + std::auto_ptr backing) +{ + e::slice table; + e::slice key; + uint64_t timestamp; + up = up >> table >> key >> timestamp; + + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " commit record read failed; invariants violated"; + m_avoid_commit_if_possible = true; + return; + } + + internal_read("commit record", seqno, table, key, backing); + m_ops[seqno].require_lock = true; + m_ops[seqno].require_fetch = true; +} +#endif + +void +transaction :: internal_read(const char* source, uint64_t seqno, + const e::slice& table, + const e::slice& key, + e::compat::shared_ptr backing, + daemon* d) +{ + ensure_initialized(); + + if (s_debug_mode) + { + LOG(INFO) << m_tg << "[" << seqno << "] = " << source << " initiated read(\"" + << e::strescape(table.str()) << "\", \"" + << e::strescape(key.str()) << "\")"; + } + + if (m_state > EXECUTING) + { + LOG_IF(INFO, s_debug_mode) << "NOP: state > EXECUTING"; + return; + } + + operation op; + comparison cmp; + op.type = LOG_ENTRY_TX_READ; + cmp.type = true; + op.table = table; + cmp.table = true; + op.key = key; + cmp.key = true; + op.backing = backing; + + if (!resize_to_hold(seqno) || + !m_ops[seqno].merge(op, cmp)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " read failed; invariants violated"; + avoid_commit_if_possible(d); + return; + } +} + +void +transaction :: write(comm_id id, uint64_t nonce, uint64_t seqno, + const e::slice& table, + const e::slice& key, + const e::slice& value, + std::auto_ptr _backing, + daemon* d) +{ + e::compat::shared_ptr backing(_backing.release()); + po6::threads::mutex::hold hold(&m_mtx); + internal_write("client", seqno, table, key, value, backing, d); + m_ops[seqno].require_write = true; + m_ops[seqno].set_client(id, nonce); + work_state_machine(d); +} + +void +transaction :: paxos_2a_write(uint64_t seqno, + e::unpacker up, + std::auto_ptr _backing, + daemon* d) +{ + e::slice table; + e::slice key; + e::slice value; + up = up >> table >> key >> value; + + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " paxos 2a write failed; invariants violated"; + po6::threads::mutex::hold hold(&m_mtx); + avoid_commit_if_possible(d); + return; + } + + e::compat::shared_ptr backing(_backing.release()); + po6::threads::mutex::hold hold(&m_mtx); + internal_write("paxos 2a", seqno, table, key, value, backing, d); + m_ops[seqno].require_write = true; + m_ops[seqno].write_started = true; + // XXX copy timestamp + work_state_machine(d); +} + +#if 0 +void +transaction :: commit_record_write(uint64_t seqno, + e::unpacker up, + std::auto_ptr backing) +{ + e::slice table; + e::slice key; + e::slice value; + up = up >> table >> key >> value; + + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " commit record write failed; invariants violated"; + m_avoid_commit_if_possible = true; + return; + } + + internal_write("commit record", seqno, table, key, value, backing); + m_ops[seqno].require_lock = true; + m_ops[seqno].require_flush = true; +} +#endif + +void +transaction :: internal_write(const char* source, uint64_t seqno, + const e::slice& table, + const e::slice& key, + const e::slice& value, + e::compat::shared_ptr backing, + daemon* d) +{ + ensure_initialized(); + + if (s_debug_mode) + { + LOG(INFO) << m_tg << "[" << seqno << "] = " << source << " initiated write(\"" + << e::strescape(table.str()) << "\", \"" + << e::strescape(key.str()) << "\", \"" + << e::strescape(value.str()) << "\")"; + } + + if (m_state > EXECUTING) + { + LOG_IF(INFO, s_debug_mode) << "NOP: state > EXECUTING"; + return; + } + + operation op; + comparison cmp; + op.type = LOG_ENTRY_TX_WRITE; + cmp.type = true; + op.table = table; + cmp.table = true; + op.key = key; + cmp.key = true; + op.value = value; + cmp.value = true; + op.backing = backing; + + if (!resize_to_hold(seqno) || + !m_ops[seqno].merge(op, cmp)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " write failed; invariants violated"; + avoid_commit_if_possible(d); + return; + } +} + +void +transaction :: commit(comm_id id, uint64_t nonce, uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + internal_end_of_transaction("client", "commit", LOG_ENTRY_TX_PREPARE, seqno, d); + m_ops[seqno].set_client(id, nonce); + work_state_machine(d); +} + +void +transaction :: paxos_2a_prepare(uint64_t seqno, + e::unpacker up, + std::auto_ptr, + daemon* d) +{ + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " paxos 2a commit failed; invariants violated"; + po6::threads::mutex::hold hold(&m_mtx); + avoid_commit_if_possible(d); + return; + } + + po6::threads::mutex::hold hold(&m_mtx); + internal_end_of_transaction("paxos 2a", "commit", LOG_ENTRY_TX_PREPARE, seqno, d); + work_state_machine(d); +} + +void +transaction :: commit_record_prepare(uint64_t seqno, + e::unpacker up, + e::compat::shared_ptr, + daemon* d) +{ + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " commit record commit failed; invariants violated"; + avoid_commit_if_possible(d); + return; + } + + internal_end_of_transaction("commit record", "commit", LOG_ENTRY_TX_PREPARE, seqno, d); + work_state_machine(d); +} + +void +transaction :: abort(comm_id id, uint64_t nonce, uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + internal_end_of_transaction("client", "abort", LOG_ENTRY_TX_ABORT, seqno, d); + m_ops[seqno].set_client(id, nonce); + work_state_machine(d); +} + +void +transaction :: paxos_2a_abort(uint64_t seqno, + e::unpacker up, + std::auto_ptr, + daemon* d) +{ + if (up.error() || up.remain()) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " paxos 2a abort failed; invariants violated"; + po6::threads::mutex::hold hold(&m_mtx); + avoid_commit_if_possible(d); + return; + } + + po6::threads::mutex::hold hold(&m_mtx); + internal_end_of_transaction("paxos 2a", "abort", LOG_ENTRY_TX_ABORT, seqno, d); + work_state_machine(d); +} + +void +transaction :: internal_end_of_transaction(const char* source, const char* func, log_entry_t let, uint64_t seqno, daemon* d) +{ + ensure_initialized(); + + if (s_debug_mode) + { + LOG(INFO) << m_tg << "[" << seqno << "] = " << source << " initiated " << func << "()"; + } + + if (m_state > EXECUTING) + { + LOG_IF(INFO, s_debug_mode) << "NOP: state > EXECUTING"; + return; + } + + operation op; + comparison cmp; + op.type = let; + cmp.type = true; + + if (!resize_to_hold(seqno) || + !m_ops[seqno].merge(op, cmp)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " " << func << " failed; invariants violated"; + avoid_commit_if_possible(d); + return; + } +} + +void +transaction :: paxos_2a(uint64_t seqno, + log_entry_t t, + e::unpacker up, + std::auto_ptr backing, + daemon* d) +{ + assert(is_paxos_2a_log_entry(t)); + assert(!up.error()); + + switch (t) + { + case LOG_ENTRY_TX_BEGIN: + return paxos_2a_begin(seqno, up, backing, d); + case LOG_ENTRY_TX_READ: + return paxos_2a_read(seqno, up, backing, d); + case LOG_ENTRY_TX_WRITE: + return paxos_2a_write(seqno, up, backing, d); + case LOG_ENTRY_TX_PREPARE: + return paxos_2a_prepare(seqno, up, backing, d); + case LOG_ENTRY_TX_ABORT: + return paxos_2a_abort(seqno, up, backing, d); + case LOG_ENTRY_LOCAL_VOTE_1A: + case LOG_ENTRY_LOCAL_VOTE_2A: + case LOG_ENTRY_LOCAL_LEARN: + case LOG_ENTRY_CONFIG: + case LOG_ENTRY_NOP: + default: + ::abort(); + } +} + +void +transaction :: paxos_2b(comm_id id, uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + internal_paxos_2b(id, seqno, d); + work_state_machine(d); +} + +void +transaction :: internal_paxos_2b(comm_id id, uint64_t seqno, daemon* d) +{ + ensure_initialized(); + + if (m_state > EXECUTING) + { + LOG_IF(INFO, s_debug_mode) << m_tg << "[" << seqno << "] paxos 2b dropped: state > EXECUTING"; + return; + } + + unsigned idx = m_group.index(id); + + if (idx >= m_group.members_sz) + { + if (m_init_timestamp == 0) + { + m_deferred_2b.push_back(std::make_pair(id, seqno)); + LOG_IF(INFO, s_debug_mode) << m_tg << "[" << seqno << "] paxos 2b deferred until transaction begins"; + } + else + { + LOG(ERROR) << "paxos phase2b failed: " << id << " not a member of " << m_group.id; + } + + return; + } + + if (!resize_to_hold(seqno)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << "[" << seqno << "] paxos 2b failed: cannot resize to hold sequence number"; + avoid_commit_if_possible(d); + return; + } + + m_ops[seqno].durable[idx] = true; + + if (s_debug_mode) + { + if (d->m_us.id == id) + { + LOG(INFO) << m_tg << "[" << seqno << "] durable on this server"; + } + else + { + LOG(INFO) << m_tg << "[" << seqno << "] durable on " << id; + } + } +} + +void +transaction :: commit_record(e::slice commit_record, std::auto_ptr _backing, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (!m_ops.empty()) + { + work_state_machine(d); + return; + } + + e::unpacker up(commit_record); + e::compat::shared_ptr backing(_backing.release()); + + while (!up.error() && up.remain()) + { + e::slice entry; + up = up >> entry; + + if (up.error()) + { + break; + } + + log_entry_t type; + transaction_group tg; + uint64_t seqno; + e::unpacker eup(entry); + eup = eup >> type >> tg >> seqno; + + if (eup.error()) + { + up = up.error_out(); + break; + } + + switch (type) + { + case LOG_ENTRY_TX_BEGIN: + commit_record_begin(seqno, eup, backing, d); + break; +#if 0 + case LOG_ENTRY_TX_READ: + commit_record_read(seqno, eup, backing); + break; + case LOG_ENTRY_TX_WRITE: + commit_record_write(seqno, eup, backing); + break; +#endif + case LOG_ENTRY_TX_PREPARE: + commit_record_prepare(seqno, eup, backing, d); + break; + case LOG_ENTRY_TX_ABORT: + case LOG_ENTRY_CONFIG: + case LOG_ENTRY_LOCAL_VOTE_1A: + case LOG_ENTRY_LOCAL_VOTE_2A: + case LOG_ENTRY_LOCAL_LEARN: + case LOG_ENTRY_NOP: + default: + LOG(ERROR) << "commit record has " << type << " entry"; + up = up.error_out(); + continue; + } + } + + if (up.error()) + { + ::abort(); // XXX + } + + if (m_ops.empty() || m_ops.back().type != LOG_ENTRY_TX_PREPARE) + { + ::abort(); // XXX + } + + work_state_machine(d); +} + +void +transaction :: callback(uint64_t seqno, daemon* d) +{ + { + po6::threads::mutex::hold hold(&m_mtx); + + if (seqno >= m_ops.size()) + { + return; + } + + m_ops[seqno].log_write_durable = true; + } + + paxos_2b(d->m_us.id, seqno, d); +} + +void +transaction :: externally_work_state_machine(daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + work_state_machine(d); +} + +void +transaction :: kvs_rd_locked(uint64_t seqno, + consus_returncode rc, + uint64_t timestamp, + const e::slice& value, + std::auto_ptr backing, + daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (seqno >= m_ops.size()) + { + return; + } + + if (m_ops[seqno].require_read_lock) + { + m_ops[seqno].read_lock_acquired = true; + m_ops[seqno].rc = rc; + m_ops[seqno].timestamp = timestamp; + m_ops[seqno].value = value; + m_ops[seqno].read_backing = e::compat::shared_ptr(backing.release()); + m_ops[seqno].read_timestamp = 0; + + if (m_state == EXECUTING) + { + m_timestamp = std::max(m_timestamp, timestamp + 1); // XXX replay + } + } + + work_state_machine(d); +} + +void +transaction :: kvs_rd_unlocked(uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (seqno >= m_ops.size()) + { + return; + } + + m_ops[seqno].read_lock_released = true; + work_state_machine(d); +} + +void +transaction :: kvs_wr_begun(uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (seqno >= m_ops.size()) + { + return; + } + + m_ops[seqno].write_started = true; + m_ops[seqno].write_timestamp = 0; + work_state_machine(d); +} + +void +transaction :: kvs_wr_finished(uint64_t seqno, daemon* d) +{ + po6::threads::mutex::hold hold(&m_mtx); + + if (seqno >= m_ops.size()) + { + return; + } + + m_ops[seqno].write_finished = true; + work_state_machine(d); +} + +void +transaction :: ensure_initialized() +{ + if (m_state == INITIALIZED) + { + m_state = EXECUTING; + } +} + +void +transaction :: work_state_machine(daemon* d) +{ + switch (m_state) + { + case INITIALIZED: + return; + case EXECUTING: + return work_state_machine_executing(d); + case LOCAL_COMMIT_VOTE: + return work_state_machine_local_commit_vote(d); + case GLOBAL_COMMIT_VOTE: + return work_state_machine_global_commit_vote(d); + case COMMITTED: + return work_state_machine_committed(d); + case ABORTED: + return work_state_machine_aborted(d); + case TERMINATED: + case COLLECTED: + break; + default: + ::abort(); + } +} + +void +transaction :: work_state_machine_executing(daemon* d) +{ + size_t done = 0; + + for (size_t i = 0; i < m_ops.size(); ++i) + { + if (m_ops[i].type == LOG_ENTRY_NOP) + { + continue; + } + + if (m_ops[i].require_read_lock && !m_ops[i].read_lock_acquired) + { + acquire_read_lock(i, d); + continue; + } + + if (m_ops[i].require_write && !m_ops[i].write_started) + { + begin_write(i, d); + continue; + } + + if (m_ops[i].log_write_durable) + { + send_paxos_2b(i, d); + } + + if (!is_durable(i)) + { + send_paxos_2a(i, d); + + if (!m_ops[i].log_write_issued) + { + std::string le = generate_log_entry(i); + d->callback_when_durable(le, m_tg, i); + m_ops[i].log_write_issued = true; + } + + continue; + } + + if (m_ops[i].client != comm_id()) + { + send_response(&m_ops[i], d); + } + + ++done; + } + + if (done == m_ops.size() && !m_ops.empty() && + (m_ops.back().type == LOG_ENTRY_TX_PREPARE || + m_ops.back().type == LOG_ENTRY_TX_ABORT)) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " transitioning to LOCAL_COMMIT_VOTE state"; + m_state = LOCAL_COMMIT_VOTE; + return work_state_machine(d); + } +} + +void +transaction :: work_state_machine_local_commit_vote(daemon* d) +{ + for (size_t i = 0; i < m_ops.size(); ++i) + { + send_paxos_2a(i, d); + send_paxos_2b(i, d); + } + + daemon::local_voter_map_t::state_reference lvsr; + local_voter* lv = d->m_local_voters.get_or_create_state(m_tg, &lvsr); + assert(lv); + lv->set_preferred_vote(m_prefer_to_commit && m_ops.back().type == LOG_ENTRY_TX_PREPARE + ? CONSUS_VOTE_COMMIT : CONSUS_VOTE_ABORT); + uint64_t outcome; + + if (!lv->outcome(&outcome)) + { + lv->externally_work_state_machine(d); + return; + } + + lv = NULL; + lvsr.release(); + assert(m_dcs_sz >= 1); + bool single_dc = m_dcs_sz == 1; + + if (outcome == CONSUS_VOTE_COMMIT) + { + if (single_dc) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " local vote chose COMMIT; transitioning to COMMITTED state"; + m_state = COMMITTED; + record_commit(d); + } + else + { + LOG_IF(INFO, s_debug_mode) << m_tg << " local vote chose COMMIT; turning to global vote"; + m_state = GLOBAL_COMMIT_VOTE; + } + } + else if (outcome == CONSUS_VOTE_ABORT) + { + // if single dc or only aborting in originating data center + if (single_dc || m_tg.group == m_tg.txid.group) + { + LOG_IF(INFO, s_debug_mode) << m_tg << " local vote chose ABORT; transitioning to ABORTED state"; + m_state = ABORTED; + record_abort(d); + } + else + { + LOG_IF(INFO, s_debug_mode) << m_tg << " local vote chose ABORT; turning to global vote"; + m_state = GLOBAL_COMMIT_VOTE; + } + } + else + { + LOG(ERROR) << m_tg << " local commit failed; invariants violated"; + return; + } + + return work_state_machine(d); +} + +void +transaction :: work_state_machine_global_commit_vote(daemon* d) +{ + std::string commit_record; + e::packer pa(&commit_record); + + for (size_t i = 0; i < m_ops.size(); ++i) + { + std::string log_entry = generate_log_entry(i); + pa = pa << e::slice(log_entry); + } + + const configuration* c = d->get_config(); + const uint64_t now = po6::monotonic_time(); + + for (unsigned i = 0; i < m_dcs_sz; ++i) + { + if (m_dcs[i] != m_group.id) + { + const paxos_group* g = c->get_group(m_dcs[i]); + + if (!g) + { + // XXX what to do here? + ::abort(); + } + + for (unsigned j = 0; j < g->members_sz; ++j) + { + // XXX coordinator failure sensitive + if (c->get_state(g->members[j]) == txman_state::ONLINE && + m_dcs_timestamps[i] + d->resend_interval() < now) + { + transaction_group tg(g->id, m_tg.txid); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(COMMIT_RECORD) + + pack_size(tg) + + pack_size(e::slice(commit_record)); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << COMMIT_RECORD << tg << e::slice(commit_record); + d->send(g->members[j], msg); + m_dcs_timestamps[i] = now; + break; + } + } + } + } + +} + +#if 0 +void +transaction :: work_state_machine_sharing(daemon* d) +{ + unsigned voted = 0; + unsigned committed = 0; + const unsigned quorum = m_dcs_sz / 2 + 1; + + if (m_dcs_sz == 1)//XXX + { + voted = 1; + committed = 1; + } + + assert(voted <= m_dcs_sz); + assert(committed <= voted); + unsigned aborted = voted - committed; + + if (aborted >= quorum) + { + send_tx_abort(d); + LOG_IF(INFO, s_debug_mode) << m_tg << " aborted; transitioning to TEARDOWN state"; + m_state = TEARDOWN; + return work_state_machine(d); + } + + if (committed >= quorum) + { + send_tx_commit(d); + LOG_IF(INFO, s_debug_mode) << m_tg << " committed; transitioning to TEARDOWN state"; + m_state = TEARDOWN; + return work_state_machine(d); + } +} +#endif + +void +transaction :: work_state_machine_committed(daemon* d) +{ + size_t non_nop = 0; + size_t done = 0; + + for (size_t i = 0; i < m_ops.size(); ++i) + { + if (m_ops[i].type == LOG_ENTRY_NOP) + { + continue; + } + + ++non_nop; + + if (m_ops[i].require_read_lock && !m_ops[i].read_lock_released) + { + release_read_lock(i, d); + continue; + } + + if (m_ops[i].require_write && !m_ops[i].write_finished) + { + finish_write(i, d); + continue; + } + + ++done; + } + + if (done == non_nop) + { + send_tx_commit(d); + LOG_IF(INFO, s_debug_mode) << m_tg << " transitioning to TERMINATED state"; + m_state = TERMINATED; + return work_state_machine(d); + } +} + +void +transaction :: work_state_machine_aborted(daemon* d) +{ + size_t non_nop = 0; + size_t done = 0; + + for (size_t i = 0; i < m_ops.size(); ++i) + { + if (m_ops[i].type == LOG_ENTRY_NOP) + { + continue; + } + + ++non_nop; + + if (m_ops[i].require_read_lock && !m_ops[i].read_lock_released) + { + release_read_lock(i, d); + continue; + } + + if (m_ops[i].require_write && !m_ops[i].write_finished) + { + cancel_write(i, d); + continue; + } + + ++done; + } + + if (done == non_nop) + { + send_tx_abort(d); + LOG_IF(INFO, s_debug_mode) << m_tg << " transitioning to TERMINATED state"; + m_state = TERMINATED; + return work_state_machine(d); + } +} + +void +transaction :: avoid_commit_if_possible(daemon* d) +{ + m_prefer_to_commit = false; + daemon::local_voter_map_t::state_reference lvsr; + local_voter* lv = d->m_local_voters.get_or_create_state(m_tg, &lvsr); + assert(lv); + lv->set_preferred_vote(CONSUS_VOTE_ABORT); +} + +bool +transaction :: is_durable(uint64_t seqno) +{ + if (seqno >= m_ops.size()) + { + return false; + } + + unsigned c = 0; + + for (unsigned i = 0; i < m_group.members_sz; ++i) + { + if (m_ops[seqno].durable[i]) + { + ++c; + } + } + + return c >= m_group.quorum(); +} + +bool +transaction :: resize_to_hold(uint64_t seqno) +{ + assert(m_state == EXECUTING); + + for (size_t i = 0; i < m_ops.size(); ++i) + { + if (m_ops[i].type != LOG_ENTRY_NOP && + m_ops[i].type >= LOG_ENTRY_TX_PREPARE && + i < seqno) + { + return false; + } + } + + if (m_ops.size() <= seqno) + { + m_ops.resize(seqno + 1); + } + + return true; +} + +void +transaction :: acquire_read_lock(uint64_t seqno, daemon* d) +{ + assert(seqno < m_ops.size()); + const uint64_t now = po6::monotonic_time(); + + if (m_ops[seqno].read_timestamp + d->resend_interval() > now) + { + return; + } + + m_ops[seqno].read_timestamp = now; + const e::slice& table(m_ops[seqno].table); + const e::slice& key(m_ops[seqno].key); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_RD_LOCK) + + pack_size(m_tg) + + sizeof(uint64_t) + + pack_size(table) + + pack_size(key); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_RD_LOCK << m_tg << seqno << table << key; + d->send(d->get_config()->choose_kvs(m_group.dc), msg); +} + +void +transaction :: release_read_lock(uint64_t seqno, daemon* d) +{ + assert(seqno < m_ops.size()); + const uint64_t now = po6::monotonic_time(); + + if (m_ops[seqno].read_timestamp + d->resend_interval() > now) + { + return; + } + + m_ops[seqno].read_timestamp = now; + const e::slice& table(m_ops[seqno].table); + const e::slice& key(m_ops[seqno].key); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_RD_UNLOCK) + + pack_size(m_tg) + + sizeof(uint64_t) + + pack_size(table) + + pack_size(key); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_RD_UNLOCK << m_tg << seqno << table << key; + d->send(d->get_config()->choose_kvs(m_group.dc), msg); +} + +void +transaction :: begin_write(uint64_t seqno, daemon* d) +{ + assert(seqno < m_ops.size()); + const uint64_t now = po6::monotonic_time(); + + if (m_ops[seqno].write_timestamp + d->resend_interval() > now) + { + return; + } + + m_ops[seqno].write_timestamp = now; + const e::slice& table(m_ops[seqno].table); + const e::slice& key(m_ops[seqno].key); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_BEGIN) + + pack_size(m_tg) + + sizeof(uint64_t) + + pack_size(table) + + pack_size(key); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_WR_BEGIN << m_tg << seqno << table << key; + d->send(d->get_config()->choose_kvs(m_group.dc), msg); +} + +void +transaction :: finish_write(uint64_t seqno, daemon* d) +{ + assert(seqno < m_ops.size()); + const uint64_t now = po6::monotonic_time(); + + if (m_ops[seqno].write_timestamp + d->resend_interval() > now) + { + return; + } + + m_ops[seqno].write_timestamp = now; + const e::slice& table(m_ops[seqno].table); + const e::slice& key(m_ops[seqno].key); + const e::slice& value(m_ops[seqno].value); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_FINISH) + + pack_size(m_tg) + + sizeof(uint64_t) + + pack_size(table) + + pack_size(key) + + sizeof(uint64_t) + + pack_size(value); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_WR_FINISH << m_tg << seqno << table << key << m_timestamp << value; + d->send(d->get_config()->choose_kvs(m_group.dc), msg); +} + +void +transaction :: cancel_write(uint64_t seqno, daemon* d) +{ + assert(seqno < m_ops.size()); + const uint64_t now = po6::monotonic_time(); + + if (m_ops[seqno].write_timestamp + d->resend_interval() > now) + { + return; + } + + m_ops[seqno].write_timestamp = now; + const e::slice& table(m_ops[seqno].table); + const e::slice& key(m_ops[seqno].key); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(KVS_WR_CANCEL) + + pack_size(m_tg) + + sizeof(uint64_t) + + pack_size(table) + + pack_size(key); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << KVS_WR_CANCEL << m_tg << seqno << table << key; + d->send(d->get_config()->choose_kvs(m_group.dc), msg); +} + +std::string +transaction :: generate_log_entry(uint64_t seqno) +{ + assert(seqno < m_ops.size()); + std::string entry; + e::packer pa(&entry); + std::vector dcs(m_dcs, m_dcs + m_dcs_sz); + operation* op = &m_ops[seqno]; + + switch (m_ops[seqno].type) + { + case LOG_ENTRY_TX_BEGIN: + pa << LOG_ENTRY_TX_BEGIN << m_tg << seqno << m_init_timestamp << dcs; + break; + case LOG_ENTRY_TX_READ: + pa << LOG_ENTRY_TX_READ << m_tg << seqno << op->table << op->key << op->timestamp; + break; + case LOG_ENTRY_TX_WRITE: + pa << LOG_ENTRY_TX_WRITE << m_tg << seqno << op->table << op->key << op->value; + break; + case LOG_ENTRY_TX_PREPARE: + pa << LOG_ENTRY_TX_PREPARE << m_tg << seqno; + break; + case LOG_ENTRY_TX_ABORT: + pa << LOG_ENTRY_TX_ABORT << m_tg << seqno; + break; + case LOG_ENTRY_LOCAL_VOTE_1A: + case LOG_ENTRY_LOCAL_VOTE_2A: + case LOG_ENTRY_LOCAL_LEARN: + case LOG_ENTRY_CONFIG: + case LOG_ENTRY_NOP: + default: + ::abort(); + } + + return entry; +} + +void +transaction :: record_commit(daemon* d) +{ + d->m_dispositions.put(m_tg, CONSUS_VOTE_COMMIT); +} + +void +transaction :: record_abort(daemon* d) +{ + d->m_dispositions.put(m_tg, CONSUS_VOTE_ABORT); +} + +void +transaction :: send_paxos_2a(uint64_t i, daemon* d) +{ + std::string le = generate_log_entry(i); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_PAXOS_2A) + + pack_size(e::slice(le)); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << TXMAN_PAXOS_2A << e::slice(le); + send_to_nondurable(i, msg, m_ops[i].paxos_timestamps, d); +} + +void +transaction :: send_paxos_2b(uint64_t i, daemon* d) +{ + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(TXMAN_PAXOS_2B) + + pack_size(m_tg) + + sizeof(uint64_t); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) << TXMAN_PAXOS_2B << m_tg << i; + send_to_group(msg, m_ops[i].paxos_2b_timestamps, d); +} + +void +transaction :: send_response(operation* op, daemon* d) +{ + assert(op->client != comm_id()); + + switch (op->type) + { + case LOG_ENTRY_TX_BEGIN: + return send_tx_begin(op, d); + case LOG_ENTRY_TX_READ: + return send_tx_read(op, d); + case LOG_ENTRY_TX_WRITE: + return send_tx_write(op, d); + case LOG_ENTRY_TX_PREPARE: + case LOG_ENTRY_TX_ABORT: + // only sent after a vote + return; + case LOG_ENTRY_LOCAL_VOTE_1A: + case LOG_ENTRY_LOCAL_VOTE_2A: + case LOG_ENTRY_LOCAL_LEARN: + case LOG_ENTRY_CONFIG: + case LOG_ENTRY_NOP: + default: + LOG(ERROR) << "sending client response for " << op->type; + return; + } +} + +void +transaction :: send_tx_begin(operation* op, daemon* d) +{ + std::vector ids(m_group.members, m_group.members + m_group.members_sz); + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(CLIENT_RESPONSE) + + sizeof(uint64_t) + + pack_size(m_tg) + + ::pack_size(ids); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << CLIENT_RESPONSE << op->nonce << m_tg.txid << ids; + d->send(op->client, msg); + op->client = comm_id(); +} + +void +transaction :: send_tx_read(operation* op, daemon* d) +{ + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(CLIENT_RESPONSE) + + sizeof(uint64_t) + + pack_size(op->rc) + + sizeof(uint64_t) + + pack_size(op->value); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << CLIENT_RESPONSE + << op->nonce + << op->rc + << op->timestamp + << op->value; + d->send(op->client, msg); + op->client = comm_id(); +} + +void +transaction :: send_tx_write(operation* op, daemon* d) +{ + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(CLIENT_RESPONSE) + + sizeof(uint64_t) + + pack_size(CONSUS_SUCCESS); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << CLIENT_RESPONSE << op->nonce << CONSUS_SUCCESS; + d->send(op->client, msg); + op->client = comm_id(); +} + +void +transaction :: send_tx_commit(daemon* d) +{ + if (m_ops.back().client == comm_id()) + { + return; + } + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(CLIENT_RESPONSE) + + sizeof(uint64_t) + + pack_size(CONSUS_SUCCESS); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << CLIENT_RESPONSE << m_ops.back().nonce << CONSUS_SUCCESS; + d->send(m_ops.back().client, msg); +} + +void +transaction :: send_tx_abort(daemon* d) +{ + if (m_ops.back().client == comm_id()) + { + return; + } + + const size_t sz = BUSYBEE_HEADER_SIZE + + pack_size(CLIENT_RESPONSE) + + sizeof(uint64_t) + + pack_size(CONSUS_ABORTED); + std::auto_ptr msg(e::buffer::create(sz)); + msg->pack_at(BUSYBEE_HEADER_SIZE) + << CLIENT_RESPONSE << m_ops.back().nonce << CONSUS_ABORTED; + d->send(m_ops.back().client, msg); +} + +void +transaction :: send_to_group(std::auto_ptr msg, uint64_t timestamps[CONSUS_MAX_REPLICATION_FACTOR], daemon* d) +{ + const uint64_t now = po6::monotonic_time(); + + for (unsigned i = 0; i < m_group.members_sz; ++i) + { + if (m_group.members[i] == d->m_us.id || + timestamps[i] + d->resend_interval() > now) + { + continue; + } + + std::auto_ptr m(msg->copy()); + d->send(m_group.members[i], m); + timestamps[i] = now; + } +} + +void +transaction :: send_to_nondurable(uint64_t seqno, std::auto_ptr msg, uint64_t timestamps[CONSUS_MAX_REPLICATION_FACTOR], daemon* d) +{ + if (seqno >= m_ops.size()) + { + return; + } + + const uint64_t now = po6::monotonic_time(); + + for (unsigned i = 0; i < m_group.members_sz; ++i) + { + if (m_group.members[i] == d->m_us.id || + m_ops[seqno].durable[i] || + timestamps[i] + d->resend_interval() > now) + { + continue; + } + + std::auto_ptr m(msg->copy()); + d->send(m_group.members[i], m); + timestamps[i] = now; + } +} diff --git a/txman/transaction.h b/txman/transaction.h new file mode 100644 index 0000000..e7a5838 --- /dev/null +++ b/txman/transaction.h @@ -0,0 +1,187 @@ +// Copyright (c) 2015, Robert Escriva +// All rights reserved. + +#ifndef consus_txman_transaction_h_ +#define consus_txman_transaction_h_ + +// consus +#include +#include "namespace.h" +#include "common/consus.h" +#include "common/ids.h" +#include "common/transaction_id.h" +#include "common/transaction_group.h" +#include "txman/log_entry_t.h" +#include "txman/paxos_synod.h" + +BEGIN_CONSUS_NAMESPACE +class daemon; + +class transaction +{ + public: + enum state_t + { + INITIALIZED, + EXECUTING, + LOCAL_COMMIT_VOTE, + GLOBAL_COMMIT_VOTE, + COMMITTED, + ABORTED, + TERMINATED, + COLLECTED + }; + + public: + transaction(const transaction_group& tg); + ~transaction() throw (); + + public: + const transaction_group& state_key() const; + bool finished(); + + // commands coming from the client + public: + void begin(comm_id id, uint64_t nonce, uint64_t timestamp, + const paxos_group& group, + const std::vector& dcs, + daemon* d); + void read(comm_id id, uint64_t nonce, uint64_t seqno, + const e::slice& table, + const e::slice& key, + std::auto_ptr backing, + daemon* d); + void write(comm_id id, uint64_t nonce, uint64_t seqno, + const e::slice& table, + const e::slice& key, + const e::slice& value, + std::auto_ptr backing, + daemon* d); + void commit(comm_id id, uint64_t nonce, uint64_t seqno, daemon* d); + void abort(comm_id id, uint64_t nonce, uint64_t seqno, daemon* d); + + public: + void paxos_2a(uint64_t seqno, log_entry_t t, e::unpacker up, + std::auto_ptr backing, daemon* d); + void paxos_2b(comm_id id, uint64_t seqno, daemon* d); + void commit_record(e::slice commit_record, + std::auto_ptr _backing, + daemon* d); + void callback(uint64_t seqno, daemon* d); + void externally_work_state_machine(daemon* d); + void kvs_rd_locked(uint64_t seqno, + consus_returncode rc, + uint64_t timestamp, + const e::slice& value, + std::auto_ptr backing, + daemon* d); + void kvs_rd_unlocked(uint64_t seqno, daemon* d); + void kvs_wr_begun(uint64_t seqno, daemon* d); + void kvs_wr_finished(uint64_t seqno, daemon* d); + + private: + struct operation; + struct comparison; + + private: + void ensure_initialized(); + void paxos_2a_begin(uint64_t seqno, e::unpacker up, + std::auto_ptr backing, daemon* d); + void paxos_2a_read(uint64_t seqno, e::unpacker up, + std::auto_ptr backing, daemon* d); + void paxos_2a_write(uint64_t seqno, e::unpacker up, + std::auto_ptr backing, daemon* d); + void paxos_2a_prepare(uint64_t seqno, e::unpacker up, + std::auto_ptr backing, daemon* d); + void paxos_2a_abort(uint64_t seqno, e::unpacker up, + std::auto_ptr backing, daemon* d); + void commit_record_begin(uint64_t seqno, e::unpacker up, + e::compat::shared_ptr backing, daemon* d); + void commit_record_read(uint64_t seqno, e::unpacker up, + e::compat::shared_ptr backing); + void commit_record_write(uint64_t seqno, e::unpacker up, + e::compat::shared_ptr backing); + void commit_record_prepare(uint64_t seqno, e::unpacker up, + e::compat::shared_ptr backing, daemon* d); + void internal_begin(const char* source, uint64_t timestamp, + const paxos_group& group, + const std::vector& dcs, + daemon* d); + void internal_read(const char* source, uint64_t seqno, + const e::slice& table, + const e::slice& key, + e::compat::shared_ptr backing, + daemon* d); + void internal_write(const char* source, uint64_t seqno, + const e::slice& table, + const e::slice& key, + const e::slice& value, + e::compat::shared_ptr backing, + daemon* d); + void internal_end_of_transaction(const char* source, + const char* op, + log_entry_t let, + uint64_t seqno, + daemon* d); + void internal_paxos_2b(comm_id id, uint64_t seqno, daemon* d); + + void work_state_machine(daemon* d); + void work_state_machine_executing(daemon* d); + void work_state_machine_local_commit_vote(daemon* d); + void work_state_machine_global_commit_vote(daemon* d); + void work_state_machine_committed(daemon* d); + void work_state_machine_aborted(daemon* d); + + // execution utils + void avoid_commit_if_possible(daemon* d); + bool is_durable(uint64_t seqno); + bool resize_to_hold(uint64_t seqno); + + // key value store utils + void acquire_read_lock(uint64_t seqno, daemon* d); + void release_read_lock(uint64_t seqno, daemon* d); + void begin_write(uint64_t seqno, daemon* d); + void finish_write(uint64_t seqno, daemon* d); + void cancel_write(uint64_t seqno, daemon* d); + + // inter-data center + std::string generate_log_entry(uint64_t seqno); + + // commit + void record_commit(daemon* d); + void record_abort(daemon* d); + + // message sending + void send_paxos_2a(uint64_t i, daemon* d); + void send_paxos_2b(uint64_t i, daemon* d); + void send_response(operation* op, daemon* d); + void send_tx_begin(operation* op, daemon* d); + void send_tx_read(operation* op, daemon* d); + void send_tx_write(operation* op, daemon* d); + void send_tx_commit(daemon* d); + void send_tx_abort(daemon* d); + void send_to_group(std::auto_ptr msg, uint64_t timestamps[CONSUS_MAX_REPLICATION_FACTOR], daemon* d); + void send_to_nondurable(uint64_t seqno, std::auto_ptr msg, uint64_t timestamps[CONSUS_MAX_REPLICATION_FACTOR], daemon* d); + + private: + const transaction_group m_tg; + po6::threads::mutex m_mtx; + uint64_t m_init_timestamp; + paxos_group m_group; + paxos_group_id m_dcs[CONSUS_MAX_REPLICATION_FACTOR]; + uint64_t m_dcs_timestamps[CONSUS_MAX_REPLICATION_FACTOR]; + size_t m_dcs_sz; + state_t m_state; + uint64_t m_timestamp; + bool m_prefer_to_commit; + std::vector m_ops; + std::vector > m_deferred_2b; + + private: + transaction(const transaction&); + transaction& operator = (const transaction&); +}; + +END_CONSUS_NAMESPACE + +#endif // consus_txman_transaction_h_ diff --git a/visibility.h b/visibility.h new file mode 100644 index 0000000..ecf2afe --- /dev/null +++ b/visibility.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2013, Cornell University + * All rights reserved. + */ + +#ifndef consus_visibility_h_ +#define consus_visibility_h_ + +#define CONSUS_API __attribute__ ((visibility ("default"))) + +#endif /* consus_visibility_h_ */