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

* Enable keys() to list elements without fetching values from server
* Add concurrency control and cancel to CI workflows

Signed-off-by: Tao He <sighingnow@gmail.com>
This commit is contained in:
Tao He 2023-01-10 15:00:34 +08:00 committed by GitHub
parent 47a5f5238f
commit 10f3435c28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 233 additions and 39 deletions

View File

@ -2,6 +2,10 @@ name: Build Deb Package
on: [push, pull_request]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
runs-on: ${{ matrix.os }}

View File

@ -2,6 +2,10 @@ name: Build and Test
on: [push, pull_request]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
runs-on: ${{ matrix.os }}

View File

@ -1,13 +0,0 @@
name: Cancel Stale Runs
on: [push, pull_request_target]
jobs:
cancel:
runs-on: ubuntu-latest
steps:
- uses: styfle/cancel-workflow-action@0.9.1
with:
workflow_id: build-deb-package.yml,build-test.yml,centos-latest.yml
access_token: ${{ github.token }}

View File

@ -2,6 +2,10 @@ name: Build & Test on CentOS Latest
on: [push, pull_request]
concurrency:
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
runs-on: ${{ matrix.os }}

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:
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);
/**
* 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.
* @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);
/**
* 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 limit is the size limit of results to be listed, we don't use default parameters
* to ensure backwards binary compatibility.
@ -421,10 +423,9 @@ namespace etcd
*/
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
* 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 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);
/**
* 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
* 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);
/**
* 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
*/
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 limit is the size limit of results to be listed, we don't use default parameters
* 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
* 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 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);
/**
* 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
* 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::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::AsyncRangeAction> ls_internal(std::string const & key, size_t const limit);
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, 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, 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, std::string const &range_end, int64_t fromIndex);
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 key;
std::string range_end;
bool keys_only;
bool count_only;
std::string value;
std::string old_value;
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));
}
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)
{
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));
}
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)
{
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));
}
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;
params.key.assign(key);
params.range_end.assign(range_end);
params.keys_only = keys_only;
params.withPrefix = false;
params.limit = limit;
params.auth_token.assign(this->token_authenticator->renew_if_expired());

View File

@ -32,6 +32,8 @@ etcdv3::ActionParameters::ActionParameters()
old_revision = 0;
lease_id = 0;
ttl = 0;
keys_only = false;
count_only = false;
kv_stub = NULL;
watch_stub = NULL;
lease_stub = NULL;
@ -56,6 +58,8 @@ void etcdv3::ActionParameters::dump(std::ostream &os) const {
os << " name: " << name << std::endl;
os << " key: " << key << 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 << " old_value: " << old_value << 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);
// 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->Finish(&reply, &status, (void*)this);
}

View File

@ -45,6 +45,11 @@ TEST_CASE("sync operations")
etcd.set("/test/new_dir/key2", "value2");
CHECK(2 == etcd.ls("/test/new_dir").keys().size());
// keys
etcd.set("/test/new_dir/key1", "value1");
etcd.set("/test/new_dir/key2", "value2");
CHECK(2 == etcd.keys("/test/new_dir").keys().size());
// rmdir
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());

View File

@ -326,6 +326,34 @@ TEST_CASE("list by range")
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/key2").get();
REQUIRE(resp1.is_ok());
CHECK("get" == resp1.action());
REQUIRE(1 == resp1.keys().size());
REQUIRE(1 == resp1.values().size());
REQUIRE(resp1.values()[0].as_string() == "value1");
etcd::Response resp2 = etcd.keys("/test/new_dir/key1", "/test/new_dir/key2").get();
REQUIRE(resp1.is_ok());
CHECK("get" == resp2.action());
REQUIRE(1 == resp2.keys().size());
REQUIRE(1 == resp2.values().size());
REQUIRE(resp2.values()[0].as_string() == "");
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
}
TEST_CASE("delete a directory")
{
etcd::Client etcd(etcd_url);