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    /// or if the start_revision has already been compacted.
283    /// No further events will be sent to the canceled watcher.
284    #[inline]
285    pub const fn canceled(&self) -> bool {
286        self.0.canceled
287    }
288
289    /// `compact_revision` is set to the minimum index if a watcher tries to watch
290    /// at a compacted index.
291    ///
292    /// This happens when creating a watcher at a compacted revision or the watcher cannot
293    /// catch up with the progress of the key-value store.
294    ///
295    /// The client should treat the watcher as canceled and should not try to create any
296    /// watcher with the same start_revision again.
297    #[inline]
298    pub const fn compact_revision(&self) -> i64 {
299        self.0.compact_revision
300    }
301
302    /// Indicates the reason for canceling the watcher.
303    #[inline]
304    pub fn cancel_reason(&self) -> &str {
305        &self.0.cancel_reason
306    }
307
308    /// Events happened on the watched keys.
309    #[inline]
310    pub fn events(&self) -> &[Event] {
311        unsafe { &*(self.0.events.as_slice() as *const _ as *const [Event]) }
312    }
313}
314
315/// Watching event.
316#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
317#[derive(Debug, Clone)]
318#[repr(transparent)]
319pub struct Event(PbEvent);
320
321impl Event {
322    /// The kind of event. If type is a `Put`, it indicates
323    /// new data has been stored to the key. If type is a `Delete`,
324    /// it indicates the key was deleted.
325    #[inline]
326    pub fn event_type(&self) -> EventType {
327        match self.0.r#type {
328            0 => EventType::Put,
329            1 => EventType::Delete,
330            i => panic!("unknown event {i}"),
331        }
332    }
333
334    /// The KeyValue for the event.
335    /// A `Put` event contains current kv pair.
336    /// A `Put` event with `kv.version()==1` indicates the creation of a key.
337    /// A `Delete` event contains the deleted key with
338    /// its modification revision set to the revision of deletion.
339    #[inline]
340    pub fn kv(&self) -> Option<&KeyValue> {
341        self.0.kv.as_ref().map(From::from)
342    }
343
344    /// The key-value pair before the event happens.
345    #[inline]
346    pub fn prev_kv(&self) -> Option<&KeyValue> {
347        self.0.prev_kv.as_ref().map(From::from)
348    }
349}
350
351/// The watching handle.
352#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
353#[derive(Debug)]
354pub struct WatchStream {
355    request_sender: WatchRequestSender,
356    response_stream: WatchResponseStream,
357}
358
359/// The sender for sending watch requests in the existing watch stream.
360///
361/// The watch request can be sending using the [`WatchStream`] or the [`WatchRequestSender`].
362///
363/// The [`WatchRequestSender`] can be obtained by splitting the [`WatchStream`] using the
364/// [`WatchStream::split`] method.
365#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
366#[derive(Debug)]
367pub struct WatchRequestSender(Sender<WatchRequest>);
368
369/// The response stream for receiving watch responses in the existing watch stream.
370///
371/// The watch response can be receiving using the [`WatchStream`] or the [`WatchResponseStream`].
372///
373/// The [`WatchResponseStream`] can be obtained by splitting the [`WatchStream`] using the
374/// [`WatchStream::split`] method.
375#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
376#[derive(Debug)]
377pub struct WatchResponseStream(Streaming<PbWatchResponse>);
378
379impl WatchResponseStream {
380    /// Receive [`WatchResponse`] from this watch response stream.
381    ///
382    /// See also [`WatchStream::message`] for receiving watch response from the [`WatchStream`].
383    #[inline]
384    pub async fn message(&mut self) -> Result<Option<WatchResponse>> {
385        self.0
386            .message()
387            .await
388            .map(|resp| resp.map(WatchResponse::new))
389            .map_err(From::from)
390    }
391}
392
393impl WatchStream {
394    /// Creates a new `WatchStream`.
395    #[inline]
396    const fn new(
397        request_sender: Sender<WatchRequest>,
398        response_stream: Streaming<PbWatchResponse>,
399    ) -> Self {
400        Self {
401            request_sender: WatchRequestSender(request_sender),
402            response_stream: WatchResponseStream(response_stream),
403        }
404    }
405
406    /// Send watch request in the existing watch stream.
407    #[inline]
408    pub async fn watch(
409        &mut self,
410        key: impl Into<Vec<u8>>,
411        options: Option<WatchOptions>,
412    ) -> Result<()> {
413        self.request_sender.watch(key, options).await
414    }
415
416    /// Cancels watch by specified `watch_id`.
417    #[inline]
418    pub async fn cancel(&mut self, watch_id: i64) -> Result<()> {
419        self.request_sender.cancel(watch_id).await
420    }
421
422    /// Requests a watch stream progress status be sent in the watch response stream as soon as
423    /// possible.
424    #[inline]
425    pub async fn request_progress(&mut self) -> Result<()> {
426        self.request_sender.request_progress().await
427    }
428
429    /// Receive [`WatchResponse`] from this watch stream.
430    #[inline]
431    pub async fn message(&mut self) -> Result<Option<WatchResponse>> {
432        self.response_stream.message().await
433    }
434
435    /// Splits the watch stream into a request sender and a response receiver (stream).
436    pub fn split(self) -> (WatchRequestSender, WatchResponseStream) {
437        (self.request_sender, self.response_stream)
438    }
439}
440
441impl WatchRequestSender {
442    /// Send watch request in the existing watch stream.
443    #[inline]
444    async fn send(&mut self, req: WatchRequest) -> Result<()> {
445        self.0
446            .send(req)
447            .await
448            .map_err(|e| Error::WatchError(e.to_string()))
449    }
450
451    /// Send watch request in the existing watch stream.
452    ///
453    /// See also [`WatchStream::watch`] for sending watch request using [`WatchStream`].
454    #[inline]
455    pub async fn watch(
456        &mut self,
457        key: impl Into<Vec<u8>>,
458        options: Option<WatchOptions>,
459    ) -> Result<()> {
460        self.send(options.unwrap_or_default().with_key(key).into())
461            .await
462    }
463
464    /// Cancels watch by specified `watch_id`.
465    ///
466    ///
467    /// See also [`WatchStream::cancel`] for canceling watch using [`WatchStream`].
468    #[inline]
469    pub async fn cancel(&mut self, watch_id: i64) -> Result<()> {
470        let req = WatchCancelRequest { watch_id };
471        self.send(req.into()).await
472    }
473
474    /// Requests a watch stream progress status be sent in the watch response stream as soon as
475    /// possible.
476    ///
477    /// See also [`WatchStream::request_progress`] for requesting watch stream progress status
478    /// using [`WatchStream`].
479    #[inline]
480    pub async fn request_progress(&mut self) -> Result<()> {
481        let req = WatchProgressRequest {};
482        self.send(req.into()).await
483    }
484}
485
486impl Stream for WatchResponseStream {
487    type Item = Result<WatchResponse>;
488
489    #[inline]
490    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
491        Pin::new(&mut self.get_mut().0)
492            .poll_next(cx)
493            .map(|t| match t {
494                Some(Ok(resp)) => Some(Ok(WatchResponse::new(resp))),
495                Some(Err(e)) => Some(Err(From::from(e))),
496                None => None,
497            })
498    }
499}
500
501impl Stream for WatchStream {
502    type Item = Result<WatchResponse>;
503
504    #[inline]
505    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
506        Pin::new(&mut self.get_mut().response_stream).poll_next(cx)
507    }
508}