Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changesets/maint_bnjjj_entity_cache_versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
### Add version in the entity cache hash ([PR #5701](https://github.com/apollographql/router/pull/5701))

[!IMPORTANT]
If you have enabled [entity caching](https://www.apollographql.com/docs/router/configuration/entity-caching), this release changes the hashing algorithm used for the cache keys. On account of this, you should anticipate additional cache regeneration cost when updating between these versions while the new hashing algorithm comes into service.

By [@bnjjj](https://github.com/bnjjj) in https://github.com/apollographql/router/pull/5701
8 changes: 6 additions & 2 deletions apollo-router/src/plugins/cache/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ use crate::services::supergraph;
use crate::spec::TYPENAME;
use crate::Context;

/// Change this key if you introduce a breaking change in entity caching algorithm to make sure it won't take the previous entries
pub(crate) const ENTITY_CACHE_VERSION: &str = "1.0";
pub(crate) const ENTITIES: &str = "_entities";
pub(crate) const REPRESENTATIONS: &str = "representations";
pub(crate) const CONTEXT_CACHE_KEY: &str = "apollo_entity_cache::key";
Expand Down Expand Up @@ -910,14 +912,15 @@ fn extract_cache_key_root(
let entity_type = entity_type_opt.unwrap_or("Query");

// the cache key is written to easily find keys matching a prefix for deletion:
// - entity cache version: current version of the hash
// - subgraph name: subgraph name
// - entity type: entity type
// - query hash: invalidate the entry for a specific query and operation name
// - additional data: separate cache entries depending on info like authorization status
let mut key = String::new();
let _ = write!(
&mut key,
"subgraph:{subgraph_name}:type:{entity_type}:hash:{query_hash}:data:{additional_data_hash}"
"version:{ENTITY_CACHE_VERSION}:subgraph:{subgraph_name}:type:{entity_type}:hash:{query_hash}:data:{additional_data_hash}"
);

if is_known_private {
Expand Down Expand Up @@ -966,13 +969,14 @@ fn extract_cache_keys(
let hashed_entity_key = hex::encode(digest.finalize().as_slice());

// the cache key is written to easily find keys matching a prefix for deletion:
// - entity cache version: current version of the hash
// - subgraph name: caching is done per subgraph
// - type: can invalidate all instances of a type
// - entity key: invalidate a specific entity
// - query hash: invalidate the entry for a specific query and operation name
// - additional data: separate cache entries depending on info like authorization status
let mut key = String::new();
let _ = write!(&mut key, "subgraph:{subgraph_name}:{typename}:{hashed_entity_key}:{query_hash}:{additional_data_hash}");
let _ = write!(&mut key, "version:{ENTITY_CACHE_VERSION}:subgraph:{subgraph_name}:{typename}:{hashed_entity_key}:{query_hash}:{additional_data_hash}");
if is_known_private {
if let Some(id) = private_id {
let _ = write!(&mut key, ":{id}");
Expand Down
12 changes: 6 additions & 6 deletions apollo-router/tests/integration/redis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,13 +407,13 @@ async fn entity_cache() -> Result<(), BoxError> {
insta::assert_json_snapshot!(response);

let s:String = client
.get("subgraph:products:type:Query:hash:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.get("version:1.0:subgraph:products:type:Query:hash:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.await
.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap());

let s: String = client.get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c").await.unwrap();
let s: String = client.get("version:1.0:subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c").await.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap());

Expand Down Expand Up @@ -517,7 +517,7 @@ async fn entity_cache() -> Result<(), BoxError> {
insta::assert_json_snapshot!(response);

let s:String = client
.get("subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.get("version:1.0:subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.await
.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
Expand Down Expand Up @@ -734,7 +734,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> {
insta::assert_json_snapshot!(response);

let s:String = client
.get("subgraph:products:type:Query:hash:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.get("version:1.0:subgraph:products:type:Query:hash:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.await
.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
Expand All @@ -755,7 +755,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> {
);

let s: String = client
.get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.get("version:1.0:subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.await
.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
Expand Down Expand Up @@ -799,7 +799,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> {
insta::assert_json_snapshot!(response);

let s:String = client
.get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:3b6ef3c8fd34c469d59f513942c5f4c8f91135e828712de2024e2cd4613c50ae:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.get("version:1.0:subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:3b6ef3c8fd34c469d59f513942c5f4c8f91135e828712de2024e2cd4613c50ae:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c")
.await
.unwrap();
let v: Value = serde_json::from_str(&s).unwrap();
Expand Down