You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generalize apollo-server graceful shutdown to all integrations
Previously, the batteries-included `apollo-server` package had a special
override of `stop()` which drains the HTTP server before letting the
actual Apollo Server `stop()` machinery begin. This meant that
`apollo-server` followed this nice shutdown lifecycle:
- Stop listening for new connections
- Close all idle connections and start closing connections as they go
idle
- Wait a grace period for all connections to close and force-close any
remaining ones
- Transition ApolloServer to the stopping state, where no operations
will run
- Run stop hooks (eg send final usage report)
This was great... but only `apollo-server` worked this way, because only
`apollo-server` has full knowledge and control over its HTTP server.
This PR adds a server draining step to the ApolloServer lifecycle and
plugin interface, and provides a built-in plugin which drains a Node
`http.Server` using the logic of the first three steps above.
`apollo-server`'s behavior is now just to automatically install the
plugin.
Specifically:
- Add a new 'phase' called `draining` that fits between `started` and
`stopping`. Like `started`, operations can still execute during
`draining`. Like `stopping`, any concurrent call to `stop()` will just
block until the first `stop()` call finishes rather than starting a
second shutdown process.
- Add a new `drainServer` plugin hook (on the object returned by
`serverWillStart`). Invoke all `drainServer` hooks in parallel during
the `draining` phase.
- Make calling `stop()` when `start()` has not yet completed
successfully into an error. That behavior was previously undefined.
Note that as of #5639, the automatic `stop()` call from signal
handlers can't happen before `start()` succeeds.
- Add `ApolloServerPluginDrainHttpServer` to `apollo-server-core`.
This plugin implements `drainServer` using the `Stopper` class
that was previously in the `apollo-server` package. The default
grace period is 10 seconds.
- Clean up integration tests to just use `stop()` with the plugin
instead of separately stopping the HTTP server. Note that for Fastify
specifically we also call `app.close` although there is some weirdness
here around both `app.close` and our Stopper closing the same server.
A comment describes the weirdness; perhaps Fastify experts can improve
this later.
- The Hapi web framework has built in logic that is similar to our
Stopper, so `apollo-server-hapi` exports
`ApolloServerPluginStopHapiServer` which should be used instead of the
other plugin with Hapi.
- Fix some test issues (eg, have FakeTimers only mock out Date.now
instead of setImmediate, drop an erroneous `const` which made an `app`
not get cleaned up, etc).
Fixes#5074.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,7 @@ The version headers in this history reflect the versions of Apollo Server itself
9
9
10
10
## vNEXT
11
11
12
+
-`apollo-server-core`: Previously, only the batteries-included `apollo-server` package supported a graceful shutdown. Now the integrations support it as well, if you tell your `ApolloServer` which HTTP server to drain with the new `ApolloServerPluginDrainHttpServer` plugin. This plugin implements a new `drainServer` plugin hook. For `apollo-server-hapi` you can use `ApolloServerPluginStopHapiServer` instead. [PR #5635](https://github.com/apollographql/apollo-server/pull/5635)
12
13
-`apollo-server-core`: Fix `experimental_approximateDocumentStoreMiB` option, which seems to have never worked before. [PR #5629](https://github.com/apollographql/apollo-server/pull/5629)
13
14
-`apollo-server-core`: Only register `SIGINT` and `SIGTERM` handlers once the server successfully starts up; trying to call `stop` on a server that hasn't successfully started had undefined behavior. By default, don't register the handlers in serverless integrations, which don't have the same lifecycle as non-serverless integrations (eg, there's no explicit `start` call); you can still explicitly set `stopOnTerminationSignals` to override this default. [PR #5639](https://github.com/apollographql/apollo-server/pull/5639)
Copy file name to clipboardExpand all lines: docs/source/api/apollo-server.md
+18-16Lines changed: 18 additions & 16 deletions
Original file line number
Diff line number
Diff line change
@@ -517,22 +517,22 @@ The `start` method triggers the following actions:
517
517
518
518
## `stop`
519
519
520
-
`ApolloServer.stop()` is an async method that tells all of Apollo Server's background tasks to complete. It calls and awaits all [`serverWillStop` plugin handlers](../integrations/plugins-event-reference/#serverwillstop) (including the [usage reporting plugin](./plugin/usage-reporting/)'s handler, which sends a final usage report to Apollo Studio). This method takes no arguments. You should only call it after [`start()`](#start) returns successfully (or [`listen()`](#listen) if you're using the batteries-included `apollo-server` package).
520
+
`ApolloServer.stop()` is an async method that tells all of Apollo Server's background tasks to complete. Specifically, it:
521
521
522
-
If your server is a [federated gateway](https://www.apollographql.com/docs/federation/gateway/), `stop` also stops gateway-specific background activities, such as polling for updated service configuration.
522
+
- Calls and awaits all [`drainServer` plugin handlers](../integrations/plugins-event-reference/#drainserver). These should generally:
523
+
* Stop listening for new connections
524
+
* Closes idle connections (i.e., connections with no current HTTP request)
525
+
* Closes active connections whenever they become idle
526
+
* Waits for all connections to be closed
527
+
* After a grace period, if any connections remain active, forcefully close them.
528
+
If you're using the batteries-included `apollo-server` package, it does this by default. (You can configure the grace period with the [`stopGracePeriodMillis` constructor option](#stopgraceperiodmillis).) Otherwise, you can use the [drain HTTP server plugin](./plugin/drain-http-server/) to drain your HTTP server.
529
+
- Transitions the server to a state where it will not start executing more GraphQL operations.
530
+
- Calls and awaits all [`serverWillStop` plugin handlers](../integrations/plugins-event-reference/#serverwillstop) (including the [usage reporting plugin](./plugin/usage-reporting/)'s handler, which sends a final usage report to Apollo Studio).
531
+
- If your server is a [federated gateway](https://www.apollographql.com/docs/federation/gateway/), `stop` also stops gateway-specific background activities, such as polling for updated service configuration.
523
532
524
-
In some circumstances, Apollo Server calls `stop` automatically when the process receives a `SIGINT` or `SIGTERM` signal. See the [`stopOnTerminationSignals` constructor option](#stoponterminationsignals) for details.
525
-
526
-
If you're using the `apollo-server` package (which handles setting up an HTTP server for you), this method first stops the HTTP server. Specifically, it:
527
-
528
-
* Stops listening for new connections
529
-
* Closes idle connections (i.e., connections with no current HTTP request)
530
-
* Closes active connections whenever they become idle
531
-
* Waits for all connections to be closed
532
-
533
-
If any connections remain active after a grace period (10 seconds by default), Apollo Server forcefully closes those connections. You can configure this grace period with the [`stopGracePeriodMillis` constructor option](#stopgraceperiodmillis).
533
+
This method takes no arguments. You should only call it after [`start()`](#start) returns successfully (or [`listen()`](#listen) if you're using the batteries-included `apollo-server` package).
534
534
535
-
If you're using a [middleware package](../integrations/middleware/) instead of `apollo-server`, you should stop your HTTP server before calling `ApolloServer.stop()`.
535
+
In some circumstances, Apollo Server calls `stop` automatically when the process receives a `SIGINT` or `SIGTERM` signal. See the [`stopOnTerminationSignals` constructor option](#stoponterminationsignals) for details.
536
536
537
537
## Framework-specific middleware function
538
538
@@ -547,23 +547,25 @@ These functions take an `options` object as a parameter. Some supported fields o
This API reference documents the `ApolloServerPluginDrainHttpServer` plugin.
10
+
11
+
This plugin is designed for use with [`apollo-server-express` and other framework-specific packages](../../integrations/middleware/#all-supported-packages) which are built on top of [Node `http.Server`s](https://nodejs.org/api/http.html#http_class_http_server). It is highly recommended that you use this plugin with packages like `apollo-server-express` if you want your server to shut down gracefully.
12
+
13
+
You do not need to explicitly use this plugin with the batteries-included `apollo-server` package: that package automatically uses this plugin internally.
14
+
15
+
When you use this plugin, Apollo Server will drain your HTTP server when you call the `stop()` method (which is also called for you when the `SIGTERM` and `SIGINT` signals are received, unless disabled with the [`stopOnTerminationSignals` constructor option](./apollo-server/#stoponterminationsignals)). Specifically, it will:
16
+
17
+
* Stop listening for new connections
18
+
* Close idle connections (i.e., connections with no current HTTP request)
19
+
* Close active connections whenever they become idle
20
+
* Wait for all connections to be closed
21
+
* After a grace period, if any connections remain active, forcefully close them.
22
+
23
+
This plugin is exported from the `apollo-server-core` package. It is tested with `apollo-server-express`, `apollo-server-koa`, and `apollo-server-fastify`. (If you're using Hapi, you should instead use the `ApolloServerPluginStopHapiServer` plugin exported from the `apollo-server-hapi` package.)
24
+
25
+
Here's a basic example of how to use it with Express. See the [framework integrations docs](../../integrations/middleware/) for examples of how to use it with other frameworks.
6. Add a plugin to your `ApolloServer` constructor to close the `SubscriptionServer`.
108
105
106
+
```javascript:title=index.js
107
+
constserver=newApolloServer({
108
+
schema,
109
+
plugins: [{
110
+
asyncserverWillStart() {
111
+
return {
112
+
asyncdrainServer() {
113
+
subscriptionServer.close();
114
+
}
115
+
};
116
+
}
117
+
}],
118
+
});
109
119
```
110
120
111
-
6. Finally, we need to adjust our existing `listen` call.
121
+
7. Finally, we need to adjust our existing `listen` call.
112
122
113
123
Most Express applications call `app.listen(...)`. **Change this to `httpServer.listen(...)`** with the same arguments. This way, the server starts listening on the HTTP and WebSocket transports simultaneously.
114
124
@@ -136,17 +146,26 @@ import typeDefs from "./typeDefs";
0 commit comments