1use std::pin::Pin;
16use std::task::{Context, Poll};
17
18use catalog::process_manager::Ticket;
19use common_recordbatch::adapter::RecordBatchMetrics;
20use common_recordbatch::{OrderOption, RecordBatch, RecordBatchStream, SendableRecordBatchStream};
21use datatypes::schema::SchemaRef;
22use futures::Stream;
23
24pub struct CancellableStreamWrapper {
25 inner: SendableRecordBatchStream,
26 ticket: Ticket,
27 cancel_on_drop: bool,
28 finished: bool,
29}
30
31impl Unpin for CancellableStreamWrapper {}
32
33impl CancellableStreamWrapper {
34 pub fn new(stream: SendableRecordBatchStream, ticket: Ticket) -> Self {
35 Self {
36 inner: stream,
37 ticket,
38 cancel_on_drop: false,
39 finished: false,
40 }
41 }
42
43 pub fn new_cancel_on_drop(stream: SendableRecordBatchStream, ticket: Ticket) -> Self {
44 Self {
45 inner: stream,
46 ticket,
47 cancel_on_drop: true,
48 finished: false,
49 }
50 }
51}
52
53impl Stream for CancellableStreamWrapper {
54 type Item = common_recordbatch::error::Result<RecordBatch>;
55
56 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
57 let this = &mut *self;
58 if this.ticket.cancellation_handle.is_cancelled() {
59 return Poll::Ready(Some(common_recordbatch::error::StreamCancelledSnafu.fail()));
60 }
61
62 if let Poll::Ready(res) = Pin::new(&mut this.inner).poll_next(cx) {
63 if res.is_none() {
64 this.finished = true;
65 }
66 return Poll::Ready(res);
67 }
68
69 this.ticket.cancellation_handle.waker().register(cx.waker());
71 if this.ticket.cancellation_handle.is_cancelled() {
73 return Poll::Ready(Some(common_recordbatch::error::StreamCancelledSnafu.fail()));
74 }
75 Poll::Pending
76 }
77}
78
79impl Drop for CancellableStreamWrapper {
80 fn drop(&mut self) {
81 if self.cancel_on_drop && !self.finished {
82 self.ticket.cancellation_handle.cancel();
83 }
84 }
85}
86
87impl RecordBatchStream for CancellableStreamWrapper {
88 fn schema(&self) -> SchemaRef {
89 self.inner.schema()
90 }
91
92 fn output_ordering(&self) -> Option<&[OrderOption]> {
93 self.inner.output_ordering()
94 }
95
96 fn metrics(&self) -> Option<RecordBatchMetrics> {
97 self.inner.metrics()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use std::pin::Pin;
104 use std::sync::Arc;
105 use std::task::{Context, Poll};
106 use std::time::Duration;
107
108 use catalog::process_manager::ProcessManager;
109 use common_recordbatch::adapter::RecordBatchMetrics;
110 use common_recordbatch::{OrderOption, RecordBatch, RecordBatchStream};
111 use datatypes::data_type::ConcreteDataType;
112 use datatypes::prelude::VectorRef;
113 use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
114 use datatypes::vectors::Int32Vector;
115 use futures::{Stream, StreamExt};
116 use tokio::time::{sleep, timeout};
117
118 use super::CancellableStreamWrapper;
119
120 struct MockRecordBatchStream {
122 schema: SchemaRef,
123 batches: Vec<common_recordbatch::error::Result<RecordBatch>>,
124 current: usize,
125 delay: Option<Duration>,
126 }
127
128 impl MockRecordBatchStream {
129 fn new(batches: Vec<common_recordbatch::error::Result<RecordBatch>>) -> Self {
130 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
131 "test_col",
132 ConcreteDataType::int32_datatype(),
133 false,
134 )]));
135
136 Self {
137 schema,
138 batches,
139 current: 0,
140 delay: None,
141 }
142 }
143
144 fn with_delay(mut self, delay: Duration) -> Self {
145 self.delay = Some(delay);
146 self
147 }
148 }
149
150 impl Stream for MockRecordBatchStream {
151 type Item = common_recordbatch::error::Result<RecordBatch>;
152
153 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
154 if let Some(delay) = self.delay {
155 let waker = cx.waker().clone();
157 let delay_clone = delay;
158 tokio::spawn(async move {
159 sleep(delay_clone).await;
160 waker.wake();
161 });
162 self.delay = None; return Poll::Pending;
164 }
165
166 if self.current >= self.batches.len() {
167 return Poll::Ready(None);
168 }
169
170 let batch = self.batches[self.current].as_ref().unwrap().clone();
171 self.current += 1;
172 Poll::Ready(Some(Ok(batch)))
173 }
174 }
175
176 impl RecordBatchStream for MockRecordBatchStream {
177 fn schema(&self) -> SchemaRef {
178 self.schema.clone()
179 }
180
181 fn output_ordering(&self) -> Option<&[OrderOption]> {
182 None
183 }
184
185 fn metrics(&self) -> Option<RecordBatchMetrics> {
186 None
187 }
188 }
189
190 fn create_test_batch() -> RecordBatch {
191 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
192 "test_col",
193 ConcreteDataType::int32_datatype(),
194 false,
195 )]));
196 RecordBatch::new(
197 schema,
198 vec![Arc::new(Int32Vector::from_values(0..3)) as VectorRef],
199 )
200 .unwrap()
201 }
202
203 #[tokio::test]
204 async fn test_stream_completes_normally() {
205 let batch = create_test_batch();
206 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch.clone())]);
207 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
208 let ticket = process_manager.register_query(
209 "catalog".to_string(),
210 vec![],
211 "query".to_string(),
212 "client".to_string(),
213 None,
214 None,
215 );
216
217 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
218
219 let result = cancellable_stream.next().await;
220 assert!(result.is_some());
221 assert!(result.unwrap().is_ok());
222
223 let end_result = cancellable_stream.next().await;
224 assert!(end_result.is_none());
225 }
226
227 #[tokio::test]
228 async fn test_stream_cancelled_before_start() {
229 let batch = create_test_batch();
230 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch)]);
231 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
232 let ticket = process_manager.register_query(
233 "catalog".to_string(),
234 vec![],
235 "query".to_string(),
236 "client".to_string(),
237 None,
238 None,
239 );
240
241 ticket.cancellation_handle.cancel();
243
244 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
245
246 let result = cancellable_stream.next().await;
247 assert!(result.is_some());
248 assert!(result.unwrap().is_err());
249 }
250
251 #[tokio::test]
252 async fn test_stream_cancelled_during_execution() {
253 let batch = create_test_batch();
254 let mock_stream =
255 MockRecordBatchStream::new(vec![Ok(batch)]).with_delay(Duration::from_millis(100));
256 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
257 let ticket = process_manager.register_query(
258 "catalog".to_string(),
259 vec![],
260 "query".to_string(),
261 "client".to_string(),
262 None,
263 None,
264 );
265 let cancellation_handle = ticket.cancellation_handle.clone();
266
267 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
268
269 tokio::spawn(async move {
271 sleep(Duration::from_millis(50)).await;
272 cancellation_handle.cancel();
273 });
274
275 let result = cancellable_stream.next().await;
276 assert!(result.is_some());
277 assert!(result.unwrap().is_err());
278 }
279
280 #[tokio::test]
281 async fn test_stream_completes_before_cancellation() {
282 let batch = create_test_batch();
283 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch.clone())]);
284 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
285 let ticket = process_manager.register_query(
286 "catalog".to_string(),
287 vec![],
288 "query".to_string(),
289 "client".to_string(),
290 None,
291 None,
292 );
293 let cancellation_handle = ticket.cancellation_handle.clone();
294
295 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
296
297 tokio::spawn(async move {
299 sleep(Duration::from_millis(100)).await;
300 cancellation_handle.cancel();
301 });
302
303 let result = cancellable_stream.next().await;
304 assert!(result.is_some());
305 assert!(result.unwrap().is_ok());
306 }
307
308 #[tokio::test]
309 async fn test_multiple_batches() {
310 let batch1 = create_test_batch();
311 let batch2 = create_test_batch();
312 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch1), Ok(batch2)]);
313 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
314 let ticket = process_manager.register_query(
315 "catalog".to_string(),
316 vec![],
317 "query".to_string(),
318 "client".to_string(),
319 None,
320 None,
321 );
322
323 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
324
325 let result1 = cancellable_stream.next().await;
327 assert!(result1.is_some());
328 assert!(result1.unwrap().is_ok());
329
330 let result2 = cancellable_stream.next().await;
332 assert!(result2.is_some());
333 assert!(result2.unwrap().is_ok());
334
335 let end_result = cancellable_stream.next().await;
337 assert!(end_result.is_none());
338 }
339
340 #[tokio::test]
341 async fn test_record_batch_stream_methods() {
342 let batch = create_test_batch();
343 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch)]);
344 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
345 let ticket = process_manager.register_query(
346 "catalog".to_string(),
347 vec![],
348 "query".to_string(),
349 "client".to_string(),
350 None,
351 None,
352 );
353
354 let cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
355
356 let schema = cancellable_stream.schema();
358 assert_eq!(schema.column_schemas().len(), 1);
359 assert_eq!(schema.column_schemas()[0].name, "test_col");
360
361 assert!(cancellable_stream.output_ordering().is_none());
363
364 assert!(cancellable_stream.metrics().is_none());
366 }
367
368 #[tokio::test]
369 async fn test_cancellation_during_pending_poll() {
370 let batch = create_test_batch();
371 let mock_stream =
372 MockRecordBatchStream::new(vec![Ok(batch)]).with_delay(Duration::from_millis(200));
373 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
374 let ticket = process_manager.register_query(
375 "catalog".to_string(),
376 vec![],
377 "query".to_string(),
378 "client".to_string(),
379 None,
380 None,
381 );
382 let cancellation_handle = ticket.cancellation_handle.clone();
383
384 let mut cancellable_stream = CancellableStreamWrapper::new(Box::pin(mock_stream), ticket);
385
386 tokio::spawn(async move {
388 sleep(Duration::from_millis(50)).await;
389 cancellation_handle.cancel();
390 });
391
392 let result = timeout(Duration::from_millis(300), cancellable_stream.next()).await;
393 assert!(result.is_ok());
394 let stream_result = result.unwrap();
395 assert!(stream_result.is_some());
396 assert!(stream_result.unwrap().is_err());
397 }
398
399 #[tokio::test]
400 async fn test_cancel_on_drop_cancels_unfinished_stream() {
401 let batch = create_test_batch();
402 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch)]);
403 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
404 let ticket = process_manager.register_query(
405 "catalog".to_string(),
406 vec![],
407 "query".to_string(),
408 "client".to_string(),
409 None,
410 None,
411 );
412 let cancellation_handle = ticket.cancellation_handle.clone();
413
414 let cancellable_stream =
415 CancellableStreamWrapper::new_cancel_on_drop(Box::pin(mock_stream), ticket);
416 drop(cancellable_stream);
417
418 assert!(cancellation_handle.is_cancelled());
419 }
420
421 #[tokio::test]
422 async fn test_cancel_on_drop_does_not_cancel_finished_stream() {
423 let batch = create_test_batch();
424 let mock_stream = MockRecordBatchStream::new(vec![Ok(batch)]);
425 let process_manager = Arc::new(ProcessManager::new("".to_string(), None));
426 let ticket = process_manager.register_query(
427 "catalog".to_string(),
428 vec![],
429 "query".to_string(),
430 "client".to_string(),
431 None,
432 None,
433 );
434 let cancellation_handle = ticket.cancellation_handle.clone();
435
436 let mut cancellable_stream =
437 CancellableStreamWrapper::new_cancel_on_drop(Box::pin(mock_stream), ticket);
438 assert!(cancellable_stream.next().await.unwrap().is_ok());
439 assert!(cancellable_stream.next().await.is_none());
440 drop(cancellable_stream);
441
442 assert!(!cancellation_handle.is_cancelled());
443 }
444}