Skip to main content

etcd_client/rpc/
watch.rs

1//! Etcd Watch RPC.
2
3use crate::caller::{ClientCaller, ClientCallerBuilder};
4pub use crate::rpc::pb::mvccpb::event::EventType;
5
6use crate::error::{Error, Result};
7use crate::intercept::InterceptedChannel;
8use crate::rpc::pb::etcdserverpb::watch_client::WatchClient as PbWatchClient;
9use crate::rpc::pb::etcdserverpb::watch_request::RequestUnion as WatchRequestUnion;
10use crate::rpc::pb::etcdserverpb::{
11    WatchCancelRequest, WatchCreateRequest, WatchProgressRequest, WatchRequest,
12    WatchResponse as PbWatchResponse,
13};
14use crate::rpc::pb::mvccpb::Event as PbEvent;
15use crate::rpc::{KeyRange, KeyValue, ResponseHeader};
16use std::pin::Pin;
17use std::task::{Context, Poll};
18use tokio::sync::mpsc::{channel, Sender};
19use tokio_stream::{wrappers::ReceiverStream, Stream};
20use tonic::Streaming;
21
22type Client = PbWatchClient<InterceptedChannel>;
23
24/// Client for watch operations.
25#[repr(transparent)]
26#[derive(Clone)]
27pub struct WatchClient {
28    inner: ClientCaller<Client>,
29}
30
31impl WatchClient {
32    /// Creates a watch client.
33    #[inline]
34    pub(crate) fn new(builder: ClientCallerBuilder) -> Self {
35        Self {
36            inner: builder.build(Client::new),
37        }
38    }
39
40    /// Limits the maximum size of a decoded message.
41    ///
42    /// Default: `4MB`
43    pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
44        self.inner = self
45            .inner
46            .with(|client| client.max_decoding_message_size(limit));
47        self
48    }
49
50    /// Watches for events happening or that have happened. Both input and output
51    /// are streams; the input stream is for creating and canceling watchers and the output
52    /// stream receives responses and events.
53    ///
54    /// One watch stream can watch on multiple key ranges, streaming events for several watches
55    /// are grouped by watch ID. The entire event history can be watched starting from the
56    /// last compaction revision.
57    pub async fn watch(
58        &mut self,
59        key: impl Into<Vec<u8>>,
60        options: Option<WatchOptions>,
61    ) -> Result<WatchStream> {
62        async fn watch_impl(client: &mut Client, options: WatchOptions) -> Result<WatchStream> {
63            let (request_sender, request_receiver) = channel::<WatchRequest>(100);
64            request_sender
65                .send(options.into())
66                .await
67                .map_err(|e| Error::WatchError(e.to_string()))?;
68            let request_stream = ReceiverStream::new(request_receiver);
69            let stream = client.watch(request_stream).await?.into_inner();
70            Ok(WatchStream::new(request_sender, stream))
71        }
72        self.inner
73            .do_call(options.unwrap_or_default().with_key(key), watch_impl)
74            .await
75    }
76}
77
78/// Options for `Watch` operation.
79#[derive(Debug, Default, Clone)]
80pub struct WatchOptions {
81    req: WatchCreateRequest,
82    key_range: KeyRange,
83}
84
85impl WatchOptions {
86    /// Sets key.
87    #[inline]
88    pub fn with_key(mut self, key: impl Into<Vec<u8>>) -> Self {
89        self.key_range.with_key(key);
90        self
91    }
92
93    /// Creates a new `WatchOptions`.
94    #[inline]
95    pub const fn new() -> Self {
96        Self {
97            req: WatchCreateRequest {
98                key: Vec::new(),
99                range_end: Vec::new(),
100                start_revision: 0,
101                progress_notify: false,
102                filters: Vec::new(),
103                prev_kv: false,
104                watch_id: 0,
105                fragment: false,
106            },
107            key_range: KeyRange::new(),
108        }
109    }
110
111    /// Sets the end of the range `[key, end)` to watch.
112    ///
113    /// If `end` is not given, only the key argument is watched.
114    ///
115    /// If `end` is equal to `\0`, all keys greater than or equal to the key argument are watched.
116    #[inline]
117    pub fn with_range(mut self, end: impl Into<Vec<u8>>) -> Self {
118        self.key_range.with_range(end);
119        self
120    }
121
122    /// Watches all keys >= key.
123    #[inline]
124    pub fn with_from_key(mut self) -> Self {
125        self.key_range.with_from_key();
126        self
127    }
128
129    /// Watches all keys prefixed with key.
130    #[inline]
131    pub fn with_prefix(mut self) -> Self {
132        self.key_range.with_prefix();
133        self
134    }
135
136    /// Watches all keys.
137    #[inline]
138    pub fn with_all_keys(mut self) -> Self {
139        self.key_range.with_all_keys();
140        self
141    }
142
143    /// Sets the revision to watch from (inclusive). No `start_revision` is "now".
144    #[inline]
145    pub const fn with_start_revision(mut self, revision: i64) -> Self {
146        self.req.start_revision = revision;
147        self
148    }
149
150    /// `progress_notify` is set so that the etcd server will periodically send a `WatchResponse` with
151    /// no events to the new watcher if there are no recent events. It is useful when clients
152    /// wish to recover a disconnected watcher starting from a recent known revision.
153    /// The etcd server may decide how often it will send notifications based on current load.
154    #[inline]
155    pub const fn with_progress_notify(mut self) -> Self {
156        self.req.progress_notify = true;
157        self
158    }
159
160    /// Filter the events at server side before it sends back to the watcher.
161    #[inline]
162    pub fn with_filters(mut self, filters: impl Into<Vec<WatchFilterType>>) -> Self {
163        self.req.filters = filters.into().into_iter().map(|f| f as i32).collect();
164        self
165    }
166
167    /// If `prev_kv` is set, created watcher gets the previous KV before the event happens.
168    /// If the previous KV is already compacted, nothing will be returned.
169    #[inline]
170    pub const fn with_prev_key(mut self) -> Self {
171        self.req.prev_kv = true;
172        self
173    }
174
175    /// If `watch_id` is provided and non-zero, it will be assigned to this watcher.
176    /// Since creating a watcher in etcd is not a synchronous operation,
177    /// this can be used ensure that ordering is correct when creating multiple
178    /// watchers on the same stream. Creating a watcher with an ID already in
179    /// use on the stream will cause an error to be returned.
180    #[inline]
181    pub const fn with_watch_id(mut self, watch_id: i64) -> Self {
182        self.req.watch_id = watch_id;
183        self
184    }
185
186    /// Enables splitting large revisions into multiple watch responses.
187    #[inline]
188    pub const fn with_fragment(mut self) -> Self {
189        self.req.fragment = true;
190        self
191    }
192}
193
194impl From<WatchOptions> for WatchCreateRequest {
195    #[inline]
196    fn from(mut options: WatchOptions) -> Self {
197        let (key, range_end) = options.key_range.build();
198        options.req.key = key;
199        options.req.range_end = range_end;
200        options.req
201    }
202}
203
204impl From<WatchOptions> for WatchRequest {
205    #[inline]
206    fn from(options: WatchOptions) -> Self {
207        Self {
208            request_union: Some(WatchRequestUnion::CreateRequest(options.into())),
209        }
210    }
211}
212
213impl From<WatchCancelRequest> for WatchRequest {
214    #[inline]
215    fn from(req: WatchCancelRequest) -> Self {
216        Self {
217            request_union: Some(WatchRequestUnion::CancelRequest(req)),
218        }
219    }
220}
221
222impl From<WatchProgressRequest> for WatchRequest {
223    #[inline]
224    fn from(req: WatchProgressRequest) -> Self {
225        Self {
226            request_union: Some(WatchRequestUnion::ProgressRequest(req)),
227        }
228    }
229}
230
231/// Watch filter type.
232#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
233#[repr(i32)]
234pub enum WatchFilterType {
235    /// Filter out put event.
236    NoPut = 0,
237    /// Filter out delete event.
238    NoDelete = 1,
239}
240
241/// Response for `Watch` operation.
242#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
243#[derive(Debug, Clone)]
244#[repr(transparent)]
245pub struct WatchResponse(PbWatchResponse);
246
247impl WatchResponse {
248    /// Creates a new `WatchResponse`.
249    #[inline]
250    const fn new(resp: PbWatchResponse) -> Self {
251        Self(resp)
252    }
253
254    /// Watch response header.
255    #[inline]
256    pub fn header(&self) -> Option<&ResponseHeader> {
257        self.0.header.as_ref().map(From::from)
258    }
259
260    /// Takes the header out of the response, leaving a [`None`] in its place.
261    #[inline]
262    pub fn take_header(&mut self) -> Option<ResponseHeader> {
263        self.0.header.take().map(ResponseHeader::new)
264    }
265
266    /// The ID of the watcher that corresponds to the response.
267    #[inline]
268    pub const fn watch_id(&self) -> i64 {
269        self.0.watch_id
270    }
271
272    /// created is set to true if the response is for a create watch request.
273    /// The client should record the watch_id and expect to receive events for
274    /// the created watcher from the same stream.
275    /// All events sent to the created watcher will attach with the same watch_id.
276    #[inline]
277    pub const fn created(&self) -> bool {
278        self.0.created
279    }
280
281    /// `canceled` is set to true if the response is for a cancel watch request.
282    /// No further events will be sent to the canceled watcher.
283    #[inline]
284    pub const fn canceled(&self) -> bool {
285        self.0.canceled
286    }
287
288    /// `compact_revision` is set to the minimum index if a watcher tries to watch
289    /// at a compacted index.
290    ///
291    /// This happens when creating a watcher at a compacted revision or the watcher cannot
292    /// catch up with the progress of the key-value store.
293    ///
294    /// The client should treat the watcher as canceled and should not try to create any
295    /// watcher with the same start_revision again.
296    #[inline]
297    pub const fn compact_revision(&self) -> i64 {
298        self.0.compact_revision
299    }
300
301    /// Indicates the reason for canceling the watcher.
302    #[inline]
303    pub fn cancel_reason(&self) -> &str {
304        &self.0.cancel_reason
305    }
306
307    /// Events happened on the watched keys.
308    #[inline]
309    pub fn events(&self) -> &[Event] {
310        unsafe { &*(self.0.events.as_slice() as *const _ as *const [Event]) }
311    }
312}
313
314/// Watching event.
315#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
316#[derive(Debug, Clone)]
317#[repr(transparent)]
318pub struct Event(PbEvent);
319
320impl Event {
321    /// The kind of event. If type is a `Put`, it indicates
322    /// new data has been stored to the key. If type is a `Delete`,
323    /// it indicates the key was deleted.
324    #[inline]
325    pub fn event_type(&self) -> EventType {
326        match self.0.r#type {
327            0 => EventType::Put,
328            1 => EventType::Delete,
329            i => panic!("unknown event {i}"),
330        }
331    }
332
333    /// The KeyValue for the event.
334    /// A `Put` event contains current kv pair.
335    /// A `Put` event with `kv.version()==1` indicates the creation of a key.
336    /// A `Delete` event contains the deleted key with
337    /// its modification revision set to the revision of deletion.
338    #[inline]
339    pub fn kv(&self) -> Option<&KeyValue> {
340        self.0.kv.as_ref().map(From::from)
341    }
342
343    /// The key-value pair before the event happens.
344    #[inline]
345    pub fn prev_kv(&self) -> Option<&KeyValue> {
346        self.0.prev_kv.as_ref().map(From::from)
347    }
348}
349
350/// The watching handle.
351#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
352#[derive(Debug)]
353pub struct WatchStream {
354    request_sender: WatchRequestSender,
355    response_stream: WatchResponseStream,
356}
357
358/// The sender for sending watch requests in the existing watch stream.
359///
360/// The watch request can be sending using the [`WatchStream`] or the [`WatchRequestSender`].
361///
362/// The [`WatchRequestSender`] can be obtained by splitting the [`WatchStream`] using the
363/// [`WatchStream::split`] method.
364#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
365#[derive(Debug)]
366pub struct WatchRequestSender(Sender<WatchRequest>);
367
368/// The response stream for receiving watch responses in the existing watch stream.
369///
370/// The watch response can be receiving using the [`WatchStream`] or the [`WatchResponseStream`].
371///
372/// The [`WatchResponseStream`] can be obtained by splitting the [`WatchStream`] using the
373/// [`WatchStream::split`] method.
374#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
375#[derive(Debug)]
376pub struct WatchResponseStream(Streaming<PbWatchResponse>);
377
378impl WatchResponseStream {
379    /// Receive [`WatchResponse`] from this watch response stream.
380    ///
381    /// See also [`WatchStream::message`] for receiving watch response from the [`WatchStream`].
382    #[inline]
383    pub async fn message(&mut self) -> Result<Option<WatchResponse>> {
384        self.0
385            .message()
386            .await
387            .map(|resp| resp.map(WatchResponse::new))
388            .map_err(From::from)
389    }
390}
391
392impl WatchStream {
393    /// Creates a new `WatchStream`.
394    #[inline]
395    const fn new(
396        request_sender: Sender<WatchRequest>,
397        response_stream: Streaming<PbWatchResponse>,
398    ) -> Self {
399        Self {
400            request_sender: WatchRequestSender(request_sender),
401            response_stream: WatchResponseStream(response_stream),
402        }
403    }
404
405    /// Send watch request in the existing watch stream.
406    #[inline]
407    pub async fn watch(
408        &mut self,
409        key: impl Into<Vec<u8>>,
410        options: Option<WatchOptions>,
411    ) -> Result<()> {
412        self.request_sender.watch(key, options).await
413    }
414
415    /// Cancels watch by specified `watch_id`.
416    #[inline]
417    pub async fn cancel(&mut self, watch_id: i64) -> Result<()> {
418        self.request_sender.cancel(watch_id).await
419    }
420
421    /// Requests a watch stream progress status be sent in the watch response stream as soon as
422    /// possible.
423    #[inline]
424    pub async fn request_progress(&mut self) -> Result<()> {
425        self.request_sender.request_progress().await
426    }
427
428    /// Receive [`WatchResponse`] from this watch stream.
429    #[inline]
430    pub async fn message(&mut self) -> Result<Option<WatchResponse>> {
431        self.response_stream.message().await
432    }
433
434    /// Splits the watch stream into a request sender and a response receiver (stream).
435    pub fn split(self) -> (WatchRequestSender, WatchResponseStream) {
436        (self.request_sender, self.response_stream)
437    }
438}
439
440impl WatchRequestSender {
441    /// Send watch request in the existing watch stream.
442    #[inline]
443    async fn send(&mut self, req: WatchRequest) -> Result<()> {
444        self.0
445            .send(req)
446            .await
447            .map_err(|e| Error::WatchError(e.to_string()))
448    }
449
450    /// Send watch request in the existing watch stream.
451    ///
452    /// See also [`WatchStream::watch`] for sending watch request using [`WatchStream`].
453    #[inline]
454    pub async fn watch(
455        &mut self,
456        key: impl Into<Vec<u8>>,
457        options: Option<WatchOptions>,
458    ) -> Result<()> {
459        self.send(options.unwrap_or_default().with_key(key).into())
460            .await
461    }
462
463    /// Cancels watch by specified `watch_id`.
464    ///
465    ///
466    /// See also [`WatchStream::cancel`] for canceling watch using [`WatchStream`].
467    #[inline]
468    pub async fn cancel(&mut self, watch_id: i64) -> Result<()> {
469        let req = WatchCancelRequest { watch_id };
470        self.send(req.into()).await
471    }
472
473    /// Requests a watch stream progress status be sent in the watch response stream as soon as
474    /// possible.
475    ///
476    /// See also [`WatchStream::request_progress`] for requesting watch stream progress status
477    /// using [`WatchStream`].
478    #[inline]
479    pub async fn request_progress(&mut self) -> Result<()> {
480        let req = WatchProgressRequest {};
481        self.send(req.into()).await
482    }
483}
484
485impl Stream for WatchResponseStream {
486    type Item = Result<WatchResponse>;
487
488    #[inline]
489    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
490        Pin::new(&mut self.get_mut().0)
491            .poll_next(cx)
492            .map(|t| match t {
493                Some(Ok(resp)) => Some(Ok(WatchResponse::new(resp))),
494                Some(Err(e)) => Some(Err(From::from(e))),
495                None => None,
496            })
497    }
498}
499
500impl Stream for WatchStream {
501    type Item = Result<WatchResponse>;
502
503    #[inline]
504    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
505        Pin::new(&mut self.get_mut().response_stream).poll_next(cx)
506    }
507}