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