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 max_sequence: Some(max_sequence),
840 sst_write_format: if flat_format {
841 FormatType::Flat
842 } else {
843 FormatType::PrimaryKey
844 },
845 preserve_row_sequence: version.options.preserve_row_sequence,
846 index_options: self.index_options.clone(),
847 index_config: self.engine_config.index.clone(),
848 inverted_index_config: self.engine_config.inverted_index.clone(),
849 fulltext_index_config: self.engine_config.fulltext_index.clone(),
850 bloom_filter_index_config: self.engine_config.bloom_filter_index.clone(),
851 }
852 }
853
854 pub(crate) async fn send_worker_request(&self, request: WorkerRequest) {
856 if let Err(e) = self
857 .request_sender
858 .send(WorkerRequestWithTime::new(request))
859 .await
860 {
861 let request = e.0.request;
862 error!(
863 "Failed to notify flush job status for region {}, request: {:?}",
864 self.region_id, request
865 );
866 if let WorkerRequest::Background {
867 notify: BackgroundNotify::FlushFinished(mut finished),
868 ..
869 } = request
870 {
871 finished.on_failure(
872 RegionClosedSnafu {
873 region_id: self.region_id,
874 }
875 .build(),
876 );
877 }
878 }
879 }
880
881 fn merge(&mut self, mut other: RegionFlushTask) {
883 assert_eq!(self.region_id, other.region_id);
884 self.senders.append(&mut other.senders);
886 }
887}
888
889struct FlushFlatMemResult {
890 num_encoded: usize,
891 num_sources: usize,
892 results: Vec<Result<(SequenceNumber, SstInfoArray, Metrics)>>,
893}
894
895struct DoFlushMemtablesResult {
896 file_metas: Vec<FileMeta>,
897 flushed_bytes: u64,
898 series_count: usize,
899 encoded_part_count: usize,
900 flush_metrics: Metrics,
901 sst_infos: Vec<SstInfo>,
902}
903
904struct FlatSources {
905 sources: SmallVec<[(FlatSource, SequenceNumber); 4]>,
906 encoded: SmallVec<[(EncodedRange, SequenceNumber); 4]>,
907}
908
909fn memtable_flat_sources(
911 schema: SchemaRef,
912 mem_ranges: MemtableRanges,
913 metadata: &RegionMetadataRef,
914 options: &RegionOptions,
915 field_column_start: usize,
916) -> Result<FlatSources> {
917 let MemtableRanges { ranges } = mem_ranges;
918 let mut flat_sources = FlatSources {
919 sources: SmallVec::new(),
920 encoded: SmallVec::new(),
921 };
922
923 if ranges.len() == 1 {
924 debug!("Flushing single flat range");
925
926 let only_range = ranges.into_values().next().unwrap();
927 let max_sequence = only_range.stats().max_sequence();
928 if let Some(encoded) = only_range.encoded() {
929 flat_sources.encoded.push((encoded, max_sequence));
930 } else {
931 let schema = only_range.record_batch_schema_hint().unwrap_or(schema);
932 let iter = only_range.build_record_batch_iter(None, None)?;
933 let iter = maybe_dedup_one(
936 options.append_mode,
937 options.merge_mode(),
938 field_column_start,
939 iter,
940 );
941 flat_sources
942 .sources
943 .push((FlatSource::new_iter(schema, iter), max_sequence));
944 };
945 } else {
946 let min_flush_rows = *ENCODE_ROW_THRESHOLD;
947 let total_rows: usize = ranges
949 .values()
950 .filter(|r| r.encoded().is_none())
951 .map(|r| r.num_rows())
952 .sum();
953 debug!(
954 "Flushing multiple flat ranges, total_rows: {}, min_flush_rows: {}, num_ranges: {}",
955 total_rows,
956 min_flush_rows,
957 ranges.len()
958 );
959 let mut rows_remaining = total_rows;
960 let mut last_iter_rows = 0;
961 let num_ranges = ranges.len();
962 let mut input_iters = Vec::with_capacity(num_ranges);
963 let mut current_ranges = Vec::new();
964
965 let schemas = ranges
966 .values()
967 .filter(|range| range.encoded().is_none())
968 .map(|range| {
969 (
970 range
971 .record_batch_schema_hint()
972 .unwrap_or_else(|| schema.clone()),
973 range.num_rows() as u64,
974 )
975 })
976 .collect::<Vec<_>>();
977 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
978 let schema = rewrite_json2_schema(
979 schemas.first().map(|(schema, _)| schema).unwrap_or(&schema),
980 &plans,
981 );
982
983 for (_range_id, range) in ranges {
984 if let Some(encoded) = range.encoded() {
985 let max_sequence = range.stats().max_sequence();
986 flat_sources.encoded.push((encoded, max_sequence));
987 continue;
988 }
989
990 if let Some(actual) = range.record_batch_schema_hint() {
991 let actual = rewrite_json2_schema(&actual, &plans);
992 ensure!(
993 actual == schema,
994 UnexpectedSnafu {
995 reason: format!(
996 "Different schemas found in a MemtableRanges, expected: {}, actual: {}",
997 schema, actual,
998 ),
999 }
1000 )
1001 }
1002
1003 let iter = range.build_record_batch_iter(None, None)?;
1004 let iter: BoxedRecordBatchIterator = if plans.is_empty() {
1005 iter
1006 } else {
1007 let plans = plans.clone();
1008 Box::new(iter.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1009 };
1010 input_iters.push(iter);
1011 let range_rows = range.num_rows();
1012 last_iter_rows += range_rows;
1013 rows_remaining -= range_rows;
1014 current_ranges.push(range);
1015
1016 if last_iter_rows >= min_flush_rows
1019 && (rows_remaining == 0 || rows_remaining >= DEFAULT_ROW_GROUP_SIZE)
1020 {
1021 debug!(
1022 "Flush batch ready, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1023 last_iter_rows,
1024 min_flush_rows,
1025 input_iters.len(),
1026 rows_remaining
1027 );
1028
1029 let max_sequence = current_ranges
1031 .iter()
1032 .map(|r| r.stats().max_sequence())
1033 .max()
1034 .unwrap_or(0);
1035 let batch_size =
1036 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1037 let stats = range.stats();
1038 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1039 }));
1040
1041 let input_iters =
1042 std::mem::replace(&mut input_iters, Vec::with_capacity(num_ranges));
1043
1044 let maybe_dedup = merge_and_dedup_with_batch_size(
1045 &schema,
1046 options.append_mode,
1047 options.merge_mode(),
1048 field_column_start,
1049 input_iters,
1050 batch_size,
1051 )?;
1052
1053 flat_sources.sources.push((
1054 FlatSource::new_iter(schema.clone(), maybe_dedup),
1055 max_sequence,
1056 ));
1057 last_iter_rows = 0;
1058 current_ranges.clear();
1059 }
1060 }
1061
1062 if !input_iters.is_empty() {
1064 debug!(
1065 "Flush remaining batch, rows: {}, min_rows: {}, num_iters: {}, remaining: {}",
1066 last_iter_rows,
1067 min_flush_rows,
1068 input_iters.len(),
1069 rows_remaining
1070 );
1071
1072 let max_sequence = current_ranges
1073 .iter()
1074 .map(|r| r.stats().max_sequence())
1075 .max()
1076 .unwrap_or(0);
1077 let batch_size =
1078 crate::batch_size::estimate_batch_size(current_ranges.iter().map(|range| {
1079 let stats = range.stats();
1080 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1081 }));
1082
1083 let maybe_dedup = merge_and_dedup_with_batch_size(
1084 &schema,
1085 options.append_mode,
1086 options.merge_mode(),
1087 field_column_start,
1088 input_iters,
1089 batch_size,
1090 )?;
1091
1092 flat_sources
1093 .sources
1094 .push((FlatSource::new_iter(schema, maybe_dedup), max_sequence));
1095 }
1096 }
1097
1098 Ok(flat_sources)
1099}
1100
1101pub fn merge_and_dedup(
1146 schema: &SchemaRef,
1147 append_mode: bool,
1148 merge_mode: MergeMode,
1149 field_column_start: usize,
1150 input_iters: Vec<BoxedRecordBatchIterator>,
1151) -> Result<BoxedRecordBatchIterator> {
1152 merge_and_dedup_with_batch_size(
1153 schema,
1154 append_mode,
1155 merge_mode,
1156 field_column_start,
1157 input_iters,
1158 DEFAULT_READ_BATCH_SIZE,
1159 )
1160}
1161
1162pub fn merge_and_dedup_with_batch_size(
1168 schema: &SchemaRef,
1169 append_mode: bool,
1170 merge_mode: MergeMode,
1171 field_column_start: usize,
1172 input_iters: Vec<BoxedRecordBatchIterator>,
1173 batch_size: usize,
1174) -> Result<BoxedRecordBatchIterator> {
1175 let batch_size = batch_size.max(1);
1176 let merge_iter = FlatMergeIterator::new(schema.clone(), input_iters, batch_size)?;
1177 let maybe_dedup = if append_mode {
1178 Box::new(merge_iter) as _
1180 } else {
1181 match merge_mode {
1183 MergeMode::LastRow => {
1184 Box::new(FlatDedupIterator::new(merge_iter, FlatLastRow::new(false))) as _
1185 }
1186 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1187 merge_iter,
1188 FlatLastNonNull::new(field_column_start, false),
1189 )) as _,
1190 }
1191 };
1192 Ok(maybe_dedup)
1193}
1194
1195pub fn maybe_dedup_one(
1196 append_mode: bool,
1197 merge_mode: MergeMode,
1198 field_column_start: usize,
1199 input_iter: BoxedRecordBatchIterator,
1200) -> BoxedRecordBatchIterator {
1201 if append_mode {
1202 input_iter
1204 } else {
1205 match merge_mode {
1207 MergeMode::LastRow => {
1208 Box::new(FlatDedupIterator::new(input_iter, FlatLastRow::new(false)))
1209 }
1210 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1211 input_iter,
1212 FlatLastNonNull::new(field_column_start, false),
1213 )),
1214 }
1215 }
1216}
1217
1218pub(crate) struct FlushScheduler {
1220 region_status: HashMap<RegionId, FlushStatus>,
1222 scheduler: SchedulerRef,
1224}
1225
1226impl FlushScheduler {
1227 pub(crate) fn new(scheduler: SchedulerRef) -> FlushScheduler {
1229 FlushScheduler {
1230 region_status: HashMap::new(),
1231 scheduler,
1232 }
1233 }
1234
1235 pub(crate) fn is_flush_requested(&self, region_id: RegionId) -> bool {
1237 self.region_status.contains_key(®ion_id)
1238 }
1239
1240 fn schedule_flush_task(
1241 &mut self,
1242 version_control: &VersionControlRef,
1243 mut task: RegionFlushTask,
1244 ) -> Result<CancellableTaskState> {
1245 let region_id = task.region_id;
1246
1247 if let Err(e) = version_control.freeze_mutable() {
1249 error!(e; "Failed to freeze the mutable memtable for region {}", region_id);
1250 task.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1251
1252 return Err(e);
1253 }
1254 let state = CancellableTaskState::new();
1256 let (job, waiters) = task.into_flush_job(version_control, state.clone());
1257 if let Err(e) = self.scheduler.schedule(job) {
1258 error!(e; "Failed to schedule flush job for region {}", region_id);
1259 waiters.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1260
1261 return Err(e);
1262 }
1263 Ok(state)
1264 }
1265
1266 pub(crate) fn schedule_flush(
1268 &mut self,
1269 region_id: RegionId,
1270 version_control: &VersionControlRef,
1271 mut task: RegionFlushTask,
1272 ) -> Result<()> {
1273 debug_assert_eq!(region_id, task.region_id);
1274
1275 let version = version_control.current().version;
1276 if version.memtables.is_empty() {
1277 debug_assert!(!self.region_status.contains_key(®ion_id));
1278 task.on_success();
1280 return Ok(());
1281 }
1282
1283 FLUSH_REQUESTS_TOTAL
1285 .with_label_values(&[task.reason.as_str()])
1286 .inc();
1287
1288 if let Some(flush_status) = self.region_status.get_mut(®ion_id) {
1290 if flush_status.has_pending_lifecycle_ddl() {
1291 task.on_failure(Arc::new(FlushCancelledSnafu.build()));
1292 return Ok(());
1293 }
1294 debug!("Merging flush task for region {}", region_id);
1296 flush_status.merge_task(task);
1297 return Ok(());
1298 }
1299
1300 let closing = task.reason == FlushReason::Closing;
1301 let state = self.schedule_flush_task(version_control, task)?;
1302
1303 let _ = self.region_status.insert(
1305 region_id,
1306 FlushStatus::new(region_id, version_control.clone(), state, closing),
1307 );
1308
1309 Ok(())
1310 }
1311
1312 pub(crate) fn on_flush_success(
1316 &mut self,
1317 region_id: RegionId,
1318 ) -> Option<(
1319 Vec<SenderDdlRequest>,
1320 Vec<SenderWriteRequest>,
1321 Vec<SenderBulkRequest>,
1322 )> {
1323 let flush_status = self.region_status.get_mut(®ion_id)?;
1324 if flush_status.pending_task.is_none() {
1326 debug!(
1329 "Region {} doesn't have any pending flush task, removing it from the status",
1330 region_id
1331 );
1332 let flush_status = self.region_status.remove(®ion_id).unwrap();
1333 return Some((
1334 flush_status.pending_ddls,
1335 flush_status.pending_writes,
1336 flush_status.pending_bulk_writes,
1337 ));
1338 }
1339
1340 let version_data = flush_status.version_control.current();
1342 if version_data.version.memtables.is_empty() {
1343 let task = flush_status.pending_task.take().unwrap();
1346 task.on_success();
1348 debug!(
1349 "Region {} has nothing to flush, removing it from the status",
1350 region_id
1351 );
1352 let flush_status = self.region_status.remove(®ion_id).unwrap();
1354 return Some((
1355 flush_status.pending_ddls,
1356 flush_status.pending_writes,
1357 flush_status.pending_bulk_writes,
1358 ));
1359 }
1360
1361 debug!("Scheduling pending flush task for region {}", region_id);
1363 let task = flush_status.pending_task.take().unwrap();
1365 let version_control = flush_status.version_control.clone();
1366 match self.schedule_flush_task(&version_control, task) {
1367 Ok(state) => {
1368 self.region_status.get_mut(®ion_id).unwrap().state = state;
1369 }
1370 Err(err) => {
1371 error!(
1372 err;
1373 "Flush succeeded for region {region_id}, but failed to schedule next flush for it."
1374 );
1375 let flush_status = self.region_status.remove(®ion_id).unwrap();
1376 flush_status.fail_all(Arc::new(RegionBusySnafu { region_id }.build()));
1377 return None;
1378 }
1379 }
1380 None
1382 }
1383
1384 pub(crate) fn on_flush_failed(
1389 &mut self,
1390 region_id: RegionId,
1391 err: Arc<Error>,
1392 ) -> Vec<SenderDdlRequest> {
1393 if matches!(err.as_ref(), Error::FlushCancelled { .. }) {
1394 info!("Region {} flush was cancelled", region_id);
1395 } else {
1396 error!(err; "Region {} failed to flush, cancel all pending tasks", region_id);
1397 FLUSH_FAILURE_TOTAL.inc();
1398 }
1399
1400 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1402 return Vec::new();
1403 };
1404
1405 flush_status.on_failure(err)
1406 }
1407
1408 pub(crate) fn try_cancel_and_add_ddl<T>(
1410 &mut self,
1411 region_id: RegionId,
1412 sender: OptionOutputTx,
1413 request: T,
1414 into_ddl_request: impl FnOnce(T) -> DdlRequest,
1415 ) -> std::result::Result<(), (OptionOutputTx, T)> {
1416 let Some(status) = self.region_status.get_mut(®ion_id) else {
1417 return Err((sender, request));
1418 };
1419
1420 let cancel_result = status.state.request_cancel();
1421 debug!(
1422 "Requested flush cancellation for region {}, result: {:?}",
1423 region_id, cancel_result
1424 );
1425 status.pending_ddls.push(SenderDdlRequest {
1426 region_id,
1427 sender,
1428 request: into_ddl_request(request),
1429 });
1430 Ok(())
1431 }
1432
1433 pub(crate) fn on_region_dropped(&mut self, region_id: RegionId) {
1435 self.remove_region_on_failure(
1436 region_id,
1437 Arc::new(RegionDroppedSnafu { region_id }.build()),
1438 );
1439 }
1440
1441 pub(crate) fn on_region_closed(&mut self, region_id: RegionId) {
1443 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1444 return;
1445 };
1446
1447 flush_status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
1448 }
1449
1450 pub(crate) fn on_region_truncated(&mut self, region_id: RegionId) {
1452 self.remove_region_on_failure(
1453 region_id,
1454 Arc::new(RegionTruncatedSnafu { region_id }.build()),
1455 );
1456 }
1457
1458 fn remove_region_on_failure(&mut self, region_id: RegionId, err: Arc<Error>) {
1459 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1461 return;
1462 };
1463
1464 flush_status.fail_all(err);
1466 }
1467
1468 pub(crate) fn add_ddl_request_to_pending(&mut self, request: SenderDdlRequest) {
1473 let status = self.region_status.get_mut(&request.region_id).unwrap();
1474 status.pending_ddls.push(request);
1475 }
1476
1477 pub(crate) fn add_write_request_to_pending(&mut self, request: SenderWriteRequest) {
1482 let status = self
1483 .region_status
1484 .get_mut(&request.request.region_id)
1485 .unwrap();
1486 status.pending_writes.push(request);
1487 }
1488
1489 pub(crate) fn add_bulk_request_to_pending(&mut self, request: SenderBulkRequest) {
1494 let status = self.region_status.get_mut(&request.region_id).unwrap();
1495 status.pending_bulk_writes.push(request);
1496 }
1497
1498 pub(crate) fn has_pending_ddls(&self, region_id: RegionId) -> bool {
1500 self.region_status
1501 .get(®ion_id)
1502 .map(|status| !status.pending_ddls.is_empty() || status.closing)
1503 .unwrap_or(false)
1504 }
1505}
1506
1507impl Drop for FlushScheduler {
1508 fn drop(&mut self) {
1509 for (region_id, flush_status) in self.region_status.drain() {
1510 flush_status.fail_all(Arc::new(RegionClosedSnafu { region_id }.build()));
1512 }
1513 }
1514}
1515
1516struct FlushStatus {
1520 region_id: RegionId,
1522 version_control: VersionControlRef,
1524 state: CancellableTaskState,
1526 pending_task: Option<RegionFlushTask>,
1528 closing: bool,
1530 pending_ddls: Vec<SenderDdlRequest>,
1532 pending_writes: Vec<SenderWriteRequest>,
1534 pending_bulk_writes: Vec<SenderBulkRequest>,
1536}
1537
1538impl FlushStatus {
1539 fn new(
1540 region_id: RegionId,
1541 version_control: VersionControlRef,
1542 state: CancellableTaskState,
1543 closing: bool,
1544 ) -> FlushStatus {
1545 FlushStatus {
1546 region_id,
1547 version_control,
1548 state,
1549 pending_task: None,
1550 closing,
1551 pending_ddls: Vec::new(),
1552 pending_writes: Vec::new(),
1553 pending_bulk_writes: Vec::new(),
1554 }
1555 }
1556
1557 fn merge_task(&mut self, task: RegionFlushTask) {
1559 self.closing |= task.reason == FlushReason::Closing;
1560 if let Some(pending) = &mut self.pending_task {
1561 pending.merge(task);
1562 } else {
1563 self.pending_task = Some(task);
1564 }
1565 }
1566
1567 fn has_pending_lifecycle_ddl(&self) -> bool {
1568 self.pending_ddls
1569 .iter()
1570 .any(|ddl| matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)))
1571 }
1572
1573 fn on_failure(self, err: Arc<Error>) -> Vec<SenderDdlRequest> {
1575 if let Some(mut task) = self.pending_task {
1576 task.on_failure(err.clone());
1577 }
1578 let mut lifecycle_ddls = Vec::new();
1579 for ddl in self.pending_ddls {
1580 if matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)) {
1581 lifecycle_ddls.push(ddl);
1582 } else {
1583 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1584 region_id: self.region_id,
1585 }));
1586 }
1587 }
1588 for write_req in self.pending_writes {
1589 write_req
1590 .sender
1591 .send(Err(err.clone()).context(FlushRegionSnafu {
1592 region_id: self.region_id,
1593 }));
1594 }
1595 for bulk_req in self.pending_bulk_writes {
1596 bulk_req
1597 .sender
1598 .send(Err(err.clone()).context(FlushRegionSnafu {
1599 region_id: self.region_id,
1600 }));
1601 }
1602 lifecycle_ddls
1603 }
1604
1605 fn fail_all(self, err: Arc<Error>) {
1606 let region_id = self.region_id;
1607 let ddls = self.on_failure(err.clone());
1608 for ddl in ddls {
1609 ddl.sender
1610 .send(Err(err.clone()).context(FlushRegionSnafu { region_id }));
1611 }
1612 }
1613
1614 fn on_region_closed(self, err: Arc<Error>) {
1615 if let Some(mut task) = self.pending_task {
1616 task.on_failure(err.clone());
1617 }
1618
1619 for ddl in self.pending_ddls {
1620 if matches!(ddl.request, DdlRequest::Close(_)) {
1621 ddl.sender.send(Ok(0));
1622 } else {
1623 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1624 region_id: self.region_id,
1625 }));
1626 }
1627 }
1628
1629 for write_req in self.pending_writes {
1630 write_req
1631 .sender
1632 .send(Err(err.clone()).context(FlushRegionSnafu {
1633 region_id: self.region_id,
1634 }));
1635 }
1636 for bulk_req in self.pending_bulk_writes {
1637 bulk_req
1638 .sender
1639 .send(Err(err.clone()).context(FlushRegionSnafu {
1640 region_id: self.region_id,
1641 }));
1642 }
1643 }
1644}
1645
1646#[cfg(test)]
1647mod tests {
1648 use api::v1::{OpType, Rows};
1649 use common_error::ext::ErrorExt;
1650 use common_error::status_code::StatusCode;
1651 use datatypes::arrow::datatypes::Schema;
1652 use datatypes::arrow::record_batch::RecordBatch;
1653 use mito_codec::row_converter::build_primary_key_codec;
1654 use tokio::sync::oneshot;
1655
1656 use super::*;
1657 use crate::cache::CacheManager;
1658 use crate::error::InvalidSchedulerStateSnafu;
1659 use crate::memtable::bulk::part::BulkPartConverter;
1660 use crate::memtable::time_series::TimeSeriesMemtableBuilder;
1661 use crate::memtable::{
1662 IterBuilder, Memtable, MemtableRange, MemtableRangeContext, MemtableStats, RangesOptions,
1663 };
1664 use crate::request::WriteRequest;
1665 use crate::schedule::scheduler::Scheduler;
1666 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1667 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1668 use crate::test_util::scheduler_util::{SchedulerEnv, VecScheduler};
1669 use crate::test_util::version_util::{VersionControlBuilder, write_rows_to_version};
1670
1671 struct FailingScheduler;
1672
1673 #[async_trait::async_trait]
1674 impl Scheduler for FailingScheduler {
1675 fn schedule(&self, _job: Job) -> Result<()> {
1676 InvalidSchedulerStateSnafu.fail()
1677 }
1678
1679 async fn stop(&self, _await_termination: bool) -> Result<()> {
1680 Ok(())
1681 }
1682 }
1683
1684 fn new_test_flush_task(
1685 env: &SchedulerEnv,
1686 region_id: RegionId,
1687 reason: FlushReason,
1688 request_sender: mpsc::Sender<WorkerRequestWithTime>,
1689 manifest_ctx: ManifestContextRef,
1690 ) -> RegionFlushTask {
1691 RegionFlushTask {
1692 region_id,
1693 reason,
1694 senders: Vec::new(),
1695 request_sender,
1696 access_layer: env.access_layer.clone(),
1697 listener: WorkerListener::default(),
1698 engine_config: Arc::new(MitoConfig::default()),
1699 row_group_size: None,
1700 cache_manager: Arc::new(CacheManager::default()),
1701 manifest_ctx,
1702 index_options: IndexOptions::default(),
1703 flush_semaphore: Arc::new(Semaphore::new(2)),
1704 is_staging: false,
1705 partition_expr: None,
1706 }
1707 }
1708
1709 fn new_test_bulk_request(
1710 region_id: RegionId,
1711 ) -> (
1712 SenderBulkRequest,
1713 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1714 ) {
1715 let metadata = metadata_for_test();
1716 let schema = to_flat_sst_arrow_schema(
1717 &metadata,
1718 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1719 );
1720 let pk_codec = build_primary_key_codec(&metadata);
1721 let mut converter = BulkPartConverter::new(&metadata, schema, 16, pk_codec, true);
1722 let kvs = build_key_values_with_ts_seq_values(
1723 &metadata,
1724 "bulk_key".to_string(),
1725 1,
1726 std::iter::once(1000i64),
1727 std::iter::once(Some(1.0f64)),
1728 1,
1729 );
1730 converter.append_key_values(&kvs).unwrap();
1731 let (sender, receiver) = oneshot::channel();
1732
1733 (
1734 SenderBulkRequest {
1735 skip_wal: false,
1736 sender: OptionOutputTx::from(sender),
1737 region_id,
1738 request: converter.convert().unwrap(),
1739 region_metadata: Some(metadata),
1740 partition_expr_version: None,
1741 },
1742 receiver,
1743 )
1744 }
1745
1746 fn new_test_write_request(
1747 region_id: RegionId,
1748 ) -> (
1749 SenderWriteRequest,
1750 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1751 ) {
1752 let (sender, receiver) = oneshot::channel();
1753 let request = WriteRequest::new(region_id, OpType::Put, Rows::default(), None).unwrap();
1754 (
1755 SenderWriteRequest {
1756 sender: OptionOutputTx::from(sender),
1757 request,
1758 },
1759 receiver,
1760 )
1761 }
1762
1763 #[test]
1764 fn test_get_mutable_limit() {
1765 assert_eq!(4, WriteBufferManagerImpl::get_mutable_limit(8));
1766 assert_eq!(5, WriteBufferManagerImpl::get_mutable_limit(10));
1767 assert_eq!(32, WriteBufferManagerImpl::get_mutable_limit(64));
1768 assert_eq!(0, WriteBufferManagerImpl::get_mutable_limit(0));
1769 }
1770
1771 #[test]
1772 fn test_over_mutable_limit() {
1773 let manager = WriteBufferManagerImpl::new(1000);
1775 manager.reserve_mem(400);
1776 assert!(!manager.should_flush_engine());
1777 assert!(!manager.should_stall());
1778
1779 manager.reserve_mem(400);
1781 assert!(manager.should_flush_engine());
1782
1783 manager.schedule_free_mem(400);
1785 assert!(!manager.should_flush_engine());
1786 assert_eq!(800, manager.memory_used.load(Ordering::Relaxed));
1787 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1788
1789 manager.free_mem(400);
1791 assert_eq!(400, manager.memory_used.load(Ordering::Relaxed));
1792 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1793 }
1794
1795 #[test]
1796 fn test_over_global() {
1797 let manager = WriteBufferManagerImpl::new(1000);
1799 manager.reserve_mem(1100);
1800 assert!(manager.should_stall());
1801 manager.schedule_free_mem(200);
1803 assert!(manager.should_flush_engine());
1804 assert!(manager.should_stall());
1805
1806 manager.schedule_free_mem(450);
1808 assert!(manager.should_flush_engine());
1809 assert!(manager.should_stall());
1810
1811 manager.reserve_mem(50);
1813 assert!(manager.should_flush_engine());
1814 manager.reserve_mem(100);
1815 assert!(manager.should_flush_engine());
1816 }
1817
1818 #[test]
1819 fn test_manager_notify() {
1820 let (sender, receiver) = watch::channel(());
1821 let manager = WriteBufferManagerImpl::new(1000).with_notifier(sender);
1822 manager.reserve_mem(500);
1823 assert!(!receiver.has_changed().unwrap());
1824 manager.schedule_free_mem(500);
1825 assert!(!receiver.has_changed().unwrap());
1826 manager.free_mem(500);
1827 assert!(receiver.has_changed().unwrap());
1828 }
1829
1830 #[tokio::test]
1831 async fn test_schedule_empty() {
1832 let env = SchedulerEnv::new().await;
1833 let (tx, _rx) = mpsc::channel(4);
1834 let mut scheduler = env.mock_flush_scheduler();
1835 let builder = VersionControlBuilder::new();
1836
1837 let version_control = Arc::new(builder.build());
1838 let (output_tx, output_rx) = oneshot::channel();
1839 let mut task = RegionFlushTask {
1840 region_id: builder.region_id(),
1841 reason: FlushReason::Manual,
1842 senders: Vec::new(),
1843 request_sender: tx,
1844 access_layer: env.access_layer.clone(),
1845 listener: WorkerListener::default(),
1846 engine_config: Arc::new(MitoConfig::default()),
1847 row_group_size: None,
1848 cache_manager: Arc::new(CacheManager::default()),
1849 manifest_ctx: env
1850 .mock_manifest_context(version_control.current().version.metadata.clone())
1851 .await,
1852 index_options: IndexOptions::default(),
1853 flush_semaphore: Arc::new(Semaphore::new(2)),
1854 is_staging: false,
1855 partition_expr: None,
1856 };
1857 task.push_sender(OptionOutputTx::from(output_tx));
1858 scheduler
1859 .schedule_flush(builder.region_id(), &version_control, task)
1860 .unwrap();
1861 assert!(scheduler.region_status.is_empty());
1862 let output = output_rx.await.unwrap().unwrap();
1863 assert_eq!(output, 0);
1864 assert!(scheduler.region_status.is_empty());
1865 }
1866
1867 #[tokio::test]
1868 async fn test_schedule_flush_failure_notifies_waiter() {
1869 let env = SchedulerEnv::new()
1870 .await
1871 .scheduler(Arc::new(FailingScheduler));
1872 let (tx, _rx) = mpsc::channel(4);
1873 let mut scheduler = env.mock_flush_scheduler();
1874 let mut builder = VersionControlBuilder::new();
1875 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
1876 let version_control = Arc::new(builder.build());
1877 let version_data = version_control.current();
1878 write_rows_to_version(&version_data.version, "host0", 0, 10);
1879 let manifest_ctx = env
1880 .mock_manifest_context(version_data.version.metadata.clone())
1881 .await;
1882 let (output_tx, output_rx) = oneshot::channel();
1883 let mut task = new_test_flush_task(
1884 &env,
1885 builder.region_id(),
1886 FlushReason::Manual,
1887 tx,
1888 manifest_ctx,
1889 );
1890 task.push_sender(OptionOutputTx::from(output_tx));
1891
1892 scheduler
1893 .schedule_flush(builder.region_id(), &version_control, task)
1894 .unwrap_err();
1895
1896 let err = output_rx
1897 .await
1898 .expect("waiter must receive explicit error")
1899 .unwrap_err();
1900 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1901 }
1902
1903 #[tokio::test]
1904 async fn test_send_worker_request_failure_notifies_flush_finished_waiter() {
1905 let env = SchedulerEnv::new().await;
1906 let (tx, rx) = mpsc::channel(1);
1907 drop(rx);
1908 let builder = VersionControlBuilder::new();
1909 let version_control = Arc::new(builder.build());
1910 let manifest_ctx = env
1911 .mock_manifest_context(version_control.current().version.metadata.clone())
1912 .await;
1913 let task = new_test_flush_task(
1914 &env,
1915 builder.region_id(),
1916 FlushReason::Manual,
1917 tx,
1918 manifest_ctx,
1919 );
1920 let (output_tx, output_rx) = oneshot::channel();
1921 let request = WorkerRequest::Background {
1922 region_id: builder.region_id(),
1923 notify: BackgroundNotify::FlushFinished(FlushFinished {
1924 region_id: builder.region_id(),
1925 flush_reason: FlushReason::Manual,
1926 flushed_entry_id: 0,
1927 senders: vec![OutputTx::new(output_tx)],
1928 _timer: FLUSH_ELAPSED.with_label_values(&["total"]).start_timer(),
1929 edit: RegionEdit {
1930 files_to_add: Vec::new(),
1931 files_to_remove: Vec::new(),
1932 timestamp_ms: None,
1933 compaction_time_window: None,
1934 flushed_entry_id: None,
1935 flushed_sequence: None,
1936 committed_sequence: None,
1937 },
1938 memtables_to_remove: smallvec![],
1939 is_staging: false,
1940 }),
1941 };
1942
1943 task.send_worker_request(request).await;
1944
1945 let output = output_rx.await.expect("waiter must receive explicit error");
1946 assert!(output.is_err());
1947 }
1948
1949 #[tokio::test]
1950 async fn test_flush_waiters_drop_notifies_waiter() {
1951 let region_id = RegionId::new(1, 1);
1952 let (output_tx, output_rx) = oneshot::channel();
1953 let waiters = FlushTaskWaiters::new(region_id, vec![OutputTx::new(output_tx)]);
1954
1955 drop(waiters);
1956
1957 let err = output_rx
1958 .await
1959 .expect("waiter must receive explicit error")
1960 .unwrap_err();
1961 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1962 }
1963
1964 #[tokio::test]
1965 async fn test_flush_failure_notifies_pending_bulk_writes() {
1966 let region_id = RegionId::new(1, 1);
1967 let version_control = Arc::new(VersionControlBuilder::new().build());
1968 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
1969 let status = FlushStatus {
1970 region_id,
1971 version_control,
1972 state: CancellableTaskState::new(),
1973 pending_task: None,
1974 closing: false,
1975 pending_ddls: Vec::new(),
1976 pending_writes: Vec::new(),
1977 pending_bulk_writes: vec![bulk_req],
1978 };
1979
1980 let pending_ddls = status.on_failure(Arc::new(RegionClosedSnafu { region_id }.build()));
1981 assert!(pending_ddls.is_empty());
1982
1983 let err = output_rx
1984 .await
1985 .expect("pending bulk write must receive explicit error")
1986 .unwrap_err();
1987 assert_eq!(err.status_code(), StatusCode::Cancelled);
1988 }
1989
1990 #[tokio::test]
1991 async fn test_flush_failure_retains_lifecycle_ddls_and_fails_pending_writes() {
1992 let region_id = RegionId::new(1, 1);
1993 let version_control = Arc::new(VersionControlBuilder::new().build());
1994 let (write_req, write_rx) = new_test_write_request(region_id);
1995 let (bulk_req, bulk_rx) = new_test_bulk_request(region_id);
1996 let (truncate_tx, mut truncate_rx) = oneshot::channel();
1997 let (drop_tx, mut drop_rx) = oneshot::channel();
1998 let status = FlushStatus {
1999 region_id,
2000 version_control,
2001 state: CancellableTaskState::new(),
2002 pending_task: None,
2003 closing: false,
2004 pending_ddls: vec![
2005 SenderDdlRequest {
2006 region_id,
2007 sender: OptionOutputTx::from(truncate_tx),
2008 request: DdlRequest::Truncate(
2009 store_api::region_request::RegionTruncateRequest::All,
2010 ),
2011 },
2012 SenderDdlRequest {
2013 region_id,
2014 sender: OptionOutputTx::from(drop_tx),
2015 request: DdlRequest::Drop(store_api::region_request::RegionDropRequest {
2016 fast_path: false,
2017 force: false,
2018 partial_drop: false,
2019 }),
2020 },
2021 ],
2022 pending_writes: vec![write_req],
2023 pending_bulk_writes: vec![bulk_req],
2024 };
2025
2026 let ddls = status.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
2027 assert_eq!(2, ddls.len());
2028 assert!(matches!(ddls[0].request, DdlRequest::Truncate(_)));
2029 assert!(matches!(ddls[1].request, DdlRequest::Drop(_)));
2030
2031 let write_err = write_rx
2032 .await
2033 .expect("pending write must receive explicit error")
2034 .unwrap_err();
2035 assert_eq!(write_err.status_code(), StatusCode::RegionBusy);
2036 let bulk_err = bulk_rx
2037 .await
2038 .expect("pending bulk write must receive explicit error")
2039 .unwrap_err();
2040 assert_eq!(bulk_err.status_code(), StatusCode::RegionBusy);
2041
2042 assert!(truncate_rx.try_recv().is_err());
2043 assert!(drop_rx.try_recv().is_err());
2044 for ddl in ddls {
2045 ddl.sender.send(Ok(0));
2046 }
2047 assert_eq!(0, truncate_rx.await.unwrap().unwrap());
2048 assert_eq!(0, drop_rx.await.unwrap().unwrap());
2049 }
2050
2051 #[tokio::test]
2052 async fn test_uncommitted_ssts_cleanup_finalized_file() {
2053 let env = SchedulerEnv::new().await;
2054 let region_id = RegionId::new(1, 1);
2055 let file_id = store_api::storage::FileId::random();
2056 let path = crate::sst::location::sst_file_path(
2057 env.access_layer.table_dir(),
2058 crate::sst::file::RegionFileId::new(region_id, file_id),
2059 env.access_layer.path_type(),
2060 );
2061 env.access_layer
2062 .object_store()
2063 .write(&path, Bytes::from_static(b"sst"))
2064 .await
2065 .unwrap();
2066 assert!(env.access_layer.object_store().exists(&path).await.unwrap());
2067
2068 let uncommitted = UncommittedSsts::new(region_id, env.access_layer.clone(), None);
2069 uncommitted.track(&[SstInfo {
2070 file_id,
2071 ..Default::default()
2072 }]);
2073 assert_eq!(1, uncommitted.num_tracked_files());
2074 uncommitted.cleanup_for_test().await.unwrap();
2075 assert_eq!(0, uncommitted.num_tracked_files());
2076
2077 let entries = env
2080 .access_layer
2081 .object_store()
2082 .list(&env.access_layer.build_region_dir(region_id))
2083 .await
2084 .unwrap();
2085 assert!(entries.iter().all(|entry| entry.path() != path));
2086 }
2087
2088 #[tokio::test]
2089 async fn test_region_closed_notifies_pending_bulk_writes() {
2090 let region_id = RegionId::new(1, 1);
2091 let version_control = Arc::new(VersionControlBuilder::new().build());
2092 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
2093 let status = FlushStatus {
2094 region_id,
2095 version_control,
2096 state: CancellableTaskState::new(),
2097 pending_task: None,
2098 closing: false,
2099 pending_ddls: Vec::new(),
2100 pending_writes: Vec::new(),
2101 pending_bulk_writes: vec![bulk_req],
2102 };
2103
2104 status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
2105
2106 let err = output_rx
2107 .await
2108 .expect("pending bulk write must receive explicit error")
2109 .unwrap_err();
2110 assert_eq!(err.status_code(), StatusCode::Cancelled);
2111 }
2112
2113 #[tokio::test]
2114 async fn test_schedule_pending_request() {
2115 let job_scheduler = Arc::new(VecScheduler::default());
2116 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2117 let (tx, _rx) = mpsc::channel(4);
2118 let mut scheduler = env.mock_flush_scheduler();
2119 let mut builder = VersionControlBuilder::new();
2120 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2122 let version_control = Arc::new(builder.build());
2123 let version_data = version_control.current();
2125 write_rows_to_version(&version_data.version, "host0", 0, 10);
2126 let manifest_ctx = env
2127 .mock_manifest_context(version_data.version.metadata.clone())
2128 .await;
2129 let mut tasks: Vec<_> = (0..3)
2131 .map(|_| RegionFlushTask {
2132 region_id: builder.region_id(),
2133 reason: FlushReason::Manual,
2134 senders: Vec::new(),
2135 request_sender: tx.clone(),
2136 access_layer: env.access_layer.clone(),
2137 listener: WorkerListener::default(),
2138 engine_config: Arc::new(MitoConfig::default()),
2139 row_group_size: None,
2140 cache_manager: Arc::new(CacheManager::default()),
2141 manifest_ctx: manifest_ctx.clone(),
2142 index_options: IndexOptions::default(),
2143 flush_semaphore: Arc::new(Semaphore::new(2)),
2144 is_staging: false,
2145 partition_expr: None,
2146 })
2147 .collect();
2148 let task = tasks.pop().unwrap();
2150 scheduler
2151 .schedule_flush(builder.region_id(), &version_control, task)
2152 .unwrap();
2153 assert_eq!(1, scheduler.region_status.len());
2155 assert_eq!(1, job_scheduler.num_jobs());
2156 let version_data = version_control.current();
2158 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2159 let output_rxs: Vec<_> = tasks
2161 .into_iter()
2162 .map(|mut task| {
2163 let (output_tx, output_rx) = oneshot::channel();
2164 task.push_sender(OptionOutputTx::from(output_tx));
2165 scheduler
2166 .schedule_flush(builder.region_id(), &version_control, task)
2167 .unwrap();
2168 output_rx
2169 })
2170 .collect();
2171 version_control.apply_edit(
2173 Some(RegionEdit {
2174 files_to_add: Vec::new(),
2175 files_to_remove: Vec::new(),
2176 timestamp_ms: None,
2177 compaction_time_window: None,
2178 flushed_entry_id: None,
2179 flushed_sequence: None,
2180 committed_sequence: None,
2181 }),
2182 &[0],
2183 builder.file_purger(),
2184 );
2185 scheduler.on_flush_success(builder.region_id());
2186 assert_eq!(1, job_scheduler.num_jobs());
2188 assert!(scheduler.region_status.is_empty());
2190 for output_rx in output_rxs {
2191 let output = output_rx.await.unwrap().unwrap();
2192 assert_eq!(output, 0);
2193 }
2194 }
2195
2196 #[test]
2198 fn test_memtable_flat_sources_single_range_append_mode_behavior() {
2199 let metadata = metadata_for_test();
2201 let schema = to_flat_sst_arrow_schema(
2202 &metadata,
2203 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2204 );
2205
2206 let capacity = 16;
2209 let pk_codec = build_primary_key_codec(&metadata);
2210 let mut converter =
2211 BulkPartConverter::new(&metadata, schema.clone(), capacity, pk_codec, true);
2212 let kvs = build_key_values_with_ts_seq_values(
2213 &metadata,
2214 "dup_key".to_string(),
2215 1,
2216 vec![1000i64, 1000i64].into_iter(),
2217 vec![Some(1.0f64), Some(2.0f64)].into_iter(),
2218 1,
2219 );
2220 converter.append_key_values(&kvs).unwrap();
2221 let part = converter.convert().unwrap();
2222
2223 let build_ranges = |append_mode: bool| -> MemtableRanges {
2226 let memtable = crate::memtable::bulk::BulkMemtable::new(
2227 1,
2228 crate::memtable::bulk::BulkMemtableConfig::default(),
2229 metadata.clone(),
2230 None,
2231 None,
2232 append_mode,
2233 MergeMode::LastRow,
2234 );
2235 memtable.write_bulk(part.clone()).unwrap();
2236 memtable.ranges(None, RangesOptions::for_flush()).unwrap()
2237 };
2238
2239 {
2241 let mem_ranges = build_ranges(false);
2242 assert_eq!(1, mem_ranges.ranges.len());
2243
2244 let options = RegionOptions {
2245 append_mode: false,
2246 merge_mode: Some(MergeMode::LastRow),
2247 ..Default::default()
2248 };
2249
2250 let flat_sources = memtable_flat_sources(
2251 schema.clone(),
2252 mem_ranges,
2253 &metadata,
2254 &options,
2255 metadata.primary_key.len(),
2256 )
2257 .unwrap();
2258 assert!(flat_sources.encoded.is_empty());
2259 assert_eq!(1, flat_sources.sources.len());
2260
2261 let mut total_rows = 0usize;
2263 for (source, _sequence) in flat_sources.sources {
2264 total_rows += source
2265 .take_iter()
2266 .map(|x| x.unwrap().num_rows())
2267 .sum::<usize>();
2268 }
2269 assert_eq!(1, total_rows, "dedup should keep a single row");
2270 }
2271
2272 {
2274 let mem_ranges = build_ranges(true);
2275 assert_eq!(1, mem_ranges.ranges.len());
2276
2277 let options = RegionOptions {
2278 append_mode: true,
2279 ..Default::default()
2280 };
2281
2282 let flat_sources = memtable_flat_sources(
2283 schema,
2284 mem_ranges,
2285 &metadata,
2286 &options,
2287 metadata.primary_key.len(),
2288 )
2289 .unwrap();
2290 assert!(flat_sources.encoded.is_empty());
2291 assert_eq!(1, flat_sources.sources.len());
2292
2293 let mut total_rows = 0usize;
2294 for (source, _sequence) in flat_sources.sources {
2295 total_rows += source
2296 .take_iter()
2297 .map(|x| x.unwrap().num_rows())
2298 .sum::<usize>();
2299 }
2300 assert_eq!(2, total_rows, "append_mode should preserve duplicates");
2301 }
2302 }
2303
2304 #[test]
2305 fn test_memtable_flat_sources_uses_non_encoded_schema() -> Result<()> {
2306 struct TestIterBuilder {
2307 schema: SchemaRef,
2308 batch: Option<RecordBatch>,
2309 }
2310
2311 impl IterBuilder for TestIterBuilder {
2312 fn build(
2313 &self,
2314 _metrics: Option<crate::memtable::MemScanMetrics>,
2315 ) -> Result<crate::memtable::BoxedBatchIterator> {
2316 unimplemented!()
2317 }
2318
2319 fn is_record_batch(&self) -> bool {
2320 true
2321 }
2322
2323 fn build_record_batch(
2324 &self,
2325 _time_range: Option<(common_time::Timestamp, common_time::Timestamp)>,
2326 _metrics: Option<crate::memtable::MemScanMetrics>,
2327 ) -> Result<BoxedRecordBatchIterator> {
2328 let Some(batch) = self.batch.clone() else {
2329 unimplemented!()
2330 };
2331 Ok(Box::new(std::iter::once(Ok(batch))))
2332 }
2333
2334 fn record_batch_schema_hint(&self) -> Option<SchemaRef> {
2335 Some(self.schema.clone())
2336 }
2337
2338 fn encoded_range(&self) -> Option<EncodedRange> {
2339 self.batch.is_none().then(|| EncodedRange {
2340 data: Bytes::new(),
2341 sst_info: SstInfo::default(),
2342 })
2343 }
2344 }
2345
2346 let metadata = metadata_for_test();
2347 let schema = to_flat_sst_arrow_schema(
2348 &metadata,
2349 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
2350 );
2351 let pk_codec = build_primary_key_codec(&metadata);
2352 let mut converter = BulkPartConverter::new(&metadata, schema.clone(), 1, pk_codec, true);
2353 let kvs = build_key_values_with_ts_seq_values(
2354 &metadata,
2355 "key".to_string(),
2356 1,
2357 std::iter::once(1000),
2358 std::iter::once(Some(1.0)),
2359 1,
2360 );
2361 converter.append_key_values(&kvs)?;
2362 let batch = converter.convert()?.batch;
2363 let encoded_schema = Arc::new(Schema::empty());
2364
2365 let new_range = |id, builder| {
2366 MemtableRange::new(
2367 Arc::new(MemtableRangeContext::new(
2368 id,
2369 Box::new(builder),
2370 Default::default(),
2371 )),
2372 MemtableStats {
2373 num_rows: 1,
2374 ..Default::default()
2375 },
2376 )
2377 };
2378 let mut ranges = std::collections::BTreeMap::new();
2379 ranges.insert(
2380 0,
2381 new_range(
2382 0,
2383 TestIterBuilder {
2384 schema: encoded_schema.clone(),
2385 batch: None,
2386 },
2387 ),
2388 );
2389 ranges.insert(
2390 1,
2391 new_range(
2392 0,
2393 TestIterBuilder {
2394 schema: schema.clone(),
2395 batch: Some(batch),
2396 },
2397 ),
2398 );
2399
2400 let sources = memtable_flat_sources(
2401 encoded_schema,
2402 MemtableRanges { ranges },
2403 &metadata,
2404 &RegionOptions {
2405 append_mode: true,
2406 ..Default::default()
2407 },
2408 metadata.primary_key.len(),
2409 )?;
2410 assert_eq!(1, sources.encoded.len());
2411 assert_eq!(1, sources.sources.len());
2412 assert_eq!(&schema, sources.sources[0].0.schema());
2413 Ok(())
2414 }
2415
2416 #[tokio::test]
2417 async fn test_schedule_pending_request_on_flush_success() {
2418 common_telemetry::init_default_ut_logging();
2419 let job_scheduler = Arc::new(VecScheduler::default());
2420 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2421 let (tx, _rx) = mpsc::channel(4);
2422 let mut scheduler = env.mock_flush_scheduler();
2423 let mut builder = VersionControlBuilder::new();
2424 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2426 let version_control = Arc::new(builder.build());
2427 let version_data = version_control.current();
2429 write_rows_to_version(&version_data.version, "host0", 0, 10);
2430 let manifest_ctx = env
2431 .mock_manifest_context(version_data.version.metadata.clone())
2432 .await;
2433 let mut tasks: Vec<_> = (0..2)
2435 .map(|_| RegionFlushTask {
2436 region_id: builder.region_id(),
2437 reason: FlushReason::Manual,
2438 senders: Vec::new(),
2439 request_sender: tx.clone(),
2440 access_layer: env.access_layer.clone(),
2441 listener: WorkerListener::default(),
2442 engine_config: Arc::new(MitoConfig::default()),
2443 row_group_size: None,
2444 cache_manager: Arc::new(CacheManager::default()),
2445 manifest_ctx: manifest_ctx.clone(),
2446 index_options: IndexOptions::default(),
2447 flush_semaphore: Arc::new(Semaphore::new(2)),
2448 is_staging: false,
2449 partition_expr: None,
2450 })
2451 .collect();
2452 let task = tasks.pop().unwrap();
2454 scheduler
2455 .schedule_flush(builder.region_id(), &version_control, task)
2456 .unwrap();
2457 assert_eq!(1, scheduler.region_status.len());
2459 assert_eq!(1, job_scheduler.num_jobs());
2460 let task = tasks.pop().unwrap();
2462 scheduler
2463 .schedule_flush(builder.region_id(), &version_control, task)
2464 .unwrap();
2465 assert!(
2466 scheduler
2467 .region_status
2468 .get(&builder.region_id())
2469 .unwrap()
2470 .pending_task
2471 .is_some()
2472 );
2473
2474 let version_data = version_control.current();
2476 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2477 version_control.apply_edit(
2479 Some(RegionEdit {
2480 files_to_add: Vec::new(),
2481 files_to_remove: Vec::new(),
2482 timestamp_ms: None,
2483 compaction_time_window: None,
2484 flushed_entry_id: None,
2485 flushed_sequence: None,
2486 committed_sequence: None,
2487 }),
2488 &[0],
2489 builder.file_purger(),
2490 );
2491 write_rows_to_version(&version_data.version, "host1", 0, 10);
2492 scheduler.on_flush_success(builder.region_id());
2493 assert_eq!(2, job_scheduler.num_jobs());
2494 assert!(
2496 scheduler
2497 .region_status
2498 .get(&builder.region_id())
2499 .unwrap()
2500 .pending_task
2501 .is_none()
2502 );
2503 }
2504
2505 #[tokio::test]
2506 async fn test_schedule_pending_request_failure_drains_pending_ddls() {
2507 common_telemetry::init_default_ut_logging();
2508 let job_scheduler = Arc::new(VecScheduler::default());
2509 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2510 let (tx, _rx) = mpsc::channel(4);
2511 let mut scheduler = env.mock_flush_scheduler();
2512 let mut builder = VersionControlBuilder::new();
2513 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2514 let version_control = Arc::new(builder.build());
2515
2516 let version_data = version_control.current();
2517 write_rows_to_version(&version_data.version, "host0", 0, 10);
2518 let manifest_ctx = env
2519 .mock_manifest_context(version_data.version.metadata.clone())
2520 .await;
2521
2522 let task = new_test_flush_task(
2523 &env,
2524 builder.region_id(),
2525 FlushReason::Manual,
2526 tx.clone(),
2527 manifest_ctx.clone(),
2528 );
2529 scheduler
2530 .schedule_flush(builder.region_id(), &version_control, task)
2531 .unwrap();
2532
2533 let task = new_test_flush_task(
2534 &env,
2535 builder.region_id(),
2536 FlushReason::Closing,
2537 tx,
2538 manifest_ctx,
2539 );
2540 scheduler
2541 .schedule_flush(builder.region_id(), &version_control, task)
2542 .unwrap();
2543
2544 let (sender, receiver) = oneshot::channel();
2545 scheduler.add_ddl_request_to_pending(SenderDdlRequest {
2546 sender: OptionOutputTx::from(sender),
2547 region_id: builder.region_id(),
2548 request: DdlRequest::Close(store_api::region_request::RegionCloseRequest::default()),
2549 });
2550
2551 let version_data = version_control.current();
2552 version_control.apply_edit(
2553 Some(RegionEdit {
2554 files_to_add: Vec::new(),
2555 files_to_remove: Vec::new(),
2556 timestamp_ms: None,
2557 compaction_time_window: None,
2558 flushed_entry_id: None,
2559 flushed_sequence: None,
2560 committed_sequence: None,
2561 }),
2562 &[0],
2563 builder.file_purger(),
2564 );
2565 write_rows_to_version(&version_data.version, "host1", 0, 10);
2566
2567 scheduler.scheduler = Arc::new(FailingScheduler);
2568 scheduler.on_flush_success(builder.region_id());
2569
2570 assert!(scheduler.region_status.is_empty());
2571 let err = receiver
2572 .await
2573 .expect("pending DDL must be notified")
2574 .unwrap_err();
2575 assert_eq!(err.status_code(), StatusCode::RegionBusy);
2576 }
2577}