1use risingwave_common::array::Op;
16use risingwave_common::row::{RowDeserializer, RowExt};
17use risingwave_common::util::epoch::EpochPair;
18use risingwave_common::util::sort_util::{ColumnOrder, topn_watermark_forwardable_order_key};
19
20use super::top_n_cache::TopNStaging;
21use super::utils::*;
22use super::{ManagedTopNState, TopNCache, TopNCacheTrait};
23use crate::common::table::state_table::StateTablePostCommit;
24use crate::executor::prelude::*;
25
26pub type TopNExecutor<S, const WITH_TIES: bool> =
29 TopNExecutorWrapper<InnerTopNExecutor<S, WITH_TIES>>;
30
31impl<S: StateStore, const WITH_TIES: bool> TopNExecutor<S, WITH_TIES> {
32 #[allow(clippy::too_many_arguments)]
33 pub fn new(
34 input: Executor,
35 ctx: ActorContextRef,
36 schema: Schema,
37 storage_key: Vec<ColumnOrder>,
38 offset_and_limit: (usize, usize),
39 order_by: Vec<ColumnOrder>,
40 state_table: StateTable<S>,
41 ) -> StreamResult<Self> {
42 Ok(TopNExecutorWrapper {
43 input,
44 ctx,
45 inner: InnerTopNExecutor::new(
46 schema,
47 storage_key,
48 offset_and_limit,
49 order_by,
50 state_table,
51 )?,
52 })
53 }
54}
55
56impl<S: StateStore> TopNExecutor<S, true> {
57 #[allow(clippy::too_many_arguments)]
60 #[cfg(test)]
61 pub fn new_with_ties_for_test(
62 input: Executor,
63 ctx: ActorContextRef,
64 schema: Schema,
65 storage_key: Vec<ColumnOrder>,
66 offset_and_limit: (usize, usize),
67 order_by: Vec<ColumnOrder>,
68 state_table: StateTable<S>,
69 ) -> StreamResult<Self> {
70 let mut inner =
71 InnerTopNExecutor::new(schema, storage_key, offset_and_limit, order_by, state_table)?;
72
73 inner.cache.high_cache_capacity = 2;
74
75 Ok(TopNExecutorWrapper { input, ctx, inner })
76 }
77}
78
79pub struct InnerTopNExecutor<S: StateStore, const WITH_TIES: bool> {
80 schema: Schema,
81
82 storage_key_indices: Vec<usize>,
84
85 managed_state: ManagedTopNState<S>,
86
87 cache: TopNCache<WITH_TIES>,
89
90 cache_key_serde: CacheKeySerde,
92
93 watermark_order_key: Option<usize>,
95}
96
97impl<S: StateStore, const WITH_TIES: bool> InnerTopNExecutor<S, WITH_TIES> {
98 #[allow(clippy::too_many_arguments)]
106 pub fn new(
107 schema: Schema,
108 storage_key: Vec<ColumnOrder>,
109 offset_and_limit: (usize, usize),
110 order_by: Vec<ColumnOrder>,
111 state_table: StateTable<S>,
112 ) -> StreamResult<Self> {
113 let num_offset = offset_and_limit.0;
114 let num_limit = offset_and_limit.1;
115
116 let cache_key_serde = create_cache_key_serde(&storage_key, &schema, &order_by, &[]);
117 let managed_state = ManagedTopNState::<S>::new(state_table, cache_key_serde.clone());
118 let data_types = schema.data_types();
119
120 Ok(Self {
121 schema,
122 managed_state,
123 storage_key_indices: storage_key.into_iter().map(|op| op.column_index).collect(),
124 cache: TopNCache::new(num_offset, num_limit, data_types),
125 cache_key_serde,
126 watermark_order_key: topn_watermark_forwardable_order_key(&order_by),
127 })
128 }
129}
130
131impl<S: StateStore, const WITH_TIES: bool> TopNExecutorBase for InnerTopNExecutor<S, WITH_TIES>
132where
133 TopNCache<WITH_TIES>: TopNCacheTrait,
134{
135 type State = S;
136
137 async fn apply_chunk(
138 &mut self,
139 chunk: StreamChunk,
140 ) -> StreamExecutorResult<Option<StreamChunk>> {
141 let mut staging = TopNStaging::new();
142
143 for (op, row_ref) in chunk.rows() {
145 let pk_row = row_ref.project(&self.storage_key_indices);
146 let cache_key = serialize_pk_to_cache_key(pk_row, &self.cache_key_serde);
147 match op {
148 Op::Insert | Op::UpdateInsert => {
149 self.managed_state.insert(row_ref);
151 self.cache.insert(cache_key, row_ref, &mut staging)
152 }
153
154 Op::Delete | Op::UpdateDelete => {
155 self.managed_state.delete(row_ref);
157 self.cache
158 .delete(
159 NO_GROUP_KEY,
160 &mut self.managed_state,
161 cache_key,
162 row_ref,
163 &mut staging,
164 )
165 .await?
166 }
167 }
168 }
169
170 let data_types = self.schema.data_types();
171 let deserializer = RowDeserializer::new(data_types.clone());
172 if staging.is_empty() {
173 return Ok(None);
174 }
175 let mut chunk_builder = StreamChunkBuilder::unlimited(data_types, Some(staging.len()));
176 for res in staging.into_deserialized_changes(&deserializer) {
177 let record = res?;
178 let _none = chunk_builder.append_record(record);
179 }
180 Ok(chunk_builder.take())
181 }
182
183 async fn flush_data(
184 &mut self,
185 epoch: EpochPair,
186 ) -> StreamExecutorResult<StateTablePostCommit<'_, S>> {
187 self.managed_state.flush(epoch).await
188 }
189
190 async fn try_flush_data(&mut self) -> StreamExecutorResult<()> {
191 self.managed_state.try_flush().await
192 }
193
194 async fn init(&mut self, epoch: EpochPair) -> StreamExecutorResult<()> {
195 self.managed_state.init_epoch(epoch).await?;
196 self.managed_state
197 .init_topn_cache(NO_GROUP_KEY, &mut self.cache)
198 .await
199 }
200
201 async fn handle_watermark(&mut self, watermark: Watermark) -> Option<Watermark> {
202 (Some(watermark.col_idx) == self.watermark_order_key).then_some(watermark)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use risingwave_common::array::stream_chunk::StreamChunkTestExt;
211 use risingwave_common::catalog::{Field, Schema};
212 use risingwave_common::types::DataType;
213 use risingwave_common::util::sort_util::OrderType;
214
215 use super::*;
216 use crate::executor::test_utils::MockSource;
217 use crate::executor::test_utils::top_n_executor::create_in_memory_state_table;
218 use crate::executor::{Barrier, Message};
219
220 mod test1 {
221
222 use risingwave_common::util::epoch::test_epoch;
223
224 use super::*;
225 use crate::executor::test_utils::StreamExecutorTestExt;
226
227 fn create_stream_chunks() -> Vec<StreamChunk> {
228 let chunk1 = StreamChunk::from_pretty(
229 " I I
230 + 1 0
231 + 2 1
232 + 3 2
233 + 10 3
234 + 9 4
235 + 8 5",
236 );
237 let chunk2 = StreamChunk::from_pretty(
238 " I I
239 + 7 6
240 - 3 2
241 - 1 0
242 + 5 7
243 - 2 1
244 + 11 8",
245 );
246 let chunk3 = StreamChunk::from_pretty(
247 " I I
248 + 6 9
249 + 12 10
250 + 13 11
251 + 14 12",
252 );
253 let chunk4 = StreamChunk::from_pretty(
254 " I I
255 - 5 7
256 - 6 9
257 - 11 8",
258 );
259 vec![chunk1, chunk2, chunk3, chunk4]
260 }
261
262 fn create_schema() -> Schema {
263 Schema {
264 fields: vec![
265 Field::unnamed(DataType::Int64),
266 Field::unnamed(DataType::Int64),
267 ],
268 }
269 }
270
271 fn storage_key() -> Vec<ColumnOrder> {
272 let mut v = order_by();
273 v.extend([ColumnOrder::new(1, OrderType::ascending())]);
274 v
275 }
276
277 fn order_by() -> Vec<ColumnOrder> {
278 vec![ColumnOrder::new(0, OrderType::ascending())]
279 }
280
281 fn stream_key() -> StreamKey {
282 vec![0, 1]
283 }
284
285 fn create_source() -> Executor {
286 let mut chunks = create_stream_chunks();
287 let schema = create_schema();
288 MockSource::with_messages(vec![
289 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
290 Message::Chunk(std::mem::take(&mut chunks[0])),
291 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
292 Message::Chunk(std::mem::take(&mut chunks[1])),
293 Message::Barrier(Barrier::new_test_barrier(test_epoch(3))),
294 Message::Chunk(std::mem::take(&mut chunks[2])),
295 Message::Barrier(Barrier::new_test_barrier(test_epoch(4))),
296 Message::Chunk(std::mem::take(&mut chunks[3])),
297 Message::Barrier(Barrier::new_test_barrier(test_epoch(5))),
298 ])
299 .into_executor(schema, stream_key())
300 }
301
302 #[tokio::test]
303 async fn test_top_n_executor_with_offset() {
304 let source = create_source();
305 let state_table = create_in_memory_state_table(
306 &[DataType::Int64, DataType::Int64],
307 &[OrderType::ascending(), OrderType::ascending()],
308 &stream_key(),
309 )
310 .await;
311
312 let schema = source.schema().clone();
313 let top_n = TopNExecutor::<_, false>::new(
314 source,
315 ActorContext::for_test(0),
316 schema,
317 storage_key(),
318 (3, 1000),
319 order_by(),
320 state_table,
321 )
322 .unwrap();
323 let mut top_n = top_n.boxed().execute();
324
325 top_n.expect_barrier().await;
327 assert_eq!(
328 top_n.expect_chunk().await.sort_rows(),
329 StreamChunk::from_pretty(
330 " I I
331 + 10 3
332 + 9 4
333 + 8 5"
334 )
335 .sort_rows(),
336 );
337 top_n.expect_barrier().await;
339 assert_eq!(
340 top_n.expect_chunk().await.sort_rows(),
341 StreamChunk::from_pretty(
342 " I I
343 - 8 5
344 + 11 8"
345 )
346 .sort_rows(),
347 );
348
349 top_n.expect_barrier().await;
351
352 assert_eq!(
354 top_n.expect_chunk().await.sort_rows(),
355 StreamChunk::from_pretty(
356 " I I
357 + 8 5
358 + 12 10
359 + 13 11
360 + 14 12"
361 )
362 .sort_rows(),
363 );
364 top_n.expect_barrier().await;
366
367 assert_eq!(
369 top_n.expect_chunk().await.sort_rows(),
370 StreamChunk::from_pretty(
371 " I I
372 - 8 5
373 - 9 4
374 - 11 8"
375 )
376 .sort_rows(),
377 );
378 top_n.expect_barrier().await;
380 }
381
382 #[tokio::test]
383 async fn test_top_n_executor_with_limit() {
384 let source = create_source();
385 let state_table = create_in_memory_state_table(
386 &[DataType::Int64, DataType::Int64],
387 &[OrderType::ascending(), OrderType::ascending()],
388 &stream_key(),
389 )
390 .await;
391 let schema = source.schema().clone();
392 let top_n = TopNExecutor::<_, false>::new(
393 source,
394 ActorContext::for_test(0),
395 schema,
396 storage_key(),
397 (0, 4),
398 order_by(),
399 state_table,
400 )
401 .unwrap();
402 let mut top_n = top_n.boxed().execute();
403
404 top_n.expect_barrier().await;
406 assert_eq!(
407 top_n.expect_chunk().await.sort_rows(),
408 StreamChunk::from_pretty(
409 " I I
410 + 1 0
411 + 2 1
412 + 3 2
413 + 8 5"
414 )
415 .sort_rows(),
416 );
417 top_n.expect_barrier().await;
421 assert_eq!(
422 top_n.expect_chunk().await.sort_rows(),
423 StreamChunk::from_pretty(
424 " I I
425 + 7 6
426 - 3 2
427 - 1 0
428 + 5 7
429 - 2 1
430 + 9 4"
431 )
432 .sort_rows(),
433 );
434
435 top_n.expect_barrier().await;
438
439 assert_eq!(
440 top_n.expect_chunk().await.sort_rows(),
441 StreamChunk::from_pretty(
442 " I I
443 - 9 4
444 + 6 9"
445 )
446 .sort_rows(),
447 );
448 top_n.expect_barrier().await;
451
452 assert_eq!(
453 top_n.expect_chunk().await.sort_rows(),
454 StreamChunk::from_pretty(
455 " I I
456 - 5 7
457 + 9 4
458 - 6 9
459 + 10 3"
460 )
461 .sort_rows(),
462 );
463 top_n.expect_barrier().await;
466 }
467
468 #[tokio::test]
470 async fn test_top_n_executor_with_limit_with_ties() {
471 let source = create_source();
472 let state_table = create_in_memory_state_table(
473 &[DataType::Int64, DataType::Int64],
474 &[OrderType::ascending(), OrderType::ascending()],
475 &stream_key(),
476 )
477 .await;
478 let schema = source.schema().clone();
479 let top_n = TopNExecutor::<_, true>::new(
480 source,
481 ActorContext::for_test(0),
482 schema,
483 storage_key(),
484 (0, 4),
485 order_by(),
486 state_table,
487 )
488 .unwrap();
489 let mut top_n = top_n.boxed().execute();
490
491 top_n.expect_barrier().await;
493 assert_eq!(
494 top_n.expect_chunk().await.sort_rows(),
495 StreamChunk::from_pretty(
496 " I I
497 + 1 0
498 + 2 1
499 + 3 2
500 + 8 5"
501 )
502 .sort_rows(),
503 );
504 top_n.expect_barrier().await;
508 assert_eq!(
509 top_n.expect_chunk().await.sort_rows(),
510 StreamChunk::from_pretty(
511 " I I
512 + 7 6
513 - 3 2
514 - 1 0
515 + 5 7
516 - 2 1
517 + 9 4"
518 )
519 .sort_rows(),
520 );
521
522 top_n.expect_barrier().await;
525
526 assert_eq!(
527 top_n.expect_chunk().await.sort_rows(),
528 StreamChunk::from_pretty(
529 " I I
530 - 9 4
531 + 6 9"
532 )
533 .sort_rows(),
534 );
535 top_n.expect_barrier().await;
538
539 assert_eq!(
540 top_n.expect_chunk().await.sort_rows(),
541 StreamChunk::from_pretty(
542 " I I
543 - 5 7
544 + 9 4
545 - 6 9
546 + 10 3"
547 )
548 .sort_rows(),
549 );
550 top_n.expect_barrier().await;
553 }
554
555 #[tokio::test]
556 async fn test_top_n_executor_with_offset_and_limit() {
557 let source = create_source();
558 let state_table = create_in_memory_state_table(
559 &[DataType::Int64, DataType::Int64],
560 &[OrderType::ascending(), OrderType::ascending()],
561 &stream_key(),
562 )
563 .await;
564 let schema = source.schema().clone();
565 let top_n = TopNExecutor::<_, false>::new(
566 source,
567 ActorContext::for_test(0),
568 schema,
569 storage_key(),
570 (3, 4),
571 order_by(),
572 state_table,
573 )
574 .unwrap();
575 let mut top_n = top_n.boxed().execute();
576
577 top_n.expect_barrier().await;
579 assert_eq!(
580 top_n.expect_chunk().await.sort_rows(),
581 StreamChunk::from_pretty(
582 " I I
583 + 10 3
584 + 9 4
585 + 8 5"
586 )
587 .sort_rows(),
588 );
589 top_n.expect_barrier().await;
591 assert_eq!(
592 top_n.expect_chunk().await.sort_rows(),
593 StreamChunk::from_pretty(
594 " I I
595 - 8 5
596 + 11 8"
597 )
598 .sort_rows(),
599 );
600 top_n.expect_barrier().await;
602
603 assert_eq!(
604 top_n.expect_chunk().await.sort_rows(),
605 StreamChunk::from_pretty(
606 " I I
607 + 8 5"
608 )
609 .sort_rows(),
610 );
611 top_n.expect_barrier().await;
613 assert_eq!(
614 top_n.expect_chunk().await.sort_rows(),
615 StreamChunk::from_pretty(
616 " I I
617 - 8 5
618 + 12 10
619 - 9 4
620 + 13 11
621 - 11 8
622 + 14 12"
623 )
624 .sort_rows(),
625 );
626 top_n.expect_barrier().await;
628 }
629 }
630
631 mod test2 {
632
633 use risingwave_common::util::epoch::test_epoch;
634 use risingwave_storage::memory::MemoryStateStore;
635
636 use super::*;
637 use crate::executor::test_utils::StreamExecutorTestExt;
638 use crate::executor::test_utils::top_n_executor::create_in_memory_state_table_from_state_store;
639 fn create_source_new() -> Executor {
640 let mut chunks = [
641 StreamChunk::from_pretty(
642 " I I I I
643 + 1 1 4 1001",
644 ),
645 StreamChunk::from_pretty(
646 " I I I I
647 + 5 1 4 1002 ",
648 ),
649 StreamChunk::from_pretty(
650 " I I I I
651 + 1 9 1 1003
652 + 9 8 1 1004
653 + 0 2 3 1005",
654 ),
655 StreamChunk::from_pretty(
656 " I I I I
657 + 1 0 2 1006",
658 ),
659 ];
660 let schema = Schema {
661 fields: vec![
662 Field::unnamed(DataType::Int64),
663 Field::unnamed(DataType::Int64),
664 Field::unnamed(DataType::Int64),
665 Field::unnamed(DataType::Int64),
666 ],
667 };
668 MockSource::with_messages(vec![
669 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
670 Message::Chunk(std::mem::take(&mut chunks[0])),
671 Message::Chunk(std::mem::take(&mut chunks[1])),
672 Message::Chunk(std::mem::take(&mut chunks[2])),
673 Message::Chunk(std::mem::take(&mut chunks[3])),
674 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
675 ])
676 .into_executor(schema, stream_key())
677 }
678
679 fn create_source_new_before_recovery() -> Executor {
680 let mut chunks = [
681 StreamChunk::from_pretty(
682 " I I I I
683 + 1 1 4 1001",
684 ),
685 StreamChunk::from_pretty(
686 " I I I I
687 + 5 1 4 1002 ",
688 ),
689 ];
690 let schema = Schema {
691 fields: vec![
692 Field::unnamed(DataType::Int64),
693 Field::unnamed(DataType::Int64),
694 Field::unnamed(DataType::Int64),
695 Field::unnamed(DataType::Int64),
696 ],
697 };
698 MockSource::with_messages(vec![
699 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
700 Message::Chunk(std::mem::take(&mut chunks[0])),
701 Message::Chunk(std::mem::take(&mut chunks[1])),
702 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
703 ])
704 .into_executor(schema, stream_key())
705 }
706
707 fn create_source_new_after_recovery() -> Executor {
708 let mut chunks = [
709 StreamChunk::from_pretty(
710 " I I I I
711 + 1 9 1 1003
712 + 9 8 1 1004
713 + 0 2 3 1005",
714 ),
715 StreamChunk::from_pretty(
716 " I I I I
717 + 1 0 2 1006",
718 ),
719 ];
720 let schema = Schema {
721 fields: vec![
722 Field::unnamed(DataType::Int64),
723 Field::unnamed(DataType::Int64),
724 Field::unnamed(DataType::Int64),
725 Field::unnamed(DataType::Int64),
726 ],
727 };
728 MockSource::with_messages(vec![
729 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
730 Message::Chunk(std::mem::take(&mut chunks[0])),
731 Message::Chunk(std::mem::take(&mut chunks[1])),
732 Message::Barrier(Barrier::new_test_barrier(test_epoch(3))),
733 ])
734 .into_executor(schema, stream_key())
735 }
736
737 fn storage_key() -> Vec<ColumnOrder> {
738 order_by()
739 }
740
741 fn order_by() -> Vec<ColumnOrder> {
742 vec![
743 ColumnOrder::new(0, OrderType::ascending()),
744 ColumnOrder::new(3, OrderType::ascending()),
745 ]
746 }
747
748 fn stream_key() -> StreamKey {
749 vec![0, 3]
750 }
751
752 #[tokio::test]
753 async fn test_top_n_executor_with_offset_and_limit_new() {
754 let source = create_source_new();
755 let state_table = create_in_memory_state_table(
756 &[
757 DataType::Int64,
758 DataType::Int64,
759 DataType::Int64,
760 DataType::Int64,
761 ],
762 &[OrderType::ascending(), OrderType::ascending()],
763 &stream_key(),
764 )
765 .await;
766 let schema = source.schema().clone();
767 let top_n = TopNExecutor::<_, false>::new(
768 source,
769 ActorContext::for_test(0),
770 schema,
771 storage_key(),
772 (1, 3),
773 order_by(),
774 state_table,
775 )
776 .unwrap();
777 let mut top_n = top_n.boxed().execute();
778
779 top_n.expect_barrier().await;
781
782 assert_eq!(
783 top_n.expect_chunk().await.sort_rows(),
784 StreamChunk::from_pretty(
785 " I I I I
786 + 5 1 4 1002"
787 )
788 .sort_rows(),
789 );
790
791 assert_eq!(
792 top_n.expect_chunk().await.sort_rows(),
793 StreamChunk::from_pretty(
794 " I I I I
795 + 1 9 1 1003
796 + 1 1 4 1001",
797 )
798 .sort_rows(),
799 );
800
801 assert_eq!(
802 top_n.expect_chunk().await.sort_rows(),
803 StreamChunk::from_pretty(
804 " I I I I
805 - 5 1 4 1002
806 + 1 0 2 1006",
807 )
808 .sort_rows(),
809 );
810
811 top_n.expect_barrier().await;
813 }
814
815 #[tokio::test]
816 async fn test_top_n_executor_with_offset_and_limit_new_after_recovery() {
817 let state_store = MemoryStateStore::new();
818 let state_table = create_in_memory_state_table_from_state_store(
819 &[
820 DataType::Int64,
821 DataType::Int64,
822 DataType::Int64,
823 DataType::Int64,
824 ],
825 &[OrderType::ascending(), OrderType::ascending()],
826 &stream_key(),
827 state_store.clone(),
828 )
829 .await;
830 let source = create_source_new_before_recovery();
831 let schema = source.schema().clone();
832 let top_n = TopNExecutor::<_, false>::new(
833 source,
834 ActorContext::for_test(0),
835 schema,
836 storage_key(),
837 (1, 3),
838 order_by(),
839 state_table,
840 )
841 .unwrap();
842 let mut top_n = top_n.boxed().execute();
843
844 top_n.expect_barrier().await;
846
847 assert_eq!(
848 top_n.expect_chunk().await.sort_rows(),
849 StreamChunk::from_pretty(
850 " I I I I
851 + 5 1 4 1002"
852 )
853 .sort_rows(),
854 );
855
856 top_n.expect_barrier().await;
858
859 let state_table = create_in_memory_state_table_from_state_store(
860 &[
861 DataType::Int64,
862 DataType::Int64,
863 DataType::Int64,
864 DataType::Int64,
865 ],
866 &[OrderType::ascending(), OrderType::ascending()],
867 &stream_key(),
868 state_store,
869 )
870 .await;
871
872 let source = create_source_new_after_recovery();
874 let schema = source.schema().clone();
875 let top_n_after_recovery = TopNExecutor::<_, false>::new(
876 source,
877 ActorContext::for_test(0),
878 schema,
879 storage_key(),
880 (1, 3),
881 order_by(),
882 state_table,
883 )
884 .unwrap();
885 let mut top_n = top_n_after_recovery.boxed().execute();
886
887 top_n.expect_barrier().await;
889
890 assert_eq!(
891 top_n.expect_chunk().await.sort_rows(),
892 StreamChunk::from_pretty(
893 " I I I I
894 + 1 9 1 1003
895 + 1 1 4 1001",
896 )
897 .sort_rows(),
898 );
899
900 assert_eq!(
901 top_n.expect_chunk().await.sort_rows(),
902 StreamChunk::from_pretty(
903 " I I I I
904 - 5 1 4 1002
905 + 1 0 2 1006",
906 )
907 .sort_rows(),
908 );
909
910 top_n.expect_barrier().await;
912 }
913 }
914
915 mod test_with_ties {
916
917 use risingwave_common::util::epoch::test_epoch;
918 use risingwave_storage::memory::MemoryStateStore;
919
920 use super::*;
921 use crate::executor::test_utils::StreamExecutorTestExt;
922 use crate::executor::test_utils::top_n_executor::create_in_memory_state_table_from_state_store;
923
924 fn create_source() -> Executor {
925 let mut chunks = [
926 StreamChunk::from_pretty(
927 " I I
928 + 1 0
929 + 2 1
930 + 3 2
931 + 10 3
932 + 9 4
933 + 8 5
934 ",
935 ),
936 StreamChunk::from_pretty(
937 " I I
938 + 3 6
939 + 3 7
940 + 1 8
941 + 2 9
942 + 10 10",
943 ),
944 StreamChunk::from_pretty(
945 " I I
946 - 1 0",
947 ),
948 StreamChunk::from_pretty(
949 " I I
950 - 1 8",
951 ),
952 ];
953 let schema = Schema {
954 fields: vec![
955 Field::unnamed(DataType::Int64),
956 Field::unnamed(DataType::Int64),
957 ],
958 };
959 MockSource::with_messages(vec![
960 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
961 Message::Chunk(std::mem::take(&mut chunks[0])),
962 Message::Chunk(std::mem::take(&mut chunks[1])),
963 Message::Chunk(std::mem::take(&mut chunks[2])),
964 Message::Chunk(std::mem::take(&mut chunks[3])),
965 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
966 ])
967 .into_executor(schema, stream_key())
968 }
969
970 fn storage_key() -> Vec<ColumnOrder> {
971 let mut v = order_by();
972 v.push(ColumnOrder::new(1, OrderType::ascending()));
973 v
974 }
975
976 fn order_by() -> Vec<ColumnOrder> {
977 vec![ColumnOrder::new(0, OrderType::ascending())]
978 }
979
980 fn stream_key() -> StreamKey {
981 vec![0, 1]
982 }
983
984 #[tokio::test]
985 async fn test_with_ties() {
986 let source = create_source();
987 let state_table = create_in_memory_state_table(
988 &[DataType::Int64, DataType::Int64],
989 &[OrderType::ascending(), OrderType::ascending()],
990 &stream_key(),
991 )
992 .await;
993 let schema = source.schema().clone();
994 let top_n = TopNExecutor::new_with_ties_for_test(
995 source,
996 ActorContext::for_test(0),
997 schema,
998 storage_key(),
999 (0, 3),
1000 order_by(),
1001 state_table,
1002 )
1003 .unwrap();
1004 let mut top_n = top_n.boxed().execute();
1005
1006 top_n.expect_barrier().await;
1008 assert_eq!(
1009 top_n.expect_chunk().await.sort_rows(),
1010 StreamChunk::from_pretty(
1011 " I I
1012 + 1 0
1013 + 2 1
1014 + 3 2"
1015 )
1016 .sort_rows(),
1017 );
1018
1019 assert_eq!(
1020 top_n.expect_chunk().await.sort_rows(),
1021 StreamChunk::from_pretty(
1022 " I I
1023 - 3 2
1024 + 1 8
1025 + 2 9"
1026 )
1027 .sort_rows(),
1028 );
1029
1030 assert_eq!(
1031 top_n.expect_chunk().await.sort_rows(),
1032 StreamChunk::from_pretty(
1033 " I I
1034 - 1 0"
1035 )
1036 .sort_rows(),
1037 );
1038
1039 assert_eq!(
1041 top_n.expect_chunk().await.sort_rows(),
1042 StreamChunk::from_pretty(
1043 " I I
1044 - 1 8
1045 + 3 2
1046 + 3 6
1047 + 3 7"
1048 )
1049 .sort_rows(),
1050 );
1051
1052 top_n.expect_barrier().await;
1054 }
1055
1056 fn create_source_before_recovery() -> Executor {
1057 let mut chunks = [
1058 StreamChunk::from_pretty(
1059 " I I
1060 + 1 0
1061 + 2 1
1062 + 3 2
1063 + 10 3
1064 + 9 4
1065 + 8 5",
1066 ),
1067 StreamChunk::from_pretty(
1068 " I I
1069 + 3 6
1070 + 3 7
1071 + 1 8
1072 + 2 9
1073 + 10 10",
1074 ),
1075 ];
1076 let schema = Schema {
1077 fields: vec![
1078 Field::unnamed(DataType::Int64),
1079 Field::unnamed(DataType::Int64),
1080 ],
1081 };
1082 MockSource::with_messages(vec![
1083 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
1084 Message::Chunk(std::mem::take(&mut chunks[0])),
1085 Message::Chunk(std::mem::take(&mut chunks[1])),
1086 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
1087 ])
1088 .into_executor(schema, stream_key())
1089 }
1090
1091 fn create_source_after_recovery() -> Executor {
1092 let mut chunks = [
1093 StreamChunk::from_pretty(
1094 " I I
1095 - 1 0",
1096 ),
1097 StreamChunk::from_pretty(
1098 " I I
1099 - 1 8",
1100 ),
1101 ];
1102 let schema = Schema {
1103 fields: vec![
1104 Field::unnamed(DataType::Int64),
1105 Field::unnamed(DataType::Int64),
1106 ],
1107 };
1108 MockSource::with_messages(vec![
1109 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
1110 Message::Chunk(std::mem::take(&mut chunks[0])),
1111 Message::Chunk(std::mem::take(&mut chunks[1])),
1112 Message::Barrier(Barrier::new_test_barrier(test_epoch(3))),
1113 ])
1114 .into_executor(schema, stream_key())
1115 }
1116
1117 #[tokio::test]
1118 async fn test_with_ties_recovery() {
1119 let state_store = MemoryStateStore::new();
1120 let state_table = create_in_memory_state_table_from_state_store(
1121 &[DataType::Int64, DataType::Int64],
1122 &[OrderType::ascending(), OrderType::ascending()],
1123 &stream_key(),
1124 state_store.clone(),
1125 )
1126 .await;
1127 let source = create_source_before_recovery();
1128 let schema = source.schema().clone();
1129 let top_n = TopNExecutor::new_with_ties_for_test(
1130 source,
1131 ActorContext::for_test(0),
1132 schema,
1133 storage_key(),
1134 (0, 3),
1135 order_by(),
1136 state_table,
1137 )
1138 .unwrap();
1139 let mut top_n = top_n.boxed().execute();
1140
1141 top_n.expect_barrier().await;
1143 assert_eq!(
1144 top_n.expect_chunk().await.sort_rows(),
1145 StreamChunk::from_pretty(
1146 " I I
1147 + 1 0
1148 + 2 1
1149 + 3 2"
1150 )
1151 .sort_rows(),
1152 );
1153
1154 assert_eq!(
1155 top_n.expect_chunk().await.sort_rows(),
1156 StreamChunk::from_pretty(
1157 " I I
1158 - 3 2
1159 + 1 8
1160 + 2 9"
1161 )
1162 .sort_rows(),
1163 );
1164
1165 top_n.expect_barrier().await;
1167
1168 let state_table = create_in_memory_state_table_from_state_store(
1169 &[DataType::Int64, DataType::Int64],
1170 &[OrderType::ascending(), OrderType::ascending()],
1171 &stream_key(),
1172 state_store,
1173 )
1174 .await;
1175
1176 let source = create_source_after_recovery();
1178 let schema = source.schema().clone();
1179 let top_n_after_recovery = TopNExecutor::new_with_ties_for_test(
1180 source,
1181 ActorContext::for_test(0),
1182 schema,
1183 storage_key(),
1184 (0, 3),
1185 order_by(),
1186 state_table,
1187 )
1188 .unwrap();
1189 let mut top_n = top_n_after_recovery.boxed().execute();
1190
1191 top_n.expect_barrier().await;
1193
1194 assert_eq!(
1195 top_n.expect_chunk().await.sort_rows(),
1196 StreamChunk::from_pretty(
1197 " I I
1198 - 1 0"
1199 )
1200 .sort_rows(),
1201 );
1202
1203 assert_eq!(
1205 top_n.expect_chunk().await.sort_rows(),
1206 StreamChunk::from_pretty(
1207 " I I
1208 - 1 8
1209 + 3 2
1210 + 3 6
1211 + 3 7"
1212 )
1213 .sort_rows(),
1214 );
1215 top_n.expect_barrier().await;
1217 }
1218 }
1219}