risingwave_common_service/
observer_manager.rs1use std::time::Duration;
16
17use risingwave_pb::meta::subscribe_response::Info;
18use risingwave_pb::meta::{SubscribeResponse, SubscribeType};
19use risingwave_rpc_client::MetaClient;
20use risingwave_rpc_client::error::RpcError;
21use thiserror_ext::AsReport;
22use tokio::task::JoinHandle;
23use tonic::{Status, Streaming};
24
25pub struct ObserverManager<T: NotificationClient, S: ObserverState> {
29 rx: T::Channel,
30 client: T,
31 observer_states: S,
32}
33
34pub trait ObserverState: Send + 'static {
35 fn subscribe_type() -> SubscribeType;
36 fn handle_notification(&mut self, resp: SubscribeResponse);
38
39 fn handle_initialization_notification(&mut self, resp: SubscribeResponse);
41}
42
43impl<S: ObserverState> ObserverManager<RpcNotificationClient, S> {
44 pub async fn new_with_meta_client(meta_client: MetaClient, observer_states: S) -> Self {
45 let client = RpcNotificationClient { meta_client };
46 Self::new(client, observer_states).await
47 }
48}
49
50#[derive(thiserror::Error, Debug)]
52pub enum ObserverError {
53 #[error("notification channel closed")]
54 ChannelClosed,
55
56 #[error(transparent)]
57 Rpc(
58 #[from]
59 #[backtrace]
60 RpcError,
61 ),
62}
63
64impl From<tonic::Status> for ObserverError {
65 fn from(status: tonic::Status) -> Self {
66 Self::Rpc(RpcError::from_meta_status(status))
67 }
68}
69
70impl<T, S> ObserverManager<T, S>
71where
72 T: NotificationClient,
73 S: ObserverState,
74{
75 pub async fn new(client: T, observer_states: S) -> Self {
76 let rx = client.subscribe(S::subscribe_type()).await.unwrap();
77 Self {
78 rx,
79 client,
80 observer_states,
81 }
82 }
83
84 async fn wait_init_notification(&mut self) -> Result<(), ObserverError> {
87 let mut notification_vec = Vec::new();
88 let init_notification = loop {
89 match self.rx.message().await? {
91 Some(notification) => {
92 if !matches!(notification.info.as_ref().unwrap(), &Info::Snapshot(_)) {
93 notification_vec.push(notification);
94 } else {
95 break notification;
96 }
97 }
98 None => return Err(ObserverError::ChannelClosed),
99 }
100 };
101
102 let Info::Snapshot(info) = init_notification.info.as_ref().unwrap() else {
103 unreachable!();
104 };
105
106 notification_vec.retain_mut(|notification| match notification.info.as_ref().unwrap() {
107 Info::Database(_)
108 | Info::Schema(_)
109 | Info::ObjectGroup(_)
110 | Info::User(_)
111 | Info::Connection(_)
112 | Info::Secret(_)
113 | Info::Function(_) => {
114 notification.version > info.version.as_ref().unwrap().catalog_version
115 }
116 Info::Node(_) => {
117 notification.version > info.version.as_ref().unwrap().worker_node_version
118 }
119 Info::HummockVersionDeltas(version_delta) => {
120 version_delta.version_deltas[0].id > info.hummock_version.as_ref().unwrap().id
121 }
122 Info::MetaBackupManifestId(_) => true,
123 Info::SystemParams(_) | Info::SessionParam(_) => true,
124 Info::Snapshot(_) | Info::HummockWriteLimits(_) => unreachable!(),
125 Info::HummockStats(_) => true,
126 Info::Recovery(_) => true,
127 Info::ClusterResource(_) => true,
128 Info::StreamingWorkerSlotMapping(_) => {
129 notification.version
130 > info
131 .version
132 .as_ref()
133 .unwrap()
134 .streaming_worker_slot_mapping_version
135 }
136 Info::ServingWorkerSlotMappings(_) => true,
137 Info::TableRefillRuntimeConfig(_) => {
138 notification.version
139 > info
140 .table_refill_runtime_config
141 .as_ref()
142 .map(|config| config.version)
143 .unwrap_or_default()
144 }
145 });
146
147 self.observer_states
148 .handle_initialization_notification(init_notification);
149
150 for notification in notification_vec {
151 self.observer_states.handle_notification(notification);
152 }
153
154 Ok(())
155 }
156
157 pub async fn start(mut self) -> JoinHandle<()> {
160 if let Err(err) = self.wait_init_notification().await {
161 tracing::warn!(error = %err.as_report(), "Receives meta's notification err");
162 self.re_subscribe().await;
163 }
164
165 tokio::spawn(async move {
166 loop {
167 match self.rx.message().await {
168 Ok(resp) => {
169 if resp.is_none() {
170 tracing::warn!("Stream of notification terminated.");
171 self.re_subscribe().await;
172 continue;
173 }
174 self.observer_states.handle_notification(resp.unwrap());
175 }
176 Err(err) => {
177 tracing::warn!(error = %err.as_report(), "Receives meta's notification err");
178 self.re_subscribe().await;
179 }
180 }
181 }
182 })
183 }
184
185 async fn re_subscribe(&mut self) {
187 loop {
188 match self.client.subscribe(S::subscribe_type()).await {
189 Ok(rx) => {
190 tracing::debug!("re-subscribe success");
191 self.rx = rx;
192 match self.wait_init_notification().await {
193 Err(err) => {
194 tracing::warn!(error = %err.as_report(), "Receives meta's notification err");
195 tokio::time::sleep(RE_SUBSCRIBE_RETRY_INTERVAL).await;
196 continue;
197 }
198 _ => {
199 break;
200 }
201 }
202 }
203 Err(_) => {
204 tokio::time::sleep(RE_SUBSCRIBE_RETRY_INTERVAL).await;
205 }
206 }
207 }
208 }
209}
210
211const RE_SUBSCRIBE_RETRY_INTERVAL: Duration = Duration::from_millis(100);
212
213#[async_trait::async_trait]
214pub trait Channel: Send + 'static {
215 type Item;
216 async fn message(&mut self) -> std::result::Result<Option<Self::Item>, Status>;
217}
218
219#[async_trait::async_trait]
220impl<T: Send + 'static> Channel for Streaming<T> {
221 type Item = T;
222
223 async fn message(&mut self) -> std::result::Result<Option<T>, Status> {
224 self.message().await
225 }
226}
227
228#[async_trait::async_trait]
229pub trait NotificationClient: Send + Sync + 'static {
230 type Channel: Channel<Item = SubscribeResponse>;
231 async fn subscribe(
232 &self,
233 subscribe_type: SubscribeType,
234 ) -> Result<Self::Channel, ObserverError>;
235}
236
237pub struct RpcNotificationClient {
238 meta_client: MetaClient,
239}
240
241impl RpcNotificationClient {
242 pub fn new(meta_client: MetaClient) -> Self {
243 Self { meta_client }
244 }
245}
246
247#[async_trait::async_trait]
248impl NotificationClient for RpcNotificationClient {
249 type Channel = Streaming<SubscribeResponse>;
250
251 async fn subscribe(
252 &self,
253 subscribe_type: SubscribeType,
254 ) -> Result<Self::Channel, ObserverError> {
255 self.meta_client
256 .subscribe(subscribe_type)
257 .await
258 .map_err(Into::into)
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use std::collections::VecDeque;
265
266 use risingwave_pb::meta::meta_snapshot::SnapshotVersion;
267 use risingwave_pb::meta::subscribe_response::Info;
268 use risingwave_pb::meta::{
269 MetaSnapshot, SubscribeResponse, SubscribeType, TableRefillRuntimeConfig,
270 };
271
272 use super::*;
273
274 struct TestChannel {
275 messages: VecDeque<SubscribeResponse>,
276 }
277
278 #[async_trait::async_trait]
279 impl Channel for TestChannel {
280 type Item = SubscribeResponse;
281
282 async fn message(&mut self) -> std::result::Result<Option<Self::Item>, Status> {
283 Ok(self.messages.pop_front())
284 }
285 }
286
287 struct TestClient;
288
289 #[async_trait::async_trait]
290 impl NotificationClient for TestClient {
291 type Channel = TestChannel;
292
293 async fn subscribe(
294 &self,
295 _subscribe_type: SubscribeType,
296 ) -> Result<Self::Channel, ObserverError> {
297 Ok(TestChannel {
298 messages: VecDeque::new(),
299 })
300 }
301 }
302
303 #[derive(Default)]
304 struct TestObserverState {
305 initialized: bool,
306 table_refill_runtime_config_versions: Vec<u64>,
307 database_notifications: usize,
308 }
309
310 impl ObserverState for TestObserverState {
311 fn subscribe_type() -> SubscribeType {
312 SubscribeType::Hummock
313 }
314
315 fn handle_notification(&mut self, resp: SubscribeResponse) {
316 match resp.info.unwrap() {
317 Info::TableRefillRuntimeConfig(_) => {
318 self.table_refill_runtime_config_versions.push(resp.version)
319 }
320 Info::Database(_) => self.database_notifications += 1,
321 info => panic!("unexpected notification: {info:?}"),
322 }
323 }
324
325 fn handle_initialization_notification(&mut self, resp: SubscribeResponse) {
326 assert!(matches!(resp.info, Some(Info::Snapshot(_))));
327 self.initialized = true;
328 }
329 }
330
331 fn notification(version: u64, info: Info) -> SubscribeResponse {
332 SubscribeResponse {
333 info: Some(info),
334 version,
335 ..Default::default()
336 }
337 }
338
339 #[tokio::test]
340 async fn test_filter_stale_table_refill_runtime_config_before_snapshot() {
341 let snapshot = SubscribeResponse {
342 info: Some(Info::Snapshot(MetaSnapshot {
343 version: Some(SnapshotVersion {
344 catalog_version: 1,
345 worker_node_version: 1,
346 streaming_worker_slot_mapping_version: 1,
347 }),
348 table_refill_runtime_config: Some(TableRefillRuntimeConfig {
349 version: 10,
350 ..Default::default()
351 }),
352 ..Default::default()
353 })),
354 ..Default::default()
355 };
356 let rx = TestChannel {
357 messages: VecDeque::from([
358 notification(
359 9,
360 Info::TableRefillRuntimeConfig(TableRefillRuntimeConfig {
361 version: 9,
362 ..Default::default()
363 }),
364 ),
365 notification(
366 11,
367 Info::TableRefillRuntimeConfig(TableRefillRuntimeConfig {
368 version: 11,
369 ..Default::default()
370 }),
371 ),
372 notification(1, Info::Database(Default::default())),
373 snapshot,
374 ]),
375 };
376 let mut observer_manager = ObserverManager {
377 rx,
378 client: TestClient,
379 observer_states: TestObserverState::default(),
380 };
381
382 observer_manager.wait_init_notification().await.unwrap();
383
384 assert!(observer_manager.observer_states.initialized);
385 assert_eq!(
386 observer_manager
387 .observer_states
388 .table_refill_runtime_config_versions,
389 vec![11]
390 );
391 assert_eq!(observer_manager.observer_states.database_notifications, 0);
392 }
393}