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