apollo_federation/query_plan/mod.rs
1use std::sync::Arc;
2
3use apollo_compiler::ExecutableDocument;
4use apollo_compiler::Name;
5use apollo_compiler::executable;
6use apollo_compiler::validation::Valid;
7use serde::Serialize;
8
9use crate::query_plan::query_planner::QueryPlanningStatistics;
10
11pub(crate) mod conditions;
12pub(crate) mod display;
13pub(crate) mod fetch_dependency_graph;
14pub(crate) mod fetch_dependency_graph_processor;
15pub mod generate;
16pub mod query_planner;
17pub(crate) mod query_planning_traversal;
18
19pub type QueryPlanCost = f64;
20
21// NOTE: This type implements `Serialize` for debugging purposes; however, it should not implement
22// `Deserialize` until two requires are met.
23// 1) `SelectionId`s and `OverrideId`s are only unique per lifetime of the application. To avoid
24// problems when caching, this needs to be changes.
25// 2) There are several types transatively used in the query plan that are from `apollo-compiler`.
26// They are serialized as strings and use the `serialize` methods provided by that crate. In
27// order to implement `Deserialize`, care needs to be taken to deserialize these correctly.
28// Moreover, how we serialize these types should also be revisited to make sure we can and want
29// to support how they are serialized long term (e.g. how `DirectiveList` is serialized can be
30// optimized).
31#[derive(Debug, Default, PartialEq, Serialize)]
32pub struct QueryPlan {
33 pub node: Option<TopLevelPlanNode>,
34 pub statistics: QueryPlanningStatistics,
35}
36
37#[derive(Debug, PartialEq, derive_more::From, Serialize)]
38pub enum TopLevelPlanNode {
39 Subscription(SubscriptionNode),
40 #[from(types(FetchNode))]
41 Fetch(Box<FetchNode>),
42 Sequence(SequenceNode),
43 Parallel(ParallelNode),
44 Flatten(FlattenNode),
45 Defer(DeferNode),
46 #[from(types(ConditionNode))]
47 Condition(Box<ConditionNode>),
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize)]
51pub struct SubscriptionNode {
52 pub primary: Box<FetchNode>,
53 // XXX(@goto-bus-stop) Is this not just always a SequenceNode?
54 pub rest: Option<Box<PlanNode>>,
55}
56
57#[derive(Debug, Clone, PartialEq, derive_more::From, Serialize)]
58pub enum PlanNode {
59 #[from(types(FetchNode))]
60 Fetch(Box<FetchNode>),
61 Sequence(SequenceNode),
62 Parallel(ParallelNode),
63 Flatten(FlattenNode),
64 Defer(DeferNode),
65 #[from(types(ConditionNode))]
66 Condition(Box<ConditionNode>),
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize)]
70pub struct FetchNode {
71 pub subgraph_name: Arc<str>,
72 /// Optional identifier for the fetch for defer support. All fetches of a given plan will be
73 /// guaranteed to have a unique `id`.
74 pub id: Option<u64>,
75 pub variable_usages: Vec<Name>,
76 /// `Selection`s in apollo-rs _can_ have a `FragmentSpread`, but this `Selection` is
77 /// specifically typing the `requires` key in a built query plan, where there can't be
78 /// `FragmentSpread`.
79 // PORT_NOTE: This was its own type in the JS codebase, but it's likely simpler to just have the
80 // constraint be implicit for router instead of creating a new type.
81 #[serde(
82 serialize_with = "crate::utils::serde_bridge::serialize_optional_vec_of_exe_selection"
83 )]
84 pub requires: Option<Vec<executable::Selection>>,
85 // PORT_NOTE: We don't serialize the "operation" string in this struct, as these query plan
86 // nodes are meant for direct consumption by router (without any serdes), so we leave the
87 // question of whether it needs to be serialized to router.
88 #[serde(serialize_with = "crate::utils::serde_bridge::serialize_valid_executable_document")]
89 pub operation_document: Valid<ExecutableDocument>,
90 pub operation_name: Option<Name>,
91 #[serde(serialize_with = "crate::utils::serde_bridge::serialize_exe_operation_type")]
92 pub operation_kind: executable::OperationType,
93 /// Optionally describe a number of "rewrites" that query plan executors should apply to the
94 /// data that is sent as the input of this fetch. Note that such rewrites should only impact the
95 /// inputs of the fetch they are applied to (meaning that, as those inputs are collected from
96 /// the current in-memory result, the rewrite should _not_ impact said in-memory results, only
97 /// what is sent in the fetch).
98 pub input_rewrites: Arc<Vec<Arc<FetchDataRewrite>>>,
99 /// Similar to `input_rewrites`, but for optional "rewrites" to apply to the data that is
100 /// received from a fetch (and before it is applied to the current in-memory results).
101 pub output_rewrites: Vec<Arc<FetchDataRewrite>>,
102 /// Similar to the other kinds of rewrites. This is a mechanism to convert a contextual path into
103 /// an argument to a resolver. Note value setters are currently unused here, but may be used in
104 /// the future.
105 pub context_rewrites: Vec<Arc<FetchDataRewrite>>,
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize)]
109pub struct SequenceNode {
110 pub nodes: Vec<PlanNode>,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize)]
114pub struct ParallelNode {
115 pub nodes: Vec<PlanNode>,
116}
117
118#[derive(Debug, Clone, PartialEq, Serialize)]
119pub struct FlattenNode {
120 pub path: Vec<FetchDataPathElement>,
121 pub node: Box<PlanNode>,
122}
123
124/// A `DeferNode` corresponds to one or more `@defer` applications at the same level of "nestedness"
125/// in the planned query.
126///
127/// It contains a "primary block" and a vector of "deferred blocks". The "primary block" represents
128/// the part of the query that is _not_ deferred (so the part of the query up until we reach the
129/// @defer(s) this handles), while each "deferred block" correspond to the deferred part of one of
130/// the @defer(s) handled by the node.
131///
132/// Note that `DeferNode`s are only generated if defer support is enabled for the query planner.
133/// Also note that if said support is enabled, then `DeferNode`s are always generated if the query
134/// has a @defer application, even if in some cases generated plan may not "truly" defer the
135/// underlying fetches (i.e. in cases where `deferred[*].node` are all undefined). This currently
136/// happens because some specific cases of defer cannot be handled, but could later also happen if
137/// we implement more advanced server-side heuristics to decide if deferring is judicious or not.
138/// This allows the executor of the plan to consistently send a defer-abiding multipart response to
139/// the client.
140#[derive(Debug, Clone, PartialEq, Serialize)]
141pub struct DeferNode {
142 /// The "primary" part of a defer, that is the non-deferred part (though could be deferred
143 /// itself for a nested defer).
144 pub primary: PrimaryDeferBlock,
145 /// The "deferred" parts of the defer (note that it's a vector). Each of those deferred elements
146 /// will correspond to a different chunk of the response to the client (after the initial
147 /// on-deferred one that is).
148 pub deferred: Vec<DeferredDeferBlock>,
149}
150
151/// The primary block of a `DeferNode`.
152#[derive(Debug, Clone, PartialEq, Serialize)]
153pub struct PrimaryDeferBlock {
154 /// The part of the original query that "selects" the data to send in that primary response
155 /// once the plan in `node` completes). Note that if the parent `DeferNode` is nested, then it
156 /// must come inside the `DeferredNode` in which it is nested, and in that case this
157 /// sub-selection will start at that parent `DeferredNode.query_path`. Note that this can be
158 /// `None` in the rare case that everything in the original query is deferred (which is not very
159 /// useful in practice, but not disallowed by the @defer spec at the moment).
160 #[serde(skip)]
161 pub sub_selection: Option<executable::SelectionSet>,
162 /// The plan to get all the data for the primary block. Same notes as for subselection: usually
163 /// defined, but can be undefined in some corner cases where nothing is to be done in the
164 /// primary block.
165 pub node: Option<Box<PlanNode>>,
166}
167
168/// A deferred block of a `DeferNode`.
169#[derive(Debug, Clone, PartialEq, Serialize)]
170pub struct DeferredDeferBlock {
171 /// References one or more fetch node(s) (by `id`) within `DeferNode.primary.node`. The plan of
172 /// this deferred part should not be started until all such fetches return.
173 pub depends: Vec<DeferredDependency>,
174 /// The optional defer label.
175 pub label: Option<String>,
176 /// Path, in the query, to the `@defer` application this corresponds to. The `sub_selection`
177 /// starts at this `query_path`.
178 pub query_path: Vec<QueryPathElement>,
179 /// The part of the original query that "selects" the data to send in the deferred response
180 /// (once the plan in `node` completes). Will be set _unless_ `node` is a `DeferNode` itself.
181 #[serde(serialize_with = "crate::utils::serde_bridge::serialize_optional_exe_selection_set")]
182 pub sub_selection: Option<executable::SelectionSet>,
183 /// The plan to get all the data for this deferred block. Usually set, but can be `None` for a
184 /// `@defer` application where everything has been fetched in the "primary block" (i.e. when
185 /// this deferred block only exists to expose what should be send to the upstream client in a
186 /// deferred response), but without declaring additional fetches. This happens for @defer
187 /// applications that cannot be handled through the query planner and where the defer cannot be
188 /// passed through to the subgraph).
189 pub node: Option<Box<PlanNode>>,
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize)]
193pub struct DeferredDependency {
194 /// A `FetchNode` ID.
195 pub id: String,
196}
197
198#[derive(Debug, Clone, PartialEq, Serialize)]
199pub struct ConditionNode {
200 pub condition_variable: Name,
201 pub if_clause: Option<Box<PlanNode>>,
202 pub else_clause: Option<Box<PlanNode>>,
203}
204
205/// The type of rewrites currently supported on the input/output data of fetches.
206///
207/// A rewrite usually identifies some sub-part of the data and some action to perform on that
208/// sub-part.
209#[derive(Debug, Clone, PartialEq, Serialize, derive_more::From)]
210pub enum FetchDataRewrite {
211 ValueSetter(FetchDataValueSetter),
212 KeyRenamer(FetchDataKeyRenamer),
213}
214
215/// A rewrite that sets a value at the provided path of the data it is applied to.
216#[derive(Debug, Clone, PartialEq, Serialize)]
217pub struct FetchDataValueSetter {
218 /// Path to the value that is set by this "rewrite".
219 pub path: Vec<FetchDataPathElement>,
220 /// The value to set at `path`. Note that the query planner currently only uses string values,
221 /// but that may change in the future.
222 pub set_value_to: serde_json_bytes::Value,
223}
224
225/// A rewrite that renames the key at the provided path of the data it is applied to.
226#[derive(Debug, Clone, PartialEq, Serialize)]
227pub struct FetchDataKeyRenamer {
228 /// Path to the key that is renamed by this "rewrite".
229 pub path: Vec<FetchDataPathElement>,
230 /// The key to rename to at `path`.
231 pub rename_key_to: Name,
232}
233
234/// Vectors of this element match path(s) to a value in fetch data. Each element is (1) a key in
235/// object data, (2) _any_ index in array data (often serialized as `@`), (3) a typename constraint
236/// on the object data at that point in the path(s) (a path should only match for objects whose
237/// `__typename` is the provided type), or (4) a parent indicator to move upwards one level in the
238/// object.
239///
240/// It's possible for vectors of this element to match no paths in fetch data, e.g. if an object key
241/// doesn't exist, or if an object's `__typename` doesn't equal the provided one. If this occurs,
242/// then query plan execution should not execute the instruction this path is associated with.
243///
244/// The path starts at the top of the data it is applied to. So for instance, for fetch data inputs,
245/// the path starts at the root of the object representing those inputs.
246///
247/// Note that the `@` is currently optional in some contexts, as query plan execution may assume
248/// upon encountering array data in a path that it should match the remaining path to the array's
249/// elements.
250#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
251pub enum FetchDataPathElement {
252 Key(Name, Option<Conditions>),
253 AnyIndex(Option<Conditions>),
254 TypenameEquals(Name),
255 Parent,
256}
257
258pub type Conditions = Vec<Name>;
259
260/// Vectors of this element match a path in a query. Each element is (1) a field in a query, or (2)
261/// an inline fragment in a query.
262#[derive(Debug, Clone, PartialEq, serde::Serialize)]
263pub enum QueryPathElement {
264 #[serde(serialize_with = "crate::utils::serde_bridge::serialize_exe_field")]
265 Field(executable::Field),
266 #[serde(serialize_with = "crate::utils::serde_bridge::serialize_exe_inline_fragment")]
267 InlineFragment(executable::InlineFragment),
268}
269
270impl PlanNode {
271 /// Returns the kind of plan node this is as a human-readable string. Exact output not guaranteed.
272 fn node_kind(&self) -> &'static str {
273 match self {
274 Self::Fetch(_) => "Fetch",
275 Self::Sequence(_) => "Sequence",
276 Self::Parallel(_) => "Parallel",
277 Self::Flatten(_) => "Flatten",
278 Self::Defer(_) => "Defer",
279 Self::Condition(_) => "Condition",
280 }
281 }
282}