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