Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fix the replication stress test and make it pass

  • Loading branch information...
commit 03169f7e6da0c54257378303b24a3e1d084cf417 1 parent 19c26f2
@rescrv authored
View
22 Makefile.am
@@ -40,7 +40,7 @@ EXTRA_DIST = \
hyperclient/nodejs/async_test.js \
hyperclient/nodejs/hyperclient.cc \
wscript
-#doc/HyperDex-$(VERSION).pdf
+# XXX doc/HyperDex-$(VERSION).pdf
lib_LTLIBRARIES = \
libhyperclient.la \
@@ -54,8 +54,8 @@ include_HEADERS = \
bin_PROGRAMS = hyperdex \
hyperdex-daemon \
hyperdex-search-stress-test \
+ hyperdex-replication-stress-test \
hyperdex-simple-consistency-stress-test
-# XXX hyperdex-replication-stress-test
hyperdexexecdir = $(exec_prefix)/libexec/$(PACKAGE)-$(VERSION)
hyperdexexec_PROGRAMS = \
@@ -208,6 +208,10 @@ hyperdex_daemon_LDADD = \
-lreplicant -lcityhash -lpopt -lglog -lrt -lpthread
hyperdex_daemon_CPPFLAGS = $(CPPFLAGS)
+daemon_test_index_encode_SOURCES = runner.cc daemon/test/index_encode.cc daemon/index_encode.cc common/float_encode.cc
+daemon_test_index_encode_CPPFLAGS = $(GTEST_CPPFLAGS) $(CPPFLAGS)
+daemon_test_index_encode_LDADD = $(GTEST_LDFLAGS) -lgtest -lpthread
+
################################################################################
################################## Coordinator #################################
################################################################################
@@ -420,21 +424,11 @@ endif
################################### Binaries ###################################
################################################################################
-#hyperdex_replication_stress_test_SOURCES = \
-# replication-stress-test.cc
-#hyperdex_replication_stress_test_CPPFLAGS = \
-# $(E_CFLAGS) \
-# $(CPPFLAGS)
-#hyperdex_replication_stress_test_LDADD = \
-# libhyperclient.la \
-# -lpopt
+hyperdex_replication_stress_test_SOURCES = replication-stress-test.cc
+hyperdex_replication_stress_test_LDADD = libhyperclient.la -lpopt
hyperdex_search_stress_test_SOURCES = search-stress-test.cc test/common.cc
hyperdex_search_stress_test_LDADD = libhyperclient.la -lpopt
hyperdex_simple_consistency_stress_test_SOURCES = simple-consistency-stress-test.cc
hyperdex_simple_consistency_stress_test_LDADD = libhyperclient.la -lpopt -lpthread
-
-daemon_test_index_encode_SOURCES = runner.cc daemon/test/index_encode.cc daemon/index_encode.cc common/float_encode.cc
-daemon_test_index_encode_CPPFLAGS = $(GTEST_CPPFLAGS) $(CPPFLAGS)
-daemon_test_index_encode_LDADD = $(GTEST_LDFLAGS) -lgtest -lpthread
View
6 common/configuration.cc
@@ -547,7 +547,7 @@ configuration :: point_leader(const char* sname, const e::slice& key)
for (size_t pl = 0; pl < m_spaces[s].subspaces[0].regions.size(); ++pl)
{
if (m_spaces[s].subspaces[0].regions[pl].lower_coord[0] <= h &&
- h < m_spaces[s].subspaces[0].regions[pl].upper_coord[0])
+ h <= m_spaces[s].subspaces[0].regions[pl].upper_coord[0])
{
assert(!m_spaces[s].subspaces[0].regions[pl].replicas.empty());
return m_spaces[s].subspaces[0].regions[pl].replicas[0].vsi;
@@ -580,7 +580,7 @@ configuration :: point_leader(const region_id& rid, const e::slice& key)
for (size_t pl = 0; pl < m_spaces[s].subspaces[0].regions.size(); ++pl)
{
if (m_spaces[s].subspaces[0].regions[pl].lower_coord[0] <= h &&
- h < m_spaces[s].subspaces[0].regions[pl].upper_coord[0])
+ h <= m_spaces[s].subspaces[0].regions[pl].upper_coord[0])
{
assert(!m_spaces[s].subspaces[0].regions[pl].replicas.empty());
return m_spaces[s].subspaces[0].regions[pl].replicas[0].vsi;
@@ -631,7 +631,7 @@ configuration :: lookup_region(const subspace_id& ssid,
{
assert(a < m_spaces[s].schema.attrs_sz);
matches &= m_spaces[s].subspaces[ss].regions[r].lower_coord[a] <= hashes[m_spaces[s].subspaces[ss].attrs[a]] &&
- hashes[m_spaces[s].subspaces[ss].attrs[a]] < m_spaces[s].subspaces[ss].regions[r].upper_coord[a];
+ hashes[m_spaces[s].subspaces[ss].attrs[a]] <= m_spaces[s].subspaces[ss].regions[r].upper_coord[a];
}
if (matches)
View
6 daemon/datalayer.cc
@@ -415,6 +415,7 @@ datalayer :: get(const region_id& ri,
datalayer::returncode
datalayer :: put(const region_id& ri,
+ const region_id& reg_id,
uint64_t seq_id,
const e::slice& key,
const std::vector<e::slice>& value,
@@ -445,7 +446,7 @@ datalayer :: put(const region_id& ri,
{
char abacking[sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint64_t)];
abacking[0] = 'a';
- e::pack64be(ri.get(), abacking + sizeof(uint8_t));
+ e::pack64be(reg_id.get(), abacking + sizeof(uint8_t));
e::pack64be(seq_id, abacking + sizeof(uint8_t) + sizeof(uint64_t));
leveldb::Slice akey(abacking, sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint64_t));
leveldb::Slice aval("", 0);
@@ -502,6 +503,7 @@ datalayer :: put(const region_id& ri,
datalayer::returncode
datalayer :: del(const region_id& ri,
+ const region_id& reg_id,
uint64_t seq_id,
const e::slice& key)
{
@@ -527,7 +529,7 @@ datalayer :: del(const region_id& ri,
{
char abacking[sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint64_t)];
abacking[0] = 'a';
- e::pack64be(ri.get(), abacking + sizeof(uint8_t));
+ e::pack64be(reg_id.get(), abacking + sizeof(uint8_t));
e::pack64be(seq_id, abacking + sizeof(uint8_t) + sizeof(uint64_t));
leveldb::Slice akey(abacking, sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint64_t));
leveldb::Slice aval("", 0);
View
2  daemon/datalayer.h
@@ -102,11 +102,13 @@ class datalayer
uint64_t* version,
reference* ref);
returncode put(const region_id& ri,
+ const region_id& reg_id,
uint64_t seq_id,
const e::slice& key,
const std::vector<e::slice>& value,
uint64_t version);
returncode del(const region_id& ri,
+ const region_id& reg_id,
uint64_t seq_id,
const e::slice& key);
returncode make_snapshot(const region_id& ri,
View
72 daemon/replication_manager.cc
@@ -188,7 +188,7 @@ replication_manager :: client_atomic(const server_id& from,
e::intrusive_ptr<keyholder> kh = get_or_create_keyholder(ri, key);
bool has_old_value = false;
uint64_t old_version = 0;
- std::vector<e::slice>* old_value = NULL; // don't use pointer?
+ std::vector<e::slice>* old_value = NULL;
kh->get_latest_version(&has_old_value, &old_version, &old_value);
if (erase && !funcs->empty())
@@ -292,13 +292,6 @@ replication_manager :: chain_op(const virtual_server_id& from,
HOLD_LOCK_FOR_KEY(ri, key);
e::intrusive_ptr<keyholder> kh = get_or_create_keyholder(ri, key);
- if (reg_id != ri)
- {
- LOG(ERROR) << "dropping CHAIN_OP send to the wrong region";
- CLEANUP_KEYHOLDER(ri, key, kh);
- return;
- }
-
// Check that a chain's put matches the dimensions of the space.
if (has_value && sc->attrs_sz != value.size() + 1)
{
@@ -361,13 +354,6 @@ replication_manager :: chain_subspace(const virtual_server_id& from,
HOLD_LOCK_FOR_KEY(ri, key);
e::intrusive_ptr<keyholder> kh = get_or_create_keyholder(ri, key);
- if (reg_id != ri)
- {
- LOG(ERROR) << "dropping CHAIN_OP send to the wrong region";
- CLEANUP_KEYHOLDER(ri, key, kh);
- return;
- }
-
// Check that a chain's put matches the dimensions of the space.
if (sc->attrs_sz != value.size() + 1 || sc->attrs_sz != hashes.size())
{
@@ -404,9 +390,9 @@ replication_manager :: chain_subspace(const virtual_server_id& from,
}
if (!(new_pend->this_old_region == m_daemon->m_config.get_region_id(from) &&
- m_daemon->m_config.tail_of_region(ri) == from) &&
+ m_daemon->m_config.tail_of_region(new_pend->this_old_region) == from) &&
!(new_pend->this_new_region == m_daemon->m_config.get_region_id(from) &&
- m_daemon->m_config.next_in_region(from) != to))
+ m_daemon->m_config.next_in_region(from) == to))
{
LOG(INFO) << "dropping CHAIN_SUBSPACE which didn't obey chaining rules";
CLEANUP_KEYHOLDER(ri, key, kh);
@@ -444,13 +430,6 @@ replication_manager :: chain_ack(const virtual_server_id& from,
return;
}
- if (reg_id != ri)
- {
- LOG(ERROR) << "dropping CHAIN_ACK send to the wrong region";
- CLEANUP_KEYHOLDER(ri, key, kh);
- return;
- }
-
e::intrusive_ptr<pending> pend = kh->get_by_version(version);
if (!pend)
@@ -492,7 +471,6 @@ replication_manager :: chain_ack(const virtual_server_id& from,
if (kh->version_on_disk() < version)
{
- assert(reg_id == ri);
e::intrusive_ptr<pending> op = kh->get_by_version(version);
assert(op);
@@ -500,11 +478,11 @@ replication_manager :: chain_ack(const virtual_server_id& from,
if (!op->has_value || (op->this_old_region != op->this_new_region && ri == op->this_old_region))
{
- rc = m_daemon->m_data.del(ri, seq_id, key);
+ rc = m_daemon->m_data.del(ri, reg_id, seq_id, key);
}
else
{
- rc = m_daemon->m_data.put(ri, seq_id, key, op->value, version);
+ rc = m_daemon->m_data.put(ri, reg_id, seq_id, key, op->value, version);
}
switch (rc)
@@ -726,31 +704,41 @@ replication_manager :: move_operations_between_queues(const virtual_server_id& u
uint64_t old_version = 0;
std::vector<e::slice>* old_value = NULL;
kh->get_latest_version(&has_old_value, &old_version, &old_value);
+ if (old_version >= kh->oldest_deferred_version()) LOG(INFO) << "VERSIONS " << old_version << " " << kh->oldest_deferred_version();
assert(old_version < kh->oldest_deferred_version());
e::intrusive_ptr<pending> new_pend = kh->oldest_deferred_op();
+ // If the version numbers don't line up, and this is not fresh, and it
+ // is not a subspace transfer
if (old_version + 1 != kh->oldest_deferred_version() &&
- !new_pend->fresh)
+ !new_pend->fresh &&
+ (new_pend->this_old_region == new_pend->this_new_region ||
+ new_pend->this_old_region == ri))
{
break;
}
- hash_objects(ri, sc, key, new_pend->has_value, new_pend->value, has_old_value, old_value ? *old_value : new_pend->value, new_pend);
-
- if (new_pend->this_old_region != ri && new_pend->this_new_region != ri)
+ // If this is not a subspace transfer
+ if (new_pend->this_old_region == new_pend->this_new_region ||
+ new_pend->this_old_region == ri)
{
- LOG(INFO) << "dropping deferred CHAIN_* which didn't get sent to the right host";
- kh->pop_oldest_deferred();
- continue;
- }
+ hash_objects(ri, sc, key, new_pend->has_value, new_pend->value, has_old_value, old_value ? *old_value : new_pend->value, new_pend);
- if (new_pend->recv != virtual_server_id() &&
- m_daemon->m_config.next_in_region(new_pend->recv) != us &&
- !m_daemon->m_config.subspace_adjacent(new_pend->recv, us))
- {
- LOG(INFO) << "dropping deferred CHAIN_* which didn't come from the right host";
- kh->pop_oldest_deferred();
- continue;
+ if (new_pend->this_old_region != ri && new_pend->this_new_region != ri)
+ {
+ LOG(INFO) << "dropping deferred CHAIN_* which didn't get sent to the right host";
+ kh->pop_oldest_deferred();
+ continue;
+ }
+
+ if (new_pend->recv != virtual_server_id() &&
+ m_daemon->m_config.next_in_region(new_pend->recv) != us &&
+ !m_daemon->m_config.subspace_adjacent(new_pend->recv, us))
+ {
+ LOG(INFO) << "dropping deferred CHAIN_* which didn't come from the right host";
+ kh->pop_oldest_deferred();
+ continue;
+ }
}
kh->shift_one_deferred_to_blocked();
View
4 daemon/replication_manager_keyholder.cc
@@ -63,13 +63,13 @@ replication_manager :: keyholder :: get_latest_version(bool* has_old_value,
if (has_blocked_ops())
{
- *has_old_value = true;
+ *has_old_value = most_recent_blocked_op()->has_value;
*old_version = most_recent_blocked_version();
*old_value = &most_recent_blocked_op()->value;
}
else if (has_committable_ops())
{
- *has_old_value = true;
+ *has_old_value = most_recent_committable_op()->has_value;
*old_version = most_recent_committable_version();
*old_value = &most_recent_committable_op()->value;
}
View
8 daemon/state_transfer_manager.cc
@@ -246,7 +246,7 @@ state_transfer_manager :: xfer_op(const virtual_server_id& from,
{
while (tis->del_iter.valid() && tis->del_iter.key() < op->key)
{
- m_daemon->m_data.del(tis->xfer.rid, 0, tis->del_iter.key());
+ m_daemon->m_data.del(tis->xfer.rid, region_id(), 0, tis->del_iter.key());
tis->del_iter.next();
}
}
@@ -259,7 +259,7 @@ state_transfer_manager :: xfer_op(const virtual_server_id& from,
while (tis->del_iter.valid())
{
- datalayer::returncode rc = m_daemon->m_data.del(tis->xfer.rid, 0, tis->del_iter.key());
+ datalayer::returncode rc = m_daemon->m_data.del(tis->xfer.rid, region_id(), 0, tis->del_iter.key());
tis->del_iter.next();
switch (rc)
@@ -282,7 +282,7 @@ state_transfer_manager :: xfer_op(const virtual_server_id& from,
if (op->has_value)
{
- datalayer::returncode rc = m_daemon->m_data.put(tis->xfer.rid, 0, op->key, op->value, op->version);
+ datalayer::returncode rc = m_daemon->m_data.put(tis->xfer.rid, region_id(), 0, op->key, op->value, op->version);
switch (rc)
{
@@ -302,7 +302,7 @@ state_transfer_manager :: xfer_op(const virtual_server_id& from,
}
else
{
- datalayer::returncode rc = m_daemon->m_data.del(tis->xfer.rid, 0, op->key);
+ datalayer::returncode rc = m_daemon->m_data.del(tis->xfer.rid, region_id(), 0, op->key);
switch (rc)
{
View
596 replication-stress-test.cc
@@ -25,193 +25,105 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-// The purpose of this test is to expose errors in the replication logic.
-//
-// The test is designed to be run on a space with dimensions A, B, C.
-// Dimension A is the key while dimensions B and C are meant to be separate
-// subspaces. The code intentionally creates certain scenarios which will
-// fail with high probability if the code is not correct.
-//
-// The prefix provided as a commandline argument is used to efficiently prune
-// out duplicate results. It should match P in "auto P R" used for each
-// subspace for basic tests. It is important to test this both on a single
-// daemon instance, and on several daemon instances. The former stresses code
-// with lightening quick responses while the latter introduces delay and
-// non-determinism.
+#define __STDC_LIMIT_MACROS
// C
-#include <cstdio>
-
-// Popt
-#include <popt.h>
+#include <cstdlib>
// STL
-#include <iomanip>
#include <map>
-#include <memory>
-#include <string>
-#include <tr1/functional>
+#include <tr1/memory>
// po6
-#include <po6/net/ipaddr.h>
-#include <po6/net/location.h>
+#include <po6/error.h>
// e
-#include <e/bitfield.h>
-#include <e/convert.h>
-
-// HyperspaceHashing
-#include "hyperspacehashing/hyperspacehashing/hashes.h"
-#include "hyperspacehashing/hyperspacehashing/prefix.h"
+#include <e/endian.h>
+#include <e/guard.h>
// HyperDex
-#include "hyperclient/hyperclient.h"
+#include "client/hyperclient.h"
+#include "tools/common.h"
-// These 32-bit values all hash (using hyperspacehashing) to be have the same
-// high-order byte as their index. E.g. index 0 has a hash of
-// 0x00??????????????, while index 255 has a hash of 0xff??????????????.
-static uint32_t nums[256];
+static bool _continuous = false;
+static long _partitions = 4;
+const char* _space = "replication";
-// These are all the incomplete operations
-static std::map<int64_t, hyperclient_returncode*> incompleteops;
-
-static const char* space = "replication";
-static const char* host = "127.0.0.1";
-static po6::net::ipaddr coord(host);
-static long port = 1234;
-static long prefix = 3;
-
-static void
-find_hashes();
-
-static void
-test0(hyperclient* cl);
-static void
-test1(hyperclient* cl);
-static void
-test2(hyperclient* cl);
-static void
-test3(hyperclient* cl);
-static void
-test4(hyperclient* cl);
+static int _testno = 0;
+static hyperclient* _cl = NULL;
+static std::map<int64_t, std::tr1::shared_ptr<hyperclient_returncode> > _incompleteops;
-class generator
+extern "C"
{
- public:
- static uint32_t end()
- {
- bool looped = false;
- uint32_t ret = 0;
-
- for (generator i; i.has_next(); i.next())
- {
- looped = true;
- ret = i;
- }
-
- assert(looped);
- return ret;
- }
-
- public:
- generator() : m_i(0) {}
-
- public:
- bool has_next() const { return m_i < 256; }
-
- public:
- void next()
- {
- uint32_t current = (m_i >> (8 - prefix));
-
- while (m_i < 256 && current == (m_i >> (8 - prefix)))
- {
- ++ m_i;
- }
- }
- public:
- operator uint32_t () const { return nums[m_i]; }
-
- private:
- uint32_t m_i;
+static struct poptOption stress_popts[] = {
+ {"continuous", 'c', POPT_ARG_NONE, NULL, 'c',
+ "run the test continuously (default: no)", 0},
+ {"partitions", 'n', POPT_ARG_LONG, &_partitions, 'n',
+ "assume each subspace is partitioned into N pieces (default: 4)", "N"},
+ {"space", 's', POPT_ARG_STRING, &_space, 's',
+ "perform all operations on the specified space (default: \"replication\")", "space"},
+ POPT_TABLEEND
};
-extern "C"
-{
+} // extern "C"
+
+#define STRESS_TESTER {NULL, 0, POPT_ARG_INCLUDE_TABLE, stress_popts, 0, "Alter the replication stress test:", NULL},
static struct poptOption popts[] = {
POPT_AUTOHELP
- {"prefix", 'P', POPT_ARG_LONG, &prefix, 'P',
- "the size of the prefix used when creating the space",
- "number"},
- {"space", 's', POPT_ARG_STRING, &space, 's',
- "the HyperDex space to use",
- "space"},
- {"host", 'h', POPT_ARG_STRING, &host, 'h',
- "the IP address of the coordinator",
- "IP"},
- {"port", 'p', POPT_ARG_LONG, &port, 'p',
- "the port number of the coordinator",
- "port"},
+ CONNECT_TABLE
+ STRESS_TESTER
POPT_TABLEEND
};
-} // extern "C"
+static void
+test0();
+static void
+test1();
+static void
+test2();
+static void
+test3();
+static void
+test4();
int
main(int argc, const char* argv[])
{
poptContext poptcon;
poptcon = poptGetContext(NULL, argc, argv, popts, POPT_CONTEXT_POSIXMEHARDER);
- e::guard g = e::makeguard(poptFreeContext, poptcon);
- g.use_variable();
+ e::guard g = e::makeguard(poptFreeContext, poptcon); g.use_variable();
+ poptSetOtherOptionHelp(poptcon, "[OPTIONS]");
int rc;
while ((rc = poptGetNextOpt(poptcon)) != -1)
{
switch (rc)
{
- case 'P':
- if (prefix < 0)
- {
- std::cerr << "prefix must be >= 0" << std::endl;
- return EXIT_FAILURE;
- }
-
- if (prefix > 64)
- {
- std::cerr << "prefix must be <= 64" << std::endl;
- return EXIT_FAILURE;
- }
-
- break;
- case 's':
+ case 'c':
+ _continuous = true;
break;
case 'h':
- try
- {
- coord = po6::net::ipaddr(host);
- }
- catch (po6::error& e)
+ if (!check_host())
{
- std::cerr << "cannot parse coordinator address" << std::endl;
return EXIT_FAILURE;
}
- catch (std::invalid_argument& e)
+ break;
+ case 'n':
+ if (_partitions <= 0)
{
- std::cerr << "cannot parse coordinator address" << std::endl;
+ std::cerr << "cannot specify a non-positive number of partitions" << std::endl;
return EXIT_FAILURE;
}
-
+ break;
+ case 's':
break;
case 'p':
- if (port < 0 || port >= (1 << 16))
+ if (!check_port())
{
- std::cerr << "port number out of range for TCP" << std::endl;
return EXIT_FAILURE;
}
-
break;
case POPT_ERROR_NOARG:
case POPT_ERROR_BADOPT:
@@ -228,297 +140,265 @@ main(int argc, const char* argv[])
}
}
- find_hashes();
+ const char** args = poptGetArgs(poptcon);
+ size_t num_args = 0;
- try
- {
- hyperclient cl(host, port);
-
- test0(&cl);
- test1(&cl);
- test2(&cl);
- test3(&cl);
- test4(&cl);
- }
- catch (po6::error& e)
- {
- std::cerr << "There was a system error: " << e.what();
- return EXIT_FAILURE;
- }
- catch (std::runtime_error& e)
+ while (args && args[num_args])
{
- std::cerr << "There was a runtime error: " << e.what();
- return EXIT_FAILURE;
- }
- catch (std::bad_alloc& e)
- {
- std::cerr << "There was a memory allocation error: " << e.what();
- return EXIT_FAILURE;
- }
- catch (std::exception& e)
- {
- std::cerr << "There was a generic error: " << e.what();
- return EXIT_FAILURE;
+ ++num_args;
}
- return EXIT_SUCCESS;
-}
-
-static void
-find_hashes()
-{
- bool found[256];
- size_t complete = 0;
-
- for (size_t i = 0; i < 256; ++i)
+ if (num_args != 0)
{
- found[i] = false;
+ std::cerr << "command takes no arguments\n" << std::endl;
+ poptPrintUsage(poptcon, stderr, 0);
+ return EXIT_FAILURE;
}
- std::vector<hyperspacehashing::hash_t> hashes(1, hyperspacehashing::EQUALITY);
- hyperspacehashing::prefix::hasher hasher(hashes);
-
- for (uint32_t value = 0; complete < 256; ++value)
+ try
{
- e::slice key(&value, sizeof(uint32_t));
- uint64_t hash = hasher.hash(key).point;
- unsigned high_byte = (hash >> 56) & 0xff;
+ hyperclient cl(_connect_host, _connect_port);
+ _cl = &cl;
- if (!found[high_byte])
+ do
{
- found[high_byte] = true;
- nums[high_byte] = value;
- ++complete;
+ test0();
+ test1();
+ test2();
+ test3();
+ test4();
}
- }
-
- const char* terminator = "";
- const char* sep;
- std::cout << "Hashes turn out to be:\n";
+ while (_continuous);
- for (size_t i = 0; i < 256; ++i)
+ return EXIT_SUCCESS;
+ }
+ catch (po6::error& e)
{
- if (i % 16 == 0)
- {
- std::cout << terminator << std::setw(4) << i;
- sep = " [";
- terminator = "]\n";
- }
-
- std::cout << sep << std::setw(4) << nums[i];
- sep = ", ";
+ std::cerr << "system error: " << e.what() << std::endl;
+ return EXIT_FAILURE;
}
-
- std::cout << "]\n" << std::endl;
}
static void
-success(int testno)
+success()
{
- std::cout << "Test " << testno << ": [\x1b[32mOK\x1b[0m]\n";
+ std::cout << "Test " << _testno << ": [\x1b[32mOK\x1b[0m]\n";
}
-#define FAIL(TESTNO, REASON) \
+#define FAIL(REASON) \
do { \
- std::cout << "Test " << TESTNO << ": [\x1b[31mFAIL\x1b[0m]\n" \
+ std::cout << "Test " << _testno << ": [\x1b[31mFAIL\x1b[0m]\n" \
<< "location: " << __FILE__ << ":" << __LINE__ << "\n" \
<< "reason: " << REASON << "\n"; \
abort(); \
} while (0)
+static uint64_t
+generate_attr(size_t idx)
+{
+ uint64_t partition_size = UINT64_MAX / _partitions;
+ return partition_size * idx + partition_size / 2;
+}
+
static void
-put(int testno,
- hyperclient* cl,
- uint32_t A, uint32_t B, uint32_t C)
+put(uint64_t A, uint64_t B, uint64_t C)
{
+ char buf[3 * sizeof(uint64_t)];
+ e::pack64le(A, buf + 0 * sizeof(uint64_t));
+ e::pack64le(B, buf + 1 * sizeof(uint64_t));
+ e::pack64le(C, buf + 2 * sizeof(uint64_t));
hyperclient_attribute attrs[2];
attrs[0].attr = "B";
- attrs[0].value = reinterpret_cast<const char*>(&B);
- attrs[0].value_sz = sizeof(uint32_t);
- attrs[0].datatype = HYPERDATATYPE_STRING;
+ attrs[0].value = buf + 1 * sizeof(uint64_t);
+ attrs[0].value_sz = sizeof(uint64_t);
+ attrs[0].datatype = HYPERDATATYPE_INT64;
attrs[1].attr = "C";
- attrs[1].value = reinterpret_cast<const char*>(&C);
- attrs[1].value_sz = sizeof(uint32_t);
- attrs[1].datatype = HYPERDATATYPE_STRING;
- hyperclient_returncode* status = new hyperclient_returncode();
- int64_t id = cl->put(space, reinterpret_cast<const char*>(&A), sizeof(uint32_t), attrs, 2, status);
+ attrs[1].value = buf + 2 * sizeof(uint64_t);
+ attrs[1].value_sz = sizeof(uint64_t);
+ attrs[1].datatype = HYPERDATATYPE_INT64;
+ std::tr1::shared_ptr<hyperclient_returncode> status(new hyperclient_returncode());
+ int64_t id = _cl->put(_space, buf, sizeof(uint64_t), attrs, 2, status.get());
if (id < 0)
{
- FAIL(testno, "put encountered error " << status);
+ FAIL("put encountered error " << status);
}
- std::pair<std::map<int64_t, hyperclient_returncode*>::iterator, bool> res;
- res = incompleteops.insert(std::make_pair(id, status));
+ std::pair<std::map<int64_t, std::tr1::shared_ptr<hyperclient_returncode> >::iterator, bool> res;
+ res = _incompleteops.insert(std::make_pair(id, status));
if (res.second != true)
{
- FAIL(testno, "put could not insert into incompleteops");
+ FAIL("put could not insert into incompleteops");
}
}
static void
-del(int testno,
- hyperclient* cl,
- uint32_t A)
+del(uint64_t A)
{
- hyperclient_returncode* status = new hyperclient_returncode();
- int64_t id = cl->del(space, reinterpret_cast<const char*>(&A), sizeof(uint32_t), status);
+ char buf[sizeof(uint64_t)];
+ e::pack64le(A, buf);
+ std::tr1::shared_ptr<hyperclient_returncode> status(new hyperclient_returncode());
+ int64_t id = _cl->del(_space, buf, sizeof(uint64_t), status.get());
if (id < 0)
{
- FAIL(testno, "del encountered error " << status);
+ FAIL("del encountered error " << status);
}
- std::pair<std::map<int64_t, hyperclient_returncode*>::iterator, bool> res;
- res = incompleteops.insert(std::make_pair(id, status));
+ std::pair<std::map<int64_t, std::tr1::shared_ptr<hyperclient_returncode> >::iterator, bool> res;
+ res = _incompleteops.insert(std::make_pair(id, status));
if (res.second != true)
{
- FAIL(testno, "del could not insert into incompleteops");
+ FAIL("del could not insert into incompleteops");
}
}
static void
-flush(int testno,
- hyperclient* cl)
+flush()
{
- while (!incompleteops.empty())
+ while (!_incompleteops.empty())
{
hyperclient_returncode status;
- int64_t id = cl->loop(10000, &status);
+ int64_t id = _cl->loop(10000, &status);
if (id < 0)
{
- FAIL(testno, "loop returned error " << status);
+ FAIL("loop returned error " << status);
}
else
{
- std::map<int64_t, hyperclient_returncode*>::iterator incomplete;
- incomplete = incompleteops.find(id);
+ std::map<int64_t, std::tr1::shared_ptr<hyperclient_returncode> >::iterator incomplete;
+ incomplete = _incompleteops.find(id);
- if (incomplete == incompleteops.end())
+ if (incomplete == _incompleteops.end())
{
- FAIL(testno, "loop returned unknown id " << id);
+ FAIL("loop returned unknown id " << id);
}
if (*(incomplete->second) != HYPERCLIENT_SUCCESS)
{
- FAIL(testno, "operation " << id << " returned " << *(incomplete->second));
+ FAIL("operation " << id << " returned " << *(incomplete->second));
}
- delete incomplete->second;
- incompleteops.erase(incomplete);
+ _incompleteops.erase(incomplete);
}
}
}
static void
-present(int testno,
- hyperclient* cl,
- uint32_t A, uint32_t B, uint32_t C)
+present(uint64_t A, uint64_t B, uint64_t C)
{
- flush(testno, cl);
+ flush();
+ char buf[3 * sizeof(uint64_t)];
+ e::pack64le(A, buf + 0 * sizeof(uint64_t));
+ e::pack64le(B, buf + 1 * sizeof(uint64_t));
+ e::pack64le(C, buf + 2 * sizeof(uint64_t));
hyperclient_returncode gstatus;
hyperclient_returncode lstatus;
hyperclient_attribute* attrs;
size_t attrs_sz;
- int64_t gid = cl->get(space, reinterpret_cast<char*>(&A), sizeof(uint32_t), &gstatus, &attrs, &attrs_sz);
+ int64_t gid = _cl->get(_space, buf, sizeof(uint64_t), &gstatus, &attrs, &attrs_sz);
if (gid < 0)
{
- FAIL(testno, "get encountered error " << gstatus);
+ FAIL("get encountered error " << gstatus);
}
- int64_t lid = cl->loop(10000, &lstatus);
+ int64_t lid = _cl->loop(10000, &lstatus);
if (lid < 0)
{
- FAIL(testno, "loop encountered error " << lstatus);
+ FAIL("loop encountered error " << lstatus);
}
if (gid != lid)
{
- FAIL(testno, "loop id (" << lid << ") does not match get id (" << gid << ")");
+ FAIL("loop id (" << lid << ") does not match get id (" << gid << ")");
}
if (gstatus != HYPERCLIENT_SUCCESS)
{
- FAIL(testno, "operation " << gid << " (a presence check) returned " << gstatus);
+ FAIL("operation " << gid << " (a presence check) returned " << gstatus);
}
if (attrs_sz != 2)
{
- FAIL(testno, "presence check: " << attrs_sz << " attributes instead of 2 attributes");
+ FAIL("presence check: " << attrs_sz << " attributes instead of 2 attributes");
}
if (strcmp(attrs[0].attr, "B") != 0)
{
- FAIL(testno, "presence check: first attribute is \"" << attrs[0].attr << "\" instead of \"B\"");
+ FAIL("presence check: first attribute is \"" << attrs[0].attr << "\" instead of \"B\"");
}
- if (attrs[0].datatype != HYPERDATATYPE_STRING)
+ if (attrs[0].datatype != HYPERDATATYPE_INT64)
{
- FAIL(testno, "presence check: first attribute is not of datatype \"string\"");
+ FAIL("presence check: first attribute is not of datatype \"int\"");
}
- if (e::slice(attrs[0].value, attrs[0].value_sz) != e::slice(&B, sizeof(uint32_t)))
+ if (e::slice(attrs[0].value, attrs[0].value_sz) != e::slice(buf + sizeof(uint64_t), sizeof(uint64_t)))
{
- FAIL(testno, "presence check: first attribute does not match " << B);
+ FAIL("presence check: first attribute does not match " << B
+ << " (" << e::slice(attrs[0].value, attrs[0].value_sz).hex() << " vs. "
+ << e::slice(buf + sizeof(uint64_t), sizeof(uint64_t)).hex() << ")");
}
if (strcmp(attrs[1].attr, "C") != 0)
{
- FAIL(testno, "presence check: second attribute is \"" << attrs[1].attr << "\" instead of \"C\"");
+ FAIL("presence check: second attribute is \"" << attrs[1].attr << "\" instead of \"C\"");
}
- if (attrs[1].datatype != HYPERDATATYPE_STRING)
+ if (attrs[1].datatype != HYPERDATATYPE_INT64)
{
- FAIL(testno, "presence check: second attribute is not of datatype \"string\"");
+ FAIL("presence check: second attribute is not of datatype \"int\"");
}
- if (e::slice(attrs[1].value, attrs[1].value_sz) != e::slice(&C, sizeof(uint32_t)))
+ if (e::slice(attrs[1].value, attrs[1].value_sz) != e::slice(buf + 2 * sizeof(uint64_t), sizeof(uint64_t)))
{
- FAIL(testno, "presence check: second attribute does not match " << C);
+ FAIL("presence check: second attribute does not match " << C
+ << " (" << e::slice(attrs[1].value, attrs[1].value_sz).hex() << " vs. "
+ << e::slice(buf + 2 * sizeof(uint64_t), sizeof(uint64_t)).hex() << ")");
}
- free(attrs);
+ hyperclient_destroy_attrs(attrs, attrs_sz);
}
static void
-absent(int testno,
- hyperclient* cl,
- uint32_t A)
+absent(uint64_t A)
{
- flush(testno, cl);
+ flush();
+ char buf[sizeof(uint64_t)];
+ e::pack64le(A, buf);
hyperclient_returncode gstatus;
hyperclient_returncode lstatus;
hyperclient_attribute* attrs = NULL;
size_t attrs_sz = 0;
- int64_t gid = cl->get(space, reinterpret_cast<char*>(&A), sizeof(uint32_t), &gstatus, &attrs, &attrs_sz);
+ int64_t gid = _cl->get(_space, buf, sizeof(uint64_t), &gstatus, &attrs, &attrs_sz);
if (gid < 0)
{
- FAIL(testno, "get encountered error " << gstatus);
+ FAIL("get encountered error " << gstatus);
}
- int64_t lid = cl->loop(10000, &lstatus);
+ int64_t lid = _cl->loop(10000, &lstatus);
if (lid < 0)
{
- FAIL(testno, "loop returned error " << lstatus);
+ FAIL("loop returned error " << lstatus);
}
if (gid != lid)
{
- FAIL(testno, "loop id (" << lid << ") does not match get id (" << gid << ")");
+ FAIL("loop id (" << lid << ") does not match get id (" << gid << ")");
}
if (gstatus != HYPERCLIENT_NOTFOUND)
{
- FAIL(testno, "operation " << gid << " (an absence check) returned " << gstatus);
+ FAIL("operation " << gid << " (an absence check) returned " << gstatus);
}
assert(!attrs && !attrs_sz);
@@ -527,52 +407,69 @@ absent(int testno,
// This test continually puts keys, ensuring that every way in which one could
// select regions from the subspaces is chosen.
void
-test0(hyperclient* cl)
+test0()
{
- for (generator A; A.has_next(); A.next())
+ _testno = 0;
+ uint64_t A;
+ uint64_t B;
+ uint64_t C;
+
+ for (long i = 0; i < _partitions; ++i)
{
- absent(0, cl, A);
+ A = generate_attr(i);
+ absent(A);
- for (generator B; B.has_next(); B.next())
+ for (long j = 0; j < _partitions; ++j)
{
- for (generator C; C.has_next(); C.next())
+ B = generate_attr(j);
+
+ for (long k = 0; k < _partitions; ++k)
{
- put(0, cl, A, B, C);
+ C = generate_attr(k);
+ put(A, B, C);
}
}
- present(0, cl, A, generator::end(), generator::end());
- del(0, cl, A);
- absent(0, cl, A);
+ present(A, B, C);
+ del(A);
+ absent(A);
}
- success(0);
+ success();
}
// This test continually puts/deletes keys, ensuring that a [PUT, DEL] sequence
// of operations is run through every way in which one could select regions from
-// the subspaces. 6 * 2**prefix requests will be performed. This tests that
-// delete plays nicely with the fresh bit.
+// the subspaces. This tests that delete plays nicely with the fresh bit.
void
-test1(hyperclient* cl)
+test1()
{
- for (generator A; A.has_next(); A.next())
+ _testno = 1;
+ uint64_t A;
+ uint64_t B;
+ uint64_t C;
+
+ for (long i = 0; i < _partitions; ++i)
{
- absent(1, cl, A);
+ A = generate_attr(i);
+ absent(A);
- for (generator B; B.has_next(); B.next())
+ for (long j = 0; j < _partitions; ++j)
{
- for (generator C; C.has_next(); C.next())
+ B = generate_attr(j);
+
+ for (long k = 0; k < _partitions; ++k)
{
- put(1, cl, A, B, C);
- del(1, cl, A);
+ C = generate_attr(k);
+ put(A, B, C);
+ del(A);
}
}
- absent(1, cl, A);
+ absent(A);
}
- success(1);
+ success();
}
// This test puts keys such that A and B are fixed, but every choice of C for
@@ -580,50 +477,68 @@ test1(hyperclient* cl)
// tests that CHAIN_SUBSPACE messages work. This tests that CHAIN_SUBSPACE
// messages work correctly.
void
-test2(hyperclient* cl)
+test2()
{
- for (generator A; A.has_next(); A.next())
+ _testno = 2;
+ uint64_t A;
+ uint64_t B;
+ uint64_t C;
+
+ for (long i = 0; i < _partitions; ++i)
{
- for (generator B; B.has_next(); B.next())
+ A = generate_attr(i);
+
+ for (long j = 0; j < _partitions; ++j)
{
- absent(2, cl, A);
+ B = generate_attr(j);
+ absent(A);
- for (generator C; C.has_next(); C.next())
+ for (long k = 0; k < _partitions; ++k)
{
- put(2, cl, A, B, C);
+ C = generate_attr(k);
+ put(A, B, C);
}
- present(2, cl, A, B, generator::end());
- del(2, cl, A);
- absent(2, cl, A);
+ present(A, B, C);
+ del(A);
+ absent(A);
}
}
- success(2);
+ success();
}
-// This is isomorphic to test2 except that columns A and C are fixed.
+// This is similar to test2 except that columns A and C are fixed.
void
-test3(hyperclient* cl)
+test3()
{
- for (generator A; A.has_next(); A.next())
+ _testno = 3;
+ uint64_t A;
+ uint64_t B;
+ uint64_t C;
+
+ for (long i = 0; i < _partitions; ++i)
{
- for (generator C; C.has_next(); C.next())
+ A = generate_attr(i);
+
+ for (long j = 0; j < _partitions; ++j)
{
- absent(3, cl, A);
+ C = generate_attr(j);
+ absent(A);
- for (generator B; B.has_next(); B.next())
+ for (long k = 0; k < _partitions; ++k)
{
- put(3, cl, A, B, C);
+ B = generate_attr(k);
+ put(A, B, C);
}
- present(3, cl, A, generator::end(), C);
- del(3, cl, A);
- absent(3, cl, A);
+ present(A, B, C);
+ del(A);
+ absent(A);
}
}
- success(3);
+ success();
}
// This test stresses the interaction of CHAIN_SUBSPACE with DELETE messages.
@@ -631,33 +546,44 @@ test3(hyperclient* cl)
// B and C to jump to another subspace. It then issues a DEL to try to create a
// deferred update.
void
-test4(hyperclient* cl)
+test4()
{
- for (generator A; A.has_next(); A.next())
+ _testno = 4;
+ uint64_t A;
+ uint64_t B;
+ uint64_t BP;
+ uint64_t C;
+ uint64_t CP;
+
+ for (long h = 0; h < _partitions; ++h)
{
- for (generator B; B.has_next(); B.next())
+ A = generate_attr(h);
+
+ for (long i = 0; i < _partitions; ++i)
{
- for (generator C; C.has_next(); C.next())
+ B = generate_attr(i);
+
+ for (long j = 0; j < _partitions; ++j)
{
- absent(4, cl, A);
+ C = generate_attr(j);
- for (generator BP; BP.has_next(); BP.next())
+ for (long k = 0; k < _partitions; ++k)
{
- for (generator CP; CP.has_next(); CP.next())
+ BP = generate_attr(k);
+
+ for (long l = 0; l < _partitions; ++l)
{
- if (B != BP && C != CP)
- {
- put(4, cl, A, B, C);
- put(4, cl, A, BP, CP);
- del(4, cl, A);
- }
+ CP = generate_attr(l);
+ put(A, B, C);
+ put(A, BP, CP);
+ del(A);
}
}
- absent(4, cl, A);
+ absent(A);
}
}
}
- success(4);
+ success();
}
Please sign in to comment.
Something went wrong with that request. Please try again.