Skip to main content

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}