1use std::collections::BTreeMap;
16use std::sync::atomic::{AtomicU64, Ordering};
17use std::sync::{Arc, LazyLock};
18use std::time::{Duration, Instant};
19
20use ahash::{HashMap, HashMapExt, HashSet, HashSetExt};
21use api::v1::alter_table_expr::Kind;
22use api::v1::column_def::{options_from_skipping, try_as_column_def};
23use api::v1::region::{
24 InsertRequest as RegionInsertRequest, InsertRequests as RegionInsertRequests,
25 RegionRequestHeader,
26};
27use api::v1::value::ValueData;
28use api::v1::{
29 AlterTableExpr, ColumnDataType, ColumnSchema, CreateTableExpr, InsertRequests,
30 RowInsertRequest, RowInsertRequests, Rows, SemanticType,
31};
32use arrow::datatypes::{DataType as ArrowDataType, Schema as ArrowSchema};
33use catalog::CatalogManagerRef;
34use client::{OutputData, OutputMeta};
35use common_catalog::consts::{
36 DEFAULT_PRIVATE_SCHEMA_NAME, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN, TRACE_ID_COLUMN,
37 TRACE_TABLE_NAME, TRACE_TABLE_NAME_SESSION_KEY, default_engine, is_ddl_reserved_table,
38 trace_operations_table_name, trace_services_table_name,
39};
40use common_event_recorder::DEFAULT_EVENTS_TABLE_NAME;
41use common_frontend::slow_query_event::SLOW_QUERY_TABLE_NAME;
42use common_grpc_expr::util::ColumnExpr;
43use common_meta::cache::TableFlownodeSetCacheRef;
44use common_meta::datanode::REGION_STATS_HISTORY_TABLE_NAME;
45use common_meta::node_manager::NodeManagerRef;
46use common_meta::peer::Peer;
47use common_meta::rpc::ddl::TriggerReason;
48use common_query::Output;
49use common_query::native_histogram::{is_native_histogram_value_type, native_histogram_value_type};
50use common_query::prelude::{greptime_timestamp, greptime_value};
51use common_telemetry::tracing_context::TracingContext;
52use common_telemetry::{debug, error, warn};
53use common_time::Timestamp;
54use common_time::timestamp::TimeUnit;
55use datatypes::schema::SkippingIndexOptions;
56use futures_util::future;
57use meter_core::data::MeterRecord;
58use meter_macros::write_meter;
59use partition::manager::PartitionRuleManagerRef;
60use session::context::QueryContextRef;
61use snafu::ResultExt;
62use snafu::prelude::*;
63use sql::partition::partition_rule_for_hexstring;
64use sql::statements::create::Partitions;
65use sql::statements::insert::Insert;
66use store_api::metric_engine_consts::{
67 LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY,
68};
69use store_api::mito_engine_options::{
70 APPEND_MODE_KEY, COMPACTION_TYPE, COMPACTION_TYPE_TWCS, MERGE_MODE_KEY, TTL_KEY,
71 TWCS_TIME_WINDOW,
72};
73use store_api::storage::{RegionId, TableId};
74use table::TableRef;
75use table::metadata::{TableInfo, TableInfoRef};
76use table::requests::{
77 AUTO_CREATE_TABLE_KEY, InsertRequest as TableInsertRequest, SEMANTIC_PER_TABLE_INDEX_KEY,
78 SEMANTIC_PIPELINE, TABLE_DATA_MODEL, TABLE_DATA_MODEL_TRACE_V1, TABLE_DATA_MODEL_TRACE_V2,
79 TRACE_TABLE_PARTITIONS_HINT_KEY, VALID_TABLE_OPTION_KEYS, is_semantic_option_key,
80 validate_semantic_option,
81};
82use table::table_reference::TableReference;
83
84use crate::batcher::PendingRowsBatcher;
85use crate::error::{
86 CatalogSnafu, ColumnOptionsSnafu, CreatePartitionRulesSnafu, InvalidInsertRequestSnafu,
87 JoinTaskSnafu, RequestInsertsSnafu, Result, TableNotFoundSnafu, WriteRejectedSnafu,
88};
89use crate::expr_helper;
90use crate::region_req_factory::RegionRequestFactory;
91use crate::req_convert::common::preprocess_row_insert_requests;
92use crate::req_convert::insert::{
93 ColumnToRow, ImpureDefaultFiller, RowToRegion, StatementToRegion, TableToRegion,
94 fill_reqs_with_impure_default, rows_to_record_batch,
95};
96use crate::statement::StatementExecutor;
97use crate::write_dispatcher::{WriteDispatcher, WriteDispatcherRef};
98
99pub struct Inserter {
100 catalog_manager: CatalogManagerRef,
101 pub(crate) partition_manager: PartitionRuleManagerRef,
102 pub(crate) node_manager: NodeManagerRef,
103 pub(crate) write_dispatcher: WriteDispatcherRef,
104 pub(crate) table_flownode_set_cache: TableFlownodeSetCacheRef,
105 auto_create_table: bool,
109 pending_rows_batcher: Option<Arc<dyn PendingRowsBatcher>>,
110 mirror_pending_rows: Arc<AtomicU64>,
114}
115
116pub type InserterRef = Arc<Inserter>;
117
118#[derive(Clone)]
120pub enum AutoCreateTableType {
121 Logical(String),
123 Physical,
125 Log,
127 LastNonNull,
129 Trace { alter_existing: bool },
131}
132
133impl AutoCreateTableType {
134 pub fn as_str(&self) -> &'static str {
135 match self {
136 AutoCreateTableType::Logical(_) => "logical",
137 AutoCreateTableType::Physical => "physical",
138 AutoCreateTableType::Log => "log",
139 AutoCreateTableType::LastNonNull => "last_non_null",
140 AutoCreateTableType::Trace { .. } => "trace",
141 }
142 }
143
144 fn alter_existing(&self) -> bool {
145 !matches!(
146 self,
147 Self::Trace {
148 alter_existing: false
149 }
150 )
151 }
152}
153
154#[derive(Clone)]
161pub struct InstantAndNormalInsertRequests {
162 pub normal_requests: RegionInsertRequests,
164 pub instant_requests: RegionInsertRequests,
167}
168
169impl Inserter {
170 pub async fn can_batch_metric_rows(
174 &self,
175 requests: &RowInsertRequests,
176 ctx: &QueryContextRef,
177 physical_table: &str,
178 ) -> Result<bool> {
179 if self.auto_create_disabled_reason(ctx)?.is_some() || ctx.extension(TTL_KEY).is_some() {
180 return Ok(false);
181 }
182 for request in &requests.inserts {
183 if request.rows.as_ref().is_some_and(|rows| {
188 rows.schema.iter().any(|column| {
189 column.datatype_extension.is_some()
190 || !matches!(
191 ColumnDataType::try_from(column.datatype),
192 Ok(ColumnDataType::TimestampSecond
193 | ColumnDataType::TimestampMillisecond
194 | ColumnDataType::TimestampMicrosecond
195 | ColumnDataType::TimestampNanosecond
196 | ColumnDataType::Float64
197 | ColumnDataType::String)
198 )
199 })
200 }) {
201 return Ok(false);
202 }
203
204 let Some(table) = self
205 .get_table(
206 ctx.current_catalog(),
207 &ctx.current_schema(),
208 &request.table_name,
209 )
210 .await?
211 else {
212 continue;
213 };
214 let info = table.table_info();
215 if info.meta.engine != METRIC_ENGINE_NAME
216 || info.is_ttl_instant_table()
217 || info
218 .meta
219 .options
220 .extra_options
221 .get(LOGICAL_TABLE_METADATA_KEY)
222 .map(String::as_str)
223 != Some(physical_table)
224 || info
225 .meta
226 .schema
227 .column_schemas()
228 .iter()
229 .any(|column| column.default_constraint().is_some())
230 {
231 return Ok(false);
232 }
233 if info
236 .meta
237 .primary_key_indices
238 .iter()
239 .any(|&index| !info.meta.schema.column_schemas()[index].is_nullable())
240 {
241 return Ok(false);
242 }
243 match self.table_flownode_set_cache.get(info.table_id()).await {
246 Ok(None) => {}
247 Ok(Some(flows)) if flows.is_empty() => {}
248 _ => return Ok(false),
249 }
250 }
251 Ok(true)
252 }
253
254 pub async fn meter_row_inserts(
257 requests: &mut RowInsertRequests,
258 ctx: &QueryContextRef,
259 ) -> Result<u64> {
260 let metered = InstantAndNormalInsertRequests {
261 normal_requests: RegionInsertRequests {
262 requests: requests
263 .inserts
264 .iter_mut()
265 .map(|request| RegionInsertRequest {
266 rows: request.rows.take(),
267 ..Default::default()
268 })
269 .collect(),
270 },
271 instant_requests: RegionInsertRequests::default(),
272 };
273 let cost = write_meter!(
274 ctx.current_catalog(),
275 ctx.current_schema(),
276 metered,
277 ctx.write_rows_to_admit(
278 ctx.current_catalog(),
279 &ctx.current_schema(),
280 count_insert_rows(&metered)?
281 ),
282 ctx.channel() as u8
283 )
284 .await
285 .context(WriteRejectedSnafu);
286 for (request, region) in requests
287 .inserts
288 .iter_mut()
289 .zip(metered.normal_requests.requests)
290 {
291 request.rows = region.rows;
292 }
293 cost
294 }
295
296 pub fn new(
298 catalog_manager: CatalogManagerRef,
299 partition_manager: PartitionRuleManagerRef,
300 node_manager: NodeManagerRef,
301 table_flownode_set_cache: TableFlownodeSetCacheRef,
302 auto_create_table: bool,
303 ) -> Self {
304 Self {
305 write_dispatcher: Arc::new(WriteDispatcher::new(
306 node_manager.clone(),
307 partition_manager.clone(),
308 )),
309 catalog_manager,
310 partition_manager,
311 node_manager,
312 table_flownode_set_cache,
313 auto_create_table,
314 pending_rows_batcher: None,
315 mirror_pending_rows: Arc::new(AtomicU64::new(0)),
316 }
317 }
318
319 pub fn with_write_dispatcher(mut self, dispatcher: WriteDispatcherRef) -> Self {
321 self.write_dispatcher = dispatcher;
322 self
323 }
324
325 pub fn with_pending_rows_batcher(
327 mut self,
328 batcher: Option<Arc<dyn PendingRowsBatcher>>,
329 ) -> Self {
330 self.pending_rows_batcher = batcher;
331 self
332 }
333
334 pub async fn handle_column_inserts(
335 &self,
336 requests: InsertRequests,
337 ctx: QueryContextRef,
338 statement_executor: &StatementExecutor,
339 ) -> Result<Output> {
340 let row_inserts = ColumnToRow::convert(requests)?;
341 self.handle_row_inserts(row_inserts, ctx, statement_executor, false, false)
342 .await
343 }
344
345 pub async fn handle_row_inserts(
347 &self,
348 mut requests: RowInsertRequests,
349 ctx: QueryContextRef,
350 statement_executor: &StatementExecutor,
351 accommodate_existing_schema: bool,
352 is_single_value: bool,
353 ) -> Result<Output> {
354 preprocess_row_insert_requests(&mut requests.inserts)?;
355 self.handle_row_inserts_with_create_type(
356 requests,
357 ctx,
358 statement_executor,
359 AutoCreateTableType::Physical,
360 accommodate_existing_schema,
361 is_single_value,
362 )
363 .await
364 }
365
366 pub async fn handle_log_inserts(
368 &self,
369 requests: RowInsertRequests,
370 ctx: QueryContextRef,
371 statement_executor: &StatementExecutor,
372 ) -> Result<Output> {
373 self.handle_row_inserts_with_create_type(
374 requests,
375 ctx,
376 statement_executor,
377 AutoCreateTableType::Log,
378 false,
379 false,
380 )
381 .await
382 }
383
384 pub async fn handle_trace_inserts(
385 &self,
386 requests: RowInsertRequests,
387 ctx: QueryContextRef,
388 statement_executor: &StatementExecutor,
389 ) -> Result<Output> {
390 self.handle_row_inserts_with_create_type(
391 requests,
392 ctx,
393 statement_executor,
394 AutoCreateTableType::Trace {
395 alter_existing: true,
396 },
397 false,
398 false,
399 )
400 .await
401 }
402
403 pub async fn handle_last_non_null_inserts(
405 &self,
406 requests: RowInsertRequests,
407 ctx: QueryContextRef,
408 statement_executor: &StatementExecutor,
409 accommodate_existing_schema: bool,
410 is_single_value: bool,
411 ) -> Result<Output> {
412 self.handle_row_inserts_with_create_type(
413 requests,
414 ctx,
415 statement_executor,
416 AutoCreateTableType::LastNonNull,
417 accommodate_existing_schema,
418 is_single_value,
419 )
420 .await
421 }
422
423 async fn handle_row_inserts_with_create_type(
425 &self,
426 mut requests: RowInsertRequests,
427 ctx: QueryContextRef,
428 statement_executor: &StatementExecutor,
429 create_type: AutoCreateTableType,
430 accommodate_existing_schema: bool,
431 is_single_value: bool,
432 ) -> Result<Output> {
433 let skip_wal = ctx.skip_wal();
434
435 let batcher = self
436 .pending_rows_batcher
437 .as_ref()
438 .filter(|_| ctx.batching_enabled());
439
440 requests.inserts.retain(|req| {
442 req.rows
443 .as_ref()
444 .map(|r| !r.rows.is_empty())
445 .unwrap_or_default()
446 });
447 validate_column_count_match(&requests)?;
448
449 let CreateAlterTableResult {
450 instant_table_ids,
451 table_infos,
452 } = self
453 .create_or_alter_tables_on_demand(
454 &mut requests,
455 &ctx,
456 create_type,
457 statement_executor,
458 accommodate_existing_schema,
459 is_single_value,
460 None,
461 )
462 .await?;
463
464 if let Some(batcher) = batcher
467 && instant_table_ids.is_empty()
468 && table_infos
469 .values()
470 .all(|info| info.meta.engine == default_engine())
471 {
472 return self
473 .submit_pending_rows(requests, table_infos, ctx, batcher)
474 .await;
475 }
476
477 let name_to_info = table_infos
478 .values()
479 .map(|info| (info.name.clone(), info.clone()))
480 .collect::<HashMap<_, _>>();
481 let inserts = RowToRegion::new(
482 name_to_info,
483 instant_table_ids,
484 self.partition_manager.as_ref(),
485 )
486 .convert(requests, skip_wal)
487 .await?;
488
489 self.do_request(inserts, &table_infos, &ctx).await
490 }
491
492 async fn submit_pending_rows(
493 &self,
494 mut requests: RowInsertRequests,
495 table_infos: HashMap<TableId, Arc<TableInfo>>,
496 ctx: QueryContextRef,
497 batcher: &Arc<dyn PendingRowsBatcher>,
498 ) -> Result<Output> {
499 requests.inserts.retain(|request| {
502 request
503 .rows
504 .as_ref()
505 .is_some_and(|rows| !rows.rows.is_empty())
506 });
507 let by_name = table_infos
508 .values()
509 .map(|info| (info.name.as_str(), info))
510 .collect::<HashMap<_, _>>();
511 let mut prepared = Vec::with_capacity(requests.inserts.len());
512 for request in &mut requests.inserts {
513 let table_info =
514 by_name
515 .get(request.table_name.as_str())
516 .context(TableNotFoundSnafu {
517 table_name: &request.table_name,
518 })?;
519 let Some(rows) = &mut request.rows else {
520 continue;
521 };
522 ImpureDefaultFiller::new((*table_info).clone())?.fill_rows(rows);
523 let batch = rows_to_record_batch(rows, table_info)?;
524 prepared.push(((*table_info).clone(), batch));
525 }
526 let metered = InstantAndNormalInsertRequests {
529 normal_requests: RegionInsertRequests {
530 requests: requests
531 .inserts
532 .into_iter()
533 .map(|request| RegionInsertRequest {
534 rows: request.rows,
535 ..Default::default()
536 })
537 .collect(),
538 },
539 instant_requests: RegionInsertRequests::default(),
540 };
541 let table_info = table_infos.values().next();
542 let catalog = table_info.map_or(ctx.current_catalog(), |info| info.catalog_name.as_str());
543 let schema =
544 table_info.map_or_else(|| ctx.current_schema(), |info| info.schema_name.clone());
545 let write_cost = write_meter!(
546 catalog,
547 &schema,
548 metered,
549 ctx.write_rows_to_admit(catalog, &schema, count_insert_rows(&metered)?),
550 ctx.channel() as u8
551 )
552 .await
553 .context(WriteRejectedSnafu)?;
554 prepared.retain(|(_, batch)| batch.num_rows() != 0);
555 let results = if prepared.is_empty() {
556 Vec::new()
557 } else {
558 let permit = batcher.acquire().await?;
560 let submissions = prepared.into_iter().map(|(info, batch)| {
561 let mut target_ctx = ctx.fork();
563 target_ctx.set_current_catalog(&info.catalog_name);
564 target_ctx.set_current_schema(&info.schema_name);
565 batcher.submit(info, batch, Arc::new(target_ctx), permit.clone())
566 });
567 future::join_all(submissions).await
569 };
570 let mut affected_rows = 0;
571 let mut meta = OutputMeta::new_with_cost(write_cost as _);
572 for result in results {
573 let output = result?;
574 affected_rows += output.extract_rows_and_cost().0;
575 meta.write_completions.extend(output.meta.write_completions);
576 }
577 Ok(Output::new(OutputData::AffectedRows(affected_rows), meta))
578 }
579
580 pub async fn handle_metric_row_inserts(
582 &self,
583 mut requests: RowInsertRequests,
584 ctx: QueryContextRef,
585 statement_executor: &StatementExecutor,
586 physical_table: String,
587 ) -> Result<Output> {
588 let skip_wal = ctx.skip_wal();
589
590 requests.inserts.retain(|req| {
592 req.rows
593 .as_ref()
594 .map(|r| !r.rows.is_empty())
595 .unwrap_or_default()
596 });
597 validate_column_count_match(&requests)?;
598
599 let physical_table_ref = self
601 .create_physical_table_on_demand(&ctx, physical_table.clone(), statement_executor)
602 .await?;
603
604 let CreateAlterTableResult {
616 instant_table_ids,
617 table_infos,
618 } = self
619 .create_or_alter_tables_on_demand(
620 &mut requests,
621 &ctx,
622 AutoCreateTableType::Logical(physical_table.clone()),
623 statement_executor,
624 true,
625 true,
626 table_time_index_unit(&physical_table_ref),
627 )
628 .await?;
629 let name_to_info = table_infos
630 .values()
631 .map(|info| (info.name.clone(), info.clone()))
632 .collect::<HashMap<_, _>>();
633 let inserts = RowToRegion::new(name_to_info, instant_table_ids, &self.partition_manager)
634 .convert(requests, skip_wal)
635 .await?;
636
637 self.do_request(inserts, &table_infos, &ctx).await
638 }
639
640 fn table_batcher(
641 &self,
642 table_info: &TableInfoRef,
643 ctx: &QueryContextRef,
644 ) -> Option<&Arc<dyn PendingRowsBatcher>> {
645 self.pending_rows_batcher.as_ref().filter(|_| {
646 ctx.batching_enabled()
647 && !table_info.is_ttl_instant_table()
648 && table_info.meta.engine == default_engine()
649 })
650 }
651
652 async fn submit_table_rows(
653 &self,
654 rows: Rows,
655 table_info: TableInfoRef,
656 ctx: QueryContextRef,
657 batcher: &Arc<dyn PendingRowsBatcher>,
658 ) -> Result<Output> {
659 let requests = RowInsertRequests {
660 inserts: vec![RowInsertRequest {
661 table_name: table_info.name.clone(),
662 rows: Some(rows),
663 }],
664 };
665 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info)]);
666 self.submit_pending_rows(requests, table_infos, ctx, batcher)
667 .await
668 }
669
670 pub async fn handle_table_insert(
671 &self,
672 request: TableInsertRequest,
673 ctx: QueryContextRef,
674 ) -> Result<Output> {
675 let catalog = request.catalog_name.as_str();
676 let schema = request.schema_name.as_str();
677 let table_name = request.table_name.as_str();
678 let table = self.get_table(catalog, schema, table_name).await?;
679 let table = table.with_context(|| TableNotFoundSnafu {
680 table_name: common_catalog::format_full_table_name(catalog, schema, table_name),
681 })?;
682 let table_info = table.table_info();
683
684 let converter = TableToRegion::new(&table_info, &self.partition_manager);
685 let skip_wal = request.skip_wal;
686 let rows = converter.prepare(request)?;
687 if let Some(batcher) = self.table_batcher(&table_info, &ctx) {
688 return self.submit_table_rows(rows, table_info, ctx, batcher).await;
689 }
690 let inserts = converter.partition(rows, skip_wal).await?;
691
692 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
693
694 self.do_request(inserts, &table_infos, &ctx).await
695 }
696
697 pub async fn handle_statement_insert(
698 &self,
699 insert: &Insert,
700 ctx: &QueryContextRef,
701 ) -> Result<Output> {
702 let converter =
703 StatementToRegion::new(self.catalog_manager.as_ref(), &self.partition_manager, ctx);
704 let (rows, table_info) = converter.prepare(insert, ctx).await?;
705 if let Some(batcher) = self.table_batcher(&table_info, ctx) {
706 return self
707 .submit_table_rows(rows, table_info, ctx.clone(), batcher)
708 .await;
709 }
710 let inserts = converter.partition(rows, table_info.clone(), ctx).await?;
711
712 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
713
714 self.do_request(inserts, &table_infos, ctx).await
715 }
716}
717
718pub async fn admit_write(rows: u64, ctx: &QueryContextRef) -> Result<QueryContextRef> {
721 write_meter!(MeterRecord::new(
724 ctx.current_catalog().to_string(),
725 ctx.current_schema(),
726 0,
727 ctx.write_rows_to_admit(ctx.current_catalog(), &ctx.current_schema(), rows),
728 ctx.channel() as u8,
729 ))
730 .await
731 .context(WriteRejectedSnafu)?;
732 Ok(Arc::new(ctx.with_write_admission()))
733}
734
735pub async fn admit_row_insert_batches(
738 batches: &mut [(QueryContextRef, RowInsertRequests)],
739) -> Result<()> {
740 let mut totals = BTreeMap::<_, (QueryContextRef, u64)>::new();
741 for (ctx, requests) in batches.iter() {
742 let catalog = ctx.current_catalog();
743 let schema = ctx.current_schema();
744 if ctx.write_rows_to_admit(catalog, &schema, 1) == 0 {
745 continue;
746 }
747 let (_, total) = totals
748 .entry((catalog.to_string(), schema.clone()))
749 .or_insert_with(|| (ctx.clone(), 0));
750 for rows in requests.inserts.iter().filter_map(|r| r.rows.as_ref()) {
751 *total =
752 total
753 .checked_add(rows.rows.len() as u64)
754 .context(InvalidInsertRequestSnafu {
755 reason: "Insert row count exceeds u64::MAX",
756 })?;
757 }
758 }
759 for (ctx, rows) in totals.values() {
760 admit_write(*rows, ctx).await?;
761 }
762 for (ctx, _) in batches {
763 *ctx = Arc::new(ctx.with_write_admission());
764 }
765 Ok(())
766}
767
768fn count_insert_rows(requests: &InstantAndNormalInsertRequests) -> Result<u64> {
769 requests
770 .normal_requests
771 .requests
772 .iter()
773 .chain(&requests.instant_requests.requests)
774 .filter_map(|request| request.rows.as_ref())
775 .try_fold(0u64, |total, rows| {
776 total
777 .checked_add(rows.rows.len() as u64)
778 .context(InvalidInsertRequestSnafu {
779 reason: "Insert row count exceeds u64::MAX",
780 })
781 })
782}
783
784impl Inserter {
785 async fn do_request(
786 &self,
787 requests: InstantAndNormalInsertRequests,
788 table_infos: &HashMap<TableId, Arc<TableInfo>>,
789 ctx: &QueryContextRef,
790 ) -> Result<Output> {
791 let requests = fill_reqs_with_impure_default(table_infos, requests)?;
793
794 let table_info = table_infos.values().next();
797 let catalog = table_info.map_or(ctx.current_catalog(), |info| info.catalog_name.as_str());
798 let schema =
799 table_info.map_or_else(|| ctx.current_schema(), |info| info.schema_name.clone());
800 let write_cost = write_meter!(
801 catalog,
802 schema.clone(),
803 requests,
804 ctx.write_rows_to_admit(catalog, &schema, count_insert_rows(&requests)?),
805 ctx.channel() as u8
806 )
807 .await
808 .context(WriteRejectedSnafu)?;
809 let request_factory = RegionRequestFactory::new(RegionRequestHeader {
810 tracing_context: TracingContext::from_current_span().to_w3c(),
811 dbname: ctx.get_db_string(),
812 ..Default::default()
813 });
814
815 let InstantAndNormalInsertRequests {
816 normal_requests,
817 instant_requests,
818 } = requests;
819
820 let flow_mirror_task = FlowMirrorTask::new(
822 &self.table_flownode_set_cache,
823 normal_requests
824 .requests
825 .iter()
826 .chain(instant_requests.requests.iter()),
827 )
828 .await?;
829 let has_instant_rows = instant_requests.requests.iter().any(|request| {
830 request
831 .rows
832 .as_ref()
833 .is_some_and(|rows| !rows.rows.is_empty())
834 });
835 flow_mirror_task.detach(
836 self.node_manager.clone(),
837 self.mirror_pending_rows.clone(),
838 has_instant_rows,
839 )?;
840
841 let dispatcher = self.write_dispatcher.clone();
842 let request = request_factory.build_insert(normal_requests);
843 let writes = dispatcher.prepare(vec![request]).await?;
844 let affected_rows =
845 common_runtime::spawn_global(async move { dispatcher.execute(writes, None).await })
846 .await
847 .context(JoinTaskSnafu)?
848 .context(RequestInsertsSnafu)?
849 .affected_rows;
850 crate::metrics::DIST_INGEST_ROW_COUNT
851 .with_label_values(&[ctx.get_db_string().as_str()])
852 .inc_by(affected_rows as u64);
853 Ok(Output::new(
854 OutputData::AffectedRows(affected_rows),
855 OutputMeta::new_with_cost(write_cost as _),
856 ))
857 }
858
859 fn auto_create_disabled_reason(&self, ctx: &QueryContextRef) -> Result<Option<&'static str>> {
863 let auto_create_table_hint = ctx
864 .extension(AUTO_CREATE_TABLE_KEY)
865 .map(|v| v.parse::<bool>())
866 .transpose()
867 .map_err(|_| {
868 InvalidInsertRequestSnafu {
869 reason: "`auto_create_table` hint must be a boolean",
870 }
871 .build()
872 })?
873 .unwrap_or(true);
874 Ok(if !self.auto_create_table {
875 Some("auto-create table is disabled by frontend config")
876 } else if !auto_create_table_hint {
877 Some("`auto_create_table` hint is disabled")
878 } else {
879 None
880 })
881 }
882
883 fn is_auto_create_exempt_private_table(schema: &str, table: &str) -> bool {
886 schema == DEFAULT_PRIVATE_SCHEMA_NAME
887 && matches!(
888 table,
889 DEFAULT_EVENTS_TABLE_NAME | SLOW_QUERY_TABLE_NAME | REGION_STATS_HISTORY_TABLE_NAME
890 )
891 }
892
893 pub async fn ensure_bulk_insert_schema(
897 &self,
898 table: TableRef,
899 request_schema: &ArrowSchema,
900 ctx: &QueryContextRef,
901 statement_executor: &StatementExecutor,
902 ) -> Result<TableRef> {
903 let table_info = table.table_info();
904 if self.auto_create_disabled_reason(ctx)?.is_some()
905 && !Self::is_auto_create_exempt_private_table(&table_info.schema_name, &table_info.name)
906 {
907 return Ok(table);
908 }
909
910 let table_schema = table.schema();
911 let schema = request_schema
912 .fields()
913 .iter()
914 .filter(|field| table_schema.column_schema_by_name(field.name()).is_none())
915 .map(|field| {
916 let data_type = field.data_type();
917 ensure!(
920 !data_type.is_nested() && !matches!(data_type, ArrowDataType::Dictionary(..)),
921 crate::error::NotSupportedSnafu {
922 feat: format!(
923 "automatically adding bulk insert column '{}' with type {:?}",
924 field.name(),
925 data_type
926 ),
927 }
928 );
929 let column = datatypes::schema::ColumnSchema::try_from(field.as_ref())
930 .context(crate::error::ConvertSchemaSnafu)?;
931 let column_def =
934 try_as_column_def(&column, false).context(crate::error::ColumnDataTypeSnafu)?;
935 Ok(ColumnSchema {
936 column_name: column_def.name,
937 datatype: column_def.data_type,
938 semantic_type: column_def.semantic_type,
939 datatype_extension: column_def.datatype_extension,
940 options: column_def.options,
941 })
942 })
943 .collect::<Result<Vec<_>>>()?;
944 let mut request = RowInsertRequest {
945 table_name: table_info.name.clone(),
946 rows: Some(Rows {
947 schema,
948 rows: Vec::new(),
949 }),
950 };
951 let Some(alter_expr) =
952 self.get_alter_table_expr_on_demand(&mut request, &table, ctx, false, false, true)?
953 else {
954 return Ok(table);
955 };
956
957 statement_executor
958 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
959 .await?;
960 self.get_table(
961 &table_info.catalog_name,
962 &table_info.schema_name,
963 &table_info.name,
964 )
965 .await?
966 .with_context(|| TableNotFoundSnafu {
967 table_name: table_info.full_table_name(),
968 })
969 }
970
971 pub async fn ensure_trace_table_on_demand(
976 &self,
977 table_name: &str,
978 request_schema: Vec<ColumnSchema>,
979 alter_existing: bool,
980 ctx: &QueryContextRef,
981 statement_executor: &StatementExecutor,
982 ) -> Result<()> {
983 let mut requests = RowInsertRequests {
984 inserts: vec![RowInsertRequest {
985 table_name: table_name.to_string(),
986 rows: Some(api::v1::Rows {
987 schema: request_schema,
988 rows: Vec::new(),
989 }),
990 }],
991 };
992 self.create_or_alter_tables_on_demand(
993 &mut requests,
994 ctx,
995 AutoCreateTableType::Trace { alter_existing },
996 statement_executor,
997 false,
998 false,
999 None,
1000 )
1001 .await?;
1002 Ok(())
1003 }
1004
1005 #[allow(clippy::too_many_arguments)]
1024 async fn create_or_alter_tables_on_demand(
1025 &self,
1026 requests: &mut RowInsertRequests,
1027 ctx: &QueryContextRef,
1028 auto_create_table_type: AutoCreateTableType,
1029 statement_executor: &StatementExecutor,
1030 accommodate_existing_schema: bool,
1031 is_single_value: bool,
1032 align_time_index_unit: Option<TimeUnit>,
1033 ) -> Result<CreateAlterTableResult> {
1034 let _timer = crate::metrics::CREATE_ALTER_ON_DEMAND
1035 .with_label_values(&[auto_create_table_type.as_str()])
1036 .start_timer();
1037 let catalog = ctx.current_catalog();
1038 let schema = ctx.current_schema();
1039
1040 let auto_create_disabled_reason = self.auto_create_disabled_reason(ctx)?;
1041 let has_auto_create_exempt_table = auto_create_disabled_reason.is_some()
1043 && requests
1044 .inserts
1045 .iter()
1046 .any(|req| Self::is_auto_create_exempt_private_table(&schema, &req.table_name));
1047 let mut table_infos = HashMap::new();
1048 if let Some(disabled_reason) = auto_create_disabled_reason
1050 && !has_auto_create_exempt_table
1051 {
1052 let mut instant_table_ids = HashSet::new();
1053 for req in &mut requests.inserts {
1054 let table = match self.get_table(catalog, &schema, &req.table_name).await? {
1055 Some(table) => table,
1056 None if is_ddl_reserved_table(&schema, &req.table_name) => {
1059 statement_executor
1060 .create_declared_relationships_table(catalog, ctx.clone())
1061 .await?
1062 }
1063 None => {
1064 return InvalidInsertRequestSnafu {
1065 reason: format!(
1066 "Table `{}` does not exist, and {}",
1067 req.table_name, disabled_reason
1068 ),
1069 }
1070 .fail();
1071 }
1072 };
1073 if align_time_index_unit.is_some()
1077 && let Some(rows) = req.rows.as_mut()
1078 && let Some(target_unit) = table_time_index_unit(&table)
1079 {
1080 convert_rows_time_unit(rows, target_unit)?;
1081 }
1082 let table_info = table.table_info();
1083 if matches!(auto_create_table_type, AutoCreateTableType::Trace { .. }) {
1084 validate_trace_table_model(&table_info, ctx)?;
1085 }
1086 if table_info.is_ttl_instant_table() {
1087 instant_table_ids.insert(table_info.table_id());
1088 }
1089 table_infos.insert(table_info.table_id(), table.table_info());
1090 }
1091 let ret = CreateAlterTableResult {
1092 instant_table_ids,
1093 table_infos,
1094 };
1095 return Ok(ret);
1096 }
1097
1098 let mut create_tables = vec![];
1099 let mut alter_tables = vec![];
1100 let mut need_refresh_table_infos = HashSet::new();
1101 let mut instant_table_ids = HashSet::new();
1102 let mut per_table_semantics: Option<Option<PerTableSemanticIndex>> = None;
1103
1104 for req in &mut requests.inserts {
1105 let auto_create_allowed = auto_create_disabled_reason.is_none()
1107 || Self::is_auto_create_exempt_private_table(&schema, &req.table_name);
1108 match self.get_table(catalog, &schema, &req.table_name).await? {
1109 Some(table) => {
1110 let table_info = table.table_info();
1111 if matches!(auto_create_table_type, AutoCreateTableType::Trace { .. }) {
1112 validate_trace_table_model(&table_info, ctx)?;
1113 }
1114 if table_info.is_ttl_instant_table() {
1115 instant_table_ids.insert(table_info.table_id());
1116 }
1117 if align_time_index_unit.is_some()
1121 && let Some(rows) = req.rows.as_mut()
1122 && let Some(target_unit) = table_time_index_unit(&table)
1123 {
1124 convert_rows_time_unit(rows, target_unit)?;
1125 }
1126 if auto_create_allowed
1127 && let Some(alter_expr) = self.get_alter_table_expr_on_demand(
1128 req,
1129 &table,
1130 ctx,
1131 accommodate_existing_schema,
1132 is_single_value,
1133 auto_create_table_type.alter_existing(),
1134 )?
1135 {
1136 alter_tables.push(alter_expr);
1137 need_refresh_table_infos.insert((
1138 catalog.to_string(),
1139 schema.clone(),
1140 req.table_name.clone(),
1141 ));
1142 } else {
1143 table_infos.insert(table_info.table_id(), table.table_info());
1144 }
1145 }
1146 None if is_ddl_reserved_table(&schema, &req.table_name) => {
1150 let table = statement_executor
1151 .create_declared_relationships_table(catalog, ctx.clone())
1152 .await?;
1153 let table_info = table.table_info();
1154 if table_info.is_ttl_instant_table() {
1155 instant_table_ids.insert(table_info.table_id());
1156 }
1157 table_infos.insert(table_info.table_id(), table_info);
1158 }
1159 None if !auto_create_allowed
1160 && let Some(disabled_reason) = auto_create_disabled_reason =>
1161 {
1162 return InvalidInsertRequestSnafu {
1163 reason: format!(
1164 "Table `{}` does not exist, and {}",
1165 req.table_name, disabled_reason,
1166 ),
1167 }
1168 .fail();
1169 }
1170 None => {
1171 if let Some(physical_unit) = align_time_index_unit
1175 && let Some(rows) = req.rows.as_mut()
1176 {
1177 convert_rows_time_unit(rows, physical_unit)?;
1178 }
1179 let semantic_index = per_table_semantics
1180 .get_or_insert_with(|| parse_per_table_semantic_index(ctx))
1181 .as_ref();
1182 let create_expr = self.get_create_table_expr_on_demand(
1183 req,
1184 &auto_create_table_type,
1185 ctx,
1186 semantic_index,
1187 )?;
1188 create_tables.push(create_expr);
1189 }
1190 }
1191 }
1192
1193 match auto_create_table_type {
1194 AutoCreateTableType::Logical(_) => {
1195 if !create_tables.is_empty() {
1196 let tables = self
1198 .create_logical_tables(create_tables, ctx, statement_executor)
1199 .await?;
1200
1201 for table in tables {
1202 let table_info = table.table_info();
1203 if table_info.is_ttl_instant_table() {
1204 instant_table_ids.insert(table_info.table_id());
1205 }
1206 table_infos.insert(table_info.table_id(), table.table_info());
1207 }
1208 }
1209 if !alter_tables.is_empty() {
1210 statement_executor
1212 .alter_logical_tables(alter_tables, ctx.clone(), TriggerReason::AutoAlter)
1213 .await?;
1214 }
1215 }
1216 AutoCreateTableType::Physical
1217 | AutoCreateTableType::Log
1218 | AutoCreateTableType::LastNonNull => {
1219 for create_table in create_tables {
1222 let table = self
1223 .create_physical_table(create_table, None, ctx, statement_executor)
1224 .await?;
1225 let table_info = table.table_info();
1226 if table_info.is_ttl_instant_table() {
1227 instant_table_ids.insert(table_info.table_id());
1228 }
1229 table_infos.insert(table_info.table_id(), table.table_info());
1230 }
1231 for alter_expr in alter_tables.into_iter() {
1232 statement_executor
1233 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
1234 .await?;
1235 }
1236 }
1237
1238 AutoCreateTableType::Trace { .. } => {
1239 let trace_table_name = ctx
1240 .extension(TRACE_TABLE_NAME_SESSION_KEY)
1241 .unwrap_or(TRACE_TABLE_NAME);
1242
1243 let trace_table_partitions = if let Some(trace_table_partitions) =
1244 ctx.extension(TRACE_TABLE_PARTITIONS_HINT_KEY)
1245 {
1246 let p = trace_table_partitions.parse::<u32>().map_err(|_| {
1247 InvalidInsertRequestSnafu {
1248 reason: format!(
1249 "Failed to parse trace_table_partitions: {}",
1250 trace_table_partitions
1251 ),
1252 }
1253 .build()
1254 })?;
1255 Some(p)
1256 } else {
1257 None
1258 };
1259
1260 for mut create_table in create_tables {
1263 if create_table.table_name == trace_services_table_name(trace_table_name)
1264 || create_table.table_name == trace_operations_table_name(trace_table_name)
1265 {
1266 create_table
1268 .table_options
1269 .insert(APPEND_MODE_KEY.to_string(), "false".to_string());
1270 create_table.table_options.remove(TTL_KEY);
1272
1273 let table = self
1274 .create_physical_table(create_table, None, ctx, statement_executor)
1275 .await?;
1276 let table_info = table.table_info();
1277 if table_info.is_ttl_instant_table() {
1278 instant_table_ids.insert(table_info.table_id());
1279 }
1280 table_infos.insert(table_info.table_id(), table.table_info());
1281 } else {
1282 let partitions = if matches!(trace_table_partitions, Some(0) | Some(1)) {
1285 None
1287 } else {
1288 let p = partition_rule_for_hexstring(
1289 TRACE_ID_COLUMN,
1290 trace_table_partitions,
1291 )
1292 .context(CreatePartitionRulesSnafu)?;
1293 Some(p)
1294 };
1295
1296 let index_columns =
1301 [TRACE_ID_COLUMN, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN];
1302 for index_column in index_columns {
1303 if let Some(col) = create_table
1304 .column_defs
1305 .iter_mut()
1306 .find(|c| c.name == index_column)
1307 {
1308 col.options =
1309 options_from_skipping(&SkippingIndexOptions::default())
1310 .context(ColumnOptionsSnafu)?;
1311 } else {
1312 warn!(
1313 "Column {} not found when creating index for trace table: {}.",
1314 index_column, create_table.table_name
1315 );
1316 }
1317 }
1318
1319 create_table.table_options.insert(
1321 TABLE_DATA_MODEL.to_string(),
1322 ctx.extension(SEMANTIC_PIPELINE)
1323 .unwrap_or(TABLE_DATA_MODEL_TRACE_V1)
1324 .to_string(),
1325 );
1326
1327 let table = self
1328 .create_physical_table(
1329 create_table,
1330 partitions,
1331 ctx,
1332 statement_executor,
1333 )
1334 .await?;
1335 let table_info = table.table_info();
1336 if table_info.is_ttl_instant_table() {
1337 instant_table_ids.insert(table_info.table_id());
1338 }
1339 table_infos.insert(table_info.table_id(), table.table_info());
1340 }
1341 }
1342 for alter_expr in alter_tables.into_iter() {
1343 statement_executor
1344 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
1345 .await?;
1346 }
1347 }
1348 }
1349
1350 for (catalog, schema, table_name) in need_refresh_table_infos {
1352 let table = self
1353 .get_table(&catalog, &schema, &table_name)
1354 .await?
1355 .context(TableNotFoundSnafu {
1356 table_name: common_catalog::format_full_table_name(
1357 &catalog,
1358 &schema,
1359 &table_name,
1360 ),
1361 })?;
1362 let table_info = table.table_info();
1363 table_infos.insert(table_info.table_id(), table.table_info());
1364 }
1365
1366 Ok(CreateAlterTableResult {
1367 instant_table_ids,
1368 table_infos,
1369 })
1370 }
1371
1372 async fn create_physical_table_on_demand(
1373 &self,
1374 ctx: &QueryContextRef,
1375 physical_table: String,
1376 statement_executor: &StatementExecutor,
1377 ) -> Result<TableRef> {
1378 let catalog_name = ctx.current_catalog();
1379 let schema_name = ctx.current_schema();
1380
1381 if let Some(table) = self
1383 .get_table(catalog_name, &schema_name, &physical_table)
1384 .await?
1385 {
1386 return Ok(table);
1387 }
1388
1389 if let Some(disabled_reason) = self.auto_create_disabled_reason(ctx)? {
1391 return InvalidInsertRequestSnafu {
1392 reason: format!(
1393 "Physical table `{physical_table}` does not exist, and {disabled_reason}"
1394 ),
1395 }
1396 .fail();
1397 }
1398
1399 let table_reference = TableReference::full(catalog_name, &schema_name, &physical_table);
1400 debug!("Ensuring physical metric table `{table_reference}` exists for insert");
1401
1402 let default_schema = vec![
1404 ColumnSchema {
1405 column_name: greptime_timestamp().to_string(),
1406 datatype: ColumnDataType::TimestampMillisecond as _,
1407 semantic_type: SemanticType::Timestamp as _,
1408 datatype_extension: None,
1409 options: None,
1410 },
1411 ColumnSchema {
1412 column_name: greptime_value().to_string(),
1413 datatype: ColumnDataType::Float64 as _,
1414 semantic_type: SemanticType::Field as _,
1415 datatype_extension: None,
1416 options: None,
1417 },
1418 ];
1419 let create_table_expr =
1420 &mut build_create_table_expr(&table_reference, &default_schema, default_engine())?;
1421
1422 create_table_expr.engine = METRIC_ENGINE_NAME.to_string();
1423 create_table_expr
1424 .table_options
1425 .insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), "true".to_string());
1426
1427 let res = statement_executor
1429 .create_table_inner(
1430 create_table_expr,
1431 None,
1432 ctx.clone(),
1433 TriggerReason::AutoCreate,
1434 )
1435 .await;
1436
1437 match res {
1438 Ok(table) => Ok(table),
1439 Err(err) => {
1440 error!(err; "Failed to create table {table_reference}");
1441 Err(err)
1442 }
1443 }
1444 }
1445
1446 async fn get_table(
1447 &self,
1448 catalog: &str,
1449 schema: &str,
1450 table: &str,
1451 ) -> Result<Option<TableRef>> {
1452 self.catalog_manager
1453 .table(catalog, schema, table, None)
1454 .await
1455 .context(CatalogSnafu)
1456 }
1457
1458 fn get_create_table_expr_on_demand(
1459 &self,
1460 req: &RowInsertRequest,
1461 create_type: &AutoCreateTableType,
1462 ctx: &QueryContextRef,
1463 semantic_index: Option<&PerTableSemanticIndex>,
1464 ) -> Result<CreateTableExpr> {
1465 let schema = ctx.current_schema();
1466 let mut table_options = std::collections::HashMap::with_capacity(4);
1467 fill_table_options_for_create(&mut table_options, create_type, ctx);
1468 apply_per_table_semantic_options(
1469 &mut table_options,
1470 semantic_index,
1471 ctx.current_schema().as_str(),
1472 &req.table_name,
1473 );
1474
1475 let engine_name = if let AutoCreateTableType::Logical(_) = create_type {
1476 METRIC_ENGINE_NAME
1478 } else {
1479 default_engine()
1480 };
1481
1482 let table_ref = TableReference::full(ctx.current_catalog(), &schema, &req.table_name);
1483 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1485 let mut create_table_expr =
1486 build_create_table_expr(&table_ref, request_schema, engine_name)?;
1487
1488 if ctx.extension(SPLUNK_PK_METADATA_ORDER_KEY).is_some() {
1490 reorder_splunk_primary_keys(&mut create_table_expr.primary_keys);
1491 }
1492
1493 debug!("Ensuring table `{table_ref}` exists for insert");
1494 create_table_expr.table_options.extend(table_options);
1495 Ok(create_table_expr)
1496 }
1497
1498 fn get_alter_table_expr_on_demand(
1506 &self,
1507 req: &mut RowInsertRequest,
1508 table: &TableRef,
1509 ctx: &QueryContextRef,
1510 accommodate_existing_schema: bool,
1511 is_single_value: bool,
1512 alter_existing: bool,
1513 ) -> Result<Option<AlterTableExpr>> {
1514 if !alter_existing {
1515 return Ok(None);
1516 }
1517
1518 let catalog_name = ctx.current_catalog();
1519 let schema_name = ctx.current_schema();
1520 let table_name = table.table_info().name.clone();
1521
1522 if is_ddl_reserved_table(&schema_name, &table_name) {
1525 return Ok(None);
1526 }
1527
1528 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1529 let request_field_count = request_schema
1530 .iter()
1531 .filter(|col| col.semantic_type == SemanticType::Field as i32)
1532 .count();
1533 let column_exprs = ColumnExpr::from_column_schemas(request_schema);
1534 let add_columns = expr_helper::extract_add_columns_expr(&table.schema(), column_exprs)?;
1535 let Some(mut add_columns) = add_columns else {
1536 return Ok(None);
1537 };
1538
1539 if is_single_value {
1540 let request_is_native_histogram = request_is_native_histogram(request_schema);
1541 let table_is_native_histogram = table_is_native_histogram(table);
1542 ensure!(
1543 request_is_native_histogram == table_is_native_histogram,
1544 InvalidInsertRequestSnafu {
1545 reason: format!(
1546 "Table `{table_name}` cannot mix native histogram and float sample fields"
1547 ),
1548 }
1549 );
1550 }
1551
1552 if accommodate_existing_schema {
1554 let table_schema = table.schema();
1555 let ts_col_name = table_schema.timestamp_column().map(|c| c.name.clone());
1557 let mut field_col_name = None;
1559 if is_single_value && request_field_count <= 1 {
1560 let mut multiple_field_cols = false;
1561 table.field_columns().for_each(|col| {
1562 if field_col_name.is_none() {
1563 field_col_name = Some(col.name.clone());
1564 } else {
1565 multiple_field_cols = true;
1566 }
1567 });
1568 if multiple_field_cols {
1569 field_col_name = None;
1570 }
1571 }
1572
1573 if let Some(rows) = req.rows.as_mut() {
1575 for col in &mut rows.schema {
1576 match col.semantic_type {
1577 x if x == SemanticType::Timestamp as i32 => {
1578 if let Some(ref ts_name) = ts_col_name
1579 && col.column_name != *ts_name
1580 {
1581 col.column_name = ts_name.clone();
1582 }
1583 }
1584 x if x == SemanticType::Field as i32 => {
1585 if let Some(ref field_name) = field_col_name
1586 && col.column_name != *field_name
1587 {
1588 col.column_name = field_name.clone();
1589 }
1590 }
1591 _ => {}
1592 }
1593 }
1594 }
1595
1596 add_columns.add_columns.retain(|col| {
1598 let def = col.column_def.as_ref().unwrap();
1599 def.semantic_type == SemanticType::Tag as i32
1600 || (def.semantic_type == SemanticType::Field as i32 && field_col_name.is_none())
1601 });
1602
1603 if add_columns.add_columns.is_empty() {
1604 return Ok(None);
1605 }
1606 }
1607
1608 Ok(Some(AlterTableExpr {
1609 catalog_name: catalog_name.to_string(),
1610 schema_name: schema_name.clone(),
1611 table_name: table_name.clone(),
1612 kind: Some(Kind::AddColumns(add_columns)),
1613 }))
1614 }
1615
1616 async fn create_physical_table(
1618 &self,
1619 mut create_table_expr: CreateTableExpr,
1620 partitions: Option<Partitions>,
1621 ctx: &QueryContextRef,
1622 statement_executor: &StatementExecutor,
1623 ) -> Result<TableRef> {
1624 let res = statement_executor
1625 .create_table_inner(
1626 &mut create_table_expr,
1627 partitions,
1628 ctx.clone(),
1629 TriggerReason::AutoCreate,
1630 )
1631 .await;
1632
1633 let table_ref = TableReference::full(
1634 &create_table_expr.catalog_name,
1635 &create_table_expr.schema_name,
1636 &create_table_expr.table_name,
1637 );
1638
1639 match res {
1640 Ok(table) => {
1641 validate_trace_table_model(&table.table_info(), ctx)?;
1642 Ok(table)
1643 }
1644 Err(err) => {
1645 error!(err; "Failed to create table {}", table_ref);
1646 Err(err)
1647 }
1648 }
1649 }
1650
1651 async fn create_logical_tables(
1652 &self,
1653 create_table_exprs: Vec<CreateTableExpr>,
1654 ctx: &QueryContextRef,
1655 statement_executor: &StatementExecutor,
1656 ) -> Result<Vec<TableRef>> {
1657 let res = statement_executor
1658 .create_logical_tables(&create_table_exprs, ctx.clone(), TriggerReason::AutoCreate)
1659 .await;
1660
1661 match res {
1662 Ok(res) => Ok(res),
1663 Err(err) => {
1664 let failed_tables = create_table_exprs
1665 .into_iter()
1666 .map(|expr| {
1667 format!(
1668 "{}.{}.{}",
1669 expr.catalog_name, expr.schema_name, expr.table_name
1670 )
1671 })
1672 .collect::<Vec<_>>();
1673 error!(
1674 err;
1675 "Failed to create logical tables {:?}",
1676 failed_tables
1677 );
1678 Err(err)
1679 }
1680 }
1681 }
1682
1683 pub fn write_dispatcher(&self) -> &WriteDispatcherRef {
1685 &self.write_dispatcher
1686 }
1687
1688 pub fn node_manager(&self) -> &NodeManagerRef {
1689 &self.node_manager
1690 }
1691
1692 pub fn partition_manager(&self) -> &PartitionRuleManagerRef {
1693 &self.partition_manager
1694 }
1695
1696 pub fn table_flownode_set_cache(&self) -> &TableFlownodeSetCacheRef {
1697 &self.table_flownode_set_cache
1698 }
1699}
1700
1701fn request_is_native_histogram(request_schema: &[ColumnSchema]) -> bool {
1702 let mut fields = request_schema
1703 .iter()
1704 .filter(|col| col.semantic_type == SemanticType::Field as i32);
1705 let Some(col) = fields.next() else {
1706 return false;
1707 };
1708
1709 fields.next().is_none()
1710 && api::helper::is_column_type_value_eq(
1711 col.datatype,
1712 col.datatype_extension.clone(),
1713 native_histogram_value_type(),
1714 )
1715}
1716
1717fn table_time_index_unit(table: &TableRef) -> Option<TimeUnit> {
1721 table
1722 .table_info()
1723 .meta
1724 .schema
1725 .timestamp_column()
1726 .and_then(|col| col.data_type.as_timestamp().map(|ts| ts.unit()))
1727}
1728
1729fn convert_rows_time_unit(rows: &mut Rows, target_unit: TimeUnit) -> Result<()> {
1730 let Some(ts_index) = rows
1731 .schema
1732 .iter()
1733 .position(|col| col.semantic_type == SemanticType::Timestamp as i32)
1734 else {
1735 return Ok(());
1736 };
1737 let Some(source_unit) = ColumnDataType::try_from(rows.schema[ts_index].datatype)
1738 .ok()
1739 .and_then(api::helper::timestamp_unit)
1740 else {
1741 return Ok(());
1742 };
1743 if source_unit == target_unit {
1744 return Ok(());
1745 }
1746
1747 rows.schema[ts_index].datatype = api::helper::timestamp_datatype(target_unit) as i32;
1748 rows.schema[ts_index].datatype_extension = None;
1750
1751 for row in &mut rows.rows {
1759 debug_assert_eq!(row.values.len(), rows.schema.len());
1760 let value = &mut row.values[ts_index];
1761 let Some(value_data) = value.value_data.take() else {
1762 continue;
1763 };
1764 value.value_data =
1765 convert_timestamp_value_data(value_data, source_unit, target_unit, ts_index)?;
1766 }
1767 Ok(())
1768}
1769
1770fn convert_timestamp_value_data(
1771 value_data: ValueData,
1772 source_unit: TimeUnit,
1773 target_unit: TimeUnit,
1774 column_index: usize,
1775) -> Result<Option<ValueData>> {
1776 let timestamp = match value_data {
1777 ValueData::TimestampSecondValue(v) => Timestamp::new_second(v),
1778 ValueData::TimestampMillisecondValue(v) => Timestamp::new_millisecond(v),
1779 ValueData::TimestampMicrosecondValue(v) => Timestamp::new_microsecond(v),
1780 ValueData::TimestampNanosecondValue(v) => Timestamp::new_nanosecond(v),
1781 other => return Ok(Some(other)),
1783 };
1784 let converted = timestamp
1785 .convert_to(target_unit)
1786 .with_context(|| InvalidInsertRequestSnafu {
1787 reason: format!(
1788 "timestamp column {column_index} value {} in unit {source_unit:?} overflows when converting to unit {target_unit:?}",
1789 timestamp.value()
1790 ),
1791 })?;
1792 Ok(api::helper::to_grpc_value(datatypes::value::Value::Timestamp(converted)).value_data)
1793}
1794
1795fn table_is_native_histogram(table: &TableRef) -> bool {
1796 let mut fields = table.field_columns();
1797 let Some(col) = fields.next() else {
1798 return false;
1799 };
1800
1801 fields.next().is_none() && is_native_histogram_value_type(&col.data_type)
1802}
1803
1804fn validate_column_count_match(requests: &RowInsertRequests) -> Result<()> {
1805 for request in &requests.inserts {
1806 let rows = request.rows.as_ref().unwrap();
1807 let column_count = rows.schema.len();
1808 rows.rows.iter().try_for_each(|r| {
1809 ensure!(
1810 r.values.len() == column_count,
1811 InvalidInsertRequestSnafu {
1812 reason: format!(
1813 "column count mismatch, columns: {}, values: {}",
1814 column_count,
1815 r.values.len()
1816 )
1817 }
1818 );
1819 Ok(())
1820 })?;
1821 }
1822 Ok(())
1823}
1824
1825pub fn validate_trace_table_model(table_info: &TableInfo, ctx: &QueryContextRef) -> Result<()> {
1828 let Some(expected @ (TABLE_DATA_MODEL_TRACE_V1 | TABLE_DATA_MODEL_TRACE_V2)) =
1829 ctx.extension(SEMANTIC_PIPELINE)
1830 else {
1831 return Ok(());
1832 };
1833 if let Some(actual) = table_info.meta.options.data_model() {
1834 ensure!(
1835 actual == expected,
1836 InvalidInsertRequestSnafu {
1837 reason: format!(
1838 "Trace table `{}` uses {actual}, but the request uses {expected}",
1839 table_info.name,
1840 ),
1841 }
1842 );
1843 }
1844 Ok(())
1845}
1846
1847pub fn fill_table_options_for_create(
1849 table_options: &mut std::collections::HashMap<String, String>,
1850 create_type: &AutoCreateTableType,
1851 ctx: &QueryContextRef,
1852) {
1853 for key in VALID_TABLE_OPTION_KEYS {
1854 if let Some(value) = ctx.extension(key) {
1855 table_options.insert(key.to_string(), value.to_string());
1856 }
1857 }
1858
1859 for (key, value) in ctx.extensions() {
1861 if is_semantic_option_key(&key) && validate_semantic_option(&key, &value) {
1862 table_options.insert(key, value);
1863 }
1864 }
1865
1866 match create_type {
1867 AutoCreateTableType::Logical(physical_table) => {
1868 table_options.insert(
1869 LOGICAL_TABLE_METADATA_KEY.to_string(),
1870 physical_table.clone(),
1871 );
1872 }
1873 AutoCreateTableType::Physical => {
1874 if let Some(append_mode) = ctx.extension(APPEND_MODE_KEY) {
1875 table_options.insert(APPEND_MODE_KEY.to_string(), append_mode.to_string());
1876 }
1877 if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1878 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1879 }
1880 if let Some(time_window) = ctx.extension(TWCS_TIME_WINDOW) {
1881 table_options.insert(TWCS_TIME_WINDOW.to_string(), time_window.to_string());
1882 table_options.insert(
1884 COMPACTION_TYPE.to_string(),
1885 COMPACTION_TYPE_TWCS.to_string(),
1886 );
1887 }
1888 }
1889 AutoCreateTableType::Log => {
1892 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1893 }
1894 AutoCreateTableType::LastNonNull => {
1895 if ctx
1896 .extension(APPEND_MODE_KEY)
1897 .is_some_and(|value| value.eq_ignore_ascii_case("true"))
1898 {
1899 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1900 table_options.insert(MERGE_MODE_KEY.to_string(), "last_row".to_string());
1901 } else if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1902 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1903 } else {
1904 table_options.insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1905 }
1906 }
1907 AutoCreateTableType::Trace { .. } => {
1908 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1909 }
1910 }
1911}
1912
1913pub type PerTableSemanticIndex = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
1919
1920pub fn parse_per_table_semantic_index(ctx: &QueryContextRef) -> Option<PerTableSemanticIndex> {
1925 let raw = ctx.extension(SEMANTIC_PER_TABLE_INDEX_KEY)?;
1926 match serde_json::from_str(raw) {
1927 Ok(index) => Some(index),
1928 Err(_) => {
1929 warn!("failed to parse semantic per-table index, skipping per-table options");
1930 None
1931 }
1932 }
1933}
1934
1935pub fn apply_per_table_semantic_options(
1943 table_options: &mut std::collections::HashMap<String, String>,
1944 index: Option<&PerTableSemanticIndex>,
1945 schema: &str,
1946 table_name: &str,
1947) {
1948 let Some(entry) = index
1949 .and_then(|index| index.get(schema))
1950 .and_then(|tables| tables.get(table_name))
1951 else {
1952 return;
1953 };
1954 for (key, value) in entry {
1955 if is_semantic_option_key(key) && validate_semantic_option(key, value) {
1956 table_options.insert(key.clone(), value.clone());
1957 }
1958 }
1959}
1960
1961pub fn build_create_table_expr(
1962 table: &TableReference,
1963 request_schema: &[ColumnSchema],
1964 engine: &str,
1965) -> Result<CreateTableExpr> {
1966 expr_helper::create_table_expr_by_column_schemas(table, request_schema, engine, None)
1967}
1968
1969pub const SPLUNK_PK_METADATA_ORDER_KEY: &str = "splunk_pk_metadata_order";
1973
1974fn reorder_splunk_primary_keys(primary_keys: &mut [String]) {
1977 const LEAD: [&str; 3] = ["host", "source", "sourcetype"];
1978 primary_keys.sort_by_key(|name| {
1981 LEAD.iter()
1982 .position(|&lead| lead == name.as_str())
1983 .unwrap_or(LEAD.len())
1984 });
1985}
1986
1987struct CreateAlterTableResult {
1989 instant_table_ids: HashSet<TableId>,
1991 table_infos: HashMap<TableId, Arc<TableInfo>>,
1993}
1994
1995const MAX_MIRROR_PENDING_ROWS: usize = 1_000_000;
2003
2004const MIRROR_LOG_INTERVAL: Duration = Duration::from_secs(10);
2006const MIRROR_LOG_NEVER_REPORTED: u64 = u64::MAX;
2007
2008struct MirrorLog {
2009 last_log_millis: AtomicU64,
2010 events: AtomicU64,
2011}
2012
2013impl MirrorLog {
2014 const fn new() -> Self {
2015 Self {
2016 last_log_millis: AtomicU64::new(MIRROR_LOG_NEVER_REPORTED),
2017 events: AtomicU64::new(0),
2018 }
2019 }
2020
2021 fn claim_report(&self, now_millis: u64) -> Option<u64> {
2024 self.events.fetch_add(1, Ordering::Relaxed);
2025 let last = self.last_log_millis.load(Ordering::Relaxed);
2026 if last != MIRROR_LOG_NEVER_REPORTED
2027 && now_millis.saturating_sub(last) < MIRROR_LOG_INTERVAL.as_millis() as u64
2028 {
2029 return None;
2030 }
2031 if self
2032 .last_log_millis
2033 .compare_exchange(last, now_millis, Ordering::Relaxed, Ordering::Relaxed)
2034 .is_err()
2035 {
2036 return None;
2037 }
2038 Some(self.events.swap(0, Ordering::Relaxed))
2039 }
2040}
2041
2042static MIRROR_DROP_LOG: MirrorLog = MirrorLog::new();
2043static MIRROR_FAILURE_LOG: MirrorLog = MirrorLog::new();
2044
2045fn mirror_drop_log_millis() -> u64 {
2048 static START: LazyLock<Instant> = LazyLock::new(Instant::now);
2049 START.elapsed().as_millis() as u64
2050}
2051
2052struct FlowMirrorTask {
2053 requests: HashMap<Peer, RegionInsertRequests>,
2054}
2055
2056impl FlowMirrorTask {
2057 async fn new(
2058 cache: &TableFlownodeSetCacheRef,
2059 requests: impl Iterator<Item = &RegionInsertRequest>,
2060 ) -> Result<Self> {
2061 let mut src_table_reqs: HashMap<TableId, Option<(Vec<Peer>, RegionInsertRequests)>> =
2062 HashMap::new();
2063
2064 for req in requests {
2065 let table_id = RegionId::from_u64(req.region_id).table_id();
2066 match src_table_reqs.get_mut(&table_id) {
2067 Some(Some((_peers, reqs))) => reqs.requests.push(req.clone()),
2068 Some(None) => continue,
2070 _ => {
2071 let peers = cache
2073 .get(table_id)
2074 .await
2075 .context(RequestInsertsSnafu)?
2076 .unwrap_or_default()
2077 .values()
2078 .cloned()
2079 .collect::<HashSet<_>>()
2080 .into_iter()
2081 .collect::<Vec<_>>();
2082
2083 if !peers.is_empty() {
2084 let mut reqs = RegionInsertRequests::default();
2085 reqs.requests.push(req.clone());
2086 src_table_reqs.insert(table_id, Some((peers, reqs)));
2087 } else {
2088 src_table_reqs.insert(table_id, None);
2090 }
2091 }
2092 }
2093 }
2094
2095 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
2096
2097 for (_table_id, (peers, reqs)) in src_table_reqs
2098 .into_iter()
2099 .filter_map(|(k, v)| v.map(|v| (k, v)))
2100 {
2101 if peers.len() == 1 {
2102 inserts
2104 .entry(peers[0].clone())
2105 .or_default()
2106 .requests
2107 .extend(reqs.requests);
2108 continue;
2109 } else {
2110 for flownode in peers {
2112 inserts
2113 .entry(flownode.clone())
2114 .or_default()
2115 .requests
2116 .extend(reqs.requests.clone());
2117 }
2118 }
2119 }
2120
2121 Ok(Self { requests: inserts })
2122 }
2123
2124 fn pending_rows(&self) -> u64 {
2133 self.requests.values().map(region_inserts_rows).sum()
2134 }
2135
2136 fn detach(
2137 self,
2138 node_manager: NodeManagerRef,
2139 mirror_pending_rows: Arc<AtomicU64>,
2140 has_instant_rows: bool,
2141 ) -> Result<()> {
2142 let num_rows = self.pending_rows();
2145 if num_rows == 0 {
2146 return Ok(());
2147 }
2148 if has_instant_rows && num_rows > MAX_MIRROR_PENDING_ROWS as u64 {
2149 return InvalidInsertRequestSnafu {
2150 reason: format!(
2151 "flow mirror batch has {num_rows} peer-fan-out rows, exceeding the limit of {}; reduce the batch size",
2152 MAX_MIRROR_PENDING_ROWS
2153 ),
2154 }
2155 .fail();
2156 }
2157
2158 let pending = reserve_mirror_pending_rows(&mirror_pending_rows, num_rows);
2159 if pending > MAX_MIRROR_PENDING_ROWS as u64 {
2160 release_mirror_pending_rows(&mirror_pending_rows, num_rows);
2161 if has_instant_rows {
2162 return Err(meter_core::collect::WriteRejected::new(
2163 "flow mirror pending rows limit exceeded for instant-TTL insert",
2164 ))
2165 .context(WriteRejectedSnafu);
2166 }
2167 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.inc_by(num_rows);
2168 if let Some(events) = MIRROR_DROP_LOG.claim_report(mirror_drop_log_millis()) {
2169 warn!(
2170 "Flow mirror write dropped: {} pending rows exceeds limit {}, {} drop events since last report",
2171 pending, MAX_MIRROR_PENDING_ROWS, events
2172 );
2173 }
2174 return Ok(());
2175 }
2176
2177 for (peer, inserts) in self.requests {
2178 let peer_rows = region_inserts_rows(&inserts);
2182 let node_manager = node_manager.clone();
2183 let mirror_pending_rows = mirror_pending_rows.clone();
2184 common_runtime::spawn_global(async move {
2185 let result = node_manager
2186 .flownode(&peer)
2187 .await
2188 .handle_inserts(inserts)
2189 .await
2190 .context(RequestInsertsSnafu);
2191
2192 match result {
2193 Ok(resp) => {
2194 let affected_rows = resp.affected_rows;
2195 crate::metrics::DIST_MIRROR_ROW_COUNT.inc_by(affected_rows);
2196 }
2197 Err(err) => {
2198 if let Some(events) =
2199 MIRROR_FAILURE_LOG.claim_report(mirror_drop_log_millis())
2200 {
2201 error!(err; flownode_id = peer.id, flownode_addr = %peer.addr, "Failed to insert data into flownode ({} total mirror failures across peers since last report)", events);
2202 }
2203 }
2204 }
2205 release_mirror_pending_rows(&mirror_pending_rows, peer_rows);
2208 });
2209 }
2210
2211 Ok(())
2212 }
2213}
2214
2215fn region_inserts_rows(inserts: &RegionInsertRequests) -> u64 {
2218 inserts
2219 .requests
2220 .iter()
2221 .filter_map(|req| req.rows.as_ref())
2222 .map(|rows| rows.rows.len() as u64)
2223 .sum()
2224}
2225
2226fn reserve_mirror_pending_rows(mirror_pending_rows: &AtomicU64, rows: u64) -> u64 {
2231 let pending = mirror_pending_rows.fetch_add(rows, Ordering::Relaxed) + rows;
2232 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(rows as i64);
2235 pending
2236}
2237
2238fn release_mirror_pending_rows(mirror_pending_rows: &AtomicU64, rows: u64) {
2244 let prev = mirror_pending_rows
2245 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |pending| {
2246 Some(pending.saturating_sub(rows))
2247 })
2248 .unwrap_or_else(|prev| prev);
2249 let released = rows.min(prev);
2253 if released > 0 {
2254 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(released as i64);
2255 }
2256}
2257
2258#[cfg(test)]
2259mod tests {
2260 use std::sync::Arc;
2261 use std::sync::atomic::{AtomicU64, Ordering};
2262
2263 use api::helper::ColumnDataTypeWrapper;
2264 use api::v1::flow::FlowResponse;
2265 use api::v1::helper::{field_column_schema, time_index_column_schema};
2266 use api::v1::{Row, RowInsertRequest, Rows, Value};
2267 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
2268 use common_meta::cache::new_table_flownode_set_cache;
2269 use common_meta::ddl::test_util::datanode_handler::NaiveDatanodeHandler;
2270 use common_meta::test_util::{MockDatanodeManager, MockFlownodeHandler, MockFlownodeManager};
2271 use common_query::native_histogram::NATIVE_HISTOGRAM_FIELD;
2272 use common_query::prelude::{greptime_native_histogram, set_default_prefix};
2273 use datatypes::data_type::ConcreteDataType;
2274 use datatypes::schema::ColumnSchema;
2275 use moka::future::Cache;
2276 use session::context::QueryContext;
2277 use table::TableRef;
2278 use table::dist_table::DummyDataSource;
2279 use table::metadata::{TableInfoBuilder, TableMetaBuilder, TableType};
2280
2281 use crate::insert::*;
2282 use crate::test_util::{
2283 create_partition_rule_manager, new_test_table_info, prepare_mocked_backend,
2284 };
2285
2286 fn make_table_ref_with_schema(
2287 ts_name: &str,
2288 field_name: &str,
2289 field_type: ConcreteDataType,
2290 ) -> TableRef {
2291 let schema = datatypes::schema::SchemaBuilder::try_from_columns(vec![
2292 ColumnSchema::new(
2293 ts_name,
2294 ConcreteDataType::timestamp_millisecond_datatype(),
2295 false,
2296 )
2297 .with_time_index(true),
2298 ColumnSchema::new(field_name, field_type, true),
2299 ])
2300 .unwrap()
2301 .build()
2302 .unwrap();
2303 let meta = TableMetaBuilder::empty()
2304 .schema(Arc::new(schema))
2305 .primary_key_indices(vec![])
2306 .value_indices(vec![1])
2307 .engine("mito")
2308 .next_column_id(0)
2309 .options(Default::default())
2310 .created_on(Default::default())
2311 .build()
2312 .unwrap();
2313 let info = Arc::new(
2314 TableInfoBuilder::default()
2315 .table_id(1)
2316 .table_version(0)
2317 .name("test_table")
2318 .schema_name(DEFAULT_SCHEMA_NAME)
2319 .catalog_name(DEFAULT_CATALOG_NAME)
2320 .desc(None)
2321 .table_type(TableType::Base)
2322 .meta(meta)
2323 .build()
2324 .unwrap(),
2325 );
2326 Arc::new(table::Table::new(
2327 info,
2328 table::metadata::FilterPushDownType::Unsupported,
2329 Arc::new(DummyDataSource),
2330 ))
2331 }
2332
2333 fn make_metric_physical_table_ref_with_time_unit(unit: TimeUnit) -> TableRef {
2334 let schema = datatypes::schema::SchemaBuilder::try_from_columns(vec![
2335 ColumnSchema::new(
2336 greptime_timestamp(),
2337 ConcreteDataType::timestamp_datatype(unit),
2338 false,
2339 )
2340 .with_time_index(true),
2341 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
2342 ])
2343 .unwrap()
2344 .build()
2345 .unwrap();
2346 let meta = TableMetaBuilder::empty()
2347 .schema(Arc::new(schema))
2348 .primary_key_indices(vec![])
2349 .value_indices(vec![1])
2350 .engine("metric")
2351 .next_column_id(0)
2352 .options(Default::default())
2353 .created_on(Default::default())
2354 .build()
2355 .unwrap();
2356 let info = Arc::new(
2357 TableInfoBuilder::default()
2358 .table_id(1)
2359 .table_version(0)
2360 .name("greptime_physical_table")
2361 .schema_name(DEFAULT_SCHEMA_NAME)
2362 .catalog_name(DEFAULT_CATALOG_NAME)
2363 .desc(None)
2364 .table_type(TableType::Base)
2365 .meta(meta)
2366 .build()
2367 .unwrap(),
2368 );
2369 Arc::new(table::Table::new(
2370 info,
2371 table::metadata::FilterPushDownType::Unsupported,
2372 Arc::new(DummyDataSource),
2373 ))
2374 }
2375
2376 fn ms_row_insert_request(timestamp_ms: i64) -> RowInsertRequest {
2377 ms_row_insert_request_named("my_metric", timestamp_ms)
2378 }
2379
2380 fn ms_row_insert_request_named(table: &str, timestamp_ms: i64) -> RowInsertRequest {
2381 RowInsertRequest {
2382 table_name: table.to_string(),
2383 rows: Some(Rows {
2384 schema: vec![
2385 time_index_column_schema(
2386 greptime_timestamp(),
2387 ColumnDataType::TimestampMillisecond,
2388 ),
2389 field_column_schema(greptime_value(), ColumnDataType::Float64),
2390 ],
2391 rows: vec![Row {
2392 values: vec![
2393 Value {
2394 value_data: Some(ValueData::TimestampMillisecondValue(timestamp_ms)),
2395 },
2396 Value {
2397 value_data: Some(ValueData::F64Value(1.0)),
2398 },
2399 ],
2400 }],
2401 }),
2402 }
2403 }
2404
2405 fn convert_row_insert_requests_time_unit(
2407 requests: &mut RowInsertRequests,
2408 target_unit: TimeUnit,
2409 ) -> Result<()> {
2410 for request in &mut requests.inserts {
2411 let Some(rows) = request.rows.as_mut() else {
2412 continue;
2413 };
2414 convert_rows_time_unit(rows, target_unit)?;
2415 }
2416 Ok(())
2417 }
2418
2419 #[test]
2420 fn test_convert_row_insert_requests_time_unit_noop_when_matching() {
2421 let mut requests = RowInsertRequests {
2422 inserts: vec![ms_row_insert_request(123)],
2423 };
2424 convert_row_insert_requests_time_unit(&mut requests, TimeUnit::Millisecond).unwrap();
2425 let rows = requests.inserts[0].rows.as_ref().unwrap();
2426 assert_eq!(
2427 rows.schema[0].datatype,
2428 ColumnDataType::TimestampMillisecond as i32
2429 );
2430 assert!(matches!(
2431 rows.rows[0].values[0].value_data,
2432 Some(ValueData::TimestampMillisecondValue(123))
2433 ));
2434 }
2435
2436 #[test]
2437 fn test_convert_row_insert_requests_time_unit_widens_losslessly() {
2438 let mut requests = RowInsertRequests {
2439 inserts: vec![ms_row_insert_request(123)],
2440 };
2441 convert_row_insert_requests_time_unit(&mut requests, TimeUnit::Microsecond).unwrap();
2442 let rows = requests.inserts[0].rows.as_ref().unwrap();
2443 assert_eq!(
2444 rows.schema[0].datatype,
2445 ColumnDataType::TimestampMicrosecond as i32
2446 );
2447 assert!(matches!(
2448 rows.rows[0].values[0].value_data,
2449 Some(ValueData::TimestampMicrosecondValue(123_000))
2450 ));
2451 }
2452
2453 #[test]
2454 fn test_convert_row_insert_requests_time_unit_truncates_on_narrowing() {
2455 let requests = |value_ns: i64| RowInsertRequests {
2458 inserts: vec![RowInsertRequest {
2459 table_name: "my_metric".to_string(),
2460 rows: Some(Rows {
2461 schema: vec![
2462 time_index_column_schema(
2463 greptime_timestamp(),
2464 ColumnDataType::TimestampNanosecond,
2465 ),
2466 field_column_schema(greptime_value(), ColumnDataType::Float64),
2467 ],
2468 rows: vec![Row {
2469 values: vec![
2470 Value {
2471 value_data: Some(ValueData::TimestampNanosecondValue(value_ns)),
2472 },
2473 Value {
2474 value_data: Some(ValueData::F64Value(1.0)),
2475 },
2476 ],
2477 }],
2478 }),
2479 }],
2480 };
2481
2482 let mut reqs = requests(123_456_789);
2483 convert_row_insert_requests_time_unit(&mut reqs, TimeUnit::Microsecond).unwrap();
2484 let rows = reqs.inserts[0].rows.as_ref().unwrap();
2485 assert!(matches!(
2486 rows.rows[0].values[0].value_data,
2487 Some(ValueData::TimestampMicrosecondValue(123_456))
2488 ));
2489
2490 let mut reqs = requests(123_456_789);
2491 convert_row_insert_requests_time_unit(&mut reqs, TimeUnit::Millisecond).unwrap();
2492 let rows = reqs.inserts[0].rows.as_ref().unwrap();
2493 assert!(matches!(
2494 rows.rows[0].values[0].value_data,
2495 Some(ValueData::TimestampMillisecondValue(123))
2496 ));
2497
2498 let mut reqs = requests(-123_456_789);
2499 convert_row_insert_requests_time_unit(&mut reqs, TimeUnit::Microsecond).unwrap();
2500 let rows = reqs.inserts[0].rows.as_ref().unwrap();
2501 assert!(matches!(
2502 rows.rows[0].values[0].value_data,
2503 Some(ValueData::TimestampMicrosecondValue(-123_457))
2504 ));
2505 }
2506
2507 #[test]
2508 fn test_convert_row_insert_requests_time_unit_overflow() {
2509 let mut requests = RowInsertRequests {
2510 inserts: vec![ms_row_insert_request(i64::MAX)],
2511 };
2512 let err =
2513 convert_row_insert_requests_time_unit(&mut requests, TimeUnit::Nanosecond).unwrap_err();
2514 assert!(err.to_string().contains("overflows"), "{err}");
2515 }
2516
2517 #[test]
2518 fn test_table_time_index_unit() {
2519 assert_eq!(
2520 table_time_index_unit(&make_metric_physical_table_ref_with_time_unit(
2521 TimeUnit::Microsecond
2522 )),
2523 Some(TimeUnit::Microsecond)
2524 );
2525 assert_eq!(
2526 table_time_index_unit(&make_metric_physical_table_ref_with_time_unit(
2527 TimeUnit::Millisecond
2528 )),
2529 Some(TimeUnit::Millisecond)
2530 );
2531 }
2532
2533 #[tokio::test]
2534 async fn test_accommodate_existing_schema_and_reject_kind_changes() {
2535 let ts_name = "my_ts";
2536 let field_name = "my_field";
2537 let table =
2538 make_table_ref_with_schema(ts_name, field_name, ConcreteDataType::float64_datatype());
2539
2540 let mut req = RowInsertRequest {
2542 table_name: "test_table".to_string(),
2543 rows: Some(Rows {
2544 schema: vec![
2545 time_index_column_schema("ts_wrong", ColumnDataType::TimestampMillisecond),
2546 field_column_schema("field_wrong", ColumnDataType::Float64),
2547 ],
2548 rows: vec![api::v1::Row {
2549 values: vec![Value::default(), Value::default()],
2550 }],
2551 }),
2552 };
2553 let ctx = Arc::new(QueryContext::with(
2554 DEFAULT_CATALOG_NAME,
2555 DEFAULT_SCHEMA_NAME,
2556 ));
2557
2558 let kv_backend = prepare_mocked_backend().await;
2559 let inserter = Inserter::new(
2560 catalog::memory::MemoryCatalogManager::new(),
2561 create_partition_rule_manager(kv_backend.clone()).await,
2562 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
2563 Arc::new(new_table_flownode_set_cache(
2564 String::new(),
2565 Cache::new(100),
2566 kv_backend.clone(),
2567 )),
2568 true,
2569 );
2570 assert!(
2572 inserter
2573 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, false, false, false)
2574 .unwrap()
2575 .is_none()
2576 );
2577 let alter_expr = inserter
2578 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, true, true, true)
2579 .unwrap();
2580 assert!(alter_expr.is_none());
2581
2582 let req_schema = req.rows.as_ref().unwrap().schema.clone();
2584 assert_eq!(req_schema[0].column_name, ts_name);
2585 assert_eq!(req_schema[1].column_name, field_name);
2586
2587 let (datatype, datatype_extension) =
2588 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
2589 .unwrap()
2590 .into_parts();
2591 let mut histogram_req = RowInsertRequest {
2592 table_name: "test_table".to_string(),
2593 rows: Some(Rows {
2594 schema: vec![
2595 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2596 api::v1::ColumnSchema {
2597 column_name: greptime_native_histogram().to_string(),
2598 datatype: datatype as i32,
2599 semantic_type: SemanticType::Field as i32,
2600 datatype_extension,
2601 options: None,
2602 },
2603 ],
2604 rows: vec![],
2605 }),
2606 };
2607 let error = inserter
2608 .get_alter_table_expr_on_demand(&mut histogram_req, &table, &ctx, false, true, true)
2609 .unwrap_err();
2610 assert!(
2611 error
2612 .to_string()
2613 .contains("cannot mix native histogram and float sample fields")
2614 );
2615
2616 let histogram_table = make_table_ref_with_schema(
2617 "ts",
2618 greptime_native_histogram(),
2619 native_histogram_value_type().clone(),
2620 );
2621 let mut sample_req = RowInsertRequest {
2622 table_name: "test_table".to_string(),
2623 rows: Some(Rows {
2624 schema: vec![
2625 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2626 field_column_schema(greptime_value(), ColumnDataType::Float64),
2627 ],
2628 rows: vec![],
2629 }),
2630 };
2631 let error = inserter
2632 .get_alter_table_expr_on_demand(
2633 &mut sample_req,
2634 &histogram_table,
2635 &ctx,
2636 false,
2637 true,
2638 true,
2639 )
2640 .unwrap_err();
2641 assert!(
2642 error
2643 .to_string()
2644 .contains("cannot mix native histogram and float sample fields")
2645 );
2646 }
2647
2648 #[test]
2649 fn test_native_histogram_detection_survives_prefix_change() {
2650 set_default_prefix(Some("custom")).unwrap();
2651 let table = make_table_ref_with_schema(
2652 "custom_timestamp",
2653 NATIVE_HISTOGRAM_FIELD,
2654 native_histogram_value_type().clone(),
2655 );
2656 let (datatype, datatype_extension) =
2657 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
2658 .unwrap()
2659 .into_parts();
2660 let request_schema = [api::v1::ColumnSchema {
2661 column_name: greptime_native_histogram().to_string(),
2662 datatype: datatype as i32,
2663 semantic_type: SemanticType::Field as i32,
2664 datatype_extension,
2665 options: None,
2666 }];
2667
2668 assert!(request_is_native_histogram(&request_schema));
2669 assert!(table_is_native_histogram(&table));
2670 }
2671
2672 #[tokio::test]
2674 async fn test_write_meter_admission() {
2675 use std::cell::Cell;
2676 use std::sync::Mutex;
2677 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2678
2679 use api::region::RegionResponse;
2680 use api::v1::region::region_request::Body;
2681 use arrow::array::{Int32Array, TimestampMillisecondArray};
2682 use arrow::record_batch::RecordBatch;
2683 use bytes::Bytes;
2684 use common_error::ext::{ErrorExt, RetryHint};
2685 use common_error::status_code::StatusCode;
2686 use common_grpc::flight::{FlightEncoder, FlightMessage};
2687 use common_meta::ddl::test_util::datanode_handler::DatanodeWatcher;
2688 use futures::future::BoxFuture;
2689 use meter_core::ItemCalculator;
2690 use meter_core::collect::{Collect, WriteRejected};
2691 use meter_core::data::MeterRecord;
2692 use meter_core::global::global_registry;
2693 use session::context::Channel;
2694
2695 const CATALOG: &str = "write_meter_test";
2696
2697 #[derive(Default)]
2698 struct Meter {
2699 reject: AtomicBool,
2700 attempts: Mutex<Vec<MeterRecord>>,
2701 accepted_value: AtomicU64,
2702 }
2703
2704 impl Collect for Meter {
2705 fn on_write(
2706 &self,
2707 record: MeterRecord,
2708 ) -> BoxFuture<'_, std::result::Result<(), WriteRejected>> {
2709 Box::pin(async move {
2710 if record.catalog != CATALOG {
2711 return Ok(());
2712 }
2713 let value = record.value;
2714 self.attempts.lock().unwrap().push(record);
2715 if self.reject.load(Ordering::Relaxed) {
2716 return Err(WriteRejected::new("database row quota exhausted"));
2717 }
2718 self.accepted_value.fetch_add(value, Ordering::Relaxed);
2719 Ok(())
2720 })
2721 }
2722
2723 fn on_read(&self, _: MeterRecord) {}
2724 }
2725
2726 impl ItemCalculator<InstantAndNormalInsertRequests> for Meter {
2727 fn calc(&self, _: &InstantAndNormalInsertRequests) -> u64 {
2728 17
2729 }
2730 }
2731
2732 let kv_backend = prepare_mocked_backend().await;
2733 let partition_manager = create_partition_rule_manager(kv_backend.clone()).await;
2734 let (sender, mut dispatched) = tokio::sync::mpsc::channel(16);
2735 let watcher = DatanodeWatcher::new(sender).with_handler(|_, request| {
2736 let rows = match request.body.unwrap() {
2737 Body::Inserts(requests) => requests
2738 .requests
2739 .iter()
2740 .filter_map(|request| request.rows.as_ref())
2741 .map(|rows| rows.rows.len())
2742 .sum(),
2743 Body::BulkInsert(_) => 2,
2745 body => panic!("unexpected request: {body:?}"),
2746 };
2747 Ok(RegionResponse::new(rows))
2748 });
2749 let flow_cache = Cache::new(100);
2750 let inserter = Inserter::new(
2751 catalog::memory::MemoryCatalogManager::new(),
2752 partition_manager,
2753 Arc::new(MockDatanodeManager::new(watcher)),
2754 Arc::new(new_table_flownode_set_cache(
2755 String::new(),
2756 flow_cache.clone(),
2757 kv_backend,
2758 )),
2759 true,
2760 );
2761 let mut table_info = new_test_table_info(1, "table_1", [1].into_iter());
2762 table_info.catalog_name = CATALOG.to_string();
2763 table_info.schema_name = "target_db".to_string();
2764 let table_info = Arc::new(table_info);
2765 let table_infos = HashMap::from_iter([(1, table_info.clone())]);
2766 let ctx = Arc::new(QueryContext::with_channel(
2767 DEFAULT_CATALOG_NAME,
2768 DEFAULT_SCHEMA_NAME,
2769 Channel::Postgres,
2770 ));
2771 let rows_request = || {
2772 let build = |num_rows| RegionInsertRequests {
2773 requests: vec![RegionInsertRequest {
2774 region_id: RegionId::new(1, 1).as_u64(),
2775 rows: Some(Rows {
2776 schema: vec![],
2777 rows: vec![api::v1::Row { values: vec![] }; num_rows],
2778 }),
2779 ..Default::default()
2780 }],
2781 };
2782 InstantAndNormalInsertRequests {
2783 normal_requests: build(3),
2784 instant_requests: build(2),
2785 }
2786 };
2787
2788 let output = inserter
2790 .do_request(rows_request(), &table_infos, &ctx)
2791 .await
2792 .unwrap();
2793 assert_eq!(output.meta.cost, 0);
2794 assert!(matches!(output.data, OutputData::AffectedRows(3)));
2795 dispatched.try_recv().unwrap();
2796
2797 let meter = Arc::new(Meter::default());
2798 global_registry().set_collector(meter.clone());
2799 global_registry().register_calculator(meter.clone());
2800 let enabled = Cell::new(false);
2802 write_meter!({
2803 enabled.set(true);
2804 MeterRecord::new("probe".into(), "probe".into(), 0, 0, 0)
2805 })
2806 .await
2807 .unwrap();
2808 let enabled = enabled.get();
2809
2810 let output = inserter
2811 .do_request(rows_request(), &table_infos, &ctx)
2812 .await
2813 .unwrap();
2814 assert_eq!(output.meta.cost, if enabled { 17 } else { 0 });
2815 assert!(matches!(output.data, OutputData::AffectedRows(3)));
2816 dispatched.try_recv().unwrap();
2817 assert!(flow_cache.contains_key(&1));
2818 flow_cache.invalidate_all();
2819 meter.reject.store(true, Ordering::Relaxed);
2820 let result = inserter
2821 .do_request(rows_request(), &table_infos, &ctx)
2822 .await;
2823 if enabled {
2824 let error = result.unwrap_err();
2825 assert_eq!(error.status_code(), StatusCode::RateLimited);
2826 assert_eq!(error.retry_hint(), RetryHint::Retryable);
2827 assert!(error.to_string().contains("database row quota exhausted"));
2828 assert!(dispatched.try_recv().is_err());
2829 assert!(
2830 !flow_cache.contains_key(&1),
2831 "rejected write reached flow mirroring"
2832 );
2833 assert_eq!(meter.accepted_value.load(Ordering::Relaxed), 17);
2834 let attempts = meter.attempts.lock().unwrap();
2835 assert_eq!(attempts.len(), 2);
2836 for record in attempts.iter() {
2837 assert_eq!(record.catalog, CATALOG);
2838 assert_eq!(record.schema, "target_db");
2839 assert_eq!(
2840 (record.rows, record.value, record.source),
2841 (5, 17, Channel::Postgres as u8)
2842 );
2843 }
2844 } else {
2845 assert_eq!(result.unwrap().meta.cost, 0);
2846 dispatched.try_recv().unwrap();
2847 assert!(meter.attempts.lock().unwrap().is_empty());
2848 }
2849 meter.attempts.lock().unwrap().clear();
2850
2851 if enabled {
2853 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2854 let rows = Rows {
2855 schema: vec![
2856 api::v1::helper::tag_column_schema("a", ColumnDataType::Int32),
2857 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2858 field_column_schema("b", ColumnDataType::Int32),
2859 ],
2860 rows: vec![api::v1::Row {
2861 values: vec![
2862 api::v1::value::ValueData::I32Value(60).into(),
2863 Value {
2864 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
2865 0,
2866 )),
2867 },
2868 api::v1::value::ValueData::I32Value(0).into(),
2869 ],
2870 }],
2871 };
2872 let error = inserter
2873 .submit_table_rows(rows, table_info.clone(), ctx.clone(), &batcher)
2874 .await
2875 .unwrap_err();
2876 assert_eq!(error.status_code(), StatusCode::RateLimited);
2877 let mut attempts = meter.attempts.lock().unwrap();
2878 assert_eq!(attempts.len(), 1);
2879 assert_eq!(attempts[0].catalog, CATALOG);
2880 assert_eq!(attempts[0].schema, "target_db");
2881 assert_eq!(attempts[0].rows, 1);
2882 attempts.clear();
2883 }
2884
2885 let table = Arc::new(table::Table::new(
2886 table_info.clone(),
2887 table::metadata::FilterPushDownType::Unsupported,
2888 Arc::new(DummyDataSource),
2889 ));
2890 let batch = RecordBatch::try_new(
2891 table_info.meta.schema.arrow_schema().clone(),
2892 vec![
2893 Arc::new(Int32Array::from(vec![60, 70])),
2894 Arc::new(TimestampMillisecondArray::from(vec![0, 1])),
2895 Arc::new(Int32Array::from(vec![0, 0])),
2896 ],
2897 )
2898 .unwrap();
2899 let bulk_insert = |batch: RecordBatch| {
2900 let flight_data = FlightEncoder::default()
2901 .encode(FlightMessage::RecordBatch(batch.clone()))
2902 .into_iter()
2903 .next()
2904 .unwrap();
2905 inserter.handle_bulk_insert(
2906 table.clone(),
2907 flight_data,
2908 batch,
2909 Bytes::new(),
2910 false,
2911 Channel::Grpc,
2912 )
2913 };
2914 assert_eq!(bulk_insert(batch.slice(0, 0)).await.unwrap(), 0);
2916 assert!(meter.attempts.lock().unwrap().is_empty());
2917 assert!(dispatched.try_recv().is_err());
2918
2919 for reject in [false, true, false] {
2921 meter.reject.store(reject, Ordering::Relaxed);
2922 let result = bulk_insert(batch.clone()).await;
2923 if enabled && reject {
2924 let error = result.unwrap_err();
2925 assert_eq!(error.status_code(), StatusCode::RateLimited);
2926 assert_eq!(error.retry_hint(), RetryHint::Retryable);
2927 assert!(dispatched.try_recv().is_err());
2928 } else {
2929 assert_eq!(result.unwrap(), 2);
2930 dispatched.try_recv().unwrap();
2931 }
2932 }
2933 {
2934 let attempts = meter.attempts.lock().unwrap();
2935 assert_eq!(attempts.len(), if enabled { 3 } else { 0 });
2936 for record in attempts.iter() {
2937 assert_eq!(record.catalog, CATALOG);
2938 assert_eq!(record.schema, "target_db");
2939 assert_eq!(
2940 (record.rows, record.value, record.source),
2941 (2, 0, Channel::Grpc as u8)
2942 );
2943 }
2944 }
2945 assert_eq!(
2946 meter.accepted_value.load(Ordering::Relaxed),
2947 if enabled { 17 } else { 0 }
2948 );
2949
2950 meter.attempts.lock().unwrap().clear();
2953 let original = Arc::new(QueryContext::with_channel(
2954 CATALOG,
2955 "a",
2956 Channel::Prometheus,
2957 ));
2958 let mut batches = ["a", "b", "a"].map(|schema| {
2959 let ctx = if schema == "a" {
2960 original.clone()
2961 } else {
2962 Arc::new(QueryContext::with_channel(
2963 CATALOG,
2964 schema,
2965 Channel::Prometheus,
2966 ))
2967 };
2968 (
2969 ctx,
2970 RowInsertRequests {
2971 inserts: vec![RowInsertRequest {
2972 table_name: "data".into(),
2973 rows: Some(Rows {
2974 schema: vec![],
2975 rows: vec![api::v1::Row::default(); 2],
2976 }),
2977 }],
2978 },
2979 )
2980 });
2981 admit_row_insert_batches(&mut batches).await.unwrap();
2982 admit_row_insert_batches(&mut batches).await.unwrap();
2983 assert_eq!(original.write_rows_to_admit(CATALOG, "a", 4), 4);
2984 for (ctx, _) in &batches {
2985 assert_eq!(
2986 ctx.write_rows_to_admit(CATALOG, &ctx.current_schema(), 2),
2987 0
2988 );
2989 assert_eq!(ctx.channel(), Channel::Prometheus);
2990 }
2991 {
2992 let attempts = meter.attempts.lock().unwrap();
2993 let totals = attempts
2994 .iter()
2995 .map(|r| (r.schema.as_str(), r.rows, r.value))
2996 .collect::<Vec<_>>();
2997 assert_eq!(
2998 totals,
2999 if enabled {
3000 vec![("a", 4, 0), ("b", 2, 0)]
3001 } else {
3002 vec![]
3003 }
3004 );
3005 }
3006 meter.attempts.lock().unwrap().clear();
3007 meter.reject.store(false, Ordering::Relaxed);
3008 let ctx = Arc::new(QueryContext::with_channel(
3009 CATALOG,
3010 "logical",
3011 Channel::Otlp,
3012 ));
3013 let admitted = admit_write(2, &ctx).await.unwrap();
3014 let mut requests = RowInsertRequests {
3015 inserts: vec![RowInsertRequest {
3016 table_name: "metric".to_string(),
3017 rows: Some(Rows {
3018 schema: vec![],
3019 rows: vec![api::v1::Row::default(); 2],
3020 }),
3021 }],
3022 };
3023 let original = requests.clone();
3024 let cost = Inserter::meter_row_inserts(&mut requests, &admitted)
3025 .await
3026 .unwrap();
3027 assert_eq!(cost, if enabled { 17 } else { 0 });
3028 assert_eq!(requests, original);
3029 let records = meter
3030 .attempts
3031 .lock()
3032 .unwrap()
3033 .iter()
3034 .map(|record| (record.rows, record.value))
3035 .collect::<Vec<_>>();
3036 assert_eq!(
3037 records,
3038 if enabled {
3039 vec![(2, 0), (0, 17)]
3040 } else {
3041 vec![]
3042 }
3043 );
3044 meter.reject.store(true, Ordering::Relaxed);
3045 let result = Inserter::meter_row_inserts(&mut requests, &admitted).await;
3046 if enabled {
3047 assert_eq!(result.unwrap_err().status_code(), StatusCode::RateLimited);
3048 } else {
3049 assert_eq!(result.unwrap(), 0);
3050 }
3051 assert_eq!(requests, original);
3052 }
3053
3054 #[test]
3055 fn test_skip_wal_does_not_change_table_options() {
3056 check_skip_wal_does_not_change_table_options(false);
3057 check_skip_wal_does_not_change_table_options(true);
3058 }
3059
3060 fn check_skip_wal_does_not_change_table_options(skip_wal: bool) {
3061 let ctx = Arc::new(QueryContext::with(
3062 DEFAULT_CATALOG_NAME,
3063 DEFAULT_SCHEMA_NAME,
3064 ));
3065 ctx.set_skip_wal(skip_wal);
3066 let mut options = Default::default();
3067 fill_table_options_for_create(&mut options, &AutoCreateTableType::Physical, &ctx);
3068 assert!(!options.contains_key(session::hints::INSERT_SKIP_WAL_HINT));
3069 assert!(!options.contains_key("skip_wal"));
3070 }
3071
3072 #[test]
3073 fn test_last_non_null_create_options_preserve_default_without_append_mode() {
3074 let ctx = Arc::new(QueryContext::with(
3075 DEFAULT_CATALOG_NAME,
3076 DEFAULT_SCHEMA_NAME,
3077 ));
3078 let mut table_options = Default::default();
3079
3080 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
3081
3082 assert_eq!(
3083 Some("last_non_null"),
3084 table_options.get(MERGE_MODE_KEY).map(String::as_str)
3085 );
3086 assert!(!table_options.contains_key(APPEND_MODE_KEY));
3087 }
3088
3089 #[test]
3090 fn test_fill_table_options_copies_semantic_extensions() {
3091 use table::requests::{
3092 SEMANTIC_METRIC_TYPE, SEMANTIC_PER_TABLE_INDEX_KEY, SEMANTIC_SIGNAL_TYPE,
3093 SEMANTIC_SOURCE, SEMANTIC_SOURCE_VERSION, SIGNAL_TYPE_METRIC, SOURCE_OPENTELEMETRY,
3094 };
3095
3096 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
3097 ctx.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_METRIC);
3098 ctx.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
3099 ctx.set_extension(SEMANTIC_SOURCE_VERSION, "2.0");
3100 ctx.set_extension(SEMANTIC_METRIC_TYPE, "bogus");
3101 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, "{}");
3103 let ctx = Arc::new(ctx);
3104 let mut table_options = Default::default();
3105
3106 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::Physical, &ctx);
3107
3108 assert_eq!(
3109 Some(SIGNAL_TYPE_METRIC),
3110 table_options.get(SEMANTIC_SIGNAL_TYPE).map(String::as_str)
3111 );
3112 assert_eq!(
3113 Some(SOURCE_OPENTELEMETRY),
3114 table_options.get(SEMANTIC_SOURCE).map(String::as_str)
3115 );
3116 assert_eq!(
3117 Some("2.0"),
3118 table_options
3119 .get(SEMANTIC_SOURCE_VERSION)
3120 .map(String::as_str)
3121 );
3122 assert!(!table_options.contains_key(SEMANTIC_METRIC_TYPE));
3123 assert!(!table_options.contains_key(SEMANTIC_PER_TABLE_INDEX_KEY));
3124 }
3125
3126 #[test]
3127 fn test_apply_per_table_semantic_options() {
3128 use table::requests::{
3129 SEMANTIC_METRIC_TYPE, SEMANTIC_METRIC_UNIT, SEMANTIC_PER_TABLE_INDEX_KEY,
3130 };
3131
3132 let index = format!(
3133 r#"{{
3134 "{DEFAULT_SCHEMA_NAME}": {{
3135 "http_requests_total": {{
3136 "greptime.semantic.metric.type": "counter",
3137 "greptime.semantic.metric.unit": "By",
3138 "greptime.semantic.metric.type_BOGUS": "x"
3139 }},
3140 "other_table": {{
3141 "greptime.semantic.metric.type": "gauge"
3142 }}
3143 }},
3144 "other_schema": {{
3145 "http_requests_total": {{
3146 "greptime.semantic.metric.type": "gauge"
3147 }}
3148 }}
3149 }}"#
3150 );
3151 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
3152 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, index);
3153 let ctx = Arc::new(ctx);
3154
3155 let index = parse_per_table_semantic_index(&ctx);
3156 assert!(index.is_some());
3157 let index = index.as_ref();
3158
3159 let mut table_options = std::collections::HashMap::new();
3160 apply_per_table_semantic_options(
3161 &mut table_options,
3162 index,
3163 DEFAULT_SCHEMA_NAME,
3164 "http_requests_total",
3165 );
3166 assert_eq!(
3168 table_options.get(SEMANTIC_METRIC_TYPE).map(String::as_str),
3169 Some("counter")
3170 );
3171 assert_eq!(
3172 table_options.get(SEMANTIC_METRIC_UNIT).map(String::as_str),
3173 Some("By")
3174 );
3175 assert!(!table_options.contains_key("greptime.semantic.metric.type_BOGUS"));
3178 assert_eq!(table_options.len(), 2);
3179
3180 let mut empty = std::collections::HashMap::new();
3181 apply_per_table_semantic_options(&mut empty, index, DEFAULT_SCHEMA_NAME, "not_in_index");
3182 assert!(empty.is_empty());
3183
3184 let mut opts = std::collections::HashMap::new();
3187 apply_per_table_semantic_options(
3188 &mut opts,
3189 index,
3190 "schema_without_entry",
3191 "http_requests_total",
3192 );
3193 assert!(opts.is_empty());
3194
3195 let bare = Arc::new(QueryContext::with(
3197 DEFAULT_CATALOG_NAME,
3198 DEFAULT_SCHEMA_NAME,
3199 ));
3200 assert!(parse_per_table_semantic_index(&bare).is_none());
3201 let mut opts = std::collections::HashMap::new();
3202 apply_per_table_semantic_options(
3203 &mut opts,
3204 None,
3205 DEFAULT_SCHEMA_NAME,
3206 "http_requests_total",
3207 );
3208 assert!(opts.is_empty());
3209 }
3210
3211 #[test]
3212 fn test_last_non_null_create_options_preserve_default_with_append_mode_false() {
3213 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
3214 ctx.set_extension(APPEND_MODE_KEY, "false");
3215 let ctx = Arc::new(ctx);
3216 let mut table_options = Default::default();
3217
3218 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
3219
3220 assert!(!table_options.contains_key(APPEND_MODE_KEY));
3221 assert_eq!(
3222 Some("last_non_null"),
3223 table_options.get(MERGE_MODE_KEY).map(String::as_str)
3224 );
3225 }
3226
3227 #[test]
3228 fn test_last_non_null_create_options_use_configured_merge_mode() {
3229 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
3230 ctx.set_extension(MERGE_MODE_KEY, "last_row");
3231 let ctx = Arc::new(ctx);
3232 let mut table_options = Default::default();
3233
3234 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
3235
3236 assert_eq!(
3237 Some("last_row"),
3238 table_options.get(MERGE_MODE_KEY).map(String::as_str)
3239 );
3240 assert!(!table_options.contains_key(APPEND_MODE_KEY));
3241 }
3242
3243 #[test]
3244 fn test_last_non_null_create_options_use_last_row_with_append_mode_true() {
3245 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
3246 ctx.set_extension(APPEND_MODE_KEY, "true");
3247 let ctx = Arc::new(ctx);
3248 let mut table_options = Default::default();
3249
3250 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
3251
3252 assert_eq!(
3253 Some("true"),
3254 table_options.get(APPEND_MODE_KEY).map(String::as_str)
3255 );
3256 assert_eq!(
3257 Some("last_row"),
3258 table_options.get(MERGE_MODE_KEY).map(String::as_str)
3259 );
3260 }
3261
3262 struct UnexpectedBatcher;
3263
3264 #[async_trait::async_trait]
3265 impl PendingRowsBatcher for UnexpectedBatcher {
3266 async fn acquire(&self) -> Result<Arc<tokio::sync::OwnedSemaphorePermit>> {
3267 panic!("empty writes must not acquire batch admission")
3268 }
3269
3270 async fn submit(
3271 &self,
3272 _table_info: TableInfoRef,
3273 _batch: arrow::record_batch::RecordBatch,
3274 _ctx: QueryContextRef,
3275 _permit: Arc<tokio::sync::OwnedSemaphorePermit>,
3276 ) -> Result<Output> {
3277 panic!("empty writes must not submit a batch")
3278 }
3279 }
3280
3281 async fn batcher_test_inserter() -> Inserter {
3282 let kv_backend = prepare_mocked_backend().await;
3283 Inserter::new(
3284 catalog::memory::MemoryCatalogManager::new(),
3285 create_partition_rule_manager(kv_backend.clone()).await,
3286 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
3287 Arc::new(new_table_flownode_set_cache(
3288 String::new(),
3289 Cache::new(100),
3290 kv_backend,
3291 )),
3292 true,
3293 )
3294 }
3295
3296 #[tokio::test]
3297 async fn test_logical_batcher_eligibility() {
3298 use catalog::RegisterTableRequest;
3299 use catalog::memory::MemoryCatalogManager;
3300 use common_meta::instruction::{CacheIdent, CreateFlow};
3301 use common_meta::kv_backend::KvBackendRef;
3302 use common_meta::kv_backend::memory::MemoryKvBackend;
3303 use datatypes::schema::{ColumnDefaultConstraint, SchemaBuilder};
3304 let requests = RowInsertRequests {
3305 inserts: vec![RowInsertRequest {
3306 table_name: "test_table".to_string(),
3307 rows: None,
3308 }],
3309 };
3310 let original =
3311 make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
3312 .table_info();
3313 for case in [
3314 "eligible",
3315 "physical",
3316 "ordinary",
3317 "instant",
3318 "disabled",
3319 "hint",
3320 "flow",
3321 "required_tag",
3322 "default",
3323 ] {
3324 let mut info = (*original).clone();
3325 info.meta.engine = METRIC_ENGINE_NAME.to_string();
3326 info.meta.options.extra_options.insert(
3327 LOGICAL_TABLE_METADATA_KEY.to_string(),
3328 "physical".to_string(),
3329 );
3330 let mut ctx = QueryContext::arc().fork();
3331 match case {
3332 "physical" => {
3333 info.meta
3334 .options
3335 .extra_options
3336 .insert(LOGICAL_TABLE_METADATA_KEY.to_string(), "other".to_string());
3337 }
3338 "ordinary" => info.meta.engine = "mito".to_string(),
3339 "instant" => info.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant),
3340 "hint" => ctx.set_extension(AUTO_CREATE_TABLE_KEY, "false"),
3341 "required_tag" | "default" => {
3342 let mut columns = info.meta.schema.column_schemas().to_vec();
3343 let mut tag = ColumnSchema::new(
3344 "tag",
3345 ConcreteDataType::string_datatype(),
3346 case != "required_tag",
3347 );
3348 if case == "default" {
3349 tag = tag
3350 .with_default_constraint(Some(ColumnDefaultConstraint::null_value()))
3351 .unwrap();
3352 }
3353 columns.push(tag);
3354 info.meta.schema = Arc::new(
3355 SchemaBuilder::try_from_columns(columns)
3356 .unwrap()
3357 .build()
3358 .unwrap(),
3359 );
3360 info.meta.primary_key_indices = vec![2];
3361 }
3362 _ => {}
3363 }
3364 let catalog = MemoryCatalogManager::with_default_setup();
3365 let table = Arc::new(table::Table::new(
3366 Arc::new(info),
3367 table::metadata::FilterPushDownType::Unsupported,
3368 Arc::new(DummyDataSource),
3369 ));
3370 catalog
3371 .register_table_sync(RegisterTableRequest {
3372 catalog: DEFAULT_CATALOG_NAME.to_string(),
3373 schema: DEFAULT_SCHEMA_NAME.to_string(),
3374 table_name: "test_table".to_string(),
3375 table_id: 1,
3376 table,
3377 })
3378 .unwrap();
3379 let mut inserter = batcher_test_inserter().await;
3380 inserter.catalog_manager = catalog;
3381 inserter.auto_create_table = case != "disabled";
3382 let kv_backend: KvBackendRef = Arc::new(MemoryKvBackend::default());
3383 inserter.table_flownode_set_cache = Arc::new(new_table_flownode_set_cache(
3384 String::new(),
3385 Cache::new(10),
3386 kv_backend,
3387 ));
3388 if case == "flow" {
3389 inserter
3390 .table_flownode_set_cache
3391 .invalidate(&[CacheIdent::CreateFlow(CreateFlow {
3392 flow_id: 1,
3393 source_table_ids: vec![1],
3394 partition_to_peer_mapping: vec![(0, Peer::empty(1))],
3395 })])
3396 .await
3397 .unwrap();
3398 }
3399 assert_eq!(
3400 inserter
3401 .can_batch_metric_rows(&requests, &Arc::new(ctx), "physical")
3402 .await
3403 .unwrap(),
3404 case == "eligible",
3405 "{case}"
3406 );
3407 }
3408 }
3409
3410 #[tokio::test]
3411 async fn test_batcher_meter_preserves_request() {
3412 let mut requests = RowInsertRequests {
3413 inserts: vec![RowInsertRequest {
3414 table_name: "sample".to_string(),
3415 rows: Some(Rows {
3416 schema: vec![],
3417 rows: vec![api::v1::Row {
3418 values: vec![Value {
3419 value_data: Some(api::v1::value::ValueData::F64Value(1.5)),
3420 }],
3421 }],
3422 }),
3423 }],
3424 };
3425 let expected = requests.clone();
3426 Inserter::meter_row_inserts(&mut requests, &QueryContext::arc())
3427 .await
3428 .unwrap();
3429 assert_eq!(requests, expected);
3430 }
3431
3432 #[tokio::test]
3433 async fn test_instant_table_bypasses_batcher() {
3434 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
3435 let inserter = batcher_test_inserter()
3436 .await
3437 .with_pending_rows_batcher(Some(batcher));
3438 let mut ctx = session::context::QueryContextBuilder::default().build();
3439 ctx.set_batching_enabled(true);
3440 let ctx = Arc::new(ctx);
3441 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
3442 .table_info();
3443 assert!(inserter.table_batcher(&table, &ctx).is_some());
3444 let mut instant = (*table).clone();
3445 instant.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant);
3446 assert!(inserter.table_batcher(&Arc::new(instant), &ctx).is_none());
3447 }
3448
3449 #[tokio::test]
3450 async fn test_empty_prepared_rows_skip_batcher() {
3451 let inserter = batcher_test_inserter().await;
3452 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
3453 .table_info();
3454 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
3455 let ctx = QueryContext::arc();
3456 let output = inserter
3457 .submit_table_rows(
3458 Rows {
3459 schema: vec![],
3460 rows: vec![],
3461 },
3462 table.clone(),
3463 ctx.clone(),
3464 &batcher,
3465 )
3466 .await
3467 .unwrap();
3468 assert!(matches!(output.data, OutputData::AffectedRows(0)));
3469 let output = inserter
3470 .submit_pending_rows(
3471 RowInsertRequests {
3472 inserts: vec![RowInsertRequest {
3473 table_name: table.name.clone(),
3474 rows: None,
3475 }],
3476 },
3477 HashMap::from_iter([(table.table_id(), table)]),
3478 ctx,
3479 &batcher,
3480 )
3481 .await
3482 .unwrap();
3483 assert!(matches!(output.data, OutputData::AffectedRows(0)));
3484 }
3485
3486 #[derive(Clone)]
3490 struct GatedFlownodeHandler {
3491 gate: Arc<tokio::sync::Semaphore>,
3492 affected_rows: u64,
3493 }
3494
3495 #[async_trait::async_trait]
3496 impl MockFlownodeHandler for GatedFlownodeHandler {
3497 async fn handle_inserts(
3498 &self,
3499 _peer: &Peer,
3500 _requests: api::v1::region::InsertRequests,
3501 ) -> common_meta::error::Result<FlowResponse> {
3502 let _permit = self.gate.acquire().await.unwrap();
3503 Ok(FlowResponse {
3504 affected_rows: self.affected_rows,
3505 ..Default::default()
3506 })
3507 }
3508 }
3509
3510 fn flownode_peer() -> Peer {
3511 Peer {
3512 id: 1,
3513 addr: "127.0.0.1:4001".to_string(),
3514 }
3515 }
3516
3517 #[derive(Clone)]
3518 struct RecordingNodeManager {
3519 datanode_dispatch: tokio::sync::mpsc::UnboundedSender<usize>,
3520 flownode_dispatch: tokio::sync::mpsc::UnboundedSender<Peer>,
3521 gates: Arc<std::sync::Mutex<HashMap<u64, Arc<tokio::sync::Semaphore>>>>,
3522 fail_flownode: bool,
3523 }
3524
3525 #[async_trait::async_trait]
3526 impl common_meta::node_manager::DatanodeManager for RecordingNodeManager {
3527 async fn datanode(&self, peer: &Peer) -> common_meta::node_manager::DatanodeRef {
3528 Arc::new(RecordingNode {
3529 peer: peer.clone(),
3530 manager: self.clone(),
3531 })
3532 }
3533 }
3534
3535 #[async_trait::async_trait]
3536 impl common_meta::node_manager::FlownodeManager for RecordingNodeManager {
3537 async fn flownode(&self, peer: &Peer) -> common_meta::node_manager::FlownodeRef {
3538 Arc::new(RecordingNode {
3539 peer: peer.clone(),
3540 manager: self.clone(),
3541 })
3542 }
3543 }
3544
3545 struct RecordingNode {
3546 peer: Peer,
3547 manager: RecordingNodeManager,
3548 }
3549
3550 #[async_trait::async_trait]
3551 impl common_meta::node_manager::Datanode for RecordingNode {
3552 async fn handle(
3553 &self,
3554 _request: api::v1::region::RegionRequest,
3555 ) -> common_meta::error::Result<api::region::RegionResponse> {
3556 let rows = match _request.body.as_ref() {
3557 Some(api::v1::region::region_request::Body::Inserts(requests)) => requests
3558 .requests
3559 .iter()
3560 .filter_map(|request| request.rows.as_ref())
3561 .map(|rows| rows.rows.len())
3562 .sum(),
3563 _ => 0,
3564 };
3565 let _ = self.manager.datanode_dispatch.send(rows);
3566 Ok(api::region::RegionResponse::new(rows))
3567 }
3568
3569 async fn handle_query(
3570 &self,
3571 _request: common_query::request::QueryRequest,
3572 ) -> common_meta::error::Result<common_recordbatch::SendableRecordBatchStream> {
3573 unreachable!()
3574 }
3575 }
3576
3577 #[async_trait::async_trait]
3578 impl common_meta::node_manager::Flownode for RecordingNode {
3579 async fn handle(
3580 &self,
3581 _request: api::v1::flow::FlowRequest,
3582 ) -> common_meta::error::Result<FlowResponse> {
3583 unreachable!()
3584 }
3585
3586 async fn handle_inserts(
3587 &self,
3588 _request: api::v1::region::InsertRequests,
3589 ) -> common_meta::error::Result<FlowResponse> {
3590 let _ = self.manager.flownode_dispatch.send(self.peer.clone());
3591 if self.manager.fail_flownode {
3592 return Err(common_meta::error::UnexpectedSnafu {
3593 err_msg: "test flownode failure".to_string(),
3594 }
3595 .build());
3596 }
3597 let gate = self
3598 .manager
3599 .gates
3600 .lock()
3601 .unwrap()
3602 .get(&self.peer.id)
3603 .cloned();
3604 if let Some(gate) = gate {
3605 let _permit = gate.acquire().await.unwrap();
3606 }
3607 Ok(FlowResponse::default())
3608 }
3609
3610 async fn handle_mark_window_dirty(
3611 &self,
3612 _request: api::v1::flow::DirtyWindowRequests,
3613 ) -> common_meta::error::Result<FlowResponse> {
3614 unreachable!()
3615 }
3616 }
3617
3618 fn mirror_requests(peer: &Peer, num_rows: usize) -> HashMap<Peer, RegionInsertRequests> {
3619 HashMap::from_iter([(
3620 peer.clone(),
3621 RegionInsertRequests {
3622 requests: vec![RegionInsertRequest {
3623 region_id: RegionId::new(1, 1).as_u64(),
3624 rows: Some(Rows {
3625 schema: vec![],
3626 rows: vec![api::v1::Row { values: vec![] }; num_rows],
3627 }),
3628 ..Default::default()
3629 }],
3630 },
3631 )])
3632 }
3633
3634 static MIRROR_METRIC_TEST_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
3639
3640 async fn lock_mirror_metrics() -> tokio::sync::MutexGuard<'static, ()> {
3641 MIRROR_METRIC_TEST_LOCK.lock().await
3642 }
3643
3644 #[tokio::test]
3648 async fn flow_mirror_saturation_preserves_instant_and_normal_contracts() {
3649 use common_error::ext::{ErrorExt, RetryHint};
3650 use common_error::status_code::StatusCode;
3651
3652 let _guard = lock_mirror_metrics().await;
3653 let kv_backend = prepare_mocked_backend().await;
3654 let partition_manager = create_partition_rule_manager(kv_backend.clone()).await;
3655 let (datanode_tx, mut datanode_rx) = tokio::sync::mpsc::unbounded_channel();
3656 let (flownode_tx, mut flownode_rx) = tokio::sync::mpsc::unbounded_channel();
3657 let node_manager = Arc::new(RecordingNodeManager {
3658 datanode_dispatch: datanode_tx,
3659 flownode_dispatch: flownode_tx,
3660 gates: Arc::new(std::sync::Mutex::new(HashMap::new())),
3661 fail_flownode: false,
3662 });
3663 let flow_cache = Cache::new(10);
3664 let flow_cache_backend = prepare_mocked_backend().await;
3665 let inserter = Inserter::new(
3666 catalog::memory::MemoryCatalogManager::new(),
3667 partition_manager,
3668 node_manager,
3669 Arc::new(new_table_flownode_set_cache(
3670 String::new(),
3671 flow_cache.clone(),
3672 flow_cache_backend,
3673 )),
3674 true,
3675 );
3676 let peer = flownode_peer();
3677 inserter
3678 .table_flownode_set_cache
3679 .invalidate(&[common_meta::instruction::CacheIdent::CreateFlow(
3680 common_meta::instruction::CreateFlow {
3681 flow_id: 1,
3682 source_table_ids: vec![1, 2],
3683 partition_to_peer_mapping: vec![(0, peer)],
3684 },
3685 )])
3686 .await
3687 .unwrap();
3688 let mut normal_info = new_test_table_info(1, "normal_table", [1].into_iter());
3689 normal_info.catalog_name = DEFAULT_CATALOG_NAME.to_string();
3690 normal_info.schema_name = DEFAULT_SCHEMA_NAME.to_string();
3691 let mut instant_info = new_test_table_info(2, "instant_table", [1].into_iter());
3692 instant_info.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant);
3693 let table_infos =
3694 HashMap::from_iter([(1, Arc::new(normal_info)), (2, Arc::new(instant_info))]);
3695 let ctx = Arc::new(QueryContext::with(
3696 DEFAULT_CATALOG_NAME,
3697 DEFAULT_SCHEMA_NAME,
3698 ));
3699 let insert = |table_id, num_rows| RegionInsertRequests {
3700 requests: vec![RegionInsertRequest {
3701 region_id: RegionId::new(table_id, 1).as_u64(),
3702 rows: Some(Rows {
3703 schema: vec![],
3704 rows: vec![api::v1::Row::default(); num_rows],
3705 }),
3706 ..Default::default()
3707 }],
3708 };
3709 let make_request = |normal, instant| InstantAndNormalInsertRequests {
3710 normal_requests: if normal == 0 {
3711 RegionInsertRequests::default()
3712 } else {
3713 insert(1, normal)
3714 },
3715 instant_requests: if instant == 0 {
3716 RegionInsertRequests::default()
3717 } else {
3718 insert(2, instant)
3719 },
3720 };
3721
3722 let pending = inserter.mirror_pending_rows.clone();
3723 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
3724 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(MAX_MIRROR_PENDING_ROWS as i64);
3725 pending.store(MAX_MIRROR_PENDING_ROWS as u64, Ordering::Relaxed);
3726 let dropped_before = crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get();
3727 for request in [make_request(0, 1), make_request(1, 1)] {
3728 let error = inserter
3729 .do_request(request, &table_infos, &ctx)
3730 .await
3731 .unwrap_err();
3732 assert_eq!(error.status_code(), StatusCode::RateLimited);
3733 assert_eq!(error.retry_hint(), RetryHint::Retryable);
3734 assert!(datanode_rx.try_recv().is_err());
3735 assert!(flownode_rx.try_recv().is_err());
3736 assert_eq!(
3737 pending.load(Ordering::Relaxed),
3738 MAX_MIRROR_PENDING_ROWS as u64
3739 );
3740 }
3741 assert_eq!(
3742 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
3743 dropped_before
3744 );
3745
3746 for pending_before in [0, MAX_MIRROR_PENDING_ROWS as u64] {
3747 if pending_before == 0 {
3748 pending.store(0, Ordering::Relaxed);
3749 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(MAX_MIRROR_PENDING_ROWS as i64);
3750 }
3751 let gauge_before_rejection = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
3752 for (normal, instant) in [
3753 (0, MAX_MIRROR_PENDING_ROWS + 1),
3754 (MAX_MIRROR_PENDING_ROWS / 2, MAX_MIRROR_PENDING_ROWS / 2 + 1),
3755 ] {
3756 let error = inserter
3757 .do_request(make_request(normal, instant), &table_infos, &ctx)
3758 .await
3759 .unwrap_err();
3760 assert_eq!(error.status_code(), StatusCode::InvalidArguments);
3761 assert_eq!(error.retry_hint(), RetryHint::NonRetryable);
3762 let message = error.to_string();
3763 assert!(message.contains("1000001"), "{message}");
3764 assert!(message.contains("1000000"), "{message}");
3765 assert!(message.contains("reduce the batch size"), "{message}");
3766 assert!(datanode_rx.try_recv().is_err());
3767 assert!(flownode_rx.try_recv().is_err());
3768 assert_eq!(pending.load(Ordering::Relaxed), pending_before);
3769 assert_eq!(
3770 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get(),
3771 gauge_before_rejection
3772 );
3773 assert_eq!(
3774 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
3775 dropped_before
3776 );
3777 }
3778 if pending_before == 0 {
3779 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(MAX_MIRROR_PENDING_ROWS as i64);
3780 pending.store(MAX_MIRROR_PENDING_ROWS as u64, Ordering::Relaxed);
3781 }
3782 }
3783
3784 let result = inserter
3785 .do_request(make_request(1, 0), &table_infos, &ctx)
3786 .await
3787 .unwrap();
3788 assert!(matches!(result.data, OutputData::AffectedRows(1)));
3789 assert_eq!(1, datanode_rx.try_recv().unwrap());
3790 assert!(flownode_rx.try_recv().is_err());
3791 assert_eq!(
3792 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
3793 dropped_before + 1
3794 );
3795 pending.store(0, Ordering::Relaxed);
3796 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(MAX_MIRROR_PENDING_ROWS as i64);
3797 assert_eq!(
3798 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get(),
3799 gauge_before
3800 );
3801 }
3802
3803 #[tokio::test]
3804 async fn flow_mirror_new_counts_regions_and_peer_clones() {
3805 use common_meta::instruction::{CacheIdent, CreateFlow};
3806
3807 let peer1 = flownode_peer();
3808 let peer2 = Peer {
3809 id: 2,
3810 addr: "127.0.0.1:4002".to_string(),
3811 };
3812 let cache_backend = prepare_mocked_backend().await;
3813 let cache = Arc::new(new_table_flownode_set_cache(
3814 String::new(),
3815 Cache::new(10),
3816 cache_backend,
3817 ));
3818 cache
3819 .invalidate(&[CacheIdent::CreateFlow(CreateFlow {
3820 flow_id: 1,
3821 source_table_ids: vec![1],
3822 partition_to_peer_mapping: vec![(0, peer1), (1, peer2)],
3823 })])
3824 .await
3825 .unwrap();
3826 let request = |region_id| RegionInsertRequest {
3827 region_id,
3828 rows: Some(Rows {
3829 schema: vec![],
3830 rows: vec![api::v1::Row { values: vec![] }; 3],
3831 }),
3832 ..Default::default()
3833 };
3834 let requests = [
3835 request(RegionId::new(1, 0).as_u64()),
3836 request(RegionId::new(1, 1).as_u64()),
3837 ];
3838 let task = FlowMirrorTask::new(&cache, requests.iter()).await.unwrap();
3839 assert_eq!(12, task.pending_rows());
3840 assert_eq!(2, task.requests.len());
3841 }
3842
3843 #[tokio::test]
3844 async fn flow_mirror_dropped_when_pending_exceeds_limit() {
3845 let _guard = lock_mirror_metrics().await;
3846 let pending = Arc::new(AtomicU64::new(MAX_MIRROR_PENDING_ROWS as u64));
3847 let dropped_before = crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get();
3848 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
3849 let num_rows = 100;
3850
3851 let task = FlowMirrorTask {
3852 requests: mirror_requests(&flownode_peer(), num_rows),
3853 };
3854 task.detach(
3856 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
3857 pending.clone(),
3858 false,
3859 )
3860 .unwrap();
3861
3862 assert_eq!(
3863 num_rows as u64,
3864 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get() - dropped_before
3865 );
3866 assert_eq!(
3868 MAX_MIRROR_PENDING_ROWS as u64,
3869 pending.load(Ordering::Relaxed)
3870 );
3871 assert_eq!(
3874 gauge_before,
3875 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get(),
3876 "a dropped batch must not change the pending gauge"
3877 );
3878
3879 let task = FlowMirrorTask {
3880 requests: mirror_requests(&flownode_peer(), num_rows),
3881 };
3882 let error = task
3883 .detach(
3884 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
3885 pending.clone(),
3886 true,
3887 )
3888 .err()
3889 .unwrap();
3890 use common_error::ext::{ErrorExt, RetryHint};
3891 use common_error::status_code::StatusCode;
3892 assert_eq!(error.status_code(), StatusCode::RateLimited);
3893 assert_eq!(error.retry_hint(), RetryHint::Retryable);
3894 assert_eq!(
3895 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
3896 dropped_before + num_rows as u64,
3897 "rejected instant rows are not successful best-effort drops"
3898 );
3899 assert_eq!(
3900 MAX_MIRROR_PENDING_ROWS as u64,
3901 pending.load(Ordering::Relaxed)
3902 );
3903 assert_eq!(
3904 gauge_before,
3905 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get()
3906 );
3907 }
3908
3909 #[tokio::test]
3913 async fn flow_mirror_reserves_and_releases_per_peer_shares() {
3914 let _guard = lock_mirror_metrics().await;
3915 let per_peer_rows = 10;
3916 let total_rows = (2 * per_peer_rows) as u64;
3917 let start = MAX_MIRROR_PENDING_ROWS as u64 - total_rows;
3919 let pending = Arc::new(AtomicU64::new(start));
3920 let gate = Arc::new(tokio::sync::Semaphore::new(0));
3921 let node_manager = Arc::new(MockFlownodeManager::new(GatedFlownodeHandler {
3922 gate: gate.clone(),
3923 affected_rows: per_peer_rows as u64,
3924 }));
3925
3926 let mut requests = mirror_requests(&flownode_peer(), per_peer_rows);
3927 requests.extend(mirror_requests(
3928 &Peer {
3929 id: 2,
3930 addr: "127.0.0.1:4002".to_string(),
3931 },
3932 per_peer_rows,
3933 ));
3934
3935 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
3936 FlowMirrorTask { requests }
3937 .detach(node_manager, pending.clone(), false)
3938 .unwrap();
3939
3940 assert_eq!(
3943 MAX_MIRROR_PENDING_ROWS as u64,
3944 pending.load(Ordering::Relaxed)
3945 );
3946
3947 gate.add_permits(2);
3953 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
3954 while pending.load(Ordering::Relaxed) != start
3955 || crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get() != gauge_before
3956 {
3957 assert!(
3958 std::time::Instant::now() < deadline,
3959 "the mirror tasks must release exactly their own shares"
3960 );
3961 std::thread::sleep(std::time::Duration::from_millis(1));
3962 }
3963 }
3964
3965 #[tokio::test]
3966 async fn flow_mirror_fast_peer_releases_only_its_share_before_drop() {
3967 let _guard = lock_mirror_metrics().await;
3968 let peer1 = flownode_peer();
3969 let peer2 = Peer {
3970 id: 2,
3971 addr: "127.0.0.1:4002".to_string(),
3972 };
3973 let gate1 = Arc::new(tokio::sync::Semaphore::new(0));
3974 let gate2 = Arc::new(tokio::sync::Semaphore::new(0));
3975 let gates = Arc::new(std::sync::Mutex::new(HashMap::from_iter([
3976 (peer1.id, gate1.clone()),
3977 (peer2.id, gate2.clone()),
3978 ])));
3979 let (datanode_tx, _datanode_rx) = tokio::sync::mpsc::unbounded_channel();
3980 let (flownode_tx, mut flownode_rx) = tokio::sync::mpsc::unbounded_channel();
3981 let node_manager = Arc::new(RecordingNodeManager {
3982 datanode_dispatch: datanode_tx,
3983 flownode_dispatch: flownode_tx,
3984 gates,
3985 fail_flownode: false,
3986 });
3987 let pending = Arc::new(AtomicU64::new(MAX_MIRROR_PENDING_ROWS as u64 - 20));
3988 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
3989 let mut requests = mirror_requests(&peer1, 10);
3990 requests.extend(mirror_requests(&peer2, 10));
3991 let task = FlowMirrorTask { requests };
3992 task.detach(node_manager.clone(), pending.clone(), false)
3993 .unwrap();
3994 let first = tokio::time::timeout(Duration::from_secs(10), flownode_rx.recv())
3995 .await
3996 .unwrap()
3997 .unwrap();
3998 let second = tokio::time::timeout(Duration::from_secs(10), flownode_rx.recv())
3999 .await
4000 .unwrap()
4001 .unwrap();
4002 assert_ne!(first.id, second.id);
4003 let fast_gate = if first.id == peer1.id {
4004 gate1.clone()
4005 } else {
4006 gate2.clone()
4007 };
4008 let slow_gate = if first.id == peer1.id {
4009 gate2.clone()
4010 } else {
4011 gate1.clone()
4012 };
4013 fast_gate.add_permits(1);
4014 let deadline = std::time::Instant::now() + Duration::from_secs(10);
4015 while pending.load(Ordering::Relaxed) != MAX_MIRROR_PENDING_ROWS as u64 - 10 {
4016 assert!(
4017 std::time::Instant::now() < deadline,
4018 "fast peer did not release"
4019 );
4020 std::thread::sleep(Duration::from_millis(1));
4021 }
4022 let dropped_before = crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get();
4023 FlowMirrorTask {
4024 requests: mirror_requests(&peer1, 11),
4025 }
4026 .detach(node_manager, pending.clone(), false)
4027 .unwrap();
4028 assert_eq!(
4029 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
4030 dropped_before + 11
4031 );
4032 assert!(flownode_rx.try_recv().is_err());
4033 slow_gate.add_permits(1);
4034 while pending.load(Ordering::Relaxed) != MAX_MIRROR_PENDING_ROWS as u64 - 20
4035 || crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get() != gauge_before
4036 {
4037 assert!(
4038 std::time::Instant::now() < deadline,
4039 "slow peer did not release"
4040 );
4041 std::thread::sleep(Duration::from_millis(1));
4042 }
4043 }
4044
4045 #[tokio::test]
4046 async fn flow_mirror_failure_releases_budget_and_gauge() {
4047 let _guard = lock_mirror_metrics().await;
4048 let (datanode_tx, _datanode_rx) = tokio::sync::mpsc::unbounded_channel();
4049 let (flownode_tx, mut flownode_rx) = tokio::sync::mpsc::unbounded_channel();
4050 let node_manager = Arc::new(RecordingNodeManager {
4051 datanode_dispatch: datanode_tx,
4052 flownode_dispatch: flownode_tx,
4053 gates: Arc::new(std::sync::Mutex::new(HashMap::new())),
4054 fail_flownode: true,
4055 });
4056 let pending = Arc::new(AtomicU64::new(0));
4057 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
4058 FlowMirrorTask {
4059 requests: mirror_requests(&flownode_peer(), 5),
4060 }
4061 .detach(node_manager, pending.clone(), false)
4062 .unwrap();
4063 assert_eq!(
4064 tokio::time::timeout(Duration::from_secs(10), flownode_rx.recv())
4065 .await
4066 .unwrap()
4067 .unwrap()
4068 .id,
4069 flownode_peer().id
4070 );
4071 let deadline = std::time::Instant::now() + Duration::from_secs(10);
4072 while pending.load(Ordering::Relaxed) != 0
4073 || crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get() != gauge_before
4074 {
4075 assert!(
4076 std::time::Instant::now() < deadline,
4077 "failed task leaked reservation"
4078 );
4079 std::thread::sleep(Duration::from_millis(1));
4080 }
4081 }
4082
4083 #[tokio::test]
4084 async fn flow_mirror_pending_counter_consistent() {
4085 let _guard = lock_mirror_metrics().await;
4086 let pending = Arc::new(AtomicU64::new(0));
4087 let gate = Arc::new(tokio::sync::Semaphore::new(0));
4088 let node_manager = Arc::new(MockFlownodeManager::new(GatedFlownodeHandler {
4089 gate: gate.clone(),
4090 affected_rows: 7,
4091 }));
4092
4093 let gauge_before = crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get();
4095 FlowMirrorTask {
4096 requests: HashMap::new(),
4097 }
4098 .detach(node_manager.clone(), pending.clone(), false)
4099 .unwrap();
4100 assert_eq!(0, pending.load(Ordering::Relaxed));
4101 assert_eq!(
4102 gauge_before,
4103 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get(),
4104 "an empty batch must not touch the pending gauge"
4105 );
4106
4107 let dropped_before = crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get();
4108 let num_rows = 7;
4109
4110 FlowMirrorTask {
4111 requests: mirror_requests(&flownode_peer(), num_rows),
4112 }
4113 .detach(node_manager, pending.clone(), false)
4114 .unwrap();
4115
4116 assert_eq!(num_rows as u64, pending.load(Ordering::Relaxed));
4119 assert_eq!(
4120 num_rows as i64,
4121 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get() - gauge_before,
4122 "the pending gauge must track the pending rows"
4123 );
4124 assert_eq!(
4125 dropped_before,
4126 crate::metrics::DIST_MIRROR_DROPPED_ROW_COUNT.get(),
4127 "a batch within the limit must not be counted as dropped"
4128 );
4129
4130 gate.add_permits(1);
4133 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
4134 while pending.load(Ordering::Relaxed) != 0
4135 || crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.get() != gauge_before
4136 {
4137 assert!(
4138 std::time::Instant::now() < deadline,
4139 "the mirror task did not release its pending reservation"
4140 );
4141 std::thread::sleep(std::time::Duration::from_millis(1));
4142 }
4143 }
4144
4145 #[test]
4146 fn mirror_logs_are_rate_limited() {
4147 let log = Arc::new(MirrorLog::new());
4148 assert_eq!(Some(1), log.claim_report(1_000));
4149 assert_eq!(None, log.claim_report(1_500));
4150 assert_eq!(None, log.claim_report(10_999));
4151 assert_eq!(Some(3), log.claim_report(11_000));
4152 assert_eq!(None, log.claim_report(11_001));
4153 assert_eq!(Some(2), log.claim_report(21_000));
4154
4155 let concurrent = Arc::new(MirrorLog::new());
4156 assert_eq!(Some(1), concurrent.claim_report(50_000));
4157 let barrier = Arc::new(std::sync::Barrier::new(8));
4158 let threads = (0..8)
4159 .map(|_| {
4160 let log = concurrent.clone();
4161 let barrier = barrier.clone();
4162 std::thread::spawn(move || {
4163 barrier.wait();
4164 log.claim_report(60_000)
4165 })
4166 })
4167 .collect::<Vec<_>>();
4168 let winners = threads
4169 .into_iter()
4170 .filter_map(|thread| thread.join().unwrap())
4171 .collect::<Vec<_>>();
4172 assert_eq!(winners.len(), 1);
4173 let residual = concurrent.events.swap(0, Ordering::Relaxed);
4174 assert_eq!(winners[0] + residual, 8);
4175 }
4176}