1use std::cmp::Ordering;
18use std::collections::HashMap;
19use std::collections::hash_map::DefaultHasher;
20use std::hash::{Hash, Hasher};
21use std::sync::Arc;
22
23use api::prom_store::remote::label_matcher::Type as MatcherType;
24use api::prom_store::remote::{Label, LabelMatcher, Query, Sample, TimeSeries, WriteRequest};
25use api::v1::RowInsertRequests;
26use arrow::array::{
27 Array, ArrayRef, AsArray, DictionaryArray, LargeStringArray, StringArray, StringViewArray,
28};
29use arrow::compute::kernels::cast as casts;
30use arrow::datatypes::{
31 DataType, Float64Type, TimeUnit, TimestampMicrosecondType, TimestampMillisecondType,
32 TimestampNanosecondType, UInt32Type,
33};
34use common_grpc::precision::Precision;
35use common_memory_manager::MemoryGuard;
36use common_query::prelude::{greptime_timestamp, greptime_value};
37use common_recordbatch::{RecordBatch, RecordBatches};
38use common_telemetry::{tracing, warn};
39use datafusion::dataframe::DataFrame;
40use datafusion::prelude::{Expr, ident, lit, regexp_match};
41use datafusion_common::ScalarValue;
42use datafusion_expr::LogicalPlan;
43use snafu::{OptionExt, ResultExt, ensure};
44use snap::raw::{Decoder, Encoder};
45
46use crate::error::{self, Result};
47use crate::prom_remote_write::REMOTE_WRITE_V1_VERSION;
48use crate::request_memory_limiter::ServerMemoryLimiter;
49use crate::request_memory_metrics::RequestMemoryMetrics;
50use crate::row_writer::{self, MultiTableData};
51
52pub const METRIC_NAME_LABEL: &str = "__name__";
53pub const METRIC_NAME_LABEL_BYTES: &[u8] = b"__name__";
54
55pub const DATABASE_LABEL: &str = "x_greptime_database";
57pub const DATABASE_LABEL_BYTES: &[u8] = b"x_greptime_database";
58pub const DATABASE_LABEL_ALT: &str = "__database__";
59pub const DATABASE_LABEL_ALT_BYTES: &[u8] = b"__database__";
60
61#[deprecated(note = "use DATABASE_LABEL instead")]
63pub const SCHEMA_LABEL: &str = "__schema__";
64#[deprecated(note = "use DATABASE_LABEL_BYTES instead")]
65pub const SCHEMA_LABEL_BYTES: &[u8] = b"__schema__";
66
67pub const PHYSICAL_TABLE_LABEL: &str = "x_greptime_physical_table";
69pub const PHYSICAL_TABLE_LABEL_BYTES: &[u8] = b"x_greptime_physical_table";
70pub const PHYSICAL_TABLE_LABEL_ALT: &str = "__physical_table__";
71pub const PHYSICAL_TABLE_LABEL_ALT_BYTES: &[u8] = b"__physical_table__";
72
73pub const FIELD_NAME_LABEL: &str = "__field__";
75
76#[allow(deprecated)]
78pub fn is_remote_write_special_label(label: &str) -> bool {
79 label == DATABASE_LABEL
80 || label == DATABASE_LABEL_ALT
81 || label == PHYSICAL_TABLE_LABEL
82 || label == PHYSICAL_TABLE_LABEL_ALT
83 || label == SCHEMA_LABEL
84}
85
86#[allow(deprecated)]
87pub fn is_remote_read_special_label(label: &str) -> bool {
88 label == METRIC_NAME_LABEL
89 || label == DATABASE_LABEL
90 || label == DATABASE_LABEL_ALT
91 || label == SCHEMA_LABEL
92}
93
94#[allow(deprecated)]
96pub fn is_database_selection_label(label: &str) -> bool {
97 label == DATABASE_LABEL || label == DATABASE_LABEL_ALT || label == SCHEMA_LABEL
98}
99
100pub fn is_physical_table_selection_label(label: &str) -> bool {
102 label == PHYSICAL_TABLE_LABEL || label == PHYSICAL_TABLE_LABEL_ALT
103}
104
105pub fn table_name(q: &Query) -> Result<String> {
107 let mut matchers = q
108 .matchers
109 .iter()
110 .filter(|matcher| matcher.name == METRIC_NAME_LABEL);
111 let matcher = matchers
112 .next()
113 .context(error::InvalidPromRemoteRequestSnafu {
114 msg: "missing '__name__' label in timeseries",
115 })?;
116
117 if matcher.r#type != MatcherType::Eq as i32
118 || matcher.value.is_empty()
119 || matchers.next().is_some()
120 {
121 return Err(error::InvalidPromRemoteRequestSnafu {
122 msg: "expected exactly one non-empty equality matcher for '__name__'".to_string(),
123 }
124 .build());
125 }
126
127 Ok(matcher.value.clone())
128}
129
130pub fn extract_schema_from_query(query: &Query) -> Option<String> {
132 query
133 .matchers
134 .iter()
135 .find(|matcher| {
136 is_database_selection_label(&matcher.name) && matcher.r#type == MatcherType::Eq as i32
137 })
138 .map(|matcher| matcher.value.clone())
139}
140
141pub fn validate_matchers(matchers: &[LabelMatcher]) -> Result<()> {
143 for matcher in matchers {
144 let matcher_type = MatcherType::try_from(matcher.r#type).map_err(|e| {
145 error::InvalidPromRemoteRequestSnafu {
146 msg: format!("invalid LabelMatcher type, decode error: {e}"),
147 }
148 .build()
149 })?;
150 if matches!(matcher_type, MatcherType::Re | MatcherType::Nre) {
151 regex::Regex::new(&matcher.value)
155 .and_then(|_| regex::Regex::new(&anchored_label_regex(&matcher.value)))
156 .map_err(|e| {
157 error::InvalidPromRemoteRequestSnafu {
158 msg: format!("invalid regex for label '{}': {e}", matcher.name),
159 }
160 .build()
161 })?;
162 }
163 }
164 Ok(())
165}
166
167#[tracing::instrument(skip_all)]
169pub fn query_to_plan(
170 dataframe: DataFrame,
171 q: &Query,
172 timestamp_column_name: &str,
173) -> Result<LogicalPlan> {
174 let start_timestamp_ms = q.start_timestamp_ms;
175 let end_timestamp_ms = q.end_timestamp_ms;
176
177 let label_matches = &q.matchers;
178
179 let mut conditions = Vec::with_capacity(label_matches.len() + 1);
180
181 conditions
182 .push(ident(timestamp_column_name).gt_eq(lit_timestamp_millisecond(start_timestamp_ms)));
183 conditions
184 .push(ident(timestamp_column_name).lt_eq(lit_timestamp_millisecond(end_timestamp_ms)));
185
186 for m in label_matches {
187 let name = &m.name;
188
189 if is_remote_read_special_label(name) {
190 continue;
191 }
192
193 let value = &m.value;
194 let m_type = MatcherType::try_from(m.r#type).map_err(|e| {
195 error::InvalidPromRemoteRequestSnafu {
196 msg: format!("invalid LabelMatcher type, decode error: {e}",),
197 }
198 .build()
199 })?;
200
201 let label = if dataframe.schema().has_column_with_unqualified_name(name) {
203 ident(name)
204 } else {
205 lit("")
206 };
207
208 let condition = match m_type {
211 MatcherType::Eq => {
212 if value.is_empty() {
213 label.clone().eq(lit(value)).or(label.is_null())
214 } else {
215 label.eq(lit(value))
216 }
217 }
218 MatcherType::Neq => {
219 if value.is_empty() {
220 label.not_eq(lit(value))
221 } else {
222 label.clone().not_eq(lit(value)).or(label.is_null())
223 }
224 }
225 MatcherType::Re | MatcherType::Nre => {
226 let pattern = anchored_label_regex(value);
227 let matches_empty = regex::Regex::new(&pattern)
228 .map_err(|e| {
229 error::InvalidPromRemoteRequestSnafu {
230 msg: format!("invalid regex for label '{name}': {e}"),
231 }
232 .build()
233 })?
234 .is_match("");
235 let matched = regexp_match(label.clone(), lit(pattern), None);
236 if m_type == MatcherType::Re {
237 let condition = matched.is_not_null();
238 if matches_empty {
239 condition.or(label.is_null())
240 } else {
241 condition
242 }
243 } else {
244 let condition = matched.is_null();
245 if matches_empty {
248 condition.and(label.is_not_null())
249 } else {
250 condition
251 }
252 }
253 }
254 };
255 conditions.push(condition);
256 }
257
258 let conditions = conditions.into_iter().reduce(Expr::and).unwrap();
260
261 let dataframe = dataframe
262 .filter(conditions)
263 .context(error::DataFrameSnafu)?;
264
265 Ok(dataframe.into_parts().1)
266}
267
268fn anchored_label_regex(value: &str) -> String {
271 format!("^(?s:{value})$")
272}
273
274#[inline]
275fn new_label(name: String, value: String) -> Label {
276 Label { name, value }
277}
278
279fn lit_timestamp_millisecond(ts: i64) -> Expr {
280 Expr::Literal(ScalarValue::TimestampMillisecond(Some(ts), None), None)
281}
282
283fn compare_timeseries_labels(left: &[Label], right: &[Label]) -> Ordering {
285 let ordering = left.len().cmp(&right.len());
286 if ordering != Ordering::Equal {
287 return ordering;
288 }
289
290 for (left, right) in left.iter().zip(right) {
291 let ordering = left.name.cmp(&right.name);
292 if ordering != Ordering::Equal {
293 return ordering;
294 }
295
296 let ordering = left.value.cmp(&right.value);
297 if ordering != Ordering::Equal {
298 return ordering;
299 }
300 }
301
302 Ordering::Equal
303}
304
305enum LabelValues<'a> {
306 Utf8(&'a StringArray),
307 LargeUtf8(&'a LargeStringArray),
308 Utf8View(&'a StringViewArray),
309 DictionaryUtf8 {
310 dictionary: &'a DictionaryArray<UInt32Type>,
311 values: &'a StringArray,
312 },
313 Other(Vec<Option<String>>),
314}
315
316impl LabelValues<'_> {
317 fn value(&self, row: usize) -> Option<&str> {
318 match self {
319 Self::Utf8(values) => values.is_valid(row).then(|| values.value(row)),
320 Self::LargeUtf8(values) => values.is_valid(row).then(|| values.value(row)),
321 Self::Utf8View(values) => values.is_valid(row).then(|| values.value(row)),
322 Self::DictionaryUtf8 { dictionary, values } => dictionary
323 .key(row)
324 .and_then(|key| values.is_valid(key).then(|| values.value(key))),
325 Self::Other(values) => values.get(row).and_then(Option::as_deref),
326 }
327 }
328}
329
330fn row_labels<'a>(
331 columns: &'a [LabelColumn<'a>],
332 row: usize,
333) -> impl Iterator<Item = (&'a str, &'a str)> {
334 columns
335 .iter()
336 .filter_map(move |column| column.values.value(row).map(|value| (column.name, value)))
337}
338
339struct LabelColumn<'a> {
340 name: &'a str,
341 values: LabelValues<'a>,
342}
343
344fn label_columns<'a>(
345 recordbatch: &'a RecordBatch,
346 timestamp_column_name: &str,
347 value_column_name: &str,
348) -> Result<Vec<LabelColumn<'a>>> {
349 recordbatch
350 .schema
351 .column_schemas()
352 .iter()
353 .enumerate()
354 .filter(|(_, column_schema)| {
355 column_schema.name != timestamp_column_name && column_schema.name != value_column_name
356 })
357 .map(|(index, column_schema)| {
358 let array = recordbatch.column(index);
359 let values = match array.data_type() {
360 arrow::datatypes::DataType::Utf8 => LabelValues::Utf8(array.as_string::<i32>()),
361 arrow::datatypes::DataType::LargeUtf8 => {
362 LabelValues::LargeUtf8(array.as_string::<i64>())
363 }
364 arrow::datatypes::DataType::Utf8View => {
365 LabelValues::Utf8View(array.as_string_view())
366 }
367 arrow::datatypes::DataType::Dictionary(key, value)
368 if key.as_ref() == &arrow::datatypes::DataType::UInt32
369 && value.as_ref() == &arrow::datatypes::DataType::Utf8 =>
370 {
371 let dictionary = array.as_dictionary::<UInt32Type>();
372 LabelValues::DictionaryUtf8 {
373 dictionary,
374 values: dictionary.values().as_string::<i32>(),
375 }
376 }
377 _ => {
378 let values = recordbatch.iter_column_as_string(index).collect::<Vec<_>>();
379 ensure!(
380 values.len() == recordbatch.num_rows(),
381 error::InvalidPromRemoteReadQueryResultSnafu {
382 msg: format!(
383 "Cannot convert label column '{}' of datatype {:?} to string",
384 column_schema.name,
385 array.data_type()
386 ),
387 }
388 );
389 LabelValues::Other(values)
390 }
391 };
392 Ok(LabelColumn {
393 name: &column_schema.name,
394 values,
395 })
396 })
397 .collect()
398}
399
400fn hash_timeseries(columns: &[LabelColumn<'_>], row: usize) -> u64 {
401 let mut hasher = DefaultHasher::new();
402
403 for (name, value) in row_labels(columns, row) {
404 name.hash(&mut hasher);
405 value.hash(&mut hasher);
406 }
407
408 hasher.finish()
409}
410
411fn matches_timeseries(labels: &[Label], columns: &[LabelColumn<'_>], row: usize) -> bool {
412 let mut labels = labels.iter().skip(1);
413 for (name, value) in row_labels(columns, row) {
414 let Some(label) = labels.next() else {
415 return false;
416 };
417 if label.name != name || label.value != value {
418 return false;
419 }
420 }
421
422 labels.next().is_none()
423}
424
425fn new_timeseries(table: &str, columns: &[LabelColumn<'_>], row: usize) -> TimeSeries {
426 let mut labels = Vec::with_capacity(columns.len() + 1);
427 labels.push(new_label(METRIC_NAME_LABEL.to_string(), table.to_string()));
428
429 for (name, value) in row_labels(columns, row) {
430 labels.push(new_label(name.to_string(), value.to_string()));
431 }
432
433 TimeSeries {
434 labels,
435 ..Default::default()
436 }
437}
438
439pub fn recordbatches_to_timeseries(
440 table_name: &str,
441 timestamp_column_name: &str,
442 value_column_name: &str,
443 recordbatches: RecordBatches,
444) -> Result<Vec<TimeSeries>> {
445 let mut timeseries: Vec<TimeSeries> = Vec::new();
446 let mut timeseries_by_hash: HashMap<u64, Vec<usize>> = HashMap::new();
447
448 for recordbatch in recordbatches.take() {
449 append_recordbatch_to_timeseries(
450 table_name,
451 timestamp_column_name,
452 value_column_name,
453 recordbatch,
454 &mut timeseries,
455 &mut timeseries_by_hash,
456 )?;
457 }
458
459 for ts in &mut timeseries {
460 ts.samples.sort_unstable_by_key(|s| s.timestamp);
461 }
462
463 timeseries
464 .sort_unstable_by(|left, right| compare_timeseries_labels(&left.labels, &right.labels));
465 Ok(timeseries)
466}
467
468fn append_recordbatch_to_timeseries(
469 table: &str,
470 timestamp_column_name: &str,
471 value_column_name: &str,
472 recordbatch: RecordBatch,
473 timeseries: &mut Vec<TimeSeries>,
474 timeseries_by_hash: &mut HashMap<u64, Vec<usize>>,
475) -> Result<()> {
476 let ts_column = recordbatch
477 .column_by_name(timestamp_column_name)
478 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
479 msg: format!("missing timestamp column '{timestamp_column_name}' in query result"),
480 })?;
481 let ts_column: ArrayRef = match ts_column.data_type() {
490 DataType::Timestamp(TimeUnit::Millisecond, _) => ts_column.clone(),
491 DataType::Timestamp(TimeUnit::Second, _) => casts::cast(
493 ts_column,
494 &DataType::Timestamp(TimeUnit::Millisecond, None),
495 )
496 .map_err(|e| {
497 error::InvalidPromRemoteReadQueryResultSnafu {
498 msg: format!(
499 "failed to cast timestamp column '{timestamp_column_name}' of datatype {:?} to millisecond: {e}",
500 ts_column.data_type()
501 ),
502 }
503 .build()
504 })?,
505 DataType::Timestamp(TimeUnit::Microsecond, _) => Arc::new(
506 ts_column
507 .as_primitive::<TimestampMicrosecondType>()
508 .unary::<_, TimestampMillisecondType>(|v| v.div_euclid(1_000)),
509 ),
510 DataType::Timestamp(TimeUnit::Nanosecond, _) => Arc::new(
511 ts_column
512 .as_primitive::<TimestampNanosecondType>()
513 .unary::<_, TimestampMillisecondType>(|v| v.div_euclid(1_000_000)),
514 ),
515 _ => {
516 return error::InvalidPromRemoteReadQueryResultSnafu {
517 msg: format!(
518 "Expect timestamp column of datatype Timestamp(Millisecond), actual {:?}",
519 ts_column.data_type()
520 ),
521 }
522 .fail();
523 }
524 };
525 let ts_column = ts_column
526 .as_primitive_opt::<TimestampMillisecondType>()
527 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
528 msg: format!(
529 "Expect timestamp column of datatype Timestamp(Millisecond), actual {:?}",
530 ts_column.data_type()
531 ),
532 })?;
533
534 let field_column = recordbatch
537 .column_by_name(value_column_name)
538 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
539 msg: format!("missing value column '{value_column_name}' in query result"),
540 })?;
541 let field_column = field_column
542 .as_primitive_opt::<Float64Type>()
543 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
544 msg: format!(
545 "Expect value column of datatype Float64, actual {:?}",
546 field_column.data_type()
547 ),
548 })?;
549
550 let columns = label_columns(&recordbatch, timestamp_column_name, value_column_name)?;
551 let mut previous_timeseries: Option<usize> = None;
552
553 for row in 0..recordbatch.num_rows() {
554 let timeseries_index = match previous_timeseries {
555 Some(index) if matches_timeseries(×eries[index].labels, &columns, row) => index,
556 _ => {
557 let hash = hash_timeseries(&columns, row);
558 let candidates = timeseries_by_hash.entry(hash).or_default();
559 match candidates
560 .iter()
561 .copied()
562 .find(|index| matches_timeseries(×eries[*index].labels, &columns, row))
563 {
564 Some(index) => index,
565 None => {
566 let index = timeseries.len();
567 timeseries.push(new_timeseries(table, &columns, row));
568 candidates.push(index);
569 index
570 }
571 }
572 }
573 };
574 previous_timeseries = Some(timeseries_index);
575
576 if ts_column.is_null(row) || field_column.is_null(row) {
577 continue;
578 }
579
580 let value = field_column.value(row);
581 let timestamp = ts_column.value(row);
582 let sample = Sample { value, timestamp };
583
584 timeseries[timeseries_index].samples.push(sample);
585 }
586
587 Ok(())
588}
589
590pub fn to_grpc_row_insert_requests(request: &WriteRequest) -> Result<(RowInsertRequests, usize)> {
591 let _timer = crate::metrics::METRIC_HTTP_PROM_STORE_CODEC_ELAPSED
592 .with_label_values(&["convert", REMOTE_WRITE_V1_VERSION])
593 .start_timer();
594
595 let mut multi_table_data = MultiTableData::new();
596
597 for series in &request.timeseries {
598 let table_name = &series
599 .labels
600 .iter()
601 .find(|label| {
602 label.name == METRIC_NAME_LABEL
604 })
605 .context(error::InvalidPromRemoteRequestSnafu {
606 msg: "missing '__name__' label in time-series",
607 })?
608 .value;
609
610 let num_columns = series.labels.len() + 1;
613
614 let table_data = multi_table_data.get_or_default_table_data(
615 table_name,
616 num_columns,
617 series.samples.len(),
618 );
619
620 let kvs = series.labels.iter().filter_map(|label| {
622 if label.name == METRIC_NAME_LABEL {
623 None
624 } else {
625 Some((label.name.clone(), label.value.clone()))
626 }
627 });
628
629 if series.samples.len() == 1 {
630 let mut one_row = table_data.alloc_one_row();
631
632 row_writer::write_tags(table_data, kvs, &mut one_row)?;
633 row_writer::write_f64(
635 table_data,
636 greptime_value(),
637 series.samples[0].value,
638 &mut one_row,
639 )?;
640 row_writer::write_ts_to_millis(
642 table_data,
643 greptime_timestamp(),
644 Some(series.samples[0].timestamp),
645 Precision::Millisecond,
646 &mut one_row,
647 )?;
648
649 table_data.add_row(one_row);
650 } else {
651 for Sample { value, timestamp } in &series.samples {
652 let mut one_row = table_data.alloc_one_row();
653
654 let kvs = kvs.clone();
656 row_writer::write_tags(table_data, kvs, &mut one_row)?;
657 row_writer::write_f64(table_data, greptime_value(), *value, &mut one_row)?;
659 row_writer::write_ts_to_millis(
661 table_data,
662 greptime_timestamp(),
663 Some(*timestamp),
664 Precision::Millisecond,
665 &mut one_row,
666 )?;
667
668 table_data.add_row(one_row);
669 }
670 }
671
672 if !series.histograms.is_empty() {
673 warn!("Native histograms are not supported yet, data ignored");
674 }
675 }
676
677 Ok(multi_table_data.into_row_insert_requests())
678}
679
680pub const MAX_DECOMPRESSED_REQUEST_SIZE: usize = 512 * 1024 * 1024;
689
690const ZSTD_DECOMPRESS_CHUNK: usize = 256 * 1024;
692
693pub(crate) struct ChargedBuffer {
697 pub data: Vec<u8>,
698 guards: Vec<MemoryGuard<RequestMemoryMetrics>>,
699}
700
701impl std::ops::Deref for ChargedBuffer {
702 type Target = [u8];
703
704 fn deref(&self) -> &Self::Target {
705 &self.data
706 }
707}
708
709impl std::fmt::Debug for ChargedBuffer {
710 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
711 f.debug_struct("ChargedBuffer")
712 .field("data_len", &self.data.len())
713 .field("guards", &self.guards.len())
714 .finish()
715 }
716}
717
718impl ChargedBuffer {
719 pub(crate) fn new(data: Vec<u8>, guards: Vec<MemoryGuard<RequestMemoryMetrics>>) -> Self {
721 Self { data, guards }
722 }
723
724 pub(crate) fn into_parts(self) -> (Vec<u8>, Vec<MemoryGuard<RequestMemoryMetrics>>) {
727 (self.data, self.guards)
728 }
729}
730
731pub(crate) async fn snappy_decompress_limited(
738 buf: &[u8],
739 max_decompressed: usize,
740 limiter: &ServerMemoryLimiter,
741) -> Result<ChargedBuffer> {
742 let decoded_len =
743 snap::raw::decompress_len(buf).context(error::DecompressSnappyPromRemoteRequestSnafu)?;
744 ensure!(
745 decoded_len <= max_decompressed,
746 error::DecompressedBodyTooLargeSnafu {
747 size: decoded_len as u64,
748 limit: max_decompressed as u64,
749 }
750 );
751 let guard = limiter.acquire(decoded_len as u64).await?;
754 let mut decoder = Decoder::new();
755 let data = decoder
756 .decompress_vec(buf)
757 .context(error::DecompressSnappyPromRemoteRequestSnafu)?;
758 Ok(ChargedBuffer {
759 data,
760 guards: vec![guard],
761 })
762}
763
764pub(crate) async fn zstd_decompress_limited(
768 buf: &[u8],
769 max_decompressed: usize,
770 limiter: &ServerMemoryLimiter,
771) -> Result<ChargedBuffer> {
772 use std::io::Read;
773
774 let decoder = zstd::stream::read::Decoder::new(buf)
775 .context(error::DecompressZstdPromRemoteRequestSnafu)?;
776 let mut limited = decoder.take(max_decompressed as u64 + 1);
779 let mut data = Vec::new();
780 let mut guards = Vec::new();
781 let mut chunk = vec![0u8; ZSTD_DECOMPRESS_CHUNK];
782 loop {
783 let n = limited
784 .read(&mut chunk)
785 .context(error::DecompressZstdPromRemoteRequestSnafu)?;
786 if n == 0 {
787 break;
788 }
789 ensure!(
790 data.len() + n <= max_decompressed,
791 error::DecompressedBodyTooLargeSnafu {
792 size: (data.len() + n) as u64,
793 limit: max_decompressed as u64,
794 }
795 );
796 guards.push(limiter.acquire(n as u64).await?);
799 data.extend_from_slice(&chunk[..n]);
800 }
801 Ok(ChargedBuffer { data, guards })
802}
803
804#[inline]
811pub fn snappy_decompress(buf: &[u8]) -> Result<Vec<u8>> {
812 let decoded_len =
813 snap::raw::decompress_len(buf).context(error::DecompressSnappyPromRemoteRequestSnafu)?;
814 ensure!(
815 decoded_len <= MAX_DECOMPRESSED_REQUEST_SIZE,
816 error::DecompressedBodyTooLargeSnafu {
817 size: decoded_len as u64,
818 limit: MAX_DECOMPRESSED_REQUEST_SIZE as u64,
819 }
820 );
821 let mut decoder = Decoder::new();
822 decoder
823 .decompress_vec(buf)
824 .context(error::DecompressSnappyPromRemoteRequestSnafu)
825}
826
827#[inline]
828pub fn snappy_compress(buf: &[u8]) -> Result<Vec<u8>> {
829 let mut encoder = Encoder::new();
830 encoder
831 .compress_vec(buf)
832 .context(error::CompressPromRemoteRequestSnafu)
833}
834
835pub fn mock_timeseries() -> Vec<TimeSeries> {
838 vec![
839 TimeSeries {
840 labels: vec![
841 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
842 new_label("job".to_string(), "spark".to_string()),
843 ],
844 samples: vec![
845 Sample {
846 value: 1.0f64,
847 timestamp: 1000,
848 },
849 Sample {
850 value: 2.0f64,
851 timestamp: 2000,
852 },
853 ],
854 ..Default::default()
855 },
856 TimeSeries {
857 labels: vec![
858 new_label(METRIC_NAME_LABEL.to_string(), "metric2".to_string()),
859 new_label("instance".to_string(), "test_host1".to_string()),
860 new_label("idc".to_string(), "z001".to_string()),
861 ],
862 samples: vec![
863 Sample {
864 value: 3.0f64,
865 timestamp: 1000,
866 },
867 Sample {
868 value: 4.0f64,
869 timestamp: 2000,
870 },
871 ],
872 ..Default::default()
873 },
874 TimeSeries {
875 labels: vec![
876 new_label(METRIC_NAME_LABEL.to_string(), "metric3".to_string()),
877 new_label("idc".to_string(), "z002".to_string()),
878 new_label("app".to_string(), "biz".to_string()),
879 ],
880 samples: vec![
881 Sample {
882 value: 5.0f64,
883 timestamp: 1000,
884 },
885 Sample {
886 value: 6.0f64,
887 timestamp: 2000,
888 },
889 Sample {
890 value: 7.0f64,
891 timestamp: 3000,
892 },
893 ],
894 ..Default::default()
895 },
896 ]
897}
898
899pub fn mock_timeseries_new_label() -> Vec<TimeSeries> {
901 let ts_demo_metrics = TimeSeries {
902 labels: vec![
903 new_label(METRIC_NAME_LABEL.to_string(), "demo_metrics".to_string()),
904 new_label("idc".to_string(), "idc3".to_string()),
905 new_label("new_label1".to_string(), "foo".to_string()),
906 ],
907 samples: vec![Sample {
908 value: 42.0,
909 timestamp: 3000,
910 }],
911 ..Default::default()
912 };
913 let ts_multi_labels = TimeSeries {
914 labels: vec![
915 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
916 new_label("idc".to_string(), "idc4".to_string()),
917 new_label("env".to_string(), "prod".to_string()),
918 new_label("host".to_string(), "host9".to_string()),
919 new_label("new_label2".to_string(), "bar".to_string()),
920 ],
921 samples: vec![Sample {
922 value: 99.0,
923 timestamp: 4000,
924 }],
925 ..Default::default()
926 };
927
928 vec![ts_demo_metrics, ts_multi_labels]
929}
930
931pub fn mock_timeseries_special_labels() -> Vec<TimeSeries> {
933 let idc3_schema = TimeSeries {
934 labels: vec![
935 new_label(METRIC_NAME_LABEL.to_string(), "idc3_lo_table".to_string()),
936 new_label(DATABASE_LABEL.to_string(), "idc3".to_string()),
937 new_label(PHYSICAL_TABLE_LABEL.to_string(), "f1".to_string()),
938 ],
939 samples: vec![Sample {
940 value: 42.0,
941 timestamp: 3000,
942 }],
943 ..Default::default()
944 };
945 let idc4_schema = TimeSeries {
946 labels: vec![
947 new_label(
948 METRIC_NAME_LABEL.to_string(),
949 "idc4_local_table".to_string(),
950 ),
951 new_label(DATABASE_LABEL.to_string(), "idc4".to_string()),
952 new_label(PHYSICAL_TABLE_LABEL.to_string(), "f2".to_string()),
953 ],
954 samples: vec![Sample {
955 value: 99.0,
956 timestamp: 4000,
957 }],
958 ..Default::default()
959 };
960
961 vec![idc3_schema, idc4_schema]
962}
963
964#[cfg(test)]
965mod tests {
966 use std::sync::Arc;
967
968 use api::prom_store::remote::LabelMatcher;
969 use api::v1::{ColumnDataType, Row, SemanticType};
970 use arrow::array::{
971 DictionaryArray, Float64Array, StringArray, TimestampMillisecondArray, UInt32Array,
972 };
973 use arrow::datatypes::{DataType as ArrowDataType, Field, Schema as ArrowSchema, UInt32Type};
974 use common_memory_manager::OnExhaustedPolicy;
975 use common_recordbatch::DfRecordBatch;
976 use datafusion::prelude::SessionContext;
977 use datatypes::data_type::ConcreteDataType;
978 use datatypes::schema::{ColumnSchema, Schema};
979 use datatypes::vectors::{
980 Float64Vector, Int32Vector, StringVector, TimestampMillisecondVector,
981 };
982 use table::table::adapter::DfTableProviderAdapter;
983 use table::test_util::MemTable;
984
985 use super::*;
986
987 const EQ_TYPE: i32 = MatcherType::Eq as i32;
988 const NEQ_TYPE: i32 = MatcherType::Neq as i32;
989 const RE_TYPE: i32 = MatcherType::Re as i32;
990
991 #[test]
992 fn test_validate_matchers_regex_syntax() {
993 for op in [MatcherType::Re, MatcherType::Nre] {
994 for (value, valid) in [
995 ("api|web", true),
996 ("(?s)api.web", true),
997 ("(?x) api", true),
998 ("(?x) api # comment", false),
999 ("[", false),
1000 ("a)|(?:b", false),
1002 ] {
1003 let matcher = LabelMatcher {
1004 name: "job".into(),
1005 value: value.into(),
1006 r#type: op as i32,
1007 };
1008 assert_eq!(
1009 valid,
1010 validate_matchers(&[matcher]).is_ok(),
1011 "{op:?}: {value}"
1012 );
1013 }
1014 }
1015 }
1016
1017 #[test]
1018 fn test_table_name() {
1019 let q = Query {
1020 start_timestamp_ms: 1000,
1021 end_timestamp_ms: 2000,
1022 matchers: vec![],
1023 ..Default::default()
1024 };
1025 let err = table_name(&q).unwrap_err();
1026 assert!(matches!(err, error::Error::InvalidPromRemoteRequest { .. }));
1027
1028 let q = Query {
1029 start_timestamp_ms: 1000,
1030 end_timestamp_ms: 2000,
1031 matchers: vec![LabelMatcher {
1032 name: METRIC_NAME_LABEL.to_string(),
1033 value: "test".to_string(),
1034 r#type: EQ_TYPE,
1035 }],
1036 ..Default::default()
1037 };
1038 assert_eq!("test", table_name(&q).unwrap());
1039
1040 for matchers in [
1041 vec![LabelMatcher {
1042 name: METRIC_NAME_LABEL.to_string(),
1043 value: "test.*".to_string(),
1044 r#type: RE_TYPE,
1045 }],
1046 vec![LabelMatcher {
1047 name: METRIC_NAME_LABEL.to_string(),
1048 value: String::new(),
1049 r#type: EQ_TYPE,
1050 }],
1051 vec![
1052 LabelMatcher {
1053 name: METRIC_NAME_LABEL.to_string(),
1054 value: "test".to_string(),
1055 r#type: EQ_TYPE,
1056 },
1057 LabelMatcher {
1058 name: METRIC_NAME_LABEL.to_string(),
1059 value: "other".to_string(),
1060 r#type: EQ_TYPE,
1061 },
1062 ],
1063 ] {
1064 let q = Query {
1065 matchers,
1066 ..Default::default()
1067 };
1068 assert!(matches!(
1069 table_name(&q),
1070 Err(error::Error::InvalidPromRemoteRequest { .. })
1071 ));
1072 }
1073 }
1074
1075 #[test]
1076 #[allow(deprecated)]
1077 fn test_extract_schema_from_query() {
1078 let query = Query::default();
1079 assert_eq!(None, extract_schema_from_query(&query));
1080
1081 for label in [DATABASE_LABEL, DATABASE_LABEL_ALT, SCHEMA_LABEL] {
1082 let query = Query {
1083 matchers: vec![LabelMatcher {
1084 name: label.to_string(),
1085 value: "selected_schema".to_string(),
1086 r#type: EQ_TYPE,
1087 }],
1088 ..Default::default()
1089 };
1090 assert_eq!(
1091 Some("selected_schema".to_string()),
1092 extract_schema_from_query(&query)
1093 );
1094 }
1095
1096 let query = Query {
1097 matchers: vec![LabelMatcher {
1098 name: DATABASE_LABEL.to_string(),
1099 value: "selected_schema".to_string(),
1100 r#type: NEQ_TYPE,
1101 }],
1102 ..Default::default()
1103 };
1104 assert_eq!(None, extract_schema_from_query(&query));
1105 }
1106
1107 #[test]
1108 fn test_query_to_plan() {
1109 let q = Query {
1110 start_timestamp_ms: 1000,
1111 end_timestamp_ms: 2000,
1112 matchers: vec![LabelMatcher {
1113 name: METRIC_NAME_LABEL.to_string(),
1114 value: "test".to_string(),
1115 r#type: EQ_TYPE,
1116 }],
1117 ..Default::default()
1118 };
1119
1120 let schema = Arc::new(Schema::new(vec![
1121 ColumnSchema::new(
1122 greptime_timestamp(),
1123 ConcreteDataType::timestamp_millisecond_datatype(),
1124 true,
1125 ),
1126 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1127 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1128 ColumnSchema::new("job", ConcreteDataType::string_datatype(), true),
1129 ColumnSchema::new("service.name", ConcreteDataType::string_datatype(), true),
1130 ColumnSchema::new("Region", ConcreteDataType::string_datatype(), true),
1131 ]));
1132 let recordbatch = RecordBatch::new(
1133 schema,
1134 vec![
1135 Arc::new(TimestampMillisecondVector::from_vec(vec![1000])) as _,
1136 Arc::new(Float64Vector::from_vec(vec![3.0])) as _,
1137 Arc::new(StringVector::from(vec!["host1"])) as _,
1138 Arc::new(StringVector::from(vec!["job"])) as _,
1139 Arc::new(StringVector::from(vec!["api"])) as _,
1140 Arc::new(StringVector::from(vec!["us"])) as _,
1141 ],
1142 )
1143 .unwrap();
1144
1145 let ctx = SessionContext::new();
1146 let table = MemTable::table("test", recordbatch);
1147 let table_provider = Arc::new(DfTableProviderAdapter::new(table));
1148
1149 let dataframe = ctx.read_table(table_provider.clone()).unwrap();
1150 let plan = query_to_plan(dataframe, &q, greptime_timestamp()).unwrap();
1151 let display_string = format!("{}", plan.display_indent());
1152
1153 let ts_col = greptime_timestamp();
1154 let expected = format!(
1155 "Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None)\n TableScan: ?table?",
1156 ts_col, ts_col
1157 );
1158 assert_eq!(expected, display_string);
1159
1160 let q = Query {
1161 start_timestamp_ms: 1000,
1162 end_timestamp_ms: 2000,
1163 matchers: vec![
1164 LabelMatcher {
1165 name: METRIC_NAME_LABEL.to_string(),
1166 value: "test".to_string(),
1167 r#type: EQ_TYPE,
1168 },
1169 LabelMatcher {
1170 name: "job".to_string(),
1171 value: ".*prom.*".to_string(),
1172 r#type: RE_TYPE,
1173 },
1174 LabelMatcher {
1175 name: "instance".to_string(),
1176 value: "localhost".to_string(),
1177 r#type: NEQ_TYPE,
1178 },
1179 LabelMatcher {
1180 name: "service.name".to_string(),
1181 value: "api".to_string(),
1182 r#type: EQ_TYPE,
1183 },
1184 LabelMatcher {
1185 name: "Region".to_string(),
1186 value: "us".to_string(),
1187 r#type: EQ_TYPE,
1188 },
1189 ],
1190 ..Default::default()
1191 };
1192
1193 let dataframe = ctx.read_table(table_provider).unwrap();
1194 let plan = query_to_plan(dataframe, &q, greptime_timestamp()).unwrap();
1195 let display_string = format!("{}", plan.display_indent());
1196
1197 let ts_col = greptime_timestamp();
1198 let expected = format!(
1199 "Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None) AND regexp_match(?table?.job, Utf8(\"^(?s:.*prom.*)$\")) IS NOT NULL AND (?table?.instance != Utf8(\"localhost\") OR ?table?.instance IS NULL) AND ?table?.service.name = Utf8(\"api\") AND ?table?.Region = Utf8(\"us\")\n TableScan: ?table?",
1200 ts_col, ts_col
1201 );
1202 assert_eq!(expected, display_string);
1203 }
1204
1205 #[tokio::test]
1206 async fn test_remote_read_label_matcher_semantics() {
1207 let ctx = SessionContext::new();
1208 let batch = DfRecordBatch::try_from_iter(vec![
1209 (
1210 greptime_timestamp(),
1211 Arc::new(TimestampMillisecondArray::from(vec![1000; 6])) as ArrayRef,
1212 ),
1213 (
1214 "job",
1215 Arc::new(StringArray::from(vec![
1216 Some("api"),
1217 Some("my-api-server"),
1218 Some(""),
1219 None,
1220 Some("web"),
1221 Some("api\nweb"),
1222 ])) as ArrayRef,
1223 ),
1224 (
1225 "id",
1226 Arc::new(UInt32Array::from(vec![0, 1, 2, 3, 4, 5])) as ArrayRef,
1227 ),
1228 ])
1229 .unwrap();
1230 ctx.register_batch("metrics", batch).unwrap();
1231 for (name, op, value, expected) in [
1232 ("job", MatcherType::Eq, "api", vec![0]),
1233 ("job", MatcherType::Neq, "api", vec![1, 2, 3, 4, 5]),
1234 ("job", MatcherType::Eq, "", vec![2, 3]),
1235 ("job", MatcherType::Neq, "", vec![0, 1, 4, 5]),
1236 ("job", MatcherType::Re, "api", vec![0]),
1237 ("job", MatcherType::Nre, "api", vec![1, 2, 3, 4, 5]),
1238 ("job", MatcherType::Re, "api|web", vec![0, 4]),
1239 ("job", MatcherType::Nre, "api|web", vec![1, 2, 3, 5]),
1240 ("job", MatcherType::Re, "", vec![2, 3]),
1241 ("job", MatcherType::Nre, "", vec![0, 1, 4, 5]),
1242 ("job", MatcherType::Re, ".*", vec![0, 1, 2, 3, 4, 5]),
1243 ("job", MatcherType::Nre, ".*", vec![]),
1244 ("job", MatcherType::Re, ".+", vec![0, 1, 4, 5]),
1245 ("job", MatcherType::Nre, ".+", vec![2, 3]),
1246 ("job", MatcherType::Re, "(?m)api", vec![0]),
1247 ("job", MatcherType::Re, "api.web", vec![5]),
1248 ("job", MatcherType::Nre, "api.web", vec![0, 1, 2, 3, 4]),
1249 ("job", MatcherType::Re, "(?-s:api.web)", vec![]),
1250 ("job", MatcherType::Re, "(api)?", vec![0, 2, 3]),
1251 ("missing", MatcherType::Eq, "", vec![0, 1, 2, 3, 4, 5]),
1252 ("missing", MatcherType::Eq, "us", vec![]),
1253 ("missing", MatcherType::Neq, "us", vec![0, 1, 2, 3, 4, 5]),
1254 ("missing", MatcherType::Neq, "", vec![]),
1255 ("missing", MatcherType::Re, ".*", vec![0, 1, 2, 3, 4, 5]),
1256 ("missing", MatcherType::Re, ".+", vec![]),
1257 ("missing", MatcherType::Nre, ".*", vec![]),
1258 ("missing", MatcherType::Nre, ".+", vec![0, 1, 2, 3, 4, 5]),
1259 ("missing", MatcherType::Re, "", vec![0, 1, 2, 3, 4, 5]),
1260 ("missing", MatcherType::Nre, "", vec![]),
1261 ] {
1262 let query = Query {
1263 start_timestamp_ms: 1000,
1264 end_timestamp_ms: 1000,
1265 matchers: vec![LabelMatcher {
1266 name: name.into(),
1267 value: value.into(),
1268 r#type: op as i32,
1269 }],
1270 ..Default::default()
1271 };
1272 let plan = query_to_plan(
1273 ctx.table("metrics").await.unwrap(),
1274 &query,
1275 greptime_timestamp(),
1276 )
1277 .unwrap_or_else(|err| panic!("{name} {op:?} {value:?}: {err}"));
1278 let batches = ctx
1279 .execute_logical_plan(plan)
1280 .await
1281 .unwrap()
1282 .collect()
1283 .await
1284 .unwrap();
1285 let actual: Vec<u32> = batches
1286 .iter()
1287 .flat_map(|batch| {
1288 batch
1289 .column(2)
1290 .as_primitive::<UInt32Type>()
1291 .values()
1292 .to_vec()
1293 })
1294 .collect();
1295 assert_eq!(expected, actual, "{name} {op:?} {value:?}");
1296 }
1297 }
1298
1299 #[tokio::test]
1300 async fn test_remote_read_matcher_combinations_and_label_types() {
1301 let labels = StringArray::from(vec![Some("api"), Some("api"), None, Some("api")]);
1302 for label_type in [
1303 DataType::Utf8,
1304 DataType::LargeUtf8,
1305 DataType::Utf8View,
1306 DataType::Dictionary(Box::new(DataType::UInt32), Box::new(DataType::Utf8)),
1307 ] {
1308 let ctx = SessionContext::new();
1309 let batch = DfRecordBatch::try_from_iter(vec![
1310 (
1311 greptime_timestamp(),
1312 Arc::new(TimestampMillisecondArray::from(vec![999, 1000, 2000, 2001]))
1313 as ArrayRef,
1314 ),
1315 ("Job.Name", casts::cast(&labels, &label_type).unwrap()),
1316 ])
1317 .unwrap();
1318 ctx.register_batch("metrics", batch).unwrap();
1319 let mut cases = vec![
1320 (vec![("Job.Name", MatcherType::Re, ".*")], vec![1000, 2000]),
1321 (vec![("Job.Name", MatcherType::Nre, ".+")], vec![2000]),
1322 ];
1323 if label_type == DataType::Utf8 {
1325 cases.extend([
1326 (
1327 vec![
1328 ("Job.Name", MatcherType::Re, "api|web"),
1329 ("Job.Name", MatcherType::Neq, "web"),
1330 ],
1331 vec![1000],
1332 ),
1333 (
1334 vec![
1335 ("Job.Name", MatcherType::Eq, ""),
1336 ("Job.Name", MatcherType::Neq, "api"),
1337 ],
1338 vec![2000],
1339 ),
1340 (
1341 vec![
1342 ("Job.Name", MatcherType::Eq, "api"),
1343 ("Job.Name", MatcherType::Eq, "web"),
1344 ],
1345 vec![],
1346 ),
1347 (
1348 vec![
1349 ("job.name", MatcherType::Eq, ""),
1350 ("Job.Name", MatcherType::Re, ".*"),
1351 ],
1352 vec![1000, 2000],
1353 ),
1354 ]);
1355 }
1356 for (matchers, expected) in cases {
1357 let mut query = Query {
1358 start_timestamp_ms: 1000,
1359 end_timestamp_ms: 2000,
1360 matchers: matchers
1361 .iter()
1362 .map(|(name, op, value)| LabelMatcher {
1363 name: (*name).into(),
1364 value: (*value).into(),
1365 r#type: *op as i32,
1366 })
1367 .collect(),
1368 ..Default::default()
1369 };
1370 query.matchers.push(LabelMatcher {
1371 name: METRIC_NAME_LABEL.into(),
1372 value: "metrics".into(),
1373 r#type: EQ_TYPE,
1374 });
1375 let plan = query_to_plan(
1376 ctx.table("metrics").await.unwrap(),
1377 &query,
1378 greptime_timestamp(),
1379 )
1380 .unwrap();
1381 let batches = ctx
1382 .execute_logical_plan(plan)
1383 .await
1384 .unwrap()
1385 .collect()
1386 .await
1387 .unwrap();
1388 let actual: Vec<i64> = batches
1389 .iter()
1390 .flat_map(|batch| {
1391 batch
1392 .column(0)
1393 .as_primitive::<TimestampMillisecondType>()
1394 .values()
1395 .to_vec()
1396 })
1397 .collect();
1398 assert_eq!(expected, actual, "{label_type:?}: {matchers:?}");
1399 }
1400 }
1401 }
1402
1403 fn column_schemas_with(
1404 mut kts_iter: Vec<(&str, ColumnDataType, SemanticType)>,
1405 ) -> Vec<api::v1::ColumnSchema> {
1406 kts_iter.push((
1407 greptime_value(),
1408 ColumnDataType::Float64,
1409 SemanticType::Field,
1410 ));
1411 kts_iter.push((
1412 greptime_timestamp(),
1413 ColumnDataType::TimestampMillisecond,
1414 SemanticType::Timestamp,
1415 ));
1416
1417 kts_iter
1418 .into_iter()
1419 .map(|(k, t, s)| api::v1::ColumnSchema {
1420 column_name: k.to_string(),
1421 datatype: t as i32,
1422 semantic_type: s as i32,
1423 ..Default::default()
1424 })
1425 .collect()
1426 }
1427
1428 fn make_row_with_label(l1: &str, value: f64, timestamp: i64) -> Row {
1429 Row {
1430 values: vec![
1431 api::v1::Value {
1432 value_data: Some(api::v1::value::ValueData::StringValue(l1.to_string())),
1433 },
1434 api::v1::Value {
1435 value_data: Some(api::v1::value::ValueData::F64Value(value)),
1436 },
1437 api::v1::Value {
1438 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
1439 timestamp,
1440 )),
1441 },
1442 ],
1443 }
1444 }
1445
1446 fn make_row_with_2_labels(l1: &str, l2: &str, value: f64, timestamp: i64) -> Row {
1447 Row {
1448 values: vec![
1449 api::v1::Value {
1450 value_data: Some(api::v1::value::ValueData::StringValue(l1.to_string())),
1451 },
1452 api::v1::Value {
1453 value_data: Some(api::v1::value::ValueData::StringValue(l2.to_string())),
1454 },
1455 api::v1::Value {
1456 value_data: Some(api::v1::value::ValueData::F64Value(value)),
1457 },
1458 api::v1::Value {
1459 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
1460 timestamp,
1461 )),
1462 },
1463 ],
1464 }
1465 }
1466
1467 #[test]
1468 fn test_write_request_to_row_insert_exprs() {
1469 let write_request = WriteRequest {
1470 timeseries: mock_timeseries(),
1471 ..Default::default()
1472 };
1473
1474 let mut exprs = to_grpc_row_insert_requests(&write_request)
1475 .unwrap()
1476 .0
1477 .inserts;
1478 exprs.sort_unstable_by(|l, r| l.table_name.cmp(&r.table_name));
1479 assert_eq!(3, exprs.len());
1480 assert_eq!("metric1", exprs[0].table_name);
1481 assert_eq!("metric2", exprs[1].table_name);
1482 assert_eq!("metric3", exprs[2].table_name);
1483
1484 let rows = exprs[0].rows.as_ref().unwrap();
1485 let schema = &rows.schema;
1486 let rows = &rows.rows;
1487 assert_eq!(2, rows.len());
1488 assert_eq!(3, schema.len());
1489 assert_eq!(
1490 column_schemas_with(vec![("job", ColumnDataType::String, SemanticType::Tag)]),
1491 *schema
1492 );
1493 assert_eq!(
1494 &vec![
1495 make_row_with_label("spark", 1.0, 1000),
1496 make_row_with_label("spark", 2.0, 2000),
1497 ],
1498 rows
1499 );
1500
1501 let rows = exprs[1].rows.as_ref().unwrap();
1502 let schema = &rows.schema;
1503 let rows = &rows.rows;
1504 assert_eq!(2, rows.len());
1505 assert_eq!(4, schema.len());
1506 assert_eq!(
1507 column_schemas_with(vec![
1508 ("instance", ColumnDataType::String, SemanticType::Tag),
1509 ("idc", ColumnDataType::String, SemanticType::Tag)
1510 ]),
1511 *schema
1512 );
1513 assert_eq!(
1514 &vec![
1515 make_row_with_2_labels("test_host1", "z001", 3.0, 1000),
1516 make_row_with_2_labels("test_host1", "z001", 4.0, 2000),
1517 ],
1518 rows
1519 );
1520
1521 let rows = exprs[2].rows.as_ref().unwrap();
1522 let schema = &rows.schema;
1523 let rows = &rows.rows;
1524 assert_eq!(3, rows.len());
1525 assert_eq!(4, schema.len());
1526 assert_eq!(
1527 column_schemas_with(vec![
1528 ("idc", ColumnDataType::String, SemanticType::Tag),
1529 ("app", ColumnDataType::String, SemanticType::Tag)
1530 ]),
1531 *schema
1532 );
1533 assert_eq!(
1534 &vec![
1535 make_row_with_2_labels("z002", "biz", 5.0, 1000),
1536 make_row_with_2_labels("z002", "biz", 6.0, 2000),
1537 make_row_with_2_labels("z002", "biz", 7.0, 3000),
1538 ],
1539 rows
1540 );
1541 }
1542
1543 #[test]
1544 fn test_recordbatches_to_timeseries() {
1545 let schema = Arc::new(Schema::new(vec![
1546 ColumnSchema::new(
1547 greptime_timestamp(),
1548 ConcreteDataType::timestamp_millisecond_datatype(),
1549 true,
1550 ),
1551 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1552 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1553 ]));
1554
1555 let recordbatches = RecordBatches::try_new(
1556 schema.clone(),
1557 vec![
1558 RecordBatch::new(
1559 schema.clone(),
1560 vec![
1561 Arc::new(TimestampMillisecondVector::from_vec(vec![1000])) as _,
1562 Arc::new(Float64Vector::from_vec(vec![3.0])) as _,
1563 Arc::new(StringVector::from(vec!["host1"])) as _,
1564 ],
1565 )
1566 .unwrap(),
1567 RecordBatch::new(
1568 schema,
1569 vec![
1570 Arc::new(TimestampMillisecondVector::from_vec(vec![2000])) as _,
1571 Arc::new(Float64Vector::from_vec(vec![7.0])) as _,
1572 Arc::new(StringVector::from(vec!["host2"])) as _,
1573 ],
1574 )
1575 .unwrap(),
1576 ],
1577 )
1578 .unwrap();
1579
1580 let timeseries = recordbatches_to_timeseries(
1581 "metric1",
1582 greptime_timestamp(),
1583 greptime_value(),
1584 recordbatches,
1585 )
1586 .unwrap();
1587 assert_eq!(2, timeseries.len());
1588
1589 assert_eq!(
1590 vec![
1591 Label {
1592 name: METRIC_NAME_LABEL.to_string(),
1593 value: "metric1".to_string(),
1594 },
1595 Label {
1596 name: "instance".to_string(),
1597 value: "host1".to_string(),
1598 },
1599 ],
1600 timeseries[0].labels
1601 );
1602
1603 assert_eq!(
1604 timeseries[0].samples,
1605 vec![Sample {
1606 value: 3.0,
1607 timestamp: 1000,
1608 }]
1609 );
1610
1611 assert_eq!(
1612 vec![
1613 Label {
1614 name: METRIC_NAME_LABEL.to_string(),
1615 value: "metric1".to_string(),
1616 },
1617 Label {
1618 name: "instance".to_string(),
1619 value: "host2".to_string(),
1620 },
1621 ],
1622 timeseries[1].labels
1623 );
1624 assert_eq!(
1625 timeseries[1].samples,
1626 vec![Sample {
1627 value: 7.0,
1628 timestamp: 2000,
1629 }]
1630 );
1631 }
1632
1633 #[test]
1634 fn test_recordbatches_to_timeseries_merges_across_batches() {
1635 let schema = Arc::new(Schema::new(vec![
1636 ColumnSchema::new(
1637 greptime_timestamp(),
1638 ConcreteDataType::timestamp_millisecond_datatype(),
1639 true,
1640 ),
1641 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1642 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1643 ]));
1644
1645 let recordbatches = RecordBatches::try_new(
1646 schema.clone(),
1647 vec![
1648 RecordBatch::new(
1649 schema.clone(),
1650 vec![
1651 Arc::new(TimestampMillisecondVector::from_vec(vec![3000, 1500])) as _,
1652 Arc::new(Float64Vector::from_vec(vec![30.0, 15.0])) as _,
1653 Arc::new(StringVector::from(vec!["host1", "host2"])) as _,
1654 ],
1655 )
1656 .unwrap(),
1657 RecordBatch::new(
1658 schema.clone(),
1659 vec![
1660 Arc::new(TimestampMillisecondVector::from_vec(vec![1000])) as _,
1661 Arc::new(Float64Vector::from_vec(vec![10.0])) as _,
1662 Arc::new(StringVector::from(vec!["host1"])) as _,
1663 ],
1664 )
1665 .unwrap(),
1666 RecordBatch::new(
1667 schema,
1668 vec![
1669 Arc::new(TimestampMillisecondVector::from_vec(vec![2000, 2500])) as _,
1670 Arc::new(Float64Vector::from_vec(vec![20.0, 25.0])) as _,
1671 Arc::new(StringVector::from(vec!["host1", "host2"])) as _,
1672 ],
1673 )
1674 .unwrap(),
1675 ],
1676 )
1677 .unwrap();
1678
1679 let timeseries = recordbatches_to_timeseries(
1680 "cpu_usage",
1681 greptime_timestamp(),
1682 greptime_value(),
1683 recordbatches,
1684 )
1685 .unwrap();
1686
1687 assert_eq!(2, timeseries.len());
1688
1689 assert_eq!(
1690 vec![
1691 Label {
1692 name: METRIC_NAME_LABEL.to_string(),
1693 value: "cpu_usage".to_string(),
1694 },
1695 Label {
1696 name: "instance".to_string(),
1697 value: "host1".to_string(),
1698 },
1699 ],
1700 timeseries[0].labels
1701 );
1702 assert_eq!(
1703 vec![
1704 Sample {
1705 value: 10.0,
1706 timestamp: 1000,
1707 },
1708 Sample {
1709 value: 20.0,
1710 timestamp: 2000,
1711 },
1712 Sample {
1713 value: 30.0,
1714 timestamp: 3000,
1715 },
1716 ],
1717 timeseries[0].samples
1718 );
1719
1720 assert_eq!(
1721 vec![
1722 Label {
1723 name: METRIC_NAME_LABEL.to_string(),
1724 value: "cpu_usage".to_string(),
1725 },
1726 Label {
1727 name: "instance".to_string(),
1728 value: "host2".to_string(),
1729 },
1730 ],
1731 timeseries[1].labels
1732 );
1733 assert_eq!(
1734 vec![
1735 Sample {
1736 value: 15.0,
1737 timestamp: 1500,
1738 },
1739 Sample {
1740 value: 25.0,
1741 timestamp: 2500,
1742 },
1743 ],
1744 timeseries[1].samples
1745 );
1746 }
1747
1748 #[test]
1749 fn test_recordbatches_to_timeseries_borrows_and_groups_dictionary_labels() {
1750 let arrow_schema = Arc::new(ArrowSchema::new(vec![
1751 Field::new(
1752 greptime_timestamp(),
1753 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
1754 false,
1755 ),
1756 Field::new(greptime_value(), ArrowDataType::Float64, false),
1757 Field::new_dictionary("instance", ArrowDataType::UInt32, ArrowDataType::Utf8, true),
1758 ]));
1759 let schema = Arc::new(Schema::try_from(arrow_schema.clone()).unwrap());
1760 let instance = DictionaryArray::<UInt32Type>::new(
1761 UInt32Array::from(vec![Some(0), None, Some(1), Some(0), Some(2)]),
1762 Arc::new(StringArray::from(vec![Some("host2"), Some("host1"), None])),
1763 );
1764 let batch = DfRecordBatch::try_new(
1765 arrow_schema,
1766 vec![
1767 Arc::new(TimestampMillisecondArray::from(vec![
1768 1000, 2000, 3000, 4000, 5000,
1769 ])),
1770 Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0, 5.0])),
1771 Arc::new(instance),
1772 ],
1773 )
1774 .unwrap();
1775 let recordbatch = RecordBatch::from_df_record_batch(schema.clone(), batch);
1776 let columns = label_columns(&recordbatch, greptime_timestamp(), greptime_value()).unwrap();
1777 assert!(matches!(
1778 columns[0].values,
1779 LabelValues::DictionaryUtf8 { .. }
1780 ));
1781 drop(columns);
1782 let recordbatches = RecordBatches::try_new(schema, vec![recordbatch]).unwrap();
1783
1784 let timeseries = recordbatches_to_timeseries(
1785 "metric1",
1786 greptime_timestamp(),
1787 greptime_value(),
1788 recordbatches,
1789 )
1790 .unwrap();
1791
1792 assert_eq!(3, timeseries.len());
1793 assert_eq!(
1794 vec![Label {
1795 name: METRIC_NAME_LABEL.to_string(),
1796 value: "metric1".to_string(),
1797 }],
1798 timeseries[0].labels
1799 );
1800 assert_eq!(
1801 vec![
1802 Sample {
1803 value: 2.0,
1804 timestamp: 2000,
1805 },
1806 Sample {
1807 value: 5.0,
1808 timestamp: 5000,
1809 },
1810 ],
1811 timeseries[0].samples
1812 );
1813 assert_eq!("host1", timeseries[1].labels[1].value);
1814 assert_eq!(
1815 vec![Sample {
1816 value: 3.0,
1817 timestamp: 3000,
1818 }],
1819 timeseries[1].samples
1820 );
1821 assert_eq!("host2", timeseries[2].labels[1].value);
1822 assert_eq!(
1823 vec![
1824 Sample {
1825 value: 1.0,
1826 timestamp: 1000,
1827 },
1828 Sample {
1829 value: 4.0,
1830 timestamp: 4000,
1831 },
1832 ],
1833 timeseries[2].samples
1834 );
1835 }
1836
1837 #[test]
1838 fn test_recordbatches_to_timeseries_groups_non_contiguous_series() {
1839 let schema = Arc::new(Schema::new(vec![
1840 ColumnSchema::new(
1841 greptime_timestamp(),
1842 ConcreteDataType::timestamp_millisecond_datatype(),
1843 true,
1844 ),
1845 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1846 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1847 ]));
1848 let recordbatch = RecordBatch::new(
1849 schema.clone(),
1850 vec![
1851 Arc::new(TimestampMillisecondVector::from_vec(vec![1000, 2000, 3000])) as _,
1852 Arc::new(Float64Vector::from_vec(vec![1.0, 2.0, 3.0])) as _,
1853 Arc::new(StringVector::from(vec!["host2", "host1", "host2"])) as _,
1854 ],
1855 )
1856 .unwrap();
1857
1858 let recordbatches = RecordBatches::try_new(schema, vec![recordbatch]).unwrap();
1859 let timeseries = recordbatches_to_timeseries(
1860 "metric1",
1861 greptime_timestamp(),
1862 greptime_value(),
1863 recordbatches,
1864 )
1865 .unwrap();
1866
1867 assert_eq!("host1", timeseries[0].labels[1].value);
1869 assert_eq!("host2", timeseries[1].labels[1].value);
1870 assert_eq!(
1871 vec![
1872 Sample {
1873 value: 1.0,
1874 timestamp: 1000,
1875 },
1876 Sample {
1877 value: 3.0,
1878 timestamp: 3000,
1879 },
1880 ],
1881 timeseries[1].samples
1882 );
1883 }
1884
1885 #[test]
1886 fn test_recordbatches_to_timeseries_arrow_label_types_and_nulls() {
1887 let schema = Arc::new(Schema::new(vec![
1888 ColumnSchema::new(
1889 greptime_timestamp(),
1890 ConcreteDataType::timestamp_millisecond_datatype(),
1891 true,
1892 ),
1893 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1894 ColumnSchema::new("instance", ConcreteDataType::large_string_datatype(), true),
1895 ColumnSchema::new("zone", ConcreteDataType::utf8_view_datatype(), true),
1896 ColumnSchema::new("shard", ConcreteDataType::int32_datatype(), true),
1897 ]));
1898 let recordbatch = RecordBatch::new(
1899 schema.clone(),
1900 vec![
1901 Arc::new(TimestampMillisecondVector::from_vec(vec![1000, 2000, 3000])) as _,
1902 Arc::new(Float64Vector::from_vec(vec![1.0, 2.0, 3.0])) as _,
1903 Arc::new(StringVector::from(LargeStringArray::from(vec![
1904 "host2", "host1", "host2",
1905 ]))) as _,
1906 Arc::new(StringVector::from(StringViewArray::from(vec![
1907 Some("west"),
1908 None,
1909 Some("west"),
1910 ]))) as _,
1911 Arc::new(Int32Vector::from_vec(vec![2, 1, 2])) as _,
1912 ],
1913 )
1914 .unwrap();
1915
1916 let recordbatches = RecordBatches::try_new(schema, vec![recordbatch]).unwrap();
1917 let timeseries = recordbatches_to_timeseries(
1918 "metric1",
1919 greptime_timestamp(),
1920 greptime_value(),
1921 recordbatches,
1922 )
1923 .unwrap();
1924
1925 assert_eq!(2, timeseries.len());
1926 assert_eq!(
1927 vec![
1928 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
1929 new_label("instance".to_string(), "host1".to_string()),
1930 new_label("shard".to_string(), "1".to_string()),
1931 ],
1932 timeseries[0].labels
1933 );
1934 assert_eq!(
1935 vec![
1936 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
1937 new_label("instance".to_string(), "host2".to_string()),
1938 new_label("zone".to_string(), "west".to_string()),
1939 new_label("shard".to_string(), "2".to_string()),
1940 ],
1941 timeseries[1].labels
1942 );
1943 assert_eq!(
1944 vec![
1945 Sample {
1946 value: 1.0,
1947 timestamp: 1000,
1948 },
1949 Sample {
1950 value: 3.0,
1951 timestamp: 3000,
1952 },
1953 ],
1954 timeseries[1].samples
1955 );
1956 }
1957
1958 #[tokio::test]
1959 async fn test_snappy_varint_bomb_is_rejected_before_allocation() {
1960 let bomb = [0xffu8, 0xff, 0xff, 0xff, 0x0f];
1964 let limiter = ServerMemoryLimiter::default();
1965
1966 let err = snappy_decompress_limited(&bomb, MAX_DECOMPRESSED_REQUEST_SIZE, &limiter)
1967 .await
1968 .unwrap_err();
1969 assert!(matches!(
1970 err,
1971 crate::error::Error::DecompressedBodyTooLarge { .. }
1972 ));
1973 assert_eq!(0, limiter.used_bytes());
1974 }
1975
1976 #[tokio::test]
1977 async fn test_snappy_decompress_limited_roundtrip() {
1978 let raw = vec![b'a'; 1024 * 1024];
1979 let mut encoder = snap::raw::Encoder::new();
1980 let compressed = encoder.compress_vec(&raw).unwrap();
1981 let limiter = ServerMemoryLimiter::default();
1982
1983 let decoded =
1984 snappy_decompress_limited(&compressed, MAX_DECOMPRESSED_REQUEST_SIZE, &limiter)
1985 .await
1986 .unwrap();
1987 assert_eq!(&decoded[..], &raw[..]);
1988 }
1989
1990 #[tokio::test]
1991 async fn test_snappy_charges_and_releases_quota() {
1992 let raw = vec![b'a'; 64 * 1024];
1993 let mut encoder = snap::raw::Encoder::new();
1994 let compressed = encoder.compress_vec(&raw).unwrap();
1995 let limiter = ServerMemoryLimiter::new(128 * 1024, OnExhaustedPolicy::Fail);
1996
1997 let decoded =
1998 snappy_decompress_limited(&compressed, MAX_DECOMPRESSED_REQUEST_SIZE, &limiter)
1999 .await
2000 .unwrap();
2001 assert!(limiter.used_bytes() >= 64 * 1024);
2003 assert!(limiter.used_bytes() <= 65 * 1024);
2004 drop(decoded);
2005 assert_eq!(0, limiter.used_bytes());
2006 }
2007
2008 #[tokio::test]
2009 async fn test_snappy_rejects_when_quota_exhausted() {
2010 let raw = vec![b'a'; 64 * 1024];
2011 let mut encoder = snap::raw::Encoder::new();
2012 let compressed = encoder.compress_vec(&raw).unwrap();
2013 let limiter = ServerMemoryLimiter::new(1024, OnExhaustedPolicy::Fail);
2015
2016 let err = snappy_decompress_limited(&compressed, MAX_DECOMPRESSED_REQUEST_SIZE, &limiter)
2017 .await
2018 .unwrap_err();
2019 assert!(matches!(
2020 err,
2021 crate::error::Error::MemoryLimitExceeded { .. }
2022 ));
2023 assert_eq!(0, limiter.used_bytes(), "failed charge must be released");
2024 }
2025
2026 #[tokio::test]
2027 async fn test_zstd_at_and_over_the_cap() {
2028 let limiter = ServerMemoryLimiter::default();
2029 let small_cap = 8 * 1024;
2030
2031 let raw = vec![0u8; small_cap];
2033 let compressed = zstd::stream::encode_all(&raw[..], 3).unwrap();
2034 let decoded = zstd_decompress_limited(&compressed, small_cap, &limiter)
2035 .await
2036 .unwrap();
2037 assert_eq!(decoded.len(), small_cap);
2038
2039 let raw = vec![0u8; small_cap + 1];
2041 let compressed = zstd::stream::encode_all(&raw[..], 3).unwrap();
2042 let err = zstd_decompress_limited(&compressed, small_cap, &limiter)
2043 .await
2044 .unwrap_err();
2045 assert!(matches!(
2046 err,
2047 crate::error::Error::DecompressedBodyTooLarge { .. }
2048 ));
2049 }
2050
2051 #[tokio::test]
2052 async fn test_zstd_charges_incrementally() {
2053 let raw = vec![0u8; 2 * 1024 * 1024];
2056 let compressed = zstd::stream::encode_all(&raw[..], 19).unwrap();
2057 assert!(compressed.len() < 16 * 1024);
2058 let limiter = ServerMemoryLimiter::new(4 * 1024 * 1024, OnExhaustedPolicy::Fail);
2059
2060 let decoded = zstd_decompress_limited(&compressed, MAX_DECOMPRESSED_REQUEST_SIZE, &limiter)
2061 .await
2062 .unwrap();
2063 assert_eq!(decoded.len(), raw.len());
2064 assert!(limiter.used_bytes() >= raw.len() as u64);
2065 drop(decoded);
2066 assert_eq!(0, limiter.used_bytes());
2067 }
2068
2069 #[test]
2070 fn test_snappy_response_decompress_is_capped() {
2071 let bomb = [0xffu8, 0xff, 0xff, 0xff, 0x0f];
2073 let err = snappy_decompress(&bomb).unwrap_err();
2074 assert!(matches!(
2075 err,
2076 crate::error::Error::DecompressedBodyTooLarge { .. }
2077 ));
2078 }
2079}