mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-12 15:02:32 +00:00
perf(servers): avoid rebuilding JSON records payload
Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com>
This commit is contained in:
@@ -105,11 +105,11 @@ impl IntoResponse for JsonResponse {
|
||||
})
|
||||
.collect();
|
||||
|
||||
json!({
|
||||
"data": data,
|
||||
"execution_time_ms": execution_time,
|
||||
})
|
||||
.to_string()
|
||||
let data = Value::Array(data.into_iter().map(Value::Object).collect());
|
||||
let mut payload = Map::new();
|
||||
payload.insert("data".to_string(), data);
|
||||
payload.insert("execution_time_ms".to_string(), Value::from(execution_time));
|
||||
Value::Object(payload).to_string()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -133,3 +133,122 @@ impl IntoResponse for JsonResponse {
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use axum::body::to_bytes;
|
||||
use axum::http::{StatusCode, header};
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
use super::*;
|
||||
use crate::http::{ColumnSchema, HttpRecordsOutput, OutputSchema};
|
||||
|
||||
fn records(column_names: &[&str], rows: Vec<Vec<Value>>) -> HttpRecordsOutput {
|
||||
let total_rows = rows.len();
|
||||
HttpRecordsOutput {
|
||||
schema: OutputSchema::new(
|
||||
column_names
|
||||
.iter()
|
||||
.map(|name| ColumnSchema::new((*name).to_string(), "Json".to_string()))
|
||||
.collect(),
|
||||
),
|
||||
rows,
|
||||
total_rows,
|
||||
metrics: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn old_records_payload(records: &HttpRecordsOutput, execution_time: u64) -> String {
|
||||
let schema = records.schema();
|
||||
let data: Vec<Map<String, Value>> = records
|
||||
.rows()
|
||||
.iter()
|
||||
.map(|row| {
|
||||
schema
|
||||
.column_schemas
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, col)| (col.name.clone(), row[i].clone()))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
json!({
|
||||
"data": data,
|
||||
"execution_time_ms": execution_time,
|
||||
})
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_records_response_matches_old_json_representation() {
|
||||
let records = records(
|
||||
&["z", "a", "z", "nested", "escaped", "nullable"],
|
||||
vec![
|
||||
vec![
|
||||
json!(1),
|
||||
json!("first"),
|
||||
json!(2),
|
||||
json!({"outer": {"items": [null, "value"]}}),
|
||||
json!("line\nwith \"quotes\" and \\slashes"),
|
||||
Value::Null,
|
||||
],
|
||||
vec![
|
||||
json!(3),
|
||||
json!("second"),
|
||||
json!(4),
|
||||
json!({"array": [true, {"key": "value"}]}),
|
||||
json!("\tescaped"),
|
||||
Value::Null,
|
||||
],
|
||||
],
|
||||
);
|
||||
let execution_time = 42;
|
||||
let expected = old_records_payload(&records, execution_time);
|
||||
|
||||
let response = JsonResponse {
|
||||
output: vec![GreptimeQueryOutput::Records(records)],
|
||||
execution_time_ms: execution_time,
|
||||
}
|
||||
.into_response();
|
||||
|
||||
assert_eq!(StatusCode::OK, response.status());
|
||||
let body = to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
||||
assert_eq!(expected.as_bytes(), body.as_ref());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_empty_records_response_matches_old_json_representation_and_headers() {
|
||||
let records = records(&[], vec![]);
|
||||
let execution_time = 42;
|
||||
let expected = old_records_payload(&records, execution_time);
|
||||
|
||||
let response = JsonResponse {
|
||||
output: vec![GreptimeQueryOutput::Records(records)],
|
||||
execution_time_ms: execution_time,
|
||||
}
|
||||
.into_response();
|
||||
|
||||
assert_eq!(StatusCode::OK, response.status());
|
||||
assert_eq!(
|
||||
mime::APPLICATION_JSON.as_ref(),
|
||||
response.headers().get(header::CONTENT_TYPE).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
ResponseFormat::Json.as_str(),
|
||||
response.headers().get(&GREPTIME_DB_HEADER_FORMAT).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
execution_time.to_string(),
|
||||
response
|
||||
.headers()
|
||||
.get(&GREPTIME_DB_HEADER_EXECUTION_TIME)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let body = to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
||||
assert_eq!(expected.as_bytes(), body.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user