From b9284c91d1c5b42caf0a7992946219cca984bd89 Mon Sep 17 00:00:00 2001 From: Tao He Date: Tue, 27 Sep 2022 11:24:41 +0800 Subject: [PATCH] Add transaction tests and documentations Signed-off-by: Tao He --- .github/workflows/build-test.yml | 3 ++ README.md | 29 +++++++++++ tst/TransactionTest.cpp | 89 ++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 tst/TransactionTest.cpp diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d0d9990..2080174 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -172,6 +172,9 @@ jobs: echo "Run the etcd watcher test ........................." ./build/bin/WatcherTest + echo "Run the etcd transaction test ........................." + ./build/bin/TransactionTest + echo "Run the etcd election test ........................." ./build/bin/ElectionTest diff --git a/README.md b/README.md index 13e65bb..66bcd27 100644 --- a/README.md +++ b/README.md @@ -734,6 +734,35 @@ is constructed. Without handler, the internal state can be checked via `KeepAlive::Check()` and it will rethrow the async exception when there are errors during keeping the lease alive. +### Etcd transactions + +Etcd v3's [Transaction APIs](https://etcd.io/docs/v3.4/learning/api/#transaction) is supported via the +`etcdv3::Transaction` interfaces. A set of convenient APIs are use to add operations to a transaction, e.g., + +```cpp + etcdv3::Transaction txn; + txn.setup_put("/test/x1", "1"); + txn.setup_put("/test/x2", "2"); + txn.setup_put("/test/x3", "3"); + etcd::Response resp = etcd.txn(txn).get(); +``` + +Transactions in etcd supports set a set of comparison targets to specify the condition of transaction, e.g., + +```cpp + etcdv3::Transaction txn; + + // setup the conditions + txn.reset_key("/test/x1"); + txn.init_compare("1", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE); + + txn.reset_key("/test/x2"); + txn.init_compare("2", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE); +``` + +See full example of the usages of transaction APIs, please refer to [./tst/TransactionTest.cpp](./tst/TransactionTest.cpp), +for full list of the transaction operation APIs, see [./etcd/v3/Transaction.hpp](./etcd/v3/Transaction.hpp). + ### Election API Etcd v3's [election APIs](https://github.com/etcd-io/etcd/blob/main/server/etcdserver/api/v3election/v3electionpb/v3election.proto) diff --git a/tst/TransactionTest.cpp b/tst/TransactionTest.cpp new file mode 100644 index 0000000..d470d70 --- /dev/null +++ b/tst/TransactionTest.cpp @@ -0,0 +1,89 @@ +#define CATCH_CONFIG_MAIN +#include + +#include +#include +#include + +#include "etcd/Client.hpp" +#include "etcd/v3/Transaction.hpp" + + +TEST_CASE("setup") +{ + etcd::Client etcd("http://127.0.0.1:2379"); + etcd.rmdir("/test", true).wait(); +} + +TEST_CASE("add a new key") +{ + etcd::Client etcd("http://127.0.0.1:2379"); + etcd.rmdir("/test", true).wait(); + + // do some put using txn + { + etcdv3::Transaction txn; + txn.setup_put("/test/x1", "1"); + txn.setup_put("/test/x2", "2"); + txn.setup_put("/test/x3", "3"); + etcd::Response resp = etcd.txn(txn).get(); + REQUIRE(0 == resp.error_code()); + } + + // check the value + { + etcd::Response resp = etcd.get("/test/x1").get(); + REQUIRE(0 == resp.error_code()); + CHECK(resp.value().as_string() == "1"); + + resp = etcd.get("/test/x2").get(); + REQUIRE(0 == resp.error_code()); + CHECK(resp.value().as_string() == "2"); + + resp = etcd.get("/test/x3").get(); + REQUIRE(0 == resp.error_code()); + CHECK(resp.value().as_string() == "3"); + } + + // do some put and delete using txn + { + etcdv3::Transaction txn; + + // setup the conditions + txn.reset_key("/test/x1"); + txn.init_compare("1", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE); + + txn.reset_key("/test/x2"); + txn.init_compare("2", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE); + + txn.setup_put("/test/x1", "111"); + txn.setup_delete("/test/x2"); + txn.setup_delete("/test/x3"); + txn.setup_put("/test/x4", "4"); + etcd::Response resp = etcd.txn(txn).get(); + REQUIRE(0 == resp.error_code()); + } + + // check the value + { + etcd::Response resp = etcd.get("/test/x1").get(); + REQUIRE(0 == resp.error_code()); + CHECK(resp.value().as_string() == "111"); + + resp = etcd.get("/test/x2").get(); + REQUIRE(etcdv3::ERROR_KEY_NOT_FOUND == resp.error_code()); + + resp = etcd.get("/test/x3").get(); + REQUIRE(etcdv3::ERROR_KEY_NOT_FOUND == resp.error_code()); + + resp = etcd.get("/test/x4").get(); + REQUIRE(0 == resp.error_code()); + CHECK(resp.value().as_string() == "4"); + } +} + +TEST_CASE("cleanup") +{ + etcd::Client etcd("http://127.0.0.1:2379"); + REQUIRE(0 == etcd.rmdir("/test", true).get().error_code()); +}