cache: add grpc read-only response caching

This commit is contained in:
a.tolmachev
2026-05-04 09:34:34 +00:00
parent b2c5e28cba
commit a61064ba3a
13 changed files with 306 additions and 91 deletions
+2
View File
@@ -3605,6 +3605,7 @@ mod tests {
package: "echo".to_owned(),
service: "EchoService".to_owned(),
method: "UnaryEcho".to_owned(),
read_only: false,
descriptor_ref: DescriptorId::new("desc_echo"),
descriptor_set_b64: grpc_test_support::descriptor_set_b64(),
}),
@@ -3658,6 +3659,7 @@ mod tests {
package: "echo".to_owned(),
service: "EchoService".to_owned(),
method: "ServerEcho".to_owned(),
read_only: false,
descriptor_ref: DescriptorId::new("desc_echo_stream"),
descriptor_set_b64: grpc_test_support::descriptor_set_b64(),
});
+98 -4
View File
@@ -5061,13 +5061,23 @@ fn validate_response_cache_policy(
));
}
if execution_config.streaming.is_some() {
return Err(ApiError::validation_with_context(
"response cache is supported only for unary operations".to_owned(),
json!({
"field": "execution_config.response_cache",
}),
));
}
match target {
Target::Rest(rest_target) if rest_target.method == crank_core::HttpMethod::Get => {}
Target::Graphql(graphql_target)
if graphql_target.operation_type == crank_core::GraphqlOperationType::Query => {}
Target::Grpc(grpc_target) if grpc_target.read_only => {}
_ => {
return Err(ApiError::validation_with_context(
"response cache is supported only for REST GET and GraphQL query operations"
"response cache is supported only for REST GET, GraphQL query, and read-only unary gRPC operations"
.to_owned(),
json!({
"field": "execution_config.response_cache",
@@ -5093,8 +5103,8 @@ mod tests {
use std::collections::BTreeMap;
use crank_core::{
ExecutionConfig, GraphqlOperationType, GraphqlTarget, HttpMethod, ResponseCachePolicy,
RestTarget, Target,
ExecutionConfig, GraphqlOperationType, GraphqlTarget, GrpcTarget, HttpMethod,
ResponseCachePolicy, RestTarget, StreamingConfig, Target, TransportBehavior,
};
use super::validate_response_cache_policy;
@@ -5145,7 +5155,7 @@ mod tests {
assert!(matches!(error, crate::error::ApiError::Validation { .. }));
assert_eq!(
error.to_string(),
"response cache is supported only for REST GET and GraphQL query operations"
"response cache is supported only for REST GET, GraphQL query, and read-only unary gRPC operations"
);
}
@@ -5162,6 +5172,90 @@ mod tests {
assert!(result.is_ok());
}
#[test]
fn accepts_response_cache_for_read_only_unary_grpc() {
let target = Target::Grpc(GrpcTarget {
server_addr: "http://127.0.0.1:50051".to_owned(),
package: "echo".to_owned(),
service: "EchoService".to_owned(),
method: "UnaryEcho".to_owned(),
read_only: true,
descriptor_ref: "desc_echo".into(),
descriptor_set_b64: "ZmFrZQ==".to_owned(),
});
let result = validate_response_cache_policy(&target, &cacheable_execution_config());
assert!(result.is_ok());
}
#[test]
fn rejects_response_cache_for_non_read_only_grpc() {
let target = Target::Grpc(GrpcTarget {
server_addr: "http://127.0.0.1:50051".to_owned(),
package: "echo".to_owned(),
service: "EchoService".to_owned(),
method: "UnaryEcho".to_owned(),
read_only: false,
descriptor_ref: "desc_echo".into(),
descriptor_set_b64: "ZmFrZQ==".to_owned(),
});
let error =
validate_response_cache_policy(&target, &cacheable_execution_config()).unwrap_err();
assert!(matches!(error, crate::error::ApiError::Validation { .. }));
assert_eq!(
error.to_string(),
"response cache is supported only for REST GET, GraphQL query, and read-only unary gRPC operations"
);
}
#[test]
fn rejects_response_cache_for_streaming_grpc() {
let target = Target::Grpc(GrpcTarget {
server_addr: "http://127.0.0.1:50051".to_owned(),
package: "echo".to_owned(),
service: "EchoService".to_owned(),
method: "ServerEcho".to_owned(),
read_only: true,
descriptor_ref: "desc_echo".into(),
descriptor_set_b64: "ZmFrZQ==".to_owned(),
});
let mut execution_config = cacheable_execution_config();
execution_config.streaming = Some(StreamingConfig {
mode: crank_core::ExecutionMode::Window,
transport_behavior: TransportBehavior::ServerStream,
window_duration_ms: Some(1_000),
poll_interval_ms: None,
upstream_timeout_ms: Some(1_000),
idle_timeout_ms: None,
max_session_lifetime_ms: None,
max_items: Some(1),
max_bytes: None,
aggregation_mode: crank_core::AggregationMode::RawItems,
summary_path: None,
items_path: None,
cursor_path: None,
status_path: None,
done_path: None,
redacted_paths: Vec::new(),
truncate_item_fields: false,
max_field_length: None,
drop_duplicates: false,
sampling_rate: None,
tool_family: crank_core::ToolFamilyConfig::default(),
});
let error = validate_response_cache_policy(&target, &execution_config).unwrap_err();
assert!(matches!(error, crate::error::ApiError::Validation { .. }));
assert_eq!(
error.to_string(),
"response cache is supported only for unary operations"
);
}
}
fn enrich_operation_summary(