blob: 96b2c7bd38819c991b4359698c626dbc6ca794f3 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! This test is used to verify the correctness of the pooled parser rigorously
//! (50 iterations, 25 iterations for each input data), by comparing the results
//! with the prost parser.
//!
//! Test with `--features unsafe-split` to enable the unsafe optimization.
use std::{fs, sync::Arc, thread};
use bytes::Bytes;
use pb_types::{Exemplar, Label, MetricMetadata, Sample, TimeSeries, WriteRequest};
use prost::Message;
use remote_write::pooled_parser::PooledParser;
const ITERATIONS: usize = 50;
fn load_test_data() -> (Bytes, Bytes) {
let data1 = Bytes::from(
fs::read("tests/workloads/1709380533560664458.data").expect("test data load failed"),
);
let data2 = Bytes::from(
fs::read("tests/workloads/1709380533705807779.data").expect("test data load failed"),
);
(data1, data2)
}
fn parse_with_prost(data: &Bytes) -> WriteRequest {
WriteRequest::decode(data.clone()).expect("prost decode failed")
}
fn parse_with_pooled(data: &Bytes) -> WriteRequest {
let data_copy = data.clone();
let parser = PooledParser;
let pooled_request = parser.decode(data_copy).expect("pooled decode failed");
// Convert pooled types to pb_types to compare with prost.
let mut write_request = WriteRequest {
timeseries: Vec::new(),
metadata: Vec::new(),
};
for pooled_ts in &pooled_request.timeseries {
let mut timeseries = TimeSeries {
labels: Vec::new(),
samples: Vec::new(),
exemplars: Vec::new(),
};
for pooled_label in &pooled_ts.labels {
timeseries.labels.push(Label {
name: String::from_utf8_lossy(&pooled_label.name).to_string(),
value: String::from_utf8_lossy(&pooled_label.value).to_string(),
});
}
for pooled_sample in &pooled_ts.samples {
timeseries.samples.push(Sample {
value: pooled_sample.value,
timestamp: pooled_sample.timestamp,
});
}
for pooled_exemplar in &pooled_ts.exemplars {
let mut exemplar = Exemplar {
labels: Vec::new(),
value: pooled_exemplar.value,
timestamp: pooled_exemplar.timestamp,
};
for pooled_label in &pooled_exemplar.labels {
exemplar.labels.push(Label {
name: String::from_utf8_lossy(&pooled_label.name).to_string(),
value: String::from_utf8_lossy(&pooled_label.value).to_string(),
});
}
timeseries.exemplars.push(exemplar);
}
write_request.timeseries.push(timeseries);
}
for pooled_metadata in pooled_request.metadata.iter() {
let metadata = MetricMetadata {
r#type: pooled_metadata.metric_type as i32,
metric_family_name: String::from_utf8_lossy(&pooled_metadata.metric_family_name)
.to_string(),
help: String::from_utf8_lossy(&pooled_metadata.help).to_string(),
unit: String::from_utf8_lossy(&pooled_metadata.unit).to_string(),
};
write_request.metadata.push(metadata);
}
// pooled_request will be dropped here and returned to the pool.
write_request
}
#[test]
fn test_sequential_correctness() {
let (data1, data2) = load_test_data();
let datasets = [&data1, &data2];
for iteration in 0..ITERATIONS {
let data_index = iteration % 2;
let data = datasets[data_index];
let prost_result = parse_with_prost(data);
let pooled_result = parse_with_pooled(data);
assert_eq!(
&prost_result, &pooled_result,
"Data {} WriteRequest mismatch",
data_index
);
}
}
#[test]
fn test_concurrent_correctness() {
let (data1, data2) = load_test_data();
let data1 = Arc::new(data1);
let data2 = Arc::new(data2);
let mut handles = Vec::new();
for iteration in 0..ITERATIONS {
let data1_clone = Arc::clone(&data1);
let data2_clone = Arc::clone(&data2);
let handle = thread::spawn(move || {
let data_index = iteration % 2;
let data = if data_index == 0 {
&*data1_clone
} else {
&*data2_clone
};
let prost_result = parse_with_prost(data);
let pooled_result = parse_with_pooled(data);
assert_eq!(
&prost_result, &pooled_result,
"Data {} WriteRequest mismatch",
data_index
);
});
handles.push(handle);
}
for handle in handles {
handle.join().expect("thread completion failed");
}
}