Skip to content

Commit 95c54b4

Browse files
tomgassonfacebook-github-bot
authored andcommitted
Allow persisting to also include the query text for safe migration (#3917)
Summary: Hi folks. We're attempting to start using persisted queries. However, we're not 100% confident in our ability to switch over to them in one big step safely. Fetching the persisted documents is a new capability for our GraphQL server. If something goes wrong fetching the document, or if it introduces unexpected latency to our existing production queries we want to be able to fallback to the query text until we're more confident in its maturity. Relay currently supports outputting the query text, or outputting the persisted document id. This PR introduces a middle state, where the text and id are both output. When both are sent with the GraphQL request from the client to the server, the server is able to control the rollout of persisted queries via a feature flag. This PR introduces a `safeMigration` bool to the remote persist config, which when set will also output the query text in the operation params as well as the id ```js persistConfig: { url: ..., params: ..., includeQueryText: true } ``` The benefits of persisted queries (saving the network bytes) won't come until we've progressed past the migration stage, but it makes it possible to do the move in the first place. Pull Request resolved: #3917 Reviewed By: tyao1 Differential Revision: D46075478 Pulled By: captbaritone fbshipit-source-id: 528ce0cec6fcee50ea80f416edb2e4568c487b77
1 parent c530888 commit 95c54b4

File tree

4 files changed

+68
-66
lines changed

4 files changed

+68
-66
lines changed

compiler/crates/relay-codegen/src/build_ast.rs

Lines changed: 38 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,24 +1874,21 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
18741874
metadata_items.sort_unstable_by_key(|entry| entry.key);
18751875

18761876
// Construct metadata object
1877-
let metadata_prop = ObjectEntry {
1878-
key: CODEGEN_CONSTANTS.metadata,
1879-
value: Primitive::Key(self.object(metadata_items)),
1880-
};
1881-
let name_prop = ObjectEntry {
1882-
key: CODEGEN_CONSTANTS.name,
1883-
value: Primitive::String(request_parameters.name),
1884-
};
1885-
let operation_kind_prop = ObjectEntry {
1886-
key: CODEGEN_CONSTANTS.operation_kind,
1887-
value: Primitive::String(match request_parameters.operation_kind {
1888-
OperationKind::Query => CODEGEN_CONSTANTS.query,
1889-
OperationKind::Mutation => CODEGEN_CONSTANTS.mutation,
1890-
OperationKind::Subscription => CODEGEN_CONSTANTS.subscription,
1891-
}),
1892-
};
1877+
let mut params_object = vec![];
18931878

1894-
let id_prop = ObjectEntry {
1879+
if let Some(ref text) = &request_parameters.text {
1880+
params_object.push(ObjectEntry {
1881+
key: CODEGEN_CONSTANTS.cache_id,
1882+
value: Primitive::RawString(md5(text)),
1883+
});
1884+
} else if request_parameters.id.is_none() {
1885+
params_object.push(ObjectEntry {
1886+
key: CODEGEN_CONSTANTS.cache_id,
1887+
value: Primitive::RawString(md5(operation.name.item.0.lookup())),
1888+
});
1889+
}
1890+
1891+
params_object.push(ObjectEntry {
18951892
key: CODEGEN_CONSTANTS.id,
18961893
value: match request_parameters.id {
18971894
Some(QueryID::Persisted { id, .. }) => Primitive::RawString(id.clone()),
@@ -1903,50 +1900,31 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
19031900
}
19041901
None => Primitive::Null,
19051902
},
1906-
};
1903+
});
1904+
params_object.push(ObjectEntry {
1905+
key: CODEGEN_CONSTANTS.metadata,
1906+
value: Primitive::Key(self.object(metadata_items)),
1907+
});
1908+
params_object.push(ObjectEntry {
1909+
key: CODEGEN_CONSTANTS.name,
1910+
value: Primitive::String(request_parameters.name),
1911+
});
1912+
params_object.push(ObjectEntry {
1913+
key: CODEGEN_CONSTANTS.operation_kind,
1914+
value: Primitive::String(match request_parameters.operation_kind {
1915+
OperationKind::Query => CODEGEN_CONSTANTS.query,
1916+
OperationKind::Mutation => CODEGEN_CONSTANTS.mutation,
1917+
OperationKind::Subscription => CODEGEN_CONSTANTS.subscription,
1918+
}),
1919+
});
19071920

1908-
let mut params_object = if let Some(text) = request_parameters.text {
1909-
vec![
1910-
ObjectEntry {
1911-
key: CODEGEN_CONSTANTS.cache_id,
1912-
value: Primitive::RawString(md5(&text)),
1913-
},
1914-
id_prop,
1915-
metadata_prop,
1916-
name_prop,
1917-
operation_kind_prop,
1918-
ObjectEntry {
1919-
key: CODEGEN_CONSTANTS.text,
1920-
value: Primitive::RawString(text),
1921-
},
1922-
]
1923-
} else if request_parameters.id.is_some() {
1924-
vec![
1925-
id_prop,
1926-
metadata_prop,
1927-
name_prop,
1928-
operation_kind_prop,
1929-
ObjectEntry {
1930-
key: CODEGEN_CONSTANTS.text,
1931-
value: Primitive::Null,
1932-
},
1933-
]
1934-
} else {
1935-
vec![
1936-
ObjectEntry {
1937-
key: CODEGEN_CONSTANTS.cache_id,
1938-
value: Primitive::RawString(md5(operation.name.item.0.lookup())),
1939-
},
1940-
id_prop,
1941-
metadata_prop,
1942-
name_prop,
1943-
operation_kind_prop,
1944-
ObjectEntry {
1945-
key: CODEGEN_CONSTANTS.text,
1946-
value: Primitive::Null,
1947-
},
1948-
]
1949-
};
1921+
params_object.push(ObjectEntry {
1922+
key: CODEGEN_CONSTANTS.text,
1923+
value: match request_parameters.text {
1924+
Some(text) => Primitive::RawString(text),
1925+
None => Primitive::Null,
1926+
},
1927+
});
19501928

19511929
let provided_variables = if top_level_statements
19521930
.contains(CODEGEN_CONSTANTS.provided_variables_definition.lookup())

compiler/crates/relay-compiler/src/artifact_content/content.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,20 @@ pub fn generate_operation(
176176
fragment_locations: &FragmentLocations,
177177
) -> Result<Vec<u8>, FmtError> {
178178
let mut request_parameters = build_request_params(normalization_operation);
179+
179180
if id_and_text_hash.is_some() {
180181
request_parameters.id = id_and_text_hash;
182+
if project_config
183+
.persist
184+
.as_ref()
185+
.map_or(false, |config| config.include_query_text())
186+
{
187+
request_parameters.text = text.clone();
188+
}
181189
} else {
182190
request_parameters.text = text.clone();
183-
};
191+
}
192+
184193
let operation_fragment = FragmentDefinition {
185194
name: reader_operation.name.map(|x| FragmentDefinitionName(x.0)),
186195
variable_definitions: reader_operation.variable_definitions.clone(),

compiler/crates/relay-config/src/project_config.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type FnvIndexMap<K, V> = IndexMap<K, V, FnvBuildHasher>;
4343
pub type ProjectName = StringKey;
4444

4545
#[derive(Clone, Debug, Serialize, Deserialize)]
46-
#[serde(deny_unknown_fields)]
46+
#[serde(deny_unknown_fields, rename_all = "camelCase")]
4747
pub struct RemotePersistConfig {
4848
/// URL to send a POST request to to persist.
4949
pub url: String,
@@ -62,6 +62,9 @@ pub struct RemotePersistConfig {
6262
deserialize_with = "deserialize_semaphore_permits"
6363
)]
6464
pub semaphore_permits: Option<usize>,
65+
66+
#[serde(default)]
67+
pub include_query_text: bool,
6568
}
6669

6770
fn deserialize_semaphore_permits<'de, D>(d: D) -> Result<Option<usize>, D::Error>
@@ -98,6 +101,9 @@ pub struct LocalPersistConfig {
98101

99102
#[serde(default)]
100103
pub algorithm: LocalPersistAlgorithm,
104+
105+
#[serde(default)]
106+
pub include_query_text: bool,
101107
}
102108

103109
#[derive(Debug, Serialize, Clone)]
@@ -107,6 +113,15 @@ pub enum PersistConfig {
107113
Local(LocalPersistConfig),
108114
}
109115

116+
impl PersistConfig {
117+
pub fn include_query_text(&self) -> bool {
118+
match self {
119+
PersistConfig::Remote(remote_config) => remote_config.include_query_text,
120+
PersistConfig::Local(local_config) => local_config.include_query_text,
121+
}
122+
}
123+
}
124+
110125
impl<'de> Deserialize<'de> for PersistConfig {
111126
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
112127
let value = Value::deserialize(deserializer)?;

packages/relay-runtime/util/RelayConcreteNode.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ export type ProvidedVariablesType = {+[key: string]: {get(): mixed}};
4343

4444
/**
4545
* Contains the parameters required for executing a GraphQL request.
46-
* The operation can either be provided as a persisted `id` or `text`. If given
47-
* in `text` format, a `cacheID` as a hash of the text should be set to be used
48-
* for local caching.
46+
* The operation can either be provided as a persisted `id` or `text` or both.
47+
* If `text` format is provided, a `cacheID` as a hash of the text should be set
48+
* to be used for local caching.
4949
*/
5050
export type RequestParameters =
5151
| {
5252
+id: string,
53-
+text: null,
53+
+text: string | null,
5454
// common fields
5555
+name: string,
5656
+operationKind: 'mutation' | 'query' | 'subscription',

0 commit comments

Comments
 (0)