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