Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve documentation of @cacheControl #6161

Merged
merged 6 commits into from
Jan 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,202 @@ The behaviour of this plugin can be configured by passing an object at the gatew
The `@cacheControl` directive can be used to give to subgraphs the control over the cache behavior
for the fields and types they are defining. You can add this directive during composition.

- [See here for Federation to learn more about the `@cacheControl` directive](https://www.apollographql.com/docs/federation/performance/caching/#using-cache-hints-with-subgraphs)
- [See here for GraphQL Mesh to learn more about the `@cacheControl` in subgraph definitions](/docs/gateway/other-features/performance/response-caching)
You can learn more about the behavior of the `@cacheControl` directive in the following section. If
you want to use the configuration API on the gateway, you can skip the `@cacheControl` directive
section.

### `@cacheControl` directive's behavior

This directive allows you to control response caching from the subgraph. But the behavior can be
different depending on the subgraph configuration.

The following 3 section shows the ways of using the `@cacheControl` directive in different ways. The
usage examples in the other sections represent the third(last) use case in the following three.

#### Apollo Server with `Cache-Control` header (This response cache plugin is not required)

Apollo Server handles `@cacheControl` directives to set HTTP caching headers in the HTTP response to
the gateway. Then the gateway can cache the response based on these headers.
[Learn more about Apollo Server's cache control behavior](https://www.apollographql.com/docs/apollo-server/performance/caching).
In this case, gateway is still response of caching the response. Hive Gateway can handle http
caching headers using [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching).

```mermaid
flowchart TD
A["GraphQL Client"] -->|"GraphQL Request"| B(Go shopping)
B("Hive Gateway") --> C{Let me think}
C -->|"Cache Hit"| D["Get the cached response from Cache Storage"]
C -->|"Not Cached"| E["Send HTTP Response Back"]
C{"Subgraph Executor"}
D ---|"Return the cached response"| C
E
E ---|"Get 'Cache-Control' header, and store the response for the following requests"| C{"Subgraph Executor gets the query"}
```

##### Example

Let's say you have a subgraph that defines a `Post` type with a `@cacheControl` directive, your Hive
Gateway has [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching) enabled.

```graphql
type Post @cacheControl(maxAge: 240) {
id: Int!
title: String
}
```

When the gateway receives a query that selects the `Post` type, it will cache the response for 240
seconds.

```graphql
query {
posts {
id
title
}
}
```

In this case, the gateway will cache the response for 240 seconds. If the same query is made within
240 seconds, the gateway will return the cached response.

#### GraphQL Yoga with the response caching plugin (This response cache plugin is not required)

On the other hand, GraphQL Yoga handles `@cacheControl` directives to configure the response caching
behavior rather than `Cache-Control` headers like Apollo Server. It leverages `ETag` headers to
cache the response and invalidate it by mutations.
[Learn more about GraphQL Yoga's response caching behavior](https://the-guild.dev/graphql/yoga-server/docs/features/response-caching).
So even if nothing is configured on the gateway but Yoga on the subgraph uses the response caching
plugin on its own. But this won't reduce the HTTP connection traffic in between gateway and
subgraph.

```mermaid
flowchart TD
A["GraphQL Client"]
B(Go shopping)
B("Hive Gateway")
E["Send HTTP Response Back"]
E
E["Subgraph Yoga Server"]
E --- n1["Response Cache Plugin"]
n1 ---|"If Cached"| n2["Get the cached response from Cache Storage"]
n1 ---|"If Not Cached"| n3["Prepare the result by making database calls etc, and store it for the future calls"]
subgraph B["Hive Gateway"]
n5
n4["Query Planner"]
end
n4
n4 ---|"Generate Subgraph Query"| n5["Execution Engine"]
n5
E
B
n5
n5
n5 ---|"HTTP Request"| E
A --- B
```

##### Example

Let's say you have a subgraph that defines a `Post` type with a `@cacheControl` directive, your Hive
Gateway has [HTTP Caching plugin](/docs/gateway/other-features/performance/http-caching) enabled.

```graphql
type Post @cacheControl(maxAge: 240) {
id: Int!
title: String
}
```

When the gateway receives a query that selects the `Post` type, it will forward the request to the
subgraph directly.

```graphql
query {
posts {
id
title
}
}
```

Then Yoga Server will generate the response and cache it for 240 seconds. If the same query is made
within 240 seconds, Yoga Server will return the cached response. So it will always receive the HTTP
request but do the less work to generate the response. The difference between others, this case
won't reduce the HTTP connection traffic in between gateway and subgraph, but it will reduce the
work that subgraph needs to do to generate the response.

#### Subgraphs with any server implementation using directives directly (This response cache plugin is required)

Besides these two, you can let the gateway handle the caching on its own. In this case, you need to
define the following in the subgraphs so the supergraph has `@cacheControl` directives. Then, the
response caching plugin on the gateway will handle the caching based on these directives.

```graphql
extend schema
@link(
# Federation spec version should be at least 2.1 to support `@composeDirective`
url: "https://specs.apollo.dev/federation/v2.1"
import: ["@composeDirective"]
)
# Import `@cacheControl`
@link(url: "https://the-guild.dev/mesh/v1.0", import: ["@cacheControl"])
# Use `@composeDirective` to define the directive as exposed
@composeDirective(name: "@cacheControl")

# Then add the actual directive definitions
enum CacheControlScope {
PUBLIC
PRIVATE
}

directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
```

In this case, the gateway will cache the response based on the `@cacheControl` directives defined in
the subgraphs.

The difference between the two is that the gateway will control the caching behavior based on the
`@cacheControl` directives defined in the subgraphs. For any cached responses, the gateway will skip
query planning and federation execution phase, and return the response directly.

```mermaid
flowchart TD
A["GraphQL Client"]
B(Go shopping)
B("Hive Gateway")
E["Send HTTP Response Back"]
E
E["Subgraph Yoga Server"]
E --- n1["Response Cache Plugin"]
n1 ---|"If Cached"| n2["Get the cached response from Cache Storage"]
n1 ---|"If Not Cached"| n3["Prepare the result by making database calls etc, and store it for the future calls"]
subgraph B["Hive Gateway"]
n7["Get Stored Response"]
n6["Response Caching Plugin"]
n5
n4["Query Planner"]
end
n4
n4 ---|"Generate Subgraph Query"| n5["Execution Engine"]
n5
E
B
n5
n5
n5 ---|"HTTP Request"| E
A
B
n6 ---|"If Not Cached"| n4
n6 ---|"If Cache Hit"| n7
n7 ---|"Respond Cached Response back to Client"| A
n5 ---|"Store the response for future calls"| n6
A ---|"Send GraphQL Request via HTTP "| n6
```

## Session based caching

Expand Down
Loading