1use std::collections::HashMap;
18use std::num::NonZeroU64;
19use std::sync::atomic::{AtomicUsize, Ordering};
20use std::sync::{Arc, Mutex};
21use std::time::Instant;
22
23use bytes::Bytes;
24use common_base::cancellation::CancellableFuture;
25use common_telemetry::{debug, error, info};
26use datatypes::arrow::datatypes::SchemaRef;
27use partition::expr::PartitionExpr;
28use smallvec::{SmallVec, smallvec};
29use snafu::{ResultExt, ensure};
30use store_api::metadata::RegionMetadataRef;
31use store_api::region_request::RegionFlushReason;
32use store_api::storage::{RegionId, SequenceNumber};
33use strum::IntoStaticStr;
34use tokio::sync::{Semaphore, mpsc, watch};
35
36use crate::access_layer::{
37 AccessLayerRef, Metrics, OperationType, SstInfoArray, SstWriteRequest, WriteType,
38};
39use crate::cache::CacheManagerRef;
40use crate::compaction::{collect_json2_rewrite_plans, rewrite_json2_batch, rewrite_json2_schema};
41use crate::config::MitoConfig;
42use crate::engine::region_hook::SstFileInfo;
43use crate::error::{
44 Error, FlushCancelledSnafu, FlushRegionSnafu, JoinSnafu, RegionBusySnafu, RegionClosedSnafu,
45 RegionDroppedSnafu, RegionTruncatedSnafu, Result, UnexpectedSnafu,
46};
47use crate::manifest::action::{RegionEdit, RegionMetaAction, RegionMetaActionList};
48use crate::memtable::bulk::ENCODE_ROW_THRESHOLD;
49use crate::memtable::{BoxedRecordBatchIterator, EncodedRange, MemtableRanges, RangesOptions};
50use crate::metrics::{
51 FLUSH_BYTES_TOTAL, FLUSH_ELAPSED, FLUSH_FAILURE_TOTAL, FLUSH_FILE_TOTAL, FLUSH_REQUESTS_TOTAL,
52 INFLIGHT_FLUSH_COUNT,
53};
54use crate::read::FlatSource;
55use crate::read::flat_dedup::{FlatDedupIterator, FlatLastNonNull, FlatLastRow};
56use crate::read::flat_merge::FlatMergeIterator;
57use crate::region::options::{IndexOptions, MergeMode, RegionOptions};
58use crate::region::version::{VersionControlData, VersionControlRef, VersionRef};
59use crate::region::{ManifestContextRef, RegionLeaderState, RegionRoleState, parse_partition_expr};
60use crate::request::{
61 BackgroundNotify, DdlRequest, FlushFailed, FlushFinished, OnFailure, OptionOutputTx, OutputTx,
62 SenderBulkRequest, SenderDdlRequest, SenderWriteRequest, WorkerRequest, WorkerRequestWithTime,
63};
64use crate::schedule::CancellableTaskState;
65use crate::schedule::scheduler::{Job, SchedulerRef};
66use crate::sst::file::{FileMeta, RegionFileId, UncommittedSsts};
67use crate::sst::parquet::metadata::extract_primary_key_range;
68use crate::sst::parquet::{
69 DEFAULT_READ_BATCH_SIZE, DEFAULT_ROW_GROUP_SIZE, SstInfo, WriteOptions, flat_format,
70};
71use crate::sst::{FlatSchemaOptions, FormatType, to_flat_sst_arrow_schema};
72use crate::worker::WorkerListener;
73
74pub trait WriteBufferManager: Send + Sync + std::fmt::Debug {
78 fn should_flush_engine(&self) -> bool;
80
81 fn should_stall(&self) -> bool;
83
84 fn reserve_mem(&self, mem: usize);
86
87 fn schedule_free_mem(&self, mem: usize);
92
93 fn free_mem(&self, mem: usize);
95
96 fn memory_usage(&self) -> usize;
98
99 fn flush_limit(&self) -> usize;
104}
105
106pub type WriteBufferManagerRef = Arc<dyn WriteBufferManager>;
107
108#[derive(Debug)]
113pub struct WriteBufferManagerImpl {
114 global_write_buffer_size: usize,
116 mutable_limit: usize,
118 memory_used: AtomicUsize,
120 memory_active: AtomicUsize,
122 notifier: Option<watch::Sender<()>>,
125}
126
127impl WriteBufferManagerImpl {
128 pub fn new(global_write_buffer_size: usize) -> Self {
130 Self {
131 global_write_buffer_size,
132 mutable_limit: Self::get_mutable_limit(global_write_buffer_size),
133 memory_used: AtomicUsize::new(0),
134 memory_active: AtomicUsize::new(0),
135 notifier: None,
136 }
137 }
138
139 pub fn with_notifier(mut self, notifier: watch::Sender<()>) -> Self {
141 self.notifier = Some(notifier);
142 self
143 }
144
145 pub fn mutable_usage(&self) -> usize {
147 self.memory_active.load(Ordering::Relaxed)
148 }
149
150 fn get_mutable_limit(global_write_buffer_size: usize) -> usize {
152 global_write_buffer_size / 2
154 }
155}
156
157impl WriteBufferManager for WriteBufferManagerImpl {
158 fn should_flush_engine(&self) -> bool {
159 let mutable_memtable_memory_usage = self.memory_active.load(Ordering::Relaxed);
160 if mutable_memtable_memory_usage >= self.mutable_limit {
161 debug!(
162 "Engine should flush (over mutable limit), mutable_usage: {}, memory_usage: {}, mutable_limit: {}, global_limit: {}",
163 mutable_memtable_memory_usage,
164 self.memory_usage(),
165 self.mutable_limit,
166 self.global_write_buffer_size,
167 );
168 return true;
169 }
170
171 let memory_usage = self.memory_used.load(Ordering::Relaxed);
172 if memory_usage >= self.global_write_buffer_size {
173 return true;
174 }
175
176 false
177 }
178
179 fn should_stall(&self) -> bool {
180 self.memory_usage() >= self.global_write_buffer_size
181 }
182
183 fn reserve_mem(&self, mem: usize) {
184 self.memory_used.fetch_add(mem, Ordering::Relaxed);
185 self.memory_active.fetch_add(mem, Ordering::Relaxed);
186 }
187
188 fn schedule_free_mem(&self, mem: usize) {
189 self.memory_active.fetch_sub(mem, Ordering::Relaxed);
190 }
191
192 fn free_mem(&self, mem: usize) {
193 self.memory_used.fetch_sub(mem, Ordering::Relaxed);
194 if let Some(notifier) = &self.notifier {
195 let _ = notifier.send(());
199 }
200 }
201
202 fn memory_usage(&self) -> usize {
203 self.memory_used.load(Ordering::Relaxed)
204 }
205
206 fn flush_limit(&self) -> usize {
207 self.mutable_limit
208 }
209}
210
211#[derive(Debug, IntoStaticStr, Clone, Copy, PartialEq, Eq)]
213pub enum FlushReason {
214 EngineFull,
216 RegionFull,
218 Manual,
220 Alter,
222 Periodically,
224 Downgrading,
226 EnterStaging,
228 RegionMigration,
230 Repartition,
232 RemoteWalPrune,
234 Closing,
236}
237
238impl FlushReason {
239 fn as_str(&self) -> &'static str {
241 self.into()
242 }
243}
244
245impl From<RegionFlushReason> for FlushReason {
246 fn from(reason: RegionFlushReason) -> Self {
247 match reason {
248 RegionFlushReason::RegionMigration => FlushReason::RegionMigration,
249 RegionFlushReason::Repartition => FlushReason::Repartition,
250 RegionFlushReason::RemoteWalPrune => FlushReason::RemoteWalPrune,
251 RegionFlushReason::Closing => FlushReason::Closing,
252 RegionFlushReason::Downgrading => FlushReason::Downgrading,
253 }
254 }
255}
256
257pub(crate) struct RegionFlushTask {
259 pub(crate) region_id: RegionId,
261 pub(crate) reason: FlushReason,
263 pub(crate) senders: Vec<OutputTx>,
265 pub(crate) request_sender: mpsc::Sender<WorkerRequestWithTime>,
267
268 pub(crate) access_layer: AccessLayerRef,
269 pub(crate) listener: WorkerListener,
270 pub(crate) engine_config: Arc<MitoConfig>,
271 pub(crate) row_group_size: Option<usize>,
272 pub(crate) cache_manager: CacheManagerRef,
273 pub(crate) manifest_ctx: ManifestContextRef,
274
275 pub(crate) index_options: IndexOptions,
277 pub(crate) flush_semaphore: Arc<Semaphore>,
279 pub(crate) is_staging: bool,
281 pub(crate) partition_expr: Option<String>,
285}
286
287struct FlushTaskWaiters {
288 region_id: RegionId,
289 senders: Mutex<Vec<OutputTx>>,
290}
291
292impl FlushTaskWaiters {
293 fn new(region_id: RegionId, senders: Vec<OutputTx>) -> Self {
294 Self {
295 region_id,
296 senders: Mutex::new(senders),
297 }
298 }
299
300 fn take(&self) -> Vec<OutputTx> {
301 std::mem::take(&mut *self.senders.lock().unwrap())
302 }
303
304 fn on_failure(&self, err: Arc<Error>) {
305 for sender in self.take() {
306 sender.send(Err(err.clone()).context(FlushRegionSnafu {
307 region_id: self.region_id,
308 }));
309 }
310 }
311}
312
313impl Drop for FlushTaskWaiters {
314 fn drop(&mut self) {
315 self.on_failure(Arc::new(
316 RegionBusySnafu {
317 region_id: self.region_id,
318 }
319 .build(),
320 ));
321 }
322}
323
324impl RegionFlushTask {
325 pub(crate) fn push_sender(&mut self, mut sender: OptionOutputTx) {
327 if let Some(sender) = sender.take_inner() {
328 self.senders.push(sender);
329 }
330 }
331
332 fn on_success(self) {
334 for sender in self.senders {
335 sender.send(Ok(0));
336 }
337 }
338
339 fn on_failure(&mut self, err: Arc<Error>) {
341 for sender in self.senders.drain(..) {
342 sender.send(Err(err.clone()).context(FlushRegionSnafu {
343 region_id: self.region_id,
344 }));
345 }
346 }
347
348 fn into_flush_job(
352 mut self,
353 version_control: &VersionControlRef,
354 state: CancellableTaskState,
355 ) -> (Job, Arc<FlushTaskWaiters>) {
356 let version_data = version_control.current();
359 let waiters = Arc::new(FlushTaskWaiters::new(
360 self.region_id,
361 std::mem::take(&mut self.senders),
362 ));
363 let job_waiters = waiters.clone();
364
365 let job = Box::pin(async move {
366 self.senders = job_waiters.take();
367 INFLIGHT_FLUSH_COUNT.inc();
368 self.do_flush(version_data, state).await;
369 INFLIGHT_FLUSH_COUNT.dec();
370 });
371 (job, waiters)
372 }
373
374 async fn do_flush(&mut self, version_data: VersionControlData, state: CancellableTaskState) {
376 let timer = FLUSH_ELAPSED.with_label_values(&["total"]).start_timer();
377 let uncommitted = UncommittedSsts::new(
378 self.region_id,
379 self.access_layer.clone(),
380 Some(self.cache_manager.clone()),
381 );
382 self.listener.on_flush_begin(self.region_id).await;
383 let flush_result = if state.is_cancelled() {
384 FlushCancelledSnafu.fail()
385 } else {
386 self.flush_memtables(&version_data, &state, &uncommitted)
387 .await
388 };
389
390 let worker_request = match flush_result {
391 Ok(edit) => {
392 let memtables_to_remove = version_data
393 .version
394 .memtables
395 .immutables()
396 .iter()
397 .map(|m| m.id())
398 .collect();
399 let flush_finished = FlushFinished {
400 region_id: self.region_id,
401 flush_reason: self.reason,
402 flushed_entry_id: version_data.last_entry_id,
404 senders: std::mem::take(&mut self.senders),
405 _timer: timer,
406 edit,
407 memtables_to_remove,
408 is_staging: self.is_staging,
409 };
410 WorkerRequest::Background {
411 region_id: self.region_id,
412 notify: BackgroundNotify::FlushFinished(flush_finished),
413 }
414 }
415 Err(e) => {
416 let err = Arc::new(e);
417 let failed = FlushFailed { err: err.clone() };
418 if failed.is_cancelled() {
419 info!("Flush cancelled for region {}", self.region_id);
420 } else {
421 error!(err; "Failed to flush region {}", self.region_id);
422 }
423 timer.stop_and_discard();
425 uncommitted.cleanup().await;
426
427 self.on_failure(err.clone());
428 WorkerRequest::Background {
429 region_id: self.region_id,
430 notify: BackgroundNotify::FlushFailed(failed),
431 }
432 }
433 };
434 self.send_worker_request(worker_request).await;
435 }
436
437 async fn flush_memtables(
440 &self,
441 version_data: &VersionControlData,
442 state: &CancellableTaskState,
443 uncommitted: &UncommittedSsts,
444 ) -> Result<RegionEdit> {
445 let version = &version_data.version;
448 let timer = FLUSH_ELAPSED
449 .with_label_values(&["flush_memtables"])
450 .start_timer();
451
452 let mut write_opts = WriteOptions {
453 write_buffer_size: self.engine_config.sst_write_buffer_size,
454 float_field_encoding: version.options.float_field_encoding,
455 ..Default::default()
456 };
457 if let Some(row_group_size) = self.row_group_size {
458 write_opts.row_group_size = row_group_size;
459 }
460
461 let DoFlushMemtablesResult {
462 file_metas,
463 flushed_bytes,
464 series_count,
465 encoded_part_count,
466 flush_metrics,
467 sst_infos,
468 } = self
469 .do_flush_memtables(version, write_opts, state, uncommitted)
470 .await?;
471
472 if !file_metas.is_empty() {
473 FLUSH_BYTES_TOTAL.inc_by(flushed_bytes);
474 }
475
476 let mut file_ids = Vec::with_capacity(file_metas.len());
477 let mut total_rows = 0;
478 let mut total_bytes = 0;
479 for meta in &file_metas {
480 file_ids.push(meta.file_id);
481 total_rows += meta.num_rows;
482 total_bytes += meta.file_size;
483 }
484 info!(
485 "Successfully flush memtables, region: {}, reason: {}, files: {:?}, series count: {}, total_rows: {}, total_bytes: {}, cost: {:?}, encoded_part_count: {}, metrics: {:?}",
486 self.region_id,
487 self.reason.as_str(),
488 file_ids,
489 series_count,
490 total_rows,
491 total_bytes,
492 timer.stop_and_record(),
493 encoded_part_count,
494 flush_metrics,
495 );
496 flush_metrics.observe();
497
498 let hook = self.manifest_ctx.hook();
499 if let Some(hook) = &hook {
500 let files: Vec<SstFileInfo<'_>> = sst_infos
501 .iter()
502 .zip(file_metas.iter())
503 .map(|(sst_info, file_meta)| SstFileInfo {
504 sst_info_ref: sst_info,
505 file_meta,
506 })
507 .collect();
508 hook.on_sst_files_written(self.region_id, &version.metadata, &files)
509 .await;
510 }
511
512 let edit = RegionEdit {
513 files_to_add: file_metas,
514 files_to_remove: Vec::new(),
515 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
516 compaction_time_window: None,
517 flushed_entry_id: Some(version_data.last_entry_id),
519 flushed_sequence: Some(version_data.committed_sequence),
520 committed_sequence: None,
521 };
522 info!(
523 "Applying {edit:?} to region {}, is_staging: {}",
524 self.region_id, self.is_staging
525 );
526
527 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit.clone()));
528
529 if !state.mark_commit_started() {
531 return FlushCancelledSnafu.fail();
532 }
533 self.listener.on_flush_commit_begin(self.region_id).await;
534
535 let expected_state = if matches!(self.reason, FlushReason::Downgrading) {
536 RegionLeaderState::Downgrading
537 } else {
538 let current_state = self.manifest_ctx.current_state();
540 if current_state == RegionRoleState::Leader(RegionLeaderState::Staging) {
541 RegionLeaderState::Staging
542 } else {
543 RegionLeaderState::Writable
544 }
545 };
546 let manifest_version = match self
547 .manifest_ctx
548 .update_manifest(expected_state, action_list, self.is_staging)
549 .await
550 {
551 Ok(manifest_version) => {
552 uncommitted.disarm_cleanup();
553 manifest_version
554 }
555 Err(e) => {
556 if e.may_have_persisted_manifest_update() {
557 uncommitted.disarm_cleanup();
558 } else {
559 info!(
560 "Cleaning uncommitted SSTs because the manifest update was not persisted, region: {}, job: flush, error: {:?}",
561 self.region_id, e
562 );
563 uncommitted.cleanup().await;
564 }
565 return Err(e);
566 }
567 };
568 info!(
569 "Successfully update manifest version to {manifest_version}, region: {}, is_staging: {}, reason: {}",
570 self.region_id,
571 self.is_staging,
572 self.reason.as_str()
573 );
574
575 Ok(edit)
576 }
577
578 async fn do_flush_memtables(
579 &self,
580 version: &VersionRef,
581 write_opts: WriteOptions,
582 state: &CancellableTaskState,
583 uncommitted: &UncommittedSsts,
584 ) -> Result<DoFlushMemtablesResult> {
585 let memtables = version.memtables.immutables();
586 let mut file_metas = Vec::with_capacity(memtables.len());
587 let mut flushed_bytes = 0;
588 let mut series_count = 0;
589 let mut encoded_part_count = 0;
590 let mut flush_metrics = Metrics::new(WriteType::Flush);
591 let partition_expr = parse_partition_expr(self.partition_expr.as_deref())?;
592 let hook = self.manifest_ctx.hook();
593 let mut all_sst_infos = Vec::new();
594 for mem in memtables {
595 if mem.is_empty() {
596 continue;
598 }
599
600 let compact_start = std::time::Instant::now();
602 if let Err(e) = mem.compact(true) {
603 common_telemetry::error!(e; "Failed to compact memtable before flush");
604 }
605 let compact_cost = compact_start.elapsed();
606 flush_metrics.compact_memtable += compact_cost;
607
608 let mem_stats = mem.stats();
609 let batch_size = crate::batch_size::estimate_batch_size([(
610 mem_stats.num_rows() as u64,
611 mem_stats.bytes_allocated() as u64,
612 )]);
613 let mem_ranges =
615 mem.ranges(None, RangesOptions::for_flush().with_batch_size(batch_size))?;
616 let num_mem_ranges = mem_ranges.ranges.len();
617
618 let num_mem_rows = mem_ranges.num_rows();
620 let memtable_series_count = mem_ranges.series_count();
621 let memtable_id = mem.id();
622 series_count += memtable_series_count;
625
626 let flush_start = Instant::now();
627 let FlushFlatMemResult {
628 num_encoded,
629 num_sources,
630 results,
631 } = self
632 .flush_flat_mem_ranges(version, &write_opts, mem_ranges, state, uncommitted)
633 .await?;
634 encoded_part_count += num_encoded;
635 for (source_idx, result) in results.into_iter().enumerate() {
636 let (max_sequence, ssts_written, metrics) = result?;
637 if ssts_written.is_empty() {
638 continue;
640 }
641
642 common_telemetry::debug!(
643 "Region {} flush one memtable {} {}/{}, metrics: {:?}",
644 self.region_id,
645 memtable_id,
646 source_idx,
647 num_sources,
648 metrics
649 );
650
651 flush_metrics = flush_metrics.merge(metrics);
652
653 for sst_info in &ssts_written {
654 flushed_bytes += sst_info.file_size;
655 let pk_range = sst_info
656 .file_metadata
657 .as_ref()
658 .and_then(|meta| extract_primary_key_range(meta, &version.metadata));
659 file_metas.push(Self::new_file_meta(
660 self.region_id,
661 max_sequence,
662 sst_info,
663 partition_expr.clone(),
664 pk_range,
665 version.options.preserve_row_sequence,
666 ));
667 }
668 if hook.is_some() {
669 all_sst_infos.extend(ssts_written);
670 }
671 }
672
673 common_telemetry::debug!(
674 "Region {} flush {} memtables for {}, num_mem_ranges: {}, num_encoded: {}, num_rows: {}, flush_cost: {:?}, compact_cost: {:?}",
675 self.region_id,
676 num_sources,
677 memtable_id,
678 num_mem_ranges,
679 num_encoded,
680 num_mem_rows,
681 flush_start.elapsed(),
682 compact_cost,
683 );
684 }
685
686 Ok(DoFlushMemtablesResult {
687 file_metas,
688 flushed_bytes,
689 series_count,
690 encoded_part_count,
691 flush_metrics,
692 sst_infos: all_sst_infos,
693 })
694 }
695
696 async fn flush_flat_mem_ranges(
697 &self,
698 version: &VersionRef,
699 write_opts: &WriteOptions,
700 mem_ranges: MemtableRanges,
701 state: &CancellableTaskState,
702 uncommitted: &UncommittedSsts,
703 ) -> Result<FlushFlatMemResult> {
704 let batch_schema = to_flat_sst_arrow_schema(
705 &version.metadata,
706 &FlatSchemaOptions::from_encoding(version.metadata.primary_key_encoding),
707 );
708 let field_column_start =
709 flat_format::field_column_start(&version.metadata, batch_schema.fields().len());
710 let flat_sources = memtable_flat_sources(
711 batch_schema,
712 mem_ranges,
713 &version.metadata,
714 &version.options,
715 field_column_start,
716 )?;
717 let mut tasks = Vec::with_capacity(flat_sources.encoded.len() + flat_sources.sources.len());
718 let num_encoded = flat_sources.encoded.len();
719 for (source, max_sequence) in flat_sources.sources {
720 let write_request = self.new_write_request(version, max_sequence, source);
721 let access_layer = self.access_layer.clone();
722 let write_opts = write_opts.clone();
723 let semaphore = self.flush_semaphore.clone();
724 let uncommitted = uncommitted.clone();
725 let task = common_runtime::spawn_global(async move {
726 let _permit = semaphore.acquire().await.unwrap();
727 let mut metrics = Metrics::new(WriteType::Flush);
728 let ssts = access_layer
729 .write_sst(write_request, &write_opts, &mut metrics)
730 .await?;
731 uncommitted.track(&ssts);
732 FLUSH_FILE_TOTAL.inc_by(ssts.len() as u64);
733 Ok((max_sequence, ssts, metrics))
734 });
735 tasks.push(task);
736 }
737 for (encoded, max_sequence) in flat_sources.encoded {
738 let access_layer = self.access_layer.clone();
739 let cache_manager = self.cache_manager.clone();
740 let region_id = version.metadata.region_id;
741 let write_buffer_size = write_opts.write_buffer_size;
742 let semaphore = self.flush_semaphore.clone();
743 let uncommitted = uncommitted.clone();
744 let task = common_runtime::spawn_global(async move {
745 let _permit = semaphore.acquire().await.unwrap();
746 let metrics = access_layer
747 .put_sst(
748 &encoded.data,
749 RegionFileId::new(region_id, encoded.sst_info.file_id),
750 &cache_manager,
751 write_buffer_size,
752 )
753 .await?;
754 uncommitted.track(std::slice::from_ref(&encoded.sst_info));
755 FLUSH_FILE_TOTAL.inc();
756 Ok((max_sequence, smallvec![encoded.sst_info], metrics))
757 });
758 tasks.push(task);
759 }
760 let num_sources = tasks.len();
761 let abort_handles = tasks
762 .iter()
763 .map(|task| task.abort_handle())
764 .collect::<Vec<_>>();
765 let join_all = futures::future::join_all(tasks);
766 tokio::pin!(join_all);
767 let results = match CancellableFuture::new(join_all.as_mut(), state.cancel_handle()).await {
768 Ok(results) => results
769 .into_iter()
770 .map(|result| result.context(JoinSnafu))
771 .collect::<Result<Vec<_>>>()?,
772 Err(_) => {
773 for handle in abort_handles {
774 handle.abort();
775 }
776 let _ = join_all.await;
779 return FlushCancelledSnafu.fail();
780 }
781 };
782 Ok(FlushFlatMemResult {
783 num_encoded,
784 num_sources,
785 results,
786 })
787 }
788
789 fn new_file_meta(
790 region_id: RegionId,
791 max_sequence: u64,
792 sst_info: &SstInfo,
793 partition_expr: Option<PartitionExpr>,
794 primary_key_range: Option<(Bytes, Bytes)>,
795 preserve_row_sequence: bool,
796 ) -> FileMeta {
797 let (primary_key_min, primary_key_max) = match primary_key_range {
798 Some((min, max)) => (Some(min), Some(max)),
799 None => (None, None),
800 };
801 FileMeta {
802 region_id,
803 file_id: sst_info.file_id,
804 time_range: sst_info.time_range,
805 level: 0,
806 file_size: sst_info.file_size,
807 max_row_group_uncompressed_size: sst_info.max_row_group_uncompressed_size,
808 available_indexes: sst_info.index_metadata.build_available_indexes(),
809 indexes: sst_info.index_metadata.build_indexes(),
810 index_file_size: sst_info.index_metadata.file_size,
811 index_version: 0,
812 num_rows: sst_info.num_rows as u64,
813 num_row_groups: sst_info.num_row_groups,
814 sequence: NonZeroU64::new(max_sequence),
815 partition_expr,
816 num_series: sst_info.num_series,
817 primary_key_min,
818 primary_key_max,
819 preserve_row_sequence,
820 }
821 }
822
823 fn new_write_request(
824 &self,
825 version: &VersionRef,
826 max_sequence: u64,
827 source: FlatSource,
828 ) -> SstWriteRequest {
829 let flat_format = version
830 .options
831 .sst_format
832 .map(|f| f == FormatType::Flat)
833 .unwrap_or(self.engine_config.default_flat_format);
834 SstWriteRequest {
835 op_type: OperationType::Flush,
836 metadata: version.metadata.clone(),
837 source,
838 cache_manager: self.cache_manager.clone(),
839 storage: version.options.storage.clone(),
840 max_sequence: Some(max_sequence),
841 sst_write_format: if flat_format {
842 FormatType::Flat
843 } else {
844 FormatType::PrimaryKey
845 },
846 preserve_row_sequence: version.options.preserve_row_sequence,
847 index_options: self.index_options.clone(),
848 index_config: self.engine_config.index.clone(),
849 inverted_index_config: self.engine_config.inverted_index.clone(),
850 fulltext_index_config: self.engine_config.fulltext_index.clone(),
851 bloom_filter_index_config: self.engine_config.bloom_filter_index.clone(),
852 }
853 }
854
855 pub(crate) async fn send_worker_request(&self, request: WorkerRequest) {
857 if let Err(e) = self
858 .request_sender
859 .send(WorkerRequestWithTime::new(request))
860 .await
861 {
862 let request = e.0.request;
863 error!(
864 "Failed to notify flush job status for region {}, request: {:?}",
865 self.region_id, request
866 );
867 if let WorkerRequest::Background {
868 notify: BackgroundNotify::FlushFinished(mut finished),
869 ..
870 } = request
871 {
872 finished.on_failure(
873 RegionClosedSnafu {
874 region_id: self.region_id,
875 }
876 .build(),
877 );
878 }
879 }
880 }
881
882 fn merge(&mut self, mut other: RegionFlushTask) {
884 assert_eq!(self.region_id, other.region_id);
885 self.senders.append(&mut other.senders);
887 }
888}
889
890struct FlushFlatMemResult {
891 num_encoded: usize,
892 num_sources: usize,
893 results: Vec<Result<(SequenceNumber, SstInfoArray, Metrics)>>,
894}
895
896struct DoFlushMemtablesResult {
897 file_metas: Vec<FileMeta>,
898 flushed_bytes: u64,
899 series_count: usize,
900 encoded_part_count: usize,
901 flush_metrics: Metrics,
902 sst_infos: Vec<SstInfo>,
903}
904
905struct FlatSources {
906 sources: SmallVec<[(FlatSource, SequenceNumber); 4]>,
907 encoded: SmallVec<[(EncodedRange, SequenceNumber); 4]>,
908}
909
910fn memtable_flat_sources(
912 schema: SchemaRef,
913 mem_ranges: MemtableRanges,
914 metadata: &RegionMetadataRef,
915 options: &RegionOptions,
916 field_column_start: usize,
917) -> Result<FlatSources> {
918 let MemtableRanges { ranges } = mem_ranges;
919 let mut flat_sources = FlatSources {
920 sources: SmallVec::new(),
921 encoded: SmallVec::new(),
922 };
923
924 if ranges.len() == 1 {
925 debug!("Flushing single flat range");
926
927 let only_range = ranges.into_values().next().unwrap();
928 let max_sequence = only_range.stats().max_sequence();
929 if let Some(encoded) = only_range.encoded() {
930 flat_sources.encoded.push((encoded, max_sequence));
931 } else {
932 let schema = only_range.record_batch_schema_hint().unwrap_or(schema);
933 let iter = only_range.build_record_batch_iter(None, None)?;
934 let iter = maybe_dedup_one(
937 options.append_mode,
938 options.merge_mode(),
939 field_column_start,
940 iter,
941 );
942 flat_sources
943 .sources
944 .push((FlatSource::new_iter(schema, iter), max_sequence));
945 };
946 } else {
947 let min_flush_rows = *ENCODE_ROW_THRESHOLD;
948 let total_rows: usize = ranges
950 .values()
951 .filter(|r| r.encoded().is_none())
952 .map(|r| r.num_rows())
953 .sum();
954 debug!(
955 "Flushing multiple flat ranges, total_rows: {}, min_flush_rows: {}, num_ranges: {}",
956 total_rows,
957 min_flush_rows,
958 ranges.len()
959 );
960 let mut rows_remaining = total_rows;
961 let mut last_iter_rows = 0;
962 let num_ranges = ranges.len();
963 let mut input_iters = Vec::with_capacity(num_ranges);
964 let mut current_ranges = Vec::new();
965
966 let schemas = ranges
967 .values()
968 .filter(|range| range.encoded().is_none())
969 .map(|range| {
970 (
971 range
972 .record_batch_schema_hint()
973 .unwrap_or_else(|| schema.clone()),
974 range.num_rows() as u64,
975 )
976 })
977 .collect::<Vec<_>>();
978 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
979 let schema = rewrite_json2_schema(
980 schemas.first().map(|(schema, _)| schema).unwrap_or(&schema),
981 &plans,
982 );
983
984 for (_range_id, range) in ranges {
985 if let Some(encoded) = range.encoded() {
986 let max_sequence = range.stats().max_sequence();
987 flat_sources.encoded.push((encoded, max_sequence));
988 continue;
989 }
990
991 if let Some(actual) = range.record_batch_schema_hint() {
992 let actual = rewrite_json2_schema(&actual, &plans);
993 ensure!(
994 actual == schema,
995 UnexpectedSnafu {
996 reason: format!(
997 "Different schemas found in a MemtableRanges, expected: {}, actual: {}",
998 schema, actual,
999 ),
1000 }
1001 )
1002 }
1003
1004 let iter = range.build_record_batch_iter(None, None)?;
1005 let iter: BoxedRecordBatchIterator = if plans.is_empty() {
1006 iter
1007 } else {
1008 let plans = plans.clone();
1009 Box::new(iter.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1010 };
1011 input_iters.push(iter);
1012 let range_rows = range.num_rows();
1013 last_iter_rows += range_rows;
1014 rows_remaining -= range_rows;
1015 current_ranges.push(range);
1016
1017 if last_iter_rows >= min_flush_rows
1020 && (rows_remaining == 0 || rows_remaining >= DEFAULT_ROW_GROUP_SIZE)
1021 {
1022 debug!(
1023 "Flush batch ready, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1024 last_iter_rows,
1025 min_flush_rows,
1026 input_iters.len(),
1027 rows_remaining
1028 );
1029
1030 let max_sequence = current_ranges
1032 .iter()
1033 .map(|r| r.stats().max_sequence())
1034 .max()
1035 .unwrap_or(0);
1036 let batch_size =
1037 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1038 let stats = range.stats();
1039 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1040 }));
1041
1042 let input_iters =
1043 std::mem::replace(&mut input_iters, Vec::with_capacity(num_ranges));
1044
1045 let maybe_dedup = merge_and_dedup_with_batch_size(
1046 &schema,
1047 options.append_mode,
1048 options.merge_mode(),
1049 field_column_start,
1050 input_iters,
1051 batch_size,
1052 )?;
1053
1054 flat_sources.sources.push((
1055 FlatSource::new_iter(schema.clone(), maybe_dedup),
1056 max_sequence,
1057 ));
1058 last_iter_rows = 0;
1059 current_ranges.clear();
1060 }
1061 }
1062
1063 if !input_iters.is_empty() {
1065 debug!(
1066 "Flush remaining batch, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1067 last_iter_rows,
1068 min_flush_rows,
1069 input_iters.len(),
1070 rows_remaining
1071 );
1072
1073 let max_sequence = current_ranges
1074 .iter()
1075 .map(|r| r.stats().max_sequence())
1076 .max()
1077 .unwrap_or(0);
1078 let batch_size =
1079 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1080 let stats = range.stats();
1081 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1082 }));
1083
1084 let maybe_dedup = merge_and_dedup_with_batch_size(
1085 &schema,
1086 options.append_mode,
1087 options.merge_mode(),
1088 field_column_start,
1089 input_iters,
1090 batch_size,
1091 )?;
1092
1093 flat_sources
1094 .sources
1095 .push((FlatSource::new_iter(schema, maybe_dedup), max_sequence));
1096 }
1097 }
1098
1099 Ok(flat_sources)
1100}
1101
1102pub fn merge_and_dedup(
1147 schema: &SchemaRef,
1148 append_mode: bool,
1149 merge_mode: MergeMode,
1150 field_column_start: usize,
1151 input_iters: Vec<BoxedRecordBatchIterator>,
1152) -> Result<BoxedRecordBatchIterator> {
1153 merge_and_dedup_with_batch_size(
1154 schema,
1155 append_mode,
1156 merge_mode,
1157 field_column_start,
1158 input_iters,
1159 DEFAULT_READ_BATCH_SIZE,
1160 )
1161}
1162
1163pub fn merge_and_dedup_with_batch_size(
1169 schema: &SchemaRef,
1170 append_mode: bool,
1171 merge_mode: MergeMode,
1172 field_column_start: usize,
1173 input_iters: Vec<BoxedRecordBatchIterator>,
1174 batch_size: usize,
1175) -> Result<BoxedRecordBatchIterator> {
1176 let batch_size = batch_size.max(1);
1177 let merge_iter = FlatMergeIterator::new(schema.clone(), input_iters, batch_size)?;
1178 let maybe_dedup = if append_mode {
1179 Box::new(merge_iter) as _
1181 } else {
1182 match merge_mode {
1184 MergeMode::LastRow => {
1185 Box::new(FlatDedupIterator::new(merge_iter, FlatLastRow::new(false))) as _
1186 }
1187 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1188 merge_iter,
1189 FlatLastNonNull::new(field_column_start, false),
1190 )) as _,
1191 }
1192 };
1193 Ok(maybe_dedup)
1194}
1195
1196pub fn maybe_dedup_one(
1197 append_mode: bool,
1198 merge_mode: MergeMode,
1199 field_column_start: usize,
1200 input_iter: BoxedRecordBatchIterator,
1201) -> BoxedRecordBatchIterator {
1202 if append_mode {
1203 input_iter
1205 } else {
1206 match merge_mode {
1208 MergeMode::LastRow => {
1209 Box::new(FlatDedupIterator::new(input_iter, FlatLastRow::new(false)))
1210 }
1211 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1212 input_iter,
1213 FlatLastNonNull::new(field_column_start, false),
1214 )),
1215 }
1216 }
1217}
1218
1219pub(crate) struct FlushScheduler {
1221 region_status: HashMap<RegionId, FlushStatus>,
1223 scheduler: SchedulerRef,
1225}
1226
1227impl FlushScheduler {
1228 pub(crate) fn new(scheduler: SchedulerRef) -> FlushScheduler {
1230 FlushScheduler {
1231 region_status: HashMap::new(),
1232 scheduler,
1233 }
1234 }
1235
1236 pub(crate) fn is_flush_requested(&self, region_id: RegionId) -> bool {
1238 self.region_status.contains_key(®ion_id)
1239 }
1240
1241 fn schedule_flush_task(
1242 &mut self,
1243 version_control: &VersionControlRef,
1244 mut task: RegionFlushTask,
1245 ) -> Result<CancellableTaskState> {
1246 let region_id = task.region_id;
1247
1248 if let Err(e) = version_control.freeze_mutable() {
1250 error!(e; "Failed to freeze the mutable memtable for region {}", region_id);
1251 task.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1252
1253 return Err(e);
1254 }
1255 let state = CancellableTaskState::new();
1257 let (job, waiters) = task.into_flush_job(version_control, state.clone());
1258 if let Err(e) = self.scheduler.schedule(job) {
1259 error!(e; "Failed to schedule flush job for region {}", region_id);
1260 waiters.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1261
1262 return Err(e);
1263 }
1264 Ok(state)
1265 }
1266
1267 pub(crate) fn schedule_flush(
1269 &mut self,
1270 region_id: RegionId,
1271 version_control: &VersionControlRef,
1272 mut task: RegionFlushTask,
1273 ) -> Result<()> {
1274 debug_assert_eq!(region_id, task.region_id);
1275
1276 let version = version_control.current().version;
1277 if version.memtables.is_empty() {
1278 debug_assert!(!self.region_status.contains_key(®ion_id));
1279 task.on_success();
1281 return Ok(());
1282 }
1283
1284 FLUSH_REQUESTS_TOTAL
1286 .with_label_values(&[task.reason.as_str()])
1287 .inc();
1288
1289 if let Some(flush_status) = self.region_status.get_mut(®ion_id) {
1291 if flush_status.has_pending_lifecycle_ddl() {
1292 task.on_failure(Arc::new(FlushCancelledSnafu.build()));
1293 return Ok(());
1294 }
1295 debug!("Merging flush task for region {}", region_id);
1297 flush_status.merge_task(task);
1298 return Ok(());
1299 }
1300
1301 let closing = task.reason == FlushReason::Closing;
1302 let state = self.schedule_flush_task(version_control, task)?;
1303
1304 let _ = self.region_status.insert(
1306 region_id,
1307 FlushStatus::new(region_id, version_control.clone(), state, closing),
1308 );
1309
1310 Ok(())
1311 }
1312
1313 pub(crate) fn on_flush_success(
1317 &mut self,
1318 region_id: RegionId,
1319 ) -> Option<(
1320 Vec<SenderDdlRequest>,
1321 Vec<SenderWriteRequest>,
1322 Vec<SenderBulkRequest>,
1323 )> {
1324 let flush_status = self.region_status.get_mut(®ion_id)?;
1325 if flush_status.pending_task.is_none() {
1327 debug!(
1330 "Region {} doesn't have any pending flush task, removing it from the status",
1331 region_id
1332 );
1333 let flush_status = self.region_status.remove(®ion_id).unwrap();
1334 return Some((
1335 flush_status.pending_ddls,
1336 flush_status.pending_writes,
1337 flush_status.pending_bulk_writes,
1338 ));
1339 }
1340
1341 let version_data = flush_status.version_control.current();
1343 if version_data.version.memtables.is_empty() {
1344 let task = flush_status.pending_task.take().unwrap();
1347 task.on_success();
1349 debug!(
1350 "Region {} has nothing to flush, removing it from the status",
1351 region_id
1352 );
1353 let flush_status = self.region_status.remove(®ion_id).unwrap();
1355 return Some((
1356 flush_status.pending_ddls,
1357 flush_status.pending_writes,
1358 flush_status.pending_bulk_writes,
1359 ));
1360 }
1361
1362 debug!("Scheduling pending flush task for region {}", region_id);
1364 let task = flush_status.pending_task.take().unwrap();
1366 let version_control = flush_status.version_control.clone();
1367 match self.schedule_flush_task(&version_control, task) {
1368 Ok(state) => {
1369 self.region_status.get_mut(®ion_id).unwrap().state = state;
1370 }
1371 Err(err) => {
1372 error!(
1373 err;
1374 "Flush succeeded for region {region_id}, but failed to schedule next flush for it."
1375 );
1376 let flush_status = self.region_status.remove(®ion_id).unwrap();
1377 flush_status.fail_all(Arc::new(RegionBusySnafu { region_id }.build()));
1378 return None;
1379 }
1380 }
1381 None
1383 }
1384
1385 pub(crate) fn on_flush_failed(
1390 &mut self,
1391 region_id: RegionId,
1392 err: Arc<Error>,
1393 ) -> Vec<SenderDdlRequest> {
1394 if matches!(err.as_ref(), Error::FlushCancelled { .. }) {
1395 info!("Region {} flush was cancelled", region_id);
1396 } else {
1397 error!(err; "Region {} failed to flush, cancel all pending tasks", region_id);
1398 FLUSH_FAILURE_TOTAL.inc();
1399 }
1400
1401 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1403 return Vec::new();
1404 };
1405
1406 flush_status.on_failure(err)
1407 }
1408
1409 pub(crate) fn try_cancel_and_add_ddl<T>(
1411 &mut self,
1412 region_id: RegionId,
1413 sender: OptionOutputTx,
1414 request: T,
1415 into_ddl_request: impl FnOnce(T) -> DdlRequest,
1416 ) -> std::result::Result<(), (OptionOutputTx, T)> {
1417 let Some(status) = self.region_status.get_mut(®ion_id) else {
1418 return Err((sender, request));
1419 };
1420
1421 let cancel_result = status.state.request_cancel();
1422 debug!(
1423 "Requested flush cancellation for region {}, result: {:?}",
1424 region_id, cancel_result
1425 );
1426 status.pending_ddls.push(SenderDdlRequest {
1427 region_id,
1428 sender,
1429 request: into_ddl_request(request),
1430 });
1431 Ok(())
1432 }
1433
1434 pub(crate) fn on_region_dropped(&mut self, region_id: RegionId) {
1436 self.remove_region_on_failure(
1437 region_id,
1438 Arc::new(RegionDroppedSnafu { region_id }.build()),
1439 );
1440 }
1441
1442 pub(crate) fn on_region_closed(&mut self, region_id: RegionId) {
1444 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1445 return;
1446 };
1447
1448 flush_status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
1449 }
1450
1451 pub(crate) fn on_region_truncated(&mut self, region_id: RegionId) {
1453 self.remove_region_on_failure(
1454 region_id,
1455 Arc::new(RegionTruncatedSnafu { region_id }.build()),
1456 );
1457 }
1458
1459 fn remove_region_on_failure(&mut self, region_id: RegionId, err: Arc<Error>) {
1460 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1462 return;
1463 };
1464
1465 flush_status.fail_all(err);
1467 }
1468
1469 pub(crate) fn add_ddl_request_to_pending(&mut self, request: SenderDdlRequest) {
1474 let status = self.region_status.get_mut(&request.region_id).unwrap();
1475 status.pending_ddls.push(request);
1476 }
1477
1478 pub(crate) fn add_write_request_to_pending(&mut self, request: SenderWriteRequest) {
1483 let status = self
1484 .region_status
1485 .get_mut(&request.request.region_id)
1486 .unwrap();
1487 status.pending_writes.push(request);
1488 }
1489
1490 pub(crate) fn add_bulk_request_to_pending(&mut self, request: SenderBulkRequest) {
1495 let status = self.region_status.get_mut(&request.region_id).unwrap();
1496 status.pending_bulk_writes.push(request);
1497 }
1498
1499 pub(crate) fn has_pending_ddls(&self, region_id: RegionId) -> bool {
1501 self.region_status
1502 .get(®ion_id)
1503 .map(|status| !status.pending_ddls.is_empty() || status.closing)
1504 .unwrap_or(false)
1505 }
1506}
1507
1508impl Drop for FlushScheduler {
1509 fn drop(&mut self) {
1510 for (region_id, flush_status) in self.region_status.drain() {
1511 flush_status.fail_all(Arc::new(RegionClosedSnafu { region_id }.build()));
1513 }
1514 }
1515}
1516
1517struct FlushStatus {
1521 region_id: RegionId,
1523 version_control: VersionControlRef,
1525 state: CancellableTaskState,
1527 pending_task: Option<RegionFlushTask>,
1529 closing: bool,
1531 pending_ddls: Vec<SenderDdlRequest>,
1533 pending_writes: Vec<SenderWriteRequest>,
1535 pending_bulk_writes: Vec<SenderBulkRequest>,
1537}
1538
1539impl FlushStatus {
1540 fn new(
1541 region_id: RegionId,
1542 version_control: VersionControlRef,
1543 state: CancellableTaskState,
1544 closing: bool,
1545 ) -> FlushStatus {
1546 FlushStatus {
1547 region_id,
1548 version_control,
1549 state,
1550 pending_task: None,
1551 closing,
1552 pending_ddls: Vec::new(),
1553 pending_writes: Vec::new(),
1554 pending_bulk_writes: Vec::new(),
1555 }
1556 }
1557
1558 fn merge_task(&mut self, task: RegionFlushTask) {
1560 self.closing |= task.reason == FlushReason::Closing;
1561 if let Some(pending) = &mut self.pending_task {
1562 pending.merge(task);
1563 } else {
1564 self.pending_task = Some(task);
1565 }
1566 }
1567
1568 fn has_pending_lifecycle_ddl(&self) -> bool {
1569 self.pending_ddls
1570 .iter()
1571 .any(|ddl| matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)))
1572 }
1573
1574 fn on_failure(self, err: Arc<Error>) -> Vec<SenderDdlRequest> {
1576 if let Some(mut task) = self.pending_task {
1577 task.on_failure(err.clone());
1578 }
1579 let mut lifecycle_ddls = Vec::new();
1580 for ddl in self.pending_ddls {
1581 if matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)) {
1582 lifecycle_ddls.push(ddl);
1583 } else {
1584 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1585 region_id: self.region_id,
1586 }));
1587 }
1588 }
1589 for write_req in self.pending_writes {
1590 write_req
1591 .sender
1592 .send(Err(err.clone()).context(FlushRegionSnafu {
1593 region_id: self.region_id,
1594 }));
1595 }
1596 for bulk_req in self.pending_bulk_writes {
1597 bulk_req
1598 .sender
1599 .send(Err(err.clone()).context(FlushRegionSnafu {
1600 region_id: self.region_id,
1601 }));
1602 }
1603 lifecycle_ddls
1604 }
1605
1606 fn fail_all(self, err: Arc<Error>) {
1607 let region_id = self.region_id;
1608 let ddls = self.on_failure(err.clone());
1609 for ddl in ddls {
1610 ddl.sender
1611 .send(Err(err.clone()).context(FlushRegionSnafu { region_id }));
1612 }
1613 }
1614
1615 fn on_region_closed(self, err: Arc<Error>) {
1616 if let Some(mut task) = self.pending_task {
1617 task.on_failure(err.clone());
1618 }
1619
1620 for ddl in self.pending_ddls {
1621 if matches!(ddl.request, DdlRequest::Close(_)) {
1622 ddl.sender.send(Ok(0));
1623 } else {
1624 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1625 region_id: self.region_id,
1626 }));
1627 }
1628 }
1629
1630 for write_req in self.pending_writes {
1631 write_req
1632 .sender
1633 .send(Err(err.clone()).context(FlushRegionSnafu {
1634 region_id: self.region_id,
1635 }));
1636 }
1637 for bulk_req in self.pending_bulk_writes {
1638 bulk_req
1639 .sender
1640 .send(Err(err.clone()).context(FlushRegionSnafu {
1641 region_id: self.region_id,
1642 }));
1643 }
1644 }
1645}
1646
1647#[cfg(test)]
1648mod tests {
1649 use api::v1::{OpType, Rows};
1650 use common_error::ext::ErrorExt;
1651 use common_error::status_code::StatusCode;
1652 use datatypes::arrow::datatypes::Schema;
1653 use datatypes::arrow::record_batch::RecordBatch;
1654 use mito_codec::row_converter::build_primary_key_codec;
1655 use tokio::sync::oneshot;
1656
1657 use super::*;
1658 use crate::cache::CacheManager;
1659 use crate::error::InvalidSchedulerStateSnafu;
1660 use crate::memtable::bulk::part::BulkPartConverter;
1661 use crate::memtable::time_series::TimeSeriesMemtableBuilder;
1662 use crate::memtable::{
1663 IterBuilder, Memtable, MemtableRange, MemtableRangeContext, MemtableStats, RangesOptions,
1664 };
1665 use crate::request::WriteRequest;
1666 use crate::schedule::scheduler::Scheduler;
1667 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1668 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1669 use crate::test_util::scheduler_util::{SchedulerEnv, VecScheduler};
1670 use crate::test_util::version_util::{VersionControlBuilder, write_rows_to_version};
1671
1672 struct FailingScheduler;
1673
1674 #[async_trait::async_trait]
1675 impl Scheduler for FailingScheduler {
1676 fn schedule(&self, _job: Job) -> Result<()> {
1677 InvalidSchedulerStateSnafu.fail()
1678 }
1679
1680 async fn stop(&self, _await_termination: bool) -> Result<()> {
1681 Ok(())
1682 }
1683 }
1684
1685 fn new_test_flush_task(
1686 env: &SchedulerEnv,
1687 region_id: RegionId,
1688 reason: FlushReason,
1689 request_sender: mpsc::Sender<WorkerRequestWithTime>,
1690 manifest_ctx: ManifestContextRef,
1691 ) -> RegionFlushTask {
1692 RegionFlushTask {
1693 region_id,
1694 reason,
1695 senders: Vec::new(),
1696 request_sender,
1697 access_layer: env.access_layer.clone(),
1698 listener: WorkerListener::default(),
1699 engine_config: Arc::new(MitoConfig::default()),
1700 row_group_size: None,
1701 cache_manager: Arc::new(CacheManager::default()),
1702 manifest_ctx,
1703 index_options: IndexOptions::default(),
1704 flush_semaphore: Arc::new(Semaphore::new(2)),
1705 is_staging: false,
1706 partition_expr: None,
1707 }
1708 }
1709
1710 fn new_test_bulk_request(
1711 region_id: RegionId,
1712 ) -> (
1713 SenderBulkRequest,
1714 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1715 ) {
1716 let metadata = metadata_for_test();
1717 let schema = to_flat_sst_arrow_schema(
1718 &metadata,
1719 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1720 );
1721 let pk_codec = build_primary_key_codec(&metadata);
1722 let mut converter = BulkPartConverter::new(&metadata, schema, 16, pk_codec, true);
1723 let kvs = build_key_values_with_ts_seq_values(
1724 &metadata,
1725 "bulk_key".to_string(),
1726 1,
1727 std::iter::once(1000i64),
1728 std::iter::once(Some(1.0f64)),
1729 1,
1730 );
1731 converter.append_key_values(&kvs).unwrap();
1732 let (sender, receiver) = oneshot::channel();
1733
1734 (
1735 SenderBulkRequest {
1736 skip_wal: false,
1737 sender: OptionOutputTx::from(sender),
1738 region_id,
1739 request: converter.convert().unwrap(),
1740 region_metadata: Some(metadata),
1741 partition_expr_version: None,
1742 },
1743 receiver,
1744 )
1745 }
1746
1747 fn new_test_write_request(
1748 region_id: RegionId,
1749 ) -> (
1750 SenderWriteRequest,
1751 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1752 ) {
1753 let (sender, receiver) = oneshot::channel();
1754 let request = WriteRequest::new(region_id, OpType::Put, Rows::default(), None).unwrap();
1755 (
1756 SenderWriteRequest {
1757 sender: OptionOutputTx::from(sender),
1758 request,
1759 },
1760 receiver,
1761 )
1762 }
1763
1764 #[test]
1765 fn test_get_mutable_limit() {
1766 assert_eq!(4, WriteBufferManagerImpl::get_mutable_limit(8));
1767 assert_eq!(5, WriteBufferManagerImpl::get_mutable_limit(10));
1768 assert_eq!(32, WriteBufferManagerImpl::get_mutable_limit(64));
1769 assert_eq!(0, WriteBufferManagerImpl::get_mutable_limit(0));
1770 }
1771
1772 #[test]
1773 fn test_over_mutable_limit() {
1774 let manager = WriteBufferManagerImpl::new(1000);
1776 manager.reserve_mem(400);
1777 assert!(!manager.should_flush_engine());
1778 assert!(!manager.should_stall());
1779
1780 manager.reserve_mem(400);
1782 assert!(manager.should_flush_engine());
1783
1784 manager.schedule_free_mem(400);
1786 assert!(!manager.should_flush_engine());
1787 assert_eq!(800, manager.memory_used.load(Ordering::Relaxed));
1788 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1789
1790 manager.free_mem(400);
1792 assert_eq!(400, manager.memory_used.load(Ordering::Relaxed));
1793 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1794 }
1795
1796 #[test]
1797 fn test_over_global() {
1798 let manager = WriteBufferManagerImpl::new(1000);
1800 manager.reserve_mem(1100);
1801 assert!(manager.should_stall());
1802 manager.schedule_free_mem(200);
1804 assert!(manager.should_flush_engine());
1805 assert!(manager.should_stall());
1806
1807 manager.schedule_free_mem(450);
1809 assert!(manager.should_flush_engine());
1810 assert!(manager.should_stall());
1811
1812 manager.reserve_mem(50);
1814 assert!(manager.should_flush_engine());
1815 manager.reserve_mem(100);
1816 assert!(manager.should_flush_engine());
1817 }
1818
1819 #[test]
1820 fn test_manager_notify() {
1821 let (sender, receiver) = watch::channel(());
1822 let manager = WriteBufferManagerImpl::new(1000).with_notifier(sender);
1823 manager.reserve_mem(500);
1824 assert!(!receiver.has_changed().unwrap());
1825 manager.schedule_free_mem(500);
1826 assert!(!receiver.has_changed().unwrap());
1827 manager.free_mem(500);
1828 assert!(receiver.has_changed().unwrap());
1829 }
1830
1831 #[tokio::test]
1832 async fn test_schedule_empty() {
1833 let env = SchedulerEnv::new().await;
1834 let (tx, _rx) = mpsc::channel(4);
1835 let mut scheduler = env.mock_flush_scheduler();
1836 let builder = VersionControlBuilder::new();
1837
1838 let version_control = Arc::new(builder.build());
1839 let (output_tx, output_rx) = oneshot::channel();
1840 let mut task = RegionFlushTask {
1841 region_id: builder.region_id(),
1842 reason: FlushReason::Manual,
1843 senders: Vec::new(),
1844 request_sender: tx,
1845 access_layer: env.access_layer.clone(),
1846 listener: WorkerListener::default(),
1847 engine_config: Arc::new(MitoConfig::default()),
1848 row_group_size: None,
1849 cache_manager: Arc::new(CacheManager::default()),
1850 manifest_ctx: env
1851 .mock_manifest_context(version_control.current().version.metadata.clone())
1852 .await,
1853 index_options: IndexOptions::default(),
1854 flush_semaphore: Arc::new(Semaphore::new(2)),
1855 is_staging: false,
1856 partition_expr: None,
1857 };
1858 task.push_sender(OptionOutputTx::from(output_tx));
1859 scheduler
1860 .schedule_flush(builder.region_id(), &version_control, task)
1861 .unwrap();
1862 assert!(scheduler.region_status.is_empty());
1863 let output = output_rx.await.unwrap().unwrap();
1864 assert_eq!(output, 0);
1865 assert!(scheduler.region_status.is_empty());
1866 }
1867
1868 #[tokio::test]
1869 async fn test_schedule_flush_failure_notifies_waiter() {
1870 let env = SchedulerEnv::new()
1871 .await
1872 .scheduler(Arc::new(FailingScheduler));
1873 let (tx, _rx) = mpsc::channel(4);
1874 let mut scheduler = env.mock_flush_scheduler();
1875 let mut builder = VersionControlBuilder::new();
1876 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
1877 let version_control = Arc::new(builder.build());
1878 let version_data = version_control.current();
1879 write_rows_to_version(&version_data.version, "host0", 0, 10);
1880 let manifest_ctx = env
1881 .mock_manifest_context(version_data.version.metadata.clone())
1882 .await;
1883 let (output_tx, output_rx) = oneshot::channel();
1884 let mut task = new_test_flush_task(
1885 &env,
1886 builder.region_id(),
1887 FlushReason::Manual,
1888 tx,
1889 manifest_ctx,
1890 );
1891 task.push_sender(OptionOutputTx::from(output_tx));
1892
1893 scheduler
1894 .schedule_flush(builder.region_id(), &version_control, task)
1895 .unwrap_err();
1896
1897 let err = output_rx
1898 .await
1899 .expect("waiter must receive explicit error")
1900 .unwrap_err();
1901 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1902 }
1903
1904 #[tokio::test]
1905 async fn test_send_worker_request_failure_notifies_flush_finished_waiter() {
1906 let env = SchedulerEnv::new().await;
1907 let (tx, rx) = mpsc::channel(1);
1908 drop(rx);
1909 let builder = VersionControlBuilder::new();
1910 let version_control = Arc::new(builder.build());
1911 let manifest_ctx = env
1912 .mock_manifest_context(version_control.current().version.metadata.clone())
1913 .await;
1914 let task = new_test_flush_task(
1915 &env,
1916 builder.region_id(),
1917 FlushReason::Manual,
1918 tx,
1919 manifest_ctx,
1920 );
1921 let (output_tx, output_rx) = oneshot::channel();
1922 let request = WorkerRequest::Background {
1923 region_id: builder.region_id(),
1924 notify: BackgroundNotify::FlushFinished(FlushFinished {
1925 region_id: builder.region_id(),
1926 flush_reason: FlushReason::Manual,
1927 flushed_entry_id: 0,
1928 senders: vec![OutputTx::new(output_tx)],
1929 _timer: FLUSH_ELAPSED.with_label_values(&["total"]).start_timer(),
1930 edit: RegionEdit {
1931 files_to_add: Vec::new(),
1932 files_to_remove: Vec::new(),
1933 timestamp_ms: None,
1934 compaction_time_window: None,
1935 flushed_entry_id: None,
1936 flushed_sequence: None,
1937 committed_sequence: None,
1938 },
1939 memtables_to_remove: smallvec![],
1940 is_staging: false,
1941 }),
1942 };
1943
1944 task.send_worker_request(request).await;
1945
1946 let output = output_rx.await.expect("waiter must receive explicit error");
1947 assert!(output.is_err());
1948 }
1949
1950 #[tokio::test]
1951 async fn test_flush_waiters_drop_notifies_waiter() {
1952 let region_id = RegionId::new(1, 1);
1953 let (output_tx, output_rx) = oneshot::channel();
1954 let waiters = FlushTaskWaiters::new(region_id, vec![OutputTx::new(output_tx)]);
1955
1956 drop(waiters);
1957
1958 let err = output_rx
1959 .await
1960 .expect("waiter must receive explicit error")
1961 .unwrap_err();
1962 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1963 }
1964
1965 #[tokio::test]
1966 async fn test_flush_failure_notifies_pending_bulk_writes() {
1967 let region_id = RegionId::new(1, 1);
1968 let version_control = Arc::new(VersionControlBuilder::new().build());
1969 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
1970 let status = FlushStatus {
1971 region_id,
1972 version_control,
1973 state: CancellableTaskState::new(),
1974 pending_task: None,
1975 closing: false,
1976 pending_ddls: Vec::new(),
1977 pending_writes: Vec::new(),
1978 pending_bulk_writes: vec![bulk_req],
1979 };
1980
1981 let pending_ddls = status.on_failure(Arc::new(RegionClosedSnafu { region_id }.build()));
1982 assert!(pending_ddls.is_empty());
1983
1984 let err = output_rx
1985 .await
1986 .expect("pending bulk write must receive explicit error")
1987 .unwrap_err();
1988 assert_eq!(err.status_code(), StatusCode::Cancelled);
1989 }
1990
1991 #[tokio::test]
1992 async fn test_flush_failure_retains_lifecycle_ddls_and_fails_pending_writes() {
1993 let region_id = RegionId::new(1, 1);
1994 let version_control = Arc::new(VersionControlBuilder::new().build());
1995 let (write_req, write_rx) = new_test_write_request(region_id);
1996 let (bulk_req, bulk_rx) = new_test_bulk_request(region_id);
1997 let (truncate_tx, mut truncate_rx) = oneshot::channel();
1998 let (drop_tx, mut drop_rx) = oneshot::channel();
1999 let status = FlushStatus {
2000 region_id,
2001 version_control,
2002 state: CancellableTaskState::new(),
2003 pending_task: None,
2004 closing: false,
2005 pending_ddls: vec![
2006 SenderDdlRequest {
2007 region_id,
2008 sender: OptionOutputTx::from(truncate_tx),
2009 request: DdlRequest::Truncate(
2010 store_api::region_request::RegionTruncateRequest::All,
2011 ),
2012 },
2013 SenderDdlRequest {
2014 region_id,
2015 sender: OptionOutputTx::from(drop_tx),
2016 request: DdlRequest::Drop(store_api::region_request::RegionDropRequest {
2017 fast_path: false,
2018 force: false,
2019 partial_drop: false,
2020 }),
2021 },
2022 ],
2023 pending_writes: vec![write_req],
2024 pending_bulk_writes: vec![bulk_req],
2025 };
2026
2027 let ddls = status.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
2028 assert_eq!(2, ddls.len());
2029 assert!(matches!(ddls[0].request, DdlRequest::Truncate(_)));
2030 assert!(matches!(ddls[1].request, DdlRequest::Drop(_)));
2031
2032 let write_err = write_rx
2033 .await
2034 .expect("pending write must receive explicit error")
2035 .unwrap_err();
2036 assert_eq!(write_err.status_code(), StatusCode::RegionBusy);
2037 let bulk_err = bulk_rx
2038 .await
2039 .expect("pending bulk write must receive explicit error")
2040 .unwrap_err();
2041 assert_eq!(bulk_err.status_code(), StatusCode::RegionBusy);
2042
2043 assert!(truncate_rx.try_recv().is_err());
2044 assert!(drop_rx.try_recv().is_err());
2045 for ddl in ddls {
2046 ddl.sender.send(Ok(0));
2047 }
2048 assert_eq!(0, truncate_rx.await.unwrap().unwrap());
2049 assert_eq!(0, drop_rx.await.unwrap().unwrap());
2050 }
2051
2052 #[tokio::test]
2053 async fn test_uncommitted_ssts_cleanup_finalized_file() {
2054 let env = SchedulerEnv::new().await;
2055 let region_id = RegionId::new(1, 1);
2056 let file_id = store_api::storage::FileId::random();
2057 let path = crate::sst::location::sst_file_path(
2058 env.access_layer.table_dir(),
2059 crate::sst::file::RegionFileId::new(region_id, file_id),
2060 env.access_layer.path_type(),
2061 );
2062 env.access_layer
2063 .object_store()
2064 .write(&path, Bytes::from_static(b"sst"))
2065 .await
2066 .unwrap();
2067 assert!(env.access_layer.object_store().exists(&path).await.unwrap());
2068
2069 let uncommitted = UncommittedSsts::new(region_id, env.access_layer.clone(), None);
2070 uncommitted.track(&[SstInfo {
2071 file_id,
2072 ..Default::default()
2073 }]);
2074 assert_eq!(1, uncommitted.num_tracked_files());
2075 uncommitted.cleanup_for_test().await.unwrap();
2076 assert_eq!(0, uncommitted.num_tracked_files());
2077
2078 let entries = env
2081 .access_layer
2082 .object_store()
2083 .list(&env.access_layer.build_region_dir(region_id))
2084 .await
2085 .unwrap();
2086 assert!(entries.iter().all(|entry| entry.path() != path));
2087 }
2088
2089 #[tokio::test]
2090 async fn test_region_closed_notifies_pending_bulk_writes() {
2091 let region_id = RegionId::new(1, 1);
2092 let version_control = Arc::new(VersionControlBuilder::new().build());
2093 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
2094 let status = FlushStatus {
2095 region_id,
2096 version_control,
2097 state: CancellableTaskState::new(),
2098 pending_task: None,
2099 closing: false,
2100 pending_ddls: Vec::new(),
2101 pending_writes: Vec::new(),
2102 pending_bulk_writes: vec![bulk_req],
2103 };
2104
2105 status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
2106
2107 let err = output_rx
2108 .await
2109 .expect("pending bulk write must receive explicit error")
2110 .unwrap_err();
2111 assert_eq!(err.status_code(), StatusCode::Cancelled);
2112 }
2113
2114 #[tokio::test]
2115 async fn test_schedule_pending_request() {
2116 let job_scheduler = Arc::new(VecScheduler::default());
2117 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2118 let (tx, _rx) = mpsc::channel(4);
2119 let mut scheduler = env.mock_flush_scheduler();
2120 let mut builder = VersionControlBuilder::new();
2121 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2123 let version_control = Arc::new(builder.build());
2124 let version_data = version_control.current();
2126 write_rows_to_version(&version_data.version, "host0", 0, 10);
2127 let manifest_ctx = env
2128 .mock_manifest_context(version_data.version.metadata.clone())
2129 .await;
2130 let mut tasks: Vec<_> = (0..3)
2132 .map(|_| RegionFlushTask {
2133 region_id: builder.region_id(),
2134 reason: FlushReason::Manual,
2135 senders: Vec::new(),
2136 request_sender: tx.clone(),
2137 access_layer: env.access_layer.clone(),
2138 listener: WorkerListener::default(),
2139 engine_config: Arc::new(MitoConfig::default()),
2140 row_group_size: None,
2141 cache_manager: Arc::new(CacheManager::default()),
2142 manifest_ctx: manifest_ctx.clone(),
2143 index_options: IndexOptions::default(),
2144 flush_semaphore: Arc::new(Semaphore::new(2)),
2145 is_staging: false,
2146 partition_expr: None,
2147 })
2148 .collect();
2149 let task = tasks.pop().unwrap();
2151 scheduler
2152 .schedule_flush(builder.region_id(), &version_control, task)
2153 .unwrap();
2154 assert_eq!(1, scheduler.region_status.len());
2156 assert_eq!(1, job_scheduler.num_jobs());
2157 let version_data = version_control.current();
2159 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2160 let output_rxs: Vec<_> = tasks
2162 .into_iter()
2163 .map(|mut task| {
2164 let (output_tx, output_rx) = oneshot::channel();
2165 task.push_sender(OptionOutputTx::from(output_tx));
2166 scheduler
2167 .schedule_flush(builder.region_id(), &version_control, task)
2168 .unwrap();
2169 output_rx
2170 })
2171 .collect();
2172 version_control.apply_edit(
2174 Some(RegionEdit {
2175 files_to_add: Vec::new(),
2176 files_to_remove: Vec::new(),
2177 timestamp_ms: None,
2178 compaction_time_window: None,
2179 flushed_entry_id: None,
2180 flushed_sequence: None,
2181 committed_sequence: None,
2182 }),
2183 &[0],
2184 builder.file_purger(),
2185 );
2186 scheduler.on_flush_success(builder.region_id());
2187 assert_eq!(1, job_scheduler.num_jobs());
2189 assert!(scheduler.region_status.is_empty());
2191 for output_rx in output_rxs {
2192 let output = output_rx.await.unwrap().unwrap();
2193 assert_eq!(output, 0);
2194 }
2195 }
2196
2197 #[test]
2199 fn test_memtable_flat_sources_single_range_append_mode_behavior() {
2200 let metadata = metadata_for_test();
2202 let schema = to_flat_sst_arrow_schema(
2203 &metadata,
2204 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2205 );
2206
2207 let capacity = 16;
2210 let pk_codec = build_primary_key_codec(&metadata);
2211 let mut converter =
2212 BulkPartConverter::new(&metadata, schema.clone(), capacity, pk_codec, true);
2213 let kvs = build_key_values_with_ts_seq_values(
2214 &metadata,
2215 "dup_key".to_string(),
2216 1,
2217 vec![1000i64, 1000i64].into_iter(),
2218 vec![Some(1.0f64), Some(2.0f64)].into_iter(),
2219 1,
2220 );
2221 converter.append_key_values(&kvs).unwrap();
2222 let part = converter.convert().unwrap();
2223
2224 let build_ranges = |append_mode: bool| -> MemtableRanges {
2227 let memtable = crate::memtable::bulk::BulkMemtable::new(
2228 1,
2229 crate::memtable::bulk::BulkMemtableConfig::default(),
2230 metadata.clone(),
2231 None,
2232 None,
2233 append_mode,
2234 MergeMode::LastRow,
2235 );
2236 memtable.write_bulk(part.clone()).unwrap();
2237 memtable.ranges(None, RangesOptions::for_flush()).unwrap()
2238 };
2239
2240 {
2242 let mem_ranges = build_ranges(false);
2243 assert_eq!(1, mem_ranges.ranges.len());
2244
2245 let options = RegionOptions {
2246 append_mode: false,
2247 merge_mode: Some(MergeMode::LastRow),
2248 ..Default::default()
2249 };
2250
2251 let flat_sources = memtable_flat_sources(
2252 schema.clone(),
2253 mem_ranges,
2254 &metadata,
2255 &options,
2256 metadata.primary_key.len(),
2257 )
2258 .unwrap();
2259 assert!(flat_sources.encoded.is_empty());
2260 assert_eq!(1, flat_sources.sources.len());
2261
2262 let mut total_rows = 0usize;
2264 for (source, _sequence) in flat_sources.sources {
2265 total_rows += source
2266 .take_iter()
2267 .map(|x| x.unwrap().num_rows())
2268 .sum::<usize>();
2269 }
2270 assert_eq!(1, total_rows, "dedup should keep a single row");
2271 }
2272
2273 {
2275 let mem_ranges = build_ranges(true);
2276 assert_eq!(1, mem_ranges.ranges.len());
2277
2278 let options = RegionOptions {
2279 append_mode: true,
2280 ..Default::default()
2281 };
2282
2283 let flat_sources = memtable_flat_sources(
2284 schema,
2285 mem_ranges,
2286 &metadata,
2287 &options,
2288 metadata.primary_key.len(),
2289 )
2290 .unwrap();
2291 assert!(flat_sources.encoded.is_empty());
2292 assert_eq!(1, flat_sources.sources.len());
2293
2294 let mut total_rows = 0usize;
2295 for (source, _sequence) in flat_sources.sources {
2296 total_rows += source
2297 .take_iter()
2298 .map(|x| x.unwrap().num_rows())
2299 .sum::<usize>();
2300 }
2301 assert_eq!(2, total_rows, "append_mode should preserve duplicates");
2302 }
2303 }
2304
2305 #[test]
2306 fn test_memtable_flat_sources_uses_non_encoded_schema() -> Result<()> {
2307 struct TestIterBuilder {
2308 schema: SchemaRef,
2309 batch: Option<RecordBatch>,
2310 }
2311
2312 impl IterBuilder for TestIterBuilder {
2313 fn build(
2314 &self,
2315 _metrics: Option<crate::memtable::MemScanMetrics>,
2316 ) -> Result<crate::memtable::BoxedBatchIterator> {
2317 unimplemented!()
2318 }
2319
2320 fn is_record_batch(&self) -> bool {
2321 true
2322 }
2323
2324 fn build_record_batch(
2325 &self,
2326 _time_range: Option<(common_time::Timestamp, common_time::Timestamp)>,
2327 _metrics: Option<crate::memtable::MemScanMetrics>,
2328 ) -> Result<BoxedRecordBatchIterator> {
2329 let Some(batch) = self.batch.clone() else {
2330 unimplemented!()
2331 };
2332 Ok(Box::new(std::iter::once(Ok(batch))))
2333 }
2334
2335 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
2336 Some(self.schema.clone())
2337 }
2338
2339 fn encoded_range(&self) -> Option<EncodedRange> {
2340 self.batch.is_none().then(|| EncodedRange {
2341 data: Bytes::new(),
2342 sst_info: SstInfo::default(),
2343 })
2344 }
2345 }
2346
2347 let metadata = metadata_for_test();
2348 let schema = to_flat_sst_arrow_schema(
2349 &metadata,
2350 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2351 );
2352 let pk_codec = build_primary_key_codec(&metadata);
2353 let mut converter = BulkPartConverter::new(&metadata, schema.clone(), 1, pk_codec, true);
2354 let kvs = build_key_values_with_ts_seq_values(
2355 &metadata,
2356 "key".to_string(),
2357 1,
2358 std::iter::once(1000),
2359 std::iter::once(Some(1.0)),
2360 1,
2361 );
2362 converter.append_key_values(&kvs)?;
2363 let batch = converter.convert()?.batch;
2364 let encoded_schema = Arc::new(Schema::empty());
2365
2366 let new_range = |id, builder| {
2367 MemtableRange::new(
2368 Arc::new(MemtableRangeContext::new(
2369 id,
2370 Box::new(builder),
2371 Default::default(),
2372 )),
2373 MemtableStats {
2374 num_rows: 1,
2375 ..Default::default()
2376 },
2377 )
2378 };
2379 let mut ranges = std::collections::BTreeMap::new();
2380 ranges.insert(
2381 0,
2382 new_range(
2383 0,
2384 TestIterBuilder {
2385 schema: encoded_schema.clone(),
2386 batch: None,
2387 },
2388 ),
2389 );
2390 ranges.insert(
2391 1,
2392 new_range(
2393 0,
2394 TestIterBuilder {
2395 schema: schema.clone(),
2396 batch: Some(batch),
2397 },
2398 ),
2399 );
2400
2401 let sources = memtable_flat_sources(
2402 encoded_schema,
2403 MemtableRanges { ranges },
2404 &metadata,
2405 &RegionOptions {
2406 append_mode: true,
2407 ..Default::default()
2408 },
2409 metadata.primary_key.len(),
2410 )?;
2411 assert_eq!(1, sources.encoded.len());
2412 assert_eq!(1, sources.sources.len());
2413 assert_eq!(&schema, sources.sources[0].0.schema());
2414 Ok(())
2415 }
2416
2417 #[tokio::test]
2418 async fn test_schedule_pending_request_on_flush_success() {
2419 common_telemetry::init_default_ut_logging();
2420 let job_scheduler = Arc::new(VecScheduler::default());
2421 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2422 let (tx, _rx) = mpsc::channel(4);
2423 let mut scheduler = env.mock_flush_scheduler();
2424 let mut builder = VersionControlBuilder::new();
2425 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2427 let version_control = Arc::new(builder.build());
2428 let version_data = version_control.current();
2430 write_rows_to_version(&version_data.version, "host0", 0, 10);
2431 let manifest_ctx = env
2432 .mock_manifest_context(version_data.version.metadata.clone())
2433 .await;
2434 let mut tasks: Vec<_> = (0..2)
2436 .map(|_| RegionFlushTask {
2437 region_id: builder.region_id(),
2438 reason: FlushReason::Manual,
2439 senders: Vec::new(),
2440 request_sender: tx.clone(),
2441 access_layer: env.access_layer.clone(),
2442 listener: WorkerListener::default(),
2443 engine_config: Arc::new(MitoConfig::default()),
2444 row_group_size: None,
2445 cache_manager: Arc::new(CacheManager::default()),
2446 manifest_ctx: manifest_ctx.clone(),
2447 index_options: IndexOptions::default(),
2448 flush_semaphore: Arc::new(Semaphore::new(2)),
2449 is_staging: false,
2450 partition_expr: None,
2451 })
2452 .collect();
2453 let task = tasks.pop().unwrap();
2455 scheduler
2456 .schedule_flush(builder.region_id(), &version_control, task)
2457 .unwrap();
2458 assert_eq!(1, scheduler.region_status.len());
2460 assert_eq!(1, job_scheduler.num_jobs());
2461 let task = tasks.pop().unwrap();
2463 scheduler
2464 .schedule_flush(builder.region_id(), &version_control, task)
2465 .unwrap();
2466 assert!(
2467 scheduler
2468 .region_status
2469 .get(&builder.region_id())
2470 .unwrap()
2471 .pending_task
2472 .is_some()
2473 );
2474
2475 let version_data = version_control.current();
2477 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2478 version_control.apply_edit(
2480 Some(RegionEdit {
2481 files_to_add: Vec::new(),
2482 files_to_remove: Vec::new(),
2483 timestamp_ms: None,
2484 compaction_time_window: None,
2485 flushed_entry_id: None,
2486 flushed_sequence: None,
2487 committed_sequence: None,
2488 }),
2489 &[0],
2490 builder.file_purger(),
2491 );
2492 write_rows_to_version(&version_data.version, "host1", 0, 10);
2493 scheduler.on_flush_success(builder.region_id());
2494 assert_eq!(2, job_scheduler.num_jobs());
2495 assert!(
2497 scheduler
2498 .region_status
2499 .get(&builder.region_id())
2500 .unwrap()
2501 .pending_task
2502 .is_none()
2503 );
2504 }
2505
2506 #[tokio::test]
2507 async fn test_schedule_pending_request_failure_drains_pending_ddls() {
2508 common_telemetry::init_default_ut_logging();
2509 let job_scheduler = Arc::new(VecScheduler::default());
2510 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2511 let (tx, _rx) = mpsc::channel(4);
2512 let mut scheduler = env.mock_flush_scheduler();
2513 let mut builder = VersionControlBuilder::new();
2514 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2515 let version_control = Arc::new(builder.build());
2516
2517 let version_data = version_control.current();
2518 write_rows_to_version(&version_data.version, "host0", 0, 10);
2519 let manifest_ctx = env
2520 .mock_manifest_context(version_data.version.metadata.clone())
2521 .await;
2522
2523 let task = new_test_flush_task(
2524 &env,
2525 builder.region_id(),
2526 FlushReason::Manual,
2527 tx.clone(),
2528 manifest_ctx.clone(),
2529 );
2530 scheduler
2531 .schedule_flush(builder.region_id(), &version_control, task)
2532 .unwrap();
2533
2534 let task = new_test_flush_task(
2535 &env,
2536 builder.region_id(),
2537 FlushReason::Closing,
2538 tx,
2539 manifest_ctx,
2540 );
2541 scheduler
2542 .schedule_flush(builder.region_id(), &version_control, task)
2543 .unwrap();
2544
2545 let (sender, receiver) = oneshot::channel();
2546 scheduler.add_ddl_request_to_pending(SenderDdlRequest {
2547 sender: OptionOutputTx::from(sender),
2548 region_id: builder.region_id(),
2549 request: DdlRequest::Close(store_api::region_request::RegionCloseRequest::default()),
2550 });
2551
2552 let version_data = version_control.current();
2553 version_control.apply_edit(
2554 Some(RegionEdit {
2555 files_to_add: Vec::new(),
2556 files_to_remove: Vec::new(),
2557 timestamp_ms: None,
2558 compaction_time_window: None,
2559 flushed_entry_id: None,
2560 flushed_sequence: None,
2561 committed_sequence: None,
2562 }),
2563 &[0],
2564 builder.file_purger(),
2565 );
2566 write_rows_to_version(&version_data.version, "host1", 0, 10);
2567
2568 scheduler.scheduler = Arc::new(FailingScheduler);
2569 scheduler.on_flush_success(builder.region_id());
2570
2571 assert!(scheduler.region_status.is_empty());
2572 let err = receiver
2573 .await
2574 .expect("pending DDL must be notified")
2575 .unwrap_err();
2576 assert_eq!(err.status_code(), StatusCode::RegionBusy);
2577 }
2578}