From 4ad9bbae0a7c2a85953830556908435122850a7d Mon Sep 17 00:00:00 2001 From: Tao He Date: Wed, 26 Apr 2023 20:47:46 +0800 Subject: [PATCH] Get and list keys with specified revision. Signed-off-by: Tao He --- etcd/Client.hpp | 57 +++++++++++++++++++++++++++++++- etcd/SyncClient.hpp | 65 ++++++++++++++++++++++++++++++++++--- etcd/v3/Action.hpp | 4 +-- src/Client.cpp | 35 ++++++++++++++++++++ src/SyncClient.cpp | 33 +++++++++++++++++-- src/v3/AsyncRangeAction.cpp | 3 ++ tst/EtcdTest.cpp | 15 ++++++++- 7 files changed, 200 insertions(+), 12 deletions(-) diff --git a/etcd/Client.hpp b/etcd/Client.hpp index 9c01d4e..c59cfe2 100644 --- a/etcd/Client.hpp +++ b/etcd/Client.hpp @@ -259,6 +259,13 @@ namespace etcd */ pplx::task get(std::string const & key); + /** + * Get the value of specified key of specified revision from the etcd server + * @param key is the key to be read + * @param revision is the revision of the key to be read + */ + pplx::task get(std::string const & key, int64_t revision); + /** * Sets the value of a key. The key will be modified if already exists or created * if it does not exists. @@ -410,10 +417,20 @@ namespace etcd * * @param key is the key to be listed * @param limit is the size limit of results to be listed, we don't use default parameters - * to ensure backwards binary compatibility. + * to ensure backwards binary compatibility. 0 means no limit. */ pplx::task ls(std::string const & key, size_t const limit); + /** + * Gets a directory listing of the directory prefixed by the key with given revision. + * + * @param key is the key to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. 0 means no limit. + * @param revision is the revision to be listed + */ + pplx::task ls(std::string const & key, size_t const limit, int64_t revision); + /** * Gets a directory listing of the directory identified by the key and range_end, i.e., get * all keys in the range [key, range_end). @@ -434,6 +451,18 @@ namespace etcd */ pplx::task ls(std::string const & key, std::string const &range_end, size_t const limit); + /** + * Gets a directory listing of the directory identified by the key and range_end, i.e., get + * all keys in the range [key, range_end), and respects the given limit and revision. + * + * @param key is the key to be listed + * @param range_end is the end of key range to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. + * @param revision is the revision to be listed + */ + pplx::task ls(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision); + /** * Gets a directory listing of the directory prefixed by the key. * @@ -454,6 +483,18 @@ namespace etcd */ pplx::task keys(std::string const & key, size_t const limit); + /** + * Gets a directory listing of the directory prefixed by the key. + * + * Note that only keys are included in the response. + * + * @param key is the key to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. + * @param revision is the revision to be listed + */ + pplx::task keys(std::string const & key, size_t const limit, int64_t revision); + /** * List keys identified by the key and range_end, i.e., get all keys in the range [key, * range_end), and respects the given limit. @@ -478,6 +519,20 @@ namespace etcd */ pplx::task keys(std::string const & key, std::string const &range_end, size_t const limit); + /** + * List keys identified by the key and range_end, i.e., get all keys in the range [key, + * range_end). + * + * Note that only keys are included in the response. + * + * @param key is the key to be listed + * @param range_end is the end of key range to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. + * @param revision is the revision to be listed + */ + pplx::task keys(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision); + /** * Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and * a new key is created, like "/testdir/newkey" then no change happened in the value of diff --git a/etcd/SyncClient.hpp b/etcd/SyncClient.hpp index 9d61019..5313f13 100644 --- a/etcd/SyncClient.hpp +++ b/etcd/SyncClient.hpp @@ -316,6 +316,13 @@ namespace etcd */ Response get(std::string const & key); + /** + * Get the value of specified key of specified revision from the etcd server + * @param key is the key to be read + * @param revision is the revision of the key to be read + */ + Response get(std::string const & key, int64_t revision); + /** * Sets the value of a key. The key will be modified if already exists or created * if it does not exists. @@ -466,10 +473,20 @@ namespace etcd * * @param key is the key to be listed * @param limit is the size limit of results to be listed, we don't use default parameters - * to ensure backwards binary compatibility. + * to ensure backwards binary compatibility. 0 means no limit. */ Response ls(std::string const & key, size_t const limit); + /** + * Gets a directory listing of the directory prefixed by the key with given revision. + * + * @param key is the key to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. 0 means no limit. + * @param revision is the revision to be listed + */ + Response ls(std::string const & key, size_t const limit, int64_t revision); + /** * Gets a directory listing of the directory identified by the key and range_end, i.e., get * all keys in the range [key, range_end). @@ -486,10 +503,22 @@ namespace etcd * @param key is the key to be listed * @param range_end is the end of key range to be listed * @param limit is the size limit of results to be listed, we don't use default parameters - * to ensure backwards binary compatibility. + * to ensure backwards binary compatibility. 0 means no limit. */ Response ls(std::string const & key, std::string const &range_end, size_t const limit); + /** + * Gets a directory listing of the directory identified by the key and range_end, i.e., get + * all keys in the range [key, range_end), and respects the given limit and revision. + * + * @param key is the key to be listed + * @param range_end is the end of key range to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. 0 means no limit. + * @param revision is the revision to be listed + */ + Response ls(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision); + /** * Gets a directory listing of the directory prefixed by the key. * @@ -510,6 +539,18 @@ namespace etcd */ Response keys(std::string const & key, size_t const limit); + /** + * Gets a directory listing of the directory prefixed by the key with specified revision. + * + * Note that only keys are included in the response. + * + * @param key is the key to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. + * @param revision is the revision to be listed + */ + Response keys(std::string const & key, size_t const limit, int64_t revision); + /** * List keys identified by the key and range_end, i.e., get all keys in the range [key, * range_end). @@ -534,6 +575,20 @@ namespace etcd */ Response keys(std::string const & key, std::string const &range_end, size_t const limit); + /** + * List keys identified by the key and range_end, i.e., get all keys in the range [key, + * range_end), and respects the given limit and revision. + * + * Note that only keys are included in the response. + * + * @param key is the key to be listed + * @param range_end is the end of key range to be listed + * @param limit is the size limit of results to be listed, we don't use default parameters + * to ensure backwards binary compatibility. + * @param revision is the revision to be listed + */ + Response keys(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision); + /** * Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and * a new key is created, like "/testdir/newkey" then no change happened in the value of @@ -721,7 +776,7 @@ namespace etcd private: // TODO: use std::unique_ptr<> std::shared_ptr head_internal(); - std::shared_ptr get_internal(std::string const & key); + std::shared_ptr get_internal(std::string const & key, int64_t revision=0); std::shared_ptr set_internal(std::string const & key, std::string const & value, int64_t leaseId); std::shared_ptr add_internal(std::string const & key, std::string const & value, int64_t leaseId); std::shared_ptr put_internal(std::string const & key, std::string const & value); @@ -731,8 +786,8 @@ namespace etcd std::shared_ptr rm_if_internal(std::string const & key, int64_t old_index, const std::string & old_value, etcdv3::AtomicityType const & atomicity_type); std::shared_ptr rmdir_internal(std::string const & key, bool recursive = false); std::shared_ptr rmdir_internal(std::string const & key, std::string const &range_end); - std::shared_ptr ls_internal(std::string const & key, size_t const limit, bool const keys_only = false); - std::shared_ptr ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only = false); + std::shared_ptr ls_internal(std::string const & key, size_t const limit, bool const keys_only = false, int64_t revision=0); + std::shared_ptr ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only = false, int64_t revision=0); std::shared_ptr watch_internal(std::string const & key, int64_t fromIndex, bool recursive = false); std::shared_ptr watch_internal(std::string const & key, std::string const &range_end, int64_t fromIndex); std::shared_ptr leaserevoke_internal(int64_t lease_id); diff --git a/etcd/v3/Action.hpp b/etcd/v3/Action.hpp index 6f6d493..ee2f550 100644 --- a/etcd/v3/Action.hpp +++ b/etcd/v3/Action.hpp @@ -35,8 +35,8 @@ namespace etcdv3 { ActionParameters(); bool withPrefix; - int64_t revision; - int64_t old_revision; + int64_t revision = 0; + int64_t old_revision = 0; int64_t lease_id = 0; // no lease int ttl; int limit; diff --git a/src/Client.cpp b/src/Client.cpp index 3894ad6..1fe7ee2 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -244,6 +244,13 @@ pplx::task etcd::Client::get(std::string const & key) this->client->get_internal(key)); } +pplx::task etcd::Client::get(std::string const & key, int64_t revision) +{ + return etcd::detail::asyncify( + static_cast>(Response::create), + this->client->get_internal(key, revision)); +} + pplx::task etcd::Client::set(std::string const & key, std::string const & value, int ttl) { if (ttl > 0) { @@ -439,6 +446,13 @@ pplx::task etcd::Client::ls(std::string const & key, size_t cons this->client->ls_internal(key, limit)); } +pplx::task etcd::Client::ls(std::string const & key, size_t const limit, int64_t revision) +{ + return etcd::detail::asyncify( + static_cast>(Response::create), + this->client->ls_internal(key, limit, false, revision)); +} + pplx::task etcd::Client::ls(std::string const & key, std::string const &range_end) { return etcd::detail::asyncify( @@ -453,6 +467,13 @@ pplx::task etcd::Client::ls(std::string const & key, std::string this->client->ls_internal(key, range_end, limit)); } +pplx::task etcd::Client::ls(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision) +{ + return etcd::detail::asyncify( + static_cast>(Response::create), + this->client->ls_internal(key, range_end, limit, false, revision)); +} + pplx::task etcd::Client::keys(std::string const & key) { return etcd::detail::asyncify( @@ -467,6 +488,13 @@ pplx::task etcd::Client::keys(std::string const & key, size_t co this->client->ls_internal(key, limit, true)); } +pplx::task etcd::Client::keys(std::string const & key, size_t const limit, int64_t revision) +{ + return etcd::detail::asyncify( + static_cast>(Response::create), + this->client->ls_internal(key, limit, true, revision)); +} + pplx::task etcd::Client::keys(std::string const & key, std::string const &range_end) { return etcd::detail::asyncify( @@ -481,6 +509,13 @@ pplx::task etcd::Client::keys(std::string const & key, std::stri this->client->ls_internal(key, range_end, limit, true)); } +pplx::task etcd::Client::keys(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision) +{ + return etcd::detail::asyncify( + static_cast>(Response::create), + this->client->ls_internal(key, range_end, limit, true, revision)); +} + pplx::task etcd::Client::watch(std::string const & key, bool recursive) { return etcd::detail::asyncify( diff --git a/src/SyncClient.cpp b/src/SyncClient.cpp index 3e7d702..1b27103 100644 --- a/src/SyncClient.cpp +++ b/src/SyncClient.cpp @@ -482,10 +482,15 @@ etcd::Response etcd::SyncClient::get(std::string const &key) { return Response::create(this->get_internal(key)); } -std::shared_ptr etcd::SyncClient::get_internal(std::string const & key) +etcd::Response etcd::SyncClient::get(std::string const &key, int64_t revision) { + return Response::create(this->get_internal(key, revision)); +} + +std::shared_ptr etcd::SyncClient::get_internal(std::string const & key, int64_t revision) { etcdv3::ActionParameters params; params.key.assign(key); + params.revision = revision; params.withPrefix = false; params.auth_token.assign(this->token_authenticator->renew_if_expired()); params.grpc_timeout = this->grpc_timeout; @@ -756,6 +761,11 @@ etcd::Response etcd::SyncClient::ls(std::string const & key, size_t const limit) return Response::create(this->ls_internal(key, limit)); } +etcd::Response etcd::SyncClient::ls(std::string const & key, size_t const limit, int64_t revision) +{ + return Response::create(this->ls_internal(key, limit, false, revision)); +} + etcd::Response etcd::SyncClient::ls(std::string const & key, std::string const &range_end) { return Response::create(this->ls_internal(key, range_end, 0 /* default: no limit */)); @@ -766,6 +776,11 @@ etcd::Response etcd::SyncClient::ls(std::string const & key, std::string const & return Response::create(this->ls_internal(key, range_end, limit)); } +etcd::Response etcd::SyncClient::ls(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision) +{ + return Response::create(this->ls_internal(key, range_end, limit, false, revision)); +} + etcd::Response etcd::SyncClient::keys(std::string const & key) { return Response::create(this->ls_internal(key, 0 /* default: no limit */, true)); @@ -776,6 +791,11 @@ etcd::Response etcd::SyncClient::keys(std::string const & key, size_t const limi return Response::create(this->ls_internal(key, limit, true)); } +etcd::Response etcd::SyncClient::keys(std::string const & key, size_t const limit, int64_t revision) +{ + return Response::create(this->ls_internal(key, limit, true, revision)); +} + etcd::Response etcd::SyncClient::keys(std::string const & key, std::string const &range_end) { return Response::create(this->ls_internal(key, range_end, 0 /* default: no limit */, true)); @@ -786,25 +806,32 @@ etcd::Response etcd::SyncClient::keys(std::string const & key, std::string const return Response::create(this->ls_internal(key, range_end, limit, true)); } -std::shared_ptr etcd::SyncClient::ls_internal(std::string const & key, size_t const limit, bool const keys_only) { +etcd::Response etcd::SyncClient::keys(std::string const & key, std::string const &range_end, size_t const limit, int64_t revision) +{ + return Response::create(this->ls_internal(key, range_end, limit, true, revision)); +} + +std::shared_ptr etcd::SyncClient::ls_internal(std::string const & key, size_t const limit, bool const keys_only, int64_t revision) { etcdv3::ActionParameters params; params.key.assign(key); params.keys_only = keys_only; params.withPrefix = true; params.limit = limit; + params.revision = revision; params.auth_token.assign(this->token_authenticator->renew_if_expired()); params.grpc_timeout = this->grpc_timeout; params.kv_stub = stubs->kvServiceStub.get(); return std::make_shared(std::move(params)); } -std::shared_ptr etcd::SyncClient::ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only) { +std::shared_ptr etcd::SyncClient::ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only, int64_t revision) { etcdv3::ActionParameters params; params.key.assign(key); params.range_end.assign(range_end); params.keys_only = keys_only; params.withPrefix = false; params.limit = limit; + params.revision = revision; params.auth_token.assign(this->token_authenticator->renew_if_expired()); params.grpc_timeout = this->grpc_timeout; params.kv_stub = stubs->kvServiceStub.get(); diff --git a/src/v3/AsyncRangeAction.cpp b/src/v3/AsyncRangeAction.cpp index d81d592..54117cc 100644 --- a/src/v3/AsyncRangeAction.cpp +++ b/src/v3/AsyncRangeAction.cpp @@ -26,6 +26,9 @@ etcdv3::AsyncRangeAction::AsyncRangeAction( if(!parameters.range_end.empty()) { get_request.set_range_end(parameters.range_end); } + if(parameters.revision > 0) { + get_request.set_revision(parameters.revision); + } get_request.set_limit(parameters.limit); get_request.set_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_NONE); diff --git a/tst/EtcdTest.cpp b/tst/EtcdTest.cpp index 52140a7..f9d040a 100644 --- a/tst/EtcdTest.cpp +++ b/tst/EtcdTest.cpp @@ -57,11 +57,24 @@ TEST_CASE("simplified read") TEST_CASE("modify a key") { etcd::Client etcd(etcd_url); - etcd::Response resp = etcd.modify("/test/key1", "43").get(); + + // get + etcd::Response resp = etcd.get("/test/key1").get(); + REQUIRE(resp.is_ok()); + int64_t revision = resp.value().modified_index(); + CHECK("42" == resp.value().as_string()); + + // modify + resp = etcd.modify("/test/key1", "43").get(); REQUIRE(0 == resp.error_code()); // overwrite CHECK("update" == resp.action()); CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.modify("/test/key2", "43").get().error_code()); // Key not found CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string()); + + // check previous + resp = etcd.get("/test/key1", revision).get(); + REQUIRE(resp.is_ok()); + CHECK("42" == resp.value().as_string()); } TEST_CASE("set a key")