From f61d243eaacdeece1a767789d6c93b13aeed376c Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Sat, 27 Apr 2024 00:12:47 +0300 Subject: [PATCH 01/64] add test cases --- apollo-router/src/spec/query/change.rs | 562 ++++++++++++++++++++++--- 1 file changed, 515 insertions(+), 47 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index eeb29efa37..91a8985921 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -349,6 +349,10 @@ impl<'a> Hasher for QueryHashVisitor<'a> { } fn write(&mut self, bytes: &[u8]) { + // FIXME: hack I used to debug my code, remove + if bytes.len() != 1 || bytes[0] != 0xFF { + println!("{:?}", std::str::from_utf8(bytes).unwrap()); + } self.hasher.update(bytes); } } @@ -496,10 +500,6 @@ mod tests { #[test] fn me() { let schema1: &str = r#" - schema { - query: Query - } - type Query { me: User customer: User @@ -512,10 +512,6 @@ mod tests { "#; let schema2: &str = r#" - schema { - query: Query - } - type Query { me: User } @@ -544,9 +540,6 @@ mod tests { #[test] fn directive() { let schema1: &str = r#" - schema { - query: Query - } directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM type Query { @@ -561,9 +554,6 @@ mod tests { "#; let schema2: &str = r#" - schema { - query: Query - } directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM type Query { @@ -590,9 +580,6 @@ mod tests { #[test] fn interface() { let schema1: &str = r#" - schema { - query: Query - } directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM type Query { @@ -678,10 +665,6 @@ mod tests { #[test] fn entities() { let schema1: &str = r#" - schema { - query: Query - } - scalar _Any union _Entity = User @@ -699,10 +682,6 @@ mod tests { "#; let schema2: &str = r#" - schema { - query: Query - } - scalar _Any union _Entity = User @@ -728,14 +707,8 @@ mod tests { } }"#; - println!("query1: {query1}"); - let hash1 = hash_subgraph_query(schema1, query1); - println!("hash1: {hash1}"); - let hash2 = hash_subgraph_query(schema2, query1); - println!("hash2: {hash2}"); - assert_ne!(hash1, hash2); let query2 = r#"query Query1($representations:[_Any!]!){ @@ -746,14 +719,8 @@ mod tests { } }"#; - println!("query2: {query2}"); - let hash1 = hash_subgraph_query(schema1, query2); - println!("hash1: {hash1}"); - let hash2 = hash_subgraph_query(schema2, query2); - println!("hash2: {hash2}"); - assert_eq!(hash1, hash2); } @@ -1039,10 +1006,6 @@ mod tests { #[test] fn fields_with_different_arguments_have_different_hashes() { let schema: &str = r#" - schema { - query: Query - } - type Query { test(arg: Int): String } @@ -1061,19 +1024,36 @@ mod tests { } #[test] - fn fields_with_different_aliases_have_different_hashes() { + fn fields_with_different_arguments_on_nest_field_different_hashes() { let schema: &str = r#" - schema { - query: Query + type Test { + test(arg: Int): String + recursiveLink: Test } - + + type Query { + directLink: Test + } + "#; + + let query_one = "{ directLink { test recursiveLink { test(arg: 1) } } }"; + let query_two = "{ directLink { test recursiveLink { test(arg: 2) } } }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn fields_with_different_aliases_have_different_hashes() { + let schema: &str = r#" type Query { test(arg: Int): String } "#; - let query_one = "query { a: test }"; - let query_two = "query { b: test }"; + let query_one = "{ a: test }"; + let query_two = "{ b: test }"; // This assertion tests an internal hash function that isn't directly // used for the query hash, and we'll need to make it pass to rely @@ -1082,4 +1062,492 @@ mod tests { // assert!(hash(schema, query_one).doesnt_match(&hash(schema, query_two))); assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); } + + #[test] + fn operations_with_different_names_have_different_hash() { + let schema: &str = r#" + type Query { + test: String + } + "#; + + let query_one = "query Foo { test }"; + let query_two = "query Bar { test }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn adding_direction_on_operation_changes_hash() { + let schema: &str = r#" + directive @test on QUERY + type Query { + test: String + } + "#; + + let query_one = "query { test }"; + let query_two = "query @test { test }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn order_of_varibles_changes_hash() { + let schema: &str = r#" + type Query { + test1(arg: Int): String + test2(arg: Int): String + } + "#; + + let query_one = "query ($foo: Int, $bar: Int) { test1(arg: $foo) test2(arg: $bar) }"; + let query_two = "query ($foo: Int, $bar: Int) { test1(arg: $bar) test2(arg: $foo) }"; + + assert!(hash(schema, query_one).doesnt_match(&hash(schema, query_two))); + } + + #[test] + fn query_variables_with_different_types_have_different_hash() { + let schema: &str = r#" + type Query { + test(arg: Int): String + } + "#; + + let query_one = "query ($var: Int) { test(arg: $var) }"; + let query_two = "query ($var: Int!) { test(arg: $var) }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn query_variables_with_different_default_values_have_different_hash() { + let schema: &str = r#" + type Query { + test(arg: Int): String + } + "#; + + let query_one = "query ($var: Int = 1) { test(arg: $var) }"; + let query_two = "query ($var: Int = 2) { test(arg: $var) }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn adding_directive_to_query_variable_change_hash() { + let schema: &str = r#" + directive @test on VARIABLE_DEFINITION + + type Query { + test(arg: Int): String + } + "#; + + let query_one = "query ($var: Int) { test(arg: $var) }"; + let query_two = "query ($var: Int @test) { test(arg: $var) }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn order_of_directives_change_hash() { + let schema: &str = r#" + directive @foo on FIELD + directive @bar on FIELD + + type Query { + test(arg: Int): String + } + "#; + + let query_one = "{ test @foo @bar }"; + let query_two = "{ test @bar @foo }"; + + assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); + // FIXME: + assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + } + + #[test] + fn adding_directive_on_schema_changes_hash() { + let schema1: &str = r#" + schema { + query: Query + } + + type Query { + foo: String + } + "#; + + let schema2: &str = r#" + directive @test on SCHEMA + schema @test { + query: Query + } + + type Query { + foo: String + } + "#; + + let query = "{ foo }"; + + // FIXME: both hashes doesn't catch schema change + assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + } + + #[test] + fn changing_type_of_field_changes_hash() { + let schema1: &str = r#" + type Query { + test: Int + } + "#; + + let schema2: &str = r#" + type Query { + test: Float + } + "#; + + let query = "{ test }"; + + // FIXME: both hashes doesn't catch schema change + assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + } + + #[test] + fn changing_type_to_interface_changes_hash() { + let schema1: &str = r#" + type Query { + foo: Foo + } + + interface Foo { + value: String + } + "#; + + let schema2: &str = r#" + type Query { + foo: Foo + } + + type Foo { + value: String + } + "#; + + let query = "{ foo { value } }"; + + // FIXME: both hashes doesn't catch schema change + assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + } + + #[test] + fn changing_operation_kind_changes_hash() { + let schema: &str = r#" + schema { + query: Test + mutation: Test + } + + type Test { + test: String + } + "#; + + let query_one = "query { test }"; + let query_two = "mutation { test }"; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn adding_directive_on_field_should_change_hash() { + let schema: &str = r#" + directive @test on FIELD + + type Query { + test: String + } + "#; + + let query_one = "{ test }"; + let query_two = "{ test @test }"; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn adding_directive_on_fragment_spread_change_hash() { + let schema: &str = r#" + type Query { + test: String + } + "#; + + let query_one = r#" + { ...Test } + + fragment Test on Query { + test + } + "#; + let query_two = r#" + { ...Test @skip(if: false) } + + fragment Test on Query { + test + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn adding_directive_on_fragment_change_hash() { + let schema: &str = r#" + directive @test on FRAGMENT_DEFINITION + + type Query { + test: String + } + "#; + + let query_one = r#" + { ...Test } + + fragment Test on Query { + test + } + "#; + let query_two = r#" + { ...Test } + + fragment Test on Query @test { + test + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn adding_directive_on_inline_fragment_change_hash() { + let schema: &str = r#" + type Query { + test: String + } + "#; + + let query_one = "{ ... { test } }"; + let query_two = "{ ... @skip(if: false) { test } }"; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn moving_field_changes_hash() { + let schema: &str = r#" + type Query { + me: User + } + + type User { + id: ID + name: String + friend: User + } + "#; + + let query_one = r#" + { + me { + friend { + id + name + } + } + } + "#; + let query_two = r#" + { + me { + friend { + id + } + name + } + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn changing_type_of_fragment_changes_hash() { + let schema: &str = r#" + type Query { + fooOrBar: FooOrBar + } + + type Foo { + id: ID + value: String + } + + type Bar { + id: ID + value: String + } + + union FooOrBar = Foo | Bar + "#; + + let query_one = r#" + { + fooOrBar { + ... on Foo { id } + ... on Bar { id } + ... Test + } + } + + fragment Test on Foo { + value + } + "#; + let query_two = r#" + { + fooOrBar { + ... on Foo { id } + ... on Bar { id } + ... Test + } + } + + fragment Test on Bar { + value + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } + + #[test] + fn it_is_weird_so_i_dont_know_how_to_name_it_change_hash() { + let schema: &str = r#" + type Query { + id: ID + someField: SomeType + test: String + } + + type SomeType { + id: ID + test: String + } + "#; + + let query_one = r#" + { + test + someField { id test } + id + } + "#; + let query_two = r#" + { + ...test + someField { id } + } + + fragment test on Query { + id + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + // FIXME: + assert_eq!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } } From 12658acc84909d85261ae2453568099b0992848c Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 17:25:12 +0200 Subject: [PATCH 02/64] add a separator between each part of the hash this makes sure there will be no possible collision by extension (example: hashing `ab` then `cd` VS hashing `a` then `bcd`) --- apollo-router/src/spec/query/change.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 91a8985921..0d0783a8aa 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -353,6 +353,8 @@ impl<'a> Hasher for QueryHashVisitor<'a> { if bytes.len() != 1 || bytes[0] != 0xFF { println!("{:?}", std::str::from_utf8(bytes).unwrap()); } + // byte separator between each part that is hashed + self.hasher.update(&[0xFF][..]); self.hasher.update(bytes); } } From b768d00a6aa92ebdaaa117c1e3a043122f217301 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 17:28:13 +0200 Subject: [PATCH 03/64] deactivate the query string hashing for now --- apollo-router/src/spec/query/change.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 0d0783a8aa..2f528b1276 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -85,7 +85,8 @@ impl<'a> QueryHashVisitor<'a> { ) -> Result, BoxError> { let mut visitor = QueryHashVisitor::new(schema, schema_str, executable); traverse::document(&mut visitor, executable, operation_name)?; - executable.to_string().hash(&mut visitor); + //FIXME: temporarily deactivate this to trigger test failures. This muist be reactivated before merging + //executable.to_string().hash(&mut visitor); Ok(visitor.finish()) } From 78ca93675f17fc545c8cab170105252103af3992 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 18:36:31 +0200 Subject: [PATCH 04/64] fix some tests --- apollo-router/src/spec/query/change.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 2f528b1276..7f54d5951a 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -245,7 +245,7 @@ impl<'a> QueryHashVisitor<'a> { parent_type: String, type_name: String, field_def: &FieldDefinition, - arguments: &[Node], + node: &executable::Field, ) -> Result<(), BoxError> { if self.hashed_fields.insert((parent_type.clone(), type_name)) { self.hash_type_by_name(&parent_type)?; @@ -256,7 +256,7 @@ impl<'a> QueryHashVisitor<'a> { self.hash_input_value_definition(argument)?; } - for argument in arguments { + for argument in &node.arguments { self.hash_argument(argument); } @@ -267,6 +267,12 @@ impl<'a> QueryHashVisitor<'a> { } self.hash_join_field(&parent_type, &field_def.directives)?; + + for directive in &node.directives { + self.hash_directive(directive); + } + + node.alias.hash(self); } Ok(()) } @@ -364,6 +370,12 @@ impl<'a> Visitor for QueryHashVisitor<'a> { fn operation(&mut self, root_type: &str, node: &executable::Operation) -> Result<(), BoxError> { root_type.hash(self); self.hash_type_by_name(root_type)?; + node.operation_type.hash(self); + node.name.hash(self); + for variable in &node.variables {} + for directive in &node.directives { + self.hash_directive(directive); + } traverse::operation(self, root_type, node) } @@ -384,7 +396,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { parent_type.to_string(), field_def.name.as_str().to_string(), field_def, - &node.arguments, + node, )?; traverse::field(self, field_def, node) @@ -1100,7 +1112,7 @@ mod tests { } #[test] - fn order_of_varibles_changes_hash() { + fn order_of_variables_changes_hash() { let schema: &str = r#" type Query { test1(arg: Int): String @@ -1179,8 +1191,7 @@ mod tests { let query_two = "{ test @bar @foo }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1548,7 +1559,7 @@ mod tests { hash(schema, query_two).from_hash_query ); // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); From 25118643d9ca221cb5c4098e613c8ec4bd52c2c3 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 18:51:54 +0200 Subject: [PATCH 05/64] reactivate all tests --- apollo-router/src/spec/query/change.rs | 57 +++++++++----------------- 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 7f54d5951a..05cb4ec9a0 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -1055,8 +1055,7 @@ mod tests { let query_two = "{ directLink { test recursiveLink { test(arg: 2) } } }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1090,12 +1089,11 @@ mod tests { let query_two = "query Bar { test }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] - fn adding_direction_on_operation_changes_hash() { + fn adding_directive_on_operation_changes_hash() { let schema: &str = r#" directive @test on QUERY type Query { @@ -1107,8 +1105,7 @@ mod tests { let query_two = "query @test { test }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1138,8 +1135,7 @@ mod tests { let query_two = "query ($var: Int!) { test(arg: $var) }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1154,8 +1150,7 @@ mod tests { let query_two = "query ($var: Int = 2) { test(arg: $var) }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1172,8 +1167,7 @@ mod tests { let query_two = "query ($var: Int @test) { test(arg: $var) }"; assert!(hash(schema, query_one).from_hash_query != hash(schema, query_two).from_hash_query); - // FIXME: - assert!(hash(schema, query_one).from_visitor == hash(schema, query_two).from_visitor); + assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } #[test] @@ -1219,9 +1213,8 @@ mod tests { let query = "{ foo }"; - // FIXME: both hashes doesn't catch schema change - assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); - assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + assert!(hash(schema1, query).from_hash_query != hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor != hash(schema2, query).from_visitor); } #[test] @@ -1240,9 +1233,8 @@ mod tests { let query = "{ test }"; - // FIXME: both hashes doesn't catch schema change - assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); - assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + assert!(hash(schema1, query).from_hash_query != hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor != hash(schema2, query).from_visitor); } #[test] @@ -1269,9 +1261,8 @@ mod tests { let query = "{ foo { value } }"; - // FIXME: both hashes doesn't catch schema change - assert!(hash(schema1, query).from_hash_query == hash(schema2, query).from_hash_query); - assert!(hash(schema1, query).from_visitor == hash(schema2, query).from_visitor); + assert!(hash(schema1, query).from_hash_query != hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor != hash(schema2, query).from_visitor); } #[test] @@ -1294,8 +1285,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1318,8 +1308,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1352,8 +1341,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1388,8 +1376,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1410,8 +1397,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1456,8 +1442,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1514,8 +1499,7 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: - assert_eq!( + assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor ); @@ -1558,7 +1542,6 @@ mod tests { hash(schema, query_one).from_hash_query, hash(schema, query_two).from_hash_query ); - // FIXME: assert_ne!( hash(schema, query_one).from_visitor, hash(schema, query_two).from_visitor From d783eeef1304741d00a15d456155432f401c8904 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 18:55:53 +0200 Subject: [PATCH 06/64] fixes for query variables --- apollo-router/src/spec/query/change.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 05cb4ec9a0..96fefcf729 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -287,6 +287,8 @@ impl<'a> QueryHashVisitor<'a> { } if let Some(value) = t.default_value.as_ref() { self.hash_value(value); + } else { + "no default value".hash(self); } Ok(()) } @@ -372,7 +374,20 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_type_by_name(root_type)?; node.operation_type.hash(self); node.name.hash(self); - for variable in &node.variables {} + for variable in &node.variables { + variable.name.hash(self); + self.hash_type(&variable.ty)?; + + if let Some(value) = variable.default_value.as_ref() { + self.hash_value(&value); + } else { + "no default value".hash(self); + } + + for directive in &variable.directives { + self.hash_directive(&directive); + } + } for directive in &node.directives { self.hash_directive(directive); } From 459681dcdf365c3fc838361301500e5e0a1fbbe0 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 19:01:51 +0200 Subject: [PATCH 07/64] fix some field hashing tests --- apollo-router/src/spec/query/change.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 96fefcf729..de039b9ff1 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -247,7 +247,6 @@ impl<'a> QueryHashVisitor<'a> { field_def: &FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { - if self.hashed_fields.insert((parent_type.clone(), type_name)) { self.hash_type_by_name(&parent_type)?; field_def.name.hash(self); @@ -273,7 +272,6 @@ impl<'a> QueryHashVisitor<'a> { } node.alias.hash(self); - } Ok(()) } From 842dce92883c462a2674a9dbcd042e24074d5884 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 19:29:25 +0200 Subject: [PATCH 08/64] add separators --- apollo-router/src/spec/query/change.rs | 100 +++++++++++++++++++------ 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index de039b9ff1..22d47ec5b7 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -95,6 +95,7 @@ impl<'a> QueryHashVisitor<'a> { } fn hash_directive(&mut self, directive: &Node) { + "DIRECTIVE".hash(self); directive.name.as_str().hash(self); for argument in &directive.arguments { self.hash_argument(argument) @@ -102,11 +103,14 @@ impl<'a> QueryHashVisitor<'a> { } fn hash_argument(&mut self, argument: &Node) { + "ARGUMENT".hash(self); argument.name.hash(self); self.hash_value(&argument.value); } fn hash_value(&mut self, value: &ast::Value) { + "VALUE".hash(self); + match value { schema::Value::Null => "null".hash(self), schema::Value::Enum(e) => { @@ -143,6 +147,8 @@ impl<'a> QueryHashVisitor<'a> { schema::Value::Object(o) => { "object{".hash(self); for (k, v) in o.iter() { + "key".hash(self); + k.hash(self); ":".hash(self); self.hash_value(v); @@ -152,40 +158,57 @@ impl<'a> QueryHashVisitor<'a> { } } - fn hash_type_by_name(&mut self, t: &str) -> Result<(), BoxError> { - if self.hashed_types.contains(t) { + fn hash_type_by_name(&mut self, name: &str) -> Result<(), BoxError> { + "TYPE_BY_NAME".hash(self); + + println!("hash_type_by_name: will hash type: {name}"); + name.hash(self); + + if self.hashed_types.contains(name) { return Ok(()); } - self.hashed_types.insert(t.to_string()); + self.hashed_types.insert(name.to_string()); - if let Some(ty) = self.schema.types.get(t) { + if let Some(ty) = self.schema.types.get(name) { self.hash_extended_type(ty)?; } Ok(()) } fn hash_extended_type(&mut self, t: &'a ExtendedType) -> Result<(), BoxError> { + "EXTENDED_TYPE".hash(self); + match t { ExtendedType::Scalar(s) => { + "SCALAR".hash(self); + for directive in &s.directives { self.hash_directive(&directive.node); } } ExtendedType::Object(o) => { + "OBJECT".hash(self); + for directive in &o.directives { self.hash_directive(&directive.node); } self.hash_join_type(&o.name, &o.directives)?; + //FIXME: hash implemented interfaces? } ExtendedType::Interface(i) => { + "INTERFACE".hash(self); + for directive in &i.directives { self.hash_directive(&directive.node); } self.hash_join_type(&i.name, &i.directives)?; + //FIXME: hash implemented interfaces? } ExtendedType::Union(u) => { + "UNION".hash(self); + for directive in &u.directives { self.hash_directive(&directive.node); } @@ -195,23 +218,34 @@ impl<'a> QueryHashVisitor<'a> { } } ExtendedType::Enum(e) => { + "ENUM".hash(self); + for directive in &e.directives { self.hash_directive(&directive.node); } for (value, def) in &e.values { + "VALUE".hash(self); + value.hash(self); for directive in &def.directives { self.hash_directive(directive); } + + //FIXME: value definition } } ExtendedType::InputObject(o) => { + "INPUT_OBJECT".hash(self); + for directive in &o.directives { self.hash_directive(&directive.node); } for (name, ty) in &o.fields { + "KEY".hash(self); + + //FIXME: always hash the name? if ty.default_value.is_some() { name.hash(self); self.hash_input_value_definition(&ty.node)?; @@ -223,6 +257,8 @@ impl<'a> QueryHashVisitor<'a> { } fn hash_type(&mut self, t: &ast::Type) -> Result<(), BoxError> { + "TYPE".hash(self); + match t { schema::Type::Named(name) => self.hash_type_by_name(name.as_str()), schema::Type::NonNullNamed(name) => { @@ -247,31 +283,34 @@ impl<'a> QueryHashVisitor<'a> { field_def: &FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { - self.hash_type_by_name(&parent_type)?; + "FIELD".hash(self); - field_def.name.hash(self); + self.hash_type_by_name(&parent_type)?; - for argument in &field_def.arguments { - self.hash_input_value_definition(argument)?; - } + field_def.name.hash(self); + self.hash_type_by_name(&type_name)?; - for argument in &node.arguments { - self.hash_argument(argument); - } + for argument in &field_def.arguments { + self.hash_input_value_definition(argument)?; + } - self.hash_type(&field_def.ty)?; + for argument in &node.arguments { + self.hash_argument(argument); + } - for directive in &field_def.directives { - self.hash_directive(directive); - } + self.hash_type(&field_def.ty)?; - self.hash_join_field(&parent_type, &field_def.directives)?; + for directive in &field_def.directives { + self.hash_directive(directive); + } - for directive in &node.directives { - self.hash_directive(directive); - } + self.hash_join_field(&parent_type, &field_def.directives)?; + + for directive in &node.directives { + self.hash_directive(directive); + } - node.alias.hash(self); + node.alias.hash(self); Ok(()) } @@ -279,6 +318,8 @@ impl<'a> QueryHashVisitor<'a> { &mut self, t: &Node, ) -> Result<(), BoxError> { + "INPUT_VALUE".hash(self); + self.hash_type(&t.ty)?; for directive in &t.directives { self.hash_directive(directive); @@ -292,6 +333,8 @@ impl<'a> QueryHashVisitor<'a> { } fn hash_join_type(&mut self, name: &Name, directives: &DirectiveList) -> Result<(), BoxError> { + "JOIN_TYPE".hash(self); + if let Some(dir_name) = self.join_type_directive_name.as_deref() { if let Some(dir) = directives.get(dir_name) { if let Some(key) = dir.argument_by_name("key").and_then(|arg| arg.as_str()) { @@ -320,6 +363,8 @@ impl<'a> QueryHashVisitor<'a> { parent_type: &str, directives: &ast::DirectiveList, ) -> Result<(), BoxError> { + "JOIN_FIELD".hash(self); + if let Some(dir_name) = self.join_field_directive_name.as_deref() { if let Some(dir) = directives.get(dir_name) { if let Some(requires) = dir @@ -368,6 +413,8 @@ impl<'a> Hasher for QueryHashVisitor<'a> { impl<'a> Visitor for QueryHashVisitor<'a> { fn operation(&mut self, root_type: &str, node: &executable::Operation) -> Result<(), BoxError> { + "VISIT_OPERATION".hash(self); + root_type.hash(self); self.hash_type_by_name(root_type)?; node.operation_type.hash(self); @@ -399,6 +446,8 @@ impl<'a> Visitor for QueryHashVisitor<'a> { field_def: &ast::FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { + "VISIT_FIELD".hash(self); + if !self.seen_introspection && (field_def.name == "__schema" || field_def.name == "__type") { self.seen_introspection = true; @@ -416,6 +465,9 @@ impl<'a> Visitor for QueryHashVisitor<'a> { } fn fragment(&mut self, node: &executable::Fragment) -> Result<(), BoxError> { + "VISIT_FRAGMENT".hash(self); + + println!("will hash fragment: {node:?}"); node.name.hash(self); self.hash_type_by_name(node.type_condition())?; @@ -423,6 +475,10 @@ impl<'a> Visitor for QueryHashVisitor<'a> { } fn fragment_spread(&mut self, node: &executable::FragmentSpread) -> Result<(), BoxError> { + "VISIT_FRAGMENT_SPREAD".hash(self); + + println!("will hash fragment spread: {node:?}"); + node.fragment_name.hash(self); let type_condition = &self .fragments @@ -439,6 +495,8 @@ impl<'a> Visitor for QueryHashVisitor<'a> { parent_type: &str, node: &executable::InlineFragment, ) -> Result<(), BoxError> { + "VISIT_INLINE_FRAGMENT".hash(self); + if let Some(type_condition) = &node.type_condition { self.hash_type_by_name(type_condition)?; } From 9bb31df90cf627308c2343e8e0419ffccb79e9bc Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 27 May 2024 19:29:38 +0200 Subject: [PATCH 09/64] hash some directives --- apollo-router/src/spec/query/change.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 22d47ec5b7..2aba74a1cb 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -469,7 +469,11 @@ impl<'a> Visitor for QueryHashVisitor<'a> { println!("will hash fragment: {node:?}"); node.name.hash(self); + println!("type condition: {:?}", node.type_condition()); self.hash_type_by_name(node.type_condition())?; + for directive in &node.directives { + self.hash_directive(&directive); + } traverse::fragment(self, node) } @@ -487,6 +491,10 @@ impl<'a> Visitor for QueryHashVisitor<'a> { .type_condition(); self.hash_type_by_name(type_condition)?; + for directive in &node.directives { + self.hash_directive(&directive); + } + traverse::fragment_spread(self, node) } @@ -500,6 +508,10 @@ impl<'a> Visitor for QueryHashVisitor<'a> { if let Some(type_condition) = &node.type_condition { self.hash_type_by_name(type_condition)?; } + for directive in &node.directives { + self.hash_directive(&directive); + } + traverse::inline_fragment(self, parent_type, node) } From d632e8a3c001d23e03385b39485a4c994f3032a1 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 28 May 2024 15:23:39 +0200 Subject: [PATCH 10/64] hash the schema --- apollo-router/src/spec/query/change.rs | 145 ++++++++++++++++++------- 1 file changed, 103 insertions(+), 42 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 2aba74a1cb..54ea1c0630 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -4,7 +4,6 @@ use std::hash::Hash; use std::hash::Hasher; use apollo_compiler::ast; -use apollo_compiler::ast::Argument; use apollo_compiler::ast::FieldDefinition; use apollo_compiler::ast::Name; use apollo_compiler::executable; @@ -40,8 +39,6 @@ pub(crate) struct QueryHashVisitor<'a> { hasher: Sha256, fragments: HashMap<&'a ast::Name, &'a Node>, hashed_types: HashSet, - // name, field - hashed_fields: HashSet<(String, String)>, seen_introspection: bool, join_field_directive_name: Option, join_type_directive_name: Option, @@ -53,13 +50,12 @@ impl<'a> QueryHashVisitor<'a> { schema_str: &'a str, executable: &'a executable::ExecutableDocument, ) -> Self { - Self { + let mut visitor = Self { schema, schema_str, hasher: Sha256::new(), fragments: executable.fragments.iter().collect(), hashed_types: HashSet::new(), - hashed_fields: HashSet::new(), seen_introspection: false, // should we just return an error if we do not find those directives? join_field_directive_name: Schema::directive_name( @@ -74,7 +70,20 @@ impl<'a> QueryHashVisitor<'a> { ">=0.1.0", JOIN_TYPE_DIRECTIVE_NAME, ), + }; + + visitor.hash_schema(); + + visitor + } + + pub(crate) fn hash_schema(&mut self) { + // FIXME: hash directive definitions? + "^SCHEMA".hash(self); + for directive in &self.schema.schema_definition.directives { + self.hash_directive(&directive); } + "^SCHEMA-END".hash(self); } pub(crate) fn hash_query( @@ -95,21 +104,23 @@ impl<'a> QueryHashVisitor<'a> { } fn hash_directive(&mut self, directive: &Node) { - "DIRECTIVE".hash(self); + "^DIRECTIVE".hash(self); directive.name.as_str().hash(self); for argument in &directive.arguments { self.hash_argument(argument) } + "^DIRECTIVE-END".hash(self); } fn hash_argument(&mut self, argument: &Node) { - "ARGUMENT".hash(self); + "^ARGUMENT".hash(self); argument.name.hash(self); self.hash_value(&argument.value); + "^ARGUMENT-END".hash(self); } fn hash_value(&mut self, value: &ast::Value) { - "VALUE".hash(self); + "^VALUE".hash(self); match value { schema::Value::Null => "null".hash(self), @@ -138,28 +149,29 @@ impl<'a> QueryHashVisitor<'a> { b.hash(self); } schema::Value::List(l) => { - "list[".hash(self); + "^list[".hash(self); for v in l.iter() { self.hash_value(v); } - "]".hash(self); + "^]".hash(self); } schema::Value::Object(o) => { - "object{".hash(self); + "^object{".hash(self); for (k, v) in o.iter() { - "key".hash(self); + "^key".hash(self); k.hash(self); - ":".hash(self); + "^:".hash(self); self.hash_value(v); } "}".hash(self); } } + "^VALUE-END".hash(self); } fn hash_type_by_name(&mut self, name: &str) -> Result<(), BoxError> { - "TYPE_BY_NAME".hash(self); + "^TYPE_BY_NAME".hash(self); println!("hash_type_by_name: will hash type: {name}"); name.hash(self); @@ -173,22 +185,24 @@ impl<'a> QueryHashVisitor<'a> { if let Some(ty) = self.schema.types.get(name) { self.hash_extended_type(ty)?; } + "^TYPE_BY_NAME-END".hash(self); + Ok(()) } fn hash_extended_type(&mut self, t: &'a ExtendedType) -> Result<(), BoxError> { - "EXTENDED_TYPE".hash(self); + "^EXTENDED_TYPE".hash(self); match t { ExtendedType::Scalar(s) => { - "SCALAR".hash(self); + "^SCALAR".hash(self); for directive in &s.directives { self.hash_directive(&directive.node); } } ExtendedType::Object(o) => { - "OBJECT".hash(self); + "^OBJECT".hash(self); for directive in &o.directives { self.hash_directive(&directive.node); @@ -198,7 +212,7 @@ impl<'a> QueryHashVisitor<'a> { //FIXME: hash implemented interfaces? } ExtendedType::Interface(i) => { - "INTERFACE".hash(self); + "^INTERFACE".hash(self); for directive in &i.directives { self.hash_directive(&directive.node); @@ -207,7 +221,7 @@ impl<'a> QueryHashVisitor<'a> { //FIXME: hash implemented interfaces? } ExtendedType::Union(u) => { - "UNION".hash(self); + "^UNION".hash(self); for directive in &u.directives { self.hash_directive(&directive.node); @@ -218,14 +232,14 @@ impl<'a> QueryHashVisitor<'a> { } } ExtendedType::Enum(e) => { - "ENUM".hash(self); + "^ENUM".hash(self); for directive in &e.directives { self.hash_directive(&directive.node); } for (value, def) in &e.values { - "VALUE".hash(self); + "^VALUE".hash(self); value.hash(self); for directive in &def.directives { @@ -236,14 +250,14 @@ impl<'a> QueryHashVisitor<'a> { } } ExtendedType::InputObject(o) => { - "INPUT_OBJECT".hash(self); + "^INPUT_OBJECT".hash(self); for directive in &o.directives { self.hash_directive(&directive.node); } for (name, ty) in &o.fields { - "KEY".hash(self); + "^KEY".hash(self); //FIXME: always hash the name? if ty.default_value.is_some() { @@ -253,27 +267,31 @@ impl<'a> QueryHashVisitor<'a> { } } } + "^EXTENDED_TYPE-END".hash(self); + Ok(()) } fn hash_type(&mut self, t: &ast::Type) -> Result<(), BoxError> { - "TYPE".hash(self); + "^TYPE".hash(self); match t { - schema::Type::Named(name) => self.hash_type_by_name(name.as_str()), + schema::Type::Named(name) => self.hash_type_by_name(name.as_str())?, schema::Type::NonNullNamed(name) => { "!".hash(self); - self.hash_type_by_name(name.as_str()) + self.hash_type_by_name(name.as_str())?; } schema::Type::List(t) => { "[]".hash(self); - self.hash_type(t) + self.hash_type(t)?; } schema::Type::NonNullList(t) => { "[]!".hash(self); - self.hash_type(t) + self.hash_type(t)?; } } + "^TYPE-END".hash(self); + Ok(()) } fn hash_field( @@ -283,7 +301,7 @@ impl<'a> QueryHashVisitor<'a> { field_def: &FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { - "FIELD".hash(self); + "^FIELD".hash(self); self.hash_type_by_name(&parent_type)?; @@ -311,6 +329,8 @@ impl<'a> QueryHashVisitor<'a> { } node.alias.hash(self); + "^FIELD-END".hash(self); + Ok(()) } @@ -318,7 +338,7 @@ impl<'a> QueryHashVisitor<'a> { &mut self, t: &Node, ) -> Result<(), BoxError> { - "INPUT_VALUE".hash(self); + "^INPUT_VALUE".hash(self); self.hash_type(&t.ty)?; for directive in &t.directives { @@ -329,11 +349,12 @@ impl<'a> QueryHashVisitor<'a> { } else { "no default value".hash(self); } + "^INPUT_VALUE-END".hash(self); Ok(()) } fn hash_join_type(&mut self, name: &Name, directives: &DirectiveList) -> Result<(), BoxError> { - "JOIN_TYPE".hash(self); + "^JOIN_TYPE".hash(self); if let Some(dir_name) = self.join_type_directive_name.as_deref() { if let Some(dir) = directives.get(dir_name) { @@ -354,6 +375,7 @@ impl<'a> QueryHashVisitor<'a> { } } } + "^JOIN_TYPE-END".hash(self); Ok(()) } @@ -363,7 +385,7 @@ impl<'a> QueryHashVisitor<'a> { parent_type: &str, directives: &ast::DirectiveList, ) -> Result<(), BoxError> { - "JOIN_FIELD".hash(self); + "^JOIN_FIELD".hash(self); if let Some(dir_name) = self.join_field_directive_name.as_deref() { if let Some(dir) = directives.get(dir_name) { @@ -390,6 +412,7 @@ impl<'a> QueryHashVisitor<'a> { } } } + "^JOIN_FIELD-END".hash(self); Ok(()) } @@ -413,7 +436,7 @@ impl<'a> Hasher for QueryHashVisitor<'a> { impl<'a> Visitor for QueryHashVisitor<'a> { fn operation(&mut self, root_type: &str, node: &executable::Operation) -> Result<(), BoxError> { - "VISIT_OPERATION".hash(self); + "^VISIT_OPERATION".hash(self); root_type.hash(self); self.hash_type_by_name(root_type)?; @@ -437,7 +460,9 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_directive(directive); } - traverse::operation(self, root_type, node) + traverse::operation(self, root_type, node)?; + "^VISIT_OPERATION-END".hash(self); + Ok(()) } fn field( @@ -446,7 +471,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { field_def: &ast::FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { - "VISIT_FIELD".hash(self); + "^VISIT_FIELD".hash(self); if !self.seen_introspection && (field_def.name == "__schema" || field_def.name == "__type") { @@ -461,11 +486,13 @@ impl<'a> Visitor for QueryHashVisitor<'a> { node, )?; - traverse::field(self, field_def, node) + traverse::field(self, field_def, node)?; + "^VISIT_FIELD_END".hash(self); + Ok(()) } fn fragment(&mut self, node: &executable::Fragment) -> Result<(), BoxError> { - "VISIT_FRAGMENT".hash(self); + "^VISIT_FRAGMENT".hash(self); println!("will hash fragment: {node:?}"); node.name.hash(self); @@ -475,11 +502,14 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_directive(&directive); } - traverse::fragment(self, node) + traverse::fragment(self, node)?; + "^VISIT_FRAGMENT-END".hash(self); + + Ok(()) } fn fragment_spread(&mut self, node: &executable::FragmentSpread) -> Result<(), BoxError> { - "VISIT_FRAGMENT_SPREAD".hash(self); + "^VISIT_FRAGMENT_SPREAD".hash(self); println!("will hash fragment spread: {node:?}"); @@ -495,7 +525,10 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_directive(&directive); } - traverse::fragment_spread(self, node) + traverse::fragment_spread(self, node)?; + "^VISIT_FRAGMENT_SPREAD-END".hash(self); + + Ok(()) } fn inline_fragment( @@ -503,7 +536,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { parent_type: &str, node: &executable::InlineFragment, ) -> Result<(), BoxError> { - "VISIT_INLINE_FRAGMENT".hash(self); + "^VISIT_INLINE_FRAGMENT".hash(self); if let Some(type_condition) = &node.type_condition { self.hash_type_by_name(type_condition)?; @@ -512,7 +545,9 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_directive(&directive); } - traverse::inline_fragment(self, parent_type, node) + traverse::inline_fragment(self, parent_type, node)?; + "^VISIT_INLINE_FRAGMENT-END".hash(self); + Ok(()) } fn schema(&self) -> &apollo_compiler::Schema { @@ -1271,6 +1306,32 @@ mod tests { assert!(hash(schema, query_one).from_visitor != hash(schema, query_two).from_visitor); } + #[test] + fn directive_argument_type_change_hash() { + let schema1: &str = r#" + directive @foo(a: Int) on FIELD + directive @bar on FIELD + + type Query { + test(arg: Int): String + } + "#; + + let schema2: &str = r#" + directive @foo(a: Int!) on FIELD + directive @bar on FIELD + + type Query { + test(arg: Int): String + } + "#; + + let query = "{ test @foo(a: 1) }"; + + assert!(hash(schema1, query).from_hash_query != hash(schema2, query).from_hash_query); + assert!(hash(schema1, query).from_visitor != hash(schema2, query).from_visitor); + } + #[test] fn adding_directive_on_schema_changes_hash() { let schema1: &str = r#" From 9da105e2cab6370bb8a24aa45f5f40b35fa99bc3 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 28 May 2024 15:32:28 +0200 Subject: [PATCH 11/64] hash directive definitions --- apollo-router/src/spec/query.rs | 4 +++- apollo-router/src/spec/query/change.rs | 32 ++++++++++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/apollo-router/src/spec/query.rs b/apollo-router/src/spec/query.rs index b8d158cce4..60b5ed4292 100644 --- a/apollo-router/src/spec/query.rs +++ b/apollo-router/src/spec/query.rs @@ -355,7 +355,9 @@ impl Query { .collect::, SpecError>>()?; let mut visitor = - QueryHashVisitor::new(schema.supergraph_schema(), &schema.raw_sdl, document); + QueryHashVisitor::new(schema.supergraph_schema(), &schema.raw_sdl, document).map_err( + |e| SpecError::QueryHashing(format!("could not calculate the query hash: {e}")), + )?; traverse::document(&mut visitor, document, operation_name).map_err(|e| { SpecError::QueryHashing(format!("could not calculate the query hash: {e}")) })?; diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 54ea1c0630..655beb7ce8 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -49,7 +49,7 @@ impl<'a> QueryHashVisitor<'a> { schema: &'a schema::Schema, schema_str: &'a str, executable: &'a executable::ExecutableDocument, - ) -> Self { + ) -> Result { let mut visitor = Self { schema, schema_str, @@ -72,18 +72,22 @@ impl<'a> QueryHashVisitor<'a> { ), }; - visitor.hash_schema(); + visitor.hash_schema()?; - visitor + Ok(visitor) } - pub(crate) fn hash_schema(&mut self) { + pub(crate) fn hash_schema(&mut self) -> Result<(), BoxError> { // FIXME: hash directive definitions? "^SCHEMA".hash(self); + for directive_definition in self.schema.directive_definitions.values() { + self.hash_directive_definition(&directive_definition)?; + } for directive in &self.schema.schema_definition.directives { self.hash_directive(&directive); } "^SCHEMA-END".hash(self); + Ok(()) } pub(crate) fn hash_query( @@ -92,7 +96,7 @@ impl<'a> QueryHashVisitor<'a> { executable: &'a executable::ExecutableDocument, operation_name: Option<&str>, ) -> Result, BoxError> { - let mut visitor = QueryHashVisitor::new(schema, schema_str, executable); + let mut visitor = QueryHashVisitor::new(schema, schema_str, executable)?; traverse::document(&mut visitor, executable, operation_name)?; //FIXME: temporarily deactivate this to trigger test failures. This muist be reactivated before merging //executable.to_string().hash(&mut visitor); @@ -103,6 +107,20 @@ impl<'a> QueryHashVisitor<'a> { self.hasher.finalize().as_slice().into() } + fn hash_directive_definition( + &mut self, + directive_definition: &Node, + ) -> Result<(), BoxError> { + "^DIRECTIVE_DEFINITION".hash(self); + directive_definition.name.as_str().hash(self); + for argument in &directive_definition.arguments { + self.hash_input_value_definition(&argument)?; + } + "^DIRECTIVE_DEFINITION-END".hash(self); + + Ok(()) + } + fn hash_directive(&mut self, directive: &Node) { "^DIRECTIVE".hash(self); directive.name.as_str().hash(self); @@ -605,7 +623,7 @@ mod tests { .unwrap() .validate(&schema) .unwrap(); - let mut visitor = QueryHashVisitor::new(&schema, schema_str, &exec); + let mut visitor = QueryHashVisitor::new(&schema, schema_str, &exec).unwrap(); traverse::document(&mut visitor, &exec, None).unwrap(); ( @@ -624,7 +642,7 @@ mod tests { .unwrap() .validate(&schema) .unwrap(); - let mut visitor = QueryHashVisitor::new(&schema, schema_str, &exec); + let mut visitor = QueryHashVisitor::new(&schema, schema_str, &exec).unwrap(); traverse::document(&mut visitor, &exec, None).unwrap(); hex::encode(visitor.finish()) From 6e6d517665e8c32b79c8ad4526a22a32d96daeb5 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 28 May 2024 15:34:41 +0200 Subject: [PATCH 12/64] cleanup --- apollo-router/src/spec/query/change.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 655beb7ce8..8334061679 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -33,7 +33,7 @@ pub(crate) const JOIN_TYPE_DIRECTIVE_NAME: &str = "join__type"; pub(crate) struct QueryHashVisitor<'a> { schema: &'a schema::Schema, // TODO: remove once introspection has been moved out of query planning - // For now, introspection is stiull handled by the planner, so when an + // For now, introspection is still handled by the planner, so when an // introspection query is hashed, it should take the whole schema into account schema_str: &'a str, hasher: Sha256, @@ -191,7 +191,6 @@ impl<'a> QueryHashVisitor<'a> { fn hash_type_by_name(&mut self, name: &str) -> Result<(), BoxError> { "^TYPE_BY_NAME".hash(self); - println!("hash_type_by_name: will hash type: {name}"); name.hash(self); if self.hashed_types.contains(name) { @@ -443,9 +442,9 @@ impl<'a> Hasher for QueryHashVisitor<'a> { fn write(&mut self, bytes: &[u8]) { // FIXME: hack I used to debug my code, remove - if bytes.len() != 1 || bytes[0] != 0xFF { - println!("{:?}", std::str::from_utf8(bytes).unwrap()); - } + // if bytes.len() != 1 || bytes[0] != 0xFF { + // println!("{:?}", std::str::from_utf8(bytes).unwrap()); + // } // byte separator between each part that is hashed self.hasher.update(&[0xFF][..]); self.hasher.update(bytes); @@ -512,9 +511,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { fn fragment(&mut self, node: &executable::Fragment) -> Result<(), BoxError> { "^VISIT_FRAGMENT".hash(self); - println!("will hash fragment: {node:?}"); node.name.hash(self); - println!("type condition: {:?}", node.type_condition()); self.hash_type_by_name(node.type_condition())?; for directive in &node.directives { self.hash_directive(&directive); @@ -529,8 +526,6 @@ impl<'a> Visitor for QueryHashVisitor<'a> { fn fragment_spread(&mut self, node: &executable::FragmentSpread) -> Result<(), BoxError> { "^VISIT_FRAGMENT_SPREAD".hash(self); - println!("will hash fragment spread: {node:?}"); - node.fragment_name.hash(self); let type_condition = &self .fragments From 85d93fe7b5c5067c9d6a8841821da857b93143a5 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 28 May 2024 17:48:17 +0200 Subject: [PATCH 13/64] hash interface implementers --- apollo-router/src/spec/query/change.rs | 137 +++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 9 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 8334061679..1f5423027c 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -10,6 +10,7 @@ use apollo_compiler::executable; use apollo_compiler::schema; use apollo_compiler::schema::DirectiveList; use apollo_compiler::schema::ExtendedType; +use apollo_compiler::schema::InterfaceType; use apollo_compiler::validation::Valid; use apollo_compiler::Node; use apollo_compiler::NodeStr; @@ -78,7 +79,6 @@ impl<'a> QueryHashVisitor<'a> { } pub(crate) fn hash_schema(&mut self) -> Result<(), BoxError> { - // FIXME: hash directive definitions? "^SCHEMA".hash(self); for directive_definition in self.schema.directive_definitions.values() { self.hash_directive_definition(&directive_definition)?; @@ -314,7 +314,6 @@ impl<'a> QueryHashVisitor<'a> { fn hash_field( &mut self, parent_type: String, - type_name: String, field_def: &FieldDefinition, node: &executable::Field, ) -> Result<(), BoxError> { @@ -323,7 +322,7 @@ impl<'a> QueryHashVisitor<'a> { self.hash_type_by_name(&parent_type)?; field_def.name.hash(self); - self.hash_type_by_name(&type_name)?; + self.hash_type(&field_def.ty)?; for argument in &field_def.arguments { self.hash_input_value_definition(argument)?; @@ -433,6 +432,24 @@ impl<'a> QueryHashVisitor<'a> { Ok(()) } + + fn hash_interface_implementers( + &mut self, + intf: &InterfaceType, + node: &executable::Field, + ) -> Result<(), BoxError> { + "^INTERFACE_IMPL".hash(self); + + if let Some(implementers) = self.schema.implementers_map().get(&intf.name) { + for object in &implementers.objects { + self.hash_type_by_name(object)?; + traverse::selection_set(self, object, &node.selection_set.selections)?; + } + } + + "^INTERFACE_IMPL-END".hash(self); + Ok(()) + } } impl<'a> Hasher for QueryHashVisitor<'a> { @@ -496,12 +513,13 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.schema_str.hash(self); } - self.hash_field( - parent_type.to_string(), - field_def.name.as_str().to_string(), - field_def, - node, - )?; + self.hash_field(parent_type.to_string(), field_def, node)?; + + if let Some(ExtendedType::Interface(intf)) = + self.schema.types.get(field_def.ty.inner_named_type()) + { + self.hash_interface_implementers(intf, node)?; + } traverse::field(self, field_def, node)?; "^VISIT_FIELD_END".hash(self); @@ -1662,6 +1680,107 @@ mod tests { ); } + #[test] + fn changing_interface_implementors_changes_hash() { + let schema1: &str = r#" + type Query { + data: I + } + + interface I { + id: ID + value: String + } + + type Foo implements I { + id: ID + value: String + foo: String + } + + type Bar { + id: ID + value: String + bar: String + } + "#; + + let schema2: &str = r#" + type Query { + data: I + } + + interface I { + id: ID + value: String + } + + type Foo implements I { + id: ID + value: String + foo2: String + } + + type Bar { + id: ID + value: String + bar: String + } + "#; + + let schema3: &str = r#" + type Query { + data: I + } + + interface I { + id: ID + value: String + } + + type Foo implements I { + id: ID + value: String + foo: String + } + + type Bar implements I { + id: ID + value: String + bar: String + } + "#; + + let query = r#" + { + data { + id + value + } + } + "#; + + // changing an unrelated field in implementors does not change the hash + assert_eq!( + hash(schema1, query).from_hash_query, + hash(schema2, query).from_hash_query + ); + assert_eq!( + hash(schema1, query).from_visitor, + hash(schema2, query).from_visitor + ); + + // adding a new implementor changes the hash + assert_ne!( + hash(schema1, query).from_hash_query, + hash(schema3, query).from_hash_query + ); + assert_ne!( + hash(schema1, query).from_visitor, + hash(schema3, query).from_visitor + ); + } + #[test] fn it_is_weird_so_i_dont_know_how_to_name_it_change_hash() { let schema: &str = r#" From 7968c227394b3088b69881bb613d7f5cd1545428 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 28 May 2024 18:46:42 +0200 Subject: [PATCH 14/64] update hashes --- ...__non_overridden_field_yields_expected_query_plan.snap | 2 +- ...ests__overridden_field_yields_expected_query_plan.snap | 4 ++-- ..._expose_query_plan__tests__it_expose_query_plan-2.snap | 8 ++++---- ...s__expose_query_plan__tests__it_expose_query_plan.snap | 8 ++++---- ...y_planner__bridge_query_planner__tests__plan_root.snap | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap index cb657dcdce..7e9d0dd729 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap @@ -19,7 +19,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "12dda6193654ae4fe6e38bc09d4f81cc73d0c9e098692096f72d2158eef4776f", + "schemaAwareHash": "14000c34c41d03ff28d98f0998ce0c5c6442386f2b1d22ab6981e8309818686d", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap index d18a3e2b11..ae7f8b8aed 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap @@ -24,7 +24,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "00ad582ea45fc1bce436b36b21512f3d2c47b74fdbdc61e4b349289722c9ecf2", + "schemaAwareHash": "9dabf11779d5fa948462cd48bcb9cf03d7e1de5aea98bbee2582b40ce1af084b", "authorization": { "is_authenticated": false, "scopes": [], @@ -63,7 +63,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a8ebdc2151a2e5207882e43c6906c0c64167fd9a8e0c7c4becc47736a5105096", + "schemaAwareHash": "12be1b17ff6623e99cb2f573e175aa8beb843de0e7ca7e6e9be589be59bd6463", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap index 0d6ab611f6..33f99cd797 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7245d488e97c3b2ac9f5fa4dd4660940b94ad81af070013305b2c0f76337b2f9", + "schemaAwareHash": "65edb649e8367062330ab333a77e9f3b481128c15e10118f2a3bc711bffb6f4d", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "6e0b4156706ea0cf924500cfdc99dd44b9f0ed07e2d3f888d4aff156e6a33238", + "schemaAwareHash": "35636ba91ee32a212ef91b41e578c6524f6b123d483b1e3fb2c923fe401712ce", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "ff649f3d70241d5a8cd5f5d03ff4c41ecff72b0e4129a480207b05ac92318042", + "schemaAwareHash": "542dcda3a513518024c73bb58c973fb4f4f59b1cbc3e437c2ad9b156d02a06c6", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "bf9f3beda78a7a565e47c862157bad4ec871d724d752218da1168455dddca074", + "schemaAwareHash": "d137af8cb4c932ceab01a0a00b16e615e389cb9b2f8749b9d88233806d984cb4", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap index 0d6ab611f6..33f99cd797 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7245d488e97c3b2ac9f5fa4dd4660940b94ad81af070013305b2c0f76337b2f9", + "schemaAwareHash": "65edb649e8367062330ab333a77e9f3b481128c15e10118f2a3bc711bffb6f4d", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "6e0b4156706ea0cf924500cfdc99dd44b9f0ed07e2d3f888d4aff156e6a33238", + "schemaAwareHash": "35636ba91ee32a212ef91b41e578c6524f6b123d483b1e3fb2c923fe401712ce", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "ff649f3d70241d5a8cd5f5d03ff4c41ecff72b0e4129a480207b05ac92318042", + "schemaAwareHash": "542dcda3a513518024c73bb58c973fb4f4f59b1cbc3e437c2ad9b156d02a06c6", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "bf9f3beda78a7a565e47c862157bad4ec871d724d752218da1168455dddca074", + "schemaAwareHash": "d137af8cb4c932ceab01a0a00b16e615e389cb9b2f8749b9d88233806d984cb4", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap index d49c351866..57bfda29ab 100644 --- a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap @@ -15,7 +15,7 @@ Fetch( output_rewrites: None, context_rewrites: None, schema_aware_hash: QueryHash( - "a4ab3ffe0fd7863aea8cd1e85d019d2c64ec0351d62f9759bed3c9dc707ea315", + "4d049a5344d7c5ba0f75883d65ce3ff6e14c2f6aae16c31b5c0a7c909866777a", ), authorization: CacheKeyMetadata { is_authenticated: false, From 4d7ceea102312c9c5a1aabd07378e1d33ab3c260 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 14:53:20 +0200 Subject: [PATCH 15/64] lint --- apollo-router/src/spec/query/change.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 1f5423027c..7565bff57d 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -81,10 +81,10 @@ impl<'a> QueryHashVisitor<'a> { pub(crate) fn hash_schema(&mut self) -> Result<(), BoxError> { "^SCHEMA".hash(self); for directive_definition in self.schema.directive_definitions.values() { - self.hash_directive_definition(&directive_definition)?; + self.hash_directive_definition(directive_definition)?; } for directive in &self.schema.schema_definition.directives { - self.hash_directive(&directive); + self.hash_directive(directive); } "^SCHEMA-END".hash(self); Ok(()) @@ -114,7 +114,7 @@ impl<'a> QueryHashVisitor<'a> { "^DIRECTIVE_DEFINITION".hash(self); directive_definition.name.as_str().hash(self); for argument in &directive_definition.arguments { - self.hash_input_value_definition(&argument)?; + self.hash_input_value_definition(argument)?; } "^DIRECTIVE_DEFINITION-END".hash(self); @@ -481,13 +481,13 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_type(&variable.ty)?; if let Some(value) = variable.default_value.as_ref() { - self.hash_value(&value); + self.hash_value(value); } else { "no default value".hash(self); } for directive in &variable.directives { - self.hash_directive(&directive); + self.hash_directive(directive); } } for directive in &node.directives { @@ -532,7 +532,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { node.name.hash(self); self.hash_type_by_name(node.type_condition())?; for directive in &node.directives { - self.hash_directive(&directive); + self.hash_directive(directive); } traverse::fragment(self, node)?; @@ -553,7 +553,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_type_by_name(type_condition)?; for directive in &node.directives { - self.hash_directive(&directive); + self.hash_directive(directive); } traverse::fragment_spread(self, node)?; @@ -573,7 +573,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_type_by_name(type_condition)?; } for directive in &node.directives { - self.hash_directive(&directive); + self.hash_directive(directive); } traverse::inline_fragment(self, parent_type, node)?; From 9c3495266a887bdc13052f519b3aab313b62799e Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 15:57:04 +0200 Subject: [PATCH 16/64] add a test for directives applied to interface definitions --- apollo-router/src/spec/query/change.rs | 63 +++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 7565bff57d..8421cf0abf 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -1752,7 +1752,7 @@ mod tests { "#; let query = r#" - { + { data { id value @@ -1781,6 +1781,67 @@ mod tests { ); } + #[test] + fn changing_interface_directives_changes_hash() { + let schema1: &str = r#" + directive @a(name: String) on INTERFACE + + type Query { + data: I + } + + interface I @a { + id: ID + value: String + } + + type Foo implements I { + id: ID + value: String + foo: String + } + "#; + + let schema2: &str = r#" + directive @a(name: String) on INTERFACE + + type Query { + data: I + } + + interface I @a(name: "abc") { + id: ID + value: String + } + + type Foo implements I { + id: ID + value: String + foo2: String + } + + "#; + + let query = r#" + { + data { + id + value + } + } + "#; + + // changing a directive applied on the interface definition changes the hash + assert_ne!( + hash(schema1, query).from_hash_query, + hash(schema2, query).from_hash_query + ); + assert_ne!( + hash(schema1, query).from_visitor, + hash(schema2, query).from_visitor + ); + } + #[test] fn it_is_weird_so_i_dont_know_how_to_name_it_change_hash() { let schema: &str = r#" From fcb3f3f24e597eb8c9e3fc57415dec88f2692987 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 16:23:33 +0200 Subject: [PATCH 17/64] cleanup --- apollo-router/src/spec/query/change.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 8421cf0abf..ca103f5015 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -98,8 +98,7 @@ impl<'a> QueryHashVisitor<'a> { ) -> Result, BoxError> { let mut visitor = QueryHashVisitor::new(schema, schema_str, executable)?; traverse::document(&mut visitor, executable, operation_name)?; - //FIXME: temporarily deactivate this to trigger test failures. This muist be reactivated before merging - //executable.to_string().hash(&mut visitor); + executable.to_string().hash(&mut visitor); Ok(visitor.finish()) } @@ -262,8 +261,6 @@ impl<'a> QueryHashVisitor<'a> { for directive in &def.directives { self.hash_directive(directive); } - - //FIXME: value definition } } ExtendedType::InputObject(o) => { From 0b1798008fd1204840f7f890e16ad9fd800546d6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 16:47:04 +0200 Subject: [PATCH 18/64] add a metric tracking how many query plans could be reused --- .../src/query_planner/caching_query_planner.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index f6b04aba23..c7502d6f4a 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -267,9 +267,9 @@ where introspection: self.introspection, }; - if experimental_reuse_query_plans { - // if the query hash did not change with the schema update, we can reuse the previously cached entry - if let Some(hash) = hash { + if let Some(hash) = hash { + if experimental_reuse_query_plans { + // if the query hash did not change with the schema update, we can reuse the previously cached entry if hash == doc.hash { if let Some(entry) = { previous_cache.lock().await.get(&caching_key).cloned() } @@ -279,6 +279,10 @@ where continue; } } + } else { + if hash == doc.hash { + reused += 1; + } } } @@ -341,6 +345,11 @@ where } tracing::debug!("warmed up the query planner cache with {count} queries planned and {reused} queries reused"); + + ::tracing::info!( + monotonic_counter.apollo.router.query.planning.warmup.reused = reused, + query_plan_reuse_active = experimental_reuse_query_plans + ); } } From bd7e5eb28f5821f0adc1dd704ef61241415579b8 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 18:43:06 +0200 Subject: [PATCH 19/64] simplify the caching key --- .../query_planner/caching_query_planner.rs | 82 ++++++++++++++----- apollo-router/src/spec/schema.rs | 9 +- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index c7502d6f4a..7371867339 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -76,6 +76,7 @@ pub(crate) struct CachingQueryPlanner { subgraph_schemas: Arc>>>, plugins: Arc, enable_authorization_directives: bool, + experimental_reuse_query_plans: bool, config_mode: ConfigMode, introspection: bool, } @@ -139,6 +140,10 @@ where subgraph_schemas, plugins: Arc::new(plugins), enable_authorization_directives, + experimental_reuse_query_plans: configuration + .supergraph + .query_planning + .experimental_reuse_query_plans, config_mode, introspection: configuration.supergraph.introspection, }) @@ -187,7 +192,6 @@ where metadata, plan_options, config_mode: _, - sdl: _, introspection: _, }, _, @@ -259,18 +263,24 @@ where let caching_key = CachingQueryKey { query: query.clone(), operation: operation.clone(), - hash: doc.hash.clone(), - sdl: Arc::clone(&self.schema.raw_sdl), + hash: if experimental_reuse_query_plans { + CachingQueryHash::Reuse(doc.hash.clone()) + } else { + CachingQueryHash::DoNotReuse { + query_hash: doc.hash.clone(), + schema_hash: self.schema.hash.clone(), + } + }, metadata, plan_options, config_mode: self.config_mode.clone(), introspection: self.introspection, }; - if let Some(hash) = hash { + if let Some(warmup_hash) = hash { if experimental_reuse_query_plans { // if the query hash did not change with the schema update, we can reuse the previously cached entry - if hash == doc.hash { + if warmup_hash.schema_aware_query_hash() == &*doc.hash { if let Some(entry) = { previous_cache.lock().await.get(&caching_key).cloned() } { @@ -280,7 +290,7 @@ where } } } else { - if hash == doc.hash { + if warmup_hash.schema_aware_query_hash() == &*doc.hash { reused += 1; } } @@ -456,8 +466,14 @@ where let caching_key = CachingQueryKey { query: request.query.clone(), operation: request.operation_name.to_owned(), - hash: doc.hash.clone(), - sdl: Arc::clone(&self.schema.raw_sdl), + hash: if self.experimental_reuse_query_plans { + CachingQueryHash::Reuse(doc.hash.clone()) + } else { + CachingQueryHash::DoNotReuse { + query_hash: doc.hash.clone(), + schema_hash: self.schema.hash.clone(), + } + }, metadata, plan_options, config_mode: self.config_mode.clone(), @@ -598,12 +614,11 @@ fn stats_report_key_hash(stats_report_key: &str) -> String { hex::encode(result) } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct CachingQueryKey { pub(crate) query: String, - pub(crate) sdl: Arc, pub(crate) operation: Option, - pub(crate) hash: Arc, + pub(crate) hash: CachingQueryHash, pub(crate) metadata: CacheKeyMetadata, pub(crate) plan_options: PlanOptions, pub(crate) config_mode: ConfigMode, @@ -628,7 +643,6 @@ impl std::fmt::Display for CachingQueryKey { ); hasher .update(&serde_json::to_vec(&self.config_mode).expect("serialization should not fail")); - hasher.update(&serde_json::to_vec(&self.sdl).expect("serialization should not fail")); hasher.update([self.introspection as u8]); let metadata = hex::encode(hasher.finalize()); @@ -640,15 +654,41 @@ impl std::fmt::Display for CachingQueryKey { } } -impl Hash for CachingQueryKey { +// TODO: this is an intermediate type to hold the query hash while query plan reuse is still experimental +// this will be replaced by the schema aware query hash once the option is removed +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum CachingQueryHash { + Reuse(Arc), + DoNotReuse { + query_hash: Arc, + schema_hash: Arc, + }, +} + +impl CachingQueryHash { + fn schema_aware_query_hash(&self) -> &QueryHash { + match self { + CachingQueryHash::Reuse(hash) => &hash, + CachingQueryHash::DoNotReuse { query_hash, .. } => &query_hash, + } + } +} + +impl Hash for CachingQueryHash { fn hash(&self, state: &mut H) { - self.sdl.hash(state); - self.hash.0.hash(state); - self.operation.hash(state); - self.metadata.hash(state); - self.plan_options.hash(state); - self.config_mode.hash(state); - self.introspection.hash(state); + match self { + CachingQueryHash::Reuse(hash) => hash.hash(state), + CachingQueryHash::DoNotReuse { schema_hash, .. } => schema_hash.hash(state), + } + } +} + +impl std::fmt::Display for CachingQueryHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CachingQueryHash::Reuse(hash) => write!(f, "{}", hash), + CachingQueryHash::DoNotReuse { schema_hash, .. } => write!(f, "{}", schema_hash), + } } } @@ -656,7 +696,7 @@ impl Hash for CachingQueryKey { pub(crate) struct WarmUpCachingQueryKey { pub(crate) query: String, pub(crate) operation: Option, - pub(crate) hash: Option>, + pub(crate) hash: Option, pub(crate) metadata: CacheKeyMetadata, pub(crate) plan_options: PlanOptions, pub(crate) config_mode: ConfigMode, diff --git a/apollo-router/src/spec/schema.rs b/apollo-router/src/spec/schema.rs index 3a8eb716fc..2f993bfba2 100644 --- a/apollo-router/src/spec/schema.rs +++ b/apollo-router/src/spec/schema.rs @@ -18,9 +18,9 @@ use crate::configuration::ApiSchemaMode; use crate::configuration::QueryPlannerMode; use crate::error::ParseErrors; use crate::error::SchemaError; +use crate::query_planner::fetch::QueryHash; use crate::query_planner::OperationKind; use crate::Configuration; - /// A GraphQL schema. pub(crate) struct Schema { pub(crate) raw_sdl: Arc, @@ -28,6 +28,7 @@ pub(crate) struct Schema { subgraphs: HashMap, pub(crate) implementers_map: HashMap, api_schema: Option, + pub(crate) hash: Arc, } /// TODO: remove and use apollo_federation::Supergraph unconditionally @@ -128,8 +129,13 @@ impl Schema { Supergraph::ApolloCompiler(definitions) }; + let mut hasher = Sha256::new(); + hasher.update(sdl.as_bytes()); + let hash = Arc::new(QueryHash(hasher.finalize().to_vec())); + Ok(Schema { raw_sdl: Arc::new(sdl.to_owned()), + hash, supergraph, subgraphs, implementers_map, @@ -373,6 +379,7 @@ impl std::fmt::Debug for Schema { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { raw_sdl, + hash: _, supergraph: _, // skip subgraphs, implementers_map, From 22359ed1d0efcad26eb1905ef79a9a514ed48e7d Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 18:48:54 +0200 Subject: [PATCH 20/64] update hashes --- ...__non_overridden_field_yields_expected_query_plan.snap | 2 +- ...ests__overridden_field_yields_expected_query_plan.snap | 4 ++-- ...s__expose_query_plan__tests__it_expose_query_plan.snap | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap index 7e9d0dd729..e09ef4e15f 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap @@ -19,7 +19,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "14000c34c41d03ff28d98f0998ce0c5c6442386f2b1d22ab6981e8309818686d", + "schemaAwareHash": "fd392e17dc04d3a4e3d9bfe1646a9cae63ae582ad8c817ea3a3e7308ec31b6f1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap index ae7f8b8aed..3745741c66 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap @@ -24,7 +24,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "9dabf11779d5fa948462cd48bcb9cf03d7e1de5aea98bbee2582b40ce1af084b", + "schemaAwareHash": "8c7c6bfbf470a204464eebf06d7801475c36d6e4f7dcac76b6f2bd2507a7395b", "authorization": { "is_authenticated": false, "scopes": [], @@ -63,7 +63,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "12be1b17ff6623e99cb2f573e175aa8beb843de0e7ca7e6e9be589be59bd6463", + "schemaAwareHash": "3ec341b8901c2cbb9bfab1d64f3d121472cddb26c82c2dd7834cb9b1e4b99a6a", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap index 33f99cd797..28fecdceb9 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "65edb649e8367062330ab333a77e9f3b481128c15e10118f2a3bc711bffb6f4d", + "schemaAwareHash": "fcf493766d7370fb0a8d02c6065ce48a473ad9c3994d91fea33b4575a084f3cf", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "35636ba91ee32a212ef91b41e578c6524f6b123d483b1e3fb2c923fe401712ce", + "schemaAwareHash": "19b023b07925ee6f22aa10c99bfd16c99f54ed1d4f60abe42ebc409d724a17b4", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "542dcda3a513518024c73bb58c973fb4f4f59b1cbc3e437c2ad9b156d02a06c6", + "schemaAwareHash": "eb4e01d37bde820927b652493955590a42e17d852ede21a022d2692b496c79d7", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "d137af8cb4c932ceab01a0a00b16e615e389cb9b2f8749b9d88233806d984cb4", + "schemaAwareHash": "d058c28467e6eafebbd6003fbc5370a4929ced528ca5c19783b50a81cdc737e5", "authorization": { "is_authenticated": false, "scopes": [], From 4d4769626a0082e801fcbbf39a736597ba58b60b Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 3 Jun 2024 19:22:26 +0200 Subject: [PATCH 21/64] fix integration tests --- ...y_plan__tests__it_expose_query_plan-2.snap | 8 +++--- .../query_planner/caching_query_planner.rs | 2 +- apollo-router/tests/integration/redis.rs | 26 +++++++++---------- ...ts__integration__redis__query_planner.snap | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap index 33f99cd797..28fecdceb9 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "65edb649e8367062330ab333a77e9f3b481128c15e10118f2a3bc711bffb6f4d", + "schemaAwareHash": "fcf493766d7370fb0a8d02c6065ce48a473ad9c3994d91fea33b4575a084f3cf", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "35636ba91ee32a212ef91b41e578c6524f6b123d483b1e3fb2c923fe401712ce", + "schemaAwareHash": "19b023b07925ee6f22aa10c99bfd16c99f54ed1d4f60abe42ebc409d724a17b4", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "542dcda3a513518024c73bb58c973fb4f4f59b1cbc3e437c2ad9b156d02a06c6", + "schemaAwareHash": "eb4e01d37bde820927b652493955590a42e17d852ede21a022d2692b496c79d7", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "d137af8cb4c932ceab01a0a00b16e615e389cb9b2f8749b9d88233806d984cb4", + "schemaAwareHash": "d058c28467e6eafebbd6003fbc5370a4929ced528ca5c19783b50a81cdc737e5", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 7371867339..2278af2366 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -648,7 +648,7 @@ impl std::fmt::Display for CachingQueryKey { write!( f, - "plan:{}:{}:{}:{}:{}", + "plan:cache:{}:federation:{}:query_hash:{}:query:{}:metadata:{}", CACHE_KEY_VERSION, FEDERATION_VERSION, self.hash, operation, metadata, ) } diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 1d1d5bfd73..9dcb9c1c05 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -28,7 +28,7 @@ async fn query_planner() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:0:v2.8.0:16385ebef77959fcdc520ad507eb1f7f7df28f1d54a0569e3adabcb4cd00d7ce:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:9c26cb1f820a78848ba3d5d3295c16aa971368c5295422fd33cc19d4a6006a9c"; + let known_cache_key = "plan:cache:0:federation:v2.8.0:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -410,13 +410,13 @@ async fn entity_cache() -> Result<(), BoxError> { insta::assert_json_snapshot!(response); let s:String = client - .get("subgraph:products:Query:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:products:Query:10d1f6978a1319c1841bcd5ccce4713dbfe47ac3b35a27e54e6a4eeea6963db9: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("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c").await.unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap()); @@ -515,7 +515,7 @@ async fn entity_cache() -> Result<(), BoxError> { insta::assert_json_snapshot!(response); let s:String = client - .get("subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -727,7 +727,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { insta::assert_json_snapshot!(response); let s:String = client - .get("subgraph:products:Query:0df945dc1bc08f7fc02e8905b4c72aa9112f29bb7a214e4a38d199f0aa635b48:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:products:Query:10d1f6978a1319c1841bcd5ccce4713dbfe47ac3b35a27e54e6a4eeea6963db9:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -748,7 +748,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { ); let s: String = client - .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1de543dab57fde0f00247922ccc4f76d4c916ae26a89dd83cd1a62300d0cda20:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -792,7 +792,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("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:9d5e112c16d3ac274d9ca3e455c9b0d5b6eda87d1b65cd7b939782b2ce4a94b2:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -902,7 +902,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:ae8b525534cb7446a34715fc80edd41d4d29aa65c5f39f9237d4ed8459e3fe82", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", ) .await; } @@ -921,7 +921,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", ) .await; } @@ -930,7 +930,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:8a17c5b196af5e3a18d24596424e9849d198f456dd48297b852a5f2ca847169b", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", ) .await; } @@ -941,7 +941,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:275f78612ed3d45cdf6bf328ef83e368b5a44393bd8c944d4a7d694aed61f017", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", ) .await; } @@ -952,7 +952,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:15fbb62c94e8da6ea78f28a6eb86a615dcaf27ff6fd0748fac4eb614b0b17662", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", ) .await; } @@ -972,7 +972,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:0:v2.8.0:a9e605fa09adc5a4b824e690b4de6f160d47d84ede5956b58a7d300cca1f7204:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:1910d63916aae7a1066cb8c7d622fc3a8e363ed1b6ac8e214deed4046abae85c"; + let starting_key = "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; router.update_config(updated_config).await; diff --git a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap index f90305be82..c06f4d4649 100644 --- a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap +++ b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner.snap @@ -13,7 +13,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "121b9859eba2d8fa6dde0a54b6e3781274cf69f7ffb0af912e92c01c6bfff6ca", + "schemaAwareHash": "291280bd442c9013f56b28adeed7edcaca5e9c277c0cd5a650800942af575dec", "authorization": { "is_authenticated": false, "scopes": [], From adb9d11b633c712bb90f0b2838ce60498f1e8980 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 7 Jun 2024 12:38:41 +0200 Subject: [PATCH 22/64] lint --- .../src/query_planner/caching_query_planner.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index c9424ea0a5..b89341fb4d 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -290,10 +290,8 @@ where continue; } } - } else { - if warmup_hash.schema_aware_query_hash() == &*doc.hash { - reused += 1; - } + } else if warmup_hash.schema_aware_query_hash() == &*doc.hash { + reused += 1; } } @@ -669,8 +667,8 @@ pub(crate) enum CachingQueryHash { impl CachingQueryHash { fn schema_aware_query_hash(&self) -> &QueryHash { match self { - CachingQueryHash::Reuse(hash) => &hash, - CachingQueryHash::DoNotReuse { query_hash, .. } => &query_hash, + CachingQueryHash::Reuse(hash) => hash, + CachingQueryHash::DoNotReuse { query_hash, .. } => query_hash, } } } From b8361b9d8aa84870c5e8a2ef3a0ec2aeab789097 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 13 Jun 2024 10:41:49 +0200 Subject: [PATCH 23/64] update snapshots --- .../type_conditions__type_conditions_disabled.snap | 4 ++-- .../snapshots/type_conditions__type_conditions_enabled.snap | 6 +++--- ...s__type_conditions_enabled_generate_query_fragments.snap | 6 +++--- ...pe_conditions__type_conditions_enabled_list_of_list.snap | 6 +++--- ...tions__type_conditions_enabled_list_of_list_of_list.snap | 6 +++--- ...type_conditions_enabled_shouldnt_make_article_fetch.snap | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap index 84b137aa01..1d912557a1 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", + "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", "authorization": { "is_authenticated": false, "scopes": [], @@ -137,7 +137,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", + "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap index e41aeefee5..efac5390ea 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", + "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", + "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", + "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap index d92517b39d..25a289280b 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "844dc4e409cdca1334abe37c347bd4e330123078dd7e65bda8dbb57ea5bdf59c", + "schemaAwareHash": "1114a27b9f431e45eb3f80ab8c3fc5513777ff2ba300f3f8b8d8976c74ca2a54", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "ad82ce0af279c6a012d6b349ff823ba1467902223312aed1cdfc494ec3100b3e", + "schemaAwareHash": "baee3fe9cf95d42ce5ec6394c29a660b2295a4912d0319dd3e279acbd6a53657", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7c267302cf4a44a4463820237830155ab50be32c8860371d8a5c8ca905476360", + "schemaAwareHash": "21986048c49b8db0db0abd4eb955cabaf812510e950cb65a03519e3de6bd6db1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap index acffc62599..4341437051 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "1343b4972ec8be54afe990c69711ce790992a814f9654e34e2ee2b25e4097e45", + "schemaAwareHash": "d3d8c695a449c5cc4d78988ea235e1fd098cecd90557ac57a4aaecec61e14606", "authorization": { "is_authenticated": false, "scopes": [], @@ -204,7 +204,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", + "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", "authorization": { "is_authenticated": false, "scopes": [], @@ -265,7 +265,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", + "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap index 2b8feaafc3..12dc39065c 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap @@ -145,7 +145,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3698f4e74ead34f43a949e1e8459850337a1a07245f8ed627b9203904b4cfff4", + "schemaAwareHash": "1c4f2bcde4241100d497189431b5a7122eb170c27158b92d827e35eef72df93e", "authorization": { "is_authenticated": false, "scopes": [], @@ -209,7 +209,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", + "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", "authorization": { "is_authenticated": false, "scopes": [], @@ -271,7 +271,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", + "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap index 5020d447b4..f9c5006edf 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap @@ -54,7 +54,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0144f144d271437ed45f9d20706be86ffbf1e124d77c7add3db17d4a1498ce97", + "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", "authorization": { "is_authenticated": false, "scopes": [], @@ -116,7 +116,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "23759b36e5149924c757a8b9586adec2c0f6be04ecdf2c3c3ea277446daa690b", + "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", "authorization": { "is_authenticated": false, "scopes": [], @@ -176,7 +176,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "8ee58ad8b4823bcbda9126d2565e1cb04bf91ff250b1098476a1d7614a870121", + "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", "authorization": { "is_authenticated": false, "scopes": [], From ccf9fe277b165bb3ab89deb0a63b418ba252049f Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 13 Jun 2024 14:29:45 +0200 Subject: [PATCH 24/64] update snapshots --- apollo-router/tests/snapshots/set_context__set_context.snap | 4 ++-- .../set_context__set_context_dependent_fetch_failure.snap | 4 ++-- .../tests/snapshots/set_context__set_context_list.snap | 4 ++-- .../snapshots/set_context__set_context_list_of_lists.snap | 4 ++-- .../snapshots/set_context__set_context_no_typenames.snap | 4 ++-- .../snapshots/set_context__set_context_type_mismatch.snap | 4 ++-- .../tests/snapshots/set_context__set_context_union.snap | 6 +++--- .../set_context__set_context_unrelated_fetch_failure.snap | 6 +++--- .../tests/snapshots/set_context__set_context_with_null.snap | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 2e11680753..ee63a2045f 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", + "schemaAwareHash": "455f6e03cb9544abe0e3076b9eaa4bc5a11d4691e37a04e5941586be9a4f8dbf", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", + "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap index 703d8f9c59..afd18515bf 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap @@ -25,7 +25,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_dependent_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "595c36c322602fefc4658fc0070973b51800c2d2debafae5571a7c9811d80745", + "schemaAwareHash": "53ad1565bdd0fcb8158e4e958c55c8c6c341a010eb9a4c13fabb2f9cce763a35", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -71,7 +71,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "37bef7ad43bb477cdec4dfc02446bd2e11a6919dc14ab90e266af85fefde4abd", + "schemaAwareHash": "ac28f531ef4fc2b11cf415b82675d45dfebfc6152825328ccaf536474805f0e3", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index 095326167e..b97ef51e63 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -40,7 +40,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "4f746b9319e3ca4f234269464b6815eb97782f2ffe36774b998e7fb78f30abef", + "schemaAwareHash": "e7ce837dc242e62198e1a3465ecffd4e10d48b81ecc5ca826b50e8c40bbaf2be", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -86,7 +86,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", + "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index e7fbee2a8b..cdcff85049 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -44,7 +44,7 @@ expression: response "operationKind": "query", "operationName": "QueryLL__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "babf88ea82c1330e535966572a55b03a2934097cd1cf905303b86ae7c197ccaf", + "schemaAwareHash": "1da56fd5e416f7c64b8934d7d6cb0a8f6cd830979f175ac5c2e829bc24b863ce", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -90,7 +90,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "a9b24549250c12e38c398c32e9218134fab000be3b934ebc6bb38ea096343646", + "schemaAwareHash": "dd803d1e0cc28031e9033daec89ac7f4e6c9091ea1d1118626435e868f963cf3", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index 8eaa5b0202..93795cda00 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "d7cb2d1809789d49360ca0a60570555f83855f00547675f366915c9d9d90fef9", + "schemaAwareHash": "455f6e03cb9544abe0e3076b9eaa4bc5a11d4691e37a04e5941586be9a4f8dbf", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "66b954f39aead8436321c671eb71e56ce15bbe0c7b82f06b2f8f70473ce1cb6e", + "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap index 1df052723e..9dc20f192e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query_type_mismatch__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "7eae890e61f5ae512e112f5260abe0de3504041c92dbcc7aae0891c9bdf2222b", + "schemaAwareHash": "fe17a252fbf28cf859c19ffe67236434dce672be4bac21107cc5a5bb6d933755", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "d8ea99348ab32931371c85c09565cfb728d2e48cf017201cd79cb9ef860eb9c2", + "schemaAwareHash": "ade9dba9c5d74f1087e7786dc2b69835ec382bada2211c74bea0b38fa2e6c35c", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index e382988a8b..8a69ce3760 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -31,7 +31,7 @@ expression: response "operationKind": "query", "operationName": "QueryUnion__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "b9124cd1daa6e8347175ffe2108670a31c73cbc983e7812ee39f415235541005", + "schemaAwareHash": "0849c0c3aafc3139aa8a19f042f92106c60c449d3d78fe5e83aa5faaf5db4451", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "c50ca82d402a330c1b35a6d76332094c40b00d6dec6f6b2a9b0a32ced68f4e95", + "schemaAwareHash": "d8c7ee8e75f53785e4a7395d313888c263ec51a45cfb816fddea3c7632ea6660", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" @@ -134,7 +134,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "ec99886497fee9b4f13565e19cadb13ae85c83de93acb53f298944b7a29e630e", + "schemaAwareHash": "d9d5051bca849b256d5b4fcad3574457957d0355be177bd813b0754471d38581", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" diff --git a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap index 605fd4570a..c5c589f529 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "1813ba1c272be0201096b4c4c963a07638e4f4b4ac1b97e0d90d634f2fcbac11", + "schemaAwareHash": "7264ccb68c8388997cccb43e107fff40f3e9bdd7ee1e02709088632632c33f16", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -73,7 +73,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "1fdff97ad7facf07690c3e75e3dc7f1b11ff509268ef999250912a728e7a94c9", + "schemaAwareHash": "c39a9ec764eacccb2f3355678b66191a90f226708035965469413d003fcdc13c", "serviceName": "Subgraph2", "variableUsages": [] }, @@ -125,7 +125,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "c9c571eac5df81ff34e5e228934d029ed322640c97ab6ad061cbee3cd81040dc", + "schemaAwareHash": "41214780a532b9afdbfa66880bc79979634838c01222c8c6adf3d0a537b2f3b5", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap index 1e361f0a83..f4bcbc855c 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap @@ -29,7 +29,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "19bd66a3ecc2d9495dffce2279774de3275cb027254289bb61b0c1937a7738b4", + "schemaAwareHash": "79398d32fb9cb2592689c528b891a90c52960618ff94649d88c51d0ab4fbd14e", "authorization": { "is_authenticated": false, "scopes": [], @@ -82,7 +82,7 @@ expression: response "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "010ba25ca76f881bd9f0d5e338f9c07829d4d00e183828b6577d593aea0cf21e", + "schemaAwareHash": "c4d0ef1c2ad2029ccd4c4aef0bcaafacda37bf80520458f05a57531e8ecb325f", "authorization": { "is_authenticated": false, "scopes": [], From c218be89ac9aa8cb053d540d77ebc7a08b420df6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 13 Jun 2024 15:01:40 +0200 Subject: [PATCH 25/64] hash the rust query planner options too --- apollo-router/src/configuration/mod.rs | 15 +++++ .../src/query_planner/bridge_query_planner.rs | 14 +---- .../query_planner/caching_query_planner.rs | 61 +++++++++++++------ apollo-router/tests/integration/redis.rs | 14 ++--- 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 30948da734..677a9fa1a4 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -436,6 +436,21 @@ impl Configuration { type_conditioned_fetching: self.experimental_type_conditioned_fetching, } } + + pub(crate) fn rust_query_planner_config( + &self, + ) -> apollo_federation::query_plan::query_planner::QueryPlannerConfig { + apollo_federation::query_plan::query_planner::QueryPlannerConfig { + reuse_query_fragments: self.supergraph.reuse_query_fragments.unwrap_or(true), + subgraph_graphql_validation: false, + generate_query_fragments: false, + incremental_delivery: + apollo_federation::query_plan::query_planner::QueryPlanIncrementalDeliveryConfig { + enable_defer: self.supergraph.defer_support, + }, + debug: Default::default(), + } + } } impl Default for Configuration { diff --git a/apollo-router/src/query_planner/bridge_query_planner.rs b/apollo-router/src/query_planner/bridge_query_planner.rs index 404b8b17cd..669b236a16 100644 --- a/apollo-router/src/query_planner/bridge_query_planner.rs +++ b/apollo-router/src/query_planner/bridge_query_planner.rs @@ -135,19 +135,7 @@ impl PlannerMode { schema: &Schema, configuration: &Configuration, ) -> Result, ServiceBuildError> { - let config = apollo_federation::query_plan::query_planner::QueryPlannerConfig { - reuse_query_fragments: configuration - .supergraph - .reuse_query_fragments - .unwrap_or(true), - subgraph_graphql_validation: false, - generate_query_fragments: false, - incremental_delivery: - apollo_federation::query_plan::query_planner::QueryPlanIncrementalDeliveryConfig { - enable_defer: configuration.supergraph.defer_support, - }, - debug: Default::default(), - }; + let config = configuration.rust_query_planner_config(); Ok(Arc::new(QueryPlanner::new( schema.federation_supergraph(), config, diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index e5e7e89e07..6c532de6ce 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::hash::Hash; +use std::hash::Hasher; use std::ops::Deref; use std::sync::Arc; use std::task; @@ -12,9 +13,7 @@ use rand::seq::SliceRandom; use rand::thread_rng; use router_bridge::planner::PlanOptions; use router_bridge::planner::Planner; -use router_bridge::planner::QueryPlannerConfig; use router_bridge::planner::UsageReporting; -use serde::Serialize; use sha2::Digest; use sha2::Sha256; use tower::BoxError; @@ -54,15 +53,6 @@ pub(crate) type InMemoryCachePlanner = InMemoryCache>>; pub(crate) const APOLLO_OPERATION_ID: &str = "apollo_operation_id"; -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)] -pub(crate) enum ConfigMode { - //FIXME: add the Rust planner structure once it is hashable and serializable, - // for now use the JS config as it expected to be identical to the Rust one - Rust(Arc), - Both(Arc), - Js(Arc), -} - /// A query planner wrapper that caches results. /// /// The query planner performs LRU caching. @@ -77,7 +67,7 @@ pub(crate) struct CachingQueryPlanner { plugins: Arc, enable_authorization_directives: bool, experimental_reuse_query_plans: bool, - config_mode: ConfigMode, + config_mode: Arc, introspection: bool, } @@ -123,17 +113,23 @@ where let enable_authorization_directives = AuthorizationPlugin::enable_directives(configuration, &schema).unwrap_or(false); - let config_mode = match configuration.experimental_query_planner_mode { + let mut hasher = StructHasher::new(); + match configuration.experimental_query_planner_mode { crate::configuration::QueryPlannerMode::New => { - ConfigMode::Rust(Arc::new(configuration.js_query_planner_config())) + configuration.rust_query_planner_config().hash(&mut hasher); + //hasher.update(&configuration.rust_query_planner_config()); } crate::configuration::QueryPlannerMode::Legacy => { - ConfigMode::Js(Arc::new(configuration.js_query_planner_config())) + configuration.js_query_planner_config().hash(&mut hasher); + + configuration.rust_query_planner_config().hash(&mut hasher); } crate::configuration::QueryPlannerMode::Both => { - ConfigMode::Both(Arc::new(configuration.js_query_planner_config())) + configuration.js_query_planner_config().hash(&mut hasher); } }; + let config_mode = Arc::new(QueryHash(hasher.finalize())); + Ok(Self { cache, delegate, @@ -632,7 +628,7 @@ pub(crate) struct CachingQueryKey { pub(crate) hash: CachingQueryHash, pub(crate) metadata: CacheKeyMetadata, pub(crate) plan_options: PlanOptions, - pub(crate) config_mode: ConfigMode, + pub(crate) config_mode: Arc, pub(crate) introspection: bool, } @@ -652,8 +648,7 @@ impl std::fmt::Display for CachingQueryKey { hasher.update( &serde_json::to_vec(&self.plan_options).expect("serialization should not fail"), ); - hasher - .update(&serde_json::to_vec(&self.config_mode).expect("serialization should not fail")); + hasher.update(&self.config_mode.0); hasher.update([self.introspection as u8]); let metadata = hex::encode(hasher.finalize()); @@ -710,10 +705,36 @@ pub(crate) struct WarmUpCachingQueryKey { pub(crate) hash: Option, pub(crate) metadata: CacheKeyMetadata, pub(crate) plan_options: PlanOptions, - pub(crate) config_mode: ConfigMode, + pub(crate) config_mode: Arc, pub(crate) introspection: bool, } +struct StructHasher { + hasher: Sha256, +} + +impl StructHasher { + fn new() -> Self { + Self { + hasher: Sha256::new(), + } + } + fn finalize(self) -> Vec { + self.hasher.finalize().as_slice().into() + } +} + +impl Hasher for StructHasher { + fn finish(&self) -> u64 { + unreachable!() + } + + fn write(&mut self, bytes: &[u8]) { + self.hasher.update(&[0xFF][..]); + self.hasher.update(bytes); + } +} + #[cfg(test)] mod tests { use mockall::mock; diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 13edea730a..c5fde2f363 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -29,7 +29,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:0:federation:v2.8.0:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3"; + let known_cache_key = "plan:cache:0:federation:v2.8.0:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:97b2d00f7132c7948864de23abe93cccb66e3c92dbb9cbebd750ce509699d6a7"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -903,7 +903,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:54cb0f627030d43c9f1a4abcb5f23fd8565c78bca2fc530c5ff8f99f79d52445", ) .await; } @@ -922,7 +922,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:eacbe6a41cb17c8377b5e0108c304ed896322639a1d2407613449f7eb3be29f2", ) .await; } @@ -931,7 +931,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:2291724e517ee09d6ac45fa2222409f305393c559efb6da03ede72109ed60339", ) .await; @@ -943,7 +943,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:724a16d5ca1f4b753776ea36e3d10784e3cd125bb590dc5e81d4e7cd13c69d1c", ) .await; @@ -955,7 +955,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3", + "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:b11ed6d9f99aa8dd41eb48bfcd4d8592eb6f948a8abaaecfa1b5904766d4d81d", ) .await; @@ -979,7 +979,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:9480a5952736bc172c453c2e7c2189c0ea083380c33cd6b099d5f6f7b5e926b3"; + let starting_key = "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:97b2d00f7132c7948864de23abe93cccb66e3c92dbb9cbebd750ce509699d6a7"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; From 3e539690afcbc2f860bff1774e08f0a64fda967e Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 13 Jun 2024 16:27:34 +0200 Subject: [PATCH 26/64] remove JSON serialization in redis key generation --- .../src/query_planner/caching_query_planner.rs | 14 ++++++-------- apollo-router/tests/integration/redis.rs | 14 +++++++------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 6c532de6ce..03d3229cf6 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -634,7 +634,7 @@ pub(crate) struct CachingQueryKey { // Update this key every time the cache key or the query plan format has to change. // When changed it MUST BE CALLED OUT PROMINENTLY IN THE CHANGELOG. -const CACHE_KEY_VERSION: usize = 0; +const CACHE_KEY_VERSION: usize = 1; const FEDERATION_VERSION: &str = std::env!("FEDERATION_VERSION"); impl std::fmt::Display for CachingQueryKey { @@ -643,13 +643,11 @@ impl std::fmt::Display for CachingQueryKey { hasher.update(self.operation.as_deref().unwrap_or("-")); let operation = hex::encode(hasher.finalize()); - let mut hasher = Sha256::new(); - hasher.update(&serde_json::to_vec(&self.metadata).expect("serialization should not fail")); - hasher.update( - &serde_json::to_vec(&self.plan_options).expect("serialization should not fail"), - ); - hasher.update(&self.config_mode.0); - hasher.update([self.introspection as u8]); + let mut hasher = StructHasher::new(); + self.metadata.hash(&mut hasher); + self.plan_options.hash(&mut hasher); + self.config_mode.hash(&mut hasher); + self.introspection.hash(&mut hasher); let metadata = hex::encode(hasher.finalize()); write!( diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index c5fde2f363..de222255c1 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -29,7 +29,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:0:federation:v2.8.0:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:97b2d00f7132c7948864de23abe93cccb66e3c92dbb9cbebd750ce509699d6a7"; + let known_cache_key = "plan:cache:1:federation:v2.8.0:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -903,7 +903,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:54cb0f627030d43c9f1a4abcb5f23fd8565c78bca2fc530c5ff8f99f79d52445", + "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:1ef4ca9553c8d72dc3308163027fbab0c26e8ff73daa07b1139e49fed01fc5c5", ) .await; } @@ -922,7 +922,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:eacbe6a41cb17c8377b5e0108c304ed896322639a1d2407613449f7eb3be29f2", + "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:783c95a060a2a43f14f0241915247b148c542b9ff0e9238f16bae2cc172ac427", ) .await; } @@ -931,7 +931,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:2291724e517ee09d6ac45fa2222409f305393c559efb6da03ede72109ed60339", + "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:c632e2e80594b3b8b8e7bf13ee5f2d3e3e1f6cb050d41a8d751b2c2de3e60906", ) .await; @@ -943,7 +943,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:724a16d5ca1f4b753776ea36e3d10784e3cd125bb590dc5e81d4e7cd13c69d1c", + "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:25dbfa37dc81a68cb3d2302ed470a0dcd5596b23cacd20f4b1e295b98f45239c", ) .await; @@ -955,7 +955,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:b11ed6d9f99aa8dd41eb48bfcd4d8592eb6f948a8abaaecfa1b5904766d4d81d", + "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:8e23b92bb8eb456dd7596dda44f26de31b8b06349549a157bfb7c2d1b492ecc4", ) .await; @@ -979,7 +979,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:cache:0:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:97b2d00f7132c7948864de23abe93cccb66e3c92dbb9cbebd750ce509699d6a7"; + let starting_key = "plan:cache:1:federation:v2.8.0:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; From 935ecf91eacc0bf528fa9638d0cf28350e08f0bb Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 18 Jun 2024 18:26:41 +0200 Subject: [PATCH 27/64] update separators for default_valur --- apollo-router/src/spec/query/change.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index ca103f5015..e4733c65e4 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -360,7 +360,7 @@ impl<'a> QueryHashVisitor<'a> { if let Some(value) = t.default_value.as_ref() { self.hash_value(value); } else { - "no default value".hash(self); + "^INPUT_VALUE-NO_DEFAULT".hash(self); } "^INPUT_VALUE-END".hash(self); Ok(()) @@ -480,7 +480,7 @@ impl<'a> Visitor for QueryHashVisitor<'a> { if let Some(value) = variable.default_value.as_ref() { self.hash_value(value); } else { - "no default value".hash(self); + "^VISIT_OPERATION-NO_DEFAULT".hash(self); } for directive in &variable.directives { From b4ce95abba52015a538f1f03a5e39aa0be0b5ce6 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 18 Jun 2024 19:03:14 +0200 Subject: [PATCH 28/64] update snapshots --- ...dden_field_yields_expected_query_plan.snap | 2 +- ...dden_field_yields_expected_query_plan.snap | 4 +- ...y_plan__tests__it_expose_query_plan-2.snap | 8 +-- ...ery_plan__tests__it_expose_query_plan.snap | 8 +-- ...ridge_query_planner__tests__plan_root.snap | 2 +- apollo-router/tests/integration/redis.rs | 69 +++++++++++++++---- ...tegration__redis__query_planner_cache.snap | 2 +- 7 files changed, 67 insertions(+), 28 deletions(-) diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap index e09ef4e15f..8e031a2a55 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap @@ -19,7 +19,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "fd392e17dc04d3a4e3d9bfe1646a9cae63ae582ad8c817ea3a3e7308ec31b6f1", + "schemaAwareHash": "9b44446744b4cdb1a063511c35cb78fa79c519ac43671b1b60096b0984a17244", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap index 3745741c66..620fba5853 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap @@ -24,7 +24,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "8c7c6bfbf470a204464eebf06d7801475c36d6e4f7dcac76b6f2bd2507a7395b", + "schemaAwareHash": "d5ed8e0726a1b6f88dcd89ddf57620f3f878eecf169a01a921fa6f4efc2529f5", "authorization": { "is_authenticated": false, "scopes": [], @@ -63,7 +63,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3ec341b8901c2cbb9bfab1d64f3d121472cddb26c82c2dd7834cb9b1e4b99a6a", + "schemaAwareHash": "d9d748cdc6d6e7a1e85d2ad07a24e749b15ccfac02aefe28b8acf6e190796fd4", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap index 28fecdceb9..066d4a5f6f 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "fcf493766d7370fb0a8d02c6065ce48a473ad9c3994d91fea33b4575a084f3cf", + "schemaAwareHash": "527383ea0a0ef3ac37b096a8f1e5cfc0208a358ec45af6e41e5354459bf22768", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "19b023b07925ee6f22aa10c99bfd16c99f54ed1d4f60abe42ebc409d724a17b4", + "schemaAwareHash": "a85be34ef80bc1f92f03839733e8488c87f76b7b7fec0dbab3aabb9022392d95", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "eb4e01d37bde820927b652493955590a42e17d852ede21a022d2692b496c79d7", + "schemaAwareHash": "534733f432a250d11d412d158534cde6409ab67e77a8d56b39d66a2508816d42", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "d058c28467e6eafebbd6003fbc5370a4929ced528ca5c19783b50a81cdc737e5", + "schemaAwareHash": "242d0b3a066d67c9e80151b6ddf8996964ebae0ef78757bb6ea6d1e1c6a61819", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap index 28fecdceb9..066d4a5f6f 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "fcf493766d7370fb0a8d02c6065ce48a473ad9c3994d91fea33b4575a084f3cf", + "schemaAwareHash": "527383ea0a0ef3ac37b096a8f1e5cfc0208a358ec45af6e41e5354459bf22768", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "19b023b07925ee6f22aa10c99bfd16c99f54ed1d4f60abe42ebc409d724a17b4", + "schemaAwareHash": "a85be34ef80bc1f92f03839733e8488c87f76b7b7fec0dbab3aabb9022392d95", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "eb4e01d37bde820927b652493955590a42e17d852ede21a022d2692b496c79d7", + "schemaAwareHash": "534733f432a250d11d412d158534cde6409ab67e77a8d56b39d66a2508816d42", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "d058c28467e6eafebbd6003fbc5370a4929ced528ca5c19783b50a81cdc737e5", + "schemaAwareHash": "242d0b3a066d67c9e80151b6ddf8996964ebae0ef78757bb6ea6d1e1c6a61819", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap index a3d8353ba2..7b64679206 100644 --- a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap @@ -15,7 +15,7 @@ Fetch( output_rewrites: None, context_rewrites: None, schema_aware_hash: QueryHash( - "d2f2b08f11b9533d22b1c531c33c4f488f6879207efb695f1719079a76669ec2", + "0dbec8ab189f259ab0c4d1dbcfd7d505867ac6ad22965e9051b5f80bb88f008a", ), authorization: CacheKeyMetadata { is_authenticated: false, diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index de222255c1..3b0e5a9510 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -410,14 +410,35 @@ async fn entity_cache() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let s:String = client - .get("subgraph:products:Query:10d1f6978a1319c1841bcd5ccce4713dbfe47ac3b35a27e54e6a4eeea6963db9:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") - .await - .unwrap(); + let cache_key = "subgraph:products:Query:de16db3b7eca8c9c1471657c634153d01b70d543a416ecc2042c7d870a1fcee1:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let s: String = match client.get(cache_key).await { + Ok(s) => s, + Err(e) => { + println!("keys in Redis server:"); + let mut scan = client.scan("subgraph:*", Some(u32::MAX), Some(ScanType::String)); + while let Some(key) = scan.next().await { + let key = key.as_ref().unwrap().results(); + println!("\t{key:?}"); + } + panic!("key {cache_key} not found: {e}\nIf you see this error, make sure the federation version you use matches the redis key."); + } + }; 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:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c").await.unwrap(); + let cache_key = "subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1173e6258da397d6b6c497968fea69d06aeba0e0fce17e69b6419f24ab18d4dd:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let s: String = match client.get(cache_key).await { + Ok(s) => s, + Err(e) => { + println!("keys in Redis server:"); + let mut scan = client.scan("subgraph:*", Some(u32::MAX), Some(ScanType::String)); + while let Some(key) = scan.next().await { + let key = key.as_ref().unwrap().results(); + println!("\t{key:?}"); + } + panic!("key {cache_key} not found: {e}\nIf you see this error, make sure the federation version you use matches the redis key."); + } + }; let v: Value = serde_json::from_str(&s).unwrap(); insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap()); @@ -515,10 +536,19 @@ async fn entity_cache() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let s:String = client - .get("subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") - .await - .unwrap(); + let cache_key = "subgraph:reviews:Product:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:1173e6258da397d6b6c497968fea69d06aeba0e0fce17e69b6419f24ab18d4dd:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let s: String = match client.get(cache_key).await { + Ok(s) => s, + Err(e) => { + println!("keys in Redis server:"); + let mut scan = client.scan("subgraph:*", Some(u32::MAX), Some(ScanType::String)); + while let Some(key) = scan.next().await { + let key = key.as_ref().unwrap().results(); + println!("\t{key:?}"); + } + panic!("key {cache_key} not found: {e}\nIf you see this error, make sure the federation version you use matches the redis key."); + } + }; let v: Value = serde_json::from_str(&s).unwrap(); insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap()); @@ -727,10 +757,19 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let s:String = client - .get("subgraph:products:Query:10d1f6978a1319c1841bcd5ccce4713dbfe47ac3b35a27e54e6a4eeea6963db9:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") - .await - .unwrap(); + let cache_key = "subgraph:products:Query:de16db3b7eca8c9c1471657c634153d01b70d543a416ecc2042c7d870a1fcee1:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let s: String = match client.get(cache_key).await { + Ok(s) => s, + Err(e) => { + println!("keys in Redis server:"); + let mut scan = client.scan("plan:*", Some(u32::MAX), Some(ScanType::String)); + while let Some(key) = scan.next().await { + let key = key.as_ref().unwrap().results(); + println!("\t{key:?}"); + } + panic!("key {cache_key} not found: {e}\nIf you see this error, make sure the federation version you use matches the redis key."); + } + }; let v: Value = serde_json::from_str(&s).unwrap(); assert_eq!( v.as_object().unwrap().get("data").unwrap(), @@ -749,7 +788,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { ); let s: String = client - .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:162f839e85338ad22f6d23c1caccba94f2eee899e91b618ad2e7af9181c5eff6:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:1173e6258da397d6b6c497968fea69d06aeba0e0fce17e69b6419f24ab18d4dd:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -793,7 +832,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { insta::assert_json_snapshot!(response); let s:String = client - .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:9d5e112c16d3ac274d9ca3e455c9b0d5b6eda87d1b65cd7b939782b2ce4a94b2:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("subgraph:reviews:Product:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:716216b556dec381ea72d96361bb68c506bd8bac5b8a27fa6afbef37f19ee7bb:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); diff --git a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap index c06f4d4649..5eb6e78a76 100644 --- a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap +++ b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap @@ -13,7 +13,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "291280bd442c9013f56b28adeed7edcaca5e9c277c0cd5a650800942af575dec", + "schemaAwareHash": "72bc70bb20ff36db19f715992f76f436894578d2fdafffbcd22ce648f4487658", "authorization": { "is_authenticated": false, "scopes": [], From edca1bc7f7b4f81e89567727cf78f97233c45140 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 24 Jun 2024 14:33:57 +0200 Subject: [PATCH 29/64] update snapshots --- .../type_conditions__type_conditions_disabled.snap | 4 ++-- .../snapshots/type_conditions__type_conditions_enabled.snap | 6 +++--- ...s__type_conditions_enabled_generate_query_fragments.snap | 6 +++--- ...pe_conditions__type_conditions_enabled_list_of_list.snap | 6 +++--- ...tions__type_conditions_enabled_list_of_list_of_list.snap | 6 +++--- ...type_conditions_enabled_shouldnt_make_article_fetch.snap | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap index 1d912557a1..80e936b17d 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", + "schemaAwareHash": "9b054568bddf1217eb42d169f54e194096e22aea75cd638b44b54bc82c4f27f8", "authorization": { "is_authenticated": false, "scopes": [], @@ -137,7 +137,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", + "schemaAwareHash": "ab9dbc0f8fc1d50e6a977a88ef8bcefbbf94d21b9a9ab1ac4b2c2022ab31c8aa", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap index efac5390ea..3f5fb6944f 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", + "schemaAwareHash": "9b054568bddf1217eb42d169f54e194096e22aea75cd638b44b54bc82c4f27f8", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", + "schemaAwareHash": "ab9dbc0f8fc1d50e6a977a88ef8bcefbbf94d21b9a9ab1ac4b2c2022ab31c8aa", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", + "schemaAwareHash": "1aaf2176e0e5f61d096650e8b47fe6b776ec729af07ff11ee64f57deafe485c1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap index 25a289280b..2887cdcfb1 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "1114a27b9f431e45eb3f80ab8c3fc5513777ff2ba300f3f8b8d8976c74ca2a54", + "schemaAwareHash": "02ac878417595ad357d1d3919d52f55b1f9d3835c80997266a7e2b5b123649c8", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "baee3fe9cf95d42ce5ec6394c29a660b2295a4912d0319dd3e279acbd6a53657", + "schemaAwareHash": "7f86b775337e7e94ddb551cb1b856507b75f6c91e3a84201268656a71bda4cf3", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "21986048c49b8db0db0abd4eb955cabaf812510e950cb65a03519e3de6bd6db1", + "schemaAwareHash": "e4f1726f70fe5224da2bce89ffa3eee89eb2965fbec4aebff96e5d410f0327c1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap index 4341437051..fdb1a7cfb5 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "d3d8c695a449c5cc4d78988ea235e1fd098cecd90557ac57a4aaecec61e14606", + "schemaAwareHash": "a73065fd4d8f841eb87ba08872814df522fad0afdd441994fcfe9eaca1024e8e", "authorization": { "is_authenticated": false, "scopes": [], @@ -204,7 +204,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", + "schemaAwareHash": "ab9dbc0f8fc1d50e6a977a88ef8bcefbbf94d21b9a9ab1ac4b2c2022ab31c8aa", "authorization": { "is_authenticated": false, "scopes": [], @@ -265,7 +265,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", + "schemaAwareHash": "1aaf2176e0e5f61d096650e8b47fe6b776ec729af07ff11ee64f57deafe485c1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap index 12dc39065c..951f295700 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap @@ -145,7 +145,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "1c4f2bcde4241100d497189431b5a7122eb170c27158b92d827e35eef72df93e", + "schemaAwareHash": "9bcc2ea8d4e5f253c2634cc004abf610f6f605d743a96055ffd29570499d00dd", "authorization": { "is_authenticated": false, "scopes": [], @@ -209,7 +209,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", + "schemaAwareHash": "ab9dbc0f8fc1d50e6a977a88ef8bcefbbf94d21b9a9ab1ac4b2c2022ab31c8aa", "authorization": { "is_authenticated": false, "scopes": [], @@ -271,7 +271,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", + "schemaAwareHash": "1aaf2176e0e5f61d096650e8b47fe6b776ec729af07ff11ee64f57deafe485c1", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap index f9c5006edf..2f33a338f6 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap @@ -54,7 +54,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "41436d0927d84707949dcd40b1e491260fdcf6acccf32048d69be4827e3d8d1a", + "schemaAwareHash": "9b054568bddf1217eb42d169f54e194096e22aea75cd638b44b54bc82c4f27f8", "authorization": { "is_authenticated": false, "scopes": [], @@ -116,7 +116,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "db9baa300e0e5598c6c8d4d58f5e0ba3a0e8b497524cdd397d206c8f56d70f75", + "schemaAwareHash": "ab9dbc0f8fc1d50e6a977a88ef8bcefbbf94d21b9a9ab1ac4b2c2022ab31c8aa", "authorization": { "is_authenticated": false, "scopes": [], @@ -176,7 +176,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "2e95165c1544191220460f13ca702c27aa1c45370db13833bcf8f62c1f3ec222", + "schemaAwareHash": "1aaf2176e0e5f61d096650e8b47fe6b776ec729af07ff11ee64f57deafe485c1", "authorization": { "is_authenticated": false, "scopes": [], From 56ce0dbf0d47b085f8b9ae6c22f937c90ccdff6c Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 24 Jun 2024 15:52:25 +0200 Subject: [PATCH 30/64] update SetContext snapshots --- apollo-router/tests/snapshots/set_context__set_context.snap | 4 ++-- .../set_context__set_context_dependent_fetch_failure.snap | 4 ++-- .../tests/snapshots/set_context__set_context_list.snap | 4 ++-- .../snapshots/set_context__set_context_list_of_lists.snap | 4 ++-- .../snapshots/set_context__set_context_no_typenames.snap | 4 ++-- .../snapshots/set_context__set_context_type_mismatch.snap | 4 ++-- .../tests/snapshots/set_context__set_context_union.snap | 6 +++--- .../set_context__set_context_unrelated_fetch_failure.snap | 6 +++--- .../tests/snapshots/set_context__set_context_with_null.snap | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index ee63a2045f..15367649a1 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "455f6e03cb9544abe0e3076b9eaa4bc5a11d4691e37a04e5941586be9a4f8dbf", + "schemaAwareHash": "acade90fff8745b7a0e64b4099484ac89418e700359b2557908916e3ea0cf48f", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", + "schemaAwareHash": "928c213d570e4ae9b6cf8e672be69a2cc23cba2b1770c105af7d7aa282bf6796", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap index afd18515bf..6d4e23d429 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap @@ -25,7 +25,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_dependent_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "53ad1565bdd0fcb8158e4e958c55c8c6c341a010eb9a4c13fabb2f9cce763a35", + "schemaAwareHash": "0b5e851f362b3b2b310df4e8c3f72e8062a2f940566cbc798873b27cd1be6fe3", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -71,7 +71,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "ac28f531ef4fc2b11cf415b82675d45dfebfc6152825328ccaf536474805f0e3", + "schemaAwareHash": "e2f85c2dae4a2cdc237705789924e28e9b599958cc68ac564d8e0f4847b66bb5", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index b97ef51e63..028f0887b3 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -40,7 +40,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "e7ce837dc242e62198e1a3465ecffd4e10d48b81ecc5ca826b50e8c40bbaf2be", + "schemaAwareHash": "357ae007b1edf8e01f3c24576f3b6318dff623b2a353c8ab22f5ba17c03b5cfd", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -86,7 +86,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", + "schemaAwareHash": "928c213d570e4ae9b6cf8e672be69a2cc23cba2b1770c105af7d7aa282bf6796", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index cdcff85049..3c92d600ab 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -44,7 +44,7 @@ expression: response "operationKind": "query", "operationName": "QueryLL__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "1da56fd5e416f7c64b8934d7d6cb0a8f6cd830979f175ac5c2e829bc24b863ce", + "schemaAwareHash": "8390c37f635cb49ef0a72ce8549af110c1e424cf1e4c4268b7b77c1587ad9b75", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -90,7 +90,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "dd803d1e0cc28031e9033daec89ac7f4e6c9091ea1d1118626435e868f963cf3", + "schemaAwareHash": "e81043725f620ca6a3981b9a38655a48aaae950542b01adfb47a35fdc9ddb515", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index 93795cda00..61323f48d2 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "455f6e03cb9544abe0e3076b9eaa4bc5a11d4691e37a04e5941586be9a4f8dbf", + "schemaAwareHash": "acade90fff8745b7a0e64b4099484ac89418e700359b2557908916e3ea0cf48f", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "9fe64db3ff3104c82a913c515ae3a6993ee3833ecd0322351d277c963724ab72", + "schemaAwareHash": "928c213d570e4ae9b6cf8e672be69a2cc23cba2b1770c105af7d7aa282bf6796", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap index 9dc20f192e..2698fd488e 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query_type_mismatch__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "fe17a252fbf28cf859c19ffe67236434dce672be4bac21107cc5a5bb6d933755", + "schemaAwareHash": "4a9c30a02e37610bb4250522aee7966df311d974497f69c3cfa8575888efbe77", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "ade9dba9c5d74f1087e7786dc2b69835ec382bada2211c74bea0b38fa2e6c35c", + "schemaAwareHash": "98fc26e79ed09899e97e06746ff32d2b12f34d900daa2df0e4f7ec62865b8e2a", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index 8a69ce3760..c85b9ecdf8 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -31,7 +31,7 @@ expression: response "operationKind": "query", "operationName": "QueryUnion__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "0849c0c3aafc3139aa8a19f042f92106c60c449d3d78fe5e83aa5faaf5db4451", + "schemaAwareHash": "b16b2cfb58581d40a2fed656dbd4453230bdc1d4a02de3dcfbba44bd31596982", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "d8c7ee8e75f53785e4a7395d313888c263ec51a45cfb816fddea3c7632ea6660", + "schemaAwareHash": "f2c8b8e0f780b9b27b0150db8a8499f1e18799a16955f54f5300b02883320b0f", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" @@ -134,7 +134,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "d9d5051bca849b256d5b4fcad3574457957d0355be177bd813b0754471d38581", + "schemaAwareHash": "3cda39493e006fdba051bb1e1dfa442ec11481730b9a1ae38bf2ea6be9f65d28", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" diff --git a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap index c5c589f529..654b463f91 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "7264ccb68c8388997cccb43e107fff40f3e9bdd7ee1e02709088632632c33f16", + "schemaAwareHash": "b0d0dcdc5b6ea7ace2e28e1213815864c5b08459e30397a88fb453f23464129d", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -73,7 +73,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "c39a9ec764eacccb2f3355678b66191a90f226708035965469413d003fcdc13c", + "schemaAwareHash": "5cf7c273012ee3e34ca5920fa165b96fe627810a7bd2b58bffa9ffb6fc3ab9a4", "serviceName": "Subgraph2", "variableUsages": [] }, @@ -125,7 +125,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "41214780a532b9afdbfa66880bc79979634838c01222c8c6adf3d0a537b2f3b5", + "schemaAwareHash": "f3a668cab683abee57c05ba45a8ae14820a45684c55e45322c1f25ef1571d56d", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap index f4bcbc855c..7ef49de985 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap @@ -29,7 +29,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "79398d32fb9cb2592689c528b891a90c52960618ff94649d88c51d0ab4fbd14e", + "schemaAwareHash": "a5d5145b852929f145a34f35cf467f156d9e9e57c1b179c7bf9f4c88b80a32ae", "authorization": { "is_authenticated": false, "scopes": [], @@ -82,7 +82,7 @@ expression: response "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "c4d0ef1c2ad2029ccd4c4aef0bcaafacda37bf80520458f05a57531e8ecb325f", + "schemaAwareHash": "9aa5718c2fd338a3c5177ab5b2e4343eb0980f21956dcb3d3a138793f254a279", "authorization": { "is_authenticated": false, "scopes": [], From 049834e0f2ab52e2200ceb1d8aabb223f2292c8e Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 27 Jun 2024 09:36:07 +0200 Subject: [PATCH 31/64] fix log message --- .../src/query_planner/caching_query_planner.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index ee3bb8841f..64169676ca 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -225,10 +225,16 @@ where } else { cache_keys.len() }; - tracing::info!( - "warming up the query plan cache with {} queries, this might take a while", - capacity - ); + + if capacity == 0 { + tracing::info!("no queries available to warm up the query plan cache",); + return; + } else { + tracing::info!( + "warming up the query plan cache with {} queries, this might take a while", + capacity + ); + } // persisted queries are added first because they should get a lower priority in the LRU cache, // since a lot of them may be there to support old clients From 52bee1d3b5e30c971a6e00edbbcb70ed2f3376fc Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 09:42:19 +0200 Subject: [PATCH 32/64] docs and changeset --- .changesets/fix_geal_fix_hashing_algorithm.md | 25 +++++++++++++++++++ .../configuration/in-memory-caching.mdx | 2 ++ 2 files changed, 27 insertions(+) create mode 100644 .changesets/fix_geal_fix_hashing_algorithm.md diff --git a/.changesets/fix_geal_fix_hashing_algorithm.md b/.changesets/fix_geal_fix_hashing_algorithm.md new file mode 100644 index 0000000000..67319160c2 --- /dev/null +++ b/.changesets/fix_geal_fix_hashing_algorithm.md @@ -0,0 +1,25 @@ +### Fix cache key hashing algorithm ([Issue #5160](https://github.com/apollographql/router/issues/5160)) + + + +The Router contains a schema aware query hashing algorithm, designed to return the same hash across schema updates if they do not affect the query. It was used mainly to avoid planning a lot of queries when warming up the query plan cache. It was deactivated due to a regression. This reintroduces this algorithm, with a serie of reliability and performance fixes, along with better observability. + +The hashing algorithm can be activated in configuration, if query plan cache warm up is already enabled: + +```yaml title="router.yaml" +supergraph: + query_planning: + warmed_up_queries: 100 + experimental_reuse_query_plans: true +``` + +There is a counter metric named `apollo.router.query.planning.warmup.reused` that can be used to track the hashing algorithm benefits: +- if the `experimental_reuse_query_plans` option is false, the `query_plan_reuse_active` metric attribute will be false. Cache warm up will not reuse query plans according to the algorithm, but it will evaluate if some of them could have been reused and report that in the metric +- if the `experimental_reuse_query_plans` option is true, then the `query_plan_reuse_active` metric attribute will be true + +Fixes included in this change: +- strenghten the hashing algorithm to prevent collisions. In particular, the query string is always hashed in, making sure that different queries cannot get the same hash +- remove inefficiencies in cache key generation +- use prefixes for each part of the Redis cache key, so they become self describing + +By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/5255 \ No newline at end of file diff --git a/docs/source/configuration/in-memory-caching.mdx b/docs/source/configuration/in-memory-caching.mdx index 4e483906a6..115c85c4ed 100644 --- a/docs/source/configuration/in-memory-caching.mdx +++ b/docs/source/configuration/in-memory-caching.mdx @@ -102,6 +102,8 @@ supergraph: experimental_reuse_query_plans: true ``` +To check if the Router deployment would benefit from this option, check the counter metric named `apollo.router.query.planning.warmup.reused` that indicates how many query plans were reused in warmup if its `query_plan_reuse_active` is true, or how many query plans would have been reused if the option was active, when `query_plan_reuse_active` is false. + ## Caching automatic persisted queries (APQ) [Automatic Persisted Queries (**APQ**)](/apollo-server/performance/apq/) enable GraphQL clients to send a server the _hash_ of their query string, _instead of_ sending the query string itself. When query strings are very large, this can significantly reduce network usage. From df38cec5cfad389204527a12fc41c28ce047c05b Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 09:43:24 +0200 Subject: [PATCH 33/64] update changeset --- .changesets/fix_geal_fix_hashing_algorithm.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.changesets/fix_geal_fix_hashing_algorithm.md b/.changesets/fix_geal_fix_hashing_algorithm.md index 67319160c2..9e8589a96e 100644 --- a/.changesets/fix_geal_fix_hashing_algorithm.md +++ b/.changesets/fix_geal_fix_hashing_algorithm.md @@ -1,6 +1,7 @@ ### Fix cache key hashing algorithm ([Issue #5160](https://github.com/apollographql/router/issues/5160)) - +> [!IMPORTANT] +> If you have enabled [Distributed query plan caching](https://www.apollographql.com/docs/router/configuration/distributed-caching/#distributed-query-plan-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. The Router contains a schema aware query hashing algorithm, designed to return the same hash across schema updates if they do not affect the query. It was used mainly to avoid planning a lot of queries when warming up the query plan cache. It was deactivated due to a regression. This reintroduces this algorithm, with a serie of reliability and performance fixes, along with better observability. @@ -22,4 +23,4 @@ Fixes included in this change: - remove inefficiencies in cache key generation - use prefixes for each part of the Redis cache key, so they become self describing -By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/5255 \ No newline at end of file +By [@Geal](https://github.com/Geal) in https://github.com/apollographql/router/pull/5255 From 42a7dfe855b150f9261f4825962c167bdbedfcab Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 10:27:24 +0200 Subject: [PATCH 34/64] use both schema hash and query hash --- apollo-router/src/query_planner/caching_query_planner.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 64169676ca..9afa1a3628 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -688,7 +688,10 @@ impl Hash for CachingQueryHash { fn hash(&self, state: &mut H) { match self { CachingQueryHash::Reuse(hash) => hash.hash(state), - CachingQueryHash::DoNotReuse { schema_hash, .. } => schema_hash.hash(state), + CachingQueryHash::DoNotReuse { schema_hash, query_hash } => { + schema_hash.hash(state); + query_hash.hash(state); + } } } } @@ -697,7 +700,7 @@ impl std::fmt::Display for CachingQueryHash { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { CachingQueryHash::Reuse(hash) => write!(f, "{}", hash), - CachingQueryHash::DoNotReuse { schema_hash, .. } => write!(f, "{}", schema_hash), + CachingQueryHash::DoNotReuse { schema_hash, query_hash } => write!(f, "{}:{}", schema_hash, query_hash), } } } From f80c1e77e206010aec78f22825a18b251285f01f Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 11:17:00 +0200 Subject: [PATCH 35/64] more precise way to write the redis cache key --- .../src/query_planner/caching_query_planner.rs | 6 +++--- apollo-router/tests/integration/redis.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 9afa1a3628..f1eaed197f 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -658,7 +658,7 @@ impl std::fmt::Display for CachingQueryKey { write!( f, - "plan:cache:{}:federation:{}:query_hash:{}:query:{}:metadata:{}", + "plan:cache:{}:federation:{}:{}:opname:{}:metadata:{}", CACHE_KEY_VERSION, FEDERATION_VERSION, self.hash, operation, metadata, ) } @@ -699,8 +699,8 @@ impl Hash for CachingQueryHash { impl std::fmt::Display for CachingQueryHash { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - CachingQueryHash::Reuse(hash) => write!(f, "{}", hash), - CachingQueryHash::DoNotReuse { schema_hash, query_hash } => write!(f, "{}:{}", schema_hash, query_hash), + CachingQueryHash::Reuse(hash) => write!(f, "query:{}", hash), + CachingQueryHash::DoNotReuse { schema_hash, query_hash } => write!(f, "schema:{}:query:{}", schema_hash, query_hash), } } } diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index e1951c23f0..878b777d19 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -29,7 +29,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:1:federation:v2.8.1:query_hash:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; + let known_cache_key = "plan:cache:1:federation:v2.8.1:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); @@ -957,7 +957,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:1ef4ca9553c8d72dc3308163027fbab0c26e8ff73daa07b1139e49fed01fc5c5", + "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:1ef4ca9553c8d72dc3308163027fbab0c26e8ff73daa07b1139e49fed01fc5c5", ) .await; } @@ -976,7 +976,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:783c95a060a2a43f14f0241915247b148c542b9ff0e9238f16bae2cc172ac427", + "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:783c95a060a2a43f14f0241915247b148c542b9ff0e9238f16bae2cc172ac427", ) .await; } @@ -985,7 +985,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:c632e2e80594b3b8b8e7bf13ee5f2d3e3e1f6cb050d41a8d751b2c2de3e60906", + "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:c632e2e80594b3b8b8e7bf13ee5f2d3e3e1f6cb050d41a8d751b2c2de3e60906", ) .await; @@ -997,7 +997,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:25dbfa37dc81a68cb3d2302ed470a0dcd5596b23cacd20f4b1e295b98f45239c", + "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:25dbfa37dc81a68cb3d2302ed470a0dcd5596b23cacd20f4b1e295b98f45239c", ) .await; @@ -1009,7 +1009,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:8e23b92bb8eb456dd7596dda44f26de31b8b06349549a157bfb7c2d1b492ecc4", + "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:8e23b92bb8eb456dd7596dda44f26de31b8b06349549a157bfb7c2d1b492ecc4", ) .await; @@ -1033,7 +1033,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:cache:1:federation:v2.8.1:query_hash:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; + let starting_key = "plan:cache:1:federation:v2.8.1:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; From 4b492de607d5f7f5a29c5217f94fdabd3ff177cc Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 11:38:33 +0200 Subject: [PATCH 36/64] fmt --- .../src/query_planner/caching_query_planner.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index f1eaed197f..504ba4488b 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -688,7 +688,10 @@ impl Hash for CachingQueryHash { fn hash(&self, state: &mut H) { match self { CachingQueryHash::Reuse(hash) => hash.hash(state), - CachingQueryHash::DoNotReuse { schema_hash, query_hash } => { + CachingQueryHash::DoNotReuse { + schema_hash, + query_hash, + } => { schema_hash.hash(state); query_hash.hash(state); } @@ -700,7 +703,10 @@ impl std::fmt::Display for CachingQueryHash { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { CachingQueryHash::Reuse(hash) => write!(f, "query:{}", hash), - CachingQueryHash::DoNotReuse { schema_hash, query_hash } => write!(f, "schema:{}:query:{}", schema_hash, query_hash), + CachingQueryHash::DoNotReuse { + schema_hash, + query_hash, + } => write!(f, "schema:{}:query:{}", schema_hash, query_hash), } } } From 183bc4c6f00183f1b0d97486c2dc6b1604161e42 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 16:13:01 +0200 Subject: [PATCH 37/64] Update apollo-router/src/query_planner/caching_query_planner.rs Co-authored-by: Jeremy Lempereur --- apollo-router/src/query_planner/caching_query_planner.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 504ba4488b..0aec909edd 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -117,7 +117,6 @@ where match configuration.experimental_query_planner_mode { crate::configuration::QueryPlannerMode::New => { configuration.rust_query_planner_config().hash(&mut hasher); - //hasher.update(&configuration.rust_query_planner_config()); } crate::configuration::QueryPlannerMode::Legacy => { configuration.js_query_planner_config().hash(&mut hasher); From b4ed22a382b99a2370ff0c95aff34bba7a288f3a Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 16:13:29 +0200 Subject: [PATCH 38/64] Update apollo-router/src/query_planner/caching_query_planner.rs Co-authored-by: Jeremy Lempereur --- apollo-router/src/query_planner/caching_query_planner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 0aec909edd..52c3fe692f 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -226,7 +226,7 @@ where }; if capacity == 0 { - tracing::info!("no queries available to warm up the query plan cache",); + tracing::info!("no queries available to warm up the query plan cache"); return; } else { tracing::info!( From 1a462111c479160da638918687b207983c92bad8 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 1 Jul 2024 16:56:47 +0200 Subject: [PATCH 39/64] fix config hashing --- apollo-router/src/query_planner/caching_query_planner.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 52c3fe692f..56975746c5 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -120,11 +120,10 @@ where } crate::configuration::QueryPlannerMode::Legacy => { configuration.js_query_planner_config().hash(&mut hasher); - - configuration.rust_query_planner_config().hash(&mut hasher); } crate::configuration::QueryPlannerMode::Both => { configuration.js_query_planner_config().hash(&mut hasher); + configuration.rust_query_planner_config().hash(&mut hasher); } }; let config_mode = Arc::new(QueryHash(hasher.finalize())); From 750aaa185d8a963dd290e7ee5a4ce8701de3a013 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:22:59 +0200 Subject: [PATCH 40/64] fix --- apollo-router/src/router_factory.rs | 2 +- apollo-router/src/spec/schema.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apollo-router/src/router_factory.rs b/apollo-router/src/router_factory.rs index 3325b4b6b2..33c254379a 100644 --- a/apollo-router/src/router_factory.rs +++ b/apollo-router/src/router_factory.rs @@ -179,7 +179,7 @@ impl RouterSuperServiceFactory for YamlRouterFactory { .get("telemetry") .cloned(); if let Some(plugin_config) = &mut telemetry_config { - inject_schema_id(Some(&schema.hash), plugin_config); + inject_schema_id(Some(&schema.hash.to_string()), plugin_config); match factory .create_instance( PluginInit::builder() diff --git a/apollo-router/src/spec/schema.rs b/apollo-router/src/spec/schema.rs index 96320861f1..46cf990411 100644 --- a/apollo-router/src/spec/schema.rs +++ b/apollo-router/src/spec/schema.rs @@ -108,8 +108,6 @@ impl Schema { let implementers_map = definitions.implementers_map(); let supergraph = Supergraph::from_schema(definitions)?; - let schema_id = Arc::new(Schema::schema_id(&raw_sdl)); - let api_schema = supergraph .to_api_schema(ApiSchemaOptions { include_defer: config.supergraph.defer_support, From 52e3424a3e0ffdb16b5ab3eacf44d03f717a4ce4 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:38:07 +0200 Subject: [PATCH 41/64] fix QP test --- apollo-router/tests/integration/redis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index c3f11206da..2641218ec6 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -26,7 +26,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:1:federation:v2.8.1:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; + let known_cache_key = "plan:cache:1:federation:v2.8.1:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:37c3131d1b214d56ba318dbd3fbe84645812515ce9f2742304df79d100d54dfb"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); From 66ebc0d6d55c2aa33290f83c6bee45adcba0c453 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:42:45 +0200 Subject: [PATCH 42/64] introduce the metrics --- .changesets/fix_geal_fix_hashing_algorithm.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changesets/fix_geal_fix_hashing_algorithm.md b/.changesets/fix_geal_fix_hashing_algorithm.md index 9e8589a96e..6b3cfac1d3 100644 --- a/.changesets/fix_geal_fix_hashing_algorithm.md +++ b/.changesets/fix_geal_fix_hashing_algorithm.md @@ -14,7 +14,8 @@ supergraph: experimental_reuse_query_plans: true ``` -There is a counter metric named `apollo.router.query.planning.warmup.reused` that can be used to track the hashing algorithm benefits: + +We've also introduced changes to our metrics to help track the impact of these changes. There is a counter metric named `apollo.router.query.planning.warmup.reused` that can be used to track the hashing algorithm benefits: - if the `experimental_reuse_query_plans` option is false, the `query_plan_reuse_active` metric attribute will be false. Cache warm up will not reuse query plans according to the algorithm, but it will evaluate if some of them could have been reused and report that in the metric - if the `experimental_reuse_query_plans` option is true, then the `query_plan_reuse_active` metric attribute will be true From d4170f7efe15ee52d3ed3725ca79e8514303acc9 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:43:52 +0200 Subject: [PATCH 43/64] Apply suggestions from code review Co-authored-by: Gary Pennington --- .changesets/fix_geal_fix_hashing_algorithm.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.changesets/fix_geal_fix_hashing_algorithm.md b/.changesets/fix_geal_fix_hashing_algorithm.md index 6b3cfac1d3..f9cc818f66 100644 --- a/.changesets/fix_geal_fix_hashing_algorithm.md +++ b/.changesets/fix_geal_fix_hashing_algorithm.md @@ -3,7 +3,7 @@ > [!IMPORTANT] > If you have enabled [Distributed query plan caching](https://www.apollographql.com/docs/router/configuration/distributed-caching/#distributed-query-plan-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. -The Router contains a schema aware query hashing algorithm, designed to return the same hash across schema updates if they do not affect the query. It was used mainly to avoid planning a lot of queries when warming up the query plan cache. It was deactivated due to a regression. This reintroduces this algorithm, with a serie of reliability and performance fixes, along with better observability. +The Router contains a schema aware query hashing algorithm, designed to return the same hash across schema updates if they do not affect the query. It was used mainly to avoid planning a lot of queries when warming up the query plan cache. It was deactivated due to a regression. This reintroduces this algorithm, with a number of reliability and performance fixes, along with better observability. The hashing algorithm can be activated in configuration, if query plan cache warm up is already enabled: @@ -19,8 +19,8 @@ We've also introduced changes to our metrics to help track the impact of these c - if the `experimental_reuse_query_plans` option is false, the `query_plan_reuse_active` metric attribute will be false. Cache warm up will not reuse query plans according to the algorithm, but it will evaluate if some of them could have been reused and report that in the metric - if the `experimental_reuse_query_plans` option is true, then the `query_plan_reuse_active` metric attribute will be true -Fixes included in this change: -- strenghten the hashing algorithm to prevent collisions. In particular, the query string is always hashed in, making sure that different queries cannot get the same hash +Summary: +- strengthen the hashing algorithm to prevent collisions. In particular, the query string is always hashed in, making sure that different queries cannot get the same hash - remove inefficiencies in cache key generation - use prefixes for each part of the Redis cache key, so they become self describing From df8e6fc3285d175db49eb2bf82374de040ccef5b Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:51:37 +0200 Subject: [PATCH 44/64] add a prefix for planner configuration hashing --- apollo-router/src/query_planner/caching_query_planner.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index f7087b3864..05892f5100 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -117,12 +117,15 @@ where let mut hasher = StructHasher::new(); match configuration.experimental_query_planner_mode { crate::configuration::QueryPlannerMode::New => { + "PLANNER-NEW".hash(&mut hasher); configuration.rust_query_planner_config().hash(&mut hasher); } crate::configuration::QueryPlannerMode::Legacy => { + "PLANNER-LEGACY".hash(&mut hasher); configuration.js_query_planner_config().hash(&mut hasher); } crate::configuration::QueryPlannerMode::Both => { + "PLANNER-BOTH".hash(&mut hasher); configuration.js_query_planner_config().hash(&mut hasher); configuration.rust_query_planner_config().hash(&mut hasher); } From 52e27df186e134065cf890c996c00ea13a3b7b71 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 11:57:59 +0200 Subject: [PATCH 45/64] use the new metrics --- apollo-router/src/query_planner/caching_query_planner.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 05892f5100..c3cd220d27 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -381,8 +381,10 @@ where tracing::debug!("warmed up the query planner cache with {count} queries planned and {reused} queries reused"); - ::tracing::info!( - monotonic_counter.apollo.router.query.planning.warmup.reused = reused, + u64_counter!( + "apollo.router.query.planning.warmup.reused", + "The number of query plans that were reused instead of regenerated during query planner warm up", + reused as u64, query_plan_reuse_active = experimental_reuse_query_plans ); } From 66c9a0001bb1fdad394bb7d58093ef22be491a82 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 12:33:20 +0200 Subject: [PATCH 46/64] algorithm documentation --- apollo-router/src/spec/query/change.rs | 52 ++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 18e0b781f2..5a61ea8c98 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -1,3 +1,45 @@ +//! Schema aware query hashing algorithm +//! +//! This is a query visitor that calculates a hash of all fields, along with all +//! the relevant types and directives in the schema. It is designed to generate +//! the same hash for the same query across schema updates if the schema change +//! would not affect that query. As an example, if a new type is added to the +//! schema, we know that it will have no impact to an existing query that cannot +//! be using it. +//! This algorithm is used in 2 places: +//! * in the query planner cache: generating query plans can be expensive, so the +//! router has a warm up feature, where upon receving a new schema, it will take +//! the most used queries and plan them, before switching traffic to the new +//! schema. Generating all of those plans takes a lot of time. By using this +//! hashing algorithm, we can detect that the schema change does not affect the +//! query, which means that we can reuse the old query plan directly and avoid +//! the expensive planning task +//! * in entity caching: the responses returned by subgraphs can change depending +//! on the schema (example: a field moving from String to Int), so we need to +//! detect that. One way to do it was to add the schema hash to the cache key, but +//! as a result it wipes the cache on every schema update, which will cause +//! performance and reliability issues. With this hashing algorithm, cached entries +//! can be kept across schema updates +//! +//! ## Technical details +//! +//! ### Query string hashing +//! A full hash of the query string is added along with the schema level data. This +//! is technically making the algorithm less useful, because the same query with +//! different indentation would get a different hash, while there would be no difference +//! in the query plan or the subgraph response. But this makes sure that if we forget +//! something in the way we hash the query, we will avoid collisions. +//! +//! ### Prefixes and suffixes +//! Across the entire visitor, we add prefixes and suffixes like this: +//! +//! ```rust +//! "^SCHEMA".hash(self); +//! ``` +//! +//! This prevents possible collision while hashing multiple things in a sequence. The +//! `^` character cannot be present in a GraphQL query or schema outside of comments +//! or strings, so this is a good separator. use std::collections::HashMap; use std::collections::HashSet; use std::hash::Hash; @@ -97,6 +139,7 @@ impl<'a> QueryHashVisitor<'a> { ) -> Result, BoxError> { let mut visitor = QueryHashVisitor::new(schema, schema_str, executable)?; traverse::document(&mut visitor, executable, operation_name)?; + // hash the entire query string to prevent collisions executable.to_string().hash(&mut visitor); Ok(visitor.finish()) } @@ -191,6 +234,7 @@ impl<'a> QueryHashVisitor<'a> { name.hash(self); + // we need this this to avoid an infinite loop when hashing types that refer to each other if self.hashed_types.contains(name) { return Ok(()); } @@ -216,6 +260,8 @@ impl<'a> QueryHashVisitor<'a> { self.hash_directive(&directive.node); } } + // this only hashes the type level info, not the fields, because those will be taken from the query + // we will still hash the fields using for the key ExtendedType::Object(o) => { "^OBJECT".hash(self); @@ -334,6 +380,8 @@ impl<'a> QueryHashVisitor<'a> { self.hash_directive(directive); } + // for every field, we also need to look at fields defined in `@requires` because + // they will affect the query plan self.hash_join_field(&parent_type, &field_def.directives)?; for directive in &node.directives { @@ -454,10 +502,6 @@ impl<'a> Hasher for QueryHashVisitor<'a> { } fn write(&mut self, bytes: &[u8]) { - // FIXME: hack I used to debug my code, remove - // if bytes.len() != 1 || bytes[0] != 0xFF { - // println!("{:?}", std::str::from_utf8(bytes).unwrap()); - // } // byte separator between each part that is hashed self.hasher.update(&[0xFF][..]); self.hasher.update(bytes); From 391ab816d0f52f98e17681d5ab3d2c186e453ce5 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 12:34:31 +0200 Subject: [PATCH 47/64] Update apollo-router/src/spec/query/change.rs Co-authored-by: Ivan Goncharov --- apollo-router/src/spec/query/change.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 5a61ea8c98..cbea6494c2 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -223,7 +223,7 @@ impl<'a> QueryHashVisitor<'a> { "^:".hash(self); self.hash_value(v); } - "}".hash(self); + "^}".hash(self); } } "^VALUE-END".hash(self); From e1067ed8d0a11edfdf2ed2312410ceb860a83a02 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 17:43:13 +0200 Subject: [PATCH 48/64] add more tests --- apollo-router/src/spec/query/change.rs | 182 ++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 7 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index cbea6494c2..8982bfdbc0 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -744,32 +744,84 @@ mod tests { #[test] fn directive() { let schema1: &str = r#" - directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM + directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM | UNION | INPUT_OBJECT type Query { me: User customer: User + s: S + u: U + e: E + inp(i: I): ID } type User { id: ID! name: String } + + scalar S + + type A { + a: ID + } + + type B { + b: ID + } + + union U = A | B + + enum E { + A + B + } + + input I { + a: Int = 0 + b: Int + } "#; let schema2: &str = r#" - directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM - + directive @test on OBJECT | FIELD_DEFINITION | INTERFACE | SCALAR | ENUM | UNION | INPUT_OBJECT + type Query { me: User customer: User @test + s: S + u: U + e: E + inp(i: I): ID } - type User { id: ID! @test name: String } + + scalar S @test + + type A { + a: ID + } + + type B { + b: ID + } + + union U @test = A | B + + enum E @test { + A + B + } + + + input I @test { + a: Int = 0 + b: Int + } "#; let query = "query { me { name } }"; assert!(hash(schema1, query).equals(&hash(schema2, query))); @@ -779,6 +831,18 @@ mod tests { let query = "query { customer { id } }"; assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { s }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { u { ...on A { a } ...on B { b } } }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { e }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { inp(i: { b: 0 }) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); } #[test] @@ -833,12 +897,12 @@ mod tests { } #[test] - fn arguments() { + fn arguments_int() { let schema1: &str = r#" type Query { a(i: Int): Int b(i: Int = 1): Int - c(i: Int = 1, j: Int): Int + c(i: Int = 1, j: Int = null): Int } "#; @@ -846,7 +910,7 @@ mod tests { type Query { a(i: Int!): Int b(i: Int = 2): Int - c(i: Int = 2, j: Int): Int + c(i: Int = 2, j: Int = null): Int } "#; @@ -866,6 +930,110 @@ mod tests { assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); } + #[test] + fn arguments_float() { + let schema1: &str = r#" + type Query { + a(i: Float): Int + b(i: Float = 1.0): Int + c(i: Float = 1.0, j: Int): Int + } + "#; + + let schema2: &str = r#" + type Query { + a(i: Float!): Int + b(i: Float = 2.0): Int + c(i: Float = 2.0, j: Int): Int + } + "#; + + let query = "query { a(i: 0) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b(i: 0)}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { c(j: 0)}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { c(i:0, j: 0)}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + } + + #[test] + fn arguments_list() { + let schema1: &str = r#" + type Query { + a(i: [Float]): Int + b(i: [Float] = [1.0]): Int + c(i: [Float] = [1.0], j: Int): Int + } + "#; + + let schema2: &str = r#" + type Query { + a(i: [Float!]): Int + b(i: [Float] = [2.0]): Int + c(i: [Float] = [2.0], j: Int): Int + } + "#; + + let query = "query { a(i: [0]) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b(i: [0])}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { c(j: 0)}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { c(i: [0], j: 0)}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + } + + #[test] + fn arguments_object() { + let schema1: &str = r#" + input T { + d: Int + e: String + } + + type Query { + a(i: T): Int + b(i: T = { d: 1, e: "a" }): Int + } + "#; + + let schema2: &str = r#" + input T { + d: Int + e: String + } + + type Query { + a(i: T!): Int + b(i: T = { d: 2, e: "b" }): Int + } + "#; + + let query = "query { a(i: { d: 1, e: \"a\" }) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { b(i: { d: 3, e: \"c\" })}"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + } + #[test] fn entities() { let schema1: &str = r#" From bf734b92c4a879d057d688f3820043c6e8b01da9 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 18:16:47 +0200 Subject: [PATCH 49/64] more tests for input objects --- apollo-router/src/spec/query/change.rs | 36 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 8982bfdbc0..1b456d671d 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -316,13 +316,12 @@ impl<'a> QueryHashVisitor<'a> { } for (name, ty) in &o.fields { - "^KEY".hash(self); + "^NAME".hash(self); - //FIXME: always hash the name? - if ty.default_value.is_some() { - name.hash(self); - self.hash_input_value_definition(&ty.node)?; - } + name.hash(self); + + "^ARGUMENT".hash(self); + self.hash_input_value_definition(&ty.node)?; } } } @@ -1005,10 +1004,18 @@ mod tests { d: Int e: String } + input U { + c: Int + } + input V { + d: Int = 0 + } type Query { a(i: T): Int b(i: T = { d: 1, e: "a" }): Int + c(c: U): Int + d(d: V): Int } "#; @@ -1017,10 +1024,18 @@ mod tests { d: Int e: String } + input U { + c: Int! + } + input V { + d: Int = 1 + } type Query { a(i: T!): Int b(i: T = { d: 2, e: "b" }): Int + c(c: U): Int + d(d: V): Int } "#; @@ -1032,6 +1047,15 @@ mod tests { let query = "query { b(i: { d: 3, e: \"c\" })}"; assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { c(c: { c: 0 }) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { d(d: { }) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); + + let query = "query { d(d: { d: 2 }) }"; + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); } #[test] From 81604c235b8c7e601204e804194bb381df576d71 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Wed, 31 Jul 2024 18:36:01 +0200 Subject: [PATCH 50/64] update metadata hashing --- apollo-router/tests/integration/redis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 2641218ec6..fe7f0d96b7 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -26,7 +26,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:1:federation:v2.8.1:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:37c3131d1b214d56ba318dbd3fbe84645812515ce9f2742304df79d100d54dfb"; + let known_cache_key = "plan:cache:1:federation:v2.8.1:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:383ebaca21536f16f63ba5ba2602e593382178ad1e539617af4487a2f9497947"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); From bd11be275eecd6cb8732d4ca6ceb8d414acb8700 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 27 Aug 2024 14:57:25 +0200 Subject: [PATCH 51/64] update snapshots --- .../query_planner/caching_query_planner.rs | 1 - apollo-router/tests/integration/redis.rs | 21 +++++++------------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 58af4a8e66..536840a26e 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -61,7 +61,6 @@ pub(crate) enum ConfigMode { //FIXME: add the Rust planner structure once it is hashable and serializable, // for now use the JS config as it expected to be identical to the Rust one Rust(Arc), - Both(Arc), BothBestEffort(Arc), Js(Arc), } diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 8259f786a8..88794319aa 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -26,8 +26,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:1:federation:v2.8.3:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:383ebaca21536f16f63ba5ba2602e593382178ad1e539617af4487a2f9497947"; - + let known_cache_key = "plan:cache:1:federation:v2.8.3:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); let connection_task = client.connect(); @@ -963,7 +962,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:1ef4ca9553c8d72dc3308163027fbab0c26e8ff73daa07b1139e49fed01fc5c5", + "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:56fcacc8de2e085d1f46a0dfcdf3b1e8c0a2549975388c8d2b8ecb64d522c14f" ) .await; } @@ -982,8 +981,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:783c95a060a2a43f14f0241915247b148c542b9ff0e9238f16bae2cc172ac427", - ) + "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:214819cece87efcdd3ae25c19de048154c403b0a98a2f09047e3416a1aefca36" ) .await; } @@ -991,9 +989,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:c632e2e80594b3b8b8e7bf13ee5f2d3e3e1f6cb050d41a8d751b2c2de3e60906", - - ) + "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f" ) .await; } @@ -1003,8 +999,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:25dbfa37dc81a68cb3d2302ed470a0dcd5596b23cacd20f4b1e295b98f45239c", - + "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:334396bce746bff275068c97e126891f7b867edb88bf32d707169016ae0bf220" ) .await; } @@ -1015,8 +1010,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:8e23b92bb8eb456dd7596dda44f26de31b8b06349549a157bfb7c2d1b492ecc4", - + "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:f92d99f543f3911c0bae7edcccd4d5bf76de37ac1f2f6c0b4deb44cb62ed9e35" ) .await; } @@ -1039,8 +1033,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:664d923109135eee5e1f316e19f73a3db1a3fafadc7019cb9b8a9b13d70ac2a3"; - + let starting_key = "plan:cache:1:federation:v2.8.3:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; router.update_config(updated_config).await; From 1b76c0ad01743272dfaf4497d87760ea9d265b1f Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 29 Aug 2024 14:43:59 +0200 Subject: [PATCH 52/64] isolate loops --- apollo-router/src/spec/query/change.rs | 102 ++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 1b456d671d..167322af6c 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -154,9 +154,12 @@ impl<'a> QueryHashVisitor<'a> { ) -> Result<(), BoxError> { "^DIRECTIVE_DEFINITION".hash(self); directive_definition.name.as_str().hash(self); + "^ARGUMENT_LIST".hash(self); for argument in &directive_definition.arguments { self.hash_input_value_definition(argument)?; } + "^ARGUMENT_LIST_END".hash(self); + "^DIRECTIVE_DEFINITION-END".hash(self); Ok(()) @@ -165,9 +168,12 @@ impl<'a> QueryHashVisitor<'a> { fn hash_directive(&mut self, directive: &Node) { "^DIRECTIVE".hash(self); directive.name.as_str().hash(self); + "^ARGUMENT_LIST".hash(self); for argument in &directive.arguments { - self.hash_argument(argument) + self.hash_argument(argument); } + "^ARGUMENT_END".hash(self); + "^DIRECTIVE-END".hash(self); } @@ -220,7 +226,7 @@ impl<'a> QueryHashVisitor<'a> { "^key".hash(self); k.hash(self); - "^:".hash(self); + "^value:".hash(self); self.hash_value(v); } "^}".hash(self); @@ -256,18 +262,22 @@ impl<'a> QueryHashVisitor<'a> { ExtendedType::Scalar(s) => { "^SCALAR".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &s.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIST_END".hash(self); } // this only hashes the type level info, not the fields, because those will be taken from the query // we will still hash the fields using for the key ExtendedType::Object(o) => { "^OBJECT".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &o.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIST_END".hash(self); self.hash_join_type(&o.name, &o.directives)?; //FIXME: hash implemented interfaces? @@ -275,30 +285,40 @@ impl<'a> QueryHashVisitor<'a> { ExtendedType::Interface(i) => { "^INTERFACE".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &i.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIST_END".hash(self); + self.hash_join_type(&i.name, &i.directives)?; //FIXME: hash implemented interfaces? } ExtendedType::Union(u) => { "^UNION".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &u.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIS_END".hash(self); + "^MEMBER_LIST".hash(self); for member in &u.members { self.hash_type_by_name(member.as_str())?; } + "^MEMBER_LIST_END".hash(self); } ExtendedType::Enum(e) => { "^ENUM".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &e.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIST_END".hash(self); + "^ENUM_VALUE_LIST".hash(self); for (value, def) in &e.values { "^VALUE".hash(self); @@ -307,14 +327,18 @@ impl<'a> QueryHashVisitor<'a> { self.hash_directive(directive); } } + "^ENUM_VALUE_LIST_END".hash(self); } ExtendedType::InputObject(o) => { "^INPUT_OBJECT".hash(self); + "^DIRECTIVE_LIST".hash(self); for directive in &o.directives { self.hash_directive(&directive.node); } + "^DIRECTIVE_LIST_END".hash(self); + "^FIELD_LIST".hash(self); for (name, ty) in &o.fields { "^NAME".hash(self); @@ -323,6 +347,7 @@ impl<'a> QueryHashVisitor<'a> { "^ARGUMENT".hash(self); self.hash_input_value_definition(&ty.node)?; } + "^FIELD_LIST_END".hash(self); } } "^EXTENDED_TYPE-END".hash(self); @@ -365,27 +390,33 @@ impl<'a> QueryHashVisitor<'a> { field_def.name.hash(self); self.hash_type(&field_def.ty)?; + // for every field, we also need to look at fields defined in `@requires` because + // they will affect the query plan + self.hash_join_field(&parent_type, &field_def.directives)?; + + "^FIELD_DEF_DIRECTIVE_LIST".hash(self); + for directive in &field_def.directives { + self.hash_directive(directive); + } + "^FIELD_DEF_DIRECTIVE_LIST_END".hash(self); + + "^ARGUMENT_DEF_LIST".hash(self); for argument in &field_def.arguments { self.hash_input_value_definition(argument)?; } + "^ARGUMENT_DEF_LIST_END".hash(self); + "^ARGUMENT_LIST".hash(self); for argument in &node.arguments { self.hash_argument(argument); } + "^ARGUMENT_LIST_END".hash(self); - self.hash_type(&field_def.ty)?; - - for directive in &field_def.directives { - self.hash_directive(directive); - } - - // for every field, we also need to look at fields defined in `@requires` because - // they will affect the query plan - self.hash_join_field(&parent_type, &field_def.directives)?; - + "^DIRECTIVE_LIST".hash(self); for directive in &node.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); node.alias.hash(self); "^FIELD-END".hash(self); @@ -400,9 +431,11 @@ impl<'a> QueryHashVisitor<'a> { "^INPUT_VALUE".hash(self); self.hash_type(&t.ty)?; + "^DIRECTIVE_LIST".hash(self); for directive in &t.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); if let Some(value) = t.default_value.as_ref() { self.hash_value(value); } else { @@ -484,10 +517,12 @@ impl<'a> QueryHashVisitor<'a> { "^INTERFACE_IMPL".hash(self); if let Some(implementers) = self.schema.implementers_map().get(&intf.name) { + "^IMPLEMENTER_LIST".hash(self); for object in &implementers.objects { self.hash_type_by_name(object)?; traverse::selection_set(self, object, &node.selection_set.selections)?; } + "^IMPLEMENTER_LIST_END".hash(self); } "^INTERFACE_IMPL-END".hash(self); @@ -515,6 +550,8 @@ impl<'a> Visitor for QueryHashVisitor<'a> { self.hash_type_by_name(root_type)?; node.operation_type.hash(self); node.name.hash(self); + + "^VARIABLE_LIST".hash(self); for variable in &node.variables { variable.name.hash(self); self.hash_type(&variable.ty)?; @@ -525,13 +562,19 @@ impl<'a> Visitor for QueryHashVisitor<'a> { "^VISIT_OPERATION-NO_DEFAULT".hash(self); } + "^DIRECTIVE_LIST".hash(self); for directive in &variable.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); } + "^VARIABLE_LIST_END".hash(self); + + "^DIRECTIVE_LIST".hash(self); for directive in &node.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); traverse::operation(self, root_type, node)?; "^VISIT_OPERATION-END".hash(self); @@ -570,9 +613,11 @@ impl<'a> Visitor for QueryHashVisitor<'a> { node.name.hash(self); self.hash_type_by_name(node.type_condition())?; + "^DIRECTIVE_LIST".hash(self); for directive in &node.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); traverse::fragment(self, node)?; "^VISIT_FRAGMENT-END".hash(self); @@ -591,9 +636,11 @@ impl<'a> Visitor for QueryHashVisitor<'a> { .type_condition(); self.hash_type_by_name(type_condition)?; + "^DIRECTIVE_LIST".hash(self); for directive in &node.directives { self.hash_directive(directive); } + "^DIRECTIVE_LIST_END".hash(self); traverse::fragment_spread(self, node)?; "^VISIT_FRAGMENT_SPREAD-END".hash(self); @@ -2116,4 +2163,35 @@ mod tests { hash(schema, query_two).from_visitor ); } + + #[test] + fn it_change_directive_location() { + let schema: &str = r#" + directive @foo on QUERY | VARIABLE_DEFINITION + + type Query { + field(arg: String): String + } + "#; + + let query_one = r#" + query Test ($arg: String @foo) { + field(arg: $arg) + } + "#; + let query_two = r#" + query Test ($arg: String) @foo { + field(arg: $arg) + } + "#; + + assert_ne!( + hash(schema, query_one).from_hash_query, + hash(schema, query_two).from_hash_query + ); + assert_ne!( + hash(schema, query_one).from_visitor, + hash(schema, query_two).from_visitor + ); + } } From 555e5076321c1a9297044e963f66919b6d7dcdbe Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Thu, 29 Aug 2024 17:11:12 +0200 Subject: [PATCH 53/64] hash implemented interfaces --- apollo-router/src/spec/query/change.rs | 87 +++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index 167322af6c..e8056afc9b 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -280,6 +280,8 @@ impl<'a> QueryHashVisitor<'a> { "^DIRECTIVE_LIST_END".hash(self); self.hash_join_type(&o.name, &o.directives)?; + "^OBJECT_END".hash(self); + //FIXME: hash implemented interfaces? } ExtendedType::Interface(i) => { @@ -292,7 +294,29 @@ impl<'a> QueryHashVisitor<'a> { "^DIRECTIVE_LIST_END".hash(self); self.hash_join_type(&i.name, &i.directives)?; - //FIXME: hash implemented interfaces? + + "^IMPLEMENTED_INTERFACES_LIST".hash(self); + for implementor in &i.implements_interfaces { + self.hash_type_by_name(&implementor.name)?; + } + "^IMPLEMENTED_INTERFACES_LIST_END".hash(self); + + if let Some(implementers) = self.schema().implementers_map().get(&i.name) { + "^IMPLEMENTER_OBJECT_LIST".hash(self); + + for object in &implementers.objects { + self.hash_type_by_name(&object)?; + } + "^IMPLEMENTER_OBJECT_LIST_END".hash(self); + + "^IMPLEMENTER_INTERFACE_LIST".hash(self); + for interface in &implementers.interfaces { + self.hash_type_by_name(&interface)?; + } + "^IMPLEMENTER_INTERFACE_LIST_END".hash(self); + } + + "^INTERFACE_END".hash(self); } ExtendedType::Union(u) => { "^UNION".hash(self); @@ -2194,4 +2218,65 @@ mod tests { hash(schema, query_two).from_visitor ); } + + #[test] + fn it_changes_on_implementors_list_changes() { + let schema_one: &str = r#" + interface SomeInterface { + value: String + } + + type Foo implements SomeInterface { + value: String + } + + type Bar implements SomeInterface { + value: String + } + + union FooOrBar = Foo | Bar + + type Query { + fooOrBar: FooOrBar + } + "#; + let schema_two: &str = r#" + interface SomeInterface { + value: String + } + + type Foo { + value: String # <= This field shouldn't be a part of query plan anymore + } + + type Bar implements SomeInterface { + value: String + } + + union FooOrBar = Foo | Bar + + type Query { + fooOrBar: FooOrBar + } + "#; + + let query = r#" + { + fooOrBar { + ... on SomeInterface { + value + } + } + } + "#; + + assert_ne!( + hash(schema_one, query).from_hash_query, + hash(schema_two, query).from_hash_query + ); + assert_ne!( + hash(schema_one, query).from_visitor, + hash(schema_two, query).from_visitor + ); + } } From 6bf14a7697bc596999ea5ff209e683578415ecf3 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 30 Aug 2024 12:28:06 +0200 Subject: [PATCH 54/64] lint --- apollo-router/src/spec/query/change.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index e8056afc9b..da046a47ca 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -305,13 +305,13 @@ impl<'a> QueryHashVisitor<'a> { "^IMPLEMENTER_OBJECT_LIST".hash(self); for object in &implementers.objects { - self.hash_type_by_name(&object)?; + self.hash_type_by_name(object)?; } "^IMPLEMENTER_OBJECT_LIST_END".hash(self); "^IMPLEMENTER_INTERFACE_LIST".hash(self); for interface in &implementers.interfaces { - self.hash_type_by_name(&interface)?; + self.hash_type_by_name(interface)?; } "^IMPLEMENTER_INTERFACE_LIST_END".hash(self); } From 2a3772831011de5bebffedf7b667fecc70bc8eab Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 30 Aug 2024 12:36:36 +0200 Subject: [PATCH 55/64] hash implemented interfaces for objects --- ...__non_overridden_field_yields_expected_query_plan.snap | 2 +- ...ests__overridden_field_yields_expected_query_plan.snap | 4 ++-- ..._expose_query_plan__tests__it_expose_query_plan-2.snap | 8 ++++---- ...s__expose_query_plan__tests__it_expose_query_plan.snap | 8 ++++---- ...y_planner__bridge_query_planner__tests__plan_root.snap | 2 +- apollo-router/src/spec/query/change.rs | 8 ++++++-- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap index 00388549e0..2ccfdec21f 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__non_overridden_field_yields_expected_query_plan.snap @@ -19,7 +19,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "4254834683f20e6c16804aa7c8c3ab0571a5f9dde3da3bc8c18c7c26b5a2ad36", + "schemaAwareHash": "c9edb9986e936d233bb43c0ec2c84b54a99ab9ffe4125d39b63c2d623014f7bd", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap index 655a7f7421..bbfac62c78 100644 --- a/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap +++ b/apollo-router/src/plugins/progressive_override/snapshots/apollo_router__plugins__progressive_override__tests__overridden_field_yields_expected_query_plan.snap @@ -24,7 +24,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a69b4eb6ed66adb96bea8b7f459d1995223d1b301c2440f7403bc79872da8176", + "schemaAwareHash": "88e6ce535180b5abf2100c1c54209fca8feb246d4df0f3a787892f78bc1687f4", "authorization": { "is_authenticated": false, "scopes": [], @@ -63,7 +63,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "70e0a35ab459ea22bf191acf7bb3d786e596f147ab69dd9dca269c2f79ea2362", + "schemaAwareHash": "0c9b110922cff5700de74b26875f55186f956139c56d7a3310fe68540904d582", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap index 11773f28d2..9001325d52 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan-2.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "59d34b5438e3aef7a7c8f8f5f62831ecbf936b76cb72c6bf6cb8f09d43b9992a", + "schemaAwareHash": "834837bc7eef55f887c6024a431d7e6e61da866f4cbcce8e0aadb40e544833fb", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5ed35165fdd993eecabd055b587bb16d1ca6d42a4758c4caca2153cf03b4d2c6", + "schemaAwareHash": "b7a453f6ee82d3c4acc71cdeca5f3829b73c890b9945fbfa920e8873f73a3c47", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "be17552dccd52596484494452f5abc9f78225d8acd8f6117d2f3725abd7acf05", + "schemaAwareHash": "4964fc8df945d63da18f3292ae361d13a8bded8c5c24d86f854e848bf1731168", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5c58e86ce5ce7882ac36ee2a4c4c8668b78fe37afa9431336fe957d361ba45a1", + "schemaAwareHash": "a33fdbe9b64b1987062c95a60f76f175f410114da4bc7e806485f697122bae35", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap index 11773f28d2..9001325d52 100644 --- a/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap +++ b/apollo-router/src/plugins/snapshots/apollo_router__plugins__expose_query_plan__tests__it_expose_query_plan.snap @@ -69,7 +69,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "59d34b5438e3aef7a7c8f8f5f62831ecbf936b76cb72c6bf6cb8f09d43b9992a", + "schemaAwareHash": "834837bc7eef55f887c6024a431d7e6e61da866f4cbcce8e0aadb40e544833fb", "authorization": { "is_authenticated": false, "scopes": [], @@ -109,7 +109,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5ed35165fdd993eecabd055b587bb16d1ca6d42a4758c4caca2153cf03b4d2c6", + "schemaAwareHash": "b7a453f6ee82d3c4acc71cdeca5f3829b73c890b9945fbfa920e8873f73a3c47", "authorization": { "is_authenticated": false, "scopes": [], @@ -156,7 +156,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "be17552dccd52596484494452f5abc9f78225d8acd8f6117d2f3725abd7acf05", + "schemaAwareHash": "4964fc8df945d63da18f3292ae361d13a8bded8c5c24d86f854e848bf1731168", "authorization": { "is_authenticated": false, "scopes": [], @@ -200,7 +200,7 @@ expression: "serde_json::to_value(response).unwrap()" "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5c58e86ce5ce7882ac36ee2a4c4c8668b78fe37afa9431336fe957d361ba45a1", + "schemaAwareHash": "a33fdbe9b64b1987062c95a60f76f175f410114da4bc7e806485f697122bae35", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap index 2c44d5adc8..abe0e59a29 100644 --- a/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap +++ b/apollo-router/src/query_planner/snapshots/apollo_router__query_planner__bridge_query_planner__tests__plan_root.snap @@ -15,7 +15,7 @@ Fetch( output_rewrites: None, context_rewrites: None, schema_aware_hash: QueryHash( - "8e970ad6b2fffed10d58dd1677b60620741b2522ed7cafc22d7a1f9de60066ee", + "14cb7370aaf42fa54e522635525359a71d29dde90a7283d8a3d5ee91bd55bd33", ), authorization: CacheKeyMetadata { is_authenticated: false, diff --git a/apollo-router/src/spec/query/change.rs b/apollo-router/src/spec/query/change.rs index da046a47ca..741af34d61 100644 --- a/apollo-router/src/spec/query/change.rs +++ b/apollo-router/src/spec/query/change.rs @@ -282,7 +282,11 @@ impl<'a> QueryHashVisitor<'a> { self.hash_join_type(&o.name, &o.directives)?; "^OBJECT_END".hash(self); - //FIXME: hash implemented interfaces? + "^IMPLEMENTED_INTERFACES_LIST".hash(self); + for interface in &o.implements_interfaces { + self.hash_type_by_name(&interface.name)?; + } + "^IMPLEMENTED_INTERFACES_LIST_END".hash(self); } ExtendedType::Interface(i) => { "^INTERFACE".hash(self); @@ -957,7 +961,7 @@ mod tests { "#; let query = "query { me { id name } }"; - assert!(hash(schema1, query).equals(&hash(schema2, query))); + assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); let query = "query { customer { id } }"; assert!(hash(schema1, query).doesnt_match(&hash(schema2, query))); From 603b27b482ac0ed840acb6549bf1a73371e33e3f Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 30 Aug 2024 14:46:06 +0200 Subject: [PATCH 56/64] update keys in tests --- apollo-router/tests/integration/redis.rs | 26 +++++++++---------- ...tegration__redis__query_planner_cache.snap | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index f1f99a510c..058206552e 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -26,7 +26,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { // 2. run `docker compose up -d` and connect to the redis container by running `docker-compose exec redis /bin/bash`. // 3. Run the `redis-cli` command from the shell and start the redis `monitor` command. // 4. Run this test and yank the updated cache key from the redis logs. - let known_cache_key = "plan:cache:1:federation:v2.9.0:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:ff7e3b6ae2b7bd28b00641b22ff94aab8d18b6201a2d4598811d56d23a472460:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; + let known_cache_key = "plan:cache:1:federation:v2.9.0:schema:5abb5fecf7df056396fb90fdf38d430b8c1fec55ec132fde878161608af18b76:query:c8d18ca3ab45646df6a2e0b53dfecfc37b587e1e6822bc51f31e50e9c54c3826:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); let connection_task = client.connect(); @@ -409,7 +409,7 @@ async fn entity_cache() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let cache_key = "version:1.0:subgraph:products:type:Query:hash:90f4e43ccdeeec2f9d76d5b80e90a267a1144c672c7358838de1b1526041abbe:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let cache_key = "version:1.0:subgraph:products:type:Query:hash:064e2e457222ec3bb7d1ef1680844bcd8cd5e488aab8f8fbb5b0723b3845a860:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; let s: String = match client.get(cache_key).await { Ok(s) => s, Err(e) => { @@ -425,7 +425,7 @@ async fn entity_cache() -> Result<(), BoxError> { let v: Value = serde_json::from_str(&s).unwrap(); insta::assert_json_snapshot!(v.as_object().unwrap().get("data").unwrap()); - let cache_key = "version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:9dc4223318b8aedd77cfb2fdec641bbc873bab8617bc85d4c4424cc824e6f777:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let cache_key = "version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:bb499c8657caa4b40cb8af57582628f9f4cfbd2ba00050f41d1d7354f4857388:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; let s: String = match client.get(cache_key).await { Ok(s) => s, Err(e) => { @@ -545,7 +545,7 @@ async fn entity_cache() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let cache_key = "version:1.0:subgraph:reviews:type:Product:entity:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:hash:9dc4223318b8aedd77cfb2fdec641bbc873bab8617bc85d4c4424cc824e6f777:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let cache_key = "version:1.0:subgraph:reviews:type:Product:entity:d9a4cd73308dd13ca136390c10340823f94c335b9da198d2339c886c738abf0d:hash:bb499c8657caa4b40cb8af57582628f9f4cfbd2ba00050f41d1d7354f4857388:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; let s: String = match client.get(cache_key).await { Ok(s) => s, Err(e) => { @@ -776,7 +776,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { .unwrap(); insta::assert_json_snapshot!(response); - let cache_key = "version:1.0:subgraph:products:type:Query:hash:90f4e43ccdeeec2f9d76d5b80e90a267a1144c672c7358838de1b1526041abbe:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; + let cache_key = "version:1.0:subgraph:products:type:Query:hash:064e2e457222ec3bb7d1ef1680844bcd8cd5e488aab8f8fbb5b0723b3845a860:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c"; let s: String = match client.get(cache_key).await { Ok(s) => s, Err(e) => { @@ -808,7 +808,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { ); let s: String = client - .get("version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:9dc4223318b8aedd77cfb2fdec641bbc873bab8617bc85d4c4424cc824e6f777:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:bb499c8657caa4b40cb8af57582628f9f4cfbd2ba00050f41d1d7354f4857388:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -852,7 +852,7 @@ async fn entity_cache_authorization() -> Result<(), BoxError> { insta::assert_json_snapshot!(response); let s:String = client - .get("version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:13b94e7d63d659c9fd457c0ddee5d329d1f4f63de0e9676302aa8fa591e17cce:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") + .get("version:1.0:subgraph:reviews:type:Product:entity:4911f7a9dbad8a47b8900d65547503a2f3c0359f65c0bc5652ad9b9843281f66:hash:ff4a0b4ccd53e4b868cb74d1928157f1d672f18f51d2c9fbc5f8578996bef062:data:d9d84a3c7ffc27b0190a671212f3740e5b8478e84e23825830e97822e25cf05c") .await .unwrap(); let v: Value = serde_json::from_str(&s).unwrap(); @@ -962,7 +962,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:56fcacc8de2e085d1f46a0dfcdf3b1e8c0a2549975388c8d2b8ecb64d522c14f" + "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:56fcacc8de2e085d1f46a0dfcdf3b1e8c0a2549975388c8d2b8ecb64d522c14f" ) .await; } @@ -981,7 +981,7 @@ async fn query_planner_redis_update_planner_mode() { async fn query_planner_redis_update_introspection() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_introspection.router.yaml"), - "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:214819cece87efcdd3ae25c19de048154c403b0a98a2f09047e3416a1aefca36" ) + "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:214819cece87efcdd3ae25c19de048154c403b0a98a2f09047e3416a1aefca36" ) .await; } @@ -989,7 +989,7 @@ async fn query_planner_redis_update_introspection() { async fn query_planner_redis_update_defer() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f" ) + "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:e3f21779503e898c8798a568e10416162670e2c29fced43cb9a71dbf60a31beb" ) .await; } @@ -999,7 +999,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:334396bce746bff275068c97e126891f7b867edb88bf32d707169016ae0bf220" + "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:334396bce746bff275068c97e126891f7b867edb88bf32d707169016ae0bf220" ) .await; } @@ -1010,7 +1010,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:f92d99f543f3911c0bae7edcccd4d5bf76de37ac1f2f6c0b4deb44cb62ed9e35" + "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:f92d99f543f3911c0bae7edcccd4d5bf76de37ac1f2f6c0b4deb44cb62ed9e35" ) .await; } @@ -1033,7 +1033,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.assert_started().await; router.clear_redis_cache().await; - let starting_key = "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:ca955be743f0dedae36910f6173d8f3e1076441b20a8e9d3466c320d39d83062:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; + let starting_key = "plan:cache:1:federation:v2.9.0:schema:522be889cf593392b55a9794fc0e3b636d06f5dee9ac886d459dd1c24cc0b0e2:query:bf61634007a26128b577c88fd1d520da4d87791658df6cb3d7295e2fba30e5dd:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:58a9ebbc752cb56c9111dbb7b81570c6abb7526837480cd205788a85c4de263f"; router.execute_default_query().await; router.assert_redis_cache_contains(starting_key, None).await; router.update_config(updated_config).await; diff --git a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap index e345583e26..80189d4c6b 100644 --- a/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap +++ b/apollo-router/tests/integration/snapshots/integration_tests__integration__redis__query_planner_cache.snap @@ -13,7 +13,7 @@ expression: query_plan "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "cddd53761788e1a8900afb57aa1d758a2a7d54b77b2e4d31e0915a33610c707f", + "schemaAwareHash": "ccd5e1b7b22c087a92eece5c1e787ddce2ae6aad5b782b345c857eab0047cab4", "authorization": { "is_authenticated": false, "scopes": [], From a5c0c7cf4cea8c0b317474868c5daff64968d6f3 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 30 Aug 2024 15:50:53 +0200 Subject: [PATCH 57/64] update snapshots --- .../type_conditions__type_conditions_disabled.snap | 4 ++-- .../snapshots/type_conditions__type_conditions_enabled.snap | 6 +++--- ...s__type_conditions_enabled_generate_query_fragments.snap | 6 +++--- ...pe_conditions__type_conditions_enabled_list_of_list.snap | 6 +++--- ...tions__type_conditions_enabled_list_of_list_of_list.snap | 6 +++--- ...type_conditions_enabled_shouldnt_make_article_fetch.snap | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap index 224cd2fb09..91a8082da2 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_disabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5201830580c9c5fadd9c59aea072878f84465c1ae9d905207fa281aa7c1d5340", + "schemaAwareHash": "b3e3042071f0437d8bd2a638371a935052bc2721b3bee1ed2f193ef66bf576bc", "authorization": { "is_authenticated": false, "scopes": [], @@ -137,7 +137,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "62ff891f6971184d3e42b98f8166be72027b5479f9ec098af460a48ea6f6cbf4", + "schemaAwareHash": "96a0c87d46e1c4c549bfdf3a2d7066ce1f6d07c81bf19f4eba1e6f1b645d8549", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap index da66cee5c2..869f19c8f2 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5201830580c9c5fadd9c59aea072878f84465c1ae9d905207fa281aa7c1d5340", + "schemaAwareHash": "b3e3042071f0437d8bd2a638371a935052bc2721b3bee1ed2f193ef66bf576bc", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "62ff891f6971184d3e42b98f8166be72027b5479f9ec098af460a48ea6f6cbf4", + "schemaAwareHash": "96a0c87d46e1c4c549bfdf3a2d7066ce1f6d07c81bf19f4eba1e6f1b645d8549", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7e6f6850777335eb1421a30a45f6888bb9e5d0acf8f55d576d55d1c4b7d23ec7", + "schemaAwareHash": "8e8cc8c9ca9f9ea93d94d6d2691226de51aa679cca38e85de6196aa734a4fddd", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap index e5e2cc616a..a3eacfdef5 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_generate_query_fragments.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "0e1644746fe4beab7def35ec8cc12bde39874c6bb8b9dfd928456196b814a111", + "schemaAwareHash": "041630393a2155b6f157c1af80c98ebf84a3d5d525c59d358d33dbe46ab6dfe8", "authorization": { "is_authenticated": false, "scopes": [], @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "6510f6b9672829bd9217618b78ef6f329fbddb125f88184d04e6faaa982ff8bb", + "schemaAwareHash": "e2ed1f89252adb1a0e5e1404379e57cb2c748aca31f3cc174605695a5fbde8be", "authorization": { "is_authenticated": false, "scopes": [], @@ -201,7 +201,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "6bc34c108f7cf81896971bffad76dc5275d46231b4dfe492ccc205dda9a4aa16", + "schemaAwareHash": "ab2b67e988c4819458b74eeffe8efef45e75d4fd37634f651cb7f35726bc3a9b", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap index 9d70336225..f90a72a408 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list.snap @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "51a7aadec14b66d9f6c737be7418bac0be1af89fcc55dac55d9e9b125bc3682d", + "schemaAwareHash": "6a953104fff20cd55f36eb8a7f30aba3284e0e8d853d56a601cc9e2dc5a6e072", "authorization": { "is_authenticated": false, "scopes": [], @@ -204,7 +204,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "62ff891f6971184d3e42b98f8166be72027b5479f9ec098af460a48ea6f6cbf4", + "schemaAwareHash": "96a0c87d46e1c4c549bfdf3a2d7066ce1f6d07c81bf19f4eba1e6f1b645d8549", "authorization": { "is_authenticated": false, "scopes": [], @@ -265,7 +265,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7e6f6850777335eb1421a30a45f6888bb9e5d0acf8f55d576d55d1c4b7d23ec7", + "schemaAwareHash": "8e8cc8c9ca9f9ea93d94d6d2691226de51aa679cca38e85de6196aa734a4fddd", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap index 5a6a4b30bc..d146bf5524 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_list_of_list_of_list.snap @@ -145,7 +145,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "e6f45a784fb669930586f13fc587f55798089a87ee4b23a7d1736e0516367a6a", + "schemaAwareHash": "3aa4072e4b7eb2b6b28b9e45ba4f90640ed78f506ee3954fe51833ac3e43de81", "authorization": { "is_authenticated": false, "scopes": [], @@ -209,7 +209,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "62ff891f6971184d3e42b98f8166be72027b5479f9ec098af460a48ea6f6cbf4", + "schemaAwareHash": "96a0c87d46e1c4c549bfdf3a2d7066ce1f6d07c81bf19f4eba1e6f1b645d8549", "authorization": { "is_authenticated": false, "scopes": [], @@ -271,7 +271,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7e6f6850777335eb1421a30a45f6888bb9e5d0acf8f55d576d55d1c4b7d23ec7", + "schemaAwareHash": "8e8cc8c9ca9f9ea93d94d6d2691226de51aa679cca38e85de6196aa734a4fddd", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap index acd8fb6676..1fab0639b6 100644 --- a/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap +++ b/apollo-router/tests/snapshots/type_conditions__type_conditions_enabled_shouldnt_make_article_fetch.snap @@ -54,7 +54,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "5201830580c9c5fadd9c59aea072878f84465c1ae9d905207fa281aa7c1d5340", + "schemaAwareHash": "b3e3042071f0437d8bd2a638371a935052bc2721b3bee1ed2f193ef66bf576bc", "authorization": { "is_authenticated": false, "scopes": [], @@ -116,7 +116,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "62ff891f6971184d3e42b98f8166be72027b5479f9ec098af460a48ea6f6cbf4", + "schemaAwareHash": "96a0c87d46e1c4c549bfdf3a2d7066ce1f6d07c81bf19f4eba1e6f1b645d8549", "authorization": { "is_authenticated": false, "scopes": [], @@ -176,7 +176,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "7e6f6850777335eb1421a30a45f6888bb9e5d0acf8f55d576d55d1c4b7d23ec7", + "schemaAwareHash": "8e8cc8c9ca9f9ea93d94d6d2691226de51aa679cca38e85de6196aa734a4fddd", "authorization": { "is_authenticated": false, "scopes": [], From 0151f0f9c2cc51046bc3c667b5ac0b43f947d9d0 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 6 Sep 2024 14:42:27 +0200 Subject: [PATCH 58/64] fix --- apollo-router/src/query_planner/caching_query_planner.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 6f6d452ce4..27f0fb696f 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -301,7 +301,7 @@ where let caching_key = CachingQueryKey { query: query.clone(), - operation: operation.clone(), + operation: operation_name.clone(), hash: if experimental_reuse_query_plans { CachingQueryHash::Reuse(doc.hash.clone()) } else { @@ -310,7 +310,6 @@ where schema_hash: self.schema.hash.clone(), } }, ->>>>>>> dev metadata, plan_options, config_mode: self.config_mode.clone(), From 1fcb7046f16e9220130fc6fbb957edb0ad186ba0 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 9 Sep 2024 11:09:14 +0200 Subject: [PATCH 59/64] add a mode to measure query plan reuse --- apollo-router/src/configuration/mod.rs | 16 ++- ...nfiguration__tests__schema_generation.snap | 30 +++++- .../query_planner/caching_query_planner.rs | 101 ++++++++++++++---- .../src/query_planner/dual_query_planner.rs | 2 +- .../src/services/supergraph/service.rs | 3 +- .../configuration/in-memory-caching.mdx | 4 +- 6 files changed, 130 insertions(+), 26 deletions(-) diff --git a/apollo-router/src/configuration/mod.rs b/apollo-router/src/configuration/mod.rs index 635bce0ff5..a154a2e638 100644 --- a/apollo-router/src/configuration/mod.rs +++ b/apollo-router/src/configuration/mod.rs @@ -934,7 +934,7 @@ pub(crate) struct QueryPlanning { /// If cache warm up is configured, this will allow the router to keep a query plan created with /// the old schema, if it determines that the schema update does not affect the corresponding query - pub(crate) experimental_reuse_query_plans: bool, + pub(crate) experimental_reuse_query_plans: QueryPlanReuseMode, /// Set the size of a pool of workers to enable query planning parallelism. /// Default: 1. @@ -976,6 +976,20 @@ impl QueryPlanning { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, Deserialize, Serialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub(crate) enum QueryPlanReuseMode { + /// Do not reuse query plans across schema and configuration updates + #[default] + DoNotReuse, + /// Do not reuse query plans across schema and configuration updates, but check if query plans + /// could have been reused, by comparing the query plan with the previous query plan + Measure, + /// Reuse query plans across schema and configuration updates for some queries + /// if the update would not affect these queries + Reuse, +} + /// Cache configuration #[derive(Debug, Clone, Default, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields, default)] diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index c514cb925d..e6da3a84ec 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -4390,6 +4390,31 @@ expression: "&schema" ], "type": "object" }, + "QueryPlanReuseMode": { + "oneOf": [ + { + "description": "Do not reuse query plans across schema and configuration updates", + "enum": [ + "do_not_reuse" + ], + "type": "string" + }, + { + "description": "Do not reuse query plans across schema and configuration updates, but check if query plans could have been reused, by comparing the query plan with the previous query plan", + "enum": [ + "measure" + ], + "type": "string" + }, + { + "description": "Reuse query plans across schema and configuration updates for some queries if the update would not affect these queries", + "enum": [ + "reuse" + ], + "type": "string" + } + ] + }, "QueryPlannerMode": { "description": "Query planner modes.", "oneOf": [ @@ -4452,9 +4477,8 @@ expression: "&schema" "type": "integer" }, "experimental_reuse_query_plans": { - "default": false, - "description": "If cache warm up is configured, this will allow the router to keep a query plan created with the old schema, if it determines that the schema update does not affect the corresponding query", - "type": "boolean" + "$ref": "#/definitions/QueryPlanReuseMode", + "description": "#/definitions/QueryPlanReuseMode" }, "legacy_introspection_caching": { "default": true, diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 27f0fb696f..0d4eae8afd 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -23,11 +23,13 @@ use tower::ServiceExt; use tower_service::Service; use tracing::Instrument; +use super::dual_query_planner::opt_plan_node_matches; use super::fetch::QueryHash; use crate::cache::estimate_size; use crate::cache::storage::InMemoryCache; use crate::cache::storage::ValueType; use crate::cache::DeduplicatingCache; +use crate::configuration::QueryPlanReuseMode; use crate::error::CacheResolverError; use crate::error::QueryPlannerError; use crate::plugins::authorization::AuthorizationPlugin; @@ -78,7 +80,7 @@ pub(crate) struct CachingQueryPlanner { subgraph_schemas: Arc>>>, plugins: Arc, enable_authorization_directives: bool, - experimental_reuse_query_plans: bool, + experimental_reuse_query_plans: QueryPlanReuseMode, config_mode: Arc, introspection: bool, legacy_introspection_caching: bool, @@ -180,7 +182,7 @@ where persisted_query_layer: &PersistedQueryLayer, previous_cache: Option, count: Option, - experimental_reuse_query_plans: bool, + experimental_reuse_query_plans: QueryPlanReuseMode, experimental_pql_prewarm: bool, ) { let _timer = Timer::new(|duration| { @@ -280,6 +282,7 @@ where let mut count = 0usize; let mut reused = 0usize; + let mut could_have_reused = 0usize; for WarmUpCachingQueryKey { mut query, operation_name, @@ -302,7 +305,7 @@ where let caching_key = CachingQueryKey { query: query.clone(), operation: operation_name.clone(), - hash: if experimental_reuse_query_plans { + hash: if experimental_reuse_query_plans == QueryPlanReuseMode::Reuse { CachingQueryHash::Reuse(doc.hash.clone()) } else { CachingQueryHash::DoNotReuse { @@ -310,14 +313,15 @@ where schema_hash: self.schema.hash.clone(), } }, - metadata, - plan_options, + metadata: metadata.clone(), + plan_options: plan_options.clone(), config_mode: self.config_mode.clone(), introspection: self.introspection, }; - if let Some(warmup_hash) = hash { - if experimental_reuse_query_plans { + let mut should_measure = None; + if let Some(warmup_hash) = hash.clone() { + if experimental_reuse_query_plans == QueryPlanReuseMode::Reuse { if let Some(ref previous_cache) = previous_cache { // if the query hash did not change with the schema update, we can reuse the previously cached entry if warmup_hash.schema_aware_query_hash() == &*doc.hash { @@ -329,9 +333,19 @@ where continue; } } - } else if warmup_hash.schema_aware_query_hash() == &*doc.hash { - reused += 1; } + } else if self.experimental_reuse_query_plans == QueryPlanReuseMode::Measure + && warmup_hash.schema_aware_query_hash() == &*doc.hash + { + should_measure = Some(CachingQueryKey { + query: query.clone(), + operation: operation_name.clone(), + hash: warmup_hash.clone(), + metadata: metadata.clone(), + plan_options: plan_options.clone(), + config_mode: self.config_mode.clone(), + introspection: self.introspection, + }); } }; @@ -367,8 +381,8 @@ where }); let request = QueryPlannerRequest { - query, - operation_name, + query: query.clone(), + operation_name: operation_name.clone(), context: context.clone(), }; @@ -381,6 +395,44 @@ where Ok(QueryPlannerResponse { content, .. }) => { if let Some(content) = content.clone() { count += 1; + + // we want to measure query plan reuse + if let Some(reused_cache_key) = should_measure { + if let Some(previous) = previous_cache { + let previous_plan = { + let mut cache = previous.lock().await; + cache.get(&reused_cache_key).cloned() + }; + + if let Some(previous_content) = + previous_plan.and_then(|res| res.ok()) + { + if let ( + QueryPlannerContent::Plan { + plan: previous_plan, + }, + QueryPlannerContent::Plan { plan: new_plan }, + ) = (previous_content, &content) + { + let matched = opt_plan_node_matches( + &Some(&*previous_plan.root), + &Some(&*new_plan.root), + ); + + if matched.is_ok() { + could_have_reused += 1; + } + u64_counter!( + "apollo.router.operations.query_planner.reuse", + "Measure possible mismatches when reusing query plans", + 1, + "is_matched" = matched.is_ok() + ); + } + } + } + } + tokio::spawn(async move { entry.insert(Ok(content.clone())).await; }); @@ -399,12 +451,25 @@ where tracing::debug!("warmed up the query planner cache with {count} queries planned and {reused} queries reused"); - u64_counter!( - "apollo.router.query.planning.warmup.reused", - "The number of query plans that were reused instead of regenerated during query planner warm up", - reused as u64, - query_plan_reuse_active = experimental_reuse_query_plans - ); + match experimental_reuse_query_plans { + QueryPlanReuseMode::DoNotReuse => {} + QueryPlanReuseMode::Reuse => { + u64_counter!( + "apollo.router.query.planning.warmup.reused", + "The number of query plans that were reused instead of regenerated during query planner warm up", + reused as u64, + query_plan_reuse_active = true + ); + } + QueryPlanReuseMode::Measure => { + u64_counter!( + "apollo.router.query.planning.warmup.reused", + "The number of query plans that were reused instead of regenerated during query planner warm up", + could_have_reused as u64, + query_plan_reuse_active = false + ); + } + } } } @@ -513,7 +578,7 @@ where let caching_key = CachingQueryKey { query: request.query.clone(), operation: request.operation_name.to_owned(), - hash: if self.experimental_reuse_query_plans { + hash: if self.experimental_reuse_query_plans == QueryPlanReuseMode::Reuse { CachingQueryHash::Reuse(doc.hash.clone()) } else { CachingQueryHash::DoNotReuse { diff --git a/apollo-router/src/query_planner/dual_query_planner.rs b/apollo-router/src/query_planner/dual_query_planner.rs index 6a880cf538..abcb161243 100644 --- a/apollo-router/src/query_planner/dual_query_planner.rs +++ b/apollo-router/src/query_planner/dual_query_planner.rs @@ -332,7 +332,7 @@ pub fn diff_plan(js_plan: &QueryPlanResult, rust_plan: &QueryPlan) -> String { } } -fn opt_plan_node_matches( +pub(crate) fn opt_plan_node_matches( this: &Option>, other: &Option>, ) -> Result<(), MatchFailure> { diff --git a/apollo-router/src/services/supergraph/service.rs b/apollo-router/src/services/supergraph/service.rs index dec84074f8..f132e2d861 100644 --- a/apollo-router/src/services/supergraph/service.rs +++ b/apollo-router/src/services/supergraph/service.rs @@ -28,6 +28,7 @@ use tracing_futures::Instrument; use crate::batching::BatchQuery; use crate::configuration::Batching; +use crate::configuration::QueryPlanReuseMode; use crate::context::OPERATION_NAME; use crate::error::CacheResolverError; use crate::graphql; @@ -944,7 +945,7 @@ impl SupergraphCreator { persisted_query_layer: &PersistedQueryLayer, previous_cache: Option, count: Option, - experimental_reuse_query_plans: bool, + experimental_reuse_query_plans: QueryPlanReuseMode, experimental_pql_prewarm: bool, ) { self.query_planner_service diff --git a/docs/source/configuration/in-memory-caching.mdx b/docs/source/configuration/in-memory-caching.mdx index 9a85cc630b..c500042c61 100644 --- a/docs/source/configuration/in-memory-caching.mdx +++ b/docs/source/configuration/in-memory-caching.mdx @@ -96,10 +96,10 @@ It can be activated through this option: supergraph: query_planning: warmed_up_queries: 100 - experimental_reuse_query_plans: true + experimental_reuse_query_plans: reuse # possible values: do_not_reuse (default), reuse, measure ``` -To check if the Router deployment would benefit from this option, check the counter metric named `apollo.router.query.planning.warmup.reused` that indicates how many query plans were reused in warmup if its `query_plan_reuse_active` is true, or how many query plans would have been reused if the option was active, when `query_plan_reuse_active` is false. +To check if the Router deployment would benefit from this option, set the option to `measure`. The router will then check, when warming up the cache, if a query plan could have been reused instead of generated, by comparing old and new query plans. It will be visible in the counter metric `apollo.router.query.planning.warmup.reused`, with its attribute `query_plan_reuse_active` set to `true` in "reuse" mode, or `false` in "measure" mode. ## Caching automatic persisted queries (APQ) From c57ec6671c911ef55fdf1c8b02262ca7bcc0002d Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 9 Sep 2024 11:12:41 +0200 Subject: [PATCH 60/64] fix --- apollo-router/src/query_planner/caching_query_planner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 0d4eae8afd..6cd4feb382 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -322,7 +322,7 @@ where let mut should_measure = None; if let Some(warmup_hash) = hash.clone() { if experimental_reuse_query_plans == QueryPlanReuseMode::Reuse { - if let Some(ref previous_cache) = previous_cache { + if let Some(ref previous_cache) = &previous_cache { // if the query hash did not change with the schema update, we can reuse the previously cached entry if warmup_hash.schema_aware_query_hash() == &*doc.hash { if let Some(entry) = @@ -398,7 +398,7 @@ where // we want to measure query plan reuse if let Some(reused_cache_key) = should_measure { - if let Some(previous) = previous_cache { + if let Some(previous) = &previous_cache { let previous_plan = { let mut cache = previous.lock().await; cache.get(&reused_cache_key).cloned() From fc348d78a33e8e2b4785a6eee4dd712d42f87b2f Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 18 Oct 2024 12:20:07 +0200 Subject: [PATCH 61/64] unneeded change --- apollo-router/src/spec/schema.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/schema.rs b/apollo-router/src/spec/schema.rs index 7f9cc9584a..2208a5863e 100644 --- a/apollo-router/src/spec/schema.rs +++ b/apollo-router/src/spec/schema.rs @@ -21,6 +21,7 @@ use crate::error::ParseErrors; use crate::error::SchemaError; use crate::query_planner::OperationKind; use crate::Configuration; + /// A GraphQL schema. pub(crate) struct Schema { pub(crate) raw_sdl: Arc, @@ -331,11 +332,11 @@ impl std::fmt::Debug for Schema { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { raw_sdl, - schema_id: _, supergraph: _, // skip subgraphs, implementers_map, api_schema: _, // skip + schema_id: _, } = self; f.debug_struct("Schema") .field("raw_sdl", raw_sdl) From e22917da92c23123e54243ac4fe300f4c846b284 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 21 Oct 2024 11:51:10 +0200 Subject: [PATCH 62/64] snapshots --- .../type_conditions___test_type_conditions_disabled-2.snap | 4 ++-- .../type_conditions___test_type_conditions_enabled-2.snap | 6 +++--- ..._type_conditions_enabled_generate_query_fragments-2.snap | 6 +++--- ...tions___test_type_conditions_enabled_list_of_list-2.snap | 6 +++--- ...test_type_conditions_enabled_list_of_list_of_list-2.snap | 6 +++--- ...pe_conditions_enabled_shouldnt_make_article_fetch-2.snap | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_disabled-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_disabled-2.snap index 17f0ec285c..1fc491cfbe 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_disabled-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_disabled-2.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "587c887350ef75eaf4b647be94fd682616bcd33909e15fb797cee226e95fa36a", + "schemaAwareHash": "2960376d2fee3d2824d1b865d6a76d9e2443699f7fcb2a55173d695fba2d15f9", "authorization": { "is_authenticated": false, "scopes": [], @@ -137,7 +137,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "622eb49d4e52ff2636348e103f941d04b783fec97de59d0ae6635d9272f97ad8", + "schemaAwareHash": "2fc6648210b90348ebe38aef9e954bce4e35bccb7189613ee08963531299801d", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled-2.snap index 01e49a0721..aabe3a540f 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled-2.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "587c887350ef75eaf4b647be94fd682616bcd33909e15fb797cee226e95fa36a", + "schemaAwareHash": "2960376d2fee3d2824d1b865d6a76d9e2443699f7fcb2a55173d695fba2d15f9", "authorization": { "is_authenticated": false, "scopes": [], @@ -140,7 +140,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a0bf36d3a611df53c3a60b9b124a2887f2d266858221c606ace0985d101d64bd", + "schemaAwareHash": "4cf32f2a341741d4d1aefce12d606d5d8256da8272ade7b09ebdd8d0a85b25f1", "authorization": { "is_authenticated": false, "scopes": [], @@ -199,7 +199,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3e84a53f967bf40d4c08254a94f3fa32a828ab3ad8184a22bb3439c596ecaaf4", + "schemaAwareHash": "d66b482dcecd848c357053199209a93c8e16566c064e39e321255e234eccb44c", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_generate_query_fragments-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_generate_query_fragments-2.snap index ed94ed7a85..0524fc1574 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_generate_query_fragments-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_generate_query_fragments-2.snap @@ -79,7 +79,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "70ca85b28e861b24a7749862930a5f965c4c6e8074d60a87a3952d596fe7cc36", + "schemaAwareHash": "abf4e5132d29ce6e9358b4040344a447d8330819b4cd79d56d928b6ea9c59c14", "authorization": { "is_authenticated": false, "scopes": [], @@ -140,7 +140,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "1d21a65a3b5a31e17f7834750ef5b37fb49d99d0a1e2145f00a62d43c5f8423a", + "schemaAwareHash": "d6ec6a575e11bfa6c561c4bc14d3a335ec8978cca3dd2fa1d7da4a21802faee5", "authorization": { "is_authenticated": false, "scopes": [], @@ -199,7 +199,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "df321f6532c2c9eda0d8c042e5f08073c24e558dd0cae01054886b79416a6c08", + "schemaAwareHash": "e28fe0a3bcc056d2e35f3f9144624d7d6a68c01ba4f0a2aeb897af5e8fcad34f", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list-2.snap index c990725153..ba4734addb 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list-2.snap @@ -141,7 +141,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "365685f6b9f1c5dd02506b27f50d63486f1ca6b5ced7b0253fc050ef73732e03", + "schemaAwareHash": "bb14a52022a449e0cddb6950d8f29156d4e43968aef0d5b4f9229efd6e4a98fa", "authorization": { "is_authenticated": false, "scopes": [], @@ -203,7 +203,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a0bf36d3a611df53c3a60b9b124a2887f2d266858221c606ace0985d101d64bd", + "schemaAwareHash": "4cf32f2a341741d4d1aefce12d606d5d8256da8272ade7b09ebdd8d0a85b25f1", "authorization": { "is_authenticated": false, "scopes": [], @@ -263,7 +263,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3e84a53f967bf40d4c08254a94f3fa32a828ab3ad8184a22bb3439c596ecaaf4", + "schemaAwareHash": "d66b482dcecd848c357053199209a93c8e16566c064e39e321255e234eccb44c", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list_of_list-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list_of_list-2.snap index f206e94c89..d5846c924d 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list_of_list-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_list_of_list_of_list-2.snap @@ -145,7 +145,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "47d7643dd7226529814a57e0382aafb3790c2d8a8b26354aa2f60e9c9f097a05", + "schemaAwareHash": "89a27ab82257e3b23e6ef8164f85362c3c8c3d1c3c293354e52c6e91e95a171e", "authorization": { "is_authenticated": false, "scopes": [], @@ -208,7 +208,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a0bf36d3a611df53c3a60b9b124a2887f2d266858221c606ace0985d101d64bd", + "schemaAwareHash": "4cf32f2a341741d4d1aefce12d606d5d8256da8272ade7b09ebdd8d0a85b25f1", "authorization": { "is_authenticated": false, "scopes": [], @@ -269,7 +269,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3e84a53f967bf40d4c08254a94f3fa32a828ab3ad8184a22bb3439c596ecaaf4", + "schemaAwareHash": "d66b482dcecd848c357053199209a93c8e16566c064e39e321255e234eccb44c", "authorization": { "is_authenticated": false, "scopes": [], diff --git a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_shouldnt_make_article_fetch-2.snap b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_shouldnt_make_article_fetch-2.snap index 41a6433f9f..d86aadbb45 100644 --- a/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_shouldnt_make_article_fetch-2.snap +++ b/apollo-router/tests/snapshots/type_conditions___test_type_conditions_enabled_shouldnt_make_article_fetch-2.snap @@ -54,7 +54,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "587c887350ef75eaf4b647be94fd682616bcd33909e15fb797cee226e95fa36a", + "schemaAwareHash": "2960376d2fee3d2824d1b865d6a76d9e2443699f7fcb2a55173d695fba2d15f9", "authorization": { "is_authenticated": false, "scopes": [], @@ -115,7 +115,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "a0bf36d3a611df53c3a60b9b124a2887f2d266858221c606ace0985d101d64bd", + "schemaAwareHash": "4cf32f2a341741d4d1aefce12d606d5d8256da8272ade7b09ebdd8d0a85b25f1", "authorization": { "is_authenticated": false, "scopes": [], @@ -174,7 +174,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "3e84a53f967bf40d4c08254a94f3fa32a828ab3ad8184a22bb3439c596ecaaf4", + "schemaAwareHash": "d66b482dcecd848c357053199209a93c8e16566c064e39e321255e234eccb44c", "authorization": { "is_authenticated": false, "scopes": [], From 3d053cbbb91eaffc4e06cf9315cecec9a0fa0841 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Mon, 21 Oct 2024 13:52:31 +0200 Subject: [PATCH 63/64] snapshots --- apollo-router/tests/snapshots/set_context__set_context.snap | 4 ++-- .../set_context__set_context_dependent_fetch_failure.snap | 4 ++-- .../tests/snapshots/set_context__set_context_list.snap | 4 ++-- .../snapshots/set_context__set_context_list_of_lists.snap | 4 ++-- .../snapshots/set_context__set_context_no_typenames.snap | 4 ++-- .../snapshots/set_context__set_context_type_mismatch.snap | 4 ++-- .../tests/snapshots/set_context__set_context_union.snap | 6 +++--- .../set_context__set_context_unrelated_fetch_failure.snap | 6 +++--- .../tests/snapshots/set_context__set_context_with_null.snap | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apollo-router/tests/snapshots/set_context__set_context.snap b/apollo-router/tests/snapshots/set_context__set_context.snap index 18bfcbfcc9..3f7059c6d2 100644 --- a/apollo-router/tests/snapshots/set_context__set_context.snap +++ b/apollo-router/tests/snapshots/set_context__set_context.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "0163c552923b61fbde6dbcd879ffc2bb887175dc41bbf75a272875524e664e8d", + "schemaAwareHash": "ec3ea34c0ed4070f200264b1fe72341913e75b6c004318c59ae1e8560d79230a", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "e64d79913c52a4a8b95bfae44986487a1ac73118f27df3b602972a5cbb1f360a", + "schemaAwareHash": "17a15203bd16920e0e31fe1777094f154a5aed8709241825cd6a2eea7396daf4", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap index 4c44587cdd..de3479ad55 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_dependent_fetch_failure.snap @@ -40,7 +40,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_dependent_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "6bcaa7a2d52a416d5278eaef6be102427f328b6916075f193c87459516a7fb6d", + "schemaAwareHash": "7aa4f670f51cba253cb3b27855680c35207a0c56ddd3d098ff965ddd599593db", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -86,7 +86,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "0e56752501c8cbf53429c5aa2df95765ea2c7cba95db9213ce42918699232651", + "schemaAwareHash": "db2d72c6573995591b3355efbceb994871dd83e8a77b527ecdd3d066d37c5d14", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list.snap b/apollo-router/tests/snapshots/set_context__set_context_list.snap index d6dd312f0a..09755f69d4 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list.snap @@ -40,7 +40,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "805348468cefee0e3e745cb1bcec0ab4bd44ba55f6ddb91e52e0bc9b437c2dee", + "schemaAwareHash": "4bcd5f0282828f0ba652cdfdcc165d62fa860436658b46e1bb4951db865ac518", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -86,7 +86,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "e64d79913c52a4a8b95bfae44986487a1ac73118f27df3b602972a5cbb1f360a", + "schemaAwareHash": "17a15203bd16920e0e31fe1777094f154a5aed8709241825cd6a2eea7396daf4", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap index c390c1db88..692725ad41 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_list_of_lists.snap @@ -44,7 +44,7 @@ expression: response "operationKind": "query", "operationName": "QueryLL__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "53e85332dda78d566187c8886c207b81acfe3ab5ea0cafd3d71fb0b153026d80", + "schemaAwareHash": "13afbbf118b8d9dd76b6f2a8c3dba8fd4cf4df7d9d94b1087809ee50a112ffc8", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -90,7 +90,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "8ed6f85b6a77c293c97171b4a98f7dd563e98a737d4c3a9f5c54911248498ec7", + "schemaAwareHash": "3041669e92aa9425e34a91eda88a22c2cd94f4479602091779e2e0547721c3e4", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap index e9743a7902..47895b7b8c 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_no_typenames.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "0163c552923b61fbde6dbcd879ffc2bb887175dc41bbf75a272875524e664e8d", + "schemaAwareHash": "ec3ea34c0ed4070f200264b1fe72341913e75b6c004318c59ae1e8560d79230a", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "e64d79913c52a4a8b95bfae44986487a1ac73118f27df3b602972a5cbb1f360a", + "schemaAwareHash": "17a15203bd16920e0e31fe1777094f154a5aed8709241825cd6a2eea7396daf4", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap index 3208b9bf0a..f10c95dfa6 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_type_mismatch.snap @@ -32,7 +32,7 @@ expression: response "operationKind": "query", "operationName": "Query_type_mismatch__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "34c8f7c0f16220c5d4b589c8da405f49510e092756fa98629c73dea06fd7c243", + "schemaAwareHash": "e0289323a181883b84010cb607dbf828c6773f0bdf0753b7f1c0a2533bd95a6c", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -78,7 +78,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "feb578fd1831280f376d8961644e670dd8c3508d0a18fcf69a6de651e25e9ca8", + "schemaAwareHash": "67cb2b44e80b2d2aef67f644266f8bf5dae938409471cc6268926c0b182273af", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_union.snap b/apollo-router/tests/snapshots/set_context__set_context_union.snap index 6c995c1e8b..dc8431d028 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_union.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_union.snap @@ -31,7 +31,7 @@ expression: response "operationKind": "query", "operationName": "QueryUnion__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "3e768a1879f4ced427937721980688052b471dbfee0d653b212c85f2732591cc", + "schemaAwareHash": "eb98783496e031193715cf058b1b659671ea7131b38b32ba2973b1b028f762ad", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -80,7 +80,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "0c190d5db5b15f89fa45de844d2cec59725986e44fcb0dbdb9ab870a197cf026", + "schemaAwareHash": "3d506faa16c3456df832e564ae7a9dac1779134ad87604ab752fabe5e34ee4fb", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" @@ -134,7 +134,7 @@ expression: response "typeCondition": "V" } ], - "schemaAwareHash": "2d7376a8d1f7f2a929361e838bb0435ed4c4a6194fa8754af52d4b6dc7140508", + "schemaAwareHash": "78b943b7d7566f85eddfc8e8aff286af32d8b3450bc62b3fb118110c87ff240d", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_1" diff --git a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap index 49dcf6bf9b..b1a2cacb61 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_unrelated_fetch_failure.snap @@ -34,7 +34,7 @@ expression: response "operationKind": "query", "operationName": "Query_fetch_failure__Subgraph1__0", "outputRewrites": null, - "schemaAwareHash": "84a7305d62d79b5bbca976c5522d6b32c5bbcbf76b495e4430f9cdcb51c80a57", + "schemaAwareHash": "6b8c2a9f2847d961c536706cac0798dd0666c98e3489c04b07f0df1f94698148", "serviceName": "Subgraph1", "variableUsages": [] }, @@ -73,7 +73,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "acb960692b01a756fcc627cafef1c47ead8afa60fa70828e5011ba9f825218ab", + "schemaAwareHash": "7bc33d264b52d0c7ee6871577c0ba63b2a5f047d81db18fafe816a948930ac98", "serviceName": "Subgraph2", "variableUsages": [] }, @@ -125,7 +125,7 @@ expression: response "typeCondition": "U" } ], - "schemaAwareHash": "9fd65f6f213899810bce20180de6754354a25dc3c1bc97d0b7214a177cf8b0bb", + "schemaAwareHash": "3d88658e3a30ae5809704283ae8a57fe9d2ea9b088871f38431b2c1bb54d8e2b", "serviceName": "Subgraph1", "variableUsages": [ "contextualArgument_1_0" diff --git a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap index badc32bc8a..c5be3c1794 100644 --- a/apollo-router/tests/snapshots/set_context__set_context_with_null.snap +++ b/apollo-router/tests/snapshots/set_context__set_context_with_null.snap @@ -29,7 +29,7 @@ expression: response "inputRewrites": null, "outputRewrites": null, "contextRewrites": null, - "schemaAwareHash": "4c0c9f83a57e9a50ff1f6dd601ec0a1588f1485d5cfb1015822af4017263e807", + "schemaAwareHash": "c6bc982bb5cb5c357f965fe2323f17244e7a1d1729bf767643be6c1ef7b90622", "authorization": { "is_authenticated": false, "scopes": [], @@ -82,7 +82,7 @@ expression: response "renameKeyTo": "contextualArgument_1_0" } ], - "schemaAwareHash": "8db802e78024d406645f1ddc8972255e917bc738bfbed281691a45e34c92debb", + "schemaAwareHash": "15de9571ee35cf4ada409fc897e1451e34dfec7e1fcf8f70e74dcf200349bf9c", "authorization": { "is_authenticated": false, "scopes": [], From 72bc40503581fcdb03b4e2c41ee8461eb391f136 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Tue, 29 Oct 2024 09:52:44 +0100 Subject: [PATCH 64/64] add separators for the query plan cache key --- .../src/query_planner/caching_query_planner.rs | 3 +++ apollo-router/tests/integration/redis.rs | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/query_planner/caching_query_planner.rs b/apollo-router/src/query_planner/caching_query_planner.rs index 5c08f1ceb3..00b2043178 100644 --- a/apollo-router/src/query_planner/caching_query_planner.rs +++ b/apollo-router/src/query_planner/caching_query_planner.rs @@ -746,8 +746,11 @@ impl std::fmt::Display for CachingQueryKey { let operation = hex::encode(hasher.finalize()); let mut hasher = StructHasher::new(); + "^metadata".hash(&mut hasher); self.metadata.hash(&mut hasher); + "^plan_options".hash(&mut hasher); self.plan_options.hash(&mut hasher); + "^config_mode".hash(&mut hasher); self.config_mode.hash(&mut hasher); let metadata = hex::encode(hasher.finalize()); diff --git a/apollo-router/tests/integration/redis.rs b/apollo-router/tests/integration/redis.rs index 76c7b04163..2412f0958b 100644 --- a/apollo-router/tests/integration/redis.rs +++ b/apollo-router/tests/integration/redis.rs @@ -51,7 +51,7 @@ async fn query_planner_cache() -> Result<(), BoxError> { } // If this test fails and the cache key format changed you'll need to update the key here. // Look at the top of the file for instructions on getting the new cache key. - let known_cache_key = "plan:cache:1:federation:v2.9.2:schema:087288dbcef764e15c3088f332a47568275df3573719b7bbd358ede489a492e4:query:a64923334e18d0422a5b4485e3d4a4911839f72b3cc1e8582771c0b670a7ca64:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:fffd4213337929d098b1d9f7c7337d0fd167ebc4c3c4c98ffc5bb5e1d164d996"; + let known_cache_key = "plan:cache:1:federation:v2.9.3:schema:087288dbcef764e15c3088f332a47568275df3573719b7bbd358ede489a492e4:query:a64923334e18d0422a5b4485e3d4a4911839f72b3cc1e8582771c0b670a7ca64:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:847dd433267f02ffb5c21451f893b7c8cefead56fbd67b1ff188a9033f19970b"; let config = RedisConfig::from_url("redis://127.0.0.1:6379").unwrap(); let client = RedisClient::new(config, None, None, None); let connection_task = client.connect(); @@ -1004,7 +1004,7 @@ async fn connection_failure_blocks_startup() { async fn query_planner_redis_update_query_fragments() { test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_query_fragments.router.yaml"), - "plan:cache:1:federation:v2.9.2:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:cbfa8570d4e57116629a00bedd4391cb311a075269d19f311a760f0993bb0d0c" + "plan:cache:1:federation:v2.9.3:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:705198456b77953ec7d3eb4e09a6f10d96fcf02f5d5b4d6eac1bf24edc702338" ) .await; } @@ -1034,7 +1034,7 @@ async fn query_planner_redis_update_defer() { // test just passes locally. test_redis_query_plan_config_update( include_str!("fixtures/query_planner_redis_config_update_defer.router.yaml"), - "plan:cache:1:federation:v2.9.2:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:4148d368d81bada698527f710e6f71b78e632d827184a463f937b1b87af1f3c4" + "plan:cache:1:federation:v2.9.3:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:aecf7a3e824ed276b03ccea7149dea044d4c8cdb2bf6f01450d6b44619df26d6" ) .await; } @@ -1056,7 +1056,7 @@ async fn query_planner_redis_update_type_conditional_fetching() { include_str!( "fixtures/query_planner_redis_config_update_type_conditional_fetching.router.yaml" ), - "plan:cache:1:federation:v2.9.2:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:0fde8b7bd9fc39dc9c7d077aae4fe1756bfdb9df7d07de5b01712582b553c8e2" + "plan:cache:1:federation:v2.9.3:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:fdee5baffce546145614696374b1e41913b2b8db17de50ec1c8ba58f3eb1d856" ) .await; } @@ -1078,7 +1078,7 @@ async fn query_planner_redis_update_reuse_query_fragments() { include_str!( "fixtures/query_planner_redis_config_update_reuse_query_fragments.router.yaml" ), - "plan:cache:1:federation:v2.9.2:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:027e66f8bc66b24abf2d64cddcdf9dab8aec4e84538c16db4d78d16e146cba3c" + "plan:cache:1:federation:v2.9.3:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:a20af48aa2814481e8426052fadd98e82e52b492d08d015600769cc1d5782297" ) .await; } @@ -1103,7 +1103,7 @@ async fn test_redis_query_plan_config_update(updated_config: &str, new_cache_key router.clear_redis_cache().await; // If the tests above are failing, this is the key that needs to be changed first. - let starting_key = "plan:cache:1:federation:v2.9.2:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:fffd4213337929d098b1d9f7c7337d0fd167ebc4c3c4c98ffc5bb5e1d164d996"; + let starting_key = "plan:cache:1:federation:v2.9.3:schema:dd8960ccefda82ca58e8ac0bc266459fd49ee8215fd6b3cc72e7bc3d7f3464b9:query:b35a54b1486c29af240eaee4af97bb0e63c2298bafc898f29ebdfda369697ef7:opname:3973e022e93220f9212c18d0d0c543ae7c309e46640da93a4a0314de999f5112:metadata:847dd433267f02ffb5c21451f893b7c8cefead56fbd67b1ff188a9033f19970b"; assert_ne!(starting_key, new_cache_key, "starting_key (cache key for the initial config) and new_cache_key (cache key with the updated config) should not be equal. This either means that the cache key is not being generated correctly, or that the test is not actually checking the updated key."); router.execute_default_query().await;