1mod handle_alter;
18mod handle_apply_staging;
19mod handle_bulk_insert;
20mod handle_catchup;
21mod handle_close;
22mod handle_compaction;
23mod handle_copy_region;
24mod handle_create;
25mod handle_drop;
26mod handle_enter_staging;
27mod handle_flush;
28mod handle_manifest;
29mod handle_open;
30mod handle_rebuild_index;
31mod handle_remap;
32mod handle_truncate;
33mod handle_write;
34
35use std::collections::HashMap;
36use std::path::Path;
37use std::sync::Arc;
38use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
39use std::time::Duration;
40
41use common_base::Plugins;
42use common_error::ext::BoxedError;
43use common_meta::key::SchemaMetadataManagerRef;
44use common_runtime::JoinHandle;
45use common_stat::get_total_memory_bytes;
46use common_telemetry::{error, info, warn};
47use futures::future::{join_all, try_join_all};
48use object_store::ObjectStore;
49use object_store::manager::ObjectStoreManagerRef;
50use prometheus::{Histogram, IntGauge};
51use rand::{Rng, rng};
52use snafu::ResultExt;
53use store_api::logstore::LogStore;
54use store_api::region_engine::{
55 SetRegionRoleStateResponse, SetRegionRoleStateSuccess, SettableRegionRoleState,
56};
57use store_api::storage::{FileId, RegionId};
58use tokio::sync::mpsc::{Receiver, Sender};
59use tokio::sync::{Mutex, Semaphore, mpsc, oneshot, watch};
60
61use crate::access_layer::new_fs_cache_store;
62use crate::cache::write_cache::{WriteCache, WriteCacheRef};
63use crate::cache::{CacheManager, CacheManagerRef, WriteCacheUploadStoreWrapperRef};
64use crate::compaction::CompactionScheduler;
65use crate::compaction::memory_manager::{CompactionMemoryManager, new_compaction_memory_manager};
66use crate::config::MitoConfig;
67use crate::error::{self, CreateDirSnafu, JoinSnafu, Result, WorkerStoppedSnafu};
68use crate::flush::{FlushScheduler, WriteBufferManagerImpl, WriteBufferManagerRef};
69use crate::gc::{GcLimiter, GcLimiterRef};
70use crate::lease::NodeLease;
71use crate::memtable::MemtableBuilderProvider;
72use crate::metrics::{REGION_COUNT, REQUEST_WAIT_TIME, WRITE_STALLING};
73use crate::region::opener::PartitionExprFetcherRef;
74use crate::region::{
75 CatchupRegions, CatchupRegionsRef, MitoRegionRef, OpeningRegions, OpeningRegionsRef, RegionMap,
76 RegionMapRef,
77};
78use crate::request::{
79 BackgroundNotify, BulkInsertRequest, DdlRequest, OptionOutputTx, SenderBulkRequest,
80 SenderDdlRequest, SenderWriteRequest, WorkerRequest, WorkerRequestWithTime,
81};
82use crate::schedule::scheduler::{LocalScheduler, SchedulerRef};
83use crate::series_index::{
84 IndexFilePurger, SeriesIndexTaskState, series_index_channel, spawn_series_index_tasks,
85};
86use crate::sst::file::RegionFileId;
87use crate::sst::file_ref::FileReferenceManagerRef;
88use crate::sst::index::IndexBuildScheduler;
89use crate::sst::index::intermediate::IntermediateManager;
90use crate::sst::index::puffin_manager::PuffinManagerFactory;
91#[cfg(feature = "hdfs-object-store")]
92use crate::temp_file_sweeper;
93use crate::time_provider::{StdTimeProvider, TimeProviderRef};
94use crate::wal::Wal;
95use crate::worker::handle_manifest::RegionEditQueues;
96
97pub(crate) type WorkerId = u32;
99
100pub(crate) const DROPPING_MARKER_FILE: &str = ".dropping";
101
102pub(crate) const CHECK_REGION_INTERVAL: Duration = Duration::from_secs(60);
104pub(crate) const MAX_INITIAL_CHECK_DELAY_SECS: u64 = 60 * 3;
106
107#[cfg_attr(doc, aquamarine::aquamarine)]
108pub(crate) struct WorkerGroup {
145 workers: Vec<RegionWorker>,
147 flush_job_pool: SchedulerRef,
149 compact_job_pool: SchedulerRef,
151 index_build_job_pool: SchedulerRef,
153 purge_scheduler: SchedulerRef,
155 cache_manager: CacheManagerRef,
157 file_ref_manager: FileReferenceManagerRef,
159 gc_limiter: GcLimiterRef,
161 node_lease: Arc<NodeLease>,
162 object_store_manager: ObjectStoreManagerRef,
164 puffin_manager_factory: PuffinManagerFactory,
166 intermediate_manager: IntermediateManager,
168 schema_metadata_manager: SchemaMetadataManagerRef,
170}
171
172impl WorkerGroup {
173 #[allow(clippy::too_many_arguments)]
177 pub(crate) async fn start<S: LogStore>(
178 data_home: &str,
179 config: Arc<MitoConfig>,
180 log_store: Arc<S>,
181 object_store_manager: ObjectStoreManagerRef,
182 schema_metadata_manager: SchemaMetadataManagerRef,
183 file_ref_manager: FileReferenceManagerRef,
184 partition_expr_fetcher: PartitionExprFetcherRef,
185 plugins: Plugins,
186 ) -> Result<WorkerGroup> {
187 let (flush_sender, flush_receiver) = watch::channel(());
188 let write_buffer_manager = Arc::new(
189 WriteBufferManagerImpl::new(config.global_write_buffer_size.as_bytes() as usize)
190 .with_notifier(flush_sender.clone()),
191 );
192 let puffin_manager_factory = PuffinManagerFactory::new(
193 &config.index.aux_path,
194 config.index.staging_size.as_bytes(),
195 Some(config.index.write_buffer_size.as_bytes() as _),
196 config.index.staging_ttl,
197 )
198 .await?;
199 let intermediate_manager = IntermediateManager::init_fs(&config.index.aux_path)
200 .await?
201 .with_buffer_size(Some(config.index.write_buffer_size.as_bytes() as _));
202 let index_build_job_pool =
203 Arc::new(LocalScheduler::new(config.max_background_index_builds));
204 let series_index_store = series_index_store_from_config(&config, data_home).await?;
205 let series_index_disk_usage = Arc::new(AtomicU64::new(0));
206 let flush_job_pool = Arc::new(LocalScheduler::new(config.max_background_flushes));
207 let compact_job_pool = Arc::new(LocalScheduler::new(config.max_background_compactions));
208 let flush_semaphore = Arc::new(Semaphore::new(config.max_background_flushes));
209 let purge_scheduler = Arc::new(LocalScheduler::new(config.max_background_purges));
211 let upload_store_wrapper = plugins.get::<WriteCacheUploadStoreWrapperRef>();
212 let write_cache = write_cache_from_config(
213 &config,
214 puffin_manager_factory.clone(),
215 intermediate_manager.clone(),
216 upload_store_wrapper,
217 )
218 .await?;
219 let cache_manager = Arc::new(
220 CacheManager::builder()
221 .sst_meta_cache_size(config.sst_meta_cache_size.as_bytes())
222 .vector_cache_size(config.vector_cache_size.as_bytes())
223 .page_cache_size(config.page_cache_size.as_bytes())
224 .selector_result_cache_size(config.selector_result_cache_size.as_bytes())
225 .range_result_cache_size(config.range_result_cache_size.as_bytes())
226 .prefilter_result_cache_size(config.prefilter_result_cache_size.as_bytes())
227 .index_metadata_size(config.index.metadata_cache_size.as_bytes())
228 .index_content_size(config.index.content_cache_size.as_bytes())
229 .index_content_page_size(config.index.content_cache_page_size.as_bytes())
230 .index_result_cache_size(config.index.result_cache_size.as_bytes())
231 .puffin_metadata_size(config.index.metadata_cache_size.as_bytes())
232 .write_cache(write_cache)
233 .build(),
234 );
235 let time_provider = Arc::new(StdTimeProvider);
236 let total_memory = get_total_memory_bytes();
237 let total_memory = if total_memory > 0 {
238 total_memory as u64
239 } else {
240 0
241 };
242 let compaction_limit_bytes = config
243 .experimental_compaction_memory_limit
244 .resolve(total_memory);
245 let compaction_memory_manager =
246 Arc::new(new_compaction_memory_manager(compaction_limit_bytes));
247 let gc_limiter = Arc::new(GcLimiter::new(config.gc.max_concurrent_gc_job));
248 #[cfg(feature = "hdfs-object-store")]
253 let sweep_limiter = (config.gc.max_concurrent_gc_job > 0)
254 .then(|| Arc::new(Semaphore::new(config.gc.max_concurrent_gc_job)));
255 let node_lease = Arc::new(NodeLease::default());
256
257 let workers = (0..config.num_workers)
258 .map(|id| {
259 WorkerStarter {
260 id: id as WorkerId,
261 config: config.clone(),
262 log_store: log_store.clone(),
263 object_store_manager: object_store_manager.clone(),
264 write_buffer_manager: write_buffer_manager.clone(),
265 index_build_job_pool: index_build_job_pool.clone(),
266 series_index_store: series_index_store.clone(),
267 series_index_disk_usage: series_index_disk_usage.clone(),
268 flush_job_pool: flush_job_pool.clone(),
269 compact_job_pool: compact_job_pool.clone(),
270 purge_scheduler: purge_scheduler.clone(),
271 listener: WorkerListener::default(),
272 cache_manager: cache_manager.clone(),
273 compaction_memory_manager: compaction_memory_manager.clone(),
274 puffin_manager_factory: puffin_manager_factory.clone(),
275 intermediate_manager: intermediate_manager.clone(),
276 time_provider: time_provider.clone(),
277 flush_sender: flush_sender.clone(),
278 flush_receiver: flush_receiver.clone(),
279 plugins: plugins.clone(),
280 schema_metadata_manager: schema_metadata_manager.clone(),
281 file_ref_manager: file_ref_manager.clone(),
282 partition_expr_fetcher: partition_expr_fetcher.clone(),
283 flush_semaphore: flush_semaphore.clone(),
284 #[cfg(feature = "hdfs-object-store")]
285 sweep_limiter: sweep_limiter.clone(),
286 node_lease: node_lease.clone(),
287 }
288 .start()
289 })
290 .collect::<Result<Vec<_>>>()?;
291
292 Ok(WorkerGroup {
293 workers,
294 flush_job_pool,
295 compact_job_pool,
296 index_build_job_pool,
297 purge_scheduler,
298 cache_manager,
299 file_ref_manager,
300 gc_limiter,
301 node_lease,
302 object_store_manager,
303 puffin_manager_factory,
304 intermediate_manager,
305 schema_metadata_manager,
306 })
307 }
308
309 pub(crate) async fn stop(&self) -> Result<()> {
311 info!("Stop region worker group");
312
313 self.compact_job_pool.stop(true).await?;
316 self.flush_job_pool.stop(true).await?;
318 self.purge_scheduler.stop(true).await?;
320 self.index_build_job_pool.stop(true).await?;
322
323 try_join_all(self.workers.iter().map(|worker| worker.stop())).await?;
324
325 Ok(())
326 }
327
328 pub(crate) async fn submit_to_worker(
330 &self,
331 region_id: RegionId,
332 request: WorkerRequest,
333 ) -> Result<()> {
334 self.worker(region_id).submit_request(request).await
335 }
336
337 pub(crate) async fn submit_writes(&self, writes: Vec<SenderWriteRequest>) {
342 let mut writes_by_worker: HashMap<usize, Vec<SenderWriteRequest>> = HashMap::new();
343 for write in writes {
344 let index = region_id_to_index(write.request.region_id, self.workers.len());
345 writes_by_worker.entry(index).or_default().push(write);
346 }
347 join_all(
348 writes_by_worker
349 .into_iter()
350 .map(|(index, writes)| async move {
351 let worker = &self.workers[index];
352 let Err(request) = worker
353 .try_submit_request(WorkerRequest::BatchWrite(writes))
354 .await
355 else {
356 return;
357 };
358 let WorkerRequest::BatchWrite(writes) = request else {
359 unreachable!("the worker returns the request it was given");
360 };
361 for write in writes {
362 write
363 .sender
364 .send(WorkerStoppedSnafu { id: worker.id }.fail());
365 }
366 }),
367 )
368 .await;
369 }
370
371 pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
373 self.worker(region_id).is_region_exists(region_id)
374 }
375
376 pub(crate) fn is_region_opening(&self, region_id: RegionId) -> bool {
378 self.worker(region_id).is_region_opening(region_id)
379 }
380
381 pub(crate) fn is_region_catching_up(&self, region_id: RegionId) -> bool {
383 self.worker(region_id).is_region_catching_up(region_id)
384 }
385
386 pub(crate) fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
390 self.worker(region_id).get_region(region_id)
391 }
392
393 pub(crate) fn cache_manager(&self) -> CacheManagerRef {
395 self.cache_manager.clone()
396 }
397
398 pub(crate) fn file_ref_manager(&self) -> FileReferenceManagerRef {
399 self.file_ref_manager.clone()
400 }
401
402 pub(crate) fn gc_limiter(&self) -> GcLimiterRef {
403 self.gc_limiter.clone()
404 }
405
406 pub(crate) fn node_lease(&self) -> &NodeLease {
407 &self.node_lease
408 }
409
410 pub(crate) fn worker(&self, region_id: RegionId) -> &RegionWorker {
412 let index = region_id_to_index(region_id, self.workers.len());
413
414 &self.workers[index]
415 }
416
417 pub(crate) fn all_regions(&self) -> impl Iterator<Item = MitoRegionRef> + use<'_> {
418 self.workers
419 .iter()
420 .flat_map(|worker| worker.regions.list_regions())
421 }
422
423 pub(crate) fn object_store_manager(&self) -> &ObjectStoreManagerRef {
424 &self.object_store_manager
425 }
426
427 pub(crate) fn puffin_manager_factory(&self) -> &PuffinManagerFactory {
428 &self.puffin_manager_factory
429 }
430
431 pub(crate) fn intermediate_manager(&self) -> &IntermediateManager {
432 &self.intermediate_manager
433 }
434
435 pub(crate) fn schema_metadata_manager(&self) -> &SchemaMetadataManagerRef {
436 &self.schema_metadata_manager
437 }
438}
439
440#[cfg(any(test, feature = "test"))]
442impl WorkerGroup {
443 #[allow(clippy::too_many_arguments)]
447 pub(crate) async fn start_for_test<S: LogStore>(
448 data_home: &str,
449 config: Arc<MitoConfig>,
450 log_store: Arc<S>,
451 object_store_manager: ObjectStoreManagerRef,
452 write_buffer_manager: Option<WriteBufferManagerRef>,
453 listener: Option<crate::engine::listener::EventListenerRef>,
454 schema_metadata_manager: SchemaMetadataManagerRef,
455 file_ref_manager: FileReferenceManagerRef,
456 time_provider: TimeProviderRef,
457 partition_expr_fetcher: PartitionExprFetcherRef,
458 ) -> Result<WorkerGroup> {
459 let (flush_sender, flush_receiver) = watch::channel(());
460 let write_buffer_manager = write_buffer_manager.unwrap_or_else(|| {
461 Arc::new(
462 WriteBufferManagerImpl::new(config.global_write_buffer_size.as_bytes() as usize)
463 .with_notifier(flush_sender.clone()),
464 )
465 });
466 let index_build_job_pool =
467 Arc::new(LocalScheduler::new(config.max_background_index_builds));
468 let series_index_store = series_index_store_from_config(&config, data_home).await?;
469 let series_index_disk_usage = Arc::new(AtomicU64::new(0));
470 let flush_job_pool = Arc::new(LocalScheduler::new(config.max_background_flushes));
471 let compact_job_pool = Arc::new(LocalScheduler::new(config.max_background_compactions));
472 let flush_semaphore = Arc::new(Semaphore::new(config.max_background_flushes));
473 let purge_scheduler = Arc::new(LocalScheduler::new(config.max_background_flushes));
474 let puffin_manager_factory = PuffinManagerFactory::new(
475 &config.index.aux_path,
476 config.index.staging_size.as_bytes(),
477 Some(config.index.write_buffer_size.as_bytes() as _),
478 config.index.staging_ttl,
479 )
480 .await?;
481 let intermediate_manager = IntermediateManager::init_fs(&config.index.aux_path)
482 .await?
483 .with_buffer_size(Some(config.index.write_buffer_size.as_bytes() as _));
484 let write_cache = write_cache_from_config(
485 &config,
486 puffin_manager_factory.clone(),
487 intermediate_manager.clone(),
488 None,
489 )
490 .await?;
491 let cache_manager = Arc::new(
492 CacheManager::builder()
493 .sst_meta_cache_size(config.sst_meta_cache_size.as_bytes())
494 .vector_cache_size(config.vector_cache_size.as_bytes())
495 .page_cache_size(config.page_cache_size.as_bytes())
496 .selector_result_cache_size(config.selector_result_cache_size.as_bytes())
497 .range_result_cache_size(config.range_result_cache_size.as_bytes())
498 .prefilter_result_cache_size(config.prefilter_result_cache_size.as_bytes())
499 .write_cache(write_cache)
500 .build(),
501 );
502 let total_memory = get_total_memory_bytes();
503 let total_memory = if total_memory > 0 {
504 total_memory as u64
505 } else {
506 0
507 };
508 let compaction_limit_bytes = config
509 .experimental_compaction_memory_limit
510 .resolve(total_memory);
511 let compaction_memory_manager =
512 Arc::new(new_compaction_memory_manager(compaction_limit_bytes));
513 let gc_limiter = Arc::new(GcLimiter::new(config.gc.max_concurrent_gc_job));
514 #[cfg(feature = "hdfs-object-store")]
519 let sweep_limiter = (config.gc.max_concurrent_gc_job > 0)
520 .then(|| Arc::new(Semaphore::new(config.gc.max_concurrent_gc_job)));
521 let node_lease = Arc::new(NodeLease::default());
522 let workers = (0..config.num_workers)
523 .map(|id| {
524 WorkerStarter {
525 id: id as WorkerId,
526 config: config.clone(),
527 log_store: log_store.clone(),
528 object_store_manager: object_store_manager.clone(),
529 write_buffer_manager: write_buffer_manager.clone(),
530 index_build_job_pool: index_build_job_pool.clone(),
531 series_index_store: series_index_store.clone(),
532 series_index_disk_usage: series_index_disk_usage.clone(),
533 flush_job_pool: flush_job_pool.clone(),
534 compact_job_pool: compact_job_pool.clone(),
535 purge_scheduler: purge_scheduler.clone(),
536 listener: WorkerListener::new(listener.clone()),
537 cache_manager: cache_manager.clone(),
538 compaction_memory_manager: compaction_memory_manager.clone(),
539 puffin_manager_factory: puffin_manager_factory.clone(),
540 intermediate_manager: intermediate_manager.clone(),
541 time_provider: time_provider.clone(),
542 flush_sender: flush_sender.clone(),
543 flush_receiver: flush_receiver.clone(),
544 plugins: Plugins::new(),
545 schema_metadata_manager: schema_metadata_manager.clone(),
546 file_ref_manager: file_ref_manager.clone(),
547 partition_expr_fetcher: partition_expr_fetcher.clone(),
548 flush_semaphore: flush_semaphore.clone(),
549 #[cfg(feature = "hdfs-object-store")]
550 sweep_limiter: sweep_limiter.clone(),
551 node_lease: node_lease.clone(),
552 }
553 .start()
554 })
555 .collect::<Result<Vec<_>>>()?;
556
557 Ok(WorkerGroup {
558 workers,
559 flush_job_pool,
560 compact_job_pool,
561 index_build_job_pool,
562 purge_scheduler,
563 cache_manager,
564 file_ref_manager,
565 gc_limiter,
566 node_lease,
567 object_store_manager,
568 puffin_manager_factory,
569 intermediate_manager,
570 schema_metadata_manager,
571 })
572 }
573
574 pub(crate) fn purge_scheduler(&self) -> &SchedulerRef {
576 &self.purge_scheduler
577 }
578}
579
580fn region_id_to_index(id: RegionId, num_workers: usize) -> usize {
581 ((id.table_id() as usize % num_workers) + (id.region_number() as usize % num_workers))
582 % num_workers
583}
584
585async fn series_index_store_from_config(
587 config: &MitoConfig,
588 data_home: &str,
589) -> Result<Option<ObjectStore>> {
590 if !config.experimental_enable_series_index {
591 return Ok(None);
592 }
593
594 let root = Path::new(data_home).join("series_index");
595 new_fs_cache_store(&root.to_string_lossy()).await.map(Some)
596}
597
598pub async fn write_cache_from_config(
599 config: &MitoConfig,
600 puffin_manager_factory: PuffinManagerFactory,
601 intermediate_manager: IntermediateManager,
602 upload_store_wrapper: Option<WriteCacheUploadStoreWrapperRef>,
603) -> Result<Option<WriteCacheRef>> {
604 if !config.enable_write_cache {
605 return Ok(None);
606 }
607
608 tokio::fs::create_dir_all(Path::new(&config.write_cache_path))
609 .await
610 .context(CreateDirSnafu {
611 dir: &config.write_cache_path,
612 })?;
613
614 let cache = WriteCache::new_fs(
615 &config.write_cache_path,
616 config.write_cache_size,
617 config.write_cache_ttl,
618 Some(config.index_cache_percent),
619 config.enable_refill_cache_on_read,
620 puffin_manager_factory,
621 intermediate_manager,
622 config.manifest_cache_size,
623 )
624 .await?
625 .with_upload_store_wrapper(upload_store_wrapper);
626 Ok(Some(Arc::new(cache)))
627}
628
629pub(crate) fn worker_init_check_delay() -> Duration {
631 let init_check_delay = rng().random_range(0..MAX_INITIAL_CHECK_DELAY_SECS);
632 Duration::from_secs(init_check_delay)
633}
634
635struct WorkerStarter<S> {
637 id: WorkerId,
638 config: Arc<MitoConfig>,
639 log_store: Arc<S>,
640 object_store_manager: ObjectStoreManagerRef,
641 write_buffer_manager: WriteBufferManagerRef,
642 compact_job_pool: SchedulerRef,
643 index_build_job_pool: SchedulerRef,
644 series_index_store: Option<ObjectStore>,
645 series_index_disk_usage: Arc<AtomicU64>,
646 flush_job_pool: SchedulerRef,
647 purge_scheduler: SchedulerRef,
648 listener: WorkerListener,
649 cache_manager: CacheManagerRef,
650 compaction_memory_manager: Arc<CompactionMemoryManager>,
651 puffin_manager_factory: PuffinManagerFactory,
652 intermediate_manager: IntermediateManager,
653 time_provider: TimeProviderRef,
654 flush_sender: watch::Sender<()>,
656 flush_receiver: watch::Receiver<()>,
658 plugins: Plugins,
659 schema_metadata_manager: SchemaMetadataManagerRef,
660 file_ref_manager: FileReferenceManagerRef,
661 partition_expr_fetcher: PartitionExprFetcherRef,
662 flush_semaphore: Arc<Semaphore>,
663 #[cfg(feature = "hdfs-object-store")]
664 sweep_limiter: Option<Arc<Semaphore>>,
667 node_lease: Arc<NodeLease>,
668}
669
670impl<S: LogStore> WorkerStarter<S> {
671 fn start(self) -> Result<RegionWorker> {
673 let regions = Arc::new(RegionMap::default());
674 let opening_regions = Arc::new(OpeningRegions::default());
675 let catchup_regions = Arc::new(CatchupRegions::default());
676 let (sender, receiver) = mpsc::channel(self.config.worker_channel_size);
677
678 let running = Arc::new(AtomicBool::new(true));
679 let (series_index_task_state, series_index_receiver) = if self.series_index_store.is_some()
680 {
681 let (state, receiver) =
682 SeriesIndexTaskState::new(self.id, self.config.worker_channel_size);
683 (Some(Arc::new(state)), Some(receiver))
684 } else {
685 (None, None)
686 };
687 let mut series_index_purger = None;
688 let series_index_handle = self
689 .series_index_store
690 .clone()
691 .zip(series_index_task_state.clone())
692 .zip(series_index_receiver)
693 .map(|((store, state), receiver)| {
694 let (purger, purge_receiver) = series_index_channel(store.clone());
695 series_index_purger = Some(purger.clone());
696 spawn_series_index_tasks(
697 self.id,
698 store,
699 regions.clone(),
700 state,
701 receiver,
702 self.config.experimental_series_index_bucket_width,
703 purger,
704 purge_receiver,
705 self.series_index_disk_usage.clone(),
706 self.config.experimental_series_index_max_size.as_bytes(),
707 self.config.experimental_series_index_maintenance_interval,
708 self.time_provider.clone(),
709 self.config.experimental_enable_range_index,
710 )
711 });
712 let now = self.time_provider.current_time_millis();
713 let id_string = self.id.to_string();
714 let mut worker_thread = RegionWorkerLoop {
715 id: self.id,
716 config: self.config.clone(),
717 regions: regions.clone(),
718 catchup_regions: catchup_regions.clone(),
719 dropping_regions: Arc::new(RegionMap::default()),
720 opening_regions: opening_regions.clone(),
721 sender: sender.clone(),
722 receiver,
723 wal: Wal::new(self.log_store),
724 object_store_manager: self.object_store_manager.clone(),
725 running: running.clone(),
726 memtable_builder_provider: MemtableBuilderProvider::new(
727 Some(self.write_buffer_manager.clone()),
728 self.config.clone(),
729 ),
730 purge_scheduler: self.purge_scheduler.clone(),
731 write_buffer_manager: self.write_buffer_manager,
732 index_build_scheduler: IndexBuildScheduler::new(
733 self.index_build_job_pool,
734 self.config.max_background_index_builds,
735 ),
736 series_index_task_state: series_index_task_state.clone(),
737 series_index_store: self.series_index_store,
738 series_index_purger,
739 flush_scheduler: FlushScheduler::new(self.flush_job_pool),
740 compaction_scheduler: CompactionScheduler::new(
741 self.compact_job_pool,
742 sender.clone(),
743 self.cache_manager.clone(),
744 self.config.clone(),
745 self.listener.clone(),
746 self.plugins.clone(),
747 self.compaction_memory_manager.clone(),
748 self.config.experimental_compaction_on_exhausted,
749 ),
750 stalled_requests: StalledRequests::default(),
751 listener: self.listener,
752 cache_manager: self.cache_manager,
753 puffin_manager_factory: self.puffin_manager_factory,
754 intermediate_manager: self.intermediate_manager,
755 time_provider: self.time_provider,
756 last_periodical_check_millis: now,
757 flush_sender: self.flush_sender,
758 flush_receiver: self.flush_receiver,
759 stalling_count: WRITE_STALLING.with_label_values(&[&id_string]),
760 region_count: REGION_COUNT.with_label_values(&[&id_string]),
761 request_wait_time: REQUEST_WAIT_TIME.with_label_values(&[&id_string]),
762 region_edit_queues: RegionEditQueues::default(),
763 schema_metadata_manager: self.schema_metadata_manager,
764 file_ref_manager: self.file_ref_manager.clone(),
765 partition_expr_fetcher: self.partition_expr_fetcher,
766 flush_semaphore: self.flush_semaphore,
767 plugins: self.plugins,
768 #[cfg(feature = "hdfs-object-store")]
769 sweep_limiter: self.sweep_limiter,
770 node_lease: self.node_lease,
771 };
772 let handle = common_runtime::spawn_global(async move {
773 worker_thread.run().await;
774 });
775
776 Ok(RegionWorker {
777 id: self.id,
778 regions,
779 opening_regions,
780 catchup_regions,
781 sender,
782 handle: Mutex::new(Some(handle)),
783 series_index_handle: Mutex::new(series_index_handle),
784 series_index_task_state,
785 running,
786 })
787 }
788}
789
790pub(crate) struct RegionWorker {
792 id: WorkerId,
794 regions: RegionMapRef,
796 opening_regions: OpeningRegionsRef,
798 catchup_regions: CatchupRegionsRef,
800 sender: Sender<WorkerRequestWithTime>,
802 handle: Mutex<Option<JoinHandle<()>>>,
804 series_index_handle: Mutex<Option<JoinHandle<()>>>,
806 series_index_task_state: Option<Arc<SeriesIndexTaskState>>,
808 running: Arc<AtomicBool>,
810}
811
812impl RegionWorker {
813 async fn submit_request(&self, request: WorkerRequest) -> Result<()> {
815 self.try_submit_request(request)
816 .await
817 .map_err(|_| WorkerStoppedSnafu { id: self.id }.build())
818 }
819
820 async fn try_submit_request(
822 &self,
823 request: WorkerRequest,
824 ) -> std::result::Result<(), WorkerRequest> {
825 if !self.is_running() {
826 return Err(request);
827 }
828 let request_with_time = WorkerRequestWithTime::new(request);
829 if let Err(mpsc::error::SendError(request_with_time)) =
830 self.sender.send(request_with_time).await
831 {
832 warn!(
833 "Worker {} is already exited but the running flag is still true",
834 self.id
835 );
836 self.set_running(false);
838 if let Some(state) = &self.series_index_task_state {
839 state.stop();
840 }
841 return Err(request_with_time.request);
842 }
843
844 Ok(())
845 }
846
847 async fn stop(&self) -> Result<()> {
851 let handle = self.handle.lock().await.take();
852 self.set_running(false);
853 if let Some(state) = &self.series_index_task_state {
854 state.stop();
855 }
856
857 let mut worker_result = Ok(());
858 if let Some(handle) = handle {
859 info!("Stop region worker {}", self.id);
860
861 if self
862 .sender
863 .send(WorkerRequestWithTime::new(WorkerRequest::Stop))
864 .await
865 .is_err()
866 {
867 warn!("Worker {} is already exited before stop", self.id);
868 }
869
870 worker_result = handle.await.context(JoinSnafu);
871 }
872 let series_index_result = if let Some(handle) = self.series_index_handle.lock().await.take()
873 {
874 handle.await.context(JoinSnafu)
875 } else {
876 Ok(())
877 };
878 worker_result?;
879 series_index_result?;
880
881 Ok(())
882 }
883
884 fn is_running(&self) -> bool {
886 self.running.load(Ordering::Relaxed)
887 }
888
889 fn set_running(&self, value: bool) {
891 self.running.store(value, Ordering::Relaxed)
892 }
893
894 fn is_region_exists(&self, region_id: RegionId) -> bool {
896 self.regions.is_region_exists(region_id)
897 }
898
899 fn is_region_opening(&self, region_id: RegionId) -> bool {
901 self.opening_regions.is_region_exists(region_id)
902 }
903
904 fn is_region_catching_up(&self, region_id: RegionId) -> bool {
906 self.catchup_regions.is_region_exists(region_id)
907 }
908
909 fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
911 self.regions.get_region(region_id)
912 }
913
914 #[cfg(test)]
915 pub(crate) fn opening_regions(&self) -> &OpeningRegionsRef {
917 &self.opening_regions
918 }
919
920 #[cfg(test)]
921 pub(crate) fn catchup_regions(&self) -> &CatchupRegionsRef {
923 &self.catchup_regions
924 }
925}
926
927impl Drop for RegionWorker {
928 fn drop(&mut self) {
929 if let Some(state) = &self.series_index_task_state {
930 state.stop();
931 }
932 if self.is_running() {
933 self.set_running(false);
934 }
936 }
937}
938
939type RequestBuffer = Vec<WorkerRequest>;
940
941#[derive(Default)]
945pub(crate) struct StalledRequests {
946 pub(crate) requests:
953 HashMap<RegionId, (usize, Vec<SenderWriteRequest>, Vec<SenderBulkRequest>)>,
954 pub(crate) estimated_size: usize,
956}
957
958impl StalledRequests {
959 pub(crate) fn estimated_size(&self, region_id: &RegionId) -> usize {
961 self.requests
962 .get(region_id)
963 .map(|(size, _, _)| *size)
964 .unwrap_or_default()
965 }
966
967 pub(crate) fn append(
969 &mut self,
970 requests: &mut Vec<SenderWriteRequest>,
971 bulk_requests: &mut Vec<SenderBulkRequest>,
972 ) {
973 for req in requests.drain(..) {
974 self.push(req);
975 }
976 for req in bulk_requests.drain(..) {
977 self.push_bulk(req);
978 }
979 }
980
981 pub(crate) fn push(&mut self, req: SenderWriteRequest) {
983 let (size, requests, _) = self.requests.entry(req.request.region_id).or_default();
984 let req_size = req.request.estimated_size();
985 *size += req_size;
986 self.estimated_size += req_size;
987 requests.push(req);
988 }
989
990 pub(crate) fn push_bulk(&mut self, req: SenderBulkRequest) {
991 let region_id = req.region_id;
992 let (size, _, requests) = self.requests.entry(region_id).or_default();
993 let req_size = req.request.estimated_size();
994 *size += req_size;
995 self.estimated_size += req_size;
996 requests.push(req);
997 }
998
999 pub(crate) fn remove(
1001 &mut self,
1002 region_id: &RegionId,
1003 ) -> (Vec<SenderWriteRequest>, Vec<SenderBulkRequest>) {
1004 if let Some((size, write_reqs, bulk_reqs)) = self.requests.remove(region_id) {
1005 self.estimated_size -= size;
1006 (write_reqs, bulk_reqs)
1007 } else {
1008 (vec![], vec![])
1009 }
1010 }
1011
1012 pub(crate) fn stalled_count(&self) -> usize {
1014 self.requests
1015 .values()
1016 .map(|(_, reqs, bulk_reqs)| reqs.len() + bulk_reqs.len())
1017 .sum()
1018 }
1019}
1020
1021struct RegionWorkerLoop<S> {
1023 id: WorkerId,
1025 config: Arc<MitoConfig>,
1027 regions: RegionMapRef,
1029 dropping_regions: RegionMapRef,
1031 opening_regions: OpeningRegionsRef,
1033 catchup_regions: CatchupRegionsRef,
1035 sender: Sender<WorkerRequestWithTime>,
1037 receiver: Receiver<WorkerRequestWithTime>,
1039 wal: Wal<S>,
1041 object_store_manager: ObjectStoreManagerRef,
1043 running: Arc<AtomicBool>,
1045 memtable_builder_provider: MemtableBuilderProvider,
1047 purge_scheduler: SchedulerRef,
1049 write_buffer_manager: WriteBufferManagerRef,
1051 index_build_scheduler: IndexBuildScheduler,
1053 series_index_task_state: Option<Arc<SeriesIndexTaskState>>,
1055 series_index_store: Option<ObjectStore>,
1057 series_index_purger: Option<IndexFilePurger>,
1058 flush_scheduler: FlushScheduler,
1060 compaction_scheduler: CompactionScheduler,
1062 stalled_requests: StalledRequests,
1064 listener: WorkerListener,
1066 cache_manager: CacheManagerRef,
1068 puffin_manager_factory: PuffinManagerFactory,
1070 intermediate_manager: IntermediateManager,
1072 time_provider: TimeProviderRef,
1074 last_periodical_check_millis: i64,
1076 flush_sender: watch::Sender<()>,
1078 flush_receiver: watch::Receiver<()>,
1080 stalling_count: IntGauge,
1082 region_count: IntGauge,
1084 request_wait_time: Histogram,
1086 region_edit_queues: RegionEditQueues,
1088 schema_metadata_manager: SchemaMetadataManagerRef,
1090 file_ref_manager: FileReferenceManagerRef,
1092 partition_expr_fetcher: PartitionExprFetcherRef,
1094 flush_semaphore: Arc<Semaphore>,
1096 plugins: Plugins,
1098 #[cfg(feature = "hdfs-object-store")]
1099 sweep_limiter: Option<Arc<Semaphore>>,
1102 node_lease: Arc<NodeLease>,
1103}
1104
1105impl<S: LogStore> RegionWorkerLoop<S> {
1106 fn register_region(&mut self, region: MitoRegionRef) {
1108 self.compaction_scheduler
1109 .register_region(®ion.version_control, ®ion.access_layer);
1110 self.regions.insert_region(region);
1111 }
1112
1113 async fn run(&mut self) {
1115 let init_check_delay = worker_init_check_delay();
1116 info!(
1117 "Start region worker thread {}, init_check_delay: {:?}",
1118 self.id, init_check_delay
1119 );
1120 self.last_periodical_check_millis += init_check_delay.as_millis() as i64;
1121
1122 let mut write_req_buffer: Vec<SenderWriteRequest> =
1124 Vec::with_capacity(self.config.worker_request_batch_size);
1125 let mut bulk_req_buffer: Vec<SenderBulkRequest> =
1126 Vec::with_capacity(self.config.worker_request_batch_size);
1127 let mut ddl_req_buffer: Vec<SenderDdlRequest> =
1128 Vec::with_capacity(self.config.worker_request_batch_size);
1129 let mut general_req_buffer: Vec<WorkerRequest> =
1130 RequestBuffer::with_capacity(self.config.worker_request_batch_size);
1131
1132 while self.running.load(Ordering::Relaxed) {
1133 write_req_buffer.clear();
1135 ddl_req_buffer.clear();
1136 general_req_buffer.clear();
1137 let mut bulk_insert_req_num = 0;
1138
1139 let max_wait_time = self.time_provider.wait_duration(CHECK_REGION_INTERVAL);
1140 let sleep = tokio::time::sleep(max_wait_time);
1141 tokio::pin!(sleep);
1142
1143 tokio::select! {
1144 request_opt = self.receiver.recv() => {
1145 match request_opt {
1146 Some(request_with_time) => {
1147 let wait_time = request_with_time.created_at.elapsed();
1149 self.request_wait_time.observe(wait_time.as_secs_f64());
1150
1151 match request_with_time.request {
1152 WorkerRequest::Write(sender_req) => write_req_buffer.push(sender_req),
1153 WorkerRequest::BatchWrite(sender_reqs) => write_req_buffer.extend(sender_reqs),
1154 WorkerRequest::Ddl(sender_req) => ddl_req_buffer.push(sender_req),
1155 WorkerRequest::BulkInserts(bulk_insert) => {
1156 bulk_insert_req_num += 1;
1157 self.buffer_bulk_insert_request(bulk_insert, &mut bulk_req_buffer)
1158 .await;
1159 }
1160 req => general_req_buffer.push(req),
1161 }
1162 },
1163 None => break,
1165 }
1166 }
1167 recv_res = self.flush_receiver.changed() => {
1168 if recv_res.is_err() {
1169 break;
1171 } else {
1172 self.maybe_flush_worker();
1177 self.handle_stalled_requests().await;
1179 continue;
1180 }
1181 }
1182 _ = &mut sleep => {
1183 self.handle_periodical_tasks();
1185 continue;
1186 }
1187 }
1188
1189 if self.flush_receiver.has_changed().unwrap_or(false) {
1190 self.handle_stalled_requests().await;
1194 }
1195
1196 for _ in 1..self.config.worker_request_batch_size {
1198 match self.receiver.try_recv() {
1200 Ok(request_with_time) => {
1201 let wait_time = request_with_time.created_at.elapsed();
1203 self.request_wait_time.observe(wait_time.as_secs_f64());
1204
1205 match request_with_time.request {
1206 WorkerRequest::Write(sender_req) => write_req_buffer.push(sender_req),
1207 WorkerRequest::BatchWrite(sender_reqs) => {
1208 write_req_buffer.extend(sender_reqs)
1209 }
1210 WorkerRequest::Ddl(sender_req) => ddl_req_buffer.push(sender_req),
1211 WorkerRequest::BulkInserts(bulk_insert) => {
1212 bulk_insert_req_num += 1;
1213 self.buffer_bulk_insert_request(bulk_insert, &mut bulk_req_buffer)
1214 .await
1215 }
1216 req => general_req_buffer.push(req),
1217 }
1218 }
1219 Err(_) => break,
1221 }
1222 }
1223
1224 self.listener.on_recv_requests(
1225 write_req_buffer.len()
1226 + ddl_req_buffer.len()
1227 + general_req_buffer.len()
1228 + bulk_insert_req_num,
1229 );
1230
1231 self.handle_requests(
1232 &mut write_req_buffer,
1233 &mut ddl_req_buffer,
1234 &mut general_req_buffer,
1235 &mut bulk_req_buffer,
1236 )
1237 .await;
1238
1239 self.handle_periodical_tasks();
1240 }
1241
1242 self.clean().await;
1243
1244 info!("Exit region worker thread {}", self.id);
1245 }
1246
1247 async fn buffer_bulk_insert_request(
1248 &mut self,
1249 bulk_insert: BulkInsertRequest,
1250 bulk_requests: &mut Vec<SenderBulkRequest>,
1251 ) {
1252 let BulkInsertRequest {
1253 metadata,
1254 request,
1255 sender,
1256 } = bulk_insert;
1257
1258 if let Some(region_metadata) = metadata {
1259 self.handle_bulk_insert_batch(region_metadata, request, bulk_requests, sender)
1260 .await;
1261 } else {
1262 error!("Cannot find region metadata for {}", request.region_id);
1263 sender.send(
1264 error::RegionNotFoundSnafu {
1265 region_id: request.region_id,
1266 }
1267 .fail(),
1268 );
1269 }
1270 }
1271
1272 async fn handle_requests(
1276 &mut self,
1277 write_requests: &mut Vec<SenderWriteRequest>,
1278 ddl_requests: &mut Vec<SenderDdlRequest>,
1279 general_requests: &mut Vec<WorkerRequest>,
1280 bulk_requests: &mut Vec<SenderBulkRequest>,
1281 ) {
1282 for worker_req in general_requests.drain(..) {
1283 match worker_req {
1284 WorkerRequest::Write(_) | WorkerRequest::BatchWrite(_) | WorkerRequest::Ddl(_) => {
1285 continue;
1287 }
1288 WorkerRequest::BulkInserts(_) => unreachable!("bulk inserts are buffered"),
1289 WorkerRequest::Background { region_id, notify } => {
1290 if matches!(
1291 ¬ify,
1292 BackgroundNotify::RegionEdit(edit_result)
1293 if edit_result.update_region_state
1294 ) {
1295 self.handle_buffered_region_write_requests(
1304 ®ion_id,
1305 write_requests,
1306 bulk_requests,
1307 )
1308 .await;
1309 }
1310 self.handle_background_notify(region_id, notify).await;
1312 }
1313 WorkerRequest::SetRegionRoleStateGracefully {
1314 region_id,
1315 region_role_state,
1316 sender,
1317 } => {
1318 self.set_role_state_gracefully(region_id, region_role_state, sender)
1319 .await;
1320 }
1321 WorkerRequest::EditRegion(request) => {
1322 self.handle_region_edit(request);
1323 }
1324 WorkerRequest::Stop => {
1325 debug_assert!(!self.running.load(Ordering::Relaxed));
1326 }
1327 WorkerRequest::SyncRegion(req) => {
1328 self.handle_region_sync(req).await;
1329 }
1330 WorkerRequest::RemapManifests(req) => {
1331 self.handle_remap_manifests_request(req);
1332 }
1333 WorkerRequest::CopyRegionFrom(req) => {
1334 self.handle_copy_region_from_request(req);
1335 }
1336 }
1337 }
1338
1339 self.handle_write_requests(write_requests, bulk_requests, true)
1342 .await;
1343
1344 self.handle_ddl_requests(ddl_requests).await;
1345 }
1346
1347 async fn handle_ddl_requests(&mut self, ddl_requests: &mut Vec<SenderDdlRequest>) {
1349 if ddl_requests.is_empty() {
1350 return;
1351 }
1352
1353 for ddl in ddl_requests.drain(..) {
1354 let res = match ddl.request {
1355 DdlRequest::Create(req) => self.handle_create_request(ddl.region_id, req).await,
1356 DdlRequest::Drop(req) => {
1357 self.handle_drop_request(ddl.region_id, req, ddl.sender)
1358 .await;
1359 continue;
1360 }
1361 DdlRequest::Open((req, wal_entry_receiver)) => {
1362 self.handle_open_request(ddl.region_id, req, wal_entry_receiver, ddl.sender)
1363 .await;
1364 continue;
1365 }
1366 DdlRequest::OfflineCleanup(req) => {
1367 self.handle_offline_cleanup_request(ddl.region_id, req)
1368 .await
1369 }
1370 DdlRequest::Close(req) => {
1371 self.handle_close_request(ddl.region_id, req, ddl.sender)
1372 .await;
1373 continue;
1374 }
1375 DdlRequest::Alter(req) => {
1376 self.handle_alter_request(ddl.region_id, req, ddl.sender)
1377 .await;
1378 continue;
1379 }
1380 DdlRequest::Flush(req) => {
1381 self.handle_flush_request(ddl.region_id, req, ddl.sender);
1382 continue;
1383 }
1384 DdlRequest::Compact(req) => {
1385 self.handle_compaction_request(ddl.region_id, req, ddl.sender)
1386 .await;
1387 continue;
1388 }
1389 DdlRequest::BuildIndex(req) => {
1390 self.handle_build_index_request(ddl.region_id, req, ddl.sender)
1391 .await;
1392 continue;
1393 }
1394 DdlRequest::Truncate(req) => {
1395 self.handle_truncate_request(ddl.region_id, req, ddl.sender)
1396 .await;
1397 continue;
1398 }
1399 DdlRequest::Catchup((req, wal_entry_receiver)) => {
1400 self.handle_catchup_request(ddl.region_id, req, wal_entry_receiver, ddl.sender)
1401 .await;
1402 continue;
1403 }
1404 DdlRequest::EnterStaging(req) => {
1405 self.handle_enter_staging_request(
1406 ddl.region_id,
1407 req.partition_directive,
1408 ddl.sender,
1409 )
1410 .await;
1411 continue;
1412 }
1413 DdlRequest::ApplyStagingManifest(req) => {
1414 self.handle_apply_staging_manifest_request(ddl.region_id, req, ddl.sender)
1415 .await;
1416 continue;
1417 }
1418 };
1419
1420 ddl.sender.send(res);
1421 }
1422 }
1423
1424 fn handle_periodical_tasks(&mut self) {
1426 let interval = CHECK_REGION_INTERVAL.as_millis() as i64;
1427 if self
1428 .time_provider
1429 .elapsed_since(self.last_periodical_check_millis)
1430 < interval
1431 {
1432 return;
1433 }
1434
1435 self.last_periodical_check_millis = self.time_provider.current_time_millis();
1436
1437 if let Err(e) = self.flush_periodically() {
1438 error!(e; "Failed to flush regions periodically");
1439 }
1440 }
1441
1442 async fn handle_background_notify(&mut self, region_id: RegionId, notify: BackgroundNotify) {
1444 match notify {
1445 BackgroundNotify::RegionOpened { region, registered } => {
1446 self.register_region(region);
1447 self.region_count.inc();
1448
1449 let senders = self.opening_regions.remove_sender(region_id);
1452 if let Some(state) = &self.series_index_task_state {
1453 state.wake();
1454 }
1455 let _ = registered.send(());
1456 for sender in senders {
1457 sender.send(Ok(0));
1458 }
1459 }
1460 BackgroundNotify::CompactionUnit(req) => {
1461 self.handle_compaction_unit(region_id, req).await;
1462 }
1463 BackgroundNotify::CompactionDdlComplete {
1464 generation,
1465 request_index,
1466 } => {
1467 self.compaction_scheduler
1468 .on_ddl_complete(region_id, generation, request_index);
1469 }
1470 BackgroundNotify::CompactionPickFinished(req) => {
1471 self.handle_compaction_pick_finished(region_id, req).await
1472 }
1473 BackgroundNotify::FlushFinished(req) => {
1474 self.handle_flush_finished(region_id, req).await
1475 }
1476 BackgroundNotify::FlushFailed(req) => self.handle_flush_failed(region_id, req).await,
1477 BackgroundNotify::IndexBuildFinished(req) => {
1478 self.handle_index_build_finished(region_id, req).await
1479 }
1480 BackgroundNotify::IndexBuildStopped(req) => {
1481 self.handle_index_build_stopped(region_id, req).await
1482 }
1483 BackgroundNotify::IndexBuildRetry(req) => {
1484 self.handle_rebuild_index(req, OptionOutputTx::new(None))
1485 .await
1486 }
1487 BackgroundNotify::CompactionFinished(req) => {
1488 self.handle_compaction_finished(region_id, req).await
1489 }
1490 BackgroundNotify::CompactionFailed(req) => self.handle_compaction_failure(req).await,
1491 BackgroundNotify::Truncate(req) => self.handle_truncate_result(req).await,
1492 BackgroundNotify::DiscardUnflushed(req) => {
1493 self.handle_discard_unflushed_result(req).await
1494 }
1495 BackgroundNotify::RegionChange(req) => {
1496 self.handle_manifest_region_change_result(req).await
1497 }
1498 BackgroundNotify::EnterStaging(req) => self.handle_enter_staging_result(req).await,
1499 BackgroundNotify::RegionEdit(req) => self.handle_region_edit_result(req).await,
1500 BackgroundNotify::CopyRegionFromFinished(req) => {
1501 self.handle_copy_region_from_finished(req)
1502 }
1503 }
1504 }
1505
1506 async fn set_role_state_gracefully(
1508 &mut self,
1509 region_id: RegionId,
1510 region_role_state: SettableRegionRoleState,
1511 sender: oneshot::Sender<SetRegionRoleStateResponse>,
1512 ) {
1513 if let Some(region) = self.regions.get_region(region_id) {
1514 if matches!(
1515 region_role_state,
1516 SettableRegionRoleState::Follower | SettableRegionRoleState::DowngradingLeader
1517 ) {
1518 region.manifest_ctx.begin_downgrade(region_id);
1523 }
1524 #[cfg(feature = "hdfs-object-store")]
1525 let sweep_older_than = self.config.gc.unknown_file_lingering_time;
1526 #[cfg(feature = "hdfs-object-store")]
1527 let sweep_limiter = self.sweep_limiter.clone();
1528 common_runtime::spawn_global(async move {
1530 match region.set_role_state_gracefully(region_role_state).await {
1531 Ok(()) => {
1532 let last_entry_id = region.version_control.current().last_entry_id;
1533 let _ = sender.send(SetRegionRoleStateResponse::success(
1534 SetRegionRoleStateSuccess::mito(last_entry_id),
1535 ));
1536 #[cfg(feature = "hdfs-object-store")]
1539 if region_role_state == SettableRegionRoleState::Leader {
1540 temp_file_sweeper::spawn_region_sweep(
1541 region.access_layer(),
1542 region_id,
1543 sweep_older_than,
1544 sweep_limiter,
1545 );
1546 }
1547 }
1548 Err(e) => {
1549 error!(e; "Failed to set region {} role state to {:?}", region_id, region_role_state);
1550 let _ = sender.send(SetRegionRoleStateResponse::invalid_transition(
1551 BoxedError::new(e),
1552 ));
1553 }
1554 }
1555 });
1556 } else {
1557 let _ = sender.send(SetRegionRoleStateResponse::NotFound);
1558 }
1559 }
1560}
1561
1562impl<S> RegionWorkerLoop<S> {
1563 async fn clean(&self) {
1565 let regions = self.regions.list_regions();
1567 for region in regions {
1568 region.stop().await;
1569 }
1570
1571 self.regions.clear();
1572 }
1573
1574 fn notify_group(&mut self) {
1577 let _ = self.flush_sender.send(());
1579 self.flush_receiver.borrow_and_update();
1581 }
1582}
1583
1584#[derive(Default, Clone)]
1586pub(crate) struct WorkerListener {
1587 #[cfg(any(test, feature = "test"))]
1588 listener: Option<crate::engine::listener::EventListenerRef>,
1589}
1590
1591impl WorkerListener {
1592 #[cfg(any(test, feature = "test"))]
1593 pub(crate) fn new(
1594 listener: Option<crate::engine::listener::EventListenerRef>,
1595 ) -> WorkerListener {
1596 WorkerListener { listener }
1597 }
1598
1599 pub(crate) async fn on_region_open_registered(&self, _region_id: RegionId) {
1601 #[cfg(any(test, feature = "test"))]
1602 if let Some(listener) = &self.listener {
1603 listener.on_region_open_registered(_region_id).await;
1604 }
1605 }
1606
1607 pub(crate) fn on_flush_success(&self, region_id: RegionId) {
1609 #[cfg(any(test, feature = "test"))]
1610 if let Some(listener) = &self.listener {
1611 listener.on_flush_success(region_id);
1612 }
1613 let _ = region_id;
1615 }
1616
1617 pub(crate) fn on_write_stall(&self) {
1619 #[cfg(any(test, feature = "test"))]
1620 if let Some(listener) = &self.listener {
1621 listener.on_write_stall();
1622 }
1623 }
1624
1625 pub(crate) async fn on_write_wal_end(&self) {
1627 #[cfg(any(test, feature = "test"))]
1628 if let Some(listener) = &self.listener {
1629 listener.on_write_wal_end().await;
1630 }
1631 }
1632
1633 pub(crate) async fn on_flush_begin(&self, region_id: RegionId) {
1634 #[cfg(any(test, feature = "test"))]
1635 if let Some(listener) = &self.listener {
1636 listener.on_flush_begin(region_id).await;
1637 }
1638 let _ = region_id;
1640 }
1641
1642 pub(crate) async fn on_flush_commit_begin(&self, _region_id: RegionId) {
1643 #[cfg(any(test, feature = "test"))]
1644 if let Some(listener) = &self.listener {
1645 listener.on_flush_commit_begin(_region_id).await;
1646 }
1647 }
1648
1649 pub(crate) fn on_flush_cancel_requested(&self, _region_id: RegionId) {
1650 #[cfg(any(test, feature = "test"))]
1651 if let Some(listener) = &self.listener {
1652 listener.on_flush_cancel_requested(_region_id);
1653 }
1654 }
1655
1656 pub(crate) fn on_later_drop_begin(&self, region_id: RegionId) -> Option<Duration> {
1657 #[cfg(any(test, feature = "test"))]
1658 if let Some(listener) = &self.listener {
1659 return listener.on_later_drop_begin(region_id);
1660 }
1661 let _ = region_id;
1663 None
1664 }
1665
1666 pub(crate) fn on_later_drop_end(&self, region_id: RegionId, removed: bool) {
1668 #[cfg(any(test, feature = "test"))]
1669 if let Some(listener) = &self.listener {
1670 listener.on_later_drop_end(region_id, removed);
1671 }
1672 let _ = region_id;
1674 let _ = removed;
1675 }
1676
1677 pub(crate) async fn on_merge_ssts_finished(&self, region_id: RegionId) {
1678 #[cfg(any(test, feature = "test"))]
1679 if let Some(listener) = &self.listener {
1680 listener.on_merge_ssts_finished(region_id).await;
1681 }
1682 let _ = region_id;
1684 }
1685
1686 pub(crate) fn on_recv_requests(&self, request_num: usize) {
1687 #[cfg(any(test, feature = "test"))]
1688 if let Some(listener) = &self.listener {
1689 listener.on_recv_requests(request_num);
1690 }
1691 let _ = request_num;
1693 }
1694
1695 pub(crate) fn on_file_cache_filled(&self, _file_id: FileId) {
1696 #[cfg(any(test, feature = "test"))]
1697 if let Some(listener) = &self.listener {
1698 listener.on_file_cache_filled(_file_id);
1699 }
1700 }
1701
1702 pub(crate) fn on_compaction_scheduled(&self, _region_id: RegionId) {
1703 #[cfg(any(test, feature = "test"))]
1704 if let Some(listener) = &self.listener {
1705 listener.on_compaction_scheduled(_region_id);
1706 }
1707 }
1708
1709 pub(crate) async fn on_compaction_pick_begin(&self, _region_id: RegionId) {
1710 #[cfg(any(test, feature = "test"))]
1711 if let Some(listener) = &self.listener {
1712 listener.on_compaction_pick_begin(_region_id).await;
1713 }
1714 }
1715
1716 pub(crate) async fn on_compaction_commit_begin(&self, _region_id: RegionId) {
1717 #[cfg(any(test, feature = "test"))]
1718 if let Some(listener) = &self.listener {
1719 listener.on_compaction_commit_begin(_region_id).await;
1720 }
1721 }
1722
1723 pub(crate) async fn on_compaction_unit_merge_begin(&self, _region_id: RegionId, _plan_id: u64) {
1725 #[cfg(any(test, feature = "test"))]
1726 if let Some(listener) = &self.listener {
1727 listener
1728 .on_compaction_unit_merge_begin(_region_id, _plan_id)
1729 .await;
1730 }
1731 }
1732
1733 pub(crate) async fn on_compaction_unit_committed(&self, _region_id: RegionId, _plan_id: u64) {
1735 #[cfg(any(test, feature = "test"))]
1736 if let Some(listener) = &self.listener {
1737 listener
1738 .on_compaction_unit_committed(_region_id, _plan_id)
1739 .await;
1740 }
1741 }
1742
1743 pub(crate) fn on_compaction_unit_applied(&self, _region_id: RegionId, _plan_id: u64) {
1745 #[cfg(any(test, feature = "test"))]
1746 if let Some(listener) = &self.listener {
1747 listener.on_compaction_unit_applied(_region_id, _plan_id);
1748 }
1749 }
1750
1751 pub(crate) async fn on_compaction_result_notified(&self, _region_id: RegionId) {
1752 #[cfg(any(test, feature = "test"))]
1753 if let Some(listener) = &self.listener {
1754 listener.on_compaction_result_notified(_region_id).await;
1755 }
1756 }
1757
1758 pub(crate) fn on_compaction_cancel_requested(&self, _region_id: RegionId) {
1759 #[cfg(any(test, feature = "test"))]
1760 if let Some(listener) = &self.listener {
1761 listener.on_compaction_cancel_requested(_region_id);
1762 }
1763 }
1764
1765 pub(crate) async fn on_notify_region_change_result_begin(&self, _region_id: RegionId) {
1766 #[cfg(any(test, feature = "test"))]
1767 if let Some(listener) = &self.listener {
1768 listener
1769 .on_notify_region_change_result_begin(_region_id)
1770 .await;
1771 }
1772 }
1773
1774 pub(crate) async fn on_enter_staging_result_begin(&self, _region_id: RegionId) {
1775 #[cfg(any(test, feature = "test"))]
1776 if let Some(listener) = &self.listener {
1777 listener.on_enter_staging_result_begin(_region_id).await;
1778 }
1779 }
1780
1781 pub(crate) async fn on_index_build_finish(&self, _region_file_id: RegionFileId) {
1782 #[cfg(any(test, feature = "test"))]
1783 if let Some(listener) = &self.listener {
1784 listener.on_index_build_finish(_region_file_id).await;
1785 }
1786 }
1787
1788 pub(crate) async fn on_index_build_begin(&self, _region_file_id: RegionFileId) {
1789 #[cfg(any(test, feature = "test"))]
1790 if let Some(listener) = &self.listener {
1791 listener.on_index_build_begin(_region_file_id).await;
1792 }
1793 }
1794
1795 pub(crate) async fn on_index_build_abort(&self, _region_file_id: RegionFileId) {
1796 #[cfg(any(test, feature = "test"))]
1797 if let Some(listener) = &self.listener {
1798 listener.on_index_build_abort(_region_file_id).await;
1799 }
1800 }
1801
1802 pub(crate) async fn on_index_build_before_manifest_commit(
1803 &self,
1804 _region_file_id: RegionFileId,
1805 ) {
1806 #[cfg(any(test, feature = "test"))]
1807 if let Some(listener) = &self.listener {
1808 listener
1809 .on_index_build_before_manifest_commit(_region_file_id)
1810 .await;
1811 }
1812 }
1813
1814 pub(crate) async fn on_index_build_manifest_committed(&self, _region_file_id: RegionFileId) {
1815 #[cfg(any(test, feature = "test"))]
1816 if let Some(listener) = &self.listener {
1817 listener
1818 .on_index_build_manifest_committed(_region_file_id)
1819 .await;
1820 }
1821 }
1822}
1823
1824#[cfg(test)]
1825mod tests {
1826 use super::*;
1827 use crate::test_util::TestEnv;
1828
1829 #[test]
1830 fn test_region_id_to_index() {
1831 let num_workers = 4;
1832
1833 let region_id = RegionId::new(1, 2);
1834 let index = region_id_to_index(region_id, num_workers);
1835 assert_eq!(index, 3);
1836
1837 let region_id = RegionId::new(2, 3);
1838 let index = region_id_to_index(region_id, num_workers);
1839 assert_eq!(index, 1);
1840 }
1841
1842 #[tokio::test]
1843 async fn test_worker_group_start_stop() {
1844 for (enabled, existing_index) in [(false, false), (true, false), (false, true)] {
1845 let env = TestEnv::with_prefix("group-stop").await;
1847 let root = env.data_home().join("series_index");
1848 if existing_index {
1849 tokio::fs::create_dir_all(&root).await.unwrap();
1850 tokio::fs::write(root.join("retained"), b"index")
1851 .await
1852 .unwrap();
1853 }
1854 let group = env
1855 .create_worker_group(MitoConfig {
1856 num_workers: 4,
1857 experimental_enable_series_index: enabled,
1858 experimental_series_index_maintenance_interval: Duration::from_secs(3600),
1859 ..Default::default()
1860 })
1861 .await;
1862
1863 for worker in &group.workers {
1864 assert_eq!(worker.series_index_task_state.is_some(), enabled);
1865 assert_eq!(worker.series_index_handle.lock().await.is_some(), enabled);
1866 }
1867 assert_eq!(root.is_dir(), enabled || existing_index);
1868 if existing_index {
1869 assert_eq!(
1870 tokio::fs::read(root.join("retained")).await.unwrap(),
1871 b"index"
1872 );
1873 }
1874
1875 tokio::time::timeout(Duration::from_secs(5), group.stop())
1876 .await
1877 .expect("series-index tasks should stop without waiting for their interval")
1878 .unwrap();
1879 }
1880 }
1881}