Skip to main content

mito2/
request.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//! Worker requests.
16
17use std::collections::HashMap;
18use std::sync::Arc;
19use std::time::Instant;
20
21use api::helper::{
22    ColumnDataTypeWrapper, is_column_type_value_eq, is_semantic_type_eq, proto_value_type,
23    proto_value_type_match,
24};
25use api::v1::column_def::options_from_column_schema;
26use api::v1::{ColumnDataType, ColumnSchema, OpType, Rows, SemanticType, Value, WriteHint};
27use common_telemetry::info;
28use datatypes::arrow::array::{Array, ArrayRef};
29use datatypes::arrow::datatypes::DataType as ArrowDataType;
30use datatypes::arrow::record_batch::RecordBatch;
31use datatypes::prelude::DataType;
32use partition::expr::PartitionExpr;
33use prometheus::HistogramTimer;
34use prost::Message;
35use smallvec::SmallVec;
36use snafu::{OptionExt, ResultExt, ensure};
37use store_api::ManifestVersion;
38use store_api::codec::{PrimaryKeyEncoding, infer_primary_key_encoding_from_hint};
39use store_api::metadata::{ColumnMetadata, RegionMetadata, RegionMetadataRef};
40use store_api::region_engine::{
41    MitoCopyRegionFromResponse, SetRegionRoleStateResponse, SettableRegionRoleState,
42};
43use store_api::region_request::{
44    AffectedRows, ApplyStagingManifestRequest, EnterStagingRequest, RegionAlterRequest,
45    RegionBuildIndexRequest, RegionBulkInsertsRequest, RegionCatchupRequest, RegionCleanUpRequest,
46    RegionCloseRequest, RegionCompactRequest, RegionCreateRequest, RegionDropRequest,
47    RegionFlushRequest, RegionOpenRequest, RegionRequest, RegionTruncateRequest,
48    StagingPartitionDirective,
49};
50use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
51use store_api::storage::{FileId, RegionId, SequenceNumber};
52use tokio::sync::oneshot::{self, Receiver, Sender};
53
54use crate::compaction::{CompactionExecution, CompactionPickFinished};
55use crate::error::{
56    CompactRegionSnafu, ConvertColumnDataTypeSnafu, CreateDefaultSnafu, Error, FillDefaultSnafu,
57    FlushRegionSnafu, InvalidPartitionExprSnafu, InvalidRequestSnafu, MissingPartitionExprSnafu,
58    Result, UnexpectedSnafu,
59};
60use crate::flush::FlushReason;
61use crate::manifest::action::{RegionEdit, TruncateKind};
62use crate::memtable::MemtableId;
63use crate::memtable::bulk::part::BulkPart;
64use crate::metrics::COMPACTION_ELAPSED_TOTAL;
65use crate::region::MitoRegionRef;
66use crate::region::options::RegionOptions;
67use crate::sst::file::FileMeta;
68use crate::sst::index::IndexBuildType;
69use crate::wal::EntryId;
70use crate::wal::entry_distributor::WalEntryReceiver;
71
72/// Request to write a region.
73#[derive(Debug)]
74pub struct WriteRequest {
75    /// Region to write.
76    pub region_id: RegionId,
77    /// Type of the write request.
78    pub op_type: OpType,
79    /// Rows to write.
80    pub rows: Rows,
81    /// Map column name to column index in `rows`.
82    pub name_to_index: HashMap<String, usize>,
83    /// Whether each column has null.
84    pub has_null: Vec<bool>,
85    /// Whether this insert should skip WAL. Never applies to deletes.
86    pub skip_wal: bool,
87    /// Write hint.
88    pub hint: Option<WriteHint>,
89    /// Region metadata on the time of this request is created.
90    pub(crate) region_metadata: Option<RegionMetadataRef>,
91    /// Partition expression version for the region.
92    pub partition_expr_version: Option<u64>,
93}
94
95impl WriteRequest {
96    /// Creates a new request.
97    ///
98    /// Returns `Err` if `rows` are invalid.
99    pub fn new(
100        region_id: RegionId,
101        op_type: OpType,
102        rows: Rows,
103        region_metadata: Option<RegionMetadataRef>,
104    ) -> Result<WriteRequest> {
105        let mut name_to_index = HashMap::with_capacity(rows.schema.len());
106        for (index, column) in rows.schema.iter().enumerate() {
107            ensure!(
108                name_to_index
109                    .insert(column.column_name.clone(), index)
110                    .is_none(),
111                InvalidRequestSnafu {
112                    region_id,
113                    reason: format!("duplicate column {}", column.column_name),
114                }
115            );
116        }
117
118        let mut has_null = vec![false; rows.schema.len()];
119        for row in &rows.rows {
120            ensure!(
121                row.values.len() == rows.schema.len(),
122                InvalidRequestSnafu {
123                    region_id,
124                    reason: format!(
125                        "row has {} columns but schema has {}",
126                        row.values.len(),
127                        rows.schema.len()
128                    ),
129                }
130            );
131
132            for (i, (value, column_schema)) in row.values.iter().zip(&rows.schema).enumerate() {
133                validate_proto_value(region_id, value, column_schema)?;
134
135                if value.value_data.is_none() {
136                    has_null[i] = true;
137                }
138            }
139        }
140
141        Ok(WriteRequest {
142            region_id,
143            op_type,
144            rows,
145            name_to_index,
146            has_null,
147            hint: None,
148            skip_wal: false,
149            region_metadata,
150            partition_expr_version: None,
151        })
152    }
153
154    /// Sets the request-level WAL policy.
155    pub fn with_skip_wal(mut self, skip_wal: bool) -> Self {
156        self.skip_wal = skip_wal;
157        self
158    }
159
160    /// Sets the write hint.
161    pub fn with_hint(mut self, hint: Option<WriteHint>) -> Self {
162        self.hint = hint;
163        self
164    }
165
166    pub fn with_partition_expr_version(mut self, partition_expr_version: Option<u64>) -> Self {
167        self.partition_expr_version = partition_expr_version;
168        self
169    }
170
171    /// Returns the encoding hint.
172    pub fn primary_key_encoding(&self) -> PrimaryKeyEncoding {
173        infer_primary_key_encoding_from_hint(self.hint.as_ref())
174    }
175
176    /// Returns estimated size of the request.
177    pub(crate) fn estimated_size(&self) -> usize {
178        let row_size = self
179            .rows
180            .rows
181            .first()
182            .map(|row| row.encoded_len())
183            .unwrap_or(0);
184        row_size * self.rows.rows.len()
185    }
186
187    /// Gets column index by name.
188    pub fn column_index_by_name(&self, name: &str) -> Option<usize> {
189        self.name_to_index.get(name).copied()
190    }
191
192    /// Checks schema of rows is compatible with schema of the region.
193    ///
194    /// If column with default value is missing, it returns a special [FillDefault](crate::error::Error::FillDefault)
195    /// error.
196    pub(crate) fn check_schema(&self, metadata: &RegionMetadata) -> Result<()> {
197        debug_assert_eq!(self.region_id, metadata.region_id);
198
199        let region_id = self.region_id;
200        // Index all columns in rows.
201        let rows_columns = self
202            .rows
203            .schema
204            .iter()
205            .map(|schema| {
206                let column = RowColumn {
207                    request: self,
208                    schema,
209                };
210                (schema.column_name.as_str(), column)
211            })
212            .collect();
213        let need_fill_default =
214            check_request_columns(region_id, metadata, rows_columns, |column| {
215                self.check_missing_column(column)
216            })?;
217
218        // If we need to fill default values, return a special error.
219        ensure!(!need_fill_default, FillDefaultSnafu { region_id });
220
221        Ok(())
222    }
223
224    /// Tries to fill missing columns.
225    ///
226    /// Currently, our protobuf format might be inefficient when we need to fill lots of null
227    /// values.
228    pub(crate) fn fill_missing_columns(&mut self, metadata: &RegionMetadata) -> Result<()> {
229        debug_assert_eq!(self.region_id, metadata.region_id);
230
231        let mut columns_to_fill = vec![];
232        for column in &metadata.column_metadatas {
233            if !self.name_to_index.contains_key(&column.column_schema.name) {
234                columns_to_fill.push(column);
235            }
236        }
237        self.fill_columns(columns_to_fill)?;
238
239        Ok(())
240    }
241
242    /// Checks the schema and fill missing columns.
243    pub(crate) fn maybe_fill_missing_columns(&mut self, metadata: &RegionMetadata) -> Result<()> {
244        if let Err(e) = self.check_schema(metadata) {
245            if e.is_fill_default() {
246                // TODO(yingwen): Add metrics for this case.
247                // We need to fill default value. The write request may be a request
248                // sent before changing the schema.
249                self.fill_missing_columns(metadata)?;
250            } else {
251                return Err(e);
252            }
253        }
254
255        Ok(())
256    }
257
258    /// Fills default value for specific `columns`.
259    fn fill_columns(&mut self, columns: Vec<&ColumnMetadata>) -> Result<()> {
260        let mut default_values = Vec::with_capacity(columns.len());
261        let mut columns_to_fill = Vec::with_capacity(columns.len());
262        for column in columns {
263            let default_value = self.column_default_value(column)?;
264            if default_value.value_data.is_some() {
265                default_values.push(default_value);
266                columns_to_fill.push(column);
267            }
268        }
269
270        for row in &mut self.rows.rows {
271            row.values.extend(default_values.iter().cloned());
272        }
273
274        for column in columns_to_fill {
275            let (datatype, datatype_ext) =
276                ColumnDataTypeWrapper::try_from(column.column_schema.data_type.clone())
277                    .with_context(|_| ConvertColumnDataTypeSnafu {
278                        reason: format!(
279                            "no protobuf type for column {} ({:?})",
280                            column.column_schema.name, column.column_schema.data_type
281                        ),
282                    })?
283                    .to_parts();
284            self.rows.schema.push(ColumnSchema {
285                column_name: column.column_schema.name.clone(),
286                datatype: datatype as i32,
287                semantic_type: column.semantic_type as i32,
288                datatype_extension: datatype_ext,
289                options: options_from_column_schema(&column.column_schema),
290            });
291        }
292
293        Ok(())
294    }
295
296    /// Checks whether we should allow a row doesn't provide this column.
297    fn check_missing_column(&self, column: &ColumnMetadata) -> Result<()> {
298        if self.op_type == OpType::Delete {
299            if column.semantic_type == SemanticType::Field {
300                // For delete request, all tags and timestamp is required. We don't fill default
301                // tag or timestamp while deleting rows.
302                return Ok(());
303            } else {
304                return InvalidRequestSnafu {
305                    region_id: self.region_id,
306                    reason: format!("delete requests need column {}", column.column_schema.name),
307                }
308                .fail();
309            }
310        }
311
312        // Not a delete request. Checks whether they have default value.
313        check_column_can_be_filled(self.region_id, column)
314    }
315
316    /// Returns the default value for specific column.
317    fn column_default_value(&self, column: &ColumnMetadata) -> Result<Value> {
318        let default_value = match self.op_type {
319            OpType::Delete => {
320                ensure!(
321                    column.semantic_type == SemanticType::Field,
322                    InvalidRequestSnafu {
323                        region_id: self.region_id,
324                        reason: format!(
325                            "delete requests need column {}",
326                            column.column_schema.name
327                        ),
328                    }
329                );
330
331                // For delete request, we need a default value for padding so we
332                // can delete a row even a field doesn't have a default value. So the
333                // value doesn't need to following the default value constraint of the
334                // column.
335                if column.column_schema.is_nullable() {
336                    datatypes::value::Value::Null
337                } else {
338                    column.column_schema.data_type.default_value()
339                }
340            }
341            OpType::Put => {
342                // For put requests, we use the default value from column schema.
343                if column.column_schema.is_default_impure() {
344                    UnexpectedSnafu {
345                        reason: format!(
346                            "unexpected impure default value with region_id: {}, column: {}, default_value: {:?}",
347                            self.region_id,
348                            column.column_schema.name,
349                            column.column_schema.default_constraint(),
350                        ),
351                    }
352                    .fail()?
353                }
354                column
355                    .column_schema
356                    .create_default()
357                    .context(CreateDefaultSnafu {
358                        region_id: self.region_id,
359                        column: &column.column_schema.name,
360                    })?
361                    // This column doesn't have default value.
362                    .with_context(|| InvalidRequestSnafu {
363                        region_id: self.region_id,
364                        reason: format!(
365                            "column {} does not have default value",
366                            column.column_schema.name
367                        ),
368                    })?
369            }
370        };
371
372        // Convert default value into proto's value.
373        Ok(api::helper::to_grpc_value(default_value))
374    }
375}
376
377/// A column of a write request, as [check_request_columns] sees it.
378trait RequestColumn {
379    /// Returns whether the column has the data type of `column`.
380    fn has_type_of(&self, column: &ColumnMetadata) -> bool;
381
382    /// Describes the data type of the column in errors.
383    fn type_name(&self) -> String;
384
385    /// Returns the semantic type of the column if the request carries one.
386    fn semantic_type(&self) -> Option<i32>;
387
388    /// Returns whether the column contains nulls.
389    fn has_null(&self) -> bool;
390}
391
392/// Checks the columns of a write request against the region schema. Each column
393/// must be a column of the region, have its data type and, if the request carries
394/// one, its semantic type, and contain nulls only if the region column is
395/// nullable. `check_missing` checks the region columns that the request doesn't
396/// have. Returns whether there are such columns.
397fn check_request_columns<C: RequestColumn>(
398    region_id: RegionId,
399    metadata: &RegionMetadata,
400    mut columns: HashMap<&str, C>,
401    mut check_missing: impl FnMut(&ColumnMetadata) -> Result<()>,
402) -> Result<bool> {
403    let mut has_missing = false;
404    for column in &metadata.column_metadatas {
405        let Some(input_col) = columns.remove(column.column_schema.name.as_str()) else {
406            check_missing(column)?;
407            has_missing = true;
408            continue;
409        };
410        // Check data type.
411        ensure!(
412            input_col.has_type_of(column),
413            InvalidRequestSnafu {
414                region_id,
415                reason: format!(
416                    "column {} expect type {:?}, given: {}",
417                    column.column_schema.name,
418                    column.column_schema.data_type,
419                    input_col.type_name(),
420                )
421            }
422        );
423
424        // Check semantic type.
425        if let Some(semantic_type) = input_col.semantic_type() {
426            ensure!(
427                is_semantic_type_eq(semantic_type, column.semantic_type),
428                InvalidRequestSnafu {
429                    region_id,
430                    reason: format!(
431                        "column {} has semantic type {:?}, given: {}({})",
432                        column.column_schema.name,
433                        column.semantic_type,
434                        api::v1::SemanticType::try_from(semantic_type)
435                            .map(|v| v.as_str_name())
436                            .unwrap_or("Unknown"),
437                        semantic_type
438                    ),
439                }
440            );
441        }
442
443        // Check nullable.
444        ensure!(
445            !input_col.has_null() || column.column_schema.is_nullable(),
446            InvalidRequestSnafu {
447                region_id,
448                reason: format!(
449                    "column {} is not null but input has null",
450                    column.column_schema.name
451                ),
452            }
453        );
454    }
455
456    // Checks all columns in the request exist in the region.
457    if !columns.is_empty() {
458        let names: Vec<_> = columns.into_keys().collect();
459        return InvalidRequestSnafu {
460            region_id,
461            reason: format!("unknown columns: {:?}", names),
462        }
463        .fail();
464    }
465
466    Ok(has_missing)
467}
468
469/// Checks that a write may leave out `column`, which is then filled with its
470/// default value or null.
471fn check_column_can_be_filled(region_id: RegionId, column: &ColumnMetadata) -> Result<()> {
472    ensure!(
473        column.column_schema.is_nullable() || column.column_schema.default_constraint().is_some(),
474        InvalidRequestSnafu {
475            region_id,
476            reason: format!("missing column {}", column.column_schema.name),
477        }
478    );
479    Ok(())
480}
481
482/// A column of the rows of a [WriteRequest].
483struct RowColumn<'a> {
484    request: &'a WriteRequest,
485    schema: &'a ColumnSchema,
486}
487
488impl RequestColumn for RowColumn<'_> {
489    fn has_type_of(&self, column: &ColumnMetadata) -> bool {
490        is_column_type_value_eq(
491            self.schema.datatype,
492            self.schema.datatype_extension.clone(),
493            &column.column_schema.data_type,
494        )
495    }
496
497    fn type_name(&self) -> String {
498        format!(
499            "{}({})",
500            ColumnDataType::try_from(self.schema.datatype)
501                .map(|v| v.as_str_name())
502                .unwrap_or("Unknown"),
503            self.schema.datatype,
504        )
505    }
506
507    fn semantic_type(&self) -> Option<i32> {
508        Some(self.schema.semantic_type)
509    }
510
511    fn has_null(&self) -> bool {
512        // Safety: `name_to_index` has every column of the request.
513        self.request.has_null[self.request.name_to_index[&self.schema.column_name]]
514    }
515}
516
517/// A column of the batch of a bulk insert.
518struct BulkColumn<'a> {
519    array: &'a ArrayRef,
520}
521
522impl RequestColumn for BulkColumn<'_> {
523    fn has_type_of(&self, column: &ColumnMetadata) -> bool {
524        let actual = self.array.data_type();
525        let data_type = &column.column_schema.data_type;
526        // The memtables convert tags of any string encoding.
527        if column.semantic_type == SemanticType::Tag && data_type.is_string() {
528            return match actual {
529                ArrowDataType::Dictionary(key, value) => key.is_integer() && value.is_string(),
530                actual => actual.is_string(),
531            };
532        }
533        *actual == data_type.as_arrow_type()
534    }
535
536    fn type_name(&self) -> String {
537        self.array.data_type().to_string()
538    }
539
540    fn semantic_type(&self) -> Option<i32> {
541        None
542    }
543
544    fn has_null(&self) -> bool {
545        self.array.logical_null_count() > 0
546    }
547}
548
549/// Checks the batch of a bulk insert against the region schema with the rules
550/// [WriteRequest::check_schema] applies to rows: column names are unique, every
551/// column is a column of the region with its Arrow type and contains nulls only
552/// if the region column is nullable, and a missing column is nullable or has a
553/// default value. Tags may use any string encoding. Sparse batches carry the
554/// tags in one encoded primary key column instead. Returns whether the batch
555/// misses columns that the writer fills.
556pub fn check_bulk_insert_batch(metadata: &RegionMetadata, batch: &RecordBatch) -> Result<bool> {
557    let region_id = metadata.region_id;
558    let sparse = metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse;
559    let mut primary_key = None;
560    let mut columns = HashMap::with_capacity(batch.num_columns());
561    for (field, array) in batch.schema_ref().fields().iter().zip(batch.columns()) {
562        let duplicate = if sparse && field.name() == PRIMARY_KEY_COLUMN_NAME {
563            primary_key.replace(array).is_some()
564        } else {
565            columns
566                .insert(field.name().as_str(), BulkColumn { array })
567                .is_some()
568        };
569        ensure!(
570            !duplicate,
571            InvalidRequestSnafu {
572                region_id,
573                reason: format!("duplicate column {}", field.name()),
574            }
575        );
576    }
577    if sparse {
578        let primary_key = primary_key.with_context(|| InvalidRequestSnafu {
579            region_id,
580            reason: format!("missing column {PRIMARY_KEY_COLUMN_NAME}"),
581        })?;
582        // The writer casts the encoded primary key to a dictionary of binary.
583        let type_matches = match primary_key.data_type() {
584            ArrowDataType::Binary => true,
585            ArrowDataType::Dictionary(key, value) => {
586                key.is_integer() && **value == ArrowDataType::Binary
587            }
588            _ => false,
589        };
590        ensure!(
591            type_matches && primary_key.logical_null_count() == 0,
592            InvalidRequestSnafu {
593                region_id,
594                reason: format!(
595                    "column {PRIMARY_KEY_COLUMN_NAME} must be binary without nulls, given: {}",
596                    primary_key.data_type()
597                ),
598            }
599        );
600    }
601    let mut needs_fill = false;
602    check_request_columns(region_id, metadata, columns, |column| {
603        if sparse && column.semantic_type == SemanticType::Tag {
604            Ok(())
605        } else {
606            needs_fill = true;
607            check_column_can_be_filled(region_id, column)
608        }
609    })?;
610    Ok(needs_fill)
611}
612
613/// Validate proto value schema.
614pub(crate) fn validate_proto_value(
615    region_id: RegionId,
616    value: &Value,
617    column_schema: &ColumnSchema,
618) -> Result<()> {
619    if let Some(value_type) = proto_value_type(value) {
620        let column_type = ColumnDataType::try_from(column_schema.datatype).map_err(|_| {
621            InvalidRequestSnafu {
622                region_id,
623                reason: format!(
624                    "column {} has unknown type {}",
625                    column_schema.column_name, column_schema.datatype
626                ),
627            }
628            .build()
629        })?;
630        ensure!(
631            proto_value_type_match(column_type, value_type),
632            InvalidRequestSnafu {
633                region_id,
634                reason: format!(
635                    "value has type {:?}, but column {} has type {:?}({})",
636                    value_type, column_schema.column_name, column_type, column_schema.datatype,
637                ),
638            }
639        );
640    }
641
642    Ok(())
643}
644
645/// Oneshot output result sender.
646#[derive(Debug)]
647pub struct OutputTx(Sender<Result<AffectedRows>>);
648
649impl OutputTx {
650    /// Creates a new output sender.
651    pub(crate) fn new(sender: Sender<Result<AffectedRows>>) -> OutputTx {
652        OutputTx(sender)
653    }
654
655    /// Sends the `result`.
656    pub(crate) fn send(self, result: Result<AffectedRows>) {
657        // Ignores send result.
658        let _ = self.0.send(result);
659    }
660}
661
662/// Optional output result sender.
663#[derive(Debug)]
664pub(crate) struct OptionOutputTx(Option<OutputTx>);
665
666impl OptionOutputTx {
667    /// Creates a sender.
668    pub(crate) fn new(sender: Option<OutputTx>) -> OptionOutputTx {
669        OptionOutputTx(sender)
670    }
671
672    /// Creates an empty sender.
673    pub(crate) fn none() -> OptionOutputTx {
674        OptionOutputTx(None)
675    }
676
677    /// Sends the `result` and consumes the inner sender.
678    pub(crate) fn send_mut(&mut self, result: Result<AffectedRows>) {
679        if let Some(sender) = self.0.take() {
680            sender.send(result);
681        }
682    }
683
684    /// Sends the `result` and consumes the sender.
685    pub(crate) fn send(mut self, result: Result<AffectedRows>) {
686        if let Some(sender) = self.0.take() {
687            sender.send(result);
688        }
689    }
690
691    /// Takes the inner sender.
692    pub(crate) fn take_inner(&mut self) -> Option<OutputTx> {
693        self.0.take()
694    }
695}
696
697impl From<Sender<Result<AffectedRows>>> for OptionOutputTx {
698    fn from(sender: Sender<Result<AffectedRows>>) -> Self {
699        Self::new(Some(OutputTx::new(sender)))
700    }
701}
702
703impl OnFailure for OptionOutputTx {
704    fn on_failure(&mut self, err: Error) {
705        self.send_mut(Err(err));
706    }
707}
708
709/// Callback on failure.
710pub(crate) trait OnFailure {
711    /// Handles `err` on failure.
712    fn on_failure(&mut self, err: Error);
713}
714
715/// Sender and write request.
716#[derive(Debug)]
717pub(crate) struct SenderWriteRequest {
718    /// Result sender.
719    pub(crate) sender: OptionOutputTx,
720    pub(crate) request: WriteRequest,
721}
722
723pub(crate) struct SenderBulkRequest {
724    pub(crate) skip_wal: bool,
725    pub(crate) sender: OptionOutputTx,
726    pub(crate) region_id: RegionId,
727    pub(crate) request: BulkPart,
728    pub(crate) region_metadata: Option<RegionMetadataRef>,
729    pub(crate) partition_expr_version: Option<u64>,
730}
731
732#[derive(Debug)]
733pub(crate) struct BulkInsertRequest {
734    pub(crate) metadata: Option<RegionMetadataRef>,
735    pub(crate) request: RegionBulkInsertsRequest,
736    pub(crate) sender: OptionOutputTx,
737}
738
739/// Request sent to a worker with timestamp
740#[derive(Debug)]
741pub(crate) struct WorkerRequestWithTime {
742    pub(crate) request: WorkerRequest,
743    pub(crate) created_at: Instant,
744}
745
746impl WorkerRequestWithTime {
747    pub(crate) fn new(request: WorkerRequest) -> Self {
748        Self {
749            request,
750            created_at: Instant::now(),
751        }
752    }
753}
754
755/// Request sent to a worker
756#[derive(Debug)]
757pub(crate) enum WorkerRequest {
758    /// Write to a region.
759    Write(SenderWriteRequest),
760
761    /// Ddl request to a region.
762    Ddl(SenderDdlRequest),
763
764    /// Notifications from internal background jobs.
765    Background {
766        /// Id of the region to send.
767        region_id: RegionId,
768        /// Internal notification.
769        notify: BackgroundNotify,
770    },
771
772    /// The internal commands.
773    SetRegionRoleStateGracefully {
774        /// Id of the region to send.
775        region_id: RegionId,
776        /// The [SettableRegionRoleState].
777        region_role_state: SettableRegionRoleState,
778        /// The sender of [SetReadonlyResponse].
779        sender: Sender<SetRegionRoleStateResponse>,
780    },
781
782    /// Notify a worker to stop.
783    Stop,
784
785    /// Use [RegionEdit] to edit a region directly.
786    EditRegion(RegionEditRequest),
787
788    /// Keep the manifest of a region up to date.
789    SyncRegion(RegionSyncRequest),
790
791    /// Bulk inserts request and region metadata.
792    BulkInserts(BulkInsertRequest),
793
794    /// Remap manifests request.
795    RemapManifests(RemapManifestsRequest),
796
797    /// Copy region from request.
798    CopyRegionFrom(CopyRegionFromRequest),
799}
800
801impl WorkerRequest {
802    /// Creates a new open region request.
803    pub(crate) fn new_open_region_request(
804        region_id: RegionId,
805        request: RegionOpenRequest,
806        entry_receiver: Option<WalEntryReceiver>,
807    ) -> (WorkerRequest, Receiver<Result<AffectedRows>>) {
808        let (sender, receiver) = oneshot::channel();
809
810        let worker_request = WorkerRequest::Ddl(SenderDdlRequest {
811            region_id,
812            sender: sender.into(),
813            request: DdlRequest::Open((request, entry_receiver)),
814        });
815
816        (worker_request, receiver)
817    }
818
819    /// Creates a new catchup region request.
820    pub(crate) fn new_catchup_region_request(
821        region_id: RegionId,
822        request: RegionCatchupRequest,
823        entry_receiver: Option<WalEntryReceiver>,
824    ) -> (WorkerRequest, Receiver<Result<AffectedRows>>) {
825        let (sender, receiver) = oneshot::channel();
826        let worker_request = WorkerRequest::Ddl(SenderDdlRequest {
827            region_id,
828            sender: sender.into(),
829            request: DdlRequest::Catchup((request, entry_receiver)),
830        });
831        (worker_request, receiver)
832    }
833
834    /// Converts request from a [RegionRequest].
835    pub(crate) fn try_from_region_request(
836        region_id: RegionId,
837        value: RegionRequest,
838        region_metadata: Option<RegionMetadataRef>,
839    ) -> Result<(WorkerRequest, Receiver<Result<AffectedRows>>)> {
840        let (sender, receiver) = oneshot::channel();
841        let worker_request = match value {
842            RegionRequest::Put(v) => {
843                let mut write_request =
844                    WriteRequest::new(region_id, OpType::Put, v.rows, region_metadata.clone())?
845                        .with_hint(v.hint)
846                        .with_skip_wal(v.skip_wal)
847                        .with_partition_expr_version(v.partition_expr_version);
848                if write_request.primary_key_encoding() == PrimaryKeyEncoding::Dense
849                    && let Some(region_metadata) = &region_metadata
850                {
851                    write_request.maybe_fill_missing_columns(region_metadata)?;
852                }
853                WorkerRequest::Write(SenderWriteRequest {
854                    sender: sender.into(),
855                    request: write_request,
856                })
857            }
858            RegionRequest::Delete(v) => {
859                let mut write_request =
860                    WriteRequest::new(region_id, OpType::Delete, v.rows, region_metadata.clone())?
861                        .with_hint(v.hint)
862                        .with_partition_expr_version(v.partition_expr_version);
863                if write_request.primary_key_encoding() == PrimaryKeyEncoding::Dense
864                    && let Some(region_metadata) = &region_metadata
865                {
866                    write_request.maybe_fill_missing_columns(region_metadata)?;
867                }
868                WorkerRequest::Write(SenderWriteRequest {
869                    sender: sender.into(),
870                    request: write_request,
871                })
872            }
873            RegionRequest::Create(v) => WorkerRequest::Ddl(SenderDdlRequest {
874                region_id,
875                sender: sender.into(),
876                request: DdlRequest::Create(v),
877            }),
878            RegionRequest::Drop(v) => WorkerRequest::Ddl(SenderDdlRequest {
879                region_id,
880                sender: sender.into(),
881                request: DdlRequest::Drop(v),
882            }),
883            RegionRequest::Open(v) => WorkerRequest::Ddl(SenderDdlRequest {
884                region_id,
885                sender: sender.into(),
886                request: DdlRequest::Open((v, None)),
887            }),
888            RegionRequest::CleanUp(v) => WorkerRequest::Ddl(SenderDdlRequest {
889                region_id,
890                sender: sender.into(),
891                request: DdlRequest::OfflineCleanup(v),
892            }),
893            RegionRequest::Close(v) => WorkerRequest::Ddl(SenderDdlRequest {
894                region_id,
895                sender: sender.into(),
896                request: DdlRequest::Close(v),
897            }),
898            RegionRequest::Alter(v) => WorkerRequest::Ddl(SenderDdlRequest {
899                region_id,
900                sender: sender.into(),
901                request: DdlRequest::Alter(v),
902            }),
903            RegionRequest::Flush(v) => WorkerRequest::Ddl(SenderDdlRequest {
904                region_id,
905                sender: sender.into(),
906                request: DdlRequest::Flush(v),
907            }),
908            RegionRequest::Compact(v) => WorkerRequest::Ddl(SenderDdlRequest {
909                region_id,
910                sender: sender.into(),
911                request: DdlRequest::Compact(v),
912            }),
913            RegionRequest::BuildIndex(v) => WorkerRequest::Ddl(SenderDdlRequest {
914                region_id,
915                sender: sender.into(),
916                request: DdlRequest::BuildIndex(v),
917            }),
918            RegionRequest::Truncate(v) => WorkerRequest::Ddl(SenderDdlRequest {
919                region_id,
920                sender: sender.into(),
921                request: DdlRequest::Truncate(v),
922            }),
923            RegionRequest::Catchup(v) => WorkerRequest::Ddl(SenderDdlRequest {
924                region_id,
925                sender: sender.into(),
926                request: DdlRequest::Catchup((v, None)),
927            }),
928            RegionRequest::EnterStaging(v) => WorkerRequest::Ddl(SenderDdlRequest {
929                region_id,
930                sender: sender.into(),
931                request: DdlRequest::EnterStaging(v),
932            }),
933            RegionRequest::BulkInserts(mut region_bulk_inserts_request) => {
934                // The worker answers empty batches without writing them.
935                if let Some(region_metadata) = &region_metadata
936                    && region_bulk_inserts_request.payload.num_rows() > 0
937                    && check_bulk_insert_batch(
938                        region_metadata,
939                        &region_bulk_inserts_request.payload,
940                    )?
941                {
942                    // Lets the worker fill the missing columns.
943                    region_bulk_inserts_request.aligned_schema_version = None;
944                }
945                WorkerRequest::BulkInserts(BulkInsertRequest {
946                    metadata: region_metadata,
947                    sender: sender.into(),
948                    request: region_bulk_inserts_request,
949                })
950            }
951            RegionRequest::ApplyStagingManifest(v) => WorkerRequest::Ddl(SenderDdlRequest {
952                region_id,
953                sender: sender.into(),
954                request: DdlRequest::ApplyStagingManifest(v),
955            }),
956        };
957
958        Ok((worker_request, receiver))
959    }
960
961    pub(crate) fn new_set_readonly_gracefully(
962        region_id: RegionId,
963        region_role_state: SettableRegionRoleState,
964    ) -> (WorkerRequest, Receiver<SetRegionRoleStateResponse>) {
965        let (sender, receiver) = oneshot::channel();
966
967        (
968            WorkerRequest::SetRegionRoleStateGracefully {
969                region_id,
970                region_role_state,
971                sender,
972            },
973            receiver,
974        )
975    }
976
977    pub(crate) fn new_sync_region_request(
978        region_id: RegionId,
979        manifest_version: ManifestVersion,
980    ) -> (WorkerRequest, Receiver<Result<(ManifestVersion, bool)>>) {
981        let (sender, receiver) = oneshot::channel();
982        (
983            WorkerRequest::SyncRegion(RegionSyncRequest {
984                region_id,
985                manifest_version,
986                sender,
987            }),
988            receiver,
989        )
990    }
991
992    /// Converts [RemapManifestsRequest] from a [RemapManifestsRequest](store_api::region_engine::RemapManifestsRequest).
993    ///
994    /// # Errors
995    ///
996    /// Returns an error if the partition expression is invalid or missing.
997    /// Returns an error if the new partition expressions are not found for some regions.
998    #[allow(clippy::type_complexity)]
999    pub(crate) fn try_from_remap_manifests_request(
1000        store_api::region_engine::RemapManifestsRequest {
1001            region_id,
1002            input_regions,
1003            region_mapping,
1004            new_partition_exprs,
1005        }: store_api::region_engine::RemapManifestsRequest,
1006    ) -> Result<(WorkerRequest, Receiver<Result<HashMap<RegionId, String>>>)> {
1007        let (sender, receiver) = oneshot::channel();
1008        let new_partition_exprs = new_partition_exprs
1009            .into_iter()
1010            .map(|(k, v)| {
1011                Ok((
1012                    k,
1013                    PartitionExpr::from_json_str(&v)
1014                        .context(InvalidPartitionExprSnafu { expr: v })?
1015                        .context(MissingPartitionExprSnafu { region_id: k })?,
1016                ))
1017            })
1018            .collect::<Result<HashMap<_, _>>>()?;
1019
1020        let request = RemapManifestsRequest {
1021            region_id,
1022            input_regions,
1023            region_mapping,
1024            new_partition_exprs,
1025            sender,
1026        };
1027
1028        Ok((WorkerRequest::RemapManifests(request), receiver))
1029    }
1030
1031    /// Converts [CopyRegionFromRequest] from a [MitoCopyRegionFromRequest](store_api::region_engine::MitoCopyRegionFromRequest).
1032    pub(crate) fn try_from_copy_region_from_request(
1033        region_id: RegionId,
1034        store_api::region_engine::MitoCopyRegionFromRequest {
1035            source_region_id,
1036            parallelism,
1037        }: store_api::region_engine::MitoCopyRegionFromRequest,
1038    ) -> Result<(WorkerRequest, Receiver<Result<MitoCopyRegionFromResponse>>)> {
1039        let (sender, receiver) = oneshot::channel();
1040        let request = CopyRegionFromRequest {
1041            region_id,
1042            source_region_id,
1043            parallelism,
1044            sender,
1045        };
1046        Ok((WorkerRequest::CopyRegionFrom(request), receiver))
1047    }
1048}
1049
1050/// DDL request to a region.
1051#[derive(Debug)]
1052pub(crate) enum DdlRequest {
1053    Create(RegionCreateRequest),
1054    Drop(RegionDropRequest),
1055    Open((RegionOpenRequest, Option<WalEntryReceiver>)),
1056    OfflineCleanup(RegionCleanUpRequest),
1057    Close(RegionCloseRequest),
1058    Alter(RegionAlterRequest),
1059    Flush(RegionFlushRequest),
1060    Compact(RegionCompactRequest),
1061    BuildIndex(RegionBuildIndexRequest),
1062    Truncate(RegionTruncateRequest),
1063    Catchup((RegionCatchupRequest, Option<WalEntryReceiver>)),
1064    EnterStaging(EnterStagingRequest),
1065    ApplyStagingManifest(ApplyStagingManifestRequest),
1066}
1067
1068/// Sender and Ddl request.
1069#[derive(Debug)]
1070pub(crate) struct SenderDdlRequest {
1071    /// Region id of the request.
1072    pub(crate) region_id: RegionId,
1073    /// Result sender.
1074    pub(crate) sender: OptionOutputTx,
1075    /// Ddl request.
1076    pub(crate) request: DdlRequest,
1077}
1078
1079/// Notification from a background job.
1080#[derive(Debug)]
1081pub(crate) enum BackgroundNotify {
1082    /// Registers a loaded region and its scheduling state before acknowledging open.
1083    RegionOpened {
1084        region: MitoRegionRef,
1085        registered: Sender<()>,
1086    },
1087    /// Local unit publication and resource completion events.
1088    CompactionUnit(CompactionUnitNotification),
1089    /// A DDL released by compaction has actually completed, including failure.
1090    CompactionDdlComplete {
1091        generation: u64,
1092        request_index: usize,
1093    },
1094    /// Compaction planning has finished.
1095    CompactionPickFinished(CompactionPickFinished),
1096    /// Flush has finished.
1097    FlushFinished(FlushFinished),
1098    /// Flush has failed.
1099    FlushFailed(FlushFailed),
1100    /// Index build has finished.
1101    IndexBuildFinished(IndexBuildFinished),
1102    /// Index build has been stopped (aborted or succeeded).
1103    IndexBuildStopped(IndexBuildStopped),
1104    /// Index build has failed.
1105    IndexBuildFailed(IndexBuildFailed),
1106    /// An index build must be retried against the latest schema generation.
1107    IndexBuildRetry(BuildIndexRequest),
1108    /// Compaction has finished.
1109    CompactionFinished(CompactionFinished),
1110    /// Compaction has failed.
1111    CompactionFailed(CompactionFailed),
1112    /// Truncate result.
1113    Truncate(TruncateResult),
1114    /// Discard unflushed data result.
1115    DiscardUnflushed(DiscardUnflushedResult),
1116    /// Region change result.
1117    RegionChange(RegionChangeResult),
1118    /// Region edit result.
1119    RegionEdit(RegionEditResult),
1120    /// Enter staging result.
1121    EnterStaging(EnterStagingResult),
1122    /// Copy region result.
1123    CopyRegionFromFinished(CopyRegionFromFinished),
1124}
1125
1126/// Notifies a flush job is finished.
1127#[derive(Debug)]
1128pub(crate) struct FlushFinished {
1129    /// Region id.
1130    pub(crate) region_id: RegionId,
1131    /// Reason to flush.
1132    pub(crate) flush_reason: FlushReason,
1133    /// Entry id of flushed data.
1134    pub(crate) flushed_entry_id: EntryId,
1135    /// Flush result senders.
1136    pub(crate) senders: Vec<OutputTx>,
1137    /// Flush timer.
1138    pub(crate) _timer: HistogramTimer,
1139    /// Region edit to apply.
1140    pub(crate) edit: RegionEdit,
1141    /// Memtables to remove.
1142    pub(crate) memtables_to_remove: SmallVec<[MemtableId; 2]>,
1143    /// Whether the region is in staging mode.
1144    pub(crate) is_staging: bool,
1145}
1146
1147impl FlushFinished {
1148    /// Marks the flush job as successful and observes the timer.
1149    pub(crate) fn on_success(self) {
1150        for sender in self.senders {
1151            sender.send(Ok(0));
1152        }
1153    }
1154}
1155
1156impl OnFailure for FlushFinished {
1157    fn on_failure(&mut self, err: Error) {
1158        let err = Arc::new(err);
1159        for sender in self.senders.drain(..) {
1160            sender.send(Err(err.clone()).context(FlushRegionSnafu {
1161                region_id: self.region_id,
1162            }));
1163        }
1164    }
1165}
1166
1167/// Notifies a flush job is failed.
1168#[derive(Debug)]
1169pub(crate) struct FlushFailed {
1170    /// The error source of the failure.
1171    pub(crate) err: Arc<Error>,
1172}
1173
1174impl FlushFailed {
1175    /// Returns whether the flush was cancelled cooperatively.
1176    pub(crate) fn is_cancelled(&self) -> bool {
1177        matches!(self.err.as_ref(), Error::FlushCancelled { .. })
1178    }
1179}
1180
1181#[derive(Debug)]
1182pub(crate) struct IndexBuildFinished {
1183    pub(crate) manifest_version: ManifestVersion,
1184    pub(crate) file_meta: FileMeta,
1185}
1186
1187/// Notifies an index build job has been stopped.
1188#[derive(Debug)]
1189pub(crate) struct IndexBuildStopped {
1190    pub(crate) file_id: FileId,
1191}
1192
1193/// Notifies an index build job has failed.
1194#[derive(Debug)]
1195pub(crate) struct IndexBuildFailed {
1196    pub(crate) err: Arc<Error>,
1197}
1198
1199/// A local unit event carries only identity, never an input reservation lease.
1200#[derive(Debug)]
1201pub(crate) enum CompactionUnitNotification {
1202    /// A persisted edit or error awaits worker apply or failure acknowledgement.
1203    Finished {
1204        plan_id: u64,
1205        result: std::result::Result<RegionEdit, Arc<Error>>,
1206        applied: tokio::sync::oneshot::Sender<()>,
1207    },
1208    /// The task has exited and released its owned resources.
1209    Released {
1210        /// Identifies the exited attempt, not the whole compaction request.
1211        plan_id: u64,
1212    },
1213}
1214
1215/// Notifies a compaction job has finished.
1216#[derive(Debug)]
1217pub(crate) struct CompactionFinished {
1218    /// Region id.
1219    pub(crate) region_id: RegionId,
1220    /// Identity and reservation lease of the accepted execution.
1221    pub(crate) execution: CompactionExecution,
1222    /// Compaction result senders.
1223    pub(crate) senders: Vec<OutputTx>,
1224    /// Start time of compaction task.
1225    pub(crate) start_time: Instant,
1226    /// Region edit to apply.
1227    pub(crate) edit: RegionEdit,
1228}
1229
1230impl CompactionFinished {
1231    pub fn on_success(self) {
1232        // only update compaction time on success
1233        COMPACTION_ELAPSED_TOTAL
1234            .with_label_values(&["remote"])
1235            .observe(self.start_time.elapsed().as_secs_f64());
1236
1237        for sender in self.senders {
1238            sender.send(Ok(0));
1239        }
1240        info!("Successfully compacted region: {}", self.region_id);
1241    }
1242}
1243
1244impl OnFailure for CompactionFinished {
1245    /// Compaction succeeded but failed to update manifest or region's already been dropped.
1246    fn on_failure(&mut self, err: Error) {
1247        let err = Arc::new(err);
1248        for sender in self.senders.drain(..) {
1249            sender.send(Err(err.clone()).context(CompactRegionSnafu {
1250                region_id: self.region_id,
1251            }));
1252        }
1253    }
1254}
1255
1256/// A failing compaction result.
1257#[derive(Debug)]
1258pub(crate) struct CompactionFailed {
1259    pub(crate) region_id: RegionId,
1260    /// Identity and reservation lease of the accepted execution.
1261    pub(crate) execution: CompactionExecution,
1262    /// The error source of the failure.
1263    pub(crate) err: Arc<Error>,
1264}
1265
1266/// Notifies the truncate result of a region.
1267#[derive(Debug)]
1268pub(crate) struct TruncateResult {
1269    /// Region id.
1270    pub(crate) region_id: RegionId,
1271    /// Result sender.
1272    pub(crate) sender: OptionOutputTx,
1273    /// Truncate result.
1274    pub(crate) result: Result<()>,
1275    pub(crate) kind: TruncateKind,
1276}
1277
1278/// Notifies the result of discarding unflushed data from a region.
1279#[derive(Debug)]
1280pub(crate) struct DiscardUnflushedResult {
1281    /// Region id.
1282    pub(crate) region_id: RegionId,
1283    /// Result sender.
1284    pub(crate) sender: OptionOutputTx,
1285    /// Manifest update result.
1286    pub(crate) result: Result<()>,
1287    /// Last WAL entry covered by the discard operation.
1288    pub(crate) discarded_entry_id: EntryId,
1289    /// Last sequence covered by the discard operation.
1290    pub(crate) discarded_sequence: SequenceNumber,
1291    /// Estimated number of discarded rows.
1292    pub(crate) discarded_rows: u64,
1293    /// Estimated number of discarded bytes.
1294    pub(crate) discarded_bytes: u64,
1295}
1296
1297/// Notifies the region the result of writing region change action.
1298#[derive(Debug)]
1299pub(crate) struct RegionChangeResult {
1300    /// Region id.
1301    pub(crate) region_id: RegionId,
1302    /// The new region metadata to apply.
1303    pub(crate) new_meta: RegionMetadataRef,
1304    /// Result sender.
1305    pub(crate) sender: OptionOutputTx,
1306    /// Result from the manifest manager.
1307    pub(crate) result: Result<()>,
1308    /// Used for index build in schema change.
1309    pub(crate) need_index: bool,
1310    /// New options for the region.
1311    pub(crate) new_options: Option<RegionOptions>,
1312}
1313
1314/// Notifies the region the result of entering staging.
1315#[derive(Debug)]
1316pub(crate) struct EnterStagingResult {
1317    /// Region id.
1318    pub(crate) region_id: RegionId,
1319    /// The new staging partition directive to apply.
1320    pub(crate) partition_directive: StagingPartitionDirective,
1321    /// Result sender.
1322    pub(crate) sender: OptionOutputTx,
1323    /// Result from the manifest manager.
1324    pub(crate) result: Result<()>,
1325}
1326
1327#[derive(Debug)]
1328pub(crate) struct CopyRegionFromFinished {
1329    /// Region id.
1330    pub(crate) region_id: RegionId,
1331    /// Region edit to apply.
1332    pub(crate) edit: RegionEdit,
1333    /// Result sender.
1334    pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1335}
1336
1337#[derive(Debug, Default)]
1338pub(crate) struct Waiters(SmallVec<[Sender<Result<()>>; 1]>);
1339
1340impl Waiters {
1341    pub(crate) fn one(waiter: Sender<Result<()>>) -> Self {
1342        let mut waiters = SmallVec::new();
1343        waiters.push(waiter);
1344        Self(waiters)
1345    }
1346
1347    pub(crate) fn reply_with<F: Fn() -> Result<()>>(self, f: F) {
1348        for tx in self.0 {
1349            let _ = tx.send(f());
1350        }
1351    }
1352
1353    pub(crate) fn merge(&mut self, other: Self) {
1354        self.0.extend(other.0);
1355    }
1356}
1357
1358/// Request to edit a region directly.
1359#[derive(Debug)]
1360pub(crate) struct RegionEditRequest {
1361    pub(crate) region_id: RegionId,
1362    pub(crate) edit: RegionEdit,
1363    /// Whether to preload SST files into the write cache.
1364    pub(crate) preload_sst_cache: bool,
1365    /// The waiters that are waiting for this region edit's result.
1366    pub(crate) waiters: Waiters,
1367}
1368
1369impl RegionEditRequest {
1370    pub(crate) fn new(
1371        region_id: RegionId,
1372        edit: RegionEdit,
1373        preload_sst_cache: bool,
1374        waiter: Sender<Result<()>>,
1375    ) -> Self {
1376        Self {
1377            region_id,
1378            edit,
1379            preload_sst_cache,
1380            waiters: Waiters::one(waiter),
1381        }
1382    }
1383}
1384
1385/// Notifies the regin the result of editing region.
1386#[derive(Debug)]
1387pub(crate) struct RegionEditResult {
1388    /// Region id.
1389    pub(crate) region_id: RegionId,
1390    /// Result waiters.
1391    pub(crate) waiters: Waiters,
1392    /// Region edit to apply.
1393    pub(crate) edit: RegionEdit,
1394    /// Result from the manifest manager.
1395    pub(crate) result: std::result::Result<(), Arc<Error>>,
1396    /// Whether region state need to be set to Writable after handling this request.
1397    pub(crate) update_region_state: bool,
1398    /// The region is in staging mode before handling this request.
1399    pub(crate) is_staging: bool,
1400}
1401
1402#[derive(Debug)]
1403pub(crate) struct BuildIndexRequest {
1404    pub(crate) region_id: RegionId,
1405    pub(crate) build_type: IndexBuildType,
1406    /// files need to build index, empty means all.
1407    pub(crate) file_metas: Vec<FileMeta>,
1408}
1409
1410#[derive(Debug)]
1411pub(crate) struct RegionSyncRequest {
1412    pub(crate) region_id: RegionId,
1413    pub(crate) manifest_version: ManifestVersion,
1414    /// Returns the latest manifest version and a boolean indicating whether new maniefst is installed.
1415    pub(crate) sender: Sender<Result<(ManifestVersion, bool)>>,
1416}
1417
1418#[derive(Debug)]
1419pub(crate) struct RemapManifestsRequest {
1420    /// The [`RegionId`] of a staging region used to obtain table directory and storage configuration for the remap operation.
1421    pub(crate) region_id: RegionId,
1422    /// Regions to remap manifests from.
1423    pub(crate) input_regions: Vec<RegionId>,
1424    /// For each old region, which new regions should receive its files
1425    pub(crate) region_mapping: HashMap<RegionId, Vec<RegionId>>,
1426    /// New partition expressions for the new regions.
1427    pub(crate) new_partition_exprs: HashMap<RegionId, PartitionExpr>,
1428    /// Sender for the result of the remap operation.
1429    ///
1430    /// The result is a map from region IDs to their corresponding staging manifest paths.
1431    pub(crate) sender: Sender<Result<HashMap<RegionId, String>>>,
1432}
1433
1434#[derive(Debug)]
1435pub(crate) struct CopyRegionFromRequest {
1436    /// The [`RegionId`] of the target region.
1437    pub(crate) region_id: RegionId,
1438    /// The [`RegionId`] of the source region.
1439    pub(crate) source_region_id: RegionId,
1440    /// The parallelism of the copy operation.
1441    pub(crate) parallelism: usize,
1442    /// Result sender.
1443    pub(crate) sender: Sender<Result<MitoCopyRegionFromResponse>>,
1444}
1445
1446#[cfg(test)]
1447mod tests {
1448    use api::v1::value::ValueData;
1449    use api::v1::{Row, SemanticType};
1450    use datatypes::prelude::ConcreteDataType;
1451    use datatypes::schema::ColumnDefaultConstraint;
1452    use mito_codec::test_util::i64_value;
1453    use store_api::metadata::RegionMetadataBuilder;
1454    use tokio::sync::oneshot;
1455
1456    use super::*;
1457    use crate::error::Error;
1458    use crate::test_util::ts_ms_value;
1459
1460    fn new_column_schema(
1461        name: &str,
1462        data_type: ColumnDataType,
1463        semantic_type: SemanticType,
1464    ) -> ColumnSchema {
1465        ColumnSchema {
1466            column_name: name.to_string(),
1467            datatype: data_type as i32,
1468            semantic_type: semantic_type as i32,
1469            ..Default::default()
1470        }
1471    }
1472
1473    fn check_invalid_request(err: &Error, expect: &str) {
1474        if let Error::InvalidRequest {
1475            region_id: _,
1476            reason,
1477            location: _,
1478        } = err
1479        {
1480            assert_eq!(reason, expect);
1481        } else {
1482            panic!("Unexpected error {err}")
1483        }
1484    }
1485
1486    fn waiter() -> (Sender<Result<()>>, Receiver<Result<()>>) {
1487        oneshot::channel()
1488    }
1489
1490    fn assert_waiter_ok(rx: &mut Receiver<Result<()>>) {
1491        rx.try_recv().unwrap().unwrap();
1492    }
1493
1494    #[test]
1495    fn test_waiters_reply_with_single_waiter() {
1496        let (tx, mut rx) = waiter();
1497        Waiters::one(tx).reply_with(|| Ok(()));
1498        assert_waiter_ok(&mut rx);
1499    }
1500
1501    #[test]
1502    fn test_waiters_reply_with_many_waiters() {
1503        let (tx1, mut rx1) = waiter();
1504        let (tx2, mut rx2) = waiter();
1505        let (tx3, mut rx3) = waiter();
1506
1507        let waiters = Waiters(vec![tx1, tx2, tx3].into());
1508        waiters.reply_with(|| Ok(()));
1509
1510        assert_waiter_ok(&mut rx1);
1511        assert_waiter_ok(&mut rx2);
1512        assert_waiter_ok(&mut rx3);
1513    }
1514
1515    #[test]
1516    fn test_waiters_merge() {
1517        let (tx1, mut rx1) = waiter();
1518        let (tx2, mut rx2) = waiter();
1519        let (tx3, mut rx3) = waiter();
1520        let (tx4, mut rx4) = waiter();
1521
1522        let mut waiters = Waiters::one(tx1);
1523        waiters.merge(Waiters::one(tx2));
1524        waiters.merge(Waiters(vec![tx3, tx4].into()));
1525        assert_eq!(4, waiters.0.len());
1526
1527        waiters.reply_with(|| Ok(()));
1528
1529        assert_waiter_ok(&mut rx1);
1530        assert_waiter_ok(&mut rx2);
1531        assert_waiter_ok(&mut rx3);
1532        assert_waiter_ok(&mut rx4);
1533    }
1534
1535    #[test]
1536    fn test_write_request_duplicate_column() {
1537        let rows = Rows {
1538            schema: vec![
1539                new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1540                new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1541            ],
1542            rows: vec![],
1543        };
1544
1545        let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1546        check_invalid_request(&err, "duplicate column c0");
1547    }
1548
1549    #[test]
1550    fn test_valid_write_request() {
1551        let rows = Rows {
1552            schema: vec![
1553                new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1554                new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1555            ],
1556            rows: vec![Row {
1557                values: vec![i64_value(1), i64_value(2)],
1558            }],
1559        };
1560
1561        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1562        assert_eq!(0, request.column_index_by_name("c0").unwrap());
1563        assert_eq!(1, request.column_index_by_name("c1").unwrap());
1564        assert_eq!(None, request.column_index_by_name("c2"));
1565    }
1566
1567    #[test]
1568    fn test_write_request_column_num() {
1569        let rows = Rows {
1570            schema: vec![
1571                new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
1572                new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
1573            ],
1574            rows: vec![Row {
1575                values: vec![i64_value(1), i64_value(2), i64_value(3)],
1576            }],
1577        };
1578
1579        let err = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap_err();
1580        check_invalid_request(&err, "row has 3 columns but schema has 2");
1581    }
1582
1583    fn new_region_metadata() -> RegionMetadata {
1584        let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1585        builder
1586            .push_column_metadata(ColumnMetadata {
1587                column_schema: datatypes::schema::ColumnSchema::new(
1588                    "ts",
1589                    ConcreteDataType::timestamp_millisecond_datatype(),
1590                    false,
1591                ),
1592                semantic_type: SemanticType::Timestamp,
1593                column_id: 1,
1594            })
1595            .push_column_metadata(ColumnMetadata {
1596                column_schema: datatypes::schema::ColumnSchema::new(
1597                    "k0",
1598                    ConcreteDataType::int64_datatype(),
1599                    true,
1600                ),
1601                semantic_type: SemanticType::Tag,
1602                column_id: 2,
1603            })
1604            .primary_key(vec![2]);
1605        builder.build().unwrap()
1606    }
1607
1608    #[test]
1609    fn test_check_schema() {
1610        let rows = Rows {
1611            schema: vec![
1612                new_column_schema(
1613                    "ts",
1614                    ColumnDataType::TimestampMillisecond,
1615                    SemanticType::Timestamp,
1616                ),
1617                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1618            ],
1619            rows: vec![Row {
1620                values: vec![ts_ms_value(1), i64_value(2)],
1621            }],
1622        };
1623        let metadata = new_region_metadata();
1624
1625        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1626        request.check_schema(&metadata).unwrap();
1627    }
1628
1629    #[test]
1630    fn test_column_type() {
1631        let rows = Rows {
1632            schema: vec![
1633                new_column_schema("ts", ColumnDataType::Int64, SemanticType::Timestamp),
1634                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1635            ],
1636            rows: vec![Row {
1637                values: vec![i64_value(1), i64_value(2)],
1638            }],
1639        };
1640        let metadata = new_region_metadata();
1641
1642        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1643        let err = request.check_schema(&metadata).unwrap_err();
1644        check_invalid_request(
1645            &err,
1646            "column ts expect type Timestamp(Millisecond(TimestampMillisecondType)), given: INT64(4)",
1647        );
1648    }
1649
1650    #[test]
1651    fn test_semantic_type() {
1652        let rows = Rows {
1653            schema: vec![
1654                new_column_schema(
1655                    "ts",
1656                    ColumnDataType::TimestampMillisecond,
1657                    SemanticType::Tag,
1658                ),
1659                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1660            ],
1661            rows: vec![Row {
1662                values: vec![ts_ms_value(1), i64_value(2)],
1663            }],
1664        };
1665        let metadata = new_region_metadata();
1666
1667        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1668        let err = request.check_schema(&metadata).unwrap_err();
1669        check_invalid_request(&err, "column ts has semantic type Timestamp, given: TAG(0)");
1670    }
1671
1672    #[test]
1673    fn test_column_nullable() {
1674        let rows = Rows {
1675            schema: vec![
1676                new_column_schema(
1677                    "ts",
1678                    ColumnDataType::TimestampMillisecond,
1679                    SemanticType::Timestamp,
1680                ),
1681                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1682            ],
1683            rows: vec![Row {
1684                values: vec![Value { value_data: None }, i64_value(2)],
1685            }],
1686        };
1687        let metadata = new_region_metadata();
1688
1689        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1690        let err = request.check_schema(&metadata).unwrap_err();
1691        check_invalid_request(&err, "column ts is not null but input has null");
1692    }
1693
1694    #[test]
1695    fn test_column_default() {
1696        let rows = Rows {
1697            schema: vec![new_column_schema(
1698                "k0",
1699                ColumnDataType::Int64,
1700                SemanticType::Tag,
1701            )],
1702            rows: vec![Row {
1703                values: vec![i64_value(1)],
1704            }],
1705        };
1706        let metadata = new_region_metadata();
1707
1708        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1709        let err = request.check_schema(&metadata).unwrap_err();
1710        check_invalid_request(&err, "missing column ts");
1711    }
1712
1713    #[test]
1714    fn test_unknown_column() {
1715        let rows = Rows {
1716            schema: vec![
1717                new_column_schema(
1718                    "ts",
1719                    ColumnDataType::TimestampMillisecond,
1720                    SemanticType::Timestamp,
1721                ),
1722                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1723                new_column_schema("k1", ColumnDataType::Int64, SemanticType::Tag),
1724            ],
1725            rows: vec![Row {
1726                values: vec![ts_ms_value(1), i64_value(2), i64_value(3)],
1727            }],
1728        };
1729        let metadata = new_region_metadata();
1730
1731        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1732        let err = request.check_schema(&metadata).unwrap_err();
1733        check_invalid_request(&err, r#"unknown columns: ["k1"]"#);
1734    }
1735
1736    #[test]
1737    fn test_fill_impure_columns_err() {
1738        let rows = Rows {
1739            schema: vec![new_column_schema(
1740                "k0",
1741                ColumnDataType::Int64,
1742                SemanticType::Tag,
1743            )],
1744            rows: vec![Row {
1745                values: vec![i64_value(1)],
1746            }],
1747        };
1748        let metadata = {
1749            let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1750            builder
1751                .push_column_metadata(ColumnMetadata {
1752                    column_schema: datatypes::schema::ColumnSchema::new(
1753                        "ts",
1754                        ConcreteDataType::timestamp_millisecond_datatype(),
1755                        false,
1756                    )
1757                    .with_default_constraint(Some(ColumnDefaultConstraint::Function(
1758                        "now()".to_string(),
1759                    )))
1760                    .unwrap(),
1761                    semantic_type: SemanticType::Timestamp,
1762                    column_id: 1,
1763                })
1764                .push_column_metadata(ColumnMetadata {
1765                    column_schema: datatypes::schema::ColumnSchema::new(
1766                        "k0",
1767                        ConcreteDataType::int64_datatype(),
1768                        true,
1769                    ),
1770                    semantic_type: SemanticType::Tag,
1771                    column_id: 2,
1772                })
1773                .primary_key(vec![2]);
1774            builder.build().unwrap()
1775        };
1776
1777        let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1778        let err = request.check_schema(&metadata).unwrap_err();
1779        assert!(err.is_fill_default());
1780        assert!(
1781            request
1782                .fill_missing_columns(&metadata)
1783                .unwrap_err()
1784                .to_string()
1785                .contains("unexpected impure default value with region_id")
1786        );
1787    }
1788
1789    #[test]
1790    fn test_fill_missing_columns() {
1791        let rows = Rows {
1792            schema: vec![new_column_schema(
1793                "ts",
1794                ColumnDataType::TimestampMillisecond,
1795                SemanticType::Timestamp,
1796            )],
1797            rows: vec![Row {
1798                values: vec![ts_ms_value(1)],
1799            }],
1800        };
1801        let metadata = new_region_metadata();
1802
1803        let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
1804        let err = request.check_schema(&metadata).unwrap_err();
1805        assert!(err.is_fill_default());
1806        request.fill_missing_columns(&metadata).unwrap();
1807
1808        let expect_rows = Rows {
1809            schema: vec![new_column_schema(
1810                "ts",
1811                ColumnDataType::TimestampMillisecond,
1812                SemanticType::Timestamp,
1813            )],
1814            rows: vec![Row {
1815                values: vec![ts_ms_value(1)],
1816            }],
1817        };
1818        assert_eq!(expect_rows, request.rows);
1819    }
1820
1821    fn builder_with_ts_tag() -> RegionMetadataBuilder {
1822        let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
1823        builder
1824            .push_column_metadata(ColumnMetadata {
1825                column_schema: datatypes::schema::ColumnSchema::new(
1826                    "ts",
1827                    ConcreteDataType::timestamp_millisecond_datatype(),
1828                    false,
1829                ),
1830                semantic_type: SemanticType::Timestamp,
1831                column_id: 1,
1832            })
1833            .push_column_metadata(ColumnMetadata {
1834                column_schema: datatypes::schema::ColumnSchema::new(
1835                    "k0",
1836                    ConcreteDataType::int64_datatype(),
1837                    true,
1838                ),
1839                semantic_type: SemanticType::Tag,
1840                column_id: 2,
1841            })
1842            .primary_key(vec![2]);
1843        builder
1844    }
1845
1846    fn region_metadata_two_fields() -> RegionMetadata {
1847        let mut builder = builder_with_ts_tag();
1848        builder
1849            .push_column_metadata(ColumnMetadata {
1850                column_schema: datatypes::schema::ColumnSchema::new(
1851                    "f0",
1852                    ConcreteDataType::int64_datatype(),
1853                    true,
1854                ),
1855                semantic_type: SemanticType::Field,
1856                column_id: 3,
1857            })
1858            // Column is not nullable.
1859            .push_column_metadata(ColumnMetadata {
1860                column_schema: datatypes::schema::ColumnSchema::new(
1861                    "f1",
1862                    ConcreteDataType::int64_datatype(),
1863                    false,
1864                )
1865                .with_default_constraint(Some(ColumnDefaultConstraint::Value(
1866                    datatypes::value::Value::Int64(100),
1867                )))
1868                .unwrap(),
1869                semantic_type: SemanticType::Field,
1870                column_id: 4,
1871            });
1872        builder.build().unwrap()
1873    }
1874
1875    #[test]
1876    fn test_fill_missing_for_delete() {
1877        let rows = Rows {
1878            schema: vec![new_column_schema(
1879                "ts",
1880                ColumnDataType::TimestampMillisecond,
1881                SemanticType::Timestamp,
1882            )],
1883            rows: vec![Row {
1884                values: vec![ts_ms_value(1)],
1885            }],
1886        };
1887        let metadata = region_metadata_two_fields();
1888
1889        let mut request =
1890            WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1891        let err = request.check_schema(&metadata).unwrap_err();
1892        check_invalid_request(&err, "delete requests need column k0");
1893        let err = request.fill_missing_columns(&metadata).unwrap_err();
1894        check_invalid_request(&err, "delete requests need column k0");
1895
1896        let rows = Rows {
1897            schema: vec![
1898                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1899                new_column_schema(
1900                    "ts",
1901                    ColumnDataType::TimestampMillisecond,
1902                    SemanticType::Timestamp,
1903                ),
1904            ],
1905            rows: vec![Row {
1906                values: vec![i64_value(100), ts_ms_value(1)],
1907            }],
1908        };
1909        let mut request =
1910            WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1911        let err = request.check_schema(&metadata).unwrap_err();
1912        assert!(err.is_fill_default());
1913        request.fill_missing_columns(&metadata).unwrap();
1914
1915        let expect_rows = Rows {
1916            schema: vec![
1917                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1918                new_column_schema(
1919                    "ts",
1920                    ColumnDataType::TimestampMillisecond,
1921                    SemanticType::Timestamp,
1922                ),
1923                new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
1924            ],
1925            // Column f1 is not nullable and we use 0 for padding.
1926            rows: vec![Row {
1927                values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
1928            }],
1929        };
1930        assert_eq!(expect_rows, request.rows);
1931    }
1932
1933    #[test]
1934    fn test_fill_missing_without_default_in_delete() {
1935        let mut builder = builder_with_ts_tag();
1936        builder
1937            // f0 is nullable.
1938            .push_column_metadata(ColumnMetadata {
1939                column_schema: datatypes::schema::ColumnSchema::new(
1940                    "f0",
1941                    ConcreteDataType::int64_datatype(),
1942                    true,
1943                ),
1944                semantic_type: SemanticType::Field,
1945                column_id: 3,
1946            })
1947            // f1 is not nullable and don't has default.
1948            .push_column_metadata(ColumnMetadata {
1949                column_schema: datatypes::schema::ColumnSchema::new(
1950                    "f1",
1951                    ConcreteDataType::int64_datatype(),
1952                    false,
1953                ),
1954                semantic_type: SemanticType::Field,
1955                column_id: 4,
1956            });
1957        let metadata = builder.build().unwrap();
1958
1959        let rows = Rows {
1960            schema: vec![
1961                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1962                new_column_schema(
1963                    "ts",
1964                    ColumnDataType::TimestampMillisecond,
1965                    SemanticType::Timestamp,
1966                ),
1967            ],
1968            // Missing f0 (nullable), f1 (not nullable).
1969            rows: vec![Row {
1970                values: vec![i64_value(100), ts_ms_value(1)],
1971            }],
1972        };
1973        let mut request =
1974            WriteRequest::new(RegionId::new(1, 1), OpType::Delete, rows, None).unwrap();
1975        let err = request.check_schema(&metadata).unwrap_err();
1976        assert!(err.is_fill_default());
1977        request.fill_missing_columns(&metadata).unwrap();
1978
1979        let expect_rows = Rows {
1980            schema: vec![
1981                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
1982                new_column_schema(
1983                    "ts",
1984                    ColumnDataType::TimestampMillisecond,
1985                    SemanticType::Timestamp,
1986                ),
1987                new_column_schema("f1", ColumnDataType::Int64, SemanticType::Field),
1988            ],
1989            // Column f1 is not nullable and we use 0 for padding.
1990            rows: vec![Row {
1991                values: vec![i64_value(100), ts_ms_value(1), i64_value(0)],
1992            }],
1993        };
1994        assert_eq!(expect_rows, request.rows);
1995    }
1996
1997    #[test]
1998    fn test_no_default() {
1999        let rows = Rows {
2000            schema: vec![new_column_schema(
2001                "k0",
2002                ColumnDataType::Int64,
2003                SemanticType::Tag,
2004            )],
2005            rows: vec![Row {
2006                values: vec![i64_value(1)],
2007            }],
2008        };
2009        let metadata = new_region_metadata();
2010
2011        let mut request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2012        let err = request.fill_missing_columns(&metadata).unwrap_err();
2013        check_invalid_request(&err, "column ts does not have default value");
2014    }
2015
2016    #[test]
2017    fn test_missing_and_invalid() {
2018        // Missing f0 and f1 has invalid type (string).
2019        let rows = Rows {
2020            schema: vec![
2021                new_column_schema("k0", ColumnDataType::Int64, SemanticType::Tag),
2022                new_column_schema(
2023                    "ts",
2024                    ColumnDataType::TimestampMillisecond,
2025                    SemanticType::Timestamp,
2026                ),
2027                new_column_schema("f1", ColumnDataType::String, SemanticType::Field),
2028            ],
2029            rows: vec![Row {
2030                values: vec![
2031                    i64_value(100),
2032                    ts_ms_value(1),
2033                    Value {
2034                        value_data: Some(ValueData::StringValue("xxxxx".to_string())),
2035                    },
2036                ],
2037            }],
2038        };
2039        let metadata = region_metadata_two_fields();
2040
2041        let request = WriteRequest::new(RegionId::new(1, 1), OpType::Put, rows, None).unwrap();
2042        let err = request.check_schema(&metadata).unwrap_err();
2043        check_invalid_request(
2044            &err,
2045            "column f1 expect type Int64(Int64Type), given: STRING(12)",
2046        );
2047    }
2048
2049    #[test]
2050    fn test_delete_request_defaults_to_writing_wal() {
2051        let (request, _receiver) = WorkerRequest::try_from_region_request(
2052            RegionId::new(1, 1),
2053            RegionRequest::Delete(store_api::region_request::RegionDeleteRequest {
2054                rows: Rows::default(),
2055                hint: None,
2056                partition_expr_version: None,
2057            }),
2058            None,
2059        )
2060        .unwrap();
2061        let WorkerRequest::Write(request) = request else {
2062            panic!("expected a write request");
2063        };
2064        assert_eq!(request.request.op_type, OpType::Delete);
2065        assert!(!request.request.skip_wal);
2066    }
2067
2068    #[test]
2069    fn test_write_request_metadata() {
2070        let rows = Rows {
2071            schema: vec![
2072                new_column_schema("c0", ColumnDataType::Int64, SemanticType::Tag),
2073                new_column_schema("c1", ColumnDataType::Int64, SemanticType::Tag),
2074            ],
2075            rows: vec![Row {
2076                values: vec![i64_value(1), i64_value(2)],
2077            }],
2078        };
2079
2080        let metadata = Arc::new(new_region_metadata());
2081        let request = WriteRequest::new(
2082            RegionId::new(1, 1),
2083            OpType::Put,
2084            rows,
2085            Some(metadata.clone()),
2086        )
2087        .unwrap();
2088
2089        assert!(request.region_metadata.is_some());
2090        assert_eq!(
2091            request.region_metadata.unwrap().region_id,
2092            RegionId::new(1, 1)
2093        );
2094    }
2095}