-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Eagerly start subscription heartbeats (#7871)
# Overview Eagerly start subscription heartbeats in case the subscription has long-running setup steps before returning its async generator. When a subscription is configured, `subscribe` can return either an `AsyncGenerator` or a `Promise<AsyncGenerator>`. If an `AsyncGenerator` is returned, the previous code works fine. We get the generator right away and set up the heartbeat to keep things alive, even if the generator has long-running setup code before returning values. On the other hand, if the `Promise<AsyncGenerator>` takes longer than the hearbeat interval to resolve, the router will terminate the subscription. When the subscription actually starts generating data, the callback returns 404, as it has already been cleaned up. We want the system to gracefully wait for the setup code to complete, since we will do so if the setup code is placed inside the iterator itself. ## Example In this example, `subscribe` returns a `Promise<AsyncGenerator>`, but the `Promise` resolves quickly. This results in the heartbeat being started prior to the long-running setup. The heartbeat keeps the subscription alive until the setup is completed, then it can start sending data to the client. ``` return { subscribe: async () => { return { async *[Symbol.asyncIterator]() { // The heartbeat is already started, so this can wait as long as it wants await setupWithLongDelay(); while (running) { yield data; } }, }; }, } ``` On the other hand, waiting for the setup before resolving the `Promise` leads to complications. ``` return { subscribe: async () => { // The router kills this request because it doesn't start the heartbeat in time await setupWithLongDelay(); return { async *[Symbol.asyncIterator]() { while (running) { yield data; } }, }; }, } ``` ## Solution This change eagerly starts the heartbeat interval prior to awaiting the returned value from `subscribe`. This allows long-running setup code to run concurrently with the heartbeats. Previously the second example would result in a `SUBSCRIPTION_HEARTBEAT_ERROR` and subscription termination. Now, the router receives heartbeats so it correctly waits for the setup and data in both cases.
- Loading branch information
1 parent
7e64902
commit 18a3827
Showing
3 changed files
with
45 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@apollo/server": patch | ||
--- | ||
|
||
Subscription heartbeats are initialized prior to awaiting subscribe(). This allows long-running setup to happen in the returned Promise without the subscription being terminated prior to resolution. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.