Skip to main content

risingwave_stream/executor/top_n/
top_n_plain.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
26/// `TopNExecutor` works with input with modification, it keeps all the data
27/// records/rows that have been seen, and returns topN records overall.
28pub 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    /// It only has 1 capacity for high cache. Used to test the case where the last element in high
58    /// has ties.
59    #[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    /// The storage key indices of the `TopNExecutor`
83    storage_key_indices: Vec<usize>,
84
85    managed_state: ManagedTopNState<S>,
86
87    /// In-memory cache of top (N + N * `TOPN_CACHE_HIGH_CAPACITY_FACTOR`) rows
88    cache: TopNCache<WITH_TIES>,
89
90    /// Used for serializing pk into `CacheKey`.
91    cache_key_serde: CacheKeySerde,
92
93    /// The `ORDER BY` column whose watermarks can be forwarded, if any.
94    watermark_order_key: Option<usize>,
95}
96
97impl<S: StateStore, const WITH_TIES: bool> InnerTopNExecutor<S, WITH_TIES> {
98    /// # Arguments
99    ///
100    /// `storage_key` -- the storage pk. It's composed of the ORDER BY columns and the missing
101    /// columns of pk.
102    ///
103    /// `order_by_len` -- The number of fields of the ORDER BY clause, and will be used to split key
104    /// into `CacheKey`.
105    #[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        // apply the chunk to state table
144        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                    // First insert input row to state store
150                    self.managed_state.insert(row_ref);
151                    self.cache.insert(cache_key, row_ref, &mut staging)
152                }
153
154                Op::Delete | Op::UpdateDelete => {
155                    // First remove the row from state store
156                    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        // Only watermarks on the first `ORDER BY` column ordered `ASC NULLS LAST` can be forwarded.
203        // See `topn_watermark_forwardable_order_key` for the reasoning.
204        (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            // consume the init barrier
326            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            // Barrier
338            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            // barrier
350            top_n.expect_barrier().await;
351
352            // (8, 9, 10, 11, 12, 13, 14)
353            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            // barrier
365            top_n.expect_barrier().await;
366
367            // (10, 12, 13, 14)
368            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            // barrier
379            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            // consume the init barrier
405            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            // now () -> (1, 2, 3, 8)
418
419            // barrier
420            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            // (5, 7, 8, 9)
436            // barrier
437            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            // (5, 6, 7, 8)
449            // barrier
450            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            // (7, 8, 9, 10)
464            // barrier
465            top_n.expect_barrier().await;
466        }
467
468        // Should have the same result as above, since there are no duplicate sort keys.
469        #[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            // consume the init barrier
492            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            // now () -> (1, 2, 3, 8)
505
506            // barrier
507            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            // (5, 7, 8, 9)
523            // barrier
524            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            // (5, 6, 7, 8)
536            // barrier
537            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            // (7, 8, 9, 10)
551            // barrier
552            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            // consume the init barrier
578            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            // barrier
590            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            // barrier
601            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            // barrier
612            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            // barrier
627            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            // consume the init barrier
780            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            // barrier
812            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            // consume the init barrier
845            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            // barrier
857            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            // recovery
873            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            // barrier
888            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            // barrier
911            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            // consume the init barrier
1007            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            // High cache has only 2 capacity, but we need to trigger 3 inserts here!
1040            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            // barrier
1053            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            // consume the init barrier
1142            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            // barrier
1166            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            // recovery
1177            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            // barrier
1192            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            // High cache has only 2 capacity, but we need to trigger 3 inserts here!
1204            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            // barrier
1216            top_n.expect_barrier().await;
1217        }
1218    }
1219}