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, 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) partition_expr: Option<String>,
288 pub(crate) durability_barrier: DurabilityBarrier,
290}
291
292struct FlushTaskWaiters {
293 region_id: RegionId,
294 senders: Mutex<Vec<OutputTx>>,
295}
296
297impl FlushTaskWaiters {
298 fn new(region_id: RegionId, senders: Vec<OutputTx>) -> Self {
299 Self {
300 region_id,
301 senders: Mutex::new(senders),
302 }
303 }
304
305 fn take(&self) -> Vec<OutputTx> {
306 std::mem::take(&mut *self.senders.lock().unwrap())
307 }
308
309 fn on_failure(&self, err: Arc<Error>) {
310 for sender in self.take() {
311 sender.send(Err(err.clone()).context(FlushRegionSnafu {
312 region_id: self.region_id,
313 }));
314 }
315 }
316}
317
318impl Drop for FlushTaskWaiters {
319 fn drop(&mut self) {
320 self.on_failure(Arc::new(
321 RegionBusySnafu {
322 region_id: self.region_id,
323 }
324 .build(),
325 ));
326 }
327}
328
329impl RegionFlushTask {
330 pub(crate) fn push_sender(&mut self, mut sender: OptionOutputTx) {
332 if let Some(sender) = sender.take_inner() {
333 self.senders.push(sender);
334 }
335 }
336
337 fn on_success(self) {
339 for sender in self.senders {
340 sender.send(Ok(0));
341 }
342 }
343
344 fn on_failure(&mut self, err: Arc<Error>) {
346 for sender in self.senders.drain(..) {
347 sender.send(Err(err.clone()).context(FlushRegionSnafu {
348 region_id: self.region_id,
349 }));
350 }
351 }
352
353 fn into_flush_job(
357 mut self,
358 version_control: &VersionControlRef,
359 state: CancellableTaskState,
360 ) -> (Job, Arc<FlushTaskWaiters>) {
361 let version_data = version_control.current();
364 let waiters = Arc::new(FlushTaskWaiters::new(
365 self.region_id,
366 std::mem::take(&mut self.senders),
367 ));
368 let job_waiters = waiters.clone();
369
370 let job = Box::pin(async move {
371 self.senders = job_waiters.take();
372 INFLIGHT_FLUSH_COUNT.inc();
373 self.do_flush(version_data, state).await;
374 INFLIGHT_FLUSH_COUNT.dec();
375 });
376 (job, waiters)
377 }
378
379 async fn do_flush(&mut self, version_data: VersionControlData, state: CancellableTaskState) {
381 let timer = FLUSH_ELAPSED.with_label_values(&["total"]).start_timer();
382 let uncommitted = UncommittedSsts::new(
383 self.region_id,
384 self.access_layer.clone(),
385 Some(self.cache_manager.clone()),
386 );
387 self.listener.on_flush_begin(self.region_id).await;
388 let flush_result = if state.is_cancelled() {
389 FlushCancelledSnafu.fail()
390 } else {
391 self.flush_memtables(&version_data, &state, &uncommitted)
392 .await
393 };
394
395 let worker_request = match flush_result {
396 Ok(edit) => {
397 let memtables_to_remove = version_data
398 .version
399 .memtables
400 .immutables()
401 .iter()
402 .map(|m| m.id())
403 .collect();
404 let flush_finished = FlushFinished {
405 region_id: self.region_id,
406 flush_reason: self.reason,
407 flushed_entry_id: version_data.last_entry_id,
409 senders: std::mem::take(&mut self.senders),
410 _timer: timer,
411 edit,
412 memtables_to_remove,
413 };
414 WorkerRequest::Background {
415 region_id: self.region_id,
416 notify: BackgroundNotify::FlushFinished(flush_finished),
417 }
418 }
419 Err(e) => {
420 let err = Arc::new(e);
421 let failed = FlushFailed { err: err.clone() };
422 if failed.is_cancelled() {
423 info!("Flush cancelled for region {}", self.region_id);
424 } else {
425 error!(err; "Failed to flush region {}", self.region_id);
426 }
427 timer.stop_and_discard();
429 uncommitted.cleanup().await;
430
431 self.on_failure(err.clone());
432 WorkerRequest::Background {
433 region_id: self.region_id,
434 notify: BackgroundNotify::FlushFailed(failed),
435 }
436 }
437 };
438 self.send_worker_request(worker_request).await;
439 }
440
441 async fn flush_memtables(
444 &self,
445 version_data: &VersionControlData,
446 state: &CancellableTaskState,
447 uncommitted: &UncommittedSsts,
448 ) -> Result<RegionEdit> {
449 let version = &version_data.version;
452 let timer = FLUSH_ELAPSED
453 .with_label_values(&["flush_memtables"])
454 .start_timer();
455
456 let mut write_opts = WriteOptions {
457 write_buffer_size: self.engine_config.sst_write_buffer_size,
458 float_field_encoding: version.options.float_field_encoding,
459 ..Default::default()
460 };
461 if let Some(row_group_size) = self.row_group_size {
462 write_opts.row_group_size = row_group_size;
463 }
464
465 let DoFlushMemtablesResult {
466 file_metas,
467 flushed_bytes,
468 series_count,
469 encoded_part_count,
470 flush_metrics,
471 sst_infos,
472 } = self
473 .do_flush_memtables(version, write_opts, state, uncommitted)
474 .await?;
475
476 if !file_metas.is_empty() {
477 FLUSH_BYTES_TOTAL.inc_by(flushed_bytes);
478 }
479
480 let mut file_ids = Vec::with_capacity(file_metas.len());
481 let mut total_rows = 0;
482 let mut total_bytes = 0;
483 for meta in &file_metas {
484 file_ids.push(meta.file_id);
485 total_rows += meta.num_rows;
486 total_bytes += meta.file_size;
487 }
488 info!(
489 "Successfully flush memtables, region: {}, reason: {}, files: {:?}, series count: {}, total_rows: {}, total_bytes: {}, cost: {:?}, encoded_part_count: {}, metrics: {:?}",
490 self.region_id,
491 self.reason.as_str(),
492 file_ids,
493 series_count,
494 total_rows,
495 total_bytes,
496 timer.stop_and_record(),
497 encoded_part_count,
498 flush_metrics,
499 );
500 flush_metrics.observe();
501
502 let hook = self.manifest_ctx.hook();
503 if let Some(hook) = &hook {
504 let files: Vec<SstFileInfo<'_>> = sst_infos
505 .iter()
506 .zip(file_metas.iter())
507 .map(|(sst_info, file_meta)| SstFileInfo {
508 sst_info_ref: sst_info,
509 file_meta,
510 })
511 .collect();
512 hook.on_sst_files_written(self.region_id, &version.metadata, &files)
513 .await;
514 }
515
516 let durable = self.durability_barrier.wait(version_data.last_entry_id);
521 tokio::pin!(durable);
522 match CancellableFuture::new(durable.as_mut(), state.cancel_handle()).await {
523 Ok(result) => result?,
524 Err(_) => return FlushCancelledSnafu.fail(),
525 }
526
527 let edit = RegionEdit {
528 files_to_add: file_metas,
529 files_to_remove: Vec::new(),
530 timestamp_ms: Some(chrono::Utc::now().timestamp_millis()),
531 compaction_time_window: None,
532 flushed_entry_id: Some(version_data.last_entry_id),
534 flushed_sequence: Some(version_data.committed_sequence),
535 committed_sequence: None,
536 };
537 info!("Applying {edit:?} to region {}", self.region_id);
538
539 let action_list = RegionMetaActionList::with_action(RegionMetaAction::Edit(edit.clone()));
540
541 if !state.mark_commit_started() {
543 return FlushCancelledSnafu.fail();
544 }
545 self.listener.on_flush_commit_begin(self.region_id).await;
546
547 let expected_state = if matches!(self.reason, FlushReason::Downgrading) {
548 RegionLeaderState::Downgrading
549 } else {
550 RegionLeaderState::Writable
551 };
552 let manifest_version = match self
553 .manifest_ctx
554 .update_manifest(expected_state, action_list, false)
555 .await
556 {
557 Ok(manifest_version) => {
558 uncommitted.disarm_cleanup();
559 manifest_version
560 }
561 Err(e) => {
562 if e.may_have_persisted_manifest_update() {
563 uncommitted.disarm_cleanup();
564 } else {
565 info!(
566 "Cleaning uncommitted SSTs because the manifest update was not persisted, region: {}, job: flush, error: {:?}",
567 self.region_id, e
568 );
569 uncommitted.cleanup().await;
570 }
571 return Err(e);
572 }
573 };
574 info!(
575 "Successfully update manifest version to {manifest_version}, region: {}, reason: {}",
576 self.region_id,
577 self.reason.as_str()
578 );
579
580 Ok(edit)
581 }
582
583 async fn do_flush_memtables(
584 &self,
585 version: &VersionRef,
586 write_opts: WriteOptions,
587 state: &CancellableTaskState,
588 uncommitted: &UncommittedSsts,
589 ) -> Result<DoFlushMemtablesResult> {
590 let memtables = version.memtables.immutables();
591 let mut file_metas = Vec::with_capacity(memtables.len());
592 let mut flushed_bytes = 0;
593 let mut series_count = 0;
594 let mut encoded_part_count = 0;
595 let mut flush_metrics = Metrics::new(WriteType::Flush);
596 let partition_expr = parse_partition_expr(self.partition_expr.as_deref())?;
597 let hook = self.manifest_ctx.hook();
598 let mut all_sst_infos = Vec::new();
599 for mem in memtables {
600 if mem.is_empty() {
601 continue;
603 }
604
605 let compact_start = std::time::Instant::now();
607 if let Err(e) = mem.compact(true) {
608 common_telemetry::error!(e; "Failed to compact memtable before flush");
609 }
610 let compact_cost = compact_start.elapsed();
611 flush_metrics.compact_memtable += compact_cost;
612
613 let mem_stats = mem.stats();
614 let batch_size = crate::batch_size::estimate_batch_size([(
615 mem_stats.num_rows() as u64,
616 mem_stats.bytes_allocated() as u64,
617 )]);
618 let mem_ranges =
620 mem.ranges(None, RangesOptions::for_flush().with_batch_size(batch_size))?;
621 let num_mem_ranges = mem_ranges.ranges.len();
622
623 let num_mem_rows = mem_ranges.num_rows();
625 let memtable_series_count = mem_ranges.series_count();
626 let memtable_id = mem.id();
627 series_count += memtable_series_count;
630
631 let flush_start = Instant::now();
632 let FlushFlatMemResult {
633 num_encoded,
634 num_sources,
635 results,
636 } = self
637 .flush_flat_mem_ranges(version, &write_opts, mem_ranges, state, uncommitted)
638 .await?;
639 encoded_part_count += num_encoded;
640 for (source_idx, result) in results.into_iter().enumerate() {
641 let (max_sequence, ssts_written, metrics) = result?;
642 if ssts_written.is_empty() {
643 continue;
645 }
646
647 common_telemetry::debug!(
648 "Region {} flush one memtable {} {}/{}, metrics: {:?}",
649 self.region_id,
650 memtable_id,
651 source_idx,
652 num_sources,
653 metrics
654 );
655
656 flush_metrics = flush_metrics.merge(metrics);
657
658 for sst_info in &ssts_written {
659 flushed_bytes += sst_info.file_size;
660 let pk_range = sst_info
661 .file_metadata
662 .as_ref()
663 .and_then(|meta| extract_primary_key_range(meta, &version.metadata));
664 file_metas.push(Self::new_file_meta(
665 self.region_id,
666 max_sequence,
667 sst_info,
668 partition_expr.clone(),
669 pk_range,
670 version.options.preserve_row_sequence,
671 ));
672 }
673 if hook.is_some() {
674 all_sst_infos.extend(ssts_written);
675 }
676 }
677
678 common_telemetry::debug!(
679 "Region {} flush {} memtables for {}, num_mem_ranges: {}, num_encoded: {}, num_rows: {}, flush_cost: {:?}, compact_cost: {:?}",
680 self.region_id,
681 num_sources,
682 memtable_id,
683 num_mem_ranges,
684 num_encoded,
685 num_mem_rows,
686 flush_start.elapsed(),
687 compact_cost,
688 );
689 }
690
691 Ok(DoFlushMemtablesResult {
692 file_metas,
693 flushed_bytes,
694 series_count,
695 encoded_part_count,
696 flush_metrics,
697 sst_infos: all_sst_infos,
698 })
699 }
700
701 async fn flush_flat_mem_ranges(
702 &self,
703 version: &VersionRef,
704 write_opts: &WriteOptions,
705 mem_ranges: MemtableRanges,
706 state: &CancellableTaskState,
707 uncommitted: &UncommittedSsts,
708 ) -> Result<FlushFlatMemResult> {
709 let batch_schema = to_flat_sst_arrow_schema(
710 &version.metadata,
711 &FlatSchemaOptions::from_encoding(version.metadata.primary_key_encoding),
712 );
713 let field_column_start =
714 flat_format::field_column_start(&version.metadata, batch_schema.fields().len());
715 let flat_sources = memtable_flat_sources(
716 batch_schema,
717 mem_ranges,
718 &version.metadata,
719 &version.options,
720 field_column_start,
721 )?;
722 let mut tasks = Vec::with_capacity(flat_sources.encoded.len() + flat_sources.sources.len());
723 let num_encoded = flat_sources.encoded.len();
724 for (source, max_sequence) in flat_sources.sources {
725 let write_request = self.new_write_request(version, max_sequence, source);
726 let access_layer = self.access_layer.clone();
727 let write_opts = write_opts.clone();
728 let semaphore = self.flush_semaphore.clone();
729 let uncommitted = uncommitted.clone();
730 let task = common_runtime::spawn_global(async move {
731 let _permit = semaphore.acquire().await.unwrap();
732 let mut metrics = Metrics::new(WriteType::Flush);
733 let ssts = access_layer
734 .write_sst(write_request, &write_opts, &mut metrics)
735 .await?;
736 uncommitted.track(&ssts);
737 FLUSH_FILE_TOTAL.inc_by(ssts.len() as u64);
738 Ok((max_sequence, ssts, metrics))
739 });
740 tasks.push(task);
741 }
742 let build_index = self.engine_config.index.build_mode == IndexBuildMode::Sync;
743 for (mut encoded, max_sequence) in flat_sources.encoded {
744 let access_layer = self.access_layer.clone();
745 let cache_manager = self.cache_manager.clone();
746 let region_id = version.metadata.region_id;
747 let write_buffer_size = write_opts.write_buffer_size;
748 let semaphore = self.flush_semaphore.clone();
749 let uncommitted = uncommitted.clone();
750 let index_build_params = build_index.then(|| EncodedSstIndexBuildParams {
751 metadata: version.metadata.clone(),
752 index_options: self.index_options.clone(),
753 inverted_index_config: self.engine_config.inverted_index.clone(),
754 fulltext_index_config: self.engine_config.fulltext_index.clone(),
755 bloom_filter_index_config: self.engine_config.bloom_filter_index.clone(),
756 });
757 uncommitted.track_before_upload(encoded.sst_info.file_id, index_build_params.is_some());
760 let task = common_runtime::spawn_global(async move {
761 let _permit = semaphore.acquire().await.unwrap();
762 let metrics = access_layer
763 .put_sst(
764 &encoded.data,
765 region_id,
766 &mut encoded.sst_info,
767 &cache_manager,
768 write_buffer_size,
769 index_build_params,
770 )
771 .await?;
772 uncommitted.track(std::slice::from_ref(&encoded.sst_info));
773 FLUSH_FILE_TOTAL.inc();
774 Ok((max_sequence, smallvec![encoded.sst_info], metrics))
775 });
776 tasks.push(task);
777 }
778 let num_sources = tasks.len();
779 let abort_handles = tasks
780 .iter()
781 .map(|task| task.abort_handle())
782 .collect::<Vec<_>>();
783 let join_all = futures::future::join_all(tasks);
784 tokio::pin!(join_all);
785 let results = match CancellableFuture::new(join_all.as_mut(), state.cancel_handle()).await {
786 Ok(results) => results
787 .into_iter()
788 .map(|result| result.context(JoinSnafu))
789 .collect::<Result<Vec<_>>>()?,
790 Err(_) => {
791 for handle in abort_handles {
792 handle.abort();
793 }
794 let _ = join_all.await;
797 return FlushCancelledSnafu.fail();
798 }
799 };
800 Ok(FlushFlatMemResult {
801 num_encoded,
802 num_sources,
803 results,
804 })
805 }
806
807 fn new_file_meta(
808 region_id: RegionId,
809 max_sequence: u64,
810 sst_info: &SstInfo,
811 partition_expr: Option<PartitionExpr>,
812 primary_key_range: Option<(Bytes, Bytes)>,
813 preserve_row_sequence: bool,
814 ) -> FileMeta {
815 let (primary_key_min, primary_key_max) = match primary_key_range {
816 Some((min, max)) => (Some(min), Some(max)),
817 None => (None, None),
818 };
819 FileMeta {
820 region_id,
821 file_id: sst_info.file_id,
822 time_range: sst_info.time_range,
823 level: 0,
824 file_size: sst_info.file_size,
825 max_row_group_uncompressed_size: sst_info.max_row_group_uncompressed_size,
826 available_indexes: sst_info.index_metadata.build_available_indexes(),
827 indexes: sst_info.index_metadata.build_indexes(),
828 index_file_size: sst_info.index_metadata.file_size,
829 index_version: 0,
830 num_rows: sst_info.num_rows as u64,
831 num_row_groups: sst_info.num_row_groups,
832 sequence: NonZeroU64::new(max_sequence),
833 partition_expr,
834 num_series: sst_info.num_series,
835 primary_key_min,
836 primary_key_max,
837 preserve_row_sequence,
838 }
839 }
840
841 fn new_write_request(
842 &self,
843 version: &VersionRef,
844 max_sequence: u64,
845 source: FlatSource,
846 ) -> SstWriteRequest {
847 let flat_format = version
848 .options
849 .sst_format
850 .map(|f| f == FormatType::Flat)
851 .unwrap_or(self.engine_config.default_flat_format);
852 SstWriteRequest {
853 op_type: OperationType::Flush,
854 metadata: version.metadata.clone(),
855 source,
856 cache_manager: self.cache_manager.clone(),
857 max_sequence: (version.options.merge_mode() != MergeMode::LastNonNull)
860 .then_some(max_sequence),
861 sst_write_format: if flat_format {
862 FormatType::Flat
863 } else {
864 FormatType::PrimaryKey
865 },
866 preserve_row_sequence: version.options.preserve_row_sequence,
867 index_options: self.index_options.clone(),
868 index_config: self.engine_config.index.clone(),
869 inverted_index_config: self.engine_config.inverted_index.clone(),
870 fulltext_index_config: self.engine_config.fulltext_index.clone(),
871 bloom_filter_index_config: self.engine_config.bloom_filter_index.clone(),
872 }
873 }
874
875 pub(crate) async fn send_worker_request(&self, request: WorkerRequest) {
877 if let Err(e) = self
878 .request_sender
879 .send(WorkerRequestWithTime::new(request))
880 .await
881 {
882 let request = e.0.request;
883 error!(
884 "Failed to notify flush job status for region {}, request: {:?}",
885 self.region_id, request
886 );
887 if let WorkerRequest::Background {
888 notify: BackgroundNotify::FlushFinished(mut finished),
889 ..
890 } = request
891 {
892 finished.on_failure(
893 RegionClosedSnafu {
894 region_id: self.region_id,
895 }
896 .build(),
897 );
898 }
899 }
900 }
901
902 fn merge(&mut self, mut other: RegionFlushTask) {
904 assert_eq!(self.region_id, other.region_id);
905 self.senders.append(&mut other.senders);
907 }
908}
909
910struct FlushFlatMemResult {
911 num_encoded: usize,
912 num_sources: usize,
913 results: Vec<Result<(SequenceNumber, SstInfoArray, Metrics)>>,
914}
915
916struct DoFlushMemtablesResult {
917 file_metas: Vec<FileMeta>,
918 flushed_bytes: u64,
919 series_count: usize,
920 encoded_part_count: usize,
921 flush_metrics: Metrics,
922 sst_infos: Vec<SstInfo>,
923}
924
925struct FlatSources {
926 sources: SmallVec<[(FlatSource, SequenceNumber); 4]>,
927 encoded: SmallVec<[(EncodedRange, SequenceNumber); 4]>,
928}
929
930fn memtable_flat_sources(
932 schema: SchemaRef,
933 mem_ranges: MemtableRanges,
934 metadata: &RegionMetadataRef,
935 options: &RegionOptions,
936 field_column_start: usize,
937) -> Result<FlatSources> {
938 let components = group_by_sequence(
939 mem_ranges.ranges.into_values().collect(),
940 |range: &MemtableRange| {
941 let stats = range.stats();
942 (stats.min_sequence, stats.max_sequence())
943 },
944 );
945 let mut flat_sources = FlatSources {
946 sources: SmallVec::new(),
947 encoded: SmallVec::new(),
948 };
949 let mut rows_remaining = components
950 .iter()
951 .filter(|component| component.len() != 1 || component[0].encoded().is_none())
952 .flatten()
953 .map(MemtableRange::num_rows)
954 .sum::<usize>();
955 let mut pending = Vec::new();
956 let mut pending_rows = 0;
957 for component in components {
958 if component.len() == 1
961 && let Some(encoded) = component[0].encoded()
962 {
963 if !pending.is_empty() {
964 flat_sources.sources.push(build_flush_source(
965 &schema,
966 std::mem::take(&mut pending),
967 metadata,
968 options,
969 field_column_start,
970 )?);
971 pending_rows = 0;
972 }
973 flat_sources
974 .encoded
975 .push((encoded, component[0].stats().max_sequence()));
976 continue;
977 }
978 let component_rows = component.iter().map(MemtableRange::num_rows).sum::<usize>();
979 pending_rows += component_rows;
980 rows_remaining -= component_rows;
981 pending.extend(component);
982 if pending_rows >= *ENCODE_ROW_THRESHOLD
985 && (rows_remaining == 0 || rows_remaining >= DEFAULT_ROW_GROUP_SIZE)
986 {
987 flat_sources.sources.push(build_flush_source(
988 &schema,
989 std::mem::take(&mut pending),
990 metadata,
991 options,
992 field_column_start,
993 )?);
994 pending_rows = 0;
995 }
996 }
997 if !pending.is_empty() {
998 flat_sources.sources.push(build_flush_source(
999 &schema,
1000 pending,
1001 metadata,
1002 options,
1003 field_column_start,
1004 )?);
1005 }
1006 Ok(flat_sources)
1007}
1008
1009fn build_flush_source(
1012 schema: &SchemaRef,
1013 ranges: Vec<MemtableRange>,
1014 metadata: &RegionMetadataRef,
1015 options: &RegionOptions,
1016 field_column_start: usize,
1017) -> Result<(FlatSource, SequenceNumber)> {
1018 let max_sequence = ranges
1019 .iter()
1020 .map(|range| range.stats().max_sequence())
1021 .max()
1022 .unwrap_or(0);
1023 if let [range] = ranges.as_slice() {
1024 let schema = range
1026 .record_batch_schema_hint()
1027 .unwrap_or_else(|| schema.clone());
1028 let iter = maybe_dedup_one(
1029 options.append_mode,
1030 options.merge_mode(),
1031 field_column_start,
1032 range.build_record_batch_iter(None, None)?,
1033 );
1034 return Ok((FlatSource::new_iter(schema, iter), max_sequence));
1035 }
1036 let batch_size = crate::batch_size::estimate_batch_size(ranges.iter().map(|range| {
1037 let stats = range.stats();
1038 (stats.num_rows() as u64, stats.bytes_allocated() as u64)
1039 }));
1040 let schemas = ranges
1041 .iter()
1042 .map(|range| {
1043 (
1044 range
1045 .record_batch_schema_hint()
1046 .unwrap_or_else(|| schema.clone()),
1047 range.num_rows() as u64,
1048 )
1049 })
1050 .collect::<Vec<_>>();
1051 let plans = Arc::new(collect_json2_rewrite_plans(metadata, &schemas)?);
1052 let schema = rewrite_json2_schema(
1053 schemas.first().map(|(schema, _)| schema).unwrap_or(schema),
1054 &plans,
1055 );
1056 let mut iters = Vec::with_capacity(ranges.len());
1057 for range in ranges {
1058 if let Some(actual) = range.record_batch_schema_hint() {
1059 let actual = rewrite_json2_schema(&actual, &plans);
1060 ensure!(
1061 actual == schema,
1062 UnexpectedSnafu {
1063 reason: format!(
1064 "Different schemas found in a MemtableRanges, expected: {}, actual: {}",
1065 schema, actual
1066 ),
1067 }
1068 );
1069 }
1070 let iter = range.build_record_batch_iter(None, None)?;
1071 let iter: BoxedRecordBatchIterator = if plans.is_empty() {
1072 iter
1073 } else {
1074 let plans = plans.clone();
1075 Box::new(iter.map(move |batch| rewrite_json2_batch(batch?, &plans)))
1076 };
1077 iters.push(iter);
1078 }
1079 let iter = merge_and_dedup_with_batch_size(
1080 &schema,
1081 options.append_mode,
1082 options.merge_mode(),
1083 field_column_start,
1084 iters,
1085 batch_size,
1086 )?;
1087 Ok((FlatSource::new_iter(schema, iter), max_sequence))
1088}
1089
1090pub fn merge_and_dedup(
1135 schema: &SchemaRef,
1136 append_mode: bool,
1137 merge_mode: MergeMode,
1138 field_column_start: usize,
1139 input_iters: Vec<BoxedRecordBatchIterator>,
1140) -> Result<BoxedRecordBatchIterator> {
1141 merge_and_dedup_with_batch_size(
1142 schema,
1143 append_mode,
1144 merge_mode,
1145 field_column_start,
1146 input_iters,
1147 DEFAULT_READ_BATCH_SIZE,
1148 )
1149}
1150
1151pub fn merge_and_dedup_with_batch_size(
1157 schema: &SchemaRef,
1158 append_mode: bool,
1159 merge_mode: MergeMode,
1160 field_column_start: usize,
1161 input_iters: Vec<BoxedRecordBatchIterator>,
1162 batch_size: usize,
1163) -> Result<BoxedRecordBatchIterator> {
1164 let batch_size = batch_size.max(1);
1165 let merge_iter = FlatMergeIterator::new(schema.clone(), input_iters, batch_size)?;
1166 let maybe_dedup = if append_mode {
1167 Box::new(merge_iter) as _
1169 } else {
1170 match merge_mode {
1172 MergeMode::LastRow => {
1173 Box::new(FlatDedupIterator::new(merge_iter, FlatLastRow::new(false))) as _
1174 }
1175 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1176 merge_iter,
1177 FlatLastNonNull::new(field_column_start, false),
1178 )) as _,
1179 }
1180 };
1181 Ok(maybe_dedup)
1182}
1183
1184pub fn maybe_dedup_one(
1185 append_mode: bool,
1186 merge_mode: MergeMode,
1187 field_column_start: usize,
1188 input_iter: BoxedRecordBatchIterator,
1189) -> BoxedRecordBatchIterator {
1190 if append_mode {
1191 input_iter
1193 } else {
1194 match merge_mode {
1196 MergeMode::LastRow => {
1197 Box::new(FlatDedupIterator::new(input_iter, FlatLastRow::new(false)))
1198 }
1199 MergeMode::LastNonNull => Box::new(FlatDedupIterator::new(
1200 input_iter,
1201 FlatLastNonNull::new(field_column_start, false),
1202 )),
1203 }
1204 }
1205}
1206
1207pub(crate) struct FlushScheduler {
1209 region_status: HashMap<RegionId, FlushStatus>,
1211 scheduler: SchedulerRef,
1213}
1214
1215impl FlushScheduler {
1216 pub(crate) fn new(scheduler: SchedulerRef) -> FlushScheduler {
1218 FlushScheduler {
1219 region_status: HashMap::new(),
1220 scheduler,
1221 }
1222 }
1223
1224 pub(crate) fn is_flush_requested(&self, region_id: RegionId) -> bool {
1226 self.region_status.contains_key(®ion_id)
1227 }
1228
1229 fn schedule_flush_task(
1230 &mut self,
1231 version_control: &VersionControlRef,
1232 mut task: RegionFlushTask,
1233 ) -> Result<CancellableTaskState> {
1234 let region_id = task.region_id;
1235
1236 if let Err(e) = version_control.freeze_mutable() {
1238 error!(e; "Failed to freeze the mutable memtable for region {}", region_id);
1239 task.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1240
1241 return Err(e);
1242 }
1243 let state = CancellableTaskState::new();
1245 let (job, waiters) = task.into_flush_job(version_control, state.clone());
1246 if let Err(e) = self.scheduler.schedule(job) {
1247 error!(e; "Failed to schedule flush job for region {}", region_id);
1248 waiters.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
1249
1250 return Err(e);
1251 }
1252 Ok(state)
1253 }
1254
1255 pub(crate) fn schedule_flush(
1257 &mut self,
1258 region_id: RegionId,
1259 version_control: &VersionControlRef,
1260 mut task: RegionFlushTask,
1261 ) -> Result<()> {
1262 debug_assert_eq!(region_id, task.region_id);
1263
1264 let version = version_control.current().version;
1265 if version.memtables.is_empty() {
1266 debug_assert!(!self.region_status.contains_key(®ion_id));
1267 task.on_success();
1269 return Ok(());
1270 }
1271
1272 FLUSH_REQUESTS_TOTAL
1274 .with_label_values(&[task.reason.as_str()])
1275 .inc();
1276
1277 if let Some(flush_status) = self.region_status.get_mut(®ion_id) {
1279 if flush_status.has_pending_lifecycle_ddl() {
1280 task.on_failure(Arc::new(FlushCancelledSnafu.build()));
1281 return Ok(());
1282 }
1283 debug!("Merging flush task for region {}", region_id);
1285 flush_status.merge_task(task);
1286 return Ok(());
1287 }
1288
1289 let closing = task.reason == FlushReason::Closing;
1290 let state = self.schedule_flush_task(version_control, task)?;
1291
1292 let _ = self.region_status.insert(
1294 region_id,
1295 FlushStatus::new(region_id, version_control.clone(), state, closing),
1296 );
1297
1298 Ok(())
1299 }
1300
1301 pub(crate) fn on_flush_success(
1305 &mut self,
1306 region_id: RegionId,
1307 ) -> Option<(
1308 Vec<SenderDdlRequest>,
1309 Vec<SenderWriteRequest>,
1310 Vec<SenderBulkRequest>,
1311 )> {
1312 let flush_status = self.region_status.get_mut(®ion_id)?;
1313 if flush_status.pending_task.is_none() {
1315 debug!(
1318 "Region {} doesn't have any pending flush task, removing it from the status",
1319 region_id
1320 );
1321 let flush_status = self.region_status.remove(®ion_id).unwrap();
1322 return Some((
1323 flush_status.pending_ddls,
1324 flush_status.pending_writes,
1325 flush_status.pending_bulk_writes,
1326 ));
1327 }
1328
1329 let version_data = flush_status.version_control.current();
1331 if version_data.version.memtables.is_empty() {
1332 let task = flush_status.pending_task.take().unwrap();
1335 task.on_success();
1337 debug!(
1338 "Region {} has nothing to flush, removing it from the status",
1339 region_id
1340 );
1341 let flush_status = self.region_status.remove(®ion_id).unwrap();
1343 return Some((
1344 flush_status.pending_ddls,
1345 flush_status.pending_writes,
1346 flush_status.pending_bulk_writes,
1347 ));
1348 }
1349
1350 debug!("Scheduling pending flush task for region {}", region_id);
1352 let task = flush_status.pending_task.take().unwrap();
1354 let version_control = flush_status.version_control.clone();
1355 match self.schedule_flush_task(&version_control, task) {
1356 Ok(state) => {
1357 self.region_status.get_mut(®ion_id).unwrap().state = state;
1358 }
1359 Err(err) => {
1360 error!(
1361 err;
1362 "Flush succeeded for region {region_id}, but failed to schedule next flush for it."
1363 );
1364 let flush_status = self.region_status.remove(®ion_id).unwrap();
1365 flush_status.fail_all(Arc::new(RegionBusySnafu { region_id }.build()));
1366 return None;
1367 }
1368 }
1369 None
1371 }
1372
1373 pub(crate) fn on_flush_failed(
1378 &mut self,
1379 region_id: RegionId,
1380 err: Arc<Error>,
1381 ) -> Vec<SenderDdlRequest> {
1382 if matches!(err.as_ref(), Error::FlushCancelled { .. }) {
1383 info!("Region {} flush was cancelled", region_id);
1384 } else {
1385 error!(err; "Region {} failed to flush, cancel all pending tasks", region_id);
1386 FLUSH_FAILURE_TOTAL.inc();
1387 }
1388
1389 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1391 return Vec::new();
1392 };
1393
1394 flush_status.on_failure(err)
1395 }
1396
1397 pub(crate) fn try_cancel_and_add_ddl<T>(
1399 &mut self,
1400 region_id: RegionId,
1401 sender: OptionOutputTx,
1402 request: T,
1403 into_ddl_request: impl FnOnce(T) -> DdlRequest,
1404 ) -> std::result::Result<(), (OptionOutputTx, T)> {
1405 let Some(status) = self.region_status.get_mut(®ion_id) else {
1406 return Err((sender, request));
1407 };
1408
1409 let cancel_result = status.state.request_cancel();
1410 debug!(
1411 "Requested flush cancellation for region {}, result: {:?}",
1412 region_id, cancel_result
1413 );
1414 status.pending_ddls.push(SenderDdlRequest {
1415 region_id,
1416 sender,
1417 request: into_ddl_request(request),
1418 });
1419 Ok(())
1420 }
1421
1422 pub(crate) fn on_region_dropped(&mut self, region_id: RegionId) {
1424 self.remove_region_on_failure(
1425 region_id,
1426 Arc::new(RegionDroppedSnafu { region_id }.build()),
1427 );
1428 }
1429
1430 pub(crate) fn on_region_closed(&mut self, region_id: RegionId) {
1432 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1433 return;
1434 };
1435
1436 flush_status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
1437 }
1438
1439 pub(crate) fn on_region_truncated(&mut self, region_id: RegionId) {
1441 self.remove_region_on_failure(
1442 region_id,
1443 Arc::new(RegionTruncatedSnafu { region_id }.build()),
1444 );
1445 }
1446
1447 fn remove_region_on_failure(&mut self, region_id: RegionId, err: Arc<Error>) {
1448 let Some(flush_status) = self.region_status.remove(®ion_id) else {
1450 return;
1451 };
1452
1453 flush_status.fail_all(err);
1455 }
1456
1457 pub(crate) fn add_ddl_request_to_pending(&mut self, request: SenderDdlRequest) {
1462 let status = self.region_status.get_mut(&request.region_id).unwrap();
1463 status.pending_ddls.push(request);
1464 }
1465
1466 pub(crate) fn add_write_request_to_pending(&mut self, request: SenderWriteRequest) {
1471 let status = self
1472 .region_status
1473 .get_mut(&request.request.region_id)
1474 .unwrap();
1475 status.pending_writes.push(request);
1476 }
1477
1478 pub(crate) fn add_bulk_request_to_pending(&mut self, request: SenderBulkRequest) {
1483 let status = self.region_status.get_mut(&request.region_id).unwrap();
1484 status.pending_bulk_writes.push(request);
1485 }
1486
1487 pub(crate) fn has_pending_alter(&self, region_id: RegionId) -> bool {
1489 self.region_status.get(®ion_id).is_some_and(|status| {
1490 status
1491 .pending_ddls
1492 .iter()
1493 .any(|ddl| matches!(ddl.request, DdlRequest::Alter(_)))
1494 })
1495 }
1496
1497 pub(crate) fn has_close_time_flush(&self, region_id: RegionId) -> bool {
1499 self.region_status
1500 .get(®ion_id)
1501 .is_some_and(|status| status.closing)
1502 }
1503
1504 pub(crate) fn has_pending_ddls(&self, region_id: RegionId) -> bool {
1506 self.region_status
1507 .get(®ion_id)
1508 .map(|status| !status.pending_ddls.is_empty() || status.closing)
1509 .unwrap_or(false)
1510 }
1511}
1512
1513impl Drop for FlushScheduler {
1514 fn drop(&mut self) {
1515 for (region_id, flush_status) in self.region_status.drain() {
1516 flush_status.fail_all(Arc::new(RegionClosedSnafu { region_id }.build()));
1518 }
1519 }
1520}
1521
1522struct FlushStatus {
1526 region_id: RegionId,
1528 version_control: VersionControlRef,
1530 state: CancellableTaskState,
1532 pending_task: Option<RegionFlushTask>,
1534 closing: bool,
1536 pending_ddls: Vec<SenderDdlRequest>,
1538 pending_writes: Vec<SenderWriteRequest>,
1540 pending_bulk_writes: Vec<SenderBulkRequest>,
1542}
1543
1544impl FlushStatus {
1545 fn new(
1546 region_id: RegionId,
1547 version_control: VersionControlRef,
1548 state: CancellableTaskState,
1549 closing: bool,
1550 ) -> FlushStatus {
1551 FlushStatus {
1552 region_id,
1553 version_control,
1554 state,
1555 pending_task: None,
1556 closing,
1557 pending_ddls: Vec::new(),
1558 pending_writes: Vec::new(),
1559 pending_bulk_writes: Vec::new(),
1560 }
1561 }
1562
1563 fn merge_task(&mut self, task: RegionFlushTask) {
1565 self.closing |= task.reason == FlushReason::Closing;
1566 if let Some(pending) = &mut self.pending_task {
1567 pending.merge(task);
1568 } else {
1569 self.pending_task = Some(task);
1570 }
1571 }
1572
1573 fn has_pending_lifecycle_ddl(&self) -> bool {
1574 self.pending_ddls
1575 .iter()
1576 .any(|ddl| matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)))
1577 }
1578
1579 fn on_failure(self, err: Arc<Error>) -> Vec<SenderDdlRequest> {
1581 if let Some(mut task) = self.pending_task {
1582 task.on_failure(err.clone());
1583 }
1584 let mut lifecycle_ddls = Vec::new();
1585 for ddl in self.pending_ddls {
1586 if matches!(ddl.request, DdlRequest::Drop(_) | DdlRequest::Truncate(_)) {
1587 lifecycle_ddls.push(ddl);
1588 } else {
1589 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1590 region_id: self.region_id,
1591 }));
1592 }
1593 }
1594 for write_req in self.pending_writes {
1595 write_req
1596 .sender
1597 .send(Err(err.clone()).context(FlushRegionSnafu {
1598 region_id: self.region_id,
1599 }));
1600 }
1601 for bulk_req in self.pending_bulk_writes {
1602 bulk_req
1603 .sender
1604 .send(Err(err.clone()).context(FlushRegionSnafu {
1605 region_id: self.region_id,
1606 }));
1607 }
1608 lifecycle_ddls
1609 }
1610
1611 fn fail_all(self, err: Arc<Error>) {
1612 let region_id = self.region_id;
1613 let ddls = self.on_failure(err.clone());
1614 for ddl in ddls {
1615 ddl.sender
1616 .send(Err(err.clone()).context(FlushRegionSnafu { region_id }));
1617 }
1618 }
1619
1620 fn on_region_closed(self, err: Arc<Error>) {
1621 if let Some(mut task) = self.pending_task {
1622 task.on_failure(err.clone());
1623 }
1624
1625 for ddl in self.pending_ddls {
1626 if matches!(ddl.request, DdlRequest::Close(_)) {
1627 ddl.sender.send(Ok(0));
1628 } else {
1629 ddl.sender.send(Err(err.clone()).context(FlushRegionSnafu {
1630 region_id: self.region_id,
1631 }));
1632 }
1633 }
1634
1635 for write_req in self.pending_writes {
1636 write_req
1637 .sender
1638 .send(Err(err.clone()).context(FlushRegionSnafu {
1639 region_id: self.region_id,
1640 }));
1641 }
1642 for bulk_req in self.pending_bulk_writes {
1643 bulk_req
1644 .sender
1645 .send(Err(err.clone()).context(FlushRegionSnafu {
1646 region_id: self.region_id,
1647 }));
1648 }
1649 }
1650}
1651
1652#[cfg(test)]
1653mod tests {
1654 use api::v1::{OpType, Rows};
1655 use common_error::ext::{ErrorExt, RetryHint};
1656 use common_error::status_code::StatusCode;
1657 use datatypes::arrow::datatypes::Schema;
1658 use datatypes::arrow::record_batch::RecordBatch;
1659 use mito_codec::row_converter::build_primary_key_codec;
1660 use tokio::sync::oneshot;
1661
1662 use super::*;
1663 use crate::cache::CacheManager;
1664 use crate::error::InvalidSchedulerStateSnafu;
1665 use crate::memtable::bulk::part::BulkPartConverter;
1666 use crate::memtable::time_series::TimeSeriesMemtableBuilder;
1667 use crate::memtable::{
1668 IterBuilder, Memtable, MemtableRange, MemtableRangeContext, MemtableStats, RangesOptions,
1669 };
1670 use crate::request::WriteRequest;
1671 use crate::schedule::scheduler::Scheduler;
1672 use crate::sst::{FlatSchemaOptions, to_flat_sst_arrow_schema};
1673 use crate::test_util::memtable_util::{build_key_values_with_ts_seq_values, metadata_for_test};
1674 use crate::test_util::scheduler_util::{SchedulerEnv, VecScheduler};
1675 use crate::test_util::version_util::{VersionControlBuilder, write_rows_to_version};
1676
1677 struct FailingScheduler;
1678
1679 #[async_trait::async_trait]
1680 impl Scheduler for FailingScheduler {
1681 fn schedule(&self, _job: Job) -> Result<()> {
1682 InvalidSchedulerStateSnafu.fail()
1683 }
1684
1685 async fn stop(&self, _await_termination: bool) -> Result<()> {
1686 Ok(())
1687 }
1688 }
1689
1690 fn new_test_flush_task(
1691 env: &SchedulerEnv,
1692 region_id: RegionId,
1693 reason: FlushReason,
1694 request_sender: mpsc::Sender<WorkerRequestWithTime>,
1695 manifest_ctx: ManifestContextRef,
1696 ) -> RegionFlushTask {
1697 RegionFlushTask {
1698 region_id,
1699 reason,
1700 senders: Vec::new(),
1701 request_sender,
1702 access_layer: env.access_layer.clone(),
1703 listener: WorkerListener::default(),
1704 engine_config: Arc::new(MitoConfig::default()),
1705 row_group_size: None,
1706 cache_manager: Arc::new(CacheManager::default()),
1707 manifest_ctx,
1708 index_options: IndexOptions::default(),
1709 flush_semaphore: Arc::new(Semaphore::new(2)),
1710 partition_expr: None,
1711 durability_barrier: DurabilityBarrier::noop(),
1712 }
1713 }
1714
1715 fn new_test_bulk_request(
1716 region_id: RegionId,
1717 ) -> (
1718 SenderBulkRequest,
1719 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1720 ) {
1721 let metadata = metadata_for_test();
1722 let schema = to_flat_sst_arrow_schema(
1723 &metadata,
1724 &FlatSchemaOptions::from_encoding(metadata.primary_key_encoding),
1725 );
1726 let pk_codec = build_primary_key_codec(&metadata);
1727 let mut converter = BulkPartConverter::new(&metadata, schema, 16, pk_codec, true);
1728 let kvs = build_key_values_with_ts_seq_values(
1729 &metadata,
1730 "bulk_key".to_string(),
1731 1,
1732 std::iter::once(1000i64),
1733 std::iter::once(Some(1.0f64)),
1734 1,
1735 );
1736 converter.append_key_values(&kvs).unwrap();
1737 let (sender, receiver) = oneshot::channel();
1738
1739 (
1740 SenderBulkRequest {
1741 skip_wal: false,
1742 sender: OptionOutputTx::from(sender),
1743 region_id,
1744 request: converter.convert().unwrap(),
1745 region_metadata: Some(metadata),
1746 partition_expr_version: None,
1747 },
1748 receiver,
1749 )
1750 }
1751
1752 fn new_test_write_request(
1753 region_id: RegionId,
1754 ) -> (
1755 SenderWriteRequest,
1756 oneshot::Receiver<Result<store_api::region_request::AffectedRows>>,
1757 ) {
1758 let (sender, receiver) = oneshot::channel();
1759 let request = WriteRequest::new(region_id, OpType::Put, Rows::default(), None).unwrap();
1760 (
1761 SenderWriteRequest {
1762 sender: OptionOutputTx::from(sender),
1763 request,
1764 },
1765 receiver,
1766 )
1767 }
1768
1769 #[test]
1770 fn test_get_mutable_limit() {
1771 assert_eq!(4, WriteBufferManagerImpl::get_mutable_limit(8));
1772 assert_eq!(5, WriteBufferManagerImpl::get_mutable_limit(10));
1773 assert_eq!(32, WriteBufferManagerImpl::get_mutable_limit(64));
1774 assert_eq!(0, WriteBufferManagerImpl::get_mutable_limit(0));
1775 }
1776
1777 #[test]
1778 fn test_over_mutable_limit() {
1779 let manager = WriteBufferManagerImpl::new(1000);
1781 manager.reserve_mem(400);
1782 assert!(!manager.should_flush_engine());
1783 assert!(!manager.should_stall());
1784
1785 manager.reserve_mem(400);
1787 assert!(manager.should_flush_engine());
1788
1789 manager.schedule_free_mem(400);
1791 assert!(!manager.should_flush_engine());
1792 assert_eq!(800, manager.memory_used.load(Ordering::Relaxed));
1793 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1794
1795 manager.free_mem(400);
1797 assert_eq!(400, manager.memory_used.load(Ordering::Relaxed));
1798 assert_eq!(400, manager.memory_active.load(Ordering::Relaxed));
1799 }
1800
1801 #[test]
1802 fn test_over_global() {
1803 let manager = WriteBufferManagerImpl::new(1000);
1805 manager.reserve_mem(1100);
1806 assert!(manager.should_stall());
1807 manager.schedule_free_mem(200);
1809 assert!(manager.should_flush_engine());
1810 assert!(manager.should_stall());
1811
1812 manager.schedule_free_mem(450);
1814 assert!(manager.should_flush_engine());
1815 assert!(manager.should_stall());
1816
1817 manager.reserve_mem(50);
1819 assert!(manager.should_flush_engine());
1820 manager.reserve_mem(100);
1821 assert!(manager.should_flush_engine());
1822 }
1823
1824 #[test]
1825 fn test_manager_notify() {
1826 let (sender, receiver) = watch::channel(());
1827 let manager = WriteBufferManagerImpl::new(1000).with_notifier(sender);
1828 manager.reserve_mem(500);
1829 assert!(!receiver.has_changed().unwrap());
1830 manager.schedule_free_mem(500);
1831 assert!(!receiver.has_changed().unwrap());
1832 manager.free_mem(500);
1833 assert!(receiver.has_changed().unwrap());
1834 }
1835
1836 #[tokio::test]
1837 async fn test_schedule_empty() {
1838 let env = SchedulerEnv::new().await;
1839 let (tx, _rx) = mpsc::channel(4);
1840 let mut scheduler = env.mock_flush_scheduler();
1841 let builder = VersionControlBuilder::new();
1842
1843 let version_control = Arc::new(builder.build());
1844 let (output_tx, output_rx) = oneshot::channel();
1845 let mut task = RegionFlushTask {
1846 region_id: builder.region_id(),
1847 reason: FlushReason::Manual,
1848 senders: Vec::new(),
1849 request_sender: tx,
1850 access_layer: env.access_layer.clone(),
1851 listener: WorkerListener::default(),
1852 engine_config: Arc::new(MitoConfig::default()),
1853 row_group_size: None,
1854 cache_manager: Arc::new(CacheManager::default()),
1855 manifest_ctx: env
1856 .mock_manifest_context(version_control.current().version.metadata.clone())
1857 .await,
1858 index_options: IndexOptions::default(),
1859 flush_semaphore: Arc::new(Semaphore::new(2)),
1860 partition_expr: None,
1861 durability_barrier: DurabilityBarrier::noop(),
1862 };
1863 task.push_sender(OptionOutputTx::from(output_tx));
1864 scheduler
1865 .schedule_flush(builder.region_id(), &version_control, task)
1866 .unwrap();
1867 assert!(scheduler.region_status.is_empty());
1868 let output = output_rx.await.unwrap().unwrap();
1869 assert_eq!(output, 0);
1870 assert!(scheduler.region_status.is_empty());
1871 }
1872
1873 #[tokio::test]
1874 async fn test_schedule_flush_failure_notifies_waiter() {
1875 let env = SchedulerEnv::new()
1876 .await
1877 .scheduler(Arc::new(FailingScheduler));
1878 let (tx, _rx) = mpsc::channel(4);
1879 let mut scheduler = env.mock_flush_scheduler();
1880 let mut builder = VersionControlBuilder::new();
1881 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
1882 let version_control = Arc::new(builder.build());
1883 let version_data = version_control.current();
1884 write_rows_to_version(&version_data.version, "host0", 0, 10);
1885 let manifest_ctx = env
1886 .mock_manifest_context(version_data.version.metadata.clone())
1887 .await;
1888 let (output_tx, output_rx) = oneshot::channel();
1889 let mut task = new_test_flush_task(
1890 &env,
1891 builder.region_id(),
1892 FlushReason::Manual,
1893 tx,
1894 manifest_ctx,
1895 );
1896 task.push_sender(OptionOutputTx::from(output_tx));
1897
1898 scheduler
1899 .schedule_flush(builder.region_id(), &version_control, task)
1900 .unwrap_err();
1901
1902 let err = output_rx
1903 .await
1904 .expect("waiter must receive explicit error")
1905 .unwrap_err();
1906 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1907 }
1908
1909 #[tokio::test]
1910 async fn test_send_worker_request_failure_notifies_flush_finished_waiter() {
1911 let env = SchedulerEnv::new().await;
1912 let (tx, rx) = mpsc::channel(1);
1913 drop(rx);
1914 let builder = VersionControlBuilder::new();
1915 let version_control = Arc::new(builder.build());
1916 let manifest_ctx = env
1917 .mock_manifest_context(version_control.current().version.metadata.clone())
1918 .await;
1919 let task = new_test_flush_task(
1920 &env,
1921 builder.region_id(),
1922 FlushReason::Manual,
1923 tx,
1924 manifest_ctx,
1925 );
1926 let (output_tx, output_rx) = oneshot::channel();
1927 let request = WorkerRequest::Background {
1928 region_id: builder.region_id(),
1929 notify: BackgroundNotify::FlushFinished(FlushFinished {
1930 region_id: builder.region_id(),
1931 flush_reason: FlushReason::Manual,
1932 flushed_entry_id: 0,
1933 senders: vec![OutputTx::new(output_tx)],
1934 _timer: FLUSH_ELAPSED.with_label_values(&["total"]).start_timer(),
1935 edit: RegionEdit {
1936 files_to_add: Vec::new(),
1937 files_to_remove: Vec::new(),
1938 timestamp_ms: None,
1939 compaction_time_window: None,
1940 flushed_entry_id: None,
1941 flushed_sequence: None,
1942 committed_sequence: None,
1943 },
1944 memtables_to_remove: smallvec![],
1945 }),
1946 };
1947
1948 task.send_worker_request(request).await;
1949
1950 let output = output_rx.await.expect("waiter must receive explicit error");
1951 assert!(output.is_err());
1952 }
1953
1954 #[tokio::test]
1955 async fn test_flush_waiters_drop_notifies_waiter() {
1956 let region_id = RegionId::new(1, 1);
1957 let (output_tx, output_rx) = oneshot::channel();
1958 let waiters = FlushTaskWaiters::new(region_id, vec![OutputTx::new(output_tx)]);
1959
1960 drop(waiters);
1961
1962 let err = output_rx
1963 .await
1964 .expect("waiter must receive explicit error")
1965 .unwrap_err();
1966 assert_eq!(err.status_code(), StatusCode::RegionBusy);
1967 }
1968
1969 #[tokio::test]
1970 async fn test_flush_failure_notifies_pending_bulk_writes() {
1971 let region_id = RegionId::new(1, 1);
1972 let version_control = Arc::new(VersionControlBuilder::new().build());
1973 let (bulk_req, output_rx) = new_test_bulk_request(region_id);
1974 let status = FlushStatus {
1975 region_id,
1976 version_control,
1977 state: CancellableTaskState::new(),
1978 pending_task: None,
1979 closing: false,
1980 pending_ddls: Vec::new(),
1981 pending_writes: Vec::new(),
1982 pending_bulk_writes: vec![bulk_req],
1983 };
1984
1985 let pending_ddls = status.on_failure(Arc::new(RegionClosedSnafu { region_id }.build()));
1986 assert!(pending_ddls.is_empty());
1987
1988 let err = output_rx
1989 .await
1990 .expect("pending bulk write must receive explicit error")
1991 .unwrap_err();
1992 assert_eq!(err.status_code(), StatusCode::Cancelled);
1993 }
1994
1995 #[tokio::test]
1996 async fn test_flush_failure_retains_lifecycle_ddls_and_fails_pending_writes() {
1997 let region_id = RegionId::new(1, 1);
1998 let version_control = Arc::new(VersionControlBuilder::new().build());
1999 let (write_req, write_rx) = new_test_write_request(region_id);
2000 let (bulk_req, bulk_rx) = new_test_bulk_request(region_id);
2001 let (truncate_tx, mut truncate_rx) = oneshot::channel();
2002 let (drop_tx, mut drop_rx) = oneshot::channel();
2003 let status = FlushStatus {
2004 region_id,
2005 version_control,
2006 state: CancellableTaskState::new(),
2007 pending_task: None,
2008 closing: false,
2009 pending_ddls: vec![
2010 SenderDdlRequest {
2011 region_id,
2012 sender: OptionOutputTx::from(truncate_tx),
2013 request: DdlRequest::Truncate(
2014 store_api::region_request::RegionTruncateRequest::All,
2015 ),
2016 },
2017 SenderDdlRequest {
2018 region_id,
2019 sender: OptionOutputTx::from(drop_tx),
2020 request: DdlRequest::Drop(store_api::region_request::RegionDropRequest {
2021 fast_path: false,
2022 force: false,
2023 partial_drop: false,
2024 }),
2025 },
2026 ],
2027 pending_writes: vec![write_req],
2028 pending_bulk_writes: vec![bulk_req],
2029 };
2030
2031 let ddls = status.on_failure(Arc::new(RegionBusySnafu { region_id }.build()));
2032 assert_eq!(2, ddls.len());
2033 assert!(matches!(ddls[0].request, DdlRequest::Truncate(_)));
2034 assert!(matches!(ddls[1].request, DdlRequest::Drop(_)));
2035
2036 let write_err = write_rx
2037 .await
2038 .expect("pending write must receive explicit error")
2039 .unwrap_err();
2040 assert_eq!(write_err.status_code(), StatusCode::RegionBusy);
2041 let bulk_err = bulk_rx
2042 .await
2043 .expect("pending bulk write must receive explicit error")
2044 .unwrap_err();
2045 assert_eq!(bulk_err.status_code(), StatusCode::RegionBusy);
2046
2047 assert!(truncate_rx.try_recv().is_err());
2048 assert!(drop_rx.try_recv().is_err());
2049 for ddl in ddls {
2050 ddl.sender.send(Ok(0));
2051 }
2052 assert_eq!(0, truncate_rx.await.unwrap().unwrap());
2053 assert_eq!(0, drop_rx.await.unwrap().unwrap());
2054 }
2055
2056 #[tokio::test]
2057 async fn test_uncommitted_ssts_cleanup_finalized_file() {
2058 let env = SchedulerEnv::new().await;
2059 let region_id = RegionId::new(1, 1);
2060 let file_id = store_api::storage::FileId::random();
2061 let path = crate::sst::location::sst_file_path(
2062 env.access_layer.table_dir(),
2063 crate::sst::file::RegionFileId::new(region_id, file_id),
2064 env.access_layer.path_type(),
2065 );
2066 env.access_layer
2067 .object_store()
2068 .write(&path, Bytes::from_static(b"sst"))
2069 .await
2070 .unwrap();
2071 assert!(env.access_layer.object_store().exists(&path).await.unwrap());
2072
2073 let uncommitted = UncommittedSsts::new(region_id, env.access_layer.clone(), None);
2074 uncommitted.track(&[SstInfo {
2075 file_id,
2076 ..Default::default()
2077 }]);
2078 assert_eq!(1, uncommitted.num_tracked_files());
2079 uncommitted.cleanup_for_test().await.unwrap();
2080 assert_eq!(0, uncommitted.num_tracked_files());
2081
2082 let entries = env
2085 .access_layer
2086 .object_store()
2087 .list(&env.access_layer.build_region_dir(region_id))
2088 .await
2089 .unwrap();
2090 assert!(entries.iter().all(|entry| entry.path() != path));
2091 }
2092
2093 #[tokio::test]
2094 async fn test_region_closed_notifies_pending_requests() {
2095 let region_id = RegionId::new(1, 1);
2096 let version_control = Arc::new(VersionControlBuilder::new().build());
2097 let (write_req, write_rx) = new_test_write_request(region_id);
2098 let (bulk_req, bulk_rx) = new_test_bulk_request(region_id);
2099 let (ddl_tx, ddl_rx) = oneshot::channel();
2100 let status = FlushStatus {
2101 region_id,
2102 version_control,
2103 state: CancellableTaskState::new(),
2104 pending_task: None,
2105 closing: false,
2106 pending_ddls: vec![SenderDdlRequest {
2107 region_id,
2108 sender: OptionOutputTx::from(ddl_tx),
2109 request: DdlRequest::Truncate(
2110 store_api::region_request::RegionTruncateRequest::All,
2111 ),
2112 }],
2113 pending_writes: vec![write_req],
2114 pending_bulk_writes: vec![bulk_req],
2115 };
2116
2117 status.on_region_closed(Arc::new(RegionClosedSnafu { region_id }.build()));
2118
2119 for rx in [write_rx, bulk_rx] {
2120 let err = rx.await.unwrap().unwrap_err();
2121 assert_eq!(err.status_code(), StatusCode::Cancelled);
2122 assert_eq!(err.retry_hint(), RetryHint::RetryAfterRouteRefresh);
2123 }
2124 let err = ddl_rx.await.unwrap().unwrap_err();
2125 assert_eq!(err.status_code(), StatusCode::Cancelled);
2126 assert_eq!(err.retry_hint(), RetryHint::RetryAfterRouteRefresh);
2127 }
2128
2129 #[tokio::test]
2130 async fn test_schedule_pending_request() {
2131 let job_scheduler = Arc::new(VecScheduler::default());
2132 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2133 let (tx, _rx) = mpsc::channel(4);
2134 let mut scheduler = env.mock_flush_scheduler();
2135 let mut builder = VersionControlBuilder::new();
2136 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2138 let version_control = Arc::new(builder.build());
2139 let version_data = version_control.current();
2141 write_rows_to_version(&version_data.version, "host0", 0, 10);
2142 let manifest_ctx = env
2143 .mock_manifest_context(version_data.version.metadata.clone())
2144 .await;
2145 let mut tasks: Vec<_> = (0..3)
2147 .map(|_| RegionFlushTask {
2148 region_id: builder.region_id(),
2149 reason: FlushReason::Manual,
2150 senders: Vec::new(),
2151 request_sender: tx.clone(),
2152 access_layer: env.access_layer.clone(),
2153 listener: WorkerListener::default(),
2154 engine_config: Arc::new(MitoConfig::default()),
2155 row_group_size: None,
2156 cache_manager: Arc::new(CacheManager::default()),
2157 manifest_ctx: manifest_ctx.clone(),
2158 index_options: IndexOptions::default(),
2159 flush_semaphore: Arc::new(Semaphore::new(2)),
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 partition_expr: None,
2467 durability_barrier: DurabilityBarrier::noop(),
2468 })
2469 .collect();
2470 let task = tasks.pop().unwrap();
2472 scheduler
2473 .schedule_flush(builder.region_id(), &version_control, task)
2474 .unwrap();
2475 assert_eq!(1, scheduler.region_status.len());
2477 assert_eq!(1, job_scheduler.num_jobs());
2478 let task = tasks.pop().unwrap();
2480 scheduler
2481 .schedule_flush(builder.region_id(), &version_control, task)
2482 .unwrap();
2483 assert!(
2484 scheduler
2485 .region_status
2486 .get(&builder.region_id())
2487 .unwrap()
2488 .pending_task
2489 .is_some()
2490 );
2491
2492 let version_data = version_control.current();
2494 assert_eq!(0, version_data.version.memtables.immutables()[0].id());
2495 version_control.apply_edit(
2497 Some(RegionEdit {
2498 files_to_add: Vec::new(),
2499 files_to_remove: Vec::new(),
2500 timestamp_ms: None,
2501 compaction_time_window: None,
2502 flushed_entry_id: None,
2503 flushed_sequence: None,
2504 committed_sequence: None,
2505 }),
2506 &[0],
2507 builder.file_purger(),
2508 );
2509 write_rows_to_version(&version_data.version, "host1", 0, 10);
2510 scheduler.on_flush_success(builder.region_id());
2511 assert_eq!(2, job_scheduler.num_jobs());
2512 assert!(
2514 scheduler
2515 .region_status
2516 .get(&builder.region_id())
2517 .unwrap()
2518 .pending_task
2519 .is_none()
2520 );
2521 }
2522
2523 #[tokio::test]
2524 async fn test_schedule_pending_request_failure_drains_pending_ddls() {
2525 common_telemetry::init_default_ut_logging();
2526 let job_scheduler = Arc::new(VecScheduler::default());
2527 let env = SchedulerEnv::new().await.scheduler(job_scheduler.clone());
2528 let (tx, _rx) = mpsc::channel(4);
2529 let mut scheduler = env.mock_flush_scheduler();
2530 let mut builder = VersionControlBuilder::new();
2531 builder.set_memtable_builder(Arc::new(TimeSeriesMemtableBuilder::default()));
2532 let version_control = Arc::new(builder.build());
2533
2534 let version_data = version_control.current();
2535 write_rows_to_version(&version_data.version, "host0", 0, 10);
2536 let manifest_ctx = env
2537 .mock_manifest_context(version_data.version.metadata.clone())
2538 .await;
2539
2540 let task = new_test_flush_task(
2541 &env,
2542 builder.region_id(),
2543 FlushReason::Manual,
2544 tx.clone(),
2545 manifest_ctx.clone(),
2546 );
2547 scheduler
2548 .schedule_flush(builder.region_id(), &version_control, task)
2549 .unwrap();
2550
2551 let task = new_test_flush_task(
2552 &env,
2553 builder.region_id(),
2554 FlushReason::Closing,
2555 tx,
2556 manifest_ctx,
2557 );
2558 scheduler
2559 .schedule_flush(builder.region_id(), &version_control, task)
2560 .unwrap();
2561
2562 let (sender, receiver) = oneshot::channel();
2563 scheduler.add_ddl_request_to_pending(SenderDdlRequest {
2564 sender: OptionOutputTx::from(sender),
2565 region_id: builder.region_id(),
2566 request: DdlRequest::Close(store_api::region_request::RegionCloseRequest::default()),
2567 });
2568
2569 let version_data = version_control.current();
2570 version_control.apply_edit(
2571 Some(RegionEdit {
2572 files_to_add: Vec::new(),
2573 files_to_remove: Vec::new(),
2574 timestamp_ms: None,
2575 compaction_time_window: None,
2576 flushed_entry_id: None,
2577 flushed_sequence: None,
2578 committed_sequence: None,
2579 }),
2580 &[0],
2581 builder.file_purger(),
2582 );
2583 write_rows_to_version(&version_data.version, "host1", 0, 10);
2584
2585 scheduler.scheduler = Arc::new(FailingScheduler);
2586 scheduler.on_flush_success(builder.region_id());
2587
2588 assert!(scheduler.region_status.is_empty());
2589 let err = receiver
2590 .await
2591 .expect("pending DDL must be notified")
2592 .unwrap_err();
2593 assert_eq!(err.status_code(), StatusCode::RegionBusy);
2594 }
2595}