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