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, 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 ..Default::default()
455 };
456 if let Some(row_group_size) = self.row_group_size {
457 write_opts.row_group_size = row_group_size;
458 }
459
460 let DoFlushMemtablesResult {
461 file_metas,
462 flushed_bytes,
463 series_count,
464 encoded_part_count,
465 flush_metrics,
466 sst_infos,
467 } = self
468 .do_flush_memtables(version, write_opts, state, uncommitted)
469 .await?;
470
471 if !file_metas.is_empty() {
472 FLUSH_BYTES_TOTAL.inc_by(flushed_bytes);
473 }
474
475 let mut file_ids = Vec::with_capacity(file_metas.len());
476 let mut total_rows = 0;
477 let mut total_bytes = 0;
478 for meta in &file_metas {
479 file_ids.push(meta.file_id);
480 total_rows += meta.num_rows;
481 total_bytes += meta.file_size;
482 }
483 info!(
484 "Successfully flush memtables, region: {}, reason: {}, files: {:?}, series count: {}, total_rows: {}, total_bytes: {}, cost: {:?}, encoded_part_count: {}, metrics: {:?}",
485 self.region_id,
486 self.reason.as_str(),
487 file_ids,
488 series_count,
489 total_rows,
490 total_bytes,
491 timer.stop_and_record(),
492 encoded_part_count,
493 flush_metrics,
494 );
495 flush_metrics.observe();
496
497 let hook = self.manifest_ctx.hook();
498 if let Some(hook) = &hook {
499 let files: Vec<SstFileInfo<'_>> = sst_infos
500 .iter()
501 .zip(file_metas.iter())
502 .map(|(sst_info, file_meta)| SstFileInfo {
503 sst_info_ref: sst_info,
504 file_meta,
505 })
506 .collect();
507 hook.on_sst_files_written(self.region_id, &version.metadata, &files)
508 .await;
509 }
510
511 let edit = RegionEdit {
512 files_to_add: file_metas,
513 files_to_remove: Vec::new(),
514 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
515 compaction_time_window: None,
516 flushed_entry_id: Some(version_data.last_entry_id),
518 flushed_sequence: Some(version_data.committed_sequence),
519 committed_sequence: None,
520 };
521 info!(
522 "Applying {edit:?} to region {}, is_staging: {}",
523 self.region_id, self.is_staging
524 );
525
526 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit.clone()));
527
528 if !state.mark_commit_started() {
530 return FlushCancelledSnafu.fail();
531 }
532 self.listener.on_flush_commit_begin(self.region_id).await;
533
534 let expected_state = if matches!(self.reason, FlushReason::Downgrading) {
535 RegionLeaderState::Downgrading
536 } else {
537 let current_state = self.manifest_ctx.current_state();
539 if current_state == RegionRoleState::Leader(RegionLeaderState::Staging) {
540 RegionLeaderState::Staging
541 } else {
542 RegionLeaderState::Writable
543 }
544 };
545 let manifest_version = match self
546 .manifest_ctx
547 .update_manifest(expected_state, action_list, self.is_staging)
548 .await
549 {
550 Ok(manifest_version) => {
551 uncommitted.disarm_cleanup();
552 manifest_version
553 }
554 Err(e) => {
555 if e.may_have_persisted_manifest_update() {
556 uncommitted.disarm_cleanup();
557 } else {
558 info!(
559 "Cleaning uncommitted SSTs because the manifest update was not persisted, region: {}, job: flush, error: {:?}",
560 self.region_id, e
561 );
562 uncommitted.cleanup().await;
563 }
564 return Err(e);
565 }
566 };
567 info!(
568 "Successfully update manifest version to {manifest_version}, region: {}, is_staging: {}, reason: {}",
569 self.region_id,
570 self.is_staging,
571 self.reason.as_str()
572 );
573
574 Ok(edit)
575 }
576
577 async fn do_flush_memtables(
578 &self,
579 version: &VersionRef,
580 write_opts: WriteOptions,
581 state: &CancellableTaskState,
582 uncommitted: &UncommittedSsts,
583 ) -> Result<DoFlushMemtablesResult> {
584 let memtables = version.memtables.immutables();
585 let mut file_metas = Vec::with_capacity(memtables.len());
586 let mut flushed_bytes = 0;
587 let mut series_count = 0;
588 let mut encoded_part_count = 0;
589 let mut flush_metrics = Metrics::new(WriteType::Flush);
590 let partition_expr = parse_partition_expr(self.partition_expr.as_deref())?;
591 let hook = self.manifest_ctx.hook();
592 let mut all_sst_infos = Vec::new();
593 for mem in memtables {
594 if mem.is_empty() {
595 continue;
597 }
598
599 let compact_start = std::time::Instant::now();
601 if let Err(e) = mem.compact(true) {
602 common_telemetry::error!(e; "Failed to compact memtable before flush");
603 }
604 let compact_cost = compact_start.elapsed();
605 flush_metrics.compact_memtable += compact_cost;
606
607 let mem_stats = mem.stats();
608 let batch_size = crate::batch_size::estimate_batch_size([(
609 mem_stats.num_rows() as u64,
610 mem_stats.bytes_allocated() as u64,
611 )]);
612 let mem_ranges =
614 mem.ranges(None, RangesOptions::for_flush().with_batch_size(batch_size))?;
615 let num_mem_ranges = mem_ranges.ranges.len();
616
617 let num_mem_rows = mem_ranges.num_rows();
619 let memtable_series_count = mem_ranges.series_count();
620 let memtable_id = mem.id();
621 series_count += memtable_series_count;
624
625 let flush_start = Instant::now();
626 let FlushFlatMemResult {
627 num_encoded,
628 num_sources,
629 results,
630 } = self
631 .flush_flat_mem_ranges(version, &write_opts, mem_ranges, state, uncommitted)
632 .await?;
633 encoded_part_count += num_encoded;
634 for (source_idx, result) in results.into_iter().enumerate() {
635 let (max_sequence, ssts_written, metrics) = result?;
636 if ssts_written.is_empty() {
637 continue;
639 }
640
641 common_telemetry::debug!(
642 "Region {} flush one memtable {} {}/{}, metrics: {:?}",
643 self.region_id,
644 memtable_id,
645 source_idx,
646 num_sources,
647 metrics
648 );
649
650 flush_metrics = flush_metrics.merge(metrics);
651
652 for sst_info in &ssts_written {
653 flushed_bytes += sst_info.file_size;
654 let pk_range = sst_info
655 .file_metadata
656 .as_ref()
657 .and_then(|meta| extract_primary_key_range(meta, &version.metadata));
658 file_metas.push(Self::new_file_meta(
659 self.region_id,
660 max_sequence,
661 sst_info,
662 partition_expr.clone(),
663 pk_range,
664 version.options.preserve_row_sequence,
665 ));
666 }
667 if hook.is_some() {
668 all_sst_infos.extend(ssts_written);
669 }
670 }
671
672 common_telemetry::debug!(
673 "Region {} flush {} memtables for {}, num_mem_ranges: {}, num_encoded: {}, num_rows: {}, flush_cost: {:?}, compact_cost: {:?}",
674 self.region_id,
675 num_sources,
676 memtable_id,
677 num_mem_ranges,
678 num_encoded,
679 num_mem_rows,
680 flush_start.elapsed(),
681 compact_cost,
682 );
683 }
684
685 Ok(DoFlushMemtablesResult {
686 file_metas,
687 flushed_bytes,
688 series_count,
689 encoded_part_count,
690 flush_metrics,
691 sst_infos: all_sst_infos,
692 })
693 }
694
695 async fn flush_flat_mem_ranges(
696 &self,
697 version: &VersionRef,
698 write_opts: &WriteOptions,
699 mem_ranges: MemtableRanges,
700 state: &CancellableTaskState,
701 uncommitted: &UncommittedSsts,
702 ) -> Result<FlushFlatMemResult> {
703 let batch_schema = to_flat_sst_arrow_schema(
704 &version.metadata,
705 &FlatSchemaOptions::from_encoding(version.metadata.primary_key_encoding),
706 );
707 let field_column_start =
708 flat_format::field_column_start(&version.metadata, batch_schema.fields().len());
709 let flat_sources = memtable_flat_sources(
710 batch_schema,
711 mem_ranges,
712 &version.metadata,
713 &version.options,
714 field_column_start,
715 )?;
716 let mut tasks = Vec::with_capacity(flat_sources.encoded.len() + flat_sources.sources.len());
717 let num_encoded = flat_sources.encoded.len();
718 for (source, max_sequence) in flat_sources.sources {
719 let write_request = self.new_write_request(version, max_sequence, source);
720 let access_layer = self.access_layer.clone();
721 let write_opts = write_opts.clone();
722 let semaphore = self.flush_semaphore.clone();
723 let uncommitted = uncommitted.clone();
724 let task = common_runtime::spawn_global(async move {
725 let _permit = semaphore.acquire().await.unwrap();
726 let mut metrics = Metrics::new(WriteType::Flush);
727 let ssts = access_layer
728 .write_sst(write_request, &write_opts, &mut metrics)
729 .await?;
730 uncommitted.track(&ssts);
731 FLUSH_FILE_TOTAL.inc_by(ssts.len() as u64);
732 Ok((max_sequence, ssts, metrics))
733 });
734 tasks.push(task);
735 }
736 for (encoded, max_sequence) in flat_sources.encoded {
737 let access_layer = self.access_layer.clone();
738 let cache_manager = self.cache_manager.clone();
739 let region_id = version.metadata.region_id;
740 let semaphore = self.flush_semaphore.clone();
741 let uncommitted = uncommitted.clone();
742 let task = common_runtime::spawn_global(async move {
743 let _permit = semaphore.acquire().await.unwrap();
744 let metrics = access_layer
745 .put_sst(&encoded.data, region_id, &encoded.sst_info, &cache_manager)
746 .await?;
747 uncommitted.track(std::slice::from_ref(&encoded.sst_info));
748 FLUSH_FILE_TOTAL.inc();
749 Ok((max_sequence, smallvec![encoded.sst_info], metrics))
750 });
751 tasks.push(task);
752 }
753 let num_sources = tasks.len();
754 let abort_handles = tasks
755 .iter()
756 .map(|task| task.abort_handle())
757 .collect::<Vec<_>>();
758 let join_all = futures::future::join_all(tasks);
759 tokio::pin!(join_all);
760 let results = match CancellableFuture::new(join_all.as_mut(), state.cancel_handle()).await {
761 Ok(results) => results
762 .into_iter()
763 .map(|result| result.context(JoinSnafu))
764 .collect::<Result<Vec<_>>>()?,
765 Err(_) => {
766 for handle in abort_handles {
767 handle.abort();
768 }
769 let _ = join_all.await;
772 return FlushCancelledSnafu.fail();
773 }
774 };
775 Ok(FlushFlatMemResult {
776 num_encoded,
777 num_sources,
778 results,
779 })
780 }
781
782 fn new_file_meta(
783 region_id: RegionId,
784 max_sequence: u64,
785 sst_info: &SstInfo,
786 partition_expr: Option<PartitionExpr>,
787 primary_key_range: Option<(Bytes, Bytes)>,
788 preserve_row_sequence: bool,
789 ) -> FileMeta {
790 let (primary_key_min, primary_key_max) = match primary_key_range {
791 Some((min, max)) => (Some(min), Some(max)),
792 None => (None, None),
793 };
794 FileMeta {
795 region_id,
796 file_id: sst_info.file_id,
797 time_range: sst_info.time_range,
798 level: 0,
799 file_size: sst_info.file_size,
800 max_row_group_uncompressed_size: sst_info.max_row_group_uncompressed_size,
801 available_indexes: sst_info.index_metadata.build_available_indexes(),
802 indexes: sst_info.index_metadata.build_indexes(),
803 index_file_size: sst_info.index_metadata.file_size,
804 index_version: 0,
805 num_rows: sst_info.num_rows as u64,
806 num_row_groups: sst_info.num_row_groups,
807 sequence: NonZeroU64::new(max_sequence),
808 partition_expr,
809 num_series: sst_info.num_series,
810 primary_key_min,
811 primary_key_max,
812 preserve_row_sequence,
813 }
814 }
815
816 fn new_write_request(
817 &self,
818 version: &VersionRef,
819 max_sequence: u64,
820 source: FlatSource,
821 ) -> SstWriteRequest {
822 let flat_format = version
823 .options
824 .sst_format
825 .map(|f| f == FormatType::Flat)
826 .unwrap_or(self.engine_config.default_flat_format);
827 SstWriteRequest {
828 op_type: OperationType::Flush,
829 metadata: version.metadata.clone(),
830 source,
831 cache_manager: self.cache_manager.clone(),
832 storage: version.options.storage.clone(),
833 max_sequence: Some(max_sequence),
834 sst_write_format: if flat_format {
835 FormatType::Flat
836 } else {
837 FormatType::PrimaryKey
838 },
839 preserve_row_sequence: version.options.preserve_row_sequence,
840 index_options: self.index_options.clone(),
841 index_config: self.engine_config.index.clone(),
842 inverted_index_config: self.engine_config.inverted_index.clone(),
843 fulltext_index_config: self.engine_config.fulltext_index.clone(),
844 bloom_filter_index_config: self.engine_config.bloom_filter_index.clone(),
845 #[cfg(feature = "vector_index")]
846 vector_index_config: self.engine_config.vector_index.clone(),
847 }
848 }
849
850 pub(crate) async fn send_worker_request(&self, request: WorkerRequest) {
852 if let Err(e) = self
853 .request_sender
854 .send(WorkerRequestWithTime::new(request))
855 .await
856 {
857 let request = e.0.request;
858 error!(
859 "Failed to notify flush job status for region {}, request: {:?}",
860 self.region_id, request
861 );
862 if let WorkerRequest::Background {
863 notify: BackgroundNotify::FlushFinished(mut finished),
864 ..
865 } = request
866 {
867 finished.on_failure(
868 RegionClosedSnafu {
869 region_id: self.region_id,
870 }
871 .build(),
872 );
873 }
874 }
875 }
876
877 fn merge(&mut self, mut other: RegionFlushTask) {
879 assert_eq!(self.region_id, other.region_id);
880 self.senders.append(&mut other.senders);
882 }
883}
884
885struct FlushFlatMemResult {
886 num_encoded: usize,
887 num_sources: usize,
888 results: Vec<Result<(SequenceNumber, SstInfoArray, Metrics)>>,
889}
890
891struct DoFlushMemtablesResult {
892 file_metas: Vec<FileMeta>,
893 flushed_bytes: u64,
894 series_count: usize,
895 encoded_part_count: usize,
896 flush_metrics: Metrics,
897 sst_infos: Vec<SstInfo>,
898}
899
900struct FlatSources {
901 sources: SmallVec<[(FlatSource, SequenceNumber); 4]>,
902 encoded: SmallVec<[(EncodedRange, SequenceNumber); 4]>,
903}
904
905fn memtable_flat_sources(
907 schema: SchemaRef,
908 mem_ranges: MemtableRanges,
909 metadata: &RegionMetadataRef,
910 options: &RegionOptions,
911 field_column_start: usize,
912) -> Result<FlatSources> {
913 let MemtableRanges { ranges } = mem_ranges;
914 let mut flat_sources = FlatSources {
915 sources: SmallVec::new(),
916 encoded: SmallVec::new(),
917 };
918
919 if ranges.len() == 1 {
920 debug!("Flushing single flat range");
921
922 let only_range = ranges.into_values().next().unwrap();
923 let max_sequence = only_range.stats().max_sequence();
924 if let Some(encoded) = only_range.encoded() {
925 flat_sources.encoded.push((encoded, max_sequence));
926 } else {
927 let schema = only_range.record_batch_schema_hint().unwrap_or(schema);
928 let iter = only_range.build_record_batch_iter(None, None)?;
929 let iter = maybe_dedup_one(
932 options.append_mode,
933 options.merge_mode(),
934 field_column_start,
935 iter,
936 );
937 flat_sources
938 .sources
939 .push((FlatSource::new_iter(schema, iter), max_sequence));
940 };
941 } else {
942 let min_flush_rows = *ENCODE_ROW_THRESHOLD;
943 let total_rows: usize = ranges
945 .values()
946 .filter(|r| r.encoded().is_none())
947 .map(|r| r.num_rows())
948 .sum();
949 debug!(
950 "Flushing multiple flat ranges, total_rows: {}, min_flush_rows: {}, num_ranges: {}",
951 total_rows,
952 min_flush_rows,
953 ranges.len()
954 );
955 let mut rows_remaining = total_rows;
956 let mut last_iter_rows = 0;
957 let num_ranges = ranges.len();
958 let mut input_iters = Vec::with_capacity(num_ranges);
959 let mut current_ranges = Vec::new();
960
961 let schemas = ranges
962 .values()
963 .filter(|range| range.encoded().is_none())
964 .map(|range| {
965 (
966 range
967 .record_batch_schema_hint()
968 .unwrap_or_else(|| schema.clone()),
969 range.num_rows() as u64,
970 )
971 })
972 .collect::<Vec<_>>();
973 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
974 let schema = rewrite_json2_schema(
975 schemas.first().map(|(schema, _)| schema).unwrap_or(&schema),
976 &plans,
977 );
978
979 for (_range_id, range) in ranges {
980 if let Some(encoded) = range.encoded() {
981 let max_sequence = range.stats().max_sequence();
982 flat_sources.encoded.push((encoded, max_sequence));
983 continue;
984 }
985
986 if let Some(actual) = range.record_batch_schema_hint() {
987 let actual = rewrite_json2_schema(&actual, &plans);
988 ensure!(
989 actual == schema,
990 UnexpectedSnafu {
991 reason: format!(
992 "Different schemas found in a MemtableRanges, expected: {}, actual: {}",
993 schema, actual,
994 ),
995 }
996 )
997 }
998
999 let iter = range.build_record_batch_iter(None, None)?;
1000 let iter: BoxedRecordBatchIterator = if plans.is_empty() {
1001 iter
1002 } else {
1003 let plans = plans.clone();
1004 Box::new(iter.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1005 };
1006 input_iters.push(iter);
1007 let range_rows = range.num_rows();
1008 last_iter_rows += range_rows;
1009 rows_remaining -= range_rows;
1010 current_ranges.push(range);
1011
1012 if last_iter_rows >= min_flush_rows
1015 && (rows_remaining == 0 || rows_remaining >= DEFAULT_ROW_GROUP_SIZE)
1016 {
1017 debug!(
1018 "Flush batch ready, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1019 last_iter_rows,
1020 min_flush_rows,
1021 input_iters.len(),
1022 rows_remaining
1023 );
1024
1025 let max_sequence = current_ranges
1027 .iter()
1028 .map(|r| r.stats().max_sequence())
1029 .max()
1030 .unwrap_or(0);
1031 let batch_size =
1032 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1033 let stats = range.stats();
1034 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1035 }));
1036
1037 let input_iters =
1038 std::mem::replace(&mut input_iters, Vec::with_capacity(num_ranges));
1039
1040 let maybe_dedup = merge_and_dedup_with_batch_size(
1041 &schema,
1042 options.append_mode,
1043 options.merge_mode(),
1044 field_column_start,
1045 input_iters,
1046 batch_size,
1047 )?;
1048
1049 flat_sources.sources.push((
1050 FlatSource::new_iter(schema.clone(), maybe_dedup),
1051 max_sequence,
1052 ));
1053 last_iter_rows = 0;
1054 current_ranges.clear();
1055 }
1056 }
1057
1058 if !input_iters.is_empty() {
1060 debug!(
1061 "Flush remaining batch, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1062 last_iter_rows,
1063 min_flush_rows,
1064 input_iters.len(),
1065 rows_remaining
1066 );
1067
1068 let max_sequence = current_ranges
1069 .iter()
1070 .map(|r| r.stats().max_sequence())
1071 .max()
1072 .unwrap_or(0);
1073 let batch_size =
1074 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1075 let stats = range.stats();
1076 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1077 }));
1078
1079 let maybe_dedup = merge_and_dedup_with_batch_size(
1080 &schema,
1081 options.append_mode,
1082 options.merge_mode(),
1083 field_column_start,
1084 input_iters,
1085 batch_size,
1086 )?;
1087
1088 flat_sources
1089 .sources
1090 .push((FlatSource::new_iter(schema, maybe_dedup), max_sequence));
1091 }
1092 }
1093
1094 Ok(flat_sources)
1095}
1096
1097pub fn merge_and_dedup(
1142 schema: &SchemaRef,
1143 append_mode: bool,
1144 merge_mode: MergeMode,
1145 field_column_start: usize,
1146 input_iters: Vec<BoxedRecordBatchIterator>,
1147) -> Result<BoxedRecordBatchIterator> {
1148 merge_and_dedup_with_batch_size(
1149 schema,
1150 append_mode,
1151 merge_mode,
1152 field_column_start,
1153 input_iters,
1154 DEFAULT_READ_BATCH_SIZE,
1155 )
1156}
1157
1158pub fn merge_and_dedup_with_batch_size(
1164 schema: &SchemaRef,
1165 append_mode: bool,
1166 merge_mode: MergeMode,
1167 field_column_start: usize,
1168 input_iters: Vec<BoxedRecordBatchIterator>,
1169 batch_size: usize,
1170) -> Result<BoxedRecordBatchIterator> {
1171 let batch_size = batch_size.max(1);
1172 let merge_iter = FlatMergeIterator::new(schema.clone(), input_iters, batch_size)?;
1173 let maybe_dedup = if append_mode {
1174 Box::new(merge_iter) as _
1176 } else {
1177 match merge_mode {
1179 MergeMode::LastRow => {
1180 Box::new(FlatDedupIterator::new(merge_iter, FlatLastRow::new(false))) as _
1181 }
1182 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1183 merge_iter,
1184 FlatLastNonNull::new(field_column_start, false),
1185 )) as _,
1186 }
1187 };
1188 Ok(maybe_dedup)
1189}
1190
1191pub fn maybe_dedup_one(
1192 append_mode: bool,
1193 merge_mode: MergeMode,
1194 field_column_start: usize,
1195 input_iter: BoxedRecordBatchIterator,
1196) -> BoxedRecordBatchIterator {
1197 if append_mode {
1198 input_iter
1200 } else {
1201 match merge_mode {
1203 MergeMode::LastRow => {
1204 Box::new(FlatDedupIterator::new(input_iter, FlatLastRow::new(false)))
1205 }
1206 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1207 input_iter,
1208 FlatLastNonNull::new(field_column_start, false),
1209 )),
1210 }
1211 }
1212}
1213
1214pub(crate) struct FlushScheduler {
1216 region_status: HashMap<RegionId, FlushStatus>,
1218 scheduler: SchedulerRef,
1220}
1221
1222impl FlushScheduler {
1223 pub(crate) fn new(scheduler: SchedulerRef) -> FlushScheduler {
1225 FlushScheduler {
1226 region_status: HashMap::new(),
1227 scheduler,
1228 }
1229 }
1230
1231 pub(crate) fn is_flush_requested(&self, region_id: RegionId) -> bool {
1233 self.region_status.contains_key(®ion_id)
1234 }
1235
1236 fn schedule_flush_task(
1237 &mut self,
1238 version_control: &VersionControlRef,
1239 mut task: RegionFlushTask,
1240 ) -> Result<CancellableTaskState> {
1241 let region_id = task.region_id;
1242
1243 if let Err(e) = version_control.freeze_mutable() {
1245 error!(e; "Failed to freeze the mutable memtable for region {}", region_id);
1246 task.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1247
1248 return Err(e);
1249 }
1250 let state = CancellableTaskState::new();
1252 let (job, waiters) = task.into_flush_job(version_control, state.clone());
1253 if let Err(e) = self.scheduler.schedule(job) {
1254 error!(e; "Failed to schedule flush job for region {}", region_id);
1255 waiters.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1256
1257 return Err(e);
1258 }
1259 Ok(state)
1260 }
1261
1262 pub(crate) fn schedule_flush(
1264 &mut self,
1265 region_id: RegionId,
1266 version_control: &VersionControlRef,
1267 mut task: RegionFlushTask,
1268 ) -> Result<()> {
1269 debug_assert_eq!(region_id, task.region_id);
1270
1271 let version = version_control.current().version;
1272 if version.memtables.is_empty() {
1273 debug_assert!(!self.region_status.contains_key(®ion_id));
1274 task.on_success();
1276 return Ok(());
1277 }
1278
1279 FLUSH_REQUESTS_TOTAL
1281 .with_label_values(&[task.reason.as_str()])
1282 .inc();
1283
1284 if let Some(flush_status) = self.region_status.get_mut(®ion_id) {
1286 if flush_status.has_pending_lifecycle_ddl() {
1287 task.on_failure(Arc::new(FlushCancelledSnafu.build()));
1288 return Ok(());
1289 }
1290 debug!("Merging flush task for region {}", region_id);
1292 flush_status.merge_task(task);
1293 return Ok(());
1294 }
1295
1296 let closing = task.reason == FlushReason::Closing;
1297 let state = self.schedule_flush_task(version_control, task)?;
1298
1299 let _ = self.region_status.insert(
1301 region_id,
1302 FlushStatus::new(region_id, version_control.clone(), state, closing),
1303 );
1304
1305 Ok(())
1306 }
1307
1308 pub(crate) fn on_flush_success(
1312 &mut self,
1313 region_id: RegionId,
1314 ) -> Option<(
1315 Vec<SenderDdlRequest>,
1316 Vec<SenderWriteRequest>,
1317 Vec<SenderBulkRequest>,
1318 )> {
1319 let flush_status = self.region_status.get_mut(®ion_id)?;
1320 if flush_status.pending_task.is_none() {
1322 debug!(
1325 "Region {} doesn't have any pending flush task, removing it from the status",
1326 region_id
1327 );
1328 let flush_status = self.region_status.remove(®ion_id).unwrap();
1329 return Some((
1330 flush_status.pending_ddls,
1331 flush_status.pending_writes,
1332 flush_status.pending_bulk_writes,
1333 ));
1334 }
1335
1336 let version_data = flush_status.version_control.current();
1338 if version_data.version.memtables.is_empty() {
1339 let task = flush_status.pending_task.take().unwrap();
1342 task.on_success();
1344 debug!(
1345 "Region {} has nothing to flush, removing it from the status",
1346 region_id
1347 );
1348 let flush_status = self.region_status.remove(®ion_id).unwrap();
1350 return Some((
1351 flush_status.pending_ddls,
1352 flush_status.pending_writes,
1353 flush_status.pending_bulk_writes,
1354 ));
1355 }
1356
1357 debug!("Scheduling pending flush task for region {}", region_id);
1359 let task = flush_status.pending_task.take().unwrap();
1361 let version_control = flush_status.version_control.clone();
1362 match self.schedule_flush_task(&version_control, task) {
1363 Ok(state) => {
1364 self.region_status.get_mut(®ion_id).unwrap().state = state;
1365 }
1366 Err(err) => {
1367 error!(
1368 err;
1369 "Flush succeeded for region {region_id}, but failed to schedule next flush for it."
1370 );
1371 let flush_status = self.region_status.remove(®ion_id).unwrap();
1372 flush_status.fail_all(Arc::new(RegionBusySnafu { region_id }.build()));
1373 return None;
1374 }
1375 }
1376 None
1378 }
1379
1380 pub(crate) fn on_flush_failed(
1385 &mut self,
1386 region_id: RegionId,
1387 err: Arc<Error>,
1388 ) -> Vec<SenderDdlRequest> {
1389 if matches!(err.as_ref(), Error::FlushCancelled { .. }) {
1390 info!("Region {} flush was cancelled", region_id);
1391 } else {
1392 error!(err; "Region {} failed to flush, cancel all pending tasks", region_id);
1393 FLUSH_FAILURE_TOTAL.inc();
1394 }
1395
1396 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1398 return Vec::new();
1399 };
1400
1401 flush_status.on_failure(err)
1402 }
1403
1404 pub(crate) fn try_cancel_and_add_ddl<T>(
1406 &mut self,
1407 region_id: RegionId,
1408 sender: OptionOutputTx,
1409 request: T,
1410 into_ddl_request: impl FnOnce(T) -> DdlRequest,
1411 ) -> std::result::Result<(), (OptionOutputTx, T)> {
1412 let Some(status) = self.region_status.get_mut(®ion_id) else {
1413 return Err((sender, request));
1414 };
1415
1416 let cancel_result = status.state.request_cancel();
1417 debug!(
1418 "Requested flush cancellation for region {}, result: {:?}",
1419 region_id, cancel_result
1420 );
1421 status.pending_ddls.push(SenderDdlRequest {
1422 region_id,
1423 sender,
1424 request: into_ddl_request(request),
1425 });
1426 Ok(())
1427 }
1428
1429 pub(crate) fn on_region_dropped(&mut self, region_id: RegionId) {
1431 self.remove_region_on_failure(
1432 region_id,
1433 Arc::new(RegionDroppedSnafu { region_id }.build()),
1434 );
1435 }
1436
1437 pub(crate) fn on_region_closed(&mut self, region_id: RegionId) {
1439 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1440 return;
1441 };
1442
1443 flush_status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
1444 }
1445
1446 pub(crate) fn on_region_truncated(&mut self, region_id: RegionId) {
1448 self.remove_region_on_failure(
1449 region_id,
1450 Arc::new(RegionTruncatedSnafu { region_id }.build()),
1451 );
1452 }
1453
1454 fn remove_region_on_failure(&mut self, region_id: RegionId, err: Arc<Error>) {
1455 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1457 return;
1458 };
1459
1460 flush_status.fail_all(err);
1462 }
1463
1464 pub(crate) fn add_ddl_request_to_pending(&mut self, request: SenderDdlRequest) {
1469 let status = self.region_status.get_mut(&request.region_id).unwrap();
1470 status.pending_ddls.push(request);
1471 }
1472
1473 pub(crate) fn add_write_request_to_pending(&mut self, request: SenderWriteRequest) {
1478 let status = self
1479 .region_status
1480 .get_mut(&request.request.region_id)
1481 .unwrap();
1482 status.pending_writes.push(request);
1483 }
1484
1485 pub(crate) fn add_bulk_request_to_pending(&mut self, request: SenderBulkRequest) {
1490 let status = self.region_status.get_mut(&request.region_id).unwrap();
1491 status.pending_bulk_writes.push(request);
1492 }
1493
1494 pub(crate) fn has_pending_ddls(&self, region_id: RegionId) -> bool {
1496 self.region_status
1497 .get(®ion_id)
1498 .map(|status| !status.pending_ddls.is_empty() || status.closing)
1499 .unwrap_or(false)
1500 }
1501}
1502
1503impl Drop for FlushScheduler {
1504 fn drop(&mut self) {
1505 for (region_id, flush_status) in self.region_status.drain() {
1506 flush_status.fail_all(Arc::new(RegionClosedSnafu { region_id }.build()));
1508 }
1509 }
1510}
1511
1512struct FlushStatus {
1516 region_id: RegionId,
1518 version_control: VersionControlRef,
1520 state: CancellableTaskState,
1522 pending_task: Option<RegionFlushTask>,
1524 closing: bool,
1526 pending_ddls: Vec<SenderDdlRequest>,
1528 pending_writes: Vec<SenderWriteRequest>,
1530 pending_bulk_writes: Vec<SenderBulkRequest>,
1532}
1533
1534impl FlushStatus {
1535 fn new(
1536 region_id: RegionId,
1537 version_control: VersionControlRef,
1538 state: CancellableTaskState,
1539 closing: bool,
1540 ) -> FlushStatus {
1541 FlushStatus {
1542 region_id,
1543 version_control,
1544 state,
1545 pending_task: None,
1546 closing,
1547 pending_ddls: Vec::new(),
1548 pending_writes: Vec::new(),
1549 pending_bulk_writes: Vec::new(),
1550 }
1551 }
1552
1553 fn merge_task(&mut self, task: RegionFlushTask) {
1555 self.closing |= task.reason == FlushReason::Closing;
1556 if let Some(pending) = &mut self.pending_task {
1557 pending.merge(task);
1558 } else {
1559 self.pending_task = Some(task);
1560 }
1561 }
1562
1563 fn has_pending_lifecycle_ddl(&self) -> bool {
1564 self.pending_ddls
1565 .iter()
1566 .any(|ddl| matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)))
1567 }
1568
1569 fn on_failure(self, err: Arc<Error>) -> Vec<SenderDdlRequest> {
1571 if let Some(mut task) = self.pending_task {
1572 task.on_failure(err.clone());
1573 }
1574 let mut lifecycle_ddls = Vec::new();
1575 for ddl in self.pending_ddls {
1576 if matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)) {
1577 lifecycle_ddls.push(ddl);
1578 } else {
1579 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1580 region_id: self.region_id,
1581 }));
1582 }
1583 }
1584 for write_req in self.pending_writes {
1585 write_req
1586 .sender
1587 .send(Err(err.clone()).context(FlushRegionSnafu {
1588 region_id: self.region_id,
1589 }));
1590 }
1591 for bulk_req in self.pending_bulk_writes {
1592 bulk_req
1593 .sender
1594 .send(Err(err.clone()).context(FlushRegionSnafu {
1595 region_id: self.region_id,
1596 }));
1597 }
1598 lifecycle_ddls
1599 }
1600
1601 fn fail_all(self, err: Arc<Error>) {
1602 let region_id = self.region_id;
1603 let ddls = self.on_failure(err.clone());
1604 for ddl in ddls {
1605 ddl.sender
1606 .send(Err(err.clone()).context(FlushRegionSnafu { region_id }));
1607 }
1608 }
1609
1610 fn on_region_closed(self, err: Arc<Error>) {
1611 if let Some(mut task) = self.pending_task {
1612 task.on_failure(err.clone());
1613 }
1614
1615 for ddl in self.pending_ddls {
1616 if matches!(ddl.request, DdlRequest::Close(_)) {
1617 ddl.sender.send(Ok(0));
1618 } else {
1619 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1620 region_id: self.region_id,
1621 }));
1622 }
1623 }
1624
1625 for write_req in self.pending_writes {
1626 write_req
1627 .sender
1628 .send(Err(err.clone()).context(FlushRegionSnafu {
1629 region_id: self.region_id,
1630 }));
1631 }
1632 for bulk_req in self.pending_bulk_writes {
1633 bulk_req
1634 .sender
1635 .send(Err(err.clone()).context(FlushRegionSnafu {
1636 region_id: self.region_id,
1637 }));
1638 }
1639 }
1640}
1641
1642#[cfg(test)]
1643mod tests {
1644 use api::v1::{OpType, Rows};
1645 use common_error::ext::ErrorExt;
1646 use common_error::status_code::StatusCode;
1647 use datatypes::arrow::datatypes::Schema;
1648 use datatypes::arrow::record_batch::RecordBatch;
1649 use mito_codec::row_converter::build_primary_key_codec;
1650 use tokio::sync::oneshot;
1651
1652 use super::*;
1653 use crate::cache::CacheManager;
1654 use crate::error::InvalidSchedulerStateSnafu;
1655 use crate::memtable::bulk::part::BulkPartConverter;
1656 use crate::memtable::time_series::TimeSeriesMemtableBuilder;
1657 use crate::memtable::{
1658 IterBuilder, Memtable, MemtableRange, MemtableRangeContext, MemtableStats, RangesOptions,
1659 };
1660 use crate::request::WriteRequest;
1661 use crate::schedule::scheduler::Scheduler;
1662 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1663 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1664 use crate::test_util::scheduler_util::{SchedulerEnv, VecScheduler};
1665 use crate::test_util::version_util::{VersionControlBuilder, write_rows_to_version};
1666
1667 struct FailingScheduler;
1668
1669 #[async_trait::async_trait]
1670 impl Scheduler for FailingScheduler {
1671 fn schedule(&self, _job: Job) -> Result<()> {
1672 InvalidSchedulerStateSnafu.fail()
1673 }
1674
1675 async fn stop(&self, _await_termination: bool) -> Result<()> {
1676 Ok(())
1677 }
1678 }
1679
1680 fn new_test_flush_task(
1681 env: &SchedulerEnv,
1682 region_id: RegionId,
1683 reason: FlushReason,
1684 request_sender: mpsc::Sender<WorkerRequestWithTime>,
1685 manifest_ctx: ManifestContextRef,
1686 ) -> RegionFlushTask {
1687 RegionFlushTask {
1688 region_id,
1689 reason,
1690 senders: Vec::new(),
1691 request_sender,
1692 access_layer: env.access_layer.clone(),
1693 listener: WorkerListener::default(),
1694 engine_config: Arc::new(MitoConfig::default()),
1695 row_group_size: None,
1696 cache_manager: Arc::new(CacheManager::default()),
1697 manifest_ctx,
1698 index_options: IndexOptions::default(),
1699 flush_semaphore: Arc::new(Semaphore::new(2)),
1700 is_staging: false,
1701 partition_expr: None,
1702 }
1703 }
1704
1705 fn new_test_bulk_request(
1706 region_id: RegionId,
1707 ) -> (
1708 SenderBulkRequest,
1709 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1710 ) {
1711 let metadata = metadata_for_test();
1712 let schema = to_flat_sst_arrow_schema(
1713 &metadata,
1714 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1715 );
1716 let pk_codec = build_primary_key_codec(&metadata);
1717 let mut converter = BulkPartConverter::new(&metadata, schema, 16, pk_codec, true);
1718 let kvs = build_key_values_with_ts_seq_values(
1719 &metadata,
1720 "bulk_key".to_string(),
1721 1,
1722 std::iter::once(1000i64),
1723 std::iter::once(Some(1.0f64)),
1724 1,
1725 );
1726 converter.append_key_values(&kvs).unwrap();
1727 let (sender, receiver) = oneshot::channel();
1728
1729 (
1730 SenderBulkRequest {
1731 sender: OptionOutputTx::from(sender),
1732 region_id,
1733 request: converter.convert().unwrap(),
1734 region_metadata: Some(metadata),
1735 partition_expr_version: None,
1736 },
1737 receiver,
1738 )
1739 }
1740
1741 fn new_test_write_request(
1742 region_id: RegionId,
1743 ) -> (
1744 SenderWriteRequest,
1745 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1746 ) {
1747 let (sender, receiver) = oneshot::channel();
1748 let request = WriteRequest::new(region_id, OpType::Put, Rows::default(), None).unwrap();
1749 (
1750 SenderWriteRequest {
1751 sender: OptionOutputTx::from(sender),
1752 request,
1753 },
1754 receiver,
1755 )
1756 }
1757
1758 #[test]
1759 fn test_get_mutable_limit() {
1760 assert_eq!(4, WriteBufferManagerImpl::get_mutable_limit(8));
1761 assert_eq!(5, WriteBufferManagerImpl::get_mutable_limit(10));
1762 assert_eq!(32, WriteBufferManagerImpl::get_mutable_limit(64));
1763 assert_eq!(0, WriteBufferManagerImpl::get_mutable_limit(0));
1764 }
1765
1766 #[test]
1767 fn test_over_mutable_limit() {
1768 let manager = WriteBufferManagerImpl::new(1000);
1770 manager.reserve_mem(400);
1771 assert!(!manager.should_flush_engine());
1772 assert!(!manager.should_stall());
1773
1774 manager.reserve_mem(400);
1776 assert!(manager.should_flush_engine());
1777
1778 manager.schedule_free_mem(400);
1780 assert!(!manager.should_flush_engine());
1781 assert_eq!(800, manager.memory_used.load(Ordering::Relaxed));
1782 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1783
1784 manager.free_mem(400);
1786 assert_eq!(400, manager.memory_used.load(Ordering::Relaxed));
1787 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1788 }
1789
1790 #[test]
1791 fn test_over_global() {
1792 let manager = WriteBufferManagerImpl::new(1000);
1794 manager.reserve_mem(1100);
1795 assert!(manager.should_stall());
1796 manager.schedule_free_mem(200);
1798 assert!(manager.should_flush_engine());
1799 assert!(manager.should_stall());
1800
1801 manager.schedule_free_mem(450);
1803 assert!(manager.should_flush_engine());
1804 assert!(manager.should_stall());
1805
1806 manager.reserve_mem(50);
1808 assert!(manager.should_flush_engine());
1809 manager.reserve_mem(100);
1810 assert!(manager.should_flush_engine());
1811 }
1812
1813 #[test]
1814 fn test_manager_notify() {
1815 let (sender, receiver) = watch::channel(());
1816 let manager = WriteBufferManagerImpl::new(1000).with_notifier(sender);
1817 manager.reserve_mem(500);
1818 assert!(!receiver.has_changed().unwrap());
1819 manager.schedule_free_mem(500);
1820 assert!(!receiver.has_changed().unwrap());
1821 manager.free_mem(500);
1822 assert!(receiver.has_changed().unwrap());
1823 }
1824
1825 #[tokio::test]
1826 async fn test_schedule_empty() {
1827 let env = SchedulerEnv::new().await;
1828 let (tx, _rx) = mpsc::channel(4);
1829 let mut scheduler = env.mock_flush_scheduler();
1830 let builder = VersionControlBuilder::new();
1831
1832 let version_control = Arc::new(builder.build());
1833 let (output_tx, output_rx) = oneshot::channel();
1834 let mut task = RegionFlushTask {
1835 region_id: builder.region_id(),
1836 reason: FlushReason::Manual,
1837 senders: Vec::new(),
1838 request_sender: tx,
1839 access_layer: env.access_layer.clone(),
1840 listener: WorkerListener::default(),
1841 engine_config: Arc::new(MitoConfig::default()),
1842 row_group_size: None,
1843 cache_manager: Arc::new(CacheManager::default()),
1844 manifest_ctx: env
1845 .mock_manifest_context(version_control.current().version.metadata.clone())
1846 .await,
1847 index_options: IndexOptions::default(),
1848 flush_semaphore: Arc::new(Semaphore::new(2)),
1849 is_staging: false,
1850 partition_expr: None,
1851 };
1852 task.push_sender(OptionOutputTx::from(output_tx));
1853 scheduler
1854 .schedule_flush(builder.region_id(), &version_control, task)
1855 .unwrap();
1856 assert!(scheduler.region_status.is_empty());
1857 let output = output_rx.await.unwrap().unwrap();
1858 assert_eq!(output, 0);
1859 assert!(scheduler.region_status.is_empty());
1860 }
1861
1862 #[tokio::test]
1863 async fn test_schedule_flush_failure_notifies_waiter() {
1864 let env = SchedulerEnv::new()
1865 .await
1866 .scheduler(Arc::new(FailingScheduler));
1867 let (tx, _rx) = mpsc::channel(4);
1868 let mut scheduler = env.mock_flush_scheduler();
1869 let mut builder = VersionControlBuilder::new();
1870 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
1871 let version_control = Arc::new(builder.build());
1872 let version_data = version_control.current();
1873 write_rows_to_version(&version_data.version, "host0", 0, 10);
1874 let manifest_ctx = env
1875 .mock_manifest_context(version_data.version.metadata.clone())
1876 .await;
1877 let (output_tx, output_rx) = oneshot::channel();
1878 let mut task = new_test_flush_task(
1879 &env,
1880 builder.region_id(),
1881 FlushReason::Manual,
1882 tx,
1883 manifest_ctx,
1884 );
1885 task.push_sender(OptionOutputTx::from(output_tx));
1886
1887 scheduler
1888 .schedule_flush(builder.region_id(), &version_control, task)
1889 .unwrap_err();
1890
1891 let err = output_rx
1892 .await
1893 .expect("waiter must receive explicit error")
1894 .unwrap_err();
1895 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1896 }
1897
1898 #[tokio::test]
1899 async fn test_send_worker_request_failure_notifies_flush_finished_waiter() {
1900 let env = SchedulerEnv::new().await;
1901 let (tx, rx) = mpsc::channel(1);
1902 drop(rx);
1903 let builder = VersionControlBuilder::new();
1904 let version_control = Arc::new(builder.build());
1905 let manifest_ctx = env
1906 .mock_manifest_context(version_control.current().version.metadata.clone())
1907 .await;
1908 let task = new_test_flush_task(
1909 &env,
1910 builder.region_id(),
1911 FlushReason::Manual,
1912 tx,
1913 manifest_ctx,
1914 );
1915 let (output_tx, output_rx) = oneshot::channel();
1916 let request = WorkerRequest::Background {
1917 region_id: builder.region_id(),
1918 notify: BackgroundNotify::FlushFinished(FlushFinished {
1919 region_id: builder.region_id(),
1920 flush_reason: FlushReason::Manual,
1921 flushed_entry_id: 0,
1922 senders: vec![OutputTx::new(output_tx)],
1923 _timer: FLUSH_ELAPSED.with_label_values(&["total"]).start_timer(),
1924 edit: RegionEdit {
1925 files_to_add: Vec::new(),
1926 files_to_remove: Vec::new(),
1927 timestamp_ms: None,
1928 compaction_time_window: None,
1929 flushed_entry_id: None,
1930 flushed_sequence: None,
1931 committed_sequence: None,
1932 },
1933 memtables_to_remove: smallvec![],
1934 is_staging: false,
1935 }),
1936 };
1937
1938 task.send_worker_request(request).await;
1939
1940 let output = output_rx.await.expect("waiter must receive explicit error");
1941 assert!(output.is_err());
1942 }
1943
1944 #[tokio::test]
1945 async fn test_flush_waiters_drop_notifies_waiter() {
1946 let region_id = RegionId::new(1, 1);
1947 let (output_tx, output_rx) = oneshot::channel();
1948 let waiters = FlushTaskWaiters::new(region_id, vec![OutputTx::new(output_tx)]);
1949
1950 drop(waiters);
1951
1952 let err = output_rx
1953 .await
1954 .expect("waiter must receive explicit error")
1955 .unwrap_err();
1956 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1957 }
1958
1959 #[tokio::test]
1960 async fn test_flush_failure_notifies_pending_bulk_writes() {
1961 let region_id = RegionId::new(1, 1);
1962 let version_control = Arc::new(VersionControlBuilder::new().build());
1963 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
1964 let status = FlushStatus {
1965 region_id,
1966 version_control,
1967 state: CancellableTaskState::new(),
1968 pending_task: None,
1969 closing: false,
1970 pending_ddls: Vec::new(),
1971 pending_writes: Vec::new(),
1972 pending_bulk_writes: vec![bulk_req],
1973 };
1974
1975 let pending_ddls = status.on_failure(Arc::new(RegionClosedSnafu { region_id }.build()));
1976 assert!(pending_ddls.is_empty());
1977
1978 let err = output_rx
1979 .await
1980 .expect("pending bulk write must receive explicit error")
1981 .unwrap_err();
1982 assert_eq!(err.status_code(), StatusCode::Cancelled);
1983 }
1984
1985 #[tokio::test]
1986 async fn test_flush_failure_retains_lifecycle_ddls_and_fails_pending_writes() {
1987 let region_id = RegionId::new(1, 1);
1988 let version_control = Arc::new(VersionControlBuilder::new().build());
1989 let (write_req, write_rx) = new_test_write_request(region_id);
1990 let (bulk_req, bulk_rx) = new_test_bulk_request(region_id);
1991 let (truncate_tx, mut truncate_rx) = oneshot::channel();
1992 let (drop_tx, mut drop_rx) = oneshot::channel();
1993 let status = FlushStatus {
1994 region_id,
1995 version_control,
1996 state: CancellableTaskState::new(),
1997 pending_task: None,
1998 closing: false,
1999 pending_ddls: vec![
2000 SenderDdlRequest {
2001 region_id,
2002 sender: OptionOutputTx::from(truncate_tx),
2003 request: DdlRequest::Truncate(
2004 store_api::region_request::RegionTruncateRequest::All,
2005 ),
2006 },
2007 SenderDdlRequest {
2008 region_id,
2009 sender: OptionOutputTx::from(drop_tx),
2010 request: DdlRequest::Drop(store_api::region_request::RegionDropRequest {
2011 fast_path: false,
2012 force: false,
2013 partial_drop: false,
2014 }),
2015 },
2016 ],
2017 pending_writes: vec![write_req],
2018 pending_bulk_writes: vec![bulk_req],
2019 };
2020
2021 let ddls = status.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
2022 assert_eq!(2, ddls.len());
2023 assert!(matches!(ddls[0].request, DdlRequest::Truncate(_)));
2024 assert!(matches!(ddls[1].request, DdlRequest::Drop(_)));
2025
2026 let write_err = write_rx
2027 .await
2028 .expect("pending write must receive explicit error")
2029 .unwrap_err();
2030 assert_eq!(write_err.status_code(), StatusCode::RegionBusy);
2031 let bulk_err = bulk_rx
2032 .await
2033 .expect("pending bulk write must receive explicit error")
2034 .unwrap_err();
2035 assert_eq!(bulk_err.status_code(), StatusCode::RegionBusy);
2036
2037 assert!(truncate_rx.try_recv().is_err());
2038 assert!(drop_rx.try_recv().is_err());
2039 for ddl in ddls {
2040 ddl.sender.send(Ok(0));
2041 }
2042 assert_eq!(0, truncate_rx.await.unwrap().unwrap());
2043 assert_eq!(0, drop_rx.await.unwrap().unwrap());
2044 }
2045
2046 #[tokio::test]
2047 async fn test_uncommitted_ssts_cleanup_finalized_file() {
2048 let env = SchedulerEnv::new().await;
2049 let region_id = RegionId::new(1, 1);
2050 let file_id = store_api::storage::FileId::random();
2051 let path = crate::sst::location::sst_file_path(
2052 env.access_layer.table_dir(),
2053 crate::sst::file::RegionFileId::new(region_id, file_id),
2054 env.access_layer.path_type(),
2055 );
2056 env.access_layer
2057 .object_store()
2058 .write(&path, Bytes::from_static(b"sst"))
2059 .await
2060 .unwrap();
2061 assert!(env.access_layer.object_store().exists(&path).await.unwrap());
2062
2063 let uncommitted = UncommittedSsts::new(region_id, env.access_layer.clone(), None);
2064 uncommitted.track(&[SstInfo {
2065 file_id,
2066 ..Default::default()
2067 }]);
2068 assert_eq!(1, uncommitted.num_tracked_files());
2069 uncommitted.cleanup_for_test().await.unwrap();
2070 assert_eq!(0, uncommitted.num_tracked_files());
2071
2072 let entries = env
2075 .access_layer
2076 .object_store()
2077 .list(&env.access_layer.build_region_dir(region_id))
2078 .await
2079 .unwrap();
2080 assert!(entries.iter().all(|entry| entry.path() != path));
2081 }
2082
2083 #[tokio::test]
2084 async fn test_region_closed_notifies_pending_bulk_writes() {
2085 let region_id = RegionId::new(1, 1);
2086 let version_control = Arc::new(VersionControlBuilder::new().build());
2087 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
2088 let status = FlushStatus {
2089 region_id,
2090 version_control,
2091 state: CancellableTaskState::new(),
2092 pending_task: None,
2093 closing: false,
2094 pending_ddls: Vec::new(),
2095 pending_writes: Vec::new(),
2096 pending_bulk_writes: vec![bulk_req],
2097 };
2098
2099 status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
2100
2101 let err = output_rx
2102 .await
2103 .expect("pending bulk write must receive explicit error")
2104 .unwrap_err();
2105 assert_eq!(err.status_code(), StatusCode::Cancelled);
2106 }
2107
2108 #[tokio::test]
2109 async fn test_schedule_pending_request() {
2110 let job_scheduler = Arc::new(VecScheduler::default());
2111 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2112 let (tx, _rx) = mpsc::channel(4);
2113 let mut scheduler = env.mock_flush_scheduler();
2114 let mut builder = VersionControlBuilder::new();
2115 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2117 let version_control = Arc::new(builder.build());
2118 let version_data = version_control.current();
2120 write_rows_to_version(&version_data.version, "host0", 0, 10);
2121 let manifest_ctx = env
2122 .mock_manifest_context(version_data.version.metadata.clone())
2123 .await;
2124 let mut tasks: Vec<_> = (0..3)
2126 .map(|_| RegionFlushTask {
2127 region_id: builder.region_id(),
2128 reason: FlushReason::Manual,
2129 senders: Vec::new(),
2130 request_sender: tx.clone(),
2131 access_layer: env.access_layer.clone(),
2132 listener: WorkerListener::default(),
2133 engine_config: Arc::new(MitoConfig::default()),
2134 row_group_size: None,
2135 cache_manager: Arc::new(CacheManager::default()),
2136 manifest_ctx: manifest_ctx.clone(),
2137 index_options: IndexOptions::default(),
2138 flush_semaphore: Arc::new(Semaphore::new(2)),
2139 is_staging: false,
2140 partition_expr: None,
2141 })
2142 .collect();
2143 let task = tasks.pop().unwrap();
2145 scheduler
2146 .schedule_flush(builder.region_id(), &version_control, task)
2147 .unwrap();
2148 assert_eq!(1, scheduler.region_status.len());
2150 assert_eq!(1, job_scheduler.num_jobs());
2151 let version_data = version_control.current();
2153 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2154 let output_rxs: Vec<_> = tasks
2156 .into_iter()
2157 .map(|mut task| {
2158 let (output_tx, output_rx) = oneshot::channel();
2159 task.push_sender(OptionOutputTx::from(output_tx));
2160 scheduler
2161 .schedule_flush(builder.region_id(), &version_control, task)
2162 .unwrap();
2163 output_rx
2164 })
2165 .collect();
2166 version_control.apply_edit(
2168 Some(RegionEdit {
2169 files_to_add: Vec::new(),
2170 files_to_remove: Vec::new(),
2171 timestamp_ms: None,
2172 compaction_time_window: None,
2173 flushed_entry_id: None,
2174 flushed_sequence: None,
2175 committed_sequence: None,
2176 }),
2177 &[0],
2178 builder.file_purger(),
2179 );
2180 scheduler.on_flush_success(builder.region_id());
2181 assert_eq!(1, job_scheduler.num_jobs());
2183 assert!(scheduler.region_status.is_empty());
2185 for output_rx in output_rxs {
2186 let output = output_rx.await.unwrap().unwrap();
2187 assert_eq!(output, 0);
2188 }
2189 }
2190
2191 #[test]
2193 fn test_memtable_flat_sources_single_range_append_mode_behavior() {
2194 let metadata = metadata_for_test();
2196 let schema = to_flat_sst_arrow_schema(
2197 &metadata,
2198 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2199 );
2200
2201 let capacity = 16;
2204 let pk_codec = build_primary_key_codec(&metadata);
2205 let mut converter =
2206 BulkPartConverter::new(&metadata, schema.clone(), capacity, pk_codec, true);
2207 let kvs = build_key_values_with_ts_seq_values(
2208 &metadata,
2209 "dup_key".to_string(),
2210 1,
2211 vec![1000i64, 1000i64].into_iter(),
2212 vec![Some(1.0f64), Some(2.0f64)].into_iter(),
2213 1,
2214 );
2215 converter.append_key_values(&kvs).unwrap();
2216 let part = converter.convert().unwrap();
2217
2218 let build_ranges = |append_mode: bool| -> MemtableRanges {
2221 let memtable = crate::memtable::bulk::BulkMemtable::new(
2222 1,
2223 crate::memtable::bulk::BulkMemtableConfig::default(),
2224 metadata.clone(),
2225 None,
2226 None,
2227 append_mode,
2228 MergeMode::LastRow,
2229 );
2230 memtable.write_bulk(part.clone()).unwrap();
2231 memtable.ranges(None, RangesOptions::for_flush()).unwrap()
2232 };
2233
2234 {
2236 let mem_ranges = build_ranges(false);
2237 assert_eq!(1, mem_ranges.ranges.len());
2238
2239 let options = RegionOptions {
2240 append_mode: false,
2241 merge_mode: Some(MergeMode::LastRow),
2242 ..Default::default()
2243 };
2244
2245 let flat_sources = memtable_flat_sources(
2246 schema.clone(),
2247 mem_ranges,
2248 &metadata,
2249 &options,
2250 metadata.primary_key.len(),
2251 )
2252 .unwrap();
2253 assert!(flat_sources.encoded.is_empty());
2254 assert_eq!(1, flat_sources.sources.len());
2255
2256 let mut total_rows = 0usize;
2258 for (source, _sequence) in flat_sources.sources {
2259 total_rows += source
2260 .take_iter()
2261 .map(|x| x.unwrap().num_rows())
2262 .sum::<usize>();
2263 }
2264 assert_eq!(1, total_rows, "dedup should keep a single row");
2265 }
2266
2267 {
2269 let mem_ranges = build_ranges(true);
2270 assert_eq!(1, mem_ranges.ranges.len());
2271
2272 let options = RegionOptions {
2273 append_mode: true,
2274 ..Default::default()
2275 };
2276
2277 let flat_sources = memtable_flat_sources(
2278 schema,
2279 mem_ranges,
2280 &metadata,
2281 &options,
2282 metadata.primary_key.len(),
2283 )
2284 .unwrap();
2285 assert!(flat_sources.encoded.is_empty());
2286 assert_eq!(1, flat_sources.sources.len());
2287
2288 let mut total_rows = 0usize;
2289 for (source, _sequence) in flat_sources.sources {
2290 total_rows += source
2291 .take_iter()
2292 .map(|x| x.unwrap().num_rows())
2293 .sum::<usize>();
2294 }
2295 assert_eq!(2, total_rows, "append_mode should preserve duplicates");
2296 }
2297 }
2298
2299 #[test]
2300 fn test_memtable_flat_sources_uses_non_encoded_schema() -> Result<()> {
2301 struct TestIterBuilder {
2302 schema: SchemaRef,
2303 batch: Option<RecordBatch>,
2304 }
2305
2306 impl IterBuilder for TestIterBuilder {
2307 fn build(
2308 &self,
2309 _metrics: Option<crate::memtable::MemScanMetrics>,
2310 ) -> Result<crate::memtable::BoxedBatchIterator> {
2311 unimplemented!()
2312 }
2313
2314 fn is_record_batch(&self) -> bool {
2315 true
2316 }
2317
2318 fn build_record_batch(
2319 &self,
2320 _time_range: Option<(common_time::Timestamp, common_time::Timestamp)>,
2321 _metrics: Option<crate::memtable::MemScanMetrics>,
2322 ) -> Result<BoxedRecordBatchIterator> {
2323 let Some(batch) = self.batch.clone() else {
2324 unimplemented!()
2325 };
2326 Ok(Box::new(std::iter::once(Ok(batch))))
2327 }
2328
2329 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
2330 Some(self.schema.clone())
2331 }
2332
2333 fn encoded_range(&self) -> Option<EncodedRange> {
2334 self.batch.is_none().then(|| EncodedRange {
2335 data: Bytes::new(),
2336 sst_info: SstInfo::default(),
2337 })
2338 }
2339 }
2340
2341 let metadata = metadata_for_test();
2342 let schema = to_flat_sst_arrow_schema(
2343 &metadata,
2344 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2345 );
2346 let pk_codec = build_primary_key_codec(&metadata);
2347 let mut converter = BulkPartConverter::new(&metadata, schema.clone(), 1, pk_codec, true);
2348 let kvs = build_key_values_with_ts_seq_values(
2349 &metadata,
2350 "key".to_string(),
2351 1,
2352 std::iter::once(1000),
2353 std::iter::once(Some(1.0)),
2354 1,
2355 );
2356 converter.append_key_values(&kvs)?;
2357 let batch = converter.convert()?.batch;
2358 let encoded_schema = Arc::new(Schema::empty());
2359
2360 let new_range = |id, builder| {
2361 MemtableRange::new(
2362 Arc::new(MemtableRangeContext::new(
2363 id,
2364 Box::new(builder),
2365 Default::default(),
2366 )),
2367 MemtableStats {
2368 num_rows: 1,
2369 ..Default::default()
2370 },
2371 )
2372 };
2373 let mut ranges = std::collections::BTreeMap::new();
2374 ranges.insert(
2375 0,
2376 new_range(
2377 0,
2378 TestIterBuilder {
2379 schema: encoded_schema.clone(),
2380 batch: None,
2381 },
2382 ),
2383 );
2384 ranges.insert(
2385 1,
2386 new_range(
2387 0,
2388 TestIterBuilder {
2389 schema: schema.clone(),
2390 batch: Some(batch),
2391 },
2392 ),
2393 );
2394
2395 let sources = memtable_flat_sources(
2396 encoded_schema,
2397 MemtableRanges { ranges },
2398 &metadata,
2399 &RegionOptions {
2400 append_mode: true,
2401 ..Default::default()
2402 },
2403 metadata.primary_key.len(),
2404 )?;
2405 assert_eq!(1, sources.encoded.len());
2406 assert_eq!(1, sources.sources.len());
2407 assert_eq!(&schema, sources.sources[0].0.schema());
2408 Ok(())
2409 }
2410
2411 #[tokio::test]
2412 async fn test_schedule_pending_request_on_flush_success() {
2413 common_telemetry::init_default_ut_logging();
2414 let job_scheduler = Arc::new(VecScheduler::default());
2415 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2416 let (tx, _rx) = mpsc::channel(4);
2417 let mut scheduler = env.mock_flush_scheduler();
2418 let mut builder = VersionControlBuilder::new();
2419 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2421 let version_control = Arc::new(builder.build());
2422 let version_data = version_control.current();
2424 write_rows_to_version(&version_data.version, "host0", 0, 10);
2425 let manifest_ctx = env
2426 .mock_manifest_context(version_data.version.metadata.clone())
2427 .await;
2428 let mut tasks: Vec<_> = (0..2)
2430 .map(|_| RegionFlushTask {
2431 region_id: builder.region_id(),
2432 reason: FlushReason::Manual,
2433 senders: Vec::new(),
2434 request_sender: tx.clone(),
2435 access_layer: env.access_layer.clone(),
2436 listener: WorkerListener::default(),
2437 engine_config: Arc::new(MitoConfig::default()),
2438 row_group_size: None,
2439 cache_manager: Arc::new(CacheManager::default()),
2440 manifest_ctx: manifest_ctx.clone(),
2441 index_options: IndexOptions::default(),
2442 flush_semaphore: Arc::new(Semaphore::new(2)),
2443 is_staging: false,
2444 partition_expr: None,
2445 })
2446 .collect();
2447 let task = tasks.pop().unwrap();
2449 scheduler
2450 .schedule_flush(builder.region_id(), &version_control, task)
2451 .unwrap();
2452 assert_eq!(1, scheduler.region_status.len());
2454 assert_eq!(1, job_scheduler.num_jobs());
2455 let task = tasks.pop().unwrap();
2457 scheduler
2458 .schedule_flush(builder.region_id(), &version_control, task)
2459 .unwrap();
2460 assert!(
2461 scheduler
2462 .region_status
2463 .get(&builder.region_id())
2464 .unwrap()
2465 .pending_task
2466 .is_some()
2467 );
2468
2469 let version_data = version_control.current();
2471 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2472 version_control.apply_edit(
2474 Some(RegionEdit {
2475 files_to_add: Vec::new(),
2476 files_to_remove: Vec::new(),
2477 timestamp_ms: None,
2478 compaction_time_window: None,
2479 flushed_entry_id: None,
2480 flushed_sequence: None,
2481 committed_sequence: None,
2482 }),
2483 &[0],
2484 builder.file_purger(),
2485 );
2486 write_rows_to_version(&version_data.version, "host1", 0, 10);
2487 scheduler.on_flush_success(builder.region_id());
2488 assert_eq!(2, job_scheduler.num_jobs());
2489 assert!(
2491 scheduler
2492 .region_status
2493 .get(&builder.region_id())
2494 .unwrap()
2495 .pending_task
2496 .is_none()
2497 );
2498 }
2499
2500 #[tokio::test]
2501 async fn test_schedule_pending_request_failure_drains_pending_ddls() {
2502 common_telemetry::init_default_ut_logging();
2503 let job_scheduler = Arc::new(VecScheduler::default());
2504 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2505 let (tx, _rx) = mpsc::channel(4);
2506 let mut scheduler = env.mock_flush_scheduler();
2507 let mut builder = VersionControlBuilder::new();
2508 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2509 let version_control = Arc::new(builder.build());
2510
2511 let version_data = version_control.current();
2512 write_rows_to_version(&version_data.version, "host0", 0, 10);
2513 let manifest_ctx = env
2514 .mock_manifest_context(version_data.version.metadata.clone())
2515 .await;
2516
2517 let task = new_test_flush_task(
2518 &env,
2519 builder.region_id(),
2520 FlushReason::Manual,
2521 tx.clone(),
2522 manifest_ctx.clone(),
2523 );
2524 scheduler
2525 .schedule_flush(builder.region_id(), &version_control, task)
2526 .unwrap();
2527
2528 let task = new_test_flush_task(
2529 &env,
2530 builder.region_id(),
2531 FlushReason::Closing,
2532 tx,
2533 manifest_ctx,
2534 );
2535 scheduler
2536 .schedule_flush(builder.region_id(), &version_control, task)
2537 .unwrap();
2538
2539 let (sender, receiver) = oneshot::channel();
2540 scheduler.add_ddl_request_to_pending(SenderDdlRequest {
2541 sender: OptionOutputTx::from(sender),
2542 region_id: builder.region_id(),
2543 request: DdlRequest::Close(store_api::region_request::RegionCloseRequest::default()),
2544 });
2545
2546 let version_data = version_control.current();
2547 version_control.apply_edit(
2548 Some(RegionEdit {
2549 files_to_add: Vec::new(),
2550 files_to_remove: Vec::new(),
2551 timestamp_ms: None,
2552 compaction_time_window: None,
2553 flushed_entry_id: None,
2554 flushed_sequence: None,
2555 committed_sequence: None,
2556 }),
2557 &[0],
2558 builder.file_purger(),
2559 );
2560 write_rows_to_version(&version_data.version, "host1", 0, 10);
2561
2562 scheduler.scheduler = Arc::new(FailingScheduler);
2563 scheduler.on_flush_success(builder.region_id());
2564
2565 assert!(scheduler.region_status.is_empty());
2566 let err = receiver
2567 .await
2568 .expect("pending DDL must be notified")
2569 .unwrap_err();
2570 assert_eq!(err.status_code(), StatusCode::RegionBusy);
2571 }
2572}