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