A plan diagram is a directed acyclic graph made of a number of step nodes connected by arrows which show the flow of data. It also details the LayerPlans (aka "buckets") and the relationships between them.
For the following GraphQL request:
You might see a plan diagram such as:
This diagram has two main sections: the steps, and the buckets. We'll talk about the buckets first.
If you're able to convince mermaid to reliably render these two things on the same diagram in a more independent manner, please get in touch!
Buckets (aka layer plans)
A bucket, or layer plan, is where data for steps at a similar "layer" in the operation go. For each layer, the cardinality is the same for every step - every step deals with the same number of results. Generally buckets are introduced where this may no longer be the case - for example when we start handling more results due to processing the items in a list, or fewer results due to polymorphic filtering or excluding null results from further processing.
Here's an example of a bucket node:
First line (e.g.
Bucket 1 (nullableBoundary))
The first line in the bucket node always start with the word
Bucket followed by the bucket number.
Buckets are numbered from
0, but during the optimization of the operation
plan the need for certain buckets may be eradicated, resulting in "gaps" in the
numbering. Afterwards comes the reason for the bucket, in parenthesis:
subroutine or other.
A line starting with
Deps: indicates the buckets "dependencies" - that is to
say the data from the parent bucket that will be copied into this bucket when
it is created, before execution of any of the steps within the bucket
A line starting with
ROOT indicates the step that represents the "root" of
the bucket, this is often used for checking for nulls/errors and similar
Lines beginning with a number indicate the order in which the steps will be executed in that bucket. Numbered lines with multiple steps listed will execute those steps in parallel since they are independent of each other.
Immediately following a numbered line, there may be a line that begins with "ᐳ:". This indicates the "synchronous and safe" unbatched steps that will be executed immediately after the numbered line - this is an optimization that means the system doesn't need to loop multiple times to satisfy these needs.
Generally when looking at a plan diagram, you care more about the steps than
the buckets. Each step contains a first line that consists of the step class
Step removed (for brevity), followed by
[X∈Y] where X is the id
of the step, and Y is the number of the bucket to which it belongs - the latter
of which is also indicated by the border colour of the step.
Often steps will have a second line of text such as
ᐸ3.currentUserIdᐳ - this
is metadata specific to that particular step class that gives more detail on
what this step is doing.
The shape of the step's border also reveals more details about the step:
Standard synchronous steps
A standard synchronous step is represented by a simple rectangle.
In this example:
__Valueis the name of the Step (but with the redundant 'Step' removed - truly it's called
5is the step ID - every step has a unique identifier.
∈0means that the step "belongs" to LayerPlan (aka "bucket") number
- The next line contains additional step-specific metadata; in this case it's telling us that the step represents the GraphQL
These appear very similar to synchronous steps, except that they have a double border on the left and right. The key difference with these steps, and why they render more prominently, is that this is typically where your work will take place - they execute asynchronously, and so can communicate with remote services and resources.
All step classes you create will generate asynchronous steps unless you specifically opt them in to one of the optimized forms.
__ItemStep steps never execute, they're managed by Grafast manually to
represent individual entries in a list or stream (including subscription event
streams). They look like a trapezoid (US) / trapezium (UK) to imply that they
are going from a smaller set to a larger set, though this isn't always the
Unbatched synchronous steps
An unbatched synchronous step is a special variant of a synchronous step that confers no benefit from batching. This allows the system to calculate their values alongside their dependencies without having to loop multiple times. Typically they're used for trivial operations such as accessing a named property from an object or accessing the first/last entry in a list.
How to see a request's plan diagram
Your server must be configured to expose plans for you to be able to see them;
if it is then you can use a tool such as Ruru to view the execution plan,
or you can render it directly from the JSON response. You can convert the plan
JSON into mermaid format via the
planToMermaid function so you can load them
into the mermaid live editor.