risingwave_stream/executor/top_n/
top_n_appendonly.rs1use 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::{AppendOnlyTopNCacheTrait, TopNStaging};
21use super::utils::*;
22use super::{ManagedTopNState, TopNCache};
23use crate::common::table::state_table::StateTablePostCommit;
24use crate::executor::prelude::*;
25
26pub type AppendOnlyTopNExecutor<S, const WITH_TIES: bool> =
33 TopNExecutorWrapper<InnerAppendOnlyTopNExecutor<S, WITH_TIES>>;
34
35impl<S: StateStore, const WITH_TIES: bool> AppendOnlyTopNExecutor<S, WITH_TIES> {
36 pub fn new(
37 input: Executor,
38 ctx: ActorContextRef,
39 schema: Schema,
40 storage_key: Vec<ColumnOrder>,
41 offset_and_limit: (usize, usize),
42 order_by: Vec<ColumnOrder>,
43 state_table: StateTable<S>,
44 ) -> StreamResult<Self> {
45 Ok(TopNExecutorWrapper {
46 input,
47 ctx,
48 inner: InnerAppendOnlyTopNExecutor::new(
49 schema,
50 storage_key,
51 offset_and_limit,
52 order_by,
53 state_table,
54 )?,
55 })
56 }
57}
58
59pub struct InnerAppendOnlyTopNExecutor<S: StateStore, const WITH_TIES: bool> {
60 schema: Schema,
61
62 storage_key_indices: Vec<usize>,
64
65 managed_state: ManagedTopNState<S>,
67
68 cache: TopNCache<WITH_TIES>,
71
72 cache_key_serde: CacheKeySerde,
74
75 watermark_order_key: Option<usize>,
77}
78
79impl<S: StateStore, const WITH_TIES: bool> InnerAppendOnlyTopNExecutor<S, WITH_TIES> {
80 pub fn new(
81 schema: Schema,
82 storage_key: Vec<ColumnOrder>,
83 offset_and_limit: (usize, usize),
84 order_by: Vec<ColumnOrder>,
85 state_table: StateTable<S>,
86 ) -> StreamResult<Self> {
87 let num_offset = offset_and_limit.0;
88 let num_limit = offset_and_limit.1;
89
90 let cache_key_serde = create_cache_key_serde(&storage_key, &schema, &order_by, &[]);
91 let managed_state = ManagedTopNState::<S>::new(state_table, cache_key_serde.clone());
92 let data_types = schema.data_types();
93
94 Ok(Self {
95 schema,
96 managed_state,
97 storage_key_indices: storage_key.into_iter().map(|op| op.column_index).collect(),
98 cache: TopNCache::new(num_offset, num_limit, data_types),
99 cache_key_serde,
100 watermark_order_key: topn_watermark_forwardable_order_key(&order_by),
101 })
102 }
103}
104
105impl<S: StateStore, const WITH_TIES: bool> TopNExecutorBase
106 for InnerAppendOnlyTopNExecutor<S, WITH_TIES>
107where
108 TopNCache<WITH_TIES>: AppendOnlyTopNCacheTrait,
109{
110 type State = S;
111
112 async fn apply_chunk(
113 &mut self,
114 chunk: StreamChunk,
115 ) -> StreamExecutorResult<Option<StreamChunk>> {
116 let mut staging = TopNStaging::new();
117 let data_types = self.schema.data_types();
118 let deserializer = RowDeserializer::new(data_types.clone());
119 for (op, row_ref) in chunk.rows() {
121 debug_assert_eq!(op, Op::Insert);
122 let pk_row = row_ref.project(&self.storage_key_indices);
123 let cache_key = serialize_pk_to_cache_key(pk_row, &self.cache_key_serde);
124 self.cache.insert(
125 cache_key,
126 row_ref,
127 &mut staging,
128 &mut self.managed_state,
129 &deserializer,
130 )?;
131 }
132
133 if staging.is_empty() {
134 return Ok(None);
135 }
136 let mut chunk_builder = StreamChunkBuilder::unlimited(data_types, Some(staging.len()));
137 for res in staging.into_deserialized_changes(&deserializer) {
138 let record = res?;
139 let _none = chunk_builder.append_record(record);
140 }
141 Ok(chunk_builder.take())
142 }
143
144 async fn flush_data(
145 &mut self,
146 epoch: EpochPair,
147 ) -> StreamExecutorResult<StateTablePostCommit<'_, S>> {
148 self.managed_state.flush(epoch).await
149 }
150
151 async fn try_flush_data(&mut self) -> StreamExecutorResult<()> {
152 self.managed_state.try_flush().await
153 }
154
155 async fn init(&mut self, epoch: EpochPair) -> StreamExecutorResult<()> {
156 self.managed_state.init_epoch(epoch).await?;
157 self.managed_state
158 .init_topn_cache(NO_GROUP_KEY, &mut self.cache)
159 .await
160 }
161
162 async fn handle_watermark(&mut self, watermark: Watermark) -> Option<Watermark> {
163 (Some(watermark.col_idx) == self.watermark_order_key).then_some(watermark)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171
172 use risingwave_common::array::StreamChunk;
173 use risingwave_common::array::stream_chunk::StreamChunkTestExt;
174 use risingwave_common::catalog::{Field, Schema};
175 use risingwave_common::types::DataType;
176 use risingwave_common::util::epoch::test_epoch;
177 use risingwave_common::util::sort_util::{ColumnOrder, OrderType};
178
179 use super::AppendOnlyTopNExecutor;
180 use crate::executor::test_utils::top_n_executor::create_in_memory_state_table;
181 use crate::executor::test_utils::{MockSource, StreamExecutorTestExt};
182 use crate::executor::{ActorContext, Barrier, Execute, Executor, Message, StreamKey};
183
184 fn create_stream_chunks() -> Vec<StreamChunk> {
185 let chunk1 = StreamChunk::from_pretty(
186 " I I
187 + 1 0
188 + 2 1
189 + 3 2
190 + 10 3
191 + 9 4
192 + 8 5",
193 );
194 let chunk2 = StreamChunk::from_pretty(
195 " I I
196 + 7 6
197 + 3 7
198 + 1 8
199 + 9 9",
200 );
201 let chunk3 = StreamChunk::from_pretty(
202 " I I
203 + 1 12
204 + 1 13
205 + 2 14
206 + 3 15",
207 );
208 vec![chunk1, chunk2, chunk3]
209 }
210
211 fn create_schema() -> Schema {
212 Schema {
213 fields: vec![
214 Field::unnamed(DataType::Int64),
215 Field::unnamed(DataType::Int64),
216 ],
217 }
218 }
219
220 fn storage_key() -> Vec<ColumnOrder> {
221 order_by()
222 }
223
224 fn order_by() -> Vec<ColumnOrder> {
225 vec![
226 ColumnOrder::new(0, OrderType::ascending()),
227 ColumnOrder::new(1, OrderType::ascending()),
228 ]
229 }
230
231 fn stream_key() -> StreamKey {
232 vec![0, 1]
233 }
234
235 fn create_source() -> Executor {
236 let mut chunks = create_stream_chunks();
237 MockSource::with_messages(vec![
238 Message::Barrier(Barrier::new_test_barrier(test_epoch(1))),
239 Message::Chunk(std::mem::take(&mut chunks[0])),
240 Message::Barrier(Barrier::new_test_barrier(test_epoch(2))),
241 Message::Chunk(std::mem::take(&mut chunks[1])),
242 Message::Barrier(Barrier::new_test_barrier(test_epoch(3))),
243 Message::Chunk(std::mem::take(&mut chunks[2])),
244 ])
245 .into_executor(create_schema(), stream_key())
246 }
247
248 #[tokio::test]
249 async fn test_append_only_top_n_executor_with_limit() {
250 let storage_key = storage_key();
251 let source = create_source();
252 let state_table = create_in_memory_state_table(
253 &[DataType::Int64, DataType::Int64],
254 &[OrderType::ascending(), OrderType::ascending()],
255 &stream_key(),
256 )
257 .await;
258
259 let schema = source.schema().clone();
260 let top_n = AppendOnlyTopNExecutor::<_, false>::new(
261 source,
262 ActorContext::for_test(0),
263 schema,
264 storage_key,
265 (0, 5),
266 order_by(),
267 state_table,
268 )
269 .unwrap();
270 let mut top_n = top_n.boxed().execute();
271
272 top_n.expect_barrier().await;
274 assert_eq!(
275 top_n.expect_chunk().await.sort_rows(),
276 StreamChunk::from_pretty(
277 " I I
278 + 1 0
279 + 2 1
280 + 3 2
281 + 9 4
282 + 8 5"
283 )
284 .sort_rows(),
285 );
286 top_n.expect_barrier().await;
290 assert_eq!(
291 top_n.expect_chunk().await.sort_rows(),
292 StreamChunk::from_pretty(
293 " I I
294 - 9 4
295 - 8 5
296 + 3 7
297 + 1 8"
298 )
299 .sort_rows(),
300 );
301 top_n.expect_barrier().await;
305 assert_eq!(
306 top_n.expect_chunk().await.sort_rows(),
307 StreamChunk::from_pretty(
308 " I I
309 - 3 7
310 + 1 12
311 - 3 2
312 + 1 13"
313 )
314 .sort_rows(),
315 );
316 }
319
320 #[tokio::test]
321 async fn test_append_only_top_n_executor_with_offset_and_limit() {
322 let source = create_source();
323 let state_table = create_in_memory_state_table(
324 &[DataType::Int64, DataType::Int64],
325 &[OrderType::ascending(), OrderType::ascending()],
326 &stream_key(),
327 )
328 .await;
329
330 let schema = source.schema().clone();
331 let top_n = AppendOnlyTopNExecutor::<_, false>::new(
332 source,
333 ActorContext::for_test(0),
334 schema,
335 storage_key(),
336 (3, 4),
337 order_by(),
338 state_table,
339 )
340 .unwrap();
341 let mut top_n = top_n.boxed().execute();
342
343 top_n.expect_barrier().await;
345 assert_eq!(
346 top_n.expect_chunk().await.sort_rows(),
347 StreamChunk::from_pretty(
348 " I I
349 + 10 3
350 + 9 4
351 + 8 5"
352 )
353 .sort_rows(),
354 );
355 top_n.expect_barrier().await;
359 assert_eq!(
360 top_n.expect_chunk().await.sort_rows(),
361 StreamChunk::from_pretty(
362 " I I
363 + 7 6
364 - 10 3
365 + 3 7
366 - 9 4
367 + 3 2"
368 )
369 .sort_rows(),
370 );
371 top_n.expect_barrier().await;
375 assert_eq!(
376 top_n.expect_chunk().await.sort_rows(),
377 StreamChunk::from_pretty(
378 " I I
379 - 8 5
380 + 2 1
381 - 7 6
382 + 1 13
383 - 3 7
384 + 2 14"
385 )
386 .sort_rows(),
387 );
388 }
391}