Skip to main content

etcd_client/rpc/
lease.rs

1//! Etcd Lease RPC.
2
3use crate::caller::{ClientCaller, ClientCallerBuilder};
4use crate::error::Result;
5use crate::intercept::InterceptedChannel;
6use crate::rpc::pb::etcdserverpb::lease_client::LeaseClient as PbLeaseClient;
7use crate::rpc::pb::etcdserverpb::{
8    LeaseGrantRequest as PbLeaseGrantRequest, LeaseGrantResponse as PbLeaseGrantResponse,
9    LeaseKeepAliveRequest as PbLeaseKeepAliveRequest,
10    LeaseKeepAliveResponse as PbLeaseKeepAliveResponse, LeaseLeasesRequest as PbLeaseLeasesRequest,
11    LeaseLeasesResponse as PbLeaseLeasesResponse, LeaseRevokeRequest as PbLeaseRevokeRequest,
12    LeaseRevokeResponse as PbLeaseRevokeResponse, LeaseStatus as PbLeaseStatus,
13    LeaseTimeToLiveRequest as PbLeaseTimeToLiveRequest,
14    LeaseTimeToLiveResponse as PbLeaseTimeToLiveResponse,
15};
16use crate::rpc::ResponseHeader;
17use crate::vec::VecExt;
18use crate::Error;
19use std::pin::Pin;
20use std::task::{Context, Poll};
21use tokio::sync::mpsc::{channel, Sender};
22use tokio_stream::wrappers::ReceiverStream;
23use tokio_stream::Stream;
24use tonic::{IntoRequest, Request, Streaming};
25
26type Client = PbLeaseClient<InterceptedChannel>;
27
28/// Client for lease operations.
29#[repr(transparent)]
30#[derive(Clone)]
31pub struct LeaseClient {
32    inner: ClientCaller<Client>,
33}
34
35impl LeaseClient {
36    /// Creates a `LeaseClient`.
37    #[inline]
38    pub(crate) fn new(builder: ClientCallerBuilder) -> Self {
39        Self {
40            inner: builder.build(Client::new),
41        }
42    }
43
44    /// Creates a lease which expires if the server does not receive a keepAlive
45    /// within a given time to live period. All keys attached to the lease will be expired and
46    /// deleted if the lease expires. Each expired key generates a delete event in the event history.
47    #[inline]
48    pub async fn grant(
49        &mut self,
50        ttl: i64,
51        options: Option<LeaseGrantOptions>,
52    ) -> Result<LeaseGrantResponse> {
53        async fn grant_impl(
54            client: &mut Client,
55            options: LeaseGrantOptions,
56        ) -> Result<LeaseGrantResponse> {
57            Ok(LeaseGrantResponse::new(
58                client.lease_grant(options).await?.into_inner(),
59            ))
60        }
61        self.inner
62            .do_call(options.unwrap_or_default().with_ttl(ttl), grant_impl)
63            .await
64    }
65
66    /// Revokes a lease. All keys attached to the lease will expire and be deleted.
67    #[inline]
68    pub async fn revoke(&mut self, id: i64) -> Result<LeaseRevokeResponse> {
69        async fn revoke_impl(
70            client: &mut Client,
71            options: LeaseRevokeOptions,
72        ) -> Result<LeaseRevokeResponse> {
73            let resp = client.lease_revoke(options).await?.into_inner();
74            Ok(LeaseRevokeResponse::new(resp))
75        }
76
77        self.inner
78            .do_call(LeaseRevokeOptions::new().with_id(id), revoke_impl)
79            .await
80    }
81
82    /// Keeps the lease alive by streaming keep alive requests from the client
83    /// to the server and streaming keep alive responses from the server to the client.
84    #[inline]
85    pub async fn keep_alive(&mut self, id: i64) -> Result<(LeaseKeeper, LeaseKeepAliveStream)> {
86        async fn keep_alive_impl(
87            client: &mut Client,
88            options: PbLeaseKeepAliveRequest,
89        ) -> Result<(
90            Sender<PbLeaseKeepAliveRequest>,
91            Streaming<PbLeaseKeepAliveResponse>,
92        )> {
93            let (sender, receiver) = channel::<PbLeaseKeepAliveRequest>(100);
94            sender
95                .send(options)
96                .await
97                .map_err(|e| Error::LeaseKeepAliveError(e.to_string()))?;
98
99            let receiver = ReceiverStream::new(receiver);
100            let resp = client.lease_keep_alive(receiver).await?.into_inner();
101            Ok((sender, resp))
102        }
103
104        let (sender, mut stream) = self
105            .inner
106            .do_call(
107                LeaseKeepAliveOptions::new().with_id(id).into(),
108                keep_alive_impl,
109            )
110            .await?;
111
112        let id = match stream.message().await? {
113            Some(resp) => {
114                if resp.ttl <= 0 {
115                    return Err(Error::LeaseKeepAliveError("lease not found".to_string()));
116                }
117                resp.id
118            }
119            None => {
120                return Err(Error::WatchError(
121                    "failed to create lease keeper".to_string(),
122                ));
123            }
124        };
125
126        Ok((
127            LeaseKeeper::new(id, sender),
128            LeaseKeepAliveStream::new(stream),
129        ))
130    }
131
132    /// Retrieves lease information.
133    #[inline]
134    pub async fn time_to_live(
135        &mut self,
136        id: i64,
137        options: Option<LeaseTimeToLiveOptions>,
138    ) -> Result<LeaseTimeToLiveResponse> {
139        async fn time_to_live_impl(
140            client: &mut Client,
141            options: LeaseTimeToLiveOptions,
142        ) -> Result<LeaseTimeToLiveResponse> {
143            let resp = client.lease_time_to_live(options).await?.into_inner();
144            Ok(LeaseTimeToLiveResponse::new(resp))
145        }
146
147        self.inner
148            .do_call(options.unwrap_or_default().with_id(id), time_to_live_impl)
149            .await
150    }
151
152    /// Lists all existing leases.
153    #[inline]
154    pub async fn leases(&mut self) -> Result<LeaseLeasesResponse> {
155        async fn leases_impl(
156            client: &mut Client,
157            req: PbLeaseLeasesRequest,
158        ) -> Result<LeaseLeasesResponse> {
159            let resp = client.lease_leases(req).await?.into_inner();
160            Ok(LeaseLeasesResponse::new(resp))
161        }
162
163        self.inner
164            .do_call(PbLeaseLeasesRequest {}, leases_impl)
165            .await
166    }
167}
168
169/// Options for `Grant` operation.
170#[derive(Debug, Default, Clone)]
171#[repr(transparent)]
172pub struct LeaseGrantOptions(PbLeaseGrantRequest);
173
174impl LeaseGrantOptions {
175    /// Set ttl
176    #[inline]
177    const fn with_ttl(mut self, ttl: i64) -> Self {
178        self.0.ttl = ttl;
179        self
180    }
181
182    /// Set id
183    #[inline]
184    pub const fn with_id(mut self, id: i64) -> Self {
185        self.0.id = id;
186        self
187    }
188
189    /// Creates a `LeaseGrantOptions`.
190    #[inline]
191    pub const fn new() -> Self {
192        Self(PbLeaseGrantRequest { ttl: 0, id: 0 })
193    }
194}
195
196impl From<LeaseGrantOptions> for PbLeaseGrantRequest {
197    #[inline]
198    fn from(options: LeaseGrantOptions) -> Self {
199        options.0
200    }
201}
202
203impl IntoRequest<PbLeaseGrantRequest> for LeaseGrantOptions {
204    #[inline]
205    fn into_request(self) -> Request<PbLeaseGrantRequest> {
206        Request::new(self.into())
207    }
208}
209
210/// Response for `Grant` operation.
211#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
212#[derive(Debug, Clone)]
213#[repr(transparent)]
214pub struct LeaseGrantResponse(PbLeaseGrantResponse);
215
216impl LeaseGrantResponse {
217    /// Creates a new `LeaseGrantResponse` from pb lease grant response.
218    #[inline]
219    const fn new(resp: PbLeaseGrantResponse) -> Self {
220        Self(resp)
221    }
222
223    /// Get response header.
224    #[inline]
225    pub fn header(&self) -> Option<&ResponseHeader> {
226        self.0.header.as_ref().map(From::from)
227    }
228
229    /// Takes the header out of the response, leaving a [`None`] in its place.
230    #[inline]
231    pub fn take_header(&mut self) -> Option<ResponseHeader> {
232        self.0.header.take().map(ResponseHeader::new)
233    }
234
235    /// TTL is the server chosen lease time-to-live in seconds
236    #[inline]
237    pub const fn ttl(&self) -> i64 {
238        self.0.ttl
239    }
240
241    /// ID is the lease ID for the granted lease.
242    #[inline]
243    pub const fn id(&self) -> i64 {
244        self.0.id
245    }
246
247    /// Error message if return error.
248    #[inline]
249    pub fn error(&self) -> &str {
250        &self.0.error
251    }
252}
253
254/// Options for `Revoke` operation.
255#[derive(Debug, Default, Clone)]
256#[repr(transparent)]
257struct LeaseRevokeOptions(PbLeaseRevokeRequest);
258
259impl LeaseRevokeOptions {
260    /// Set id
261    #[inline]
262    fn with_id(mut self, id: i64) -> Self {
263        self.0.id = id;
264        self
265    }
266
267    /// Creates a `LeaseRevokeOptions`.
268    #[inline]
269    pub const fn new() -> Self {
270        Self(PbLeaseRevokeRequest { id: 0 })
271    }
272}
273
274impl From<LeaseRevokeOptions> for PbLeaseRevokeRequest {
275    #[inline]
276    fn from(options: LeaseRevokeOptions) -> Self {
277        options.0
278    }
279}
280
281impl IntoRequest<PbLeaseRevokeRequest> for LeaseRevokeOptions {
282    #[inline]
283    fn into_request(self) -> Request<PbLeaseRevokeRequest> {
284        Request::new(self.into())
285    }
286}
287
288/// Response for `Revoke` operation.
289#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
290#[derive(Debug, Clone)]
291#[repr(transparent)]
292pub struct LeaseRevokeResponse(PbLeaseRevokeResponse);
293
294impl LeaseRevokeResponse {
295    /// Creates a new `LeaseRevokeResponse` from pb lease revoke response.
296    #[inline]
297    const fn new(resp: PbLeaseRevokeResponse) -> Self {
298        Self(resp)
299    }
300
301    /// Get response header.
302    #[inline]
303    pub fn header(&self) -> Option<&ResponseHeader> {
304        self.0.header.as_ref().map(From::from)
305    }
306
307    /// Takes the header out of the response, leaving a [`None`] in its place.
308    #[inline]
309    pub fn take_header(&mut self) -> Option<ResponseHeader> {
310        self.0.header.take().map(ResponseHeader::new)
311    }
312}
313
314/// Options for `KeepAlive` operation.
315#[derive(Debug, Default, Clone)]
316#[repr(transparent)]
317struct LeaseKeepAliveOptions(PbLeaseKeepAliveRequest);
318
319impl LeaseKeepAliveOptions {
320    /// Set id
321    #[inline]
322    fn with_id(mut self, id: i64) -> Self {
323        self.0.id = id;
324        self
325    }
326
327    /// Creates a `LeaseKeepAliveOptions`.
328    #[inline]
329    pub const fn new() -> Self {
330        Self(PbLeaseKeepAliveRequest { id: 0 })
331    }
332}
333
334impl From<LeaseKeepAliveOptions> for PbLeaseKeepAliveRequest {
335    #[inline]
336    fn from(options: LeaseKeepAliveOptions) -> Self {
337        options.0
338    }
339}
340
341impl IntoRequest<PbLeaseKeepAliveRequest> for LeaseKeepAliveOptions {
342    #[inline]
343    fn into_request(self) -> Request<PbLeaseKeepAliveRequest> {
344        Request::new(self.into())
345    }
346}
347
348/// Response for `KeepAlive` operation.
349#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
350#[derive(Debug, Clone)]
351#[repr(transparent)]
352pub struct LeaseKeepAliveResponse(PbLeaseKeepAliveResponse);
353
354impl LeaseKeepAliveResponse {
355    /// Creates a new `LeaseKeepAliveResponse` from pb lease KeepAlive response.
356    #[inline]
357    const fn new(resp: PbLeaseKeepAliveResponse) -> Self {
358        Self(resp)
359    }
360
361    /// Get response header.
362    #[inline]
363    pub fn header(&self) -> Option<&ResponseHeader> {
364        self.0.header.as_ref().map(From::from)
365    }
366
367    /// Takes the header out of the response, leaving a [`None`] in its place.
368    #[inline]
369    pub fn take_header(&mut self) -> Option<ResponseHeader> {
370        self.0.header.take().map(ResponseHeader::new)
371    }
372
373    /// TTL is the new time-to-live for the lease.
374    #[inline]
375    pub const fn ttl(&self) -> i64 {
376        self.0.ttl
377    }
378
379    /// ID is the lease ID for the keep alive request.
380    #[inline]
381    pub const fn id(&self) -> i64 {
382        self.0.id
383    }
384}
385
386/// Options for `TimeToLive` operation.
387#[derive(Debug, Default, Clone)]
388#[repr(transparent)]
389pub struct LeaseTimeToLiveOptions(PbLeaseTimeToLiveRequest);
390
391impl LeaseTimeToLiveOptions {
392    /// ID is the lease ID for the lease.
393    #[inline]
394    const fn with_id(mut self, id: i64) -> Self {
395        self.0.id = id;
396        self
397    }
398
399    /// Keys is true to query all the keys attached to this lease.
400    #[inline]
401    pub const fn with_keys(mut self) -> Self {
402        self.0.keys = true;
403        self
404    }
405
406    /// Creates a `LeaseTimeToLiveOptions`.
407    #[inline]
408    pub const fn new() -> Self {
409        Self(PbLeaseTimeToLiveRequest { id: 0, keys: false })
410    }
411}
412
413impl From<LeaseTimeToLiveOptions> for PbLeaseTimeToLiveRequest {
414    #[inline]
415    fn from(options: LeaseTimeToLiveOptions) -> Self {
416        options.0
417    }
418}
419
420impl IntoRequest<PbLeaseTimeToLiveRequest> for LeaseTimeToLiveOptions {
421    #[inline]
422    fn into_request(self) -> Request<PbLeaseTimeToLiveRequest> {
423        Request::new(self.into())
424    }
425}
426
427/// Response for `TimeToLive` operation.
428#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
429#[derive(Debug, Clone)]
430#[repr(transparent)]
431pub struct LeaseTimeToLiveResponse(PbLeaseTimeToLiveResponse);
432
433impl LeaseTimeToLiveResponse {
434    /// Creates a new `LeaseTimeToLiveResponse` from pb lease TimeToLive response.
435    #[inline]
436    const fn new(resp: PbLeaseTimeToLiveResponse) -> Self {
437        Self(resp)
438    }
439
440    /// Get response header.
441    #[inline]
442    pub fn header(&self) -> Option<&ResponseHeader> {
443        self.0.header.as_ref().map(From::from)
444    }
445
446    /// Takes the header out of the response, leaving a [`None`] in its place.
447    #[inline]
448    pub fn take_header(&mut self) -> Option<ResponseHeader> {
449        self.0.header.take().map(ResponseHeader::new)
450    }
451
452    /// TTL is the remaining TTL in seconds for the lease; the lease will expire in under TTL+1 seconds.
453    #[inline]
454    pub const fn ttl(&self) -> i64 {
455        self.0.ttl
456    }
457
458    /// ID is the lease ID from the keep alive request.
459    #[inline]
460    pub const fn id(&self) -> i64 {
461        self.0.id
462    }
463
464    /// GrantedTTL is the initial granted time in seconds upon lease creation/renewal.
465    #[inline]
466    pub const fn granted_ttl(&self) -> i64 {
467        self.0.granted_ttl
468    }
469
470    /// Keys is the list of keys attached to this lease.
471    #[inline]
472    pub fn keys(&self) -> &[Vec<u8>] {
473        &self.0.keys
474    }
475
476    #[inline]
477    pub(crate) fn strip_keys_prefix(&mut self, prefix: &[u8]) {
478        self.0.keys.iter_mut().for_each(|key| {
479            key.strip_key_prefix(prefix);
480        });
481    }
482}
483
484/// Response for `Leases` operation.
485#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
486#[derive(Debug, Clone)]
487#[repr(transparent)]
488pub struct LeaseLeasesResponse(PbLeaseLeasesResponse);
489
490impl LeaseLeasesResponse {
491    /// Creates a new `LeaseLeasesResponse` from pb lease Leases response.
492    #[inline]
493    const fn new(resp: PbLeaseLeasesResponse) -> Self {
494        Self(resp)
495    }
496
497    /// Get response header.
498    #[inline]
499    pub fn header(&self) -> Option<&ResponseHeader> {
500        self.0.header.as_ref().map(From::from)
501    }
502
503    /// Takes the header out of the response, leaving a [`None`] in its place.
504    #[inline]
505    pub fn take_header(&mut self) -> Option<ResponseHeader> {
506        self.0.header.take().map(ResponseHeader::new)
507    }
508
509    /// Get leases status
510    #[inline]
511    pub fn leases(&self) -> &[LeaseStatus] {
512        unsafe { &*(self.0.leases.as_slice() as *const _ as *const [LeaseStatus]) }
513    }
514}
515
516/// Lease status.
517#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
518#[derive(Debug, Clone, PartialEq)]
519#[repr(transparent)]
520pub struct LeaseStatus(PbLeaseStatus);
521
522impl LeaseStatus {
523    /// Lease id.
524    #[inline]
525    pub const fn id(&self) -> i64 {
526        self.0.id
527    }
528}
529
530impl From<&PbLeaseStatus> for &LeaseStatus {
531    #[inline]
532    fn from(src: &PbLeaseStatus) -> Self {
533        unsafe { &*(src as *const _ as *const LeaseStatus) }
534    }
535}
536
537/// The lease keep alive handle.
538#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
539#[derive(Debug)]
540pub struct LeaseKeeper {
541    id: i64,
542    sender: Sender<PbLeaseKeepAliveRequest>,
543}
544
545impl LeaseKeeper {
546    /// Creates a new `LeaseKeeper`.
547    #[inline]
548    const fn new(id: i64, sender: Sender<PbLeaseKeepAliveRequest>) -> Self {
549        Self { id, sender }
550    }
551
552    /// The lease id which user want to keep alive.
553    #[inline]
554    pub const fn id(&self) -> i64 {
555        self.id
556    }
557
558    /// Sends a keep alive request and receive response
559    #[inline]
560    pub async fn keep_alive(&mut self) -> Result<()> {
561        self.sender
562            .send(LeaseKeepAliveOptions::new().with_id(self.id).into())
563            .await
564            .map_err(|e| Error::LeaseKeepAliveError(e.to_string()))
565    }
566}
567
568/// The lease keep alive response stream.
569#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
570#[derive(Debug)]
571pub struct LeaseKeepAliveStream {
572    stream: Streaming<PbLeaseKeepAliveResponse>,
573}
574
575impl LeaseKeepAliveStream {
576    /// Creates a new `LeaseKeepAliveStream`.
577    #[inline]
578    const fn new(stream: Streaming<PbLeaseKeepAliveResponse>) -> Self {
579        Self { stream }
580    }
581
582    /// Fetches the next message from this stream.
583    #[inline]
584    pub async fn message(&mut self) -> Result<Option<LeaseKeepAliveResponse>> {
585        match self.stream.message().await? {
586            Some(resp) => Ok(Some(LeaseKeepAliveResponse::new(resp))),
587            None => Ok(None),
588        }
589    }
590}
591
592impl Stream for LeaseKeepAliveStream {
593    type Item = Result<LeaseKeepAliveResponse>;
594
595    #[inline]
596    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
597        Pin::new(&mut self.get_mut().stream)
598            .poll_next(cx)
599            .map(|t| match t {
600                Some(Ok(resp)) => Some(Ok(LeaseKeepAliveResponse::new(resp))),
601                Some(Err(e)) => Some(Err(From::from(e))),
602                None => None,
603            })
604    }
605}