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