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