Enable keys() to list elements without fetching values from server

Signed-off-by: Tao He <sighingnow@gmail.com>
This commit is contained in:
Tao He 2023-01-10 14:04:07 +08:00
parent 47a5f5238f
commit 8cbc70aafb
10 changed files with 224 additions and 26 deletions

View File

@ -557,6 +557,17 @@ keys defined by the prefix. mkdir method is removed since etcdv3 treats everythi
} }
``` ```
etcd-cpp-apiv3 supports lists keys only without fetching values from etcd server:
```c++
etcd::Client etcd("http://127.0.0.1:2379");
etcd::Response resp = etcd.keys("/test/new_dir").get();
for (int i = 0; i < resp.keys().size(); ++i)
{
std::cout << resp.keys(i);
}
```
3. Removing directory: 3. Removing directory:
If you want the delete recursively then you have to pass a second `true` parameter If you want the delete recursively then you have to pass a second `true` parameter

View File

@ -373,12 +373,6 @@ namespace etcd
*/ */
pplx::task<Response> rm_if(std::string const & key, int64_t old_index); pplx::task<Response> rm_if(std::string const & key, int64_t old_index);
/**
* Gets a directory listing of the directory identified by the key.
* @param key is the key to be listed
*/
pplx::task<Response> ls(std::string const & key);
/** /**
* Removes a directory node. Fails if the parent directory dos not exists or not a directory. * Removes a directory node. Fails if the parent directory dos not exists or not a directory.
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
@ -405,7 +399,15 @@ namespace etcd
pplx::task<Response> rmdir(std::string const & key, std::string const &range_end); pplx::task<Response> rmdir(std::string const & key, std::string const &range_end);
/** /**
* Gets a directory listing of the directory identified by the key. * Gets a directory listing of the directory prefixed by the key.
*
* @param key is the key to be listed
*/
pplx::task<Response> ls(std::string const & key);
/**
* Gets a directory listing of the directory prefixed by the key.
*
* @param key is the key to be listed * @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 * @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.
@ -421,10 +423,9 @@ namespace etcd
*/ */
pplx::task<Response> ls(std::string const & key, std::string const &range_end); pplx::task<Response> ls(std::string const & key, std::string const &range_end);
/** /**
* Gets a directory listing of the directory identified by the key and range_end, i.e., get * 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). * all keys in the range [key, range_end), and respects the given limit.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
@ -433,6 +434,50 @@ namespace etcd
*/ */
pplx::task<Response> ls(std::string const & key, std::string const &range_end, size_t const limit); pplx::task<Response> ls(std::string const & key, std::string const &range_end, 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
*/
pplx::task<Response> keys(std::string const & key);
/**
* 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.
*/
pplx::task<Response> keys(std::string const & key, 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.
*
* 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
*/
pplx::task<Response> keys(std::string const & key, std::string const &range_end);
/**
* 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.
*/
pplx::task<Response> keys(std::string const & key, std::string const &range_end, size_t const limit);
/** /**
* Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and * 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 * a new key is created, like "/testdir/newkey" then no change happened in the value of

View File

@ -454,13 +454,15 @@ namespace etcd
Response rmdir(std::string const & key, std::string const &range_end); Response rmdir(std::string const & key, std::string const &range_end);
/** /**
* Gets a directory listing of the directory identified by the key. * Gets a directory listing of the directory prefixed by the key.
*
* @param key is the key to be listed * @param key is the key to be listed
*/ */
Response ls(std::string const & key); Response ls(std::string const & key);
/** /**
* Gets a directory listing of the directory identified by the key. * Gets a directory listing of the directory prefixed by the key.
*
* @param key is the key to be listed * @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 * @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.
@ -478,7 +480,7 @@ namespace etcd
/** /**
* Gets a directory listing of the directory identified by the key and range_end, i.e., get * 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). * all keys in the range [key, range_end), and respects the given limit.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
@ -487,6 +489,50 @@ namespace etcd
*/ */
Response ls(std::string const & key, std::string const &range_end, size_t const limit); Response ls(std::string const & key, std::string const &range_end, 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
*/
Response keys(std::string const & key);
/**
* 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.
*/
Response keys(std::string const & key, 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
*/
Response keys(std::string const & key, std::string const &range_end);
/**
* 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.
*
* 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.
*/
Response keys(std::string const & key, std::string const &range_end, size_t const limit);
/** /**
* Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and * 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 * a new key is created, like "/testdir/newkey" then no change happened in the value of
@ -684,8 +730,8 @@ namespace etcd
std::shared_ptr<etcdv3::AsyncCompareAndDeleteAction> rm_if_internal(std::string const & key, int64_t old_index, const std::string & old_value, etcdv3::AtomicityType const & atomicity_type); std::shared_ptr<etcdv3::AsyncCompareAndDeleteAction> rm_if_internal(std::string const & key, int64_t old_index, const std::string & old_value, etcdv3::AtomicityType const & atomicity_type);
std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, bool recursive = false); std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, bool recursive = false);
std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, std::string const &range_end); std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, std::string const &range_end);
std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, size_t const limit); std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, size_t const limit, bool const keys_only = false);
std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, std::string const &range_end, size_t const limit); std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only = false);
std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, int64_t fromIndex, bool recursive = false); std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, int64_t fromIndex, bool recursive = false);
std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, std::string const &range_end, int64_t fromIndex); std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, std::string const &range_end, int64_t fromIndex);
std::shared_ptr<etcdv3::AsyncLeaseRevokeAction> leaserevoke_internal(int64_t lease_id); std::shared_ptr<etcdv3::AsyncLeaseRevokeAction> leaserevoke_internal(int64_t lease_id);

View File

@ -43,6 +43,8 @@ namespace etcdv3
std::string name; // for campaign (in v3election) std::string name; // for campaign (in v3election)
std::string key; std::string key;
std::string range_end; std::string range_end;
bool keys_only;
bool count_only;
std::string value; std::string value;
std::string old_value; std::string old_value;
std::string auth_token; std::string auth_token;

View File

@ -487,6 +487,34 @@ pplx::task<etcd::Response> etcd::Client::ls(std::string const & key, std::string
this->client->ls_internal(key, range_end, limit)); this->client->ls_internal(key, range_end, limit));
} }
pplx::task<etcd::Response> etcd::Client::keys(std::string const & key)
{
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, 0, true));
}
pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, size_t const limit)
{
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, limit, true));
}
pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, std::string const &range_end)
{
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, 0, true));
}
pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, std::string const &range_end, size_t const limit)
{
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, limit, true));
}
pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, bool recursive) pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, bool recursive)
{ {
return etcd::detail::asyncify( return etcd::detail::asyncify(

View File

@ -787,17 +787,6 @@ etcd::Response etcd::SyncClient::ls(std::string const & key, size_t const limit)
return Response::create(this->ls_internal(key, limit)); return Response::create(this->ls_internal(key, limit));
} }
std::shared_ptr<etcdv3::AsyncRangeAction> etcd::SyncClient::ls_internal(std::string const & key, size_t const limit) {
etcdv3::ActionParameters params;
params.key.assign(key);
params.withPrefix = true;
params.limit = limit;
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<etcdv3::AsyncRangeAction>(std::move(params));
}
etcd::Response etcd::SyncClient::ls(std::string const & key, std::string const &range_end) 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 */)); return Response::create(this->ls_internal(key, range_end, 0 /* default: no limit */));
@ -808,10 +797,43 @@ etcd::Response etcd::SyncClient::ls(std::string const & key, std::string const &
return Response::create(this->ls_internal(key, range_end, limit)); return Response::create(this->ls_internal(key, range_end, limit));
} }
std::shared_ptr<etcdv3::AsyncRangeAction> etcd::SyncClient::ls_internal(std::string const & key, std::string const &range_end, size_t const limit) { etcd::Response etcd::SyncClient::keys(std::string const & key)
{
return Response::create(this->ls_internal(key, 0 /* default: no limit */, true));
}
etcd::Response etcd::SyncClient::keys(std::string const & key, size_t const limit)
{
return Response::create(this->ls_internal(key, limit, true));
}
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));
}
etcd::Response etcd::SyncClient::keys(std::string const & key, std::string const &range_end, size_t const limit)
{
return Response::create(this->ls_internal(key, range_end, limit, true));
}
std::shared_ptr<etcdv3::AsyncRangeAction> etcd::SyncClient::ls_internal(std::string const & key, size_t const limit, bool const keys_only) {
etcdv3::ActionParameters params;
params.key.assign(key);
params.keys_only = keys_only;
params.withPrefix = true;
params.limit = limit;
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<etcdv3::AsyncRangeAction>(std::move(params));
}
std::shared_ptr<etcdv3::AsyncRangeAction> etcd::SyncClient::ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only) {
etcdv3::ActionParameters params; etcdv3::ActionParameters params;
params.key.assign(key); params.key.assign(key);
params.range_end.assign(range_end); params.range_end.assign(range_end);
params.keys_only = keys_only;
params.withPrefix = false; params.withPrefix = false;
params.limit = limit; params.limit = limit;
params.auth_token.assign(this->token_authenticator->renew_if_expired()); params.auth_token.assign(this->token_authenticator->renew_if_expired());

View File

@ -32,6 +32,8 @@ etcdv3::ActionParameters::ActionParameters()
old_revision = 0; old_revision = 0;
lease_id = 0; lease_id = 0;
ttl = 0; ttl = 0;
keys_only = false;
count_only = false;
kv_stub = NULL; kv_stub = NULL;
watch_stub = NULL; watch_stub = NULL;
lease_stub = NULL; lease_stub = NULL;
@ -56,6 +58,8 @@ void etcdv3::ActionParameters::dump(std::ostream &os) const {
os << " name: " << name << std::endl; os << " name: " << name << std::endl;
os << " key: " << key << std::endl; os << " key: " << key << std::endl;
os << " range_end: " << range_end << std::endl; os << " range_end: " << range_end << std::endl;
os << " keys_only: " << keys_only << std::endl;
os << " count_only: " << count_only << std::endl;
os << " value: " << value << std::endl; os << " value: " << value << std::endl;
os << " old_value: " << old_value << std::endl; os << " old_value: " << old_value << std::endl;
os << " auth_token: " << auth_token << std::endl; os << " auth_token: " << auth_token << std::endl;

View File

@ -30,6 +30,10 @@ etcdv3::AsyncRangeAction::AsyncRangeAction(
} }
get_request.set_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_NONE); get_request.set_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_NONE);
// set keys_only and count_only
get_request.set_keys_only(params.keys_only);
get_request.set_count_only(params.count_only);
response_reader = parameters.kv_stub->AsyncRange(&context,get_request,&cq_); response_reader = parameters.kv_stub->AsyncRange(&context,get_request,&cq_);
response_reader->Finish(&reply, &status, (void*)this); response_reader->Finish(&reply, &status, (void*)this);
} }

View File

@ -45,6 +45,12 @@ TEST_CASE("sync operations")
etcd.set("/test/new_dir/key2", "value2"); etcd.set("/test/new_dir/key2", "value2");
CHECK(2 == etcd.ls("/test/new_dir").keys().size()); CHECK(2 == etcd.ls("/test/new_dir").keys().size());
// keys
CHECK(0 == etcd.keys("/test/new_dir").keys().size());
etcd.set("/test/new_dir/key1", "value1");
etcd.set("/test/new_dir/key2", "value2");
CHECK(2 == etcd.keys("/test/new_dir").keys().size());
// rmdir // rmdir
CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.rmdir("/test/new_dir").error_code()); // key not found CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.rmdir("/test/new_dir").error_code()); // key not found
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code()); CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());

View File

@ -326,6 +326,36 @@ TEST_CASE("list by range")
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok()); CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
} }
TEST_CASE("list by range, w/o values")
{
etcd::Client etcd(etcd_url);
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
etcd.set("/test/new_dir/key0", "value0").wait();
etcd.set("/test/new_dir/key1", "value1").wait();
etcd.set("/test/new_dir/key2", "value2").wait();
etcd.set("/test/new_dir/key3", "value3").wait();
etcd.set("/test/new_dir/key4", "value4").wait();
etcd::Response resp1 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key3").get();
REQUIRE(resp1.is_ok());
CHECK("get" == resp1.action());
REQUIRE(2 == resp1.keys().size());
REQUIRE(2 == resp1.values().size());
REQUIRE(resp1.values()[0].as_string() == "value0");
REQUIRE(resp1.values()[1].as_string() == "value1");
etcd::Response resp2 = etcd.keys("/test/new_dir/key1", "/test/new_dir/key3").get();
REQUIRE(resp1.is_ok());
CHECK("get" == resp2.action());
REQUIRE(2 == resp2.keys().size());
REQUIRE(2 == resp2.values().size());
REQUIRE(resp2.values()[0].as_string() == "");
REQUIRE(resp2.values()[1].as_string() == "");
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
}
TEST_CASE("delete a directory") TEST_CASE("delete a directory")
{ {
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);