1use std::collections::VecDeque;
62use std::future::pending;
63use std::mem::replace;
64use std::pin::Pin;
65
66use anyhow::anyhow;
67use futures::future::{BoxFuture, Either, select};
68use futures::stream::StreamFuture;
69use futures::{FutureExt, StreamExt, TryStreamExt};
70use futures_async_stream::try_stream;
71use risingwave_common::array::StreamChunk;
72use risingwave_common::bitmap::Bitmap;
73use risingwave_common::catalog::{TableId, TableOption};
74use risingwave_common::must_match;
75use risingwave_common::util::epoch::EpochPair;
76use risingwave_common_estimate_size::EstimateSize;
77use risingwave_connector::sink::log_store::{ChunkId, LogStoreResult};
78use risingwave_pb::id::FragmentId;
79use risingwave_storage::StateStore;
80use risingwave_storage::store::timeout_auto_rebuild::TimeoutAutoRebuildIter;
81use risingwave_storage::store::{
82 LocalStateStore, NewLocalOptions, OpConsistencyLevel, StateStoreRead,
83};
84use rw_futures_util::drop_either_future;
85use tokio::time::{Duration, Instant, Sleep, sleep_until};
86use tokio_stream::adapters::Peekable;
87
88use crate::common::log_store_impl::kv_log_store::buffer::LogStoreBufferItem;
89use crate::common::log_store_impl::kv_log_store::reader::LogStoreReadStateStreamRangeStart;
90use crate::common::log_store_impl::kv_log_store::serde::{
91 KvLogStoreItem, LogStoreItemMergeStream, LogStoreRowSerde,
92};
93use crate::common::log_store_impl::kv_log_store::state::{
94 LogStorePostSealCurrentEpoch, LogStoreReadState, LogStoreStateWriteChunkFuture,
95 LogStoreWriteState, new_log_store_state,
96};
97use crate::common::log_store_impl::kv_log_store::{
98 Epoch, FIRST_SEQ_ID, FlushInfo, LogStoreVnodeProgress, SeqId,
99};
100use crate::executor::prelude::*;
101use crate::executor::sync_kv_log_store::metrics::SyncedKvLogStoreMetrics;
102use crate::executor::{
103 Barrier, BoxedMessageStream, Message, StreamExecutorError, StreamExecutorResult, Watermark,
104};
105
106pub mod metrics {
107 use risingwave_common::id::FragmentId;
108 #[cfg(test)]
109 use risingwave_common::metrics::GaugeAggregation;
110 use risingwave_common::metrics::{LabelGuardedIntCounter, RelabeledAggregatedIntGauge};
111
112 use crate::common::log_store_impl::kv_log_store::KvLogStoreReadMetrics;
113 use crate::executor::monitor::StreamingMetrics;
114 use crate::task::ActorId;
115
116 #[derive(Clone)]
117 pub struct SyncedKvLogStoreMetrics {
118 pub unclean_state: LabelGuardedIntCounter,
120 pub clean_state: LabelGuardedIntCounter,
121 pub wait_next_poll_ns: LabelGuardedIntCounter,
122
123 pub storage_write_count: LabelGuardedIntCounter,
125 pub storage_write_size: LabelGuardedIntCounter,
126 pub pause_duration_ns: LabelGuardedIntCounter,
127
128 pub buffer_unconsumed_item_count: RelabeledAggregatedIntGauge,
130 pub buffer_unconsumed_row_count: RelabeledAggregatedIntGauge,
131 pub buffer_unconsumed_epoch_count: RelabeledAggregatedIntGauge,
132 pub buffer_unconsumed_min_epoch: RelabeledAggregatedIntGauge,
133 pub buffer_memory_bytes: RelabeledAggregatedIntGauge,
134 pub buffer_read_count: LabelGuardedIntCounter,
135 pub buffer_read_size: LabelGuardedIntCounter,
136
137 pub total_read_count: LabelGuardedIntCounter,
139 pub total_read_size: LabelGuardedIntCounter,
140 pub persistent_log_read_metrics: KvLogStoreReadMetrics,
141 pub flushed_buffer_read_metrics: KvLogStoreReadMetrics,
142 }
143
144 impl SyncedKvLogStoreMetrics {
145 pub(crate) fn new(
152 metrics: &StreamingMetrics,
153 actor_id: ActorId,
154 fragment_id: FragmentId,
155 name: &str,
156 target: &'static str,
157 ) -> Self {
158 let actor_id_str = actor_id.to_string();
159 let fragment_id_str = fragment_id.to_string();
160 let labels = &[&actor_id_str, target, &fragment_id_str, name];
161
162 let unclean_state = metrics.sync_kv_log_store_state.with_guarded_label_values(&[
163 &actor_id_str,
164 "dirty",
165 target,
166 &fragment_id_str,
167 name,
168 ]);
169 let clean_state = metrics.sync_kv_log_store_state.with_guarded_label_values(&[
170 &actor_id_str,
171 "clean",
172 target,
173 &fragment_id_str,
174 name,
175 ]);
176 let wait_next_poll_ns = metrics
177 .sync_kv_log_store_wait_next_poll_ns
178 .with_guarded_label_values(labels);
179
180 let storage_write_size = metrics
181 .sync_kv_log_store_storage_write_size
182 .with_guarded_label_values(labels);
183 let storage_write_count = metrics
184 .sync_kv_log_store_storage_write_count
185 .with_guarded_label_values(labels);
186 let storage_pause_duration_ns = metrics
187 .sync_kv_log_store_write_pause_duration_ns
188 .with_guarded_label_values(labels);
189
190 let buffer_unconsumed_item_count = metrics
191 .sync_kv_log_store_buffer_unconsumed_item_count
192 .with_guarded_label_values(labels);
193 let buffer_unconsumed_row_count = metrics
194 .sync_kv_log_store_buffer_unconsumed_row_count
195 .with_guarded_label_values(labels);
196 let buffer_unconsumed_epoch_count = metrics
197 .sync_kv_log_store_buffer_unconsumed_epoch_count
198 .with_guarded_label_values(labels);
199 let buffer_unconsumed_min_epoch = metrics
200 .sync_kv_log_store_buffer_unconsumed_min_epoch
201 .with_guarded_label_values(labels);
202 let buffer_memory_bytes = metrics
203 .sync_kv_log_store_buffer_memory_bytes
204 .with_guarded_label_values(labels);
205 let buffer_read_count = metrics
206 .sync_kv_log_store_read_count
207 .with_guarded_label_values(&[
208 &actor_id_str,
209 "buffer",
210 target,
211 &fragment_id_str,
212 name,
213 ]);
214
215 let buffer_read_size = metrics
216 .sync_kv_log_store_read_size
217 .with_guarded_label_values(&[
218 &actor_id_str,
219 "buffer",
220 target,
221 &fragment_id_str,
222 name,
223 ]);
224
225 let total_read_count = metrics
226 .sync_kv_log_store_read_count
227 .with_guarded_label_values(&[
228 &actor_id_str,
229 "total",
230 target,
231 &fragment_id_str,
232 name,
233 ]);
234
235 let total_read_size = metrics
236 .sync_kv_log_store_read_size
237 .with_guarded_label_values(&[
238 &actor_id_str,
239 "total",
240 target,
241 &fragment_id_str,
242 name,
243 ]);
244
245 const READ_PERSISTENT_LOG: &str = "persistent_log";
246 const READ_FLUSHED_BUFFER: &str = "flushed_buffer";
247
248 let persistent_log_read_size = metrics
249 .sync_kv_log_store_read_size
250 .with_guarded_label_values(&[
251 &actor_id_str,
252 READ_PERSISTENT_LOG,
253 target,
254 &fragment_id_str,
255 name,
256 ]);
257
258 let persistent_log_read_count = metrics
259 .sync_kv_log_store_read_count
260 .with_guarded_label_values(&[
261 &actor_id_str,
262 READ_PERSISTENT_LOG,
263 target,
264 &fragment_id_str,
265 name,
266 ]);
267
268 let flushed_buffer_read_size = metrics
269 .sync_kv_log_store_read_size
270 .with_guarded_label_values(&[
271 &actor_id_str,
272 READ_FLUSHED_BUFFER,
273 target,
274 &fragment_id_str,
275 name,
276 ]);
277
278 let flushed_buffer_read_count = metrics
279 .sync_kv_log_store_read_count
280 .with_guarded_label_values(&[
281 &actor_id_str,
282 READ_FLUSHED_BUFFER,
283 target,
284 &fragment_id_str,
285 name,
286 ]);
287
288 Self {
289 unclean_state,
290 clean_state,
291 wait_next_poll_ns,
292 storage_write_size,
293 storage_write_count,
294 pause_duration_ns: storage_pause_duration_ns,
295 buffer_unconsumed_item_count,
296 buffer_unconsumed_row_count,
297 buffer_unconsumed_epoch_count,
298 buffer_unconsumed_min_epoch,
299 buffer_memory_bytes,
300 buffer_read_count,
301 buffer_read_size,
302 total_read_count,
303 total_read_size,
304 persistent_log_read_metrics: KvLogStoreReadMetrics {
305 storage_read_size: persistent_log_read_size,
306 storage_read_count: persistent_log_read_count,
307 },
308 flushed_buffer_read_metrics: KvLogStoreReadMetrics {
309 storage_read_count: flushed_buffer_read_count,
310 storage_read_size: flushed_buffer_read_size,
311 },
312 }
313 }
314
315 #[cfg(test)]
316 pub(crate) fn for_test() -> Self {
317 SyncedKvLogStoreMetrics {
318 unclean_state: LabelGuardedIntCounter::test_int_counter::<5>(),
319 clean_state: LabelGuardedIntCounter::test_int_counter::<5>(),
320 wait_next_poll_ns: LabelGuardedIntCounter::test_int_counter::<4>(),
321 storage_write_count: LabelGuardedIntCounter::test_int_counter::<4>(),
322 storage_write_size: LabelGuardedIntCounter::test_int_counter::<4>(),
323 pause_duration_ns: LabelGuardedIntCounter::test_int_counter::<4>(),
324 buffer_unconsumed_item_count: RelabeledAggregatedIntGauge::test_int_gauge::<4>(
325 GaugeAggregation::Sum,
326 ),
327 buffer_unconsumed_row_count: RelabeledAggregatedIntGauge::test_int_gauge::<4>(
328 GaugeAggregation::Sum,
329 ),
330 buffer_unconsumed_epoch_count: RelabeledAggregatedIntGauge::test_int_gauge::<4>(
331 GaugeAggregation::Sum,
332 ),
333 buffer_unconsumed_min_epoch: RelabeledAggregatedIntGauge::test_int_gauge::<4>(
334 GaugeAggregation::Min,
335 ),
336 buffer_memory_bytes: RelabeledAggregatedIntGauge::test_int_gauge::<4>(
337 GaugeAggregation::Sum,
338 ),
339 buffer_read_count: LabelGuardedIntCounter::test_int_counter::<5>(),
340 buffer_read_size: LabelGuardedIntCounter::test_int_counter::<5>(),
341 total_read_count: LabelGuardedIntCounter::test_int_counter::<5>(),
342 total_read_size: LabelGuardedIntCounter::test_int_counter::<5>(),
343 persistent_log_read_metrics: KvLogStoreReadMetrics::for_test(),
344 flushed_buffer_read_metrics: KvLogStoreReadMetrics::for_test(),
345 }
346 }
347 }
348}
349
350pub(crate) type ReadFlushedChunkFuture =
351 BoxFuture<'static, LogStoreResult<(ChunkId, StreamChunk, Epoch)>>;
352
353pub struct SyncKvLogStoreContext<S: StateStore> {
354 pub table_id: TableId,
355 pub fragment_id: FragmentId,
356 pub metrics: SyncedKvLogStoreMetrics,
357 pub serde: LogStoreRowSerde,
358 pub state_store: S,
359 pub max_buffer_size: usize,
360 pub chunk_size: usize,
361 pub pause_duration_ms: Duration,
362 pub aligned: bool,
363}
364
365pub struct SyncedKvLogStoreExecutor<S: StateStore> {
366 actor_context: ActorContextRef,
367 upstream: Executor,
368 logstore_context: SyncKvLogStoreContext<S>,
369}
370
371impl<S: StateStore> SyncedKvLogStoreExecutor<S> {
373 #[expect(clippy::too_many_arguments)]
374 pub(crate) fn new(
375 actor_context: ActorContextRef,
376 table_id: TableId,
377 metrics: SyncedKvLogStoreMetrics,
378 serde: LogStoreRowSerde,
379 state_store: S,
380 buffer_size: usize,
381 chunk_size: usize,
382 upstream: Executor,
383 pause_duration_ms: Duration,
384 aligned: bool,
385 ) -> Self {
386 let logstore_context = SyncKvLogStoreContext {
387 table_id,
388 fragment_id: actor_context.fragment_id,
389 metrics,
390 serde,
391 state_store,
392 max_buffer_size: buffer_size,
393 chunk_size,
394 pause_duration_ms,
395 aligned,
396 };
397 Self {
398 actor_context,
399 upstream,
400 logstore_context,
401 }
402 }
403}
404
405pub(crate) struct FlushedChunkInfo {
406 epoch: u64,
407 start_seq_id: SeqId,
408 end_seq_id: SeqId,
409 flush_info: FlushInfo,
410 vnode_bitmap: Bitmap,
411}
412
413pub(crate) enum WriteFuture<S: LocalStateStore> {
414 Paused {
424 start_instant: Instant,
425 sleep_future: Option<Pin<Box<Sleep>>>,
426 barrier: Barrier,
427 stream: BoxedMessageStream,
428 write_state: LogStoreWriteState<S>, },
430 ReceiveFromUpstream {
431 future: StreamFuture<BoxedMessageStream>,
432 write_state: LogStoreWriteState<S>,
433 },
434 FlushingChunk {
435 epoch: u64,
436 start_seq_id: SeqId,
437 end_seq_id: SeqId,
438 future: Pin<Box<LogStoreStateWriteChunkFuture<S>>>,
439 stream: BoxedMessageStream,
440 },
441 Empty,
442}
443
444pub(crate) enum WriteFutureEvent {
445 UpstreamMessageReceived(Message),
446 ChunkFlushed(FlushedChunkInfo),
447}
448
449impl<S: LocalStateStore> WriteFuture<S> {
450 fn flush_chunk(
451 stream: BoxedMessageStream,
452 write_state: LogStoreWriteState<S>,
453 chunk: StreamChunk,
454 epoch: u64,
455 start_seq_id: SeqId,
456 end_seq_id: SeqId,
457 ) -> Self {
458 tracing::trace!(
459 start_seq_id,
460 end_seq_id,
461 epoch,
462 cardinality = chunk.cardinality(),
463 "write_future: flushing chunk"
464 );
465 Self::FlushingChunk {
466 epoch,
467 start_seq_id,
468 end_seq_id,
469 future: Box::pin(write_state.into_write_chunk_future(
470 chunk,
471 epoch,
472 start_seq_id,
473 end_seq_id,
474 )),
475 stream,
476 }
477 }
478
479 pub(crate) fn receive_from_upstream(
480 stream: BoxedMessageStream,
481 write_state: LogStoreWriteState<S>,
482 ) -> Self {
483 Self::ReceiveFromUpstream {
484 future: stream.into_future(),
485 write_state,
486 }
487 }
488
489 pub(crate) fn paused(
490 duration: Duration,
491 barrier: Barrier,
492 stream: BoxedMessageStream,
493 write_state: LogStoreWriteState<S>,
494 ) -> Self {
495 let now = Instant::now();
496 tracing::trace!(?now, ?duration, "write_future_pause");
497 Self::Paused {
498 start_instant: now,
499 sleep_future: Some(Box::pin(sleep_until(now + duration))),
500 barrier,
501 stream,
502 write_state,
503 }
504 }
505
506 pub(crate) async fn next_event(
507 &mut self,
508 metrics: &SyncedKvLogStoreMetrics,
509 ) -> StreamExecutorResult<(BoxedMessageStream, LogStoreWriteState<S>, WriteFutureEvent)> {
510 match self {
511 WriteFuture::Paused {
512 start_instant,
513 sleep_future,
514 ..
515 } => {
516 if let Some(sleep_future) = sleep_future {
517 sleep_future.await;
518 metrics
519 .pause_duration_ns
520 .inc_by(start_instant.elapsed().as_nanos() as _);
521 tracing::trace!("resuming write future");
522 }
523 must_match!(replace(self, WriteFuture::Empty), WriteFuture::Paused { stream, write_state, barrier, .. } => {
524 Ok((stream, write_state, WriteFutureEvent::UpstreamMessageReceived(Message::Barrier(barrier))))
525 })
526 }
527 WriteFuture::ReceiveFromUpstream { future, .. } => {
528 let (opt, stream) = future.await;
529 must_match!(replace(self, WriteFuture::Empty), WriteFuture::ReceiveFromUpstream { write_state, .. } => {
530 opt
531 .ok_or_else(|| anyhow!("end of upstream input").into())
532 .and_then(|result| result.map(|item| {
533 (stream, write_state, WriteFutureEvent::UpstreamMessageReceived(item))
534 }))
535 })
536 }
537 WriteFuture::FlushingChunk { future, .. } => {
538 let (write_state, result) = future.await;
539 let result = must_match!(replace(self, WriteFuture::Empty), WriteFuture::FlushingChunk { epoch, start_seq_id, end_seq_id, stream, .. } => {
540 result.map(|(flush_info, vnode_bitmap)| {
541 (stream, write_state, WriteFutureEvent::ChunkFlushed(FlushedChunkInfo {
542 epoch,
543 start_seq_id,
544 end_seq_id,
545 flush_info,
546 vnode_bitmap,
547 }))
548 })
549 });
550 result.map_err(Into::into)
551 }
552 WriteFuture::Empty => {
553 unreachable!("should not be polled after ready")
554 }
555 }
556 }
557}
558
559pub(crate) type LocalLogStoreReadState<S> =
560 LogStoreReadState<<<S as StateStore>::Local as LocalStateStore>::FlushedSnapshotReader>;
561pub(crate) type LocalLogStoreWriteState<S> = LogStoreWriteState<<S as StateStore>::Local>;
562
563impl<S: StateStore> SyncedKvLogStoreExecutor<S> {
565 pub(crate) async fn init_local_log_store_state(
566 context: &SyncKvLogStoreContext<S>,
567 first_write_epoch: EpochPair,
568 ) -> StreamExecutorResult<(LocalLogStoreReadState<S>, LocalLogStoreWriteState<S>)> {
569 let local_state_store = context
570 .state_store
571 .new_local(NewLocalOptions {
572 table_id: context.table_id,
573 fragment_id: context.fragment_id,
574 op_consistency_level: OpConsistencyLevel::Inconsistent,
575 table_option: TableOption {
576 retention_seconds: None,
577 },
578 is_replicated: false,
579 vnodes: context.serde.vnodes().clone(),
580 upload_on_flush: false,
581 })
582 .await;
583
584 let (read_state, mut initial_write_state) = new_log_store_state(
585 context.table_id,
586 local_state_store,
587 context.serde.clone(),
588 context.chunk_size,
589 );
590 initial_write_state.init(first_write_epoch).await?;
591 Ok((read_state, initial_write_state))
592 }
593
594 #[try_stream(ok = Message, error = StreamExecutorError)]
595 pub(crate) async fn aligned_message_stream(
596 actor_id: ActorId,
597 input: BoxedMessageStream,
598 read_state: LocalLogStoreReadState<S>,
599 mut initial_write_state: LocalLogStoreWriteState<S>,
600 metrics: SyncedKvLogStoreMetrics,
601 initial_write_epoch: EpochPair,
602 ) {
603 tracing::info!("aligned mode");
604 let log_store_stream = read_state
609 .read_persisted_log_store(
610 metrics.persistent_log_read_metrics.clone(),
611 initial_write_epoch.curr,
612 LogStoreReadStateStreamRangeStart::Unbounded,
613 )
614 .await?;
615
616 #[for_await]
617 for message in log_store_stream {
618 let (_epoch, message) = message?;
619 match message {
620 KvLogStoreItem::Barrier { .. } => {
621 continue;
622 }
623 KvLogStoreItem::StreamChunk { chunk, .. } => {
624 yield Message::Chunk(chunk);
625 }
626 }
627 }
628
629 let mut realigned_logstore = false;
630
631 #[for_await]
632 for message in input {
633 match message? {
634 Message::Barrier(barrier) => {
635 let is_checkpoint = barrier.is_checkpoint();
636 let mut progress = LogStoreVnodeProgress::None;
637 progress.apply_aligned(read_state.vnodes().clone(), barrier.epoch.prev, None);
638 let post_seal =
640 initial_write_state.seal_current_epoch(barrier.epoch.curr, progress.take());
641 barrier.assume_no_update_vnode_bitmap(actor_id)?;
642 yield Message::Barrier(barrier);
643 post_seal.post_yield_barrier(None).await?;
644 if !realigned_logstore && is_checkpoint {
645 realigned_logstore = true;
646 tracing::info!("realigned logstore");
647 }
648 }
649 Message::Chunk(chunk) => {
650 yield Message::Chunk(chunk);
651 }
652 Message::Watermark(watermark) => {
653 yield Message::Watermark(watermark);
654 }
655 }
656 }
657 }
658
659 pub(crate) fn apply_pause_resume_mutation(barrier: &Barrier, pause_stream: &mut bool) {
660 if let Some(mutation) = barrier.mutation.as_deref() {
661 match mutation {
662 Mutation::Pause => {
663 *pause_stream = true;
664 }
665 Mutation::Resume => {
666 *pause_stream = false;
667 }
668 _ => {}
669 }
670 }
671 }
672
673 pub(crate) fn process_upstream_chunk(
674 seq_id: SeqId,
675 stream: BoxedMessageStream,
676 write_state: LogStoreWriteState<S::Local>,
677 chunk: StreamChunk,
678 buffer: &mut SyncedLogStoreBuffer,
679 ) -> (SeqId, WriteFuture<S::Local>) {
680 let cardinality = chunk.cardinality();
681 if cardinality == 0 {
682 tracing::warn!(
683 epoch = write_state.epoch().curr,
684 "received empty chunk (cardinality=0), skipping"
685 );
686 return (
687 seq_id,
688 WriteFuture::receive_from_upstream(stream, write_state),
689 );
690 }
691
692 let start_seq_id = seq_id;
693 let new_seq_id = seq_id + cardinality as SeqId;
694 let end_seq_id = new_seq_id - 1;
695 let epoch = write_state.epoch().curr;
696 tracing::trace!(
697 start_seq_id,
698 end_seq_id,
699 new_seq_id,
700 epoch,
701 cardinality,
702 "received chunk"
703 );
704 let next_write_future = if let Some(chunk_to_flush) =
705 buffer.add_or_flush_chunk(start_seq_id, end_seq_id, chunk, epoch)
706 {
707 WriteFuture::flush_chunk(
708 stream,
709 write_state,
710 chunk_to_flush,
711 epoch,
712 start_seq_id,
713 end_seq_id,
714 )
715 } else {
716 WriteFuture::receive_from_upstream(stream, write_state)
717 };
718 (new_seq_id, next_write_future)
719 }
720
721 pub(crate) fn process_flushed_chunk(
722 stream: BoxedMessageStream,
723 write_state: LogStoreWriteState<S::Local>,
724 info: FlushedChunkInfo,
725 buffer: &mut SyncedLogStoreBuffer,
726 metrics: &SyncedKvLogStoreMetrics,
727 ) -> WriteFuture<S::Local> {
728 buffer.add_flushed_item_to_buffer(
729 info.start_seq_id,
730 info.end_seq_id,
731 info.vnode_bitmap,
732 info.epoch,
733 );
734 metrics
735 .storage_write_count
736 .inc_by(info.flush_info.flush_count as _);
737 metrics
738 .storage_write_size
739 .inc_by(info.flush_info.flush_size as _);
740 WriteFuture::receive_from_upstream(stream, write_state)
741 }
742
743 #[try_stream(ok= Message, error = StreamExecutorError)]
744 pub async fn execute_monitored(self) {
745 let wait_next_poll_ns = self.logstore_context.metrics.wait_next_poll_ns.clone();
746 #[for_await]
747 for message in self.execute_inner() {
748 let current_time = Instant::now();
749 yield message?;
750 wait_next_poll_ns.inc_by(current_time.elapsed().as_nanos() as _);
751 }
752 }
753
754 #[try_stream(ok = Message, error = StreamExecutorError)]
755 async fn execute_inner(self) {
756 let mut input = self.upstream.execute();
757
758 let first_barrier = expect_first_barrier(&mut input).await?;
760 let first_write_epoch = first_barrier.epoch;
761 yield Message::Barrier(first_barrier.clone());
762
763 let (read_state, initial_write_state) =
764 Self::init_local_log_store_state(&self.logstore_context, first_write_epoch).await?;
765
766 let mut pause_stream = first_barrier.is_pause_on_startup();
767 let initial_write_epoch = first_write_epoch;
768
769 if self.logstore_context.aligned {
770 let aligned_stream = Self::aligned_message_stream(
771 self.actor_context.id,
772 input,
773 read_state,
774 initial_write_state,
775 self.logstore_context.metrics.clone(),
776 initial_write_epoch,
777 );
778 #[for_await]
779 for message in aligned_stream {
780 yield message?;
781 }
782 return Ok(());
783 }
784
785 let mut seq_id = FIRST_SEQ_ID;
786 let mut buffer = SyncedLogStoreBuffer::new(
787 self.logstore_context.max_buffer_size,
788 self.logstore_context.chunk_size,
789 &self.logstore_context.metrics,
790 );
791
792 let log_store_stream = read_state
793 .read_persisted_log_store(
794 self.logstore_context
795 .metrics
796 .persistent_log_read_metrics
797 .clone(),
798 initial_write_epoch.curr,
799 LogStoreReadStateStreamRangeStart::Unbounded,
800 )
801 .await?;
802
803 let mut log_store_stream = tokio_stream::StreamExt::peekable(log_store_stream);
804 let mut clean_state = log_store_stream.peek().await.is_none();
805 tracing::trace!(?clean_state);
806
807 let mut read_future_state = ReadFuture::ReadingPersistedStream(log_store_stream);
808
809 let mut write_future_state = WriteFuture::receive_from_upstream(input, initial_write_state);
810
811 let mut progress = LogStoreVnodeProgress::None;
812
813 loop {
814 let select_result = {
815 let read_future = async {
816 if pause_stream {
817 pending().await
818 } else {
819 read_future_state
820 .next_message(
821 &mut progress,
822 &read_state,
823 &mut buffer,
824 &self.logstore_context.metrics,
825 )
826 .await
827 }
828 };
829 pin_mut!(read_future);
830 let write_future = write_future_state.next_event(&self.logstore_context.metrics);
831 pin_mut!(write_future);
832 let output = select(write_future, read_future).await;
833 drop_either_future(output)
834 };
835 match select_result {
836 Either::Left(result) => {
837 drop(write_future_state);
839 let (stream, mut write_state, either) = result?;
840 match either {
841 WriteFutureEvent::UpstreamMessageReceived(msg) => match msg {
842 Message::Barrier(barrier) => {
843 if clean_state && barrier.kind.is_checkpoint() && !buffer.is_empty()
844 {
845 write_future_state = WriteFuture::paused(
846 self.logstore_context.pause_duration_ms,
847 barrier,
848 stream,
849 write_state,
850 );
851 clean_state = false;
852 self.logstore_context.metrics.unclean_state.inc();
853 } else {
854 Self::apply_pause_resume_mutation(&barrier, &mut pause_stream);
855 let write_state_post_write_barrier = Self::write_barrier(
856 self.actor_context.id,
857 &mut write_state,
858 barrier.clone(),
859 &self.logstore_context.metrics,
860 progress.take(),
861 &mut buffer,
862 )
863 .await?;
864 seq_id = FIRST_SEQ_ID;
865 barrier.assume_no_update_vnode_bitmap(self.actor_context.id)?;
866
867 yield Message::Barrier(barrier);
868
869 write_state_post_write_barrier
870 .post_yield_barrier(None)
871 .await?;
872
873 write_future_state =
874 WriteFuture::receive_from_upstream(stream, write_state);
875 }
876 }
877 Message::Chunk(chunk) => {
878 let (new_seq_id, next_write_future) = Self::process_upstream_chunk(
879 seq_id,
880 stream,
881 write_state,
882 chunk,
883 &mut buffer,
884 );
885 seq_id = new_seq_id;
886 write_future_state = next_write_future;
887 }
888 Message::Watermark(watermark) => {
889 buffer.add_watermark(write_state.epoch().curr, watermark);
890 write_future_state =
891 WriteFuture::receive_from_upstream(stream, write_state);
892 }
893 },
894 WriteFutureEvent::ChunkFlushed(info) => {
895 write_future_state = Self::process_flushed_chunk(
896 stream,
897 write_state,
898 info,
899 &mut buffer,
900 &self.logstore_context.metrics,
901 );
902 }
903 }
904 }
905 Either::Right(result) => {
906 let clean_state_reached = read_future_state.mark_clean_state(
907 &mut clean_state,
908 &buffer,
909 &self.logstore_context.metrics,
910 );
911
912 if clean_state_reached {
913 if let WriteFuture::Paused { sleep_future, .. } = &mut write_future_state {
915 tracing::trace!("resuming paused future");
916 assert!(buffer.has_available_capacity());
917 *sleep_future = None;
918 }
919 }
920 let message = result?;
921 if let Message::Chunk(chunk) = &message {
922 self.logstore_context
923 .metrics
924 .total_read_count
925 .inc_by(chunk.cardinality() as _);
926 }
927
928 yield message;
929 }
930 }
931 }
932 }
933}
934
935pub(crate) type PersistedStream<S> =
936 Peekable<Pin<Box<LogStoreItemMergeStream<TimeoutAutoRebuildIter<S>>>>>;
937
938pub(crate) enum ReadFuture<S: StateStoreRead> {
939 ReadingPersistedStream(PersistedStream<S>),
940 ReadingFlushedChunk {
941 future: ReadFlushedChunkFuture,
942 end_seq_id: SeqId,
943 },
944 Idle,
945}
946
947impl<S: StateStoreRead> ReadFuture<S> {
949 pub(crate) fn mark_clean_state(
950 &self,
951 clean_state: &mut bool,
952 buffer: &SyncedLogStoreBuffer,
953 metrics: &SyncedKvLogStoreMetrics,
954 ) -> bool {
955 if !*clean_state && matches!(self, ReadFuture::Idle) && buffer.is_empty() {
956 *clean_state = true;
957 metrics.clean_state.inc();
958 true
959 } else {
960 false
961 }
962 }
963
964 pub(crate) async fn next_message(
965 &mut self,
966 progress: &mut LogStoreVnodeProgress,
967 read_state: &LogStoreReadState<S>,
968 buffer: &mut SyncedLogStoreBuffer,
969 metrics: &SyncedKvLogStoreMetrics,
970 ) -> StreamExecutorResult<Message> {
971 match self {
972 ReadFuture::ReadingPersistedStream(stream) => {
973 while let Some((epoch, item)) = stream.try_next().await? {
974 match item {
975 KvLogStoreItem::Barrier { vnodes, .. } => {
976 tracing::trace!(epoch, "read logstore barrier");
977 progress.apply_aligned(vnodes, epoch, None);
979 continue;
980 }
981 KvLogStoreItem::StreamChunk {
982 chunk,
983 progress: chunk_progress,
984 } => {
985 tracing::trace!("read logstore chunk of size: {}", chunk.cardinality());
986 progress.apply_per_vnode(epoch, chunk_progress);
987 return Ok(Message::Chunk(chunk));
988 }
989 }
990 }
991 *self = ReadFuture::Idle;
992 }
993 ReadFuture::ReadingFlushedChunk { .. } | ReadFuture::Idle => {}
994 }
995 match self {
996 ReadFuture::ReadingPersistedStream(_) => {
997 unreachable!("must have finished read persisted stream when reaching here")
998 }
999 ReadFuture::ReadingFlushedChunk { .. } => {}
1000 ReadFuture::Idle => loop {
1001 let Some((item_epoch, item)) = buffer.pop_front() else {
1002 return pending().await;
1003 };
1004 match item {
1005 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::StreamChunk {
1006 chunk,
1007 start_seq_id,
1008 end_seq_id,
1009 flushed,
1010 ..
1011 }) => {
1012 metrics.buffer_read_count.inc_by(chunk.cardinality() as _);
1013 tracing::trace!(
1014 start_seq_id,
1015 end_seq_id,
1016 flushed,
1017 cardinality = chunk.cardinality(),
1018 "read buffered chunk of size"
1019 );
1020 progress.apply_aligned(
1021 read_state.vnodes().clone(),
1022 item_epoch,
1023 Some(end_seq_id),
1024 );
1025 return Ok(Message::Chunk(chunk));
1026 }
1027 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Flushed {
1028 vnode_bitmap,
1029 start_seq_id,
1030 end_seq_id,
1031 chunk_id,
1032 }) => {
1033 tracing::trace!(start_seq_id, end_seq_id, chunk_id, "read flushed chunk");
1034 let read_metrics = metrics.flushed_buffer_read_metrics.clone();
1035 let future = read_state
1036 .read_flushed_chunk(
1037 vnode_bitmap,
1038 chunk_id,
1039 start_seq_id,
1040 end_seq_id,
1041 item_epoch,
1042 read_metrics,
1043 )
1044 .boxed();
1045 *self = ReadFuture::ReadingFlushedChunk { future, end_seq_id };
1046 break;
1047 }
1048 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Barrier { .. }) => {
1049 tracing::trace!(item_epoch, "read buffer barrier");
1050 progress.apply_aligned(read_state.vnodes().clone(), item_epoch, None);
1051 continue;
1052 }
1053 SyncedLogStoreBufferItem::Watermark(watermark) => {
1054 return Ok(Message::Watermark(watermark));
1055 }
1056 }
1057 },
1058 }
1059
1060 let (future, end_seq_id) = match self {
1061 ReadFuture::ReadingPersistedStream(_) | ReadFuture::Idle => {
1062 unreachable!("should be at ReadingFlushedChunk")
1063 }
1064 ReadFuture::ReadingFlushedChunk { future, end_seq_id } => (future, *end_seq_id),
1065 };
1066
1067 let (_, chunk, epoch) = future.await?;
1068 progress.apply_aligned(read_state.vnodes().clone(), epoch, Some(end_seq_id));
1069 tracing::trace!(
1070 end_seq_id,
1071 "read flushed chunk of size: {}",
1072 chunk.cardinality()
1073 );
1074 *self = ReadFuture::Idle;
1075 Ok(Message::Chunk(chunk))
1076 }
1077}
1078
1079impl<S: StateStore> SyncedKvLogStoreExecutor<S> {
1081 pub(crate) async fn write_barrier<'a>(
1082 actor_id: ActorId,
1083 write_state: &'a mut LogStoreWriteState<S::Local>,
1084 barrier: Barrier,
1085 metrics: &SyncedKvLogStoreMetrics,
1086 progress: LogStoreVnodeProgress,
1087 buffer: &mut SyncedLogStoreBuffer,
1088 ) -> StreamExecutorResult<LogStorePostSealCurrentEpoch<'a, S::Local>> {
1089 tracing::trace!(%actor_id, ?progress, "applying truncation");
1090 let epoch = barrier.epoch.prev;
1094 let mut writer = write_state.start_writer(false);
1095 writer.write_barrier(epoch, barrier.is_checkpoint())?;
1096
1097 if barrier.is_checkpoint() {
1098 for (epoch, item) in buffer.buffer.iter_mut().rev() {
1099 match item {
1100 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::StreamChunk {
1101 chunk,
1102 start_seq_id,
1103 end_seq_id,
1104 flushed,
1105 ..
1106 }) => {
1107 if !*flushed {
1108 writer.write_chunk(chunk, *epoch, *start_seq_id, *end_seq_id)?;
1109 *flushed = true;
1110 } else {
1111 break;
1112 }
1113 }
1114 SyncedLogStoreBufferItem::LogStore(
1115 LogStoreBufferItem::Flushed { .. } | LogStoreBufferItem::Barrier { .. },
1116 )
1117 | SyncedLogStoreBufferItem::Watermark(_) => {}
1118 }
1119 }
1120 }
1121
1122 let (flush_info, _) = writer.finish().await?;
1124 metrics
1125 .storage_write_count
1126 .inc_by(flush_info.flush_count as _);
1127 metrics
1128 .storage_write_size
1129 .inc_by(flush_info.flush_size as _);
1130 let post_seal = write_state.seal_current_epoch(barrier.epoch.curr, progress);
1131
1132 buffer.buffer.push_back((
1134 epoch,
1135 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Barrier {
1136 is_checkpoint: barrier.is_checkpoint(),
1137 next_epoch: barrier.epoch.curr,
1138 schema_change: None,
1139 is_stop: false,
1140 }),
1141 ));
1142 buffer.next_chunk_id = 0;
1143 buffer.update_buffer_metrics();
1144
1145 Ok(post_seal)
1146 }
1147}
1148
1149#[derive(EstimateSize)]
1150enum SyncedLogStoreBufferItem {
1151 LogStore(LogStoreBufferItem),
1152 Watermark(Watermark),
1153}
1154
1155pub(crate) struct SyncedLogStoreBuffer {
1156 buffer: VecDeque<(u64, SyncedLogStoreBufferItem)>,
1157 current_size: usize,
1158 max_size: usize,
1159 max_chunk_size: usize,
1160 next_chunk_id: ChunkId,
1161 metrics: SyncedKvLogStoreMetrics,
1162 flushed_count: usize,
1163}
1164
1165impl SyncedLogStoreBuffer {
1166 pub fn new(
1167 max_buffer_size: usize,
1168 chunk_size: usize,
1169 metrics: &SyncedKvLogStoreMetrics,
1170 ) -> Self {
1171 SyncedLogStoreBuffer {
1172 buffer: VecDeque::new(),
1173 current_size: 0,
1174 max_size: max_buffer_size,
1175 max_chunk_size: chunk_size,
1176 next_chunk_id: 0,
1177 metrics: metrics.clone(),
1178 flushed_count: 0,
1179 }
1180 }
1181
1182 pub fn is_empty(&self) -> bool {
1183 self.current_size == 0
1184 }
1185
1186 pub fn has_available_capacity(&self) -> bool {
1187 self.current_size < self.max_size
1188 }
1189
1190 fn add_or_flush_chunk(
1191 &mut self,
1192 start_seq_id: SeqId,
1193 end_seq_id: SeqId,
1194 chunk: StreamChunk,
1195 epoch: u64,
1196 ) -> Option<StreamChunk> {
1197 let current_size = self.current_size;
1198 let chunk_size = chunk.cardinality();
1199
1200 tracing::trace!(
1201 current_size,
1202 chunk_size,
1203 max_size = self.max_size,
1204 "checking chunk size"
1205 );
1206 let should_flush_chunk = current_size + chunk_size > self.max_size;
1207 if should_flush_chunk {
1208 tracing::trace!(start_seq_id, end_seq_id, epoch, "flushing chunk",);
1209 Some(chunk)
1210 } else {
1211 tracing::trace!(start_seq_id, end_seq_id, epoch, "buffering chunk",);
1212 self.add_chunk_to_buffer(chunk, start_seq_id, end_seq_id, epoch);
1213 None
1214 }
1215 }
1216
1217 fn add_flushed_item_to_buffer(
1220 &mut self,
1221 start_seq_id: SeqId,
1222 end_seq_id: SeqId,
1223 new_vnode_bitmap: Bitmap,
1224 epoch: u64,
1225 ) {
1226 let new_chunk_size = (end_seq_id - start_seq_id + 1) as usize;
1227
1228 if let Some((
1229 item_epoch,
1230 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Flushed {
1231 start_seq_id: prev_start_seq_id,
1232 end_seq_id: prev_end_seq_id,
1233 vnode_bitmap,
1234 ..
1235 }),
1236 )) = self.buffer.back_mut()
1237 && let flushed_chunk_size = (*prev_end_seq_id - *prev_start_seq_id + 1) as usize
1238 && let projected_flushed_chunk_size = flushed_chunk_size + new_chunk_size
1239 && projected_flushed_chunk_size <= self.max_chunk_size
1240 {
1241 assert!(
1242 *prev_end_seq_id < start_seq_id,
1243 "prev end_seq_id {} should be smaller than current start_seq_id {}",
1244 end_seq_id,
1245 start_seq_id
1246 );
1247 assert_eq!(
1248 epoch, *item_epoch,
1249 "epoch of newly added flushed item must be the same as the last flushed item"
1250 );
1251 *prev_end_seq_id = end_seq_id;
1252 *vnode_bitmap |= new_vnode_bitmap;
1253 } else {
1254 let chunk_id = self.next_chunk_id;
1255 self.next_chunk_id += 1;
1256 self.buffer.push_back((
1257 epoch,
1258 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Flushed {
1259 start_seq_id,
1260 end_seq_id,
1261 vnode_bitmap: new_vnode_bitmap,
1262 chunk_id,
1263 }),
1264 ));
1265 self.flushed_count += 1;
1266 tracing::trace!(
1267 "adding flushed item to buffer: start_seq_id: {start_seq_id}, end_seq_id: {end_seq_id}, chunk_id: {chunk_id}"
1268 );
1269 }
1270 self.update_buffer_metrics();
1272 }
1273
1274 fn add_chunk_to_buffer(
1275 &mut self,
1276 chunk: StreamChunk,
1277 start_seq_id: SeqId,
1278 end_seq_id: SeqId,
1279 epoch: u64,
1280 ) {
1281 let chunk_id = self.next_chunk_id;
1282 self.next_chunk_id += 1;
1283 self.current_size += chunk.cardinality();
1284 self.buffer.push_back((
1285 epoch,
1286 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::StreamChunk {
1287 chunk,
1288 start_seq_id,
1289 end_seq_id,
1290 flushed: false,
1291 chunk_id,
1292 }),
1293 ));
1294 self.update_buffer_metrics();
1295 }
1296
1297 pub(crate) fn add_watermark(&mut self, epoch: u64, watermark: Watermark) {
1298 self.buffer
1299 .push_back((epoch, SyncedLogStoreBufferItem::Watermark(watermark)));
1300 self.update_buffer_metrics();
1301 }
1302
1303 fn pop_front(&mut self) -> Option<(u64, SyncedLogStoreBufferItem)> {
1304 let item = self.buffer.pop_front();
1305 match &item {
1306 Some((_, SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Flushed { .. }))) => {
1307 self.flushed_count -= 1;
1308 }
1309 Some((
1310 _,
1311 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::StreamChunk {
1312 chunk, ..
1313 }),
1314 )) => {
1315 self.current_size -= chunk.cardinality();
1316 }
1317 _ => {}
1318 }
1319 self.update_buffer_metrics();
1320 item
1321 }
1322
1323 fn update_buffer_metrics(&self) {
1324 let mut epoch_count = 0;
1325 let mut row_count = 0;
1326 let mut memory_bytes = 0;
1327 for (_, item) in &self.buffer {
1328 match item {
1329 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::StreamChunk {
1330 chunk,
1331 ..
1332 }) => {
1333 row_count += chunk.cardinality();
1334 }
1335 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Flushed {
1336 start_seq_id,
1337 end_seq_id,
1338 ..
1339 }) => {
1340 row_count += (end_seq_id - start_seq_id) as usize;
1341 }
1342 SyncedLogStoreBufferItem::LogStore(LogStoreBufferItem::Barrier { .. }) => {
1343 epoch_count += 1;
1344 }
1345 SyncedLogStoreBufferItem::Watermark(_) => {}
1346 }
1347 memory_bytes += item.estimated_size();
1348 }
1349 self.metrics.buffer_unconsumed_epoch_count.set(epoch_count);
1350 self.metrics.buffer_unconsumed_row_count.set(row_count as _);
1351 self.metrics
1352 .buffer_unconsumed_item_count
1353 .set(self.buffer.len() as _);
1354 self.metrics
1355 .buffer_unconsumed_min_epoch
1356 .set_optional(self.buffer.front().map(|(epoch, _)| *epoch as _));
1357 self.metrics.buffer_memory_bytes.set(memory_bytes as _);
1358 }
1359}
1360
1361impl<S> Execute for SyncedKvLogStoreExecutor<S>
1362where
1363 S: StateStore,
1364{
1365 fn execute(self: Box<Self>) -> BoxedMessageStream {
1366 self.execute_monitored().boxed()
1367 }
1368}
1369
1370#[cfg(test)]
1371mod tests {
1372 use itertools::Itertools;
1373 use pretty_assertions::assert_eq;
1374 use risingwave_common::catalog::Field;
1375 use risingwave_common::hash::VirtualNode;
1376 use risingwave_common::test_prelude::*;
1377 use risingwave_common::util::epoch::test_epoch;
1378 use risingwave_storage::memory::MemoryStateStore;
1379
1380 use super::*;
1381 use crate::assert_stream_chunk_eq;
1382 use crate::common::log_store_impl::kv_log_store::KV_LOG_STORE_V2_INFO;
1383 use crate::common::log_store_impl::kv_log_store::test_utils::{
1384 check_stream_chunk_eq, gen_test_log_store_table, test_payload_schema,
1385 };
1386 use crate::executor::sync_kv_log_store::metrics::SyncedKvLogStoreMetrics;
1387 use crate::executor::test_utils::MockSource;
1388
1389 fn init_logger() {
1390 let _ = tracing_subscriber::fmt()
1391 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
1392 .with_ansi(false)
1393 .try_init();
1394 }
1395
1396 #[tokio::test]
1398 async fn test_read_write_buffer() {
1399 init_logger();
1400
1401 let pk_info = &KV_LOG_STORE_V2_INFO;
1402 let column_descs = test_payload_schema(pk_info);
1403 let fields = column_descs
1404 .into_iter()
1405 .map(|desc| Field::new(desc.name.clone(), desc.data_type))
1406 .collect_vec();
1407 let schema = Schema { fields };
1408 let stream_key = vec![0];
1409 let (mut tx, source) = MockSource::channel();
1410 let source = source.into_executor(schema.clone(), stream_key.clone());
1411
1412 let vnodes = Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST));
1413
1414 let table = gen_test_log_store_table(pk_info);
1415
1416 let log_store_executor = SyncedKvLogStoreExecutor::new(
1417 ActorContext::for_test(123),
1418 table.id,
1419 SyncedKvLogStoreMetrics::for_test(),
1420 LogStoreRowSerde::new(&table, vnodes, pk_info),
1421 MemoryStateStore::new(),
1422 10,
1423 256,
1424 source,
1425 Duration::from_millis(256),
1426 false,
1427 )
1428 .boxed();
1429
1430 tx.push_barrier(test_epoch(1), false);
1432
1433 let chunk_1 = StreamChunk::from_pretty(
1434 " I T
1435 + 5 10
1436 + 6 10
1437 + 8 10
1438 + 9 10
1439 + 10 11",
1440 );
1441
1442 let chunk_2 = StreamChunk::from_pretty(
1443 " I T
1444 - 5 10
1445 - 6 10
1446 - 8 10
1447 U- 9 10
1448 U+ 10 11",
1449 );
1450
1451 tx.push_chunk(chunk_1.clone());
1452 tx.push_int64_watermark(0, 7);
1453 tx.push_chunk(chunk_2.clone());
1454
1455 let mut stream = log_store_executor.execute();
1456
1457 match stream.next().await {
1458 Some(Ok(Message::Barrier(barrier))) => {
1459 assert_eq!(barrier.epoch.curr, test_epoch(1));
1460 }
1461 other => panic!("Expected a barrier message, got {:?}", other),
1462 }
1463
1464 match stream.next().await {
1465 Some(Ok(Message::Chunk(chunk))) => {
1466 assert_stream_chunk_eq!(chunk, chunk_1);
1467 }
1468 other => panic!("Expected a chunk message, got {:?}", other),
1469 }
1470
1471 match stream.next().await {
1472 Some(Ok(Message::Watermark(watermark))) => {
1473 assert_eq!(watermark, Watermark::new(0, DataType::Int64, 7_i64.into()));
1474 }
1475 other => panic!("Expected a watermark message, got {:?}", other),
1476 }
1477
1478 match stream.next().await {
1479 Some(Ok(Message::Chunk(chunk))) => {
1480 assert_stream_chunk_eq!(chunk, chunk_2);
1481 }
1482 other => panic!("Expected a chunk message, got {:?}", other),
1483 }
1484
1485 tx.push_barrier(test_epoch(2), false);
1486
1487 match stream.next().await {
1488 Some(Ok(Message::Barrier(barrier))) => {
1489 assert_eq!(barrier.epoch.curr, test_epoch(2));
1490 }
1491 other => panic!("Expected a barrier message, got {:?}", other),
1492 }
1493 }
1494
1495 #[tokio::test]
1501 async fn test_barrier_persisted_read() {
1502 init_logger();
1503
1504 let pk_info = &KV_LOG_STORE_V2_INFO;
1505 let column_descs = test_payload_schema(pk_info);
1506 let fields = column_descs
1507 .into_iter()
1508 .map(|desc| Field::new(desc.name.clone(), desc.data_type))
1509 .collect_vec();
1510 let schema = Schema { fields };
1511 let stream_key = vec![0];
1512 let (mut tx, source) = MockSource::channel();
1513 let source = source.into_executor(schema.clone(), stream_key.clone());
1514
1515 let vnodes = Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST));
1516
1517 let table = gen_test_log_store_table(pk_info);
1518
1519 let log_store_executor = SyncedKvLogStoreExecutor::new(
1520 ActorContext::for_test(123),
1521 table.id,
1522 SyncedKvLogStoreMetrics::for_test(),
1523 LogStoreRowSerde::new(&table, vnodes, pk_info),
1524 MemoryStateStore::new(),
1525 10,
1526 256,
1527 source,
1528 Duration::from_millis(256),
1529 false,
1530 )
1531 .boxed();
1532
1533 tx.push_barrier(test_epoch(1), false);
1535
1536 let chunk_1 = StreamChunk::from_pretty(
1537 " I T
1538 + 5 10
1539 + 6 10
1540 + 8 10
1541 + 9 10
1542 + 10 11",
1543 );
1544
1545 let chunk_2 = StreamChunk::from_pretty(
1546 " I T
1547 - 5 10
1548 - 6 10
1549 - 8 10
1550 U- 10 11
1551 U+ 10 10",
1552 );
1553
1554 tx.push_chunk(chunk_1.clone());
1555 tx.push_int64_watermark(0, 7);
1556 tx.push_chunk(chunk_2.clone());
1557
1558 tx.push_barrier(test_epoch(2), false);
1559
1560 let mut stream = log_store_executor.execute();
1561
1562 match stream.next().await {
1563 Some(Ok(Message::Barrier(barrier))) => {
1564 assert_eq!(barrier.epoch.curr, test_epoch(1));
1565 }
1566 other => panic!("Expected a barrier message, got {:?}", other),
1567 }
1568
1569 match stream.next().await {
1570 Some(Ok(Message::Chunk(chunk))) => {
1571 assert_stream_chunk_eq!(chunk, chunk_1);
1572 }
1573 other => panic!("Expected a chunk message, got {:?}", other),
1574 }
1575
1576 match stream.next().await {
1577 Some(Ok(Message::Watermark(watermark))) => {
1578 assert_eq!(watermark, Watermark::new(0, DataType::Int64, 7_i64.into()));
1579 }
1580 other => panic!("Expected a watermark message, got {:?}", other),
1581 }
1582
1583 match stream.next().await {
1584 Some(Ok(Message::Chunk(chunk))) => {
1585 assert_stream_chunk_eq!(chunk, chunk_2);
1586 }
1587 other => panic!("Expected a chunk message, got {:?}", other),
1588 }
1589
1590 match stream.next().await {
1591 Some(Ok(Message::Barrier(barrier))) => {
1592 assert_eq!(barrier.epoch.curr, test_epoch(2));
1593 }
1594 other => panic!("Expected a barrier message, got {:?}", other),
1595 }
1596 }
1597
1598 #[tokio::test]
1601 async fn test_max_chunk_persisted_read() {
1602 init_logger();
1603
1604 let pk_info = &KV_LOG_STORE_V2_INFO;
1605 let column_descs = test_payload_schema(pk_info);
1606 let fields = column_descs
1607 .into_iter()
1608 .map(|desc| Field::new(desc.name.clone(), desc.data_type))
1609 .collect_vec();
1610 let schema = Schema { fields };
1611 let stream_key = vec![0];
1612 let (mut tx, source) = MockSource::channel();
1613 let source = source.into_executor(schema.clone(), stream_key.clone());
1614
1615 let vnodes = Arc::new(Bitmap::ones(VirtualNode::COUNT_FOR_TEST));
1616
1617 let table = gen_test_log_store_table(pk_info);
1618
1619 let log_store_executor = SyncedKvLogStoreExecutor::new(
1620 ActorContext::for_test(123),
1621 table.id,
1622 SyncedKvLogStoreMetrics::for_test(),
1623 LogStoreRowSerde::new(&table, vnodes, pk_info),
1624 MemoryStateStore::new(),
1625 0,
1626 256,
1627 source,
1628 Duration::from_millis(256),
1629 false,
1630 )
1631 .boxed();
1632
1633 tx.push_barrier(test_epoch(1), false);
1635
1636 let chunk_1 = StreamChunk::from_pretty(
1637 " I T
1638 + 5 10
1639 + 6 10
1640 + 8 10
1641 + 9 10
1642 + 10 11",
1643 );
1644
1645 let chunk_2 = StreamChunk::from_pretty(
1646 " I T
1647 - 5 10
1648 - 6 10
1649 - 8 10
1650 U- 10 11
1651 U+ 10 10",
1652 );
1653
1654 let chunk_3 = StreamChunk::from_pretty(
1655 " I T
1656 + 11 12",
1657 );
1658
1659 tx.push_chunk(chunk_1.clone());
1660 tx.push_chunk(chunk_2.clone());
1661 tx.push_int64_watermark(0, 7);
1662 tx.push_chunk(chunk_3.clone());
1663
1664 tx.push_barrier(test_epoch(2), false);
1665
1666 let mut stream = log_store_executor.execute();
1667
1668 for i in 1..=2 {
1669 match stream.next().await {
1670 Some(Ok(Message::Barrier(barrier))) => {
1671 assert_eq!(barrier.epoch.curr, test_epoch(i));
1672 }
1673 other => panic!("Expected a barrier message, got {:?}", other),
1674 }
1675 }
1676
1677 match stream.next().await {
1678 Some(Ok(Message::Chunk(actual))) => {
1679 let expected = StreamChunk::from_pretty(
1680 " I T
1681 + 5 10
1682 + 6 10
1683 + 8 10
1684 + 9 10
1685 + 10 11
1686 - 5 10
1687 - 6 10
1688 - 8 10
1689 U- 10 11
1690 U+ 10 10",
1691 );
1692 assert_stream_chunk_eq!(actual, expected);
1693 }
1694 other => panic!("Expected a chunk message, got {:?}", other),
1695 }
1696
1697 match stream.next().await {
1698 Some(Ok(Message::Watermark(watermark))) => {
1699 assert_eq!(watermark, Watermark::new(0, DataType::Int64, 7_i64.into()));
1700 }
1701 other => panic!("Expected a watermark message, got {:?}", other),
1702 }
1703
1704 match stream.next().await {
1705 Some(Ok(Message::Chunk(actual))) => {
1706 assert_stream_chunk_eq!(actual, chunk_3);
1707 }
1708 other => panic!("Expected a chunk message, got {:?}", other),
1709 }
1710 }
1711}