Skip to main content

mito2/
worker.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Structs and utilities for writing regions.
16
17mod 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
97/// Identifier for a worker.
98pub(crate) type WorkerId = u32;
99
100pub(crate) const DROPPING_MARKER_FILE: &str = ".dropping";
101
102/// Interval to check whether regions should flush.
103pub(crate) const CHECK_REGION_INTERVAL: Duration = Duration::from_secs(60);
104/// Max delay to check region periodical tasks.
105pub(crate) const MAX_INITIAL_CHECK_DELAY_SECS: u64 = 60 * 3;
106
107#[cfg_attr(doc, aquamarine::aquamarine)]
108/// A fixed size group of [RegionWorkers](RegionWorker).
109///
110/// A worker group binds each region to a specific [RegionWorker] and sends
111/// requests to region's dedicated worker.
112///
113/// ```mermaid
114/// graph LR
115///
116/// RegionRequest -- Route by region id --> Worker0 & Worker1
117///
118/// subgraph MitoEngine
119///     subgraph WorkerGroup
120///         Worker0["RegionWorker 0"]
121///         Worker1["RegionWorker 1"]
122///     end
123/// end
124///
125/// Chan0[" Request channel 0"]
126/// Chan1[" Request channel 1"]
127/// WorkerThread1["RegionWorkerLoop 1"]
128///
129/// subgraph WorkerThread0["RegionWorkerLoop 0"]
130///     subgraph RegionMap["RegionMap (regions bound to worker 0)"]
131///         Region0["Region 0"]
132///         Region2["Region 2"]
133///     end
134///     Buffer0["RequestBuffer"]
135///
136///     Buffer0 -- modify regions --> RegionMap
137/// end
138///
139/// Worker0 --> Chan0
140/// Worker1 --> Chan1
141/// Chan0 --> Buffer0
142/// Chan1 --> WorkerThread1
143/// ```
144pub(crate) struct WorkerGroup {
145    /// Workers of the group.
146    workers: Vec<RegionWorker>,
147    /// Flush background job pool.
148    flush_job_pool: SchedulerRef,
149    /// Compaction background job pool.
150    compact_job_pool: SchedulerRef,
151    /// Scheduler for index build jobs.
152    index_build_job_pool: SchedulerRef,
153    /// Scheduler for file purgers.
154    purge_scheduler: SchedulerRef,
155    /// Cache.
156    cache_manager: CacheManagerRef,
157    /// File reference manager.
158    file_ref_manager: FileReferenceManagerRef,
159    /// Gc limiter to limit concurrent gc jobs.
160    gc_limiter: GcLimiterRef,
161    node_lease: Arc<NodeLease>,
162    /// Object store manager.
163    object_store_manager: ObjectStoreManagerRef,
164    /// Puffin manager factory.
165    puffin_manager_factory: PuffinManagerFactory,
166    /// Intermediate manager.
167    intermediate_manager: IntermediateManager,
168    /// Schema metadata manager.
169    schema_metadata_manager: SchemaMetadataManagerRef,
170}
171
172impl WorkerGroup {
173    /// Starts a worker group.
174    ///
175    /// The number of workers should be power of two.
176    #[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        // We use another scheduler to avoid purge jobs blocking other jobs.
210        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        // A sweep waits for a permit while a GC job takes one without waiting, so
249        // sharing the GC limiter would fail GC jobs and report sweeps as GC jobs.
250        // A datanode that allows no concurrent GC job runs no sweep either: a
251        // limiter of zero would never hand out the permit the task waits for.
252        #[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    /// Stops the worker group.
310    pub(crate) async fn stop(&self) -> Result<()> {
311        info!("Stop region worker group");
312
313        // TODO(yingwen): Do we need to stop gracefully?
314        // Stops the scheduler gracefully.
315        self.compact_job_pool.stop(true).await?;
316        // Stops the scheduler gracefully.
317        self.flush_job_pool.stop(true).await?;
318        // Stops the purge scheduler gracefully.
319        self.purge_scheduler.stop(true).await?;
320        // Stops the index build job pool gracefully.
321        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    /// Submits a request to a worker in the group.
329    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    /// Submits the writes of one request, those of each worker in one message, so that the
338    /// worker handles them in one batch. The messages are sent concurrently, so a full worker
339    /// queue holds back no other worker. A write whose worker has stopped receives the error a
340    /// write submitted on its own gets.
341    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    /// Returns true if the specific region exists.
372    pub(crate) fn is_region_exists(&self, region_id: RegionId) -> bool {
373        self.worker(region_id).is_region_exists(region_id)
374    }
375
376    /// Returns true if the specific region is opening.
377    pub(crate) fn is_region_opening(&self, region_id: RegionId) -> bool {
378        self.worker(region_id).is_region_opening(region_id)
379    }
380
381    /// Returns true if the specific region is catching up.
382    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    /// Returns region of specific `region_id`.
387    ///
388    /// This method should not be public.
389    pub(crate) fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
390        self.worker(region_id).get_region(region_id)
391    }
392
393    /// Returns cache of the group.
394    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    /// Get worker for specific `region_id`.
411    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// Tests methods.
441#[cfg(any(test, feature = "test"))]
442impl WorkerGroup {
443    /// Starts a worker group with `write_buffer_manager` and `listener` for tests.
444    ///
445    /// The number of workers should be power of two.
446    #[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        // A sweep waits for a permit while a GC job takes one without waiting, so
515        // sharing the GC limiter would fail GC jobs and report sweeps as GC jobs.
516        // A datanode that allows no concurrent GC job runs no sweep either: a
517        // limiter of zero would never hand out the permit the task waits for.
518        #[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    /// Returns the purge scheduler.
575    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
585/// Opens the fixed local series-index store only when the feature is enabled.
586async 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
629/// Computes a initial check delay for a worker.
630pub(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
635/// Worker start config.
636struct 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    /// Watch channel sender to notify workers to handle stalled requests.
655    flush_sender: watch::Sender<()>,
656    /// Watch channel receiver to wait for background flush job.
657    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    /// Limiter for the temporary file sweeps, or `None` if the datanode allows
665    /// no concurrent GC job.
666    sweep_limiter: Option<Arc<Semaphore>>,
667    node_lease: Arc<NodeLease>,
668}
669
670impl<S: LogStore> WorkerStarter<S> {
671    /// Starts a region worker and its background thread.
672    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
790/// Worker to write and alter regions bound to it.
791pub(crate) struct RegionWorker {
792    /// Id of the worker.
793    id: WorkerId,
794    /// Regions bound to the worker.
795    regions: RegionMapRef,
796    /// The opening regions.
797    opening_regions: OpeningRegionsRef,
798    /// The catching up regions.
799    catchup_regions: CatchupRegionsRef,
800    /// Request sender.
801    sender: Sender<WorkerRequestWithTime>,
802    /// Handle to the worker thread.
803    handle: Mutex<Option<JoinHandle<()>>>,
804    /// Handle to the sequential series-index task.
805    series_index_handle: Mutex<Option<JoinHandle<()>>>,
806    /// Controls the worker-owned series-index task.
807    series_index_task_state: Option<Arc<SeriesIndexTaskState>>,
808    /// Whether to run the worker thread.
809    running: Arc<AtomicBool>,
810}
811
812impl RegionWorker {
813    /// Submits request to background worker thread.
814    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    /// Submits request to background worker thread, or returns it if the worker is stopped.
821    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            // Manually set the running flag to false to avoid printing more warning logs.
837            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    /// Stop the worker.
848    ///
849    /// This method waits until the worker thread exists.
850    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    /// Returns true if the worker is still running.
885    fn is_running(&self) -> bool {
886        self.running.load(Ordering::Relaxed)
887    }
888
889    /// Sets whether the worker is still running.
890    fn set_running(&self, value: bool) {
891        self.running.store(value, Ordering::Relaxed)
892    }
893
894    /// Returns true if the worker contains specific region.
895    fn is_region_exists(&self, region_id: RegionId) -> bool {
896        self.regions.is_region_exists(region_id)
897    }
898
899    /// Returns true if the region is opening.
900    fn is_region_opening(&self, region_id: RegionId) -> bool {
901        self.opening_regions.is_region_exists(region_id)
902    }
903
904    /// Returns true if the region is catching up.
905    fn is_region_catching_up(&self, region_id: RegionId) -> bool {
906        self.catchup_regions.is_region_exists(region_id)
907    }
908
909    /// Returns region of specific `region_id`.
910    fn get_region(&self, region_id: RegionId) -> Option<MitoRegionRef> {
911        self.regions.get_region(region_id)
912    }
913
914    #[cfg(test)]
915    /// Returns the [OpeningRegionsRef].
916    pub(crate) fn opening_regions(&self) -> &OpeningRegionsRef {
917        &self.opening_regions
918    }
919
920    #[cfg(test)]
921    /// Returns the [CatchupRegionsRef].
922    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            // Once we drop the sender, the worker thread will receive a disconnected error.
935        }
936    }
937}
938
939type RequestBuffer = Vec<WorkerRequest>;
940
941/// Buffer for stalled write requests.
942///
943/// Maintains stalled write requests and their estimated size.
944#[derive(Default)]
945pub(crate) struct StalledRequests {
946    /// Stalled requests.
947    /// Remember to use `StalledRequests::stalled_count()` to get the total number of stalled requests
948    /// instead of `StalledRequests::requests.len()`.
949    ///
950    /// Key: RegionId
951    /// Value: (estimated size, stalled requests)
952    pub(crate) requests:
953        HashMap<RegionId, (usize, Vec<SenderWriteRequest>, Vec<SenderBulkRequest>)>,
954    /// Estimated size of all stalled requests.
955    pub(crate) estimated_size: usize,
956}
957
958impl StalledRequests {
959    /// Returns the estimated size of stalled requests for a region.
960    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    /// Appends stalled requests.
968    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    /// Pushes a stalled request to the buffer.
982    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    /// Removes stalled requests of specific region.
1000    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    /// Returns the total number of all stalled requests.
1013    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
1021/// Background worker loop to handle requests.
1022struct RegionWorkerLoop<S> {
1023    /// Id of the worker.
1024    id: WorkerId,
1025    /// Engine config.
1026    config: Arc<MitoConfig>,
1027    /// Regions bound to the worker.
1028    regions: RegionMapRef,
1029    /// Regions that are not yet fully dropped.
1030    dropping_regions: RegionMapRef,
1031    /// Regions that are opening.
1032    opening_regions: OpeningRegionsRef,
1033    /// Regions that are catching up.
1034    catchup_regions: CatchupRegionsRef,
1035    /// Request sender.
1036    sender: Sender<WorkerRequestWithTime>,
1037    /// Request receiver.
1038    receiver: Receiver<WorkerRequestWithTime>,
1039    /// WAL of the engine.
1040    wal: Wal<S>,
1041    /// Manages object stores for manifest and SSTs.
1042    object_store_manager: ObjectStoreManagerRef,
1043    /// Whether the worker thread is still running.
1044    running: Arc<AtomicBool>,
1045    /// Memtable builder provider for each region.
1046    memtable_builder_provider: MemtableBuilderProvider,
1047    /// Background purge job scheduler.
1048    purge_scheduler: SchedulerRef,
1049    /// Engine write buffer manager.
1050    write_buffer_manager: WriteBufferManagerRef,
1051    /// Scheduler for index build task.
1052    index_build_scheduler: IndexBuildScheduler,
1053    /// Controls the worker-owned series-index task.
1054    series_index_task_state: Option<Arc<SeriesIndexTaskState>>,
1055    /// Local store for series and range indexes managed by reconciliation.
1056    series_index_store: Option<ObjectStore>,
1057    series_index_purger: Option<IndexFilePurger>,
1058    /// Schedules background flush requests.
1059    flush_scheduler: FlushScheduler,
1060    /// Scheduler for compaction tasks.
1061    compaction_scheduler: CompactionScheduler,
1062    /// Stalled write requests.
1063    stalled_requests: StalledRequests,
1064    /// Event listener for tests.
1065    listener: WorkerListener,
1066    /// Cache.
1067    cache_manager: CacheManagerRef,
1068    /// Puffin manager factory for index.
1069    puffin_manager_factory: PuffinManagerFactory,
1070    /// Intermediate manager for inverted index.
1071    intermediate_manager: IntermediateManager,
1072    /// Provider to get current time.
1073    time_provider: TimeProviderRef,
1074    /// Last time to check regions periodically.
1075    last_periodical_check_millis: i64,
1076    /// Watch channel sender to notify workers to handle stalled requests.
1077    flush_sender: watch::Sender<()>,
1078    /// Watch channel receiver to wait for background flush job.
1079    flush_receiver: watch::Receiver<()>,
1080    /// Gauge of stalling request count.
1081    stalling_count: IntGauge,
1082    /// Gauge of regions in the worker.
1083    region_count: IntGauge,
1084    /// Histogram of request wait time for this worker.
1085    request_wait_time: Histogram,
1086    /// Queues for region edit requests.
1087    region_edit_queues: RegionEditQueues,
1088    /// Database level metadata manager.
1089    schema_metadata_manager: SchemaMetadataManagerRef,
1090    /// Datanode level file references manager.
1091    file_ref_manager: FileReferenceManagerRef,
1092    /// Partition expr fetcher used to backfill partition expr on open for compatibility.
1093    partition_expr_fetcher: PartitionExprFetcherRef,
1094    /// Semaphore to control flush concurrency.
1095    flush_semaphore: Arc<Semaphore>,
1096    /// Plugins for flush hooks.
1097    plugins: Plugins,
1098    #[cfg(feature = "hdfs-object-store")]
1099    /// Limiter for the temporary file sweeps, or `None` if the datanode allows
1100    /// no concurrent GC job.
1101    sweep_limiter: Option<Arc<Semaphore>>,
1102    node_lease: Arc<NodeLease>,
1103}
1104
1105impl<S: LogStore> RegionWorkerLoop<S> {
1106    /// Publishes a loaded region together with its resident compaction state.
1107    fn register_region(&mut self, region: MitoRegionRef) {
1108        self.compaction_scheduler
1109            .register_region(&region.version_control, &region.access_layer);
1110        self.regions.insert_region(region);
1111    }
1112
1113    /// Starts the worker loop.
1114    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        // Buffer to retrieve requests from receiver.
1123        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            // Clear the buffer before handling next batch of requests.
1134            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                            // Observe the wait time
1148                            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                        // The channel is disconnected.
1164                        None => break,
1165                    }
1166                }
1167                recv_res = self.flush_receiver.changed() => {
1168                    if recv_res.is_err() {
1169                        // The channel is disconnected.
1170                        break;
1171                    } else {
1172                        // Also flush this worker if other workers trigger flush as this worker may have
1173                        // a large memtable to flush. We may not have chance to flush that memtable if we
1174                        // never write to this worker. So only flushing other workers may not release enough
1175                        // memory.
1176                        self.maybe_flush_worker();
1177                        // A flush job is finished, handles stalled requests.
1178                        self.handle_stalled_requests().await;
1179                        continue;
1180                    }
1181                }
1182                _ = &mut sleep => {
1183                    // Timeout. Checks periodical tasks.
1184                    self.handle_periodical_tasks();
1185                    continue;
1186                }
1187            }
1188
1189            if self.flush_receiver.has_changed().unwrap_or(false) {
1190                // Always checks whether we could process stalled requests to avoid a request
1191                // hangs too long.
1192                // If the channel is closed, do nothing.
1193                self.handle_stalled_requests().await;
1194            }
1195
1196            // Try to recv more requests from the channel.
1197            for _ in 1..self.config.worker_request_batch_size {
1198                // We have received one request so we start from 1.
1199                match self.receiver.try_recv() {
1200                    Ok(request_with_time) => {
1201                        // Observe the wait time
1202                        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                    // We still need to handle remaining requests.
1220                    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    /// Dispatches and processes requests.
1273    ///
1274    /// `general_requests` should not contain categorized write, ddl, or bulk insert requests.
1275    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                    // These requests are categorized before dispatching general requests.
1286                    continue;
1287                }
1288                WorkerRequest::BulkInserts(_) => unreachable!("bulk inserts are buffered"),
1289                WorkerRequest::Background { region_id, notify } => {
1290                    if matches!(
1291                        &notify,
1292                        BackgroundNotify::RegionEdit(edit_result)
1293                            if edit_result.update_region_state
1294                    ) {
1295                        // Region state must be Editing when reach here.
1296                        // This call only moves write/bulk write request into stall queue. When region edit result
1297                        // is processed inside handle_background_notify and region state is switched back to Writable,
1298                        // stalled request will be processed before the next region edit is dequeued from
1299                        // RegionEditQueue immediately in handle_region_edit_result. It not only ensured pending writes
1300                        // are processed in time, but also prevents them from starvation.
1301                        // TODO(hl): maybe we need to merge those queues for pending requests like pending_ddl,
1302                        // region edits and stalled request, so we can simplify the coordination between these queues.
1303                        self.handle_buffered_region_write_requests(
1304                            &region_id,
1305                            write_requests,
1306                            bulk_requests,
1307                        )
1308                        .await;
1309                    }
1310                    // For background notify, we handle it directly.
1311                    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        // Handles all write requests first. So we can alter regions without
1340        // considering existing write requests.
1341        self.handle_write_requests(write_requests, bulk_requests, true)
1342            .await;
1343
1344        self.handle_ddl_requests(ddl_requests).await;
1345    }
1346
1347    /// Takes and handles all ddl requests.
1348    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    /// Handle periodical tasks such as region auto flush.
1425    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    /// Handles region background request
1443    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                // Finish this opening batch before the worker can process a close/reopen,
1450                // without waiting for the background opener to resume after the ACK.
1451                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    /// Handles `set_region_role_gracefully`.
1507    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                // Fence writes here: writes the worker handles later see
1519                // Downgrading, and earlier ones have finished. This only
1520                // changes a writable or staging leader, but always fences
1521                // promotions issued before.
1522                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            // We need to do this in background as we need the manifest lock.
1529            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                        // Only a writable leader reclaims what a writer that died
1537                        // before it renamed its temporary file left behind.
1538                        #[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    /// Cleans up the worker.
1564    async fn clean(&self) {
1565        // Closes remaining regions.
1566        let regions = self.regions.list_regions();
1567        for region in regions {
1568            region.stop().await;
1569        }
1570
1571        self.regions.clear();
1572    }
1573
1574    /// Notifies the whole group that a flush job is finished so other
1575    /// workers can handle stalled requests.
1576    fn notify_group(&mut self) {
1577        // Notifies all receivers.
1578        let _ = self.flush_sender.send(());
1579        // Marks the receiver in current worker as seen so the loop won't be waked up immediately.
1580        self.flush_receiver.borrow_and_update();
1581    }
1582}
1583
1584/// Wrapper that only calls event listener in tests.
1585#[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    /// Notifies the background opener after the worker acknowledges region registration.
1600    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    /// Flush is finished successfully.
1608    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        // Avoid compiler warning.
1614        let _ = region_id;
1615    }
1616
1617    /// Engine is stalled.
1618    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    /// A batch of writes is appended to the WAL.
1626    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        // Avoid compiler warning.
1639        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        // Avoid compiler warning.
1662        let _ = region_id;
1663        None
1664    }
1665
1666    /// On later drop task is finished.
1667    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        // Avoid compiler warning.
1673        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        // Avoid compiler warning.
1683        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        // Avoid compiler warning.
1692        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    /// Exposes a per-unit test gate before merge starts.
1724    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    /// Exposes a per-unit test gate between manifest commit and worker notification.
1734    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    /// Reports unit visibility to the test listener independently of request completion.
1744    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            // Use a fresh WAL directory for each case: stopping workers does not stop the log store.
1846            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}