risingwave_meta/barrier/
notifier.rs1use anyhow::anyhow;
16use futures::future::join_all;
17use tokio::sync::oneshot;
18
19use crate::{MetaError, MetaResult};
20
21pub(crate) type CollectionReceiver = oneshot::Receiver<MetaResult<()>>;
22pub(crate) type StartReceiver = oneshot::Receiver<MetaResult<Vec<CollectionReceiver>>>;
23
24pub(crate) async fn wait_collection(receivers: Vec<CollectionReceiver>) -> MetaResult<()> {
25 let mut first_error = None;
26 for result in join_all(receivers).await {
27 let result = match result {
28 Ok(result) => result,
29 Err(_) => Err(anyhow!("failed to collect barrier: notifier dropped").into()),
30 };
31 if let Err(err) = result
32 && first_error.is_none()
33 {
34 first_error = Some(err);
35 }
36 }
37 match first_error {
38 Some(err) => Err(err),
39 None => Ok(()),
40 }
41}
42
43#[derive(Debug)]
45pub(crate) struct Notifier {
46 started: oneshot::Sender<MetaResult<Vec<CollectionReceiver>>>,
47}
48
49impl Notifier {
50 pub fn new() -> (Self, StartReceiver) {
51 let (started, receiver) = oneshot::channel();
52 (Self { started }, receiver)
53 }
54
55 pub fn start(self) -> NotifierStarter {
56 NotifierStarter {
57 started: self.started,
58 pending_collection: vec![],
59 }
60 }
61
62 pub fn notify_start_failed(self, err: MetaError) {
63 self.started.send(Err(err)).ok();
64 }
65}
66
67#[derive(Debug)]
69pub(crate) struct NotifierStarter {
70 started: oneshot::Sender<MetaResult<Vec<CollectionReceiver>>>,
71 pending_collection: Vec<CollectionReceiver>,
72}
73
74impl NotifierStarter {
75 pub fn add_notify(&mut self) -> CollectionNotifier {
76 let (collected, receiver) = oneshot::channel();
77 self.pending_collection.push(receiver);
78 CollectionNotifier { collected }
79 }
80
81 pub fn started(self) {
82 self.started.send(Ok(self.pending_collection)).ok();
83 }
84
85 pub fn notify_start_failed(self, err: MetaError) {
86 self.started.send(Err(err)).ok();
87 }
88}
89
90#[derive(Debug)]
92pub(crate) struct CollectionNotifier {
93 collected: oneshot::Sender<MetaResult<()>>,
94}
95
96impl CollectionNotifier {
97 pub fn notify_collected(self) {
98 self.collected.send(Ok(())).ok();
99 }
100
101 pub fn notify_collection_failed(self, err: MetaError) {
103 self.collected.send(Err(err)).ok();
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use tokio::time::{Duration, timeout};
110
111 use super::*;
112
113 #[tokio::test]
114 async fn test_zero_collection_notifier() {
115 let (notifier, started_rx) = Notifier::new();
116 notifier.start().started();
117
118 let receivers = started_rx.await.unwrap().unwrap();
119 assert!(receivers.is_empty());
120 wait_collection(receivers).await.unwrap();
121 }
122
123 #[tokio::test]
124 async fn test_one_collection_notifier() {
125 let (notifier, started_rx) = Notifier::new();
126 let mut start = notifier.start();
127 let collection = start.add_notify();
128 start.started();
129
130 collection.notify_collected();
131 let receivers = started_rx.await.unwrap().unwrap();
132 wait_collection(receivers).await.unwrap();
133 }
134
135 #[tokio::test]
136 async fn test_wait_all_collection_notifiers() {
137 let (notifier, started_rx) = Notifier::new();
138 let mut start = notifier.start();
139 let first = start.add_notify();
140 let second = start.add_notify();
141 start.started();
142
143 let receivers = started_rx.await.unwrap().unwrap();
144 let mut wait = Box::pin(wait_collection(receivers));
145 first.notify_collected();
146 assert!(timeout(Duration::from_millis(10), &mut wait).await.is_err());
147 second.notify_collected();
148 wait.await.unwrap();
149 }
150
151 #[tokio::test]
152 async fn test_waits_for_all_collection_notifiers_after_failure() {
153 let (notifier, started_rx) = Notifier::new();
154 let mut start = notifier.start();
155 let first = start.add_notify();
156 let second = start.add_notify();
157 start.started();
158
159 let receivers = started_rx.await.unwrap().unwrap();
160 let mut wait = Box::pin(wait_collection(receivers));
161 first.notify_collection_failed(anyhow!("first part failed").into());
162 assert!(timeout(Duration::from_millis(10), &mut wait).await.is_err());
163 second.notify_collection_failed(anyhow!("second part failed").into());
164 let err = wait.await.unwrap_err();
165 assert!(err.to_string().contains("first part failed"));
166 }
167
168 #[tokio::test]
169 async fn test_dropped_collection_notifier_fails() {
170 let (notifier, started_rx) = Notifier::new();
171 let mut start = notifier.start();
172 let dropped = start.add_notify();
173 start.started();
174 drop(dropped);
175
176 let receivers = started_rx.await.unwrap().unwrap();
177 assert!(wait_collection(receivers).await.is_err());
178 }
179
180 #[tokio::test]
181 async fn test_start_failure() {
182 let (notifier, started_rx) = Notifier::new();
183 notifier.notify_start_failed(anyhow!("start failed").into());
184 assert!(started_rx.await.unwrap().is_err());
185 }
186
187 #[tokio::test]
188 async fn test_start_failure_after_entering_start_phase() {
189 let (notifier, started_rx) = Notifier::new();
190 let mut start = notifier.start();
191 let collection = start.add_notify();
192 start.notify_start_failed(anyhow!("start failed").into());
193
194 assert!(started_rx.await.unwrap().is_err());
195 collection.notify_collected();
196 }
197
198 #[tokio::test]
199 async fn test_dropped_notifier_cancels_start() {
200 let (notifier, started_rx) = Notifier::new();
201 drop(notifier);
202 assert!(started_rx.await.is_err());
203 }
204}