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