Skip to main content

risingwave_stream/executor/
now.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 std::sync::Arc;
16
17use itertools::Itertools;
18use risingwave_common::array::Op;
19use risingwave_common::row;
20use risingwave_common::types::{DefaultOrdered, Interval, Timestamptz, ToDatumRef};
21use risingwave_expr::capture_context;
22use risingwave_expr::expr::{
23    EvalErrorReport, InputRefExpression, LiteralExpression, NonStrictExpression,
24    SyncExpressionBoxExt, build_func_non_strict,
25};
26use risingwave_expr::expr_context::TIME_ZONE;
27use tokio::sync::mpsc::UnboundedReceiver;
28use tokio::time::Instant;
29use tokio_stream::wrappers::UnboundedReceiverStream;
30
31use crate::executor::monitor::StreamingMetrics;
32use crate::executor::prelude::*;
33use crate::task::{ActorEvalErrorReport, FragmentId};
34
35pub struct NowExecutor<S: StateStore> {
36    data_types: Vec<DataType>,
37
38    mode: NowMode,
39    eval_error_report: ActorEvalErrorReport,
40
41    /// Receiver of barrier channel.
42    barrier_receiver: UnboundedReceiver<Barrier>,
43
44    state_table: StateTable<S>,
45
46    progress_ratio: Option<f32>,
47
48    /// Metrics for observing the streaming `NOW()` clock and its drift from wall time.
49    metrics: Arc<StreamingMetrics>,
50    fragment_id: FragmentId,
51}
52
53pub enum NowMode {
54    /// Emit current timestamp on startup, update it on barrier.
55    UpdateCurrent,
56    /// Generate a series of timestamps starting from `start_timestamp` with `interval`.
57    /// Keep generating new timestamps on barrier.
58    GenerateSeries {
59        start_timestamp: Timestamptz,
60        interval: Interval,
61    },
62}
63
64enum ModeVars {
65    UpdateCurrent,
66    GenerateSeries {
67        chunk_builder: StreamChunkBuilder,
68        add_interval_expr: NonStrictExpression,
69    },
70}
71
72impl<S: StateStore> NowExecutor<S> {
73    #[allow(clippy::too_many_arguments)]
74    pub fn new(
75        data_types: Vec<DataType>,
76        mode: NowMode,
77        eval_error_report: ActorEvalErrorReport,
78        barrier_receiver: UnboundedReceiver<Barrier>,
79        state_table: StateTable<S>,
80        progress_ratio: Option<f32>,
81        streaming_metrics: Arc<StreamingMetrics>,
82        fragment_id: FragmentId,
83    ) -> Self {
84        Self {
85            data_types,
86            mode,
87            eval_error_report,
88            barrier_receiver,
89            state_table,
90            progress_ratio,
91            metrics: streaming_metrics,
92            fragment_id,
93        }
94    }
95
96    #[try_stream(ok = Message, error = StreamExecutorError)]
97    async fn execute_inner(self) {
98        let Self {
99            data_types,
100            mode,
101            eval_error_report,
102            barrier_receiver,
103            mut state_table,
104            progress_ratio,
105            metrics,
106            fragment_id,
107        } = self;
108
109        let mut executor_metrics = None;
110
111        info!("NowExecutor started. progress_ratio: {:?}", progress_ratio);
112
113        let max_chunk_size = crate::config::chunk_size();
114
115        // Whether the executor is paused.
116        let mut paused = false;
117        // The last timestamp **sent** to the downstream.
118        let mut last_timestamp_datum: Datum = None;
119
120        // Whether the first barrier is handled and `last_timestamp` is initialized.
121        let mut initialized = false;
122        // The monotonic time when the previous barrier batch was handled. The initial barrier
123        // establishes the baseline, excluding downtime before actor startup from catch-up.
124        let mut last_barrier_time = None;
125
126        let mut mode_vars = match &mode {
127            NowMode::UpdateCurrent => ModeVars::UpdateCurrent,
128            NowMode::GenerateSeries { interval, .. } => {
129                // in most cases there won't be more than one row except for the first time
130                let chunk_builder = StreamChunkBuilder::unlimited(data_types.clone(), Some(1));
131                let add_interval_expr =
132                    build_add_interval_expr_captured(*interval, eval_error_report)?;
133                ModeVars::GenerateSeries {
134                    chunk_builder,
135                    add_interval_expr,
136                }
137            }
138        };
139
140        const MAX_MERGE_BARRIER_SIZE: usize = 64;
141
142        #[for_await]
143        for barriers in
144            UnboundedReceiverStream::new(barrier_receiver).ready_chunks(MAX_MERGE_BARRIER_SIZE)
145        {
146            let barrier_time = Instant::now();
147            let elapsed = last_barrier_time
148                .replace(barrier_time)
149                .map(|last| barrier_time.saturating_duration_since(last));
150            let mut curr_timestamp_datum: Datum = None;
151            // Wall-clock reference derived from the most recently processed barrier's epoch.
152            // Used to observe how far the streaming NOW() lags real time.
153            let mut last_barrier_wall_ms: Option<i64> = None;
154            if barriers.len() > 1 {
155                warn!(
156                    "handle multiple barriers at once in now executor: {}",
157                    barriers.len()
158                );
159            }
160            for barrier in barriers {
161                let curr_epoch = barrier.get_curr_epoch();
162                let new_timestamp = curr_epoch.as_timestamptz();
163                last_barrier_wall_ms = Some(new_timestamp.timestamp_millis());
164                let pause_mutation =
165                    barrier
166                        .mutation
167                        .as_deref()
168                        .and_then(|mutation| match mutation {
169                            Mutation::Pause => Some(true),
170                            Mutation::Resume => Some(false),
171                            _ => None,
172                        });
173
174                if !initialized {
175                    let first_epoch = barrier.epoch;
176                    let is_pause_on_startup = barrier.is_pause_on_startup();
177                    yield Message::Barrier(barrier);
178                    // Handle the initial barrier.
179                    state_table.init_epoch(first_epoch).await?;
180                    last_timestamp_datum = state_table.get_from_one_value_table().await?;
181                    paused = is_pause_on_startup;
182                    initialized = true;
183                } else {
184                    state_table
185                        .commit_assert_no_update_vnode_bitmap(barrier.epoch)
186                        .await?;
187                    yield Message::Barrier(barrier);
188                }
189
190                // Only throttle UpdateCurrent. GenerateSeries stores the last emitted series
191                // value, which cannot advance if the throttled increment is smaller than its interval.
192                if let NowMode::UpdateCurrent = &mode
193                    && let Some(datum) = &last_timestamp_datum
194                    && let Some(progress_ratio) = progress_ratio
195                    && progress_ratio > 1.0
196                {
197                    let last_timestamp = datum.as_timestamptz();
198                    // curr_timestamp = min(last_timestamp + elapsed * progress_ratio, timestamp from epoch)
199                    // to avoid having a big gap between the last timestamp and the current timestamp,
200                    // which may cause excessive changes in downstream dynamic filter
201                    let elapsed_ms = elapsed.map_or(0.0, |elapsed| elapsed.as_secs_f64() * 1000.0);
202                    let progress_timestamp = last_timestamp
203                        .timestamp_millis()
204                        .checked_add((elapsed_ms * f64::from(progress_ratio)).ceil() as i64)
205                        .expect("progress_timestamp is out of i64 range");
206                    let adjusted_timestamp = if progress_timestamp
207                        < new_timestamp.timestamp_millis()
208                    {
209                        debug!(
210                            "adjusted next now timestamp from {} to {}. curr_epoch: {}, elapsed_ms: {}, progress_ratio: {}",
211                            new_timestamp.timestamp_millis(),
212                            progress_timestamp,
213                            curr_epoch,
214                            elapsed_ms,
215                            progress_ratio
216                        );
217                        Timestamptz::from_millis(progress_timestamp)
218                            .expect("progress_timestamp is out of timestamptz range")
219                    } else {
220                        new_timestamp
221                    };
222                    curr_timestamp_datum = Some(adjusted_timestamp.into());
223                } else {
224                    curr_timestamp_datum = Some(new_timestamp.into());
225                }
226
227                // Update paused state.
228                if let Some(pause_mutation) = pause_mutation {
229                    paused = pause_mutation;
230                }
231            }
232
233            // Do not yield any messages if paused.
234            if paused {
235                continue;
236            }
237
238            match (&mode, &mut mode_vars) {
239                (NowMode::UpdateCurrent, ModeVars::UpdateCurrent) => {
240                    let chunk = if last_timestamp_datum.is_some() {
241                        let last_row = row::once(&last_timestamp_datum);
242                        let row = row::once(&curr_timestamp_datum);
243                        state_table.update(last_row, row);
244
245                        StreamChunk::from_rows(
246                            &[(Op::Delete, last_row), (Op::Insert, row)],
247                            &data_types,
248                        )
249                    } else {
250                        let row = row::once(&curr_timestamp_datum);
251                        state_table.insert(row);
252
253                        StreamChunk::from_rows(&[(Op::Insert, row)], &data_types)
254                    };
255
256                    yield Message::Chunk(chunk);
257                    last_timestamp_datum.clone_from(&curr_timestamp_datum)
258                }
259                (
260                    &NowMode::GenerateSeries {
261                        start_timestamp, ..
262                    },
263                    &mut ModeVars::GenerateSeries {
264                        ref mut chunk_builder,
265                        ref add_interval_expr,
266                    },
267                ) => {
268                    if last_timestamp_datum.is_none() {
269                        // We haven't emit any timestamp yet. Let's emit the first one and populate the state table.
270                        let first = Some(start_timestamp.into());
271                        let first_row = row::once(&first);
272                        let _ = chunk_builder.append_row(Op::Insert, first_row);
273                        state_table.insert(first_row);
274                        last_timestamp_datum = first;
275                    }
276
277                    // Now let's step through the timestamps from the last timestamp to the current timestamp.
278                    // We use `last_row` as a temporary cursor to track the progress, and won't touch `last_timestamp`
279                    // until the end of the loop, so that `last_timestamp` is always synced with the state table.
280                    let mut last_row = OwnedRow::new(vec![last_timestamp_datum.clone()]);
281
282                    loop {
283                        if chunk_builder.size() >= max_chunk_size {
284                            // Manually yield the chunk when size exceeds the limit. We don't want to use chunk builder
285                            // with limited size here because the initial capacity can be too large for most cases.
286                            // Basically only the first several chunks can potentially exceed the `max_chunk_size`.
287                            if let Some(chunk) = chunk_builder.take() {
288                                yield Message::Chunk(chunk);
289                            }
290                        }
291
292                        let next = add_interval_expr.eval_row_infallible(&last_row).await;
293                        if DefaultOrdered(next.to_datum_ref())
294                            > DefaultOrdered(curr_timestamp_datum.to_datum_ref())
295                        {
296                            // We only increase the timestamp to the current timestamp.
297                            break;
298                        }
299
300                        let next_row = OwnedRow::new(vec![next]);
301                        let _ = chunk_builder.append_row(Op::Insert, &next_row);
302                        last_row = next_row;
303                    }
304
305                    if let Some(chunk) = chunk_builder.take() {
306                        yield Message::Chunk(chunk);
307                    }
308
309                    // Update the last timestamp.
310                    state_table.update(row::once(&last_timestamp_datum), &last_row);
311                    last_timestamp_datum =
312                        Itertools::exactly_one(last_row.into_inner().into_vec().into_iter())
313                            .unwrap();
314                }
315                _ => unreachable!(),
316            }
317
318            let curr_timestamp_datum = curr_timestamp_datum.unwrap();
319
320            if let Some(wall_ms) = last_barrier_wall_ms
321                && let ScalarImpl::Timestamptz(ts) = &curr_timestamp_datum
322            {
323                let streaming_now_ms = ts.timestamp_millis();
324                let (streaming_clock_ms, wall_clock_drift_ms) = executor_metrics
325                    .get_or_insert_with(|| {
326                        // A NOW fragment has only one actor, and recovery starts its replacement
327                        // only after the old actor stops. Therefore, on this node, fragment_id
328                        // uniquely identifies the gauge writer. If multiple actors wrote with the
329                        // same fragment_id concurrently, the last gauge update would win.
330                        let label = fragment_id.to_string();
331                        (
332                            metrics
333                                .now_streaming_clock_ms
334                                .with_guarded_label_values(&[&label]),
335                            metrics
336                                .now_wall_clock_drift_ms
337                                .with_guarded_label_values(&[&label]),
338                        )
339                    });
340                streaming_clock_ms.set(streaming_now_ms);
341                wall_clock_drift_ms.set(wall_ms.saturating_sub(streaming_now_ms));
342            }
343
344            yield Message::Watermark(Watermark::new(
345                0,
346                DataType::Timestamptz,
347                curr_timestamp_datum,
348            ));
349        }
350    }
351}
352
353impl<S: StateStore> Execute for NowExecutor<S> {
354    fn execute(self: Box<Self>) -> BoxedMessageStream {
355        self.execute_inner().boxed()
356    }
357}
358
359#[capture_context(TIME_ZONE)]
360pub fn build_add_interval_expr(
361    time_zone: &str,
362    interval: Interval,
363    eval_error_report: impl EvalErrorReport + 'static,
364) -> risingwave_expr::Result<NonStrictExpression> {
365    let timestamptz_input = InputRefExpression::new(DataType::Timestamptz, 0);
366    let interval = LiteralExpression::new(DataType::Interval, Some(interval.into()));
367    let time_zone = LiteralExpression::new(DataType::Varchar, Some(time_zone.into()));
368
369    use risingwave_pb::expr::expr_node::PbType as PbExprType;
370    build_func_non_strict(
371        PbExprType::AddWithTimeZone,
372        DataType::Timestamptz,
373        vec![
374            timestamptz_input.boxed(),
375            interval.boxed(),
376            time_zone.boxed(),
377        ],
378        eval_error_report,
379    )
380}
381
382#[cfg(test)]
383mod tests {
384    #[cfg(not(madsim))]
385    use std::time::Duration;
386
387    use risingwave_common::catalog::{ColumnDesc, ColumnId, TableId};
388    use risingwave_common::test_prelude::StreamChunkTestExt;
389    use risingwave_common::types::test_utils::IntervalTestExt;
390    use risingwave_common::util::epoch::test_epoch;
391    use risingwave_storage::memory::MemoryStateStore;
392    use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
393
394    use super::*;
395    use crate::common::table::test_utils::gen_pbtable;
396    use crate::executor::test_utils::StreamExecutorTestExt;
397
398    #[tokio::test]
399    async fn test_now() -> StreamExecutorResult<()> {
400        let state_store = create_state_store();
401        let (tx, mut now) = create_executor(NowMode::UpdateCurrent, &state_store).await;
402
403        // Init barrier
404        tx.send(Barrier::new_test_barrier(test_epoch(1))).unwrap();
405
406        // Consume the barrier
407        now.next_unwrap_ready_barrier()?;
408
409        // Consume the data chunk
410        let chunk_msg = now.next_unwrap_ready_chunk()?;
411
412        assert_eq!(
413            chunk_msg.compact_vis(),
414            StreamChunk::from_pretty(
415                " TZ
416                + 2021-04-01T00:00:00.001Z"
417            )
418        );
419
420        // Consume the watermark
421        let watermark = now.next_unwrap_ready_watermark()?;
422
423        assert_eq!(
424            watermark,
425            Watermark::new(
426                0,
427                DataType::Timestamptz,
428                ScalarImpl::Timestamptz("2021-04-01T00:00:00.001Z".parse().unwrap())
429            )
430        );
431
432        tx.send(Barrier::with_prev_epoch_for_test(
433            test_epoch(2),
434            test_epoch(1),
435        ))
436        .unwrap();
437
438        // Consume the barrier
439        now.next_unwrap_ready_barrier()?;
440
441        // Consume the data chunk
442        let chunk_msg = now.next_unwrap_ready_chunk()?;
443
444        assert_eq!(
445            chunk_msg.compact_vis(),
446            StreamChunk::from_pretty(
447                " TZ
448                - 2021-04-01T00:00:00.001Z
449                + 2021-04-01T00:00:00.002Z"
450            )
451        );
452
453        // Consume the watermark
454        let watermark = now.next_unwrap_ready_watermark()?;
455
456        assert_eq!(
457            watermark,
458            Watermark::new(
459                0,
460                DataType::Timestamptz,
461                ScalarImpl::Timestamptz("2021-04-01T00:00:00.002Z".parse().unwrap())
462            )
463        );
464
465        // No more messages until the next barrier
466        now.next_unwrap_pending();
467
468        // Recovery
469        drop((tx, now));
470        let (tx, mut now) = create_executor(NowMode::UpdateCurrent, &state_store).await;
471        tx.send(Barrier::with_prev_epoch_for_test(
472            test_epoch(3),
473            test_epoch(1),
474        ))
475        .unwrap();
476
477        // Consume the barrier
478        now.next_unwrap_ready_barrier()?;
479
480        // Consume the data chunk
481        let chunk_msg = now.next_unwrap_ready_chunk()?;
482        assert_eq!(
483            chunk_msg.compact_vis(),
484            // the last chunk was not checkpointed so the deleted old value should be `001`
485            StreamChunk::from_pretty(
486                " TZ
487                - 2021-04-01T00:00:00.001Z
488                + 2021-04-01T00:00:00.003Z"
489            )
490        );
491
492        // Consume the watermark
493        let watermark = now.next_unwrap_ready_watermark()?;
494
495        assert_eq!(
496            watermark,
497            Watermark::new(
498                0,
499                DataType::Timestamptz,
500                ScalarImpl::Timestamptz("2021-04-01T00:00:00.003Z".parse().unwrap())
501            )
502        );
503
504        // Recovery with paused
505        drop((tx, now));
506        let (tx, mut now) = create_executor(NowMode::UpdateCurrent, &state_store).await;
507        tx.send(
508            Barrier::with_prev_epoch_for_test(test_epoch(4), test_epoch(1))
509                .with_mutation(Mutation::Pause),
510        )
511        .unwrap();
512
513        // Consume the barrier
514        now.next_unwrap_ready_barrier()?;
515
516        // There should be no messages until `Resume`
517        now.next_unwrap_pending();
518
519        // Resume barrier
520        tx.send(
521            Barrier::with_prev_epoch_for_test(test_epoch(5), test_epoch(4))
522                .with_mutation(Mutation::Resume),
523        )
524        .unwrap();
525
526        // Consume the barrier
527        now.next_unwrap_ready_barrier()?;
528
529        // Consume the data chunk
530        let chunk_msg = now.next_unwrap_ready_chunk()?;
531        assert_eq!(
532            chunk_msg.compact_vis(),
533            StreamChunk::from_pretty(
534                " TZ
535                - 2021-04-01T00:00:00.001Z
536                + 2021-04-01T00:00:00.005Z"
537            )
538        );
539
540        // Consume the watermark
541        let watermark = now.next_unwrap_ready_watermark()?;
542
543        assert_eq!(
544            watermark,
545            Watermark::new(
546                0,
547                DataType::Timestamptz,
548                ScalarImpl::Timestamptz("2021-04-01T00:00:00.005Z".parse().unwrap())
549            )
550        );
551
552        Ok(())
553    }
554
555    #[tokio::test]
556    async fn test_now_start_with_paused() -> StreamExecutorResult<()> {
557        let state_store = create_state_store();
558        let (tx, mut now) = create_executor(NowMode::UpdateCurrent, &state_store).await;
559
560        // Init barrier
561        tx.send(Barrier::new_test_barrier(test_epoch(1)).with_mutation(Mutation::Pause))
562            .unwrap();
563
564        // Consume the barrier
565        now.next_unwrap_ready_barrier()?;
566
567        // There should be no messages until `Resume`
568        now.next_unwrap_pending();
569
570        // Resume barrier
571        tx.send(
572            Barrier::with_prev_epoch_for_test(test_epoch(2), test_epoch(1))
573                .with_mutation(Mutation::Resume),
574        )
575        .unwrap();
576
577        // Consume the barrier
578        now.next_unwrap_ready_barrier()?;
579
580        // Consume the data chunk
581        let chunk_msg = now.next_unwrap_ready_chunk()?;
582
583        assert_eq!(
584            chunk_msg.compact_vis(),
585            StreamChunk::from_pretty(
586                " TZ
587                + 2021-04-01T00:00:00.002Z" // <- the timestamp is extracted from the current epoch
588            )
589        );
590
591        // Consume the watermark
592        let watermark = now.next_unwrap_ready_watermark()?;
593
594        assert_eq!(
595            watermark,
596            Watermark::new(
597                0,
598                DataType::Timestamptz,
599                ScalarImpl::Timestamptz("2021-04-01T00:00:00.002Z".parse().unwrap())
600            )
601        );
602
603        // No more messages until the next barrier
604        now.next_unwrap_pending();
605
606        Ok(())
607    }
608
609    #[tokio::test]
610    async fn test_now_generate_series() -> StreamExecutorResult<()> {
611        for progress_ratio in [None, Some(2.0)] {
612            TIME_ZONE::scope(
613                "UTC".to_owned(),
614                test_now_generate_series_inner(progress_ratio),
615            )
616            .await?;
617        }
618        Ok(())
619    }
620
621    #[tokio::test]
622    async fn test_now_generate_series_with_progress_ratio() -> StreamExecutorResult<()> {
623        TIME_ZONE::scope("UTC".to_owned(), async {
624            let state_store = create_state_store();
625            let mode = || NowMode::GenerateSeries {
626                start_timestamp: "2021-04-01T00:00:00Z".parse().unwrap(),
627                // Greater than barrier_interval_ms (1000) * progress_ratio (2).
628                interval: Interval::from_millis(10_000),
629            };
630            let (tx, mut now) =
631                create_executor_with_progress_ratio(mode(), &state_store, Some(2.0)).await;
632
633            tx.send(Barrier::new_test_barrier(test_epoch(5000)))
634                .unwrap();
635            now.next_unwrap_ready_barrier()?;
636            assert_eq!(
637                now.next_unwrap_ready_chunk()?.compact_vis(),
638                StreamChunk::from_pretty("TZ\n+ 2021-04-01T00:00:00Z")
639            );
640            assert_eq!(
641                now.next_unwrap_ready_watermark()?,
642                Watermark::new(
643                    0,
644                    DataType::Timestamptz,
645                    "2021-04-01T00:00:05Z"
646                        .parse::<Timestamptz>()
647                        .unwrap()
648                        .into(),
649                )
650            );
651            now.next_unwrap_pending();
652
653            // Watermarks must advance even when no new series value is emitted.
654            // The old throttle would regress the watermark from 5s to 2s and stall there.
655            for (curr, prev, expected_watermark) in [
656                (6000, 5000, "2021-04-01T00:00:06Z"),
657                (10_000, 6000, "2021-04-01T00:00:10Z"),
658                (11_000, 10_000, "2021-04-01T00:00:11Z"),
659            ] {
660                tx.send(Barrier::with_prev_epoch_for_test(
661                    test_epoch(curr),
662                    test_epoch(prev),
663                ))
664                .unwrap();
665                now.next_unwrap_ready_barrier()?;
666                if curr == 10_000 {
667                    assert_eq!(
668                        now.next_unwrap_ready_chunk()?.compact_vis(),
669                        StreamChunk::from_pretty("TZ\n+ 2021-04-01T00:00:10Z")
670                    );
671                }
672                assert_eq!(
673                    now.next_unwrap_ready_watermark()?,
674                    Watermark::new(
675                        0,
676                        DataType::Timestamptz,
677                        expected_watermark.parse::<Timestamptz>().unwrap().into(),
678                    )
679                );
680                now.next_unwrap_pending();
681            }
682
683            // Recover after a barrier with no new series value. Catch up from the persisted
684            // series cursor, preserving every interval rather than jumping to the barrier time.
685            drop((tx, now));
686            let (tx, mut now) =
687                create_executor_with_progress_ratio(mode(), &state_store, Some(2.0)).await;
688            tx.send(Barrier::with_prev_epoch_for_test(
689                test_epoch(35_000),
690                test_epoch(11_000),
691            ))
692            .unwrap();
693            now.next_unwrap_ready_barrier()?;
694            assert_eq!(
695                now.next_unwrap_ready_chunk()?.compact_vis(),
696                StreamChunk::from_pretty(
697                    "TZ
698                    + 2021-04-01T00:00:20Z
699                    + 2021-04-01T00:00:30Z"
700                )
701            );
702            assert_eq!(
703                now.next_unwrap_ready_watermark()?,
704                Watermark::new(
705                    0,
706                    DataType::Timestamptz,
707                    "2021-04-01T00:00:35Z"
708                        .parse::<Timestamptz>()
709                        .unwrap()
710                        .into(),
711                )
712            );
713            now.next_unwrap_pending();
714            Ok(())
715        })
716        .await
717    }
718
719    #[cfg(not(madsim))]
720    #[tokio::test(start_paused = true)]
721    async fn test_now_with_progress_ratio() -> StreamExecutorResult<()> {
722        let state_store = create_state_store();
723        let progress_ratio = Some(2.0);
724        let (tx, mut now) = create_executor_with_progress_ratio(
725            NowMode::UpdateCurrent,
726            &state_store,
727            progress_ratio,
728        )
729        .await;
730
731        // Init barrier at epoch 1 (timestamp 2021-04-01T00:00:00.001Z)
732        tx.send(Barrier::new_test_barrier(test_epoch(1))).unwrap();
733
734        // Consume the barrier
735        now.next_unwrap_ready_barrier()?;
736
737        // Consume the data chunk
738        let chunk_msg = now.next_unwrap_ready_chunk()?;
739
740        assert_eq!(
741            chunk_msg.compact_vis(),
742            StreamChunk::from_pretty(
743                " TZ
744                + 2021-04-01T00:00:00.001Z"
745            )
746        );
747
748        // Consume the watermark
749        let watermark = now.next_unwrap_ready_watermark()?;
750
751        assert_eq!(
752            watermark,
753            Watermark::new(
754                0,
755                DataType::Timestamptz,
756                ScalarImpl::Timestamptz("2021-04-01T00:00:00.001Z".parse().unwrap())
757            )
758        );
759
760        // Send next barrier at epoch 5000 (timestamp 2021-04-01T00:00:00.005Z)
761        // With progress_ratio = 2.0 and one second of elapsed time,
762        // adjusted timestamp should be: 1 + (1000 * 2.0) = 2001ms = 2021-04-01T00:00:02.001Z
763        // Since 2001 < 5000, the adjusted timestamp should be used
764        tokio::time::advance(Duration::from_secs(1)).await;
765        tx.send(Barrier::with_prev_epoch_for_test(
766            test_epoch(5000),
767            test_epoch(1),
768        ))
769        .unwrap();
770
771        // Consume the barrier
772        now.next_unwrap_ready_barrier()?;
773
774        // Consume the data chunk - should show adjusted timestamp
775        let chunk_msg = now.next_unwrap_ready_chunk()?;
776
777        assert_eq!(
778            chunk_msg.compact_vis(),
779            StreamChunk::from_pretty(
780                " TZ
781                - 2021-04-01T00:00:00.001Z
782                + 2021-04-01T00:00:02.001Z" // adjusted timestamp
783            )
784        );
785
786        // Consume the watermark
787        let watermark = now.next_unwrap_ready_watermark()?;
788
789        assert_eq!(
790            watermark,
791            Watermark::new(
792                0,
793                DataType::Timestamptz,
794                ScalarImpl::Timestamptz("2021-04-01T00:00:02.001Z".parse().unwrap())
795            )
796        );
797
798        // Send another barrier at epoch 10000 (timestamp 2021-04-01T00:00:00.010Z)
799        // With another second elapsed, the adjusted timestamp should be 4001ms.
800        // Since 4001 < 10000, the adjusted timestamp should be used again
801        tokio::time::advance(Duration::from_secs(1)).await;
802        tx.send(Barrier::with_prev_epoch_for_test(
803            test_epoch(10000),
804            test_epoch(5000),
805        ))
806        .unwrap();
807
808        // Consume the barrier
809        now.next_unwrap_ready_barrier()?;
810
811        // Consume the data chunk
812        let chunk_msg = now.next_unwrap_ready_chunk()?;
813
814        assert_eq!(
815            chunk_msg.compact_vis(),
816            StreamChunk::from_pretty(
817                " TZ
818                - 2021-04-01T00:00:02.001Z
819                + 2021-04-01T00:00:04.001Z" // adjusted timestamp
820            )
821        );
822
823        // Consume the watermark
824        let watermark = now.next_unwrap_ready_watermark()?;
825
826        assert_eq!(
827            watermark,
828            Watermark::new(
829                0,
830                DataType::Timestamptz,
831                ScalarImpl::Timestamptz("2021-04-01T00:00:04.001Z".parse().unwrap())
832            )
833        );
834
835        // Send another barrier at epoch 15 (timestamp 2021-04-01T00:00:00.015Z)
836        // With another second elapsed, the adjusted timestamp should be 6001ms.
837        // Since 6001 < 15, the adjusted timestamp should be used
838        tokio::time::advance(Duration::from_secs(1)).await;
839        tx.send(Barrier::with_prev_epoch_for_test(
840            test_epoch(15000),
841            test_epoch(10000),
842        ))
843        .unwrap();
844
845        // Consume the barrier
846        now.next_unwrap_ready_barrier()?;
847
848        // Consume the data chunk
849        let chunk_msg = now.next_unwrap_ready_chunk()?;
850
851        assert_eq!(
852            chunk_msg.compact_vis(),
853            StreamChunk::from_pretty(
854                " TZ
855                - 2021-04-01T00:00:04.001Z
856                + 2021-04-01T00:00:06.001Z" // adjusted timestamp
857            )
858        );
859
860        // Consume the watermark
861        let watermark = now.next_unwrap_ready_watermark()?;
862
863        assert_eq!(
864            watermark,
865            Watermark::new(
866                0,
867                DataType::Timestamptz,
868                ScalarImpl::Timestamptz("2021-04-01T00:00:06.001Z".parse().unwrap())
869            )
870        );
871
872        // Now send a barrier at epoch 20 (timestamp 2021-04-01T00:00:00.020Z)
873        // With another second elapsed, the adjusted timestamp should be 8001ms.
874        // Since 8001 < 20, the adjusted timestamp should be used
875        tokio::time::advance(Duration::from_secs(1)).await;
876        tx.send(Barrier::with_prev_epoch_for_test(
877            test_epoch(20000),
878            test_epoch(15000),
879        ))
880        .unwrap();
881
882        // Consume the barrier
883        now.next_unwrap_ready_barrier()?;
884
885        // Consume the data chunk
886        let chunk_msg = now.next_unwrap_ready_chunk()?;
887
888        assert_eq!(
889            chunk_msg.compact_vis(),
890            StreamChunk::from_pretty(
891                " TZ
892                - 2021-04-01T00:00:06.001Z
893                + 2021-04-01T00:00:08.001Z" // adjusted timestamp
894            )
895        );
896
897        // Consume the watermark
898        let watermark = now.next_unwrap_ready_watermark()?;
899
900        assert_eq!(
901            watermark,
902            Watermark::new(
903                0,
904                DataType::Timestamptz,
905                ScalarImpl::Timestamptz("2021-04-01T00:00:08.001Z".parse().unwrap())
906            )
907        );
908
909        // Test case where epoch timestamp is smaller than adjusted timestamp
910        // Send barrier at epoch 25 (timestamp 2021-04-01T00:00:00.025Z)
911        // After another second, the adjusted timestamp would be 10001ms.
912        // Since 10001 < 25, use adjusted timestamp
913        tokio::time::advance(Duration::from_secs(1)).await;
914        tx.send(Barrier::with_prev_epoch_for_test(
915            test_epoch(25000),
916            test_epoch(20000),
917        ))
918        .unwrap();
919
920        // Consume the barrier
921        now.next_unwrap_ready_barrier()?;
922
923        // Consume the data chunk
924        let chunk_msg = now.next_unwrap_ready_chunk()?;
925
926        assert_eq!(
927            chunk_msg.compact_vis(),
928            StreamChunk::from_pretty(
929                " TZ
930                - 2021-04-01T00:00:08.001Z
931                + 2021-04-01T00:00:10.001Z" // adjusted timestamp
932            )
933        );
934
935        // Consume the watermark
936        let watermark = now.next_unwrap_ready_watermark()?;
937
938        assert_eq!(
939            watermark,
940            Watermark::new(
941                0,
942                DataType::Timestamptz,
943                ScalarImpl::Timestamptz("2021-04-01T00:00:10.001Z".parse().unwrap())
944            )
945        );
946
947        // Finally test when epoch timestamp is larger than adjusted timestamp
948        // Send barrier at epoch 30 (timestamp 2021-04-01T00:00:00.030Z)
949        // After another second, the adjusted timestamp would be 12001ms.
950        // Since 12001 < 30, use adjusted timestamp
951        tokio::time::advance(Duration::from_secs(1)).await;
952        tx.send(Barrier::with_prev_epoch_for_test(
953            test_epoch(30000),
954            test_epoch(25000),
955        ))
956        .unwrap();
957
958        // Consume the barrier
959        now.next_unwrap_ready_barrier()?;
960
961        // Consume the data chunk
962        let chunk_msg = now.next_unwrap_ready_chunk()?;
963
964        assert_eq!(
965            chunk_msg.compact_vis(),
966            StreamChunk::from_pretty(
967                " TZ
968                - 2021-04-01T00:00:10.001Z
969                + 2021-04-01T00:00:12.001Z" // adjusted timestamp
970            )
971        );
972
973        // Consume the watermark
974        let watermark = now.next_unwrap_ready_watermark()?;
975
976        assert_eq!(
977            watermark,
978            Watermark::new(
979                0,
980                DataType::Timestamptz,
981                ScalarImpl::Timestamptz("2021-04-01T00:00:12.001Z".parse().unwrap())
982            )
983        );
984
985        Ok(())
986    }
987
988    #[cfg(not(madsim))]
989    #[tokio::test(start_paused = true)]
990    async fn test_now_progress_ratio_uses_elapsed_time() -> StreamExecutorResult<()> {
991        let state_store = create_state_store();
992        let (tx, mut now) =
993            create_executor_with_progress_ratio(NowMode::UpdateCurrent, &state_store, Some(2.0))
994                .await;
995
996        tx.send(Barrier::new_test_barrier(test_epoch(1))).unwrap();
997        now.next_unwrap_ready_barrier()?;
998        now.next_unwrap_ready_chunk()?;
999        now.next_unwrap_ready_watermark()?;
1000
1001        tokio::time::advance(Duration::from_secs(5)).await;
1002        tx.send(Barrier::with_prev_epoch_for_test(
1003            test_epoch(20000),
1004            test_epoch(1),
1005        ))
1006        .unwrap();
1007
1008        now.next_unwrap_ready_barrier()?;
1009        let chunk = now.next_unwrap_ready_chunk()?;
1010        assert_eq!(
1011            chunk.compact_vis(),
1012            StreamChunk::from_pretty(
1013                " TZ
1014                - 2021-04-01T00:00:00.001Z
1015                + 2021-04-01T00:00:10.001Z"
1016            )
1017        );
1018
1019        Ok(())
1020    }
1021
1022    async fn test_now_generate_series_inner(
1023        progress_ratio: Option<f32>,
1024    ) -> StreamExecutorResult<()> {
1025        let start_timestamp = Timestamptz::from_secs(1617235190).unwrap(); // 2021-03-31 23:59:50 UTC
1026        let interval = Interval::from_millis(1000); // 1s interval
1027
1028        let state_store = create_state_store();
1029        let (tx, mut now) = create_executor_with_progress_ratio(
1030            NowMode::GenerateSeries {
1031                start_timestamp,
1032                interval,
1033            },
1034            &state_store,
1035            progress_ratio,
1036        )
1037        .await;
1038
1039        // Init barrier
1040        tx.send(Barrier::new_test_barrier(test_epoch(1000)))
1041            .unwrap();
1042        now.next_unwrap_ready_barrier()?;
1043
1044        // Initial timestamps
1045        let chunk = now.next_unwrap_ready_chunk()?;
1046        assert_eq!(chunk.cardinality(), 12); // seconds from 23:59:50 to 00:00:01 (inclusive)
1047
1048        assert_eq!(
1049            now.next_unwrap_ready_watermark()?,
1050            Watermark::new(
1051                0,
1052                DataType::Timestamptz,
1053                ScalarImpl::Timestamptz("2021-04-01T00:00:01.000Z".parse().unwrap())
1054            )
1055        );
1056
1057        tx.send(Barrier::with_prev_epoch_for_test(
1058            test_epoch(2000),
1059            test_epoch(1000),
1060        ))
1061        .unwrap();
1062        tx.send(Barrier::with_prev_epoch_for_test(
1063            test_epoch(3000),
1064            test_epoch(2000),
1065        ))
1066        .unwrap();
1067
1068        now.next_unwrap_ready_barrier()?;
1069        now.next_unwrap_ready_barrier()?;
1070
1071        let chunk = now.next_unwrap_ready_chunk()?;
1072        assert_eq!(
1073            chunk.compact_vis(),
1074            StreamChunk::from_pretty(
1075                " TZ
1076                + 2021-04-01T00:00:02.000Z
1077                + 2021-04-01T00:00:03.000Z"
1078            )
1079        );
1080
1081        let watermark = now.next_unwrap_ready_watermark()?;
1082        assert_eq!(
1083            watermark,
1084            Watermark::new(
1085                0,
1086                DataType::Timestamptz,
1087                ScalarImpl::Timestamptz("2021-04-01T00:00:03.000Z".parse().unwrap())
1088            )
1089        );
1090
1091        // Recovery
1092        drop((tx, now));
1093        let (tx, mut now) = create_executor_with_progress_ratio(
1094            NowMode::GenerateSeries {
1095                start_timestamp,
1096                interval,
1097            },
1098            &state_store,
1099            progress_ratio,
1100        )
1101        .await;
1102
1103        tx.send(Barrier::with_prev_epoch_for_test(
1104            test_epoch(4000),
1105            test_epoch(2000),
1106        ))
1107        .unwrap();
1108
1109        now.next_unwrap_ready_barrier()?;
1110
1111        let chunk = now.next_unwrap_ready_chunk()?;
1112        assert_eq!(
1113            chunk.compact_vis(),
1114            StreamChunk::from_pretty(
1115                " TZ
1116                + 2021-04-01T00:00:02.000Z
1117                + 2021-04-01T00:00:03.000Z
1118                + 2021-04-01T00:00:04.000Z"
1119            )
1120        );
1121
1122        let watermark = now.next_unwrap_ready_watermark()?;
1123        assert_eq!(
1124            watermark,
1125            Watermark::new(
1126                0,
1127                DataType::Timestamptz,
1128                ScalarImpl::Timestamptz("2021-04-01T00:00:04.000Z".parse().unwrap())
1129            )
1130        );
1131
1132        Ok(())
1133    }
1134
1135    fn create_state_store() -> MemoryStateStore {
1136        MemoryStateStore::new()
1137    }
1138
1139    async fn build_executor(
1140        mode: NowMode,
1141        state_store: &MemoryStateStore,
1142        progress_ratio: Option<f32>,
1143    ) -> (UnboundedSender<Barrier>, NowExecutor<MemoryStateStore>) {
1144        let table_id = TableId::new(1);
1145        let column_descs = vec![ColumnDesc::unnamed(ColumnId::new(0), DataType::Timestamptz)];
1146        let state_table = StateTable::from_table_catalog(
1147            &gen_pbtable(table_id, column_descs, vec![], vec![], 0),
1148            state_store.clone(),
1149            None,
1150        )
1151        .await;
1152
1153        let (sender, barrier_receiver) = unbounded_channel();
1154
1155        let eval_error_report = ActorEvalErrorReport {
1156            actor_context: ActorContext::for_test(123),
1157            identity: "NowExecutor".into(),
1158        };
1159        let now_executor = NowExecutor::new(
1160            vec![DataType::Timestamptz],
1161            mode,
1162            eval_error_report,
1163            barrier_receiver,
1164            state_table,
1165            progress_ratio,
1166            Arc::new(StreamingMetrics::unused()),
1167            0.into(),
1168        );
1169        (sender, now_executor)
1170    }
1171
1172    async fn create_executor_with_progress_ratio(
1173        mode: NowMode,
1174        state_store: &MemoryStateStore,
1175        progress_ratio: Option<f32>,
1176    ) -> (UnboundedSender<Barrier>, BoxedMessageStream) {
1177        let (sender, executor) = build_executor(mode, state_store, progress_ratio).await;
1178        (sender, executor.boxed().execute())
1179    }
1180
1181    async fn create_executor(
1182        mode: NowMode,
1183        state_store: &MemoryStateStore,
1184    ) -> (UnboundedSender<Barrier>, BoxedMessageStream) {
1185        create_executor_with_progress_ratio(mode, state_store, None).await
1186    }
1187}