- Notifications
You must be signed in to change notification settings - Fork140
The etcd-cpp-apiv3 is a C++ library for etcd's v3 client APIs, i.e., ETCDCTL_API=3.
License
etcd-cpp-apiv3/etcd-cpp-apiv3
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Theetcd-cpp-apiv3 is a C++ API foretcd's v3 client API,i.e.,ETCDCTL_API=3.
Linux
- Ubuntu 18.04, requires upgrade gRPC libraries (tested with 1.27.x).
- Ubuntu 20.04
- CentOS 8 (tested with 1.27.x)
MacOS
- MacOS 10.15
- MacOS 11.0
Windows
- Windows 10, withvcpkg
boost and openssl (Note that boost is only required if you need the asynchronous runtime)
On Ubuntu, above requirement could be installed as:
apt-get install libboost-all-dev libssl-devOn MacOS, above requirement could be installed as:
brew install boost openssl
protobuf & gRPC
On Ubuntu, above requirements related to protobuf and gRPC can be installed as:
apt-get install libgrpc-dev \ libgrpc++-dev \ libprotobuf-dev \ protobuf-compiler-grpcOn MacOS, above requirements related to protobuf and gRPC can be installed as:
brew install grpc protobufWhen building grpc from source code (e.g., onUbuntu 18.04and onCentOS),if the system-installed openssl is preferred, you need to add
-DgRPC_SSL_PROVIDER=packagewhen building gRPC with CMake.
cpprestsdk, the latest version of master branchon github should work, you can build and install this dependency using cmake with:
git clone https://github.com/microsoft/cpprestsdk.git cd cpprestsdk mkdir build && cd build cmake .. -DCPPREST_EXCLUDE_WEBSOCKETS=ON make -j$(nproc) && make install
Theetcd-cpp-apiv3 doesn't maintain a website for documentation, for detail usage of theetcd APIs, please refer tothe "etcd operations" sectionin README, and see the detail C++ interfaces inClient.hppandSyncClient.hpp.
Theetcd-cpp-apiv3 library could be easily built and installed using cmake, after all abovedependencies have been successfully installed:
git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.gitcd etcd-cpp-apiv3mkdir build && cd buildcmake ..make -j$(nproc) && make installTo use this package in your CMake project, you can either
install, then find the library using
find_package():find_package(etcd-cpp-apiv3 REQUIRED)target_link_libraries(your_targetPRIVATE etcd-cpp-api)
or, add this repository as a subdirectory in your project, and link the library directly:
add_subdirectory(thirdparty/etcd-cpp-apiv3)target_link_libraries(your_targetPRIVATE etcd-cpp-api)
or, useFetchContent:
include(FetchContent)FetchContent_Declare( etcd-cpp-apiv3 https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git)FetchContent_MakeAvailable(etcd-cpp-apiv3)target_link_libraries(your_targetPRIVATE etcd-cpp-api)
Theetcd-cpp-apiv3 should work well with etcd > 3.0. Feel free to issue an issue to us onGithub when you encounter problems when working with etcd 3.x releases.
There are various discussion about whether to support a user-transparent multi-thread executor inthe background, or, leaving the burden of thread management to the user (e.g., seeissue#100 andissue#207 for more discussion aboutthe implementation of underlying thread model).
Theetcd-cpp-apiv3 library supports both synchronous and asynchronous runtime, controlled by thecmake optionBUILD_ETCD_CORE_ONLY=ON/OFF (defaults toOFF).
- When it is set as
OFF: the library artifact name will belibetcd-cpp-api.{a,so,dylib,lib,dll}and acmake targetetcd-cpp-apiis exported and pointed to it. The library provides both synchronous runtime(etcd/SyncClient.hpp) and asynchronous runtime (etcd/Client.hpp), and thecpprestsdkis arequired dependency. - When it is set as
ON: the library artifact name will belibetcd-cpp-api-core.{a,so,dylib,lib,dll}and a cmake targetetcd-cpp-apiis exported and pointed to it. The library provides only thesynchronous runtime (etcd/SyncClient.hpp), and thecpprestsdkwon't be required.
We encourage the users to use the asynchronous runtime by default, as it provides more flexibilityand convenient APIs and less possibilities for errors that block the main thread. Note that theasynchronous runtime requirescpprestsdk and will setup a thread pool in the background.
Warning: users cannot link bothlibetcd-cpp-api.{a,so,dylib,lib,dll} andlibetcd-cpp-api-core.{a,so,dylib,lib,dll}to same program.
etcd::Clientetcd("http://127.0.0.1:2379"); etcd::Response response = etcd.get("/test/key1").get(); std::cout << response.value().as_string();
Methods of the etcd client object are sending the corresponding gRPC requests and are returningimmediately with applx::task object. The task object is responsible for handling thereception of the HTTP response as well as parsing the gRPC of the response. All of this is doneasynchronously in a background thread so you can continue your code to do other operations while thecurrent etcd operation is executing in the background or you can wait for the response with thewait() orget() methods if a synchronous behavior is enough for your needs. These methodsare blocking until the HTTP response arrives or some error situation happens.get() methodalso returns theetcd::Response object.
etcd::Clientetcd("http://127.0.0.1:2379"); pplx::task<etcd::Response> response_task = etcd.get("/test/key1");// ... do something else etcd::Response response = response_task.get(); std::cout << response.value().as_string();
The pplx library allows to do even more. You can attach continuation objects to the task if you donot care about when the response is coming you only want to specify what to do then. Thiscan be achieved by calling thethen method of the task, giving a function object parameter toit that can be used as a callback when the response is arrived and processed. The parameter of thiscallback should be either aetcd::Response or applx::task<etcd:Response>. You shouldprobably use a C++ lambda function here as a callback.
etcd::Clientetcd("http://127.0.0.1:2379"); etcd.get("/test/key1").then([](etcd::Response response) { std::cout << response.value().as_string(); });// ... your code can continue here without any delay
Your lambda function should have a parameter of typeetcd::Response orpplx::task<etcd::Response>. In the latter case you can get the actualetcd::Responseobject with theget() function of the task. Calling get can raise exceptions so this is the wayhow you can catch the errors generated by the REST interface. Theget() call will not block inthis case since the response has been already arrived (we are inside the callback).
etcd::Clientetcd("http://127.0.0.1:2379"); etcd.get("/test/key1").then([](pplx::task<etcd::Response> response_task) {try { etcd::Response response = response_task.get();// can throw std::cout << response.value().as_string(); }catch (std::exceptionconst & ex) { std::cerr << ex.what(); } });// ... your code can continue here without any delay
Connecting to multiple endpoints is supported:
// multiple endpoints are separated by comma etcd::Clientetcd("http://a.com:2379,http://b.com:2379,http://c.com:2379");// or, separated semicolon etcd::Clientetcd("http://a.com:2379;http://b.com:2379;http://c.com:2379");
Connecting to IPv6 endpoints is supported:
etcd::Clientetcd("http://::1:2379");
Behind the screen, gRPC's load balancer is used and the round-robin strategy willbe used by default.
Etcdv3 authentication has beensupported. TheClient::Client could accept ausername andpassword as arguments and handlethe authentication properly.
etcd::Clientetcd("http://127.0.0.1:2379","root","root");
Or the etcd client can be constructed explicitly:
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379","root","root");
The default authentication token will be expired every 5 minutes (300 seconds), which is controlled bythe--auth-token-ttl flag of etcd. When constructing a etcd client, a customized TTL value is allow:
etcd::Clientetcd("http://127.0.0.1:2379","root","root",300);
Enabling v3 authentication requires a bit more work for older versions etcd (etcd 3.2.x and etcd 3.3.x).First you need to set theETCDCTL_API=3, then
- add a user, and type the password:
printf'root\nroot\n'| /usr/local/bin/etcdctl user add root
- enabling authentication:
/usr/local/bin/etcdctl authenable- disable authentication:
/usr/local/bin/etcdctl --user="root:root" auth disableEtcdtransport security and certificate basedauthentication have been supported as well. TheClient::Client could accept argumentsca ,cert andprivkey for CA cert, cert and private key files for the SSL/TLS transport and authentication.Note that the later argumentscert andprivkey could be empty strings or omitted if you just needsecure transport and don't enable certificate-based client authentication (using the--client-cert-autharguments when launching etcd server).
etcd::Clientetcd("https://127.0.0.1:2379","example.rootca.cert","example.cert","example.key","round_robin");
Or the etcd client can be constructed explicitly:
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379","example.rootca.cert","example.cert","example.key");
Using secure transport but not certificated-based client authentication:
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379","example.rootca.cert");
For more details about setup about security communication between etcd server and client, pleaserefer totransport security in etcd documentationandan example about setup etcd withtransport security using openssl.
We also provide a toolsetup-ca.sh as a helper for development and testing.
If you want to use multiplehttps:// endpoints, and you are working with self-signed certificates, youmay encountered errors like
error: 14: connections to all backends failingThat means your DNS have some problems with your DNS resolver and SSL authority, you could put a domainname (a host name) to theSANS field when self-signing your certificate, e.g,
"sans": [ "etcd", "127.0.0.1", "127.0.0.2", "127.0.0.3"],And pass atarget_name_override arguments toWithSSL,
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379,https://127.0.0.2:2479","example.rootca.cert","example.cert","example.key","etcd");
For more discussion about this feature, see also#87,grpc#20186 andgrpc#22119.
By default the etcd-cpp-apiv3 library will set the following arguments for transport layer
GRPC_ARG_MAX_SEND_MESSAGE_LENGTHtoINT_MAXGRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTHtoINT_MAX
Ifload balancer strategy is specified, the following argument will be set
GRPC_ARG_LB_POLICY_NAME
When transport security is enabled andtarget_name_override is specified when working with SSL, thefollowing argument will be set
GRPC_SSL_TARGET_NAME_OVERRIDE_ARG
Further, all variants of constructors foretcd::Client accepts an extragrpc::ChannelArguments argumentwhich can be used for fine-grained control the gRPC settings, e.g.,
grpc::ChannelArguments grpc_args; grpc_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS,2000); grpc_args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS,6000); grpc_args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS,1); etcd::Clientetcd("http://127.0.0.1:2379", grpc_args);
For more motivation and discussion about the above design, please refer toissue-103.
gRPC Timeout is long-standing missing pieces in the etcd-cpp-apiv3 library. The timeout has beensupported via aset_grpc_timeout interfaces on the client,
template<typename Rep = std::micro>voidset_grpc_timeout(std::chrono::duration<Rep>const &timeout)
Anystd::chrono::duration value can be used to set the grpc timeout, e.g.,
etcd.set_grpc_timeout(std::chrono::seconds(5));Note that the timeout value is the "timeout" when waiting for responses upon the gRPC channel, i.e.,CompletionQueue::AsyncNext.It doesn't means the timeout between issuing a.set() method getting theetcd::Response, as in the async mode the such a timeduration is unpredictable and the gRPC timeout should be enough to avoid deadly waiting (e.g., waiting for alock()).
Theclass etcd::Response may yield an error code and error message when error occurs,
interror_code()const; std::stringconst &error_message()const;boolis_ok()const;
The error code would be0 when succeed, otherwise the error code might be
externconstint ERROR_GRPC_OK;externconstint ERROR_GRPC_CANCELLED;externconstint ERROR_GRPC_UNKNOWN;externconstint ERROR_GRPC_INVALID_ARGUMENT;externconstint ERROR_GRPC_DEADLINE_EXCEEDED;externconstint ERROR_GRPC_NOT_FOUND;externconstint ERROR_GRPC_ALREADY_EXISTS;externconstint ERROR_GRPC_PERMISSION_DENIED;externconstint ERROR_GRPC_UNAUTHENTICATED;externconstint ERROR_GRPC_RESOURCE_EXHAUSTED;externconstint ERROR_GRPC_FAILED_PRECONDITION;externconstint ERROR_GRPC_ABORTED;externconstint ERROR_GRPC_OUT_OF_RANGE;externconstint ERROR_GRPC_UNIMPLEMENTED;externconstint ERROR_GRPC_INTERNAL;externconstint ERROR_GRPC_UNAVAILABLE;externconstint ERROR_GRPC_DATA_LOSS;externconstint ERROR_KEY_NOT_FOUND;externconstint ERROR_COMPARE_FAILED;externconstint ERROR_KEY_ALREADY_EXISTS;externconstint ERROR_ACTION_CANCELLED;
You can read a value with theget() method of the client instance. The only parameter is thekey to be read. If the read operation is successful then the value of the key can be acquired withthevalue() method of the response. Success of the operation can be checked with theis_ok() method of the response. In case of an error, theerror_code() anderror_message() methods can be called for some further detail.
Please note that there can be two kind of error situations. There can be some problem with thecommunication between the client and the etcd server. In this case the `get()``` method of theresponse task will throw an exception as shown above. If the communication is ok but there is someproblem with the content of the actual operation, like attempting to read a non-existing key then theresponse object will give you all the details. Let's see this in an example.
The Value object of the response also holds some extra information besides the string value of thekey. You can also get the index number of the creation and the last modification of this key withthecreated_index() and themodified_index() methods.
etcd::Clientetcd("http://127.0.0.1:2379"); pplx::task<etcd::Response> response_task = etcd.get("/test/key1");try { etcd::Response response = response_task.get();// can throwif (response.is_ok()) std::cout <<"successful read, value=" << response.value().as_string();else std::cout <<"operation failed, details:" << response.error_message(); }catch (std::exceptionconst & ex) { std::cerr <<"communication problem, details:" << ex.what(); }
You can put a key-value pair to etcd with the theput() method of the client instance. The onlyparameter is the key and value to be put
etcd::Clientetcd("http://127.0.0.1:2379"); pplx::task<etcd::Response> response_task = etcd.put("foo","bar");
Setting the value of a key can be done with theset() method of the client. You simply passthe key and the value as string parameters and you are done. The newly set value object can be askedfrom the response object exactly the same way as in case of the reading (with thevalue()method). This way you can check for example the index value of your modification. You can also checkwhat was the previous value that this operation was overwritten. You can do that with theprev_value() method of the response object.
etcd::Clientetcd("http://127.0.0.1:2379"); pplx::task<etcd::Response> response_task = etcd.set("/test/key1","42");try { etcd::Response response = response_task.get();if (response.is_ok()) std::cout <<"The new value is successfully set, previous value was" << response.prev_value().as_string();else std::cout <<"operation failed, details:" << response.error_message(); }catch (std::exceptionconst & ex) { std::cerr <<"communication problem, details:" << ex.what(); }
The set method creates a new leaf node if it weren't exists already or modifies an existing one.There are a couple of other modification methods that are executing the write operation only uponsome specific conditions.
add(key, value)creates a new value if it's key does not exists and returns a "Keyalready exists" error otherwise (error codeERROR_KEY_ALREADY_EXISTS)modify(key, value)modifies an already existing value or returns a "etcd-cpp-apiv3: key not found" errorotherwise (error codeKEY_NOT_FOUND)modify_if(key, value, old_value)modifies an already existing value but only if the previousvalue equals with old_value. If the values does not match returns with "Compare failed" error(codeERROR_COMPARE_FAILED)modify_if(key, value, old_index)modifies an already existing value but only if the index ofthe previous value equals with old_index. If the indices does not match returns with "Comparefailed" error (codeERROR_COMPARE_FAILED)
Values can be deleted with therm method passing the key to be deleted as a parameter. The keyshould point to an existing value. There are conditional variations for deletion too.
rm(std::string const& key)unconditionally deletes the given keyrm_if(key, old_value)deletes an already existing value but only if the previousvalue equals with old_value. If the values does not match returns with "Compare failed" error(codeERROR_COMPARE_FAILED)rm_if(key, old_index)deletes an already existing value but only if the index ofthe previous value equals with old_index. If the indices does not match returns with "Comparefailed" error (codeERROR_COMPARE_FAILED)
Directory nodes are not supported anymore in etcdv3. However, ls and rmdir will list/deletekeys defined by the prefix. mkdir method is removed since etcdv3 treats everything as keys.
Creating a directory:
Creating a directory is not supported anymore in etcdv3 cpp client. Users should remove theAPI from their code.
Listing a directory:
Listing directory in etcd3 cpp client will return all keys that matched the given prefixrecursively.
etcd.set("/test/key1","value1").wait(); etcd.set("/test/key2","value2").wait(); etcd.set("/test/key3","value3").wait(); etcd.set("/test/subdir/foo","foo").wait(); etcd::Response resp = etcd.ls("/test").get();
resp.key()will have the following values:/test/key1s/test/key2/test/key3/test/subdir/fooNote: Regarding the returned keys when listing a directory:
- In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything istreated as keys in etcdv3.
- While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory shouldbe created first before you can set "key1".
When you list a directory the response object's
keys()andvalues()methods givesyou a vector of key names and values. Thevalue()method with an integer parameter alsoreturns with the i-th element of the values vector, soresponse.values()[i] == response.value(i).etcd::Clientetcd("http://127.0.0.1:2379"); etcd::Response resp = etcd.ls("/test/new_dir").get();for (int i =0; i < resp.keys().size(); ++i) { std::cout << resp.keys(i); std::cout <<" =" << resp.value(i).as_string() << std::endl; }
etcd-cpp-apiv3 supports lists keys only without fetching values from etcd server:
etcd::Clientetcd("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); }
Removing directory:
If you want the delete recursively then you have to pass a second
trueparameterto rmdir and supply a key. This key will be treated as a prefix. All keys that match theprefix will be deleted. All deleted keys will be placed inresponse.values()andresponse.keys(). This parameter defaults tofalse.etcd::Clientetcd("http://127.0.0.1:2379"); etcd.set("/test/key1","foo"); etcd.set("/test/key2","bar"); etcd.set("/test/key3","foo_bar"); etcd::Response resp = etcd.rmdir("/test",true).get();for (int i =0; i < resp.keys().size(); ++i) { std::cout << resp.keys(i); std::cout <<" =" << resp.value(i).as_string() << std::endl; }
However, if recursive parameter is false, functionality will be the same as just deleting a key.The key supplied will NOT be treated as a prefix and will be treated as a normal key name.
Etcd itself support using arbitrary binary data as the key and value, i.e., the key and valuecan contain\NUL (\0) and not necessary NUL-terminated strings.std::string in C++ supportsembed\0 as well, but please note that when constructingstd::string from a C-style stringthe string will be terminated by the first\0 character. Rather, you need to use the constructorwith thecount parameter explicitly. When unpack astd::string that contains\0, you need.data(),
std::string key ="key-foo\0bar"; std::string value ="value-foo\0bar"; etcd.put(key, value).wait();
Etcd lock has been supported as follows:
etcd::Clientetcd("http://127.0.0.1:2379"); etcd.lock("/test/lock");
It will create a lease and a keep-alive job behind the screen, the lease will be revoked untilthe lock is unlocked.
Users can also feed their own lease directory for lock:
etcd::Clientetcd("http://127.0.0.1:2379"); etcd.lock_with_lease("/test/lock", lease_id);
Note that the arguments forunlock() is the the same key that used forlock(), but theresponse.lock_key() that return bylock():
etcd::Clientetcd("http://127.0.0.1:2379");// lockauto response = etcd.lock("/test/lock").get();// unlockauto _ = etcd.unlock(response.lock_key()).get();
Watching for a change is possible with thewatch() operation of the client. The watch methodsimply does not deliver a response object until the watched value changes in any way (modified ordeleted). When a change happens the returned result object will be the same as the result object ofthe modification operation. So if the change is triggered by a value change, thenresponse.action() will return "set",response.value() will hold the newvalue andresponse.prev_value() will contain the previous value. In case of a deleteresponse.action() will return "delete",response.value() will be empty and should not becalled at all andresponse.prev_value() will contain the deleted value.
As mentioned in the section "handling directory nodes", directory nodes are not supported anymorein etcdv3.
However it is still possible to watch a whole "directory subtree", or more specifically a set ofkeys that match the prefix, for changes with passingtrue to the secondrecursiveparameter ofwatch (this parameter defaults tofalse if omitted). In this case themodified value object'skey() method can be handy to determine what key is actually changed.Since this can be a long lasting operation you have to be prepared that is terminated by anexception and you have to restart the watch operation.
The watch also accepts an index parameter that specifies what is the first change we are interestedabout. Since etcd stores the last couple of modifications with this feature you can ensure that yourclient does not miss a single change.
Here is an example how you can watch continuously for changes of one specific key.
voidwatch_for_changes(){ etcd.watch("/nodes", index +1,true).then([this](pplx::task<etcd::Response> resp_task) {try { etcd::Response resp = resp_task.get(); index = resp.index(); std::cout << resp.action() <<"" << resp.value().as_string() << std::endl; }catch(...) {}watch_for_changes(); });}
At first glance it seems thatwatch_for_changes() calls itself on every value change but infact it just sends the asynchronous request, sets up a callback for the response and then returns. Thecallback is executed by some thread from the pplx library's thread pool and the callback (in thiscase a small lambda function actually) will callwatch_for_changes() again from there.
Users can watch a key indefinitely or until user cancels the watch. This can be done byinstantiating a Watcher class. The supplied callback function in Watcher class will becalled every time there is an event for the specified key. Watch stream will be cancelledeither by user implicitly callingCancel() or when watcher class is destroyed.
etcd::Watcherwatcher("http://127.0.0.1:2379","/test", printResponse); etcd.set("/test/key","42");/* print response will be called*/ etcd.set("/test/key","43");/* print response will be called*/ watcher.Cancel(); etcd.set("/test/key","43");/* print response will NOT be called, since watch is already cancelled*/
A watcher will be disconnected from etcd server in some cases, for some examples, the etcdserver is restarted, or the network is temporarily unavailable. It is users' responsibilityto decide if a watcher should re-connect to the etcd server.
Here is an example how users can make a watcher re-connect to server after disconnected.
// wait the client readyvoidwait_for_connection(etcd::Client &client) {// wait until the client connects to etcd serverwhile (!client.head().get().is_ok()) {sleep(1); }}// a loop for initialized a watcher with auto-restart capabilityvoidinitialize_watcher(const std::string& endpoints,const std::string& prefix, std::function<void(etcd::Response)> callback, std::shared_ptr<etcd::Watcher>& watcher) { etcd::Clientclient(endpoints);wait_for_connection(client);// Check if the failed one has been cancelled firstif (watcher && watcher->Cancelled()) { std::cout <<"watcher's reconnect loop been cancelled" << std::endl;return; } watcher.reset(newetcd::Watcher(client, prefix, callback,true));// Note that lambda requires `mutable`qualifier. watcher->Wait([endpoints, prefix, callback,/* By reference for renewing*/ &watcher](bool cancelled)mutable {if (cancelled) { std::cout <<"watcher's reconnect loop stopped as been cancelled" << std::endl;return; }initialize_watcher(endpoints, prefix, callback, watcher); });}
The functionalities can be used as
std::string endpoints ="http://127.0.0.1:2379";std::function<void(Response)> callback = printResponse;const std::string prefix ="/test/key";// the watcher initialized in this way will auto re-connect to etcdstd::shared_ptr<etcd::Watcher> watcher;initialize_watcher(endpoints, prefix, callback, watcher);
For a complete runnable example, see also./tst/RewatchTest.cpp. Notethat you shouldn't use the watcher itself inside theWait() callback as the callback will beinvoked in a separatedetached thread where the watcher may have been destroyed.
Users can request for lease which is governed by a time-to-live(TTL) value given by the user.Moreover, user can attached the lease to a key(s) by indicating the lease id inadd(),set(),modify() andmodify_if(). Also the ttl will that was granted by etcdserver will be indicated inttl().
etcd::Clientetcd("http://127.0.0.1:2379"); etcd::Response resp = etcd.leasegrant(60).get(); etcd.set("/test/key2","bar", resp.value().lease()); std::cout <<"ttl" << resp.value().ttl();
The lease can be revoked by
etcd.leaserevoke(resp.value().lease());
A lease can also be attached with aKeepAlive object at the creation time,
std::shared_ptr<etcd::KeepAlive> keepalive = etcd.leasekeepalive(60).get(); std::cout <<"lease id:" << keepalive->Lease();
The remaining time-to-live of a lease can be inspected by
etcd::Response resp2 = etcd.leasetimetolive(resp.value().lease()).get(); std::cout <<"ttl" << resp.value().ttl();Keep alive for leases is implemented using a separate classKeepAlive, which can be used as:
etcd::KeepAlivekeepalive(etcd, ttl, lease_id);It will perform a period keep-alive action before it is cancelled explicitly, or destructed implicitly.
KeepAlive may fails (e.g., when the etcd server stopped unexpectedly), the constructor ofKeepAlivecould accept a handler of typestd::function<std::exception_ptr> and the handler will be invokedwhen exception occurs during keeping it alive.
Note that the handler will invoked in a separated thread, not the thread where theKeepAlive objectis constructed.
std::function<void (std::exception_ptr)> handler = [](std::exception_ptr eptr) {try {if (eptr) {std::rethrow_exception(eptr); } }catch(const std::runtime_error& e) { std::cerr <<"Connection failure\"" << e.what() <<"\"\n"; }catch(const std::out_of_range& e) { std::cerr <<"Lease expiry\"" << e.what() <<"\"\n"; } }; etcd::KeepAlivekeepalive(etcd, handler, ttl, lease_id);
Without handler, the internal state can be checked viaKeepAlive::Check() and it will rethrowthe async exception when there are errors during keeping the lease alive.
Note that even withhandler, theKeepAlive::Check() still rethrow if there's an async exception.When the library is built with-fno-exceptions, thehandler argument and theCheck() methodwill abort the program when there are errors during keeping the lease alive.
Etcd v3'sTransaction APIs is supported via theetcdv3::Transaction interfaces. A set of convenient APIs are use to add operations to a transaction, e.g.,
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.,
etcdv3::Transaction txn;// setup the conditions txn.add_compare_value("/test/x1","1"); txn.add_compare_value("/test/x2","2");// or, compare the last modified revision txn.add_compare_mod("/test/x3",0);// not exists txn.add_compare_mod("/test/x4", etcdv3::CompareResult::GREATER,1234);// the modified revision is greater than 1234
High-level APIs (e.g.,compare_and_create,compare_and_swap) are also provided, e.g.,fetch-and-add operation can be implemented as
auto fetch_and_add = [](etcd::Client& client, std::stringconst& key) ->void {auto value =stoi(client.get(key).get().value().as_string());while (true) {auto txn =etcdv3::Transaction(); txn.setup_compare_and_swap(key,std::to_string(value),std::to_string(value +1)); etcd::Response resp = client.txn(txn).get();if (resp.is_ok()) {break; } value =stoi(resp.value().as_string()); } };
See full example of the usages of transaction APIs, please refer to./tst/TransactionTest.cpp,for full list of the transaction operation APIs, see./etcd/v3/Transaction.hpp.
Etcd v3'selection APIsare supported via the following interfaces,
pplx::task<Response>campaign(std::stringconst &name,int64_t lease_id, std::stringconst &value);pplx::task<Response>proclaim(std::stringconst &name,int64_t lease_id, std::stringconst &key,int64_t revision, std::stringconst &value);pplx::task<Response>leader(std::stringconst &name);std::unique_ptr<SyncClient::Observer>observe(std::stringconst &name);pplx::task<Response>resign(std::stringconst &name,int64_t lease_id, std::stringconst &key,int64_t revision);
Note that if grpc timeout is set,campaign() will return an timeout error response if itcannot acquire the election ownership within the timeout period. Otherwise will block untilbecome the leader.
TheObserver returned byobserve() can be use to monitor the changes of election ownership.The observer stream will be canceled when been destructed.
std::unique_ptr<etcd::SyncClient::Observer> observer = etcd.observe("test");// wait one change event, blocked execution etcd::Response resp = observer->WaitOnce();// wait many change events, blocked executionfor (size_t i =0; i < ...; ++i) { etcd::Response resp = observer->WaitOnce(); ... }// cancel the observer observer.reset(nullptr);
for more details, please refer toetcd/Client.hpp andtst/ElectionTest.cpp.
Theetcd-cpp-apiv3 library supports to be built with-fno-exceptions flag, controlled by thecmake optionBUILD_WITH_NO_EXCEPTIONS=ON/OFF (defaults toOFF).
When building with-fno-exceptions, the library will abort the program under certain circumstances,e.g., when calling.Check() method ofKeepAlive and there are errors during keeping the lease alive,
- Cancellation of asynchronous calls(except for watch)
This project is licensed under the BSD-3-Clause license - see theLICENSE.
About
The etcd-cpp-apiv3 is a C++ library for etcd's v3 client APIs, i.e., ETCDCTL_API=3.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.