From 3de553ecd7d70f78b1436b5ea6e7159c787d140f Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Tue, 15 Jun 2021 00:07:20 +0300 Subject: [PATCH] Add a test for CAS write operation --- src/CMakeLists.txt | 8 +++ src/test_cas.cpp | 135 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_cas.sh | 7 +++ 3 files changed, 150 insertions(+) create mode 100644 src/test_cas.cpp create mode 100755 tests/test_cas.sh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc5d4e5..0a64457 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -200,6 +200,14 @@ target_link_libraries(osd_peering_pg_test tcmalloc_minimal) # test_allocator add_executable(test_allocator test_allocator.cpp allocator.cpp) +# test_cas +add_executable(test_cas + test_cas.cpp +) +target_link_libraries(test_cas + vitastor_client +) + # test_cluster_client add_executable(test_cluster_client test_cluster_client.cpp diff --git a/src/test_cas.cpp b/src/test_cas.cpp new file mode 100644 index 0000000..6c888e1 --- /dev/null +++ b/src/test_cas.cpp @@ -0,0 +1,135 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 (see README.md for details) + +#include +#include + +#include "epoll_manager.h" +#include "cluster_client.h" + +void send_read(cluster_client_t *cli, uint64_t inode, std::function cb) +{ + cluster_op_t *op = new cluster_op_t(); + op->opcode = OSD_OP_READ; + op->inode = inode; + op->offset = 0; + op->len = 4096; + op->iov.push_back(malloc_or_die(op->len), op->len); + op->callback = [cb](cluster_op_t *op) + { + uint64_t version = op->version; + int retval = op->retval; + if (retval == op->len) + retval = 0; + free(op->iov.buf[0].iov_base); + delete op; + if (cb != NULL) + cb(retval, version); + }; + cli->execute(op); +} + +void send_write(cluster_client_t *cli, uint64_t inode, int byte, uint64_t version, std::function cb) +{ + cluster_op_t *op = new cluster_op_t(); + op->opcode = OSD_OP_WRITE; + op->inode = inode; + op->offset = 0; + op->len = 4096; + op->version = version; + op->iov.push_back(malloc_or_die(op->len), op->len); + memset(op->iov.buf[0].iov_base, byte, op->len); + op->callback = [cb](cluster_op_t *op) + { + int retval = op->retval; + if (retval == op->len) + retval = 0; + free(op->iov.buf[0].iov_base); + delete op; + if (cb != NULL) + cb(retval); + }; + cli->execute(op); +} + +int main(int narg, char *args[]) +{ + json11::Json::object cfgo; + for (int i = 1; i < narg; i++) + { + if (args[i][0] == '-' && args[i][1] == '-') + { + const char *opt = args[i]+2; + cfgo[opt] = i == narg-1 ? "1" : args[++i]; + } + } + json11::Json cfg(cfgo); + uint64_t inode = (cfg["pool_id"].uint64_value() << (64-POOL_ID_BITS)) + | cfg["inode_id"].uint64_value(); + uint64_t base_ver = 0; + // Create client + auto ringloop = new ring_loop_t(512); + auto epmgr = new epoll_manager_t(ringloop); + auto cli = new cluster_client_t(ringloop, epmgr->tfd, cfg); + cli->on_ready([&]() + { + send_read(cli, inode, [&](int r, uint64_t v) + { + if (r < 0) + { + fprintf(stderr, "Initial read operation failed\n"); + exit(1); + } + base_ver = v; + // CAS v=1 = compare with zero, non-existing object + send_write(cli, inode, 0x01, base_ver+1, [&](int r) + { + if (r < 0) + { + fprintf(stderr, "CAS for non-existing object failed\n"); + exit(1); + } + // Check that read returns the new version + send_read(cli, inode, [&](int r, uint64_t v) + { + if (r < 0) + { + fprintf(stderr, "Read operation failed after write\n"); + exit(1); + } + if (v != base_ver+1) + { + fprintf(stderr, "Read operation failed to return the new version number\n"); + exit(1); + } + // CAS v=2 = compare with v=1, existing object + send_write(cli, inode, 0x02, base_ver+2, [&](int r) + { + if (r < 0) + { + fprintf(stderr, "CAS for existing object failed\n"); + exit(1); + } + // CAS v=2 again = compare with v=1, but version is 2. Must fail with -EINTR + send_write(cli, inode, 0x03, base_ver+2, [&](int r) + { + if (r != -EINTR) + { + fprintf(stderr, "CAS conflict detection failed\n"); + exit(1); + } + printf("Basic CAS test succeeded\n"); + exit(0); + }); + }); + }); + }); + }); + }); + while (1) + { + ringloop->loop(); + ringloop->wait(); + } + return 0; +} diff --git a/tests/test_cas.sh b/tests/test_cas.sh new file mode 100755 index 0000000..2cc980d --- /dev/null +++ b/tests/test_cas.sh @@ -0,0 +1,7 @@ +#!/bin/bash -ex + +. `dirname $0`/run_3osds.sh + +build/src/test_cas --pool_id 1 --inode_id 1 --etcd_address $ETCD_URL + +format_green OK