cache: scope response entries by operation version

This commit is contained in:
a.tolmachev
2026-05-04 07:45:04 +00:00
parent 50b1e5ea13
commit 6cec445b4f
6 changed files with 34 additions and 4 deletions
+1
View File
@@ -62,6 +62,7 @@ Progress:
- `ExecutionConfig.response_cache` is now part of the public operation model, and `admin-api` validates the safe Community baseline: only explicit `REST GET` operations without `auth_profile_ref`
- cache boundary and namespace rules are now documented for `platform / coordination cache` versus `response cache`
- runtime executor now performs actual response cache reads/writes for eligible `REST GET` operations with keys isolated by `workspace + agent + operation + request fingerprint`
- response cache keys now also include `operation version`, so a new published version does not reuse stale cached payloads from the previous one
- mcp-server published tool catalog now uses shared coordination snapshots across instances, while preserving the same `workspace + agent` isolation model
- pending:
- preserve the same isolation model while extending response cache beyond the first `REST GET` hot path
+28 -1
View File
@@ -789,10 +789,11 @@ fn response_cache_key(
let fingerprint_hash = URL_SAFE_NO_PAD.encode(Sha256::digest(fingerprint_bytes));
Some(format!(
"crank:response:workspace:{}:agent:{}:operation:{}:request:{}",
"crank:response:workspace:{}:agent:{}:operation:{}:version:{}:request:{}",
scope.workspace_key,
scope.agent_key,
operation.operation_id.as_str(),
operation.operation_version,
fingerprint_hash
))
}
@@ -1062,6 +1063,32 @@ mod tests {
assert_eq!(request_count.load(Ordering::SeqCst), 2);
}
#[tokio::test]
async fn invalidates_rest_get_cache_on_operation_version_change() {
let request_count = Arc::new(AtomicUsize::new(0));
let base_url = spawn_cached_rest_server(Arc::clone(&request_count)).await;
let executor = RuntimeExecutor::new()
.with_response_cache_store(Arc::new(InMemoryResponseCacheStore::default()));
let operation_v1 = test_cached_rest_get_operation(&base_url);
let mut operation_v2 = operation_v1.clone();
let context = RuntimeRequestContext::from_request_id("req_cache_version")
.with_response_cache_scope("ws_cache", "agent_sales");
let first_output = executor
.execute_with_context(&operation_v1, &json!({ "city": "msk" }), Some(&context))
.await
.unwrap();
operation_v2.operation_version = 2;
let second_output = executor
.execute_with_context(&operation_v2, &json!({ "city": "msk" }), Some(&context))
.await
.unwrap();
assert_eq!(first_output, json!({ "city": "msk", "request_count": 1 }));
assert_eq!(second_output, json!({ "city": "msk", "request_count": 2 }));
assert_eq!(request_count.load(Ordering::SeqCst), 2);
}
#[tokio::test]
async fn emits_runtime_tracing_with_request_context() {
let base_url = spawn_runtime_server().await;
+2
View File
@@ -9,6 +9,7 @@ use serde_json::Value;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RuntimeOperation {
pub operation_id: OperationId,
pub operation_version: u32,
pub tool_name: String,
pub protocol: Protocol,
pub target: Target,
@@ -24,6 +25,7 @@ impl From<Operation<Schema, MappingSet>> for RuntimeOperation {
fn from(value: Operation<Schema, MappingSet>) -> Self {
Self {
operation_id: value.id,
operation_version: value.version,
tool_name: value.name,
protocol: value.protocol,
target: value.target,
+1 -1
View File
@@ -195,7 +195,7 @@
- Enterprise может подключать свой `Valkey/Redis` на уровне single-node или cluster deployment.
- cache namespaces документированы так, чтобы:
- platform / coordination state не смешивался с response cache;
- response cache по умолчанию изолировался по `workspace + agent + operation + request fingerprint`;
- response cache по умолчанию изолировался по `workspace + agent + operation + operation version + request fingerprint`;
- внешний cache backend можно было безопасно шарить между несколькими рабочими областями и агентами.
- shared coordination cache уже применяется не только для rate limiting, но и для multi-instance snapshots published MCP catalogs.
+1 -1
View File
@@ -241,7 +241,7 @@ Crank должен корректно работать, если MCP client де
- `REST`
- `GET`
- без `auth_profile_ref`
- cache keys изолируются минимум по `workspace + agent + operation + request fingerprint`
- cache keys изолируются минимум по `workspace + agent + operation + operation version + request fingerprint`
- streaming operations публикуются как bounded tools или tool families;
- reload published tools без пересборки сервиса;
- никакой draft-логики или admin CRUD в MCP слое.
+1 -1
View File
@@ -195,7 +195,7 @@ Demo/deployment:
Стартовая модель namespace для response cache:
- `workspace + agent + operation + request fingerprint`
- `workspace + agent + operation + operation version + request fingerprint`
Стартовая модель namespace для platform / coordination cache: