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

feat(gateway): Replace serviceList API with more flexible, reactive option #1180

Closed
trevor-scheer opened this issue Nov 9, 2021 · 3 comments · Fixed by #1371
Closed

feat(gateway): Replace serviceList API with more flexible, reactive option #1180

trevor-scheer opened this issue Nov 9, 2021 · 3 comments · Fixed by #1371

Comments

@trevor-scheer
Copy link
Member

Community forum discussion has indicated the need for the Gateway to support:

  • additional flexibility around how the gateway is updated, as opposed to the current polling model which unmanaged users are locked into via serviceList
  • an officially supported version of the experimental_updateSupergraphSdl or similar API, but that is also friendly to pushing updates to Gateway
  • a push model that can be powered by polling (like serviceList), but also by events such as filesystem changes or webhooks in a more reactive way
  • all existing use cases that are supported by serviceList today, while enabling more creating ways of rolling the Gateway to a new supergraph schema
  • smooth migration from serviceList to this more flexible model
  • very clear alternatives for users not able to take advantage of the Studio ecosystem, or that need a local failover for regulatory reasons and the like

There has been community discussion on the proposed deprecation of the serviceList property (https://community.apollographql.com/t/feedback-thread-future-deprecation-of-servicelist-in-apollogateway/1053) within Apollo Gateway (v0.x and v2.x) and we appreciate the time everyone has taken to provide feedback there.
My intention is to capture future discussion here where the work is actually happening, so we can provide a more flexible alternative to serviceList that supports all existing use cases and enables new use cases, all while providing a smooth upgrade path when we do eventually deprecate serviceList.

Other explicit requirements that we’ve captured so far (if not already alluded to above):

  • cleanly support unmanaged federation use cases that currently use serviceList
  • continue supporting users of the experimental_updateSupergraphSdl hook
  • support custom failover use cases like pulling from local object storage, S3 bucket, etc.
  • support users who don’t want to run rover as a sidecar

Previous input / various discussion below:

Original thread

Capturing some out-of-band thoughts shared by @abernix:

I think there's another shape for this that I might want us to consider. As a prompt for why I think that shape might be worth considering: I think it's worth calling out that I believe experimental_updateSupergraphSdl is still merely a regular-polled interval rather than something fully capable of self-induced reactivity (of course, you could have it poll very often?). In other words, this was created as a life-cycle hook that was largely intended for observability into a polling process that the user didn't even necessarily control at first, yea? (It was either managed federation polling against our Studio GCP or a polling-to-downstream subgraphs mechanism)

I'm going to zoom out a bit from that and talk about the functionality I think we're looking to give to users — in my mind, I'd claim that it's a way to update the schema of a running gateway based on a signal. That signal might be a webhook that the Gateway receives externally, it might be a watched file, it might actually be a timer, it might be something else!

In that regard, I think something that is able to actively trigger changes itself seems desirable and more flexible - and I'd claim not really much more work.

For example, consider:

const webhookListener = externalWebhookListeningThing.listen(3000);
const fileWatcher = watchFile(join(__dirname, './super.graphql'));

const gateway = new ApolloGateway({
  supergraphSdl: (updateSupergraphSdl) => {
    // If we get a webhook, update
    webhookListener.on('trigger', (content) => updateSupergraphSdl(content.Sdl));

    // If if the supergraph SDL file changes, update!
    fileWatcher.on('change', (content) => updateSupergraphSdl);

    // Every day, just randomly update it to a new supergraph to mess with people! 🙈 
    setInterval(() => updateSupergraphSdl(randomNewSchemaForFun), 8640000)
  },
});

Of course, nobody should probably introduce three different update mechanisms, but ... they could! and they would be self-invoked, rather than just at a polled interval, right?

We can bring some nicer ergonomics for common use-cases, too. For example, I think providing a file-watching mechanism in-box that can plug into that — for what I suspect will be a very common use-case? — seems worth it to me! (Plus, it'll match the Router a bit more and get us feedback on that?)

We could even just support a mode of operation where if the supergraphSdl is just a file path that exists we treat it as such and just watch it (I think we could accurately determine that /path is not a GraphQL schema, right?). You could of course just provide a full supergraphSdl here too of course.

With such functionality in place (through either option), even though rover doesn't (yet) support watching itself, you could use common shell tooling (e.g., inotify, fswatch, watchman, chokidar, or, even a simple while true; sleep 1; done loop) to:

  • Watch a set of GraphQL SDL files
  • When those files change, trigger rover supergraph compose to a specific file path
  • (You could even have it do something more exotic, like invoke actual graphql-js merging of individual .graphql files that locally have extend-sion (sic) types amongst themselves?)
  • Have your Gateway reload with that new supergraph.

At this point, we could document our way out of needing serviceList (which largely functions as a great way to do local development today, right?) perhaps? (And, again, the Router already supports hot-supergraph reloading, today.)

The right design could take a couple iterations, but there's a straw-dog. With this, I think we could actually just rename updateSupergraphSdl to something like didSetSupergraphSdl and have it actually just be for observability (cloud, or otherwise but just generally this is the schema going into service — either coming from Studio, or an invocation of ). We could also introduce willSetSupergraphSdl which could throw if it for some reason wanted to stop such an update from happening (e.g., if it didn't like the thing it got from Studio).

@trevor-scheer
Copy link
Member Author

I like the direction @abernix is headed with this hook. One noteworthy addition I'd recommend is the ability to have cleanup handled during server shutdown. So the supergraphSdl function needs to be able to both immediately return the SDL as well as a cleanup function for Gateway to call on shutdown.

supergraphSdl(updateSdl) => string | { supergraphSdl: string; cleanup: () => void }

@andrewmcgivery
Copy link
Contributor

andrewmcgivery commented Nov 9, 2021

I think this actually looks like a very good approach that could offer a lot of flexibility for both non-managed and custom-managed.

For non-managed, the webhookListener could ping downstream services on an interval similar to how the serviceList works today. I do like the idea of being able to have multiple methods in play because I could see a world where I do something like...

if(process.env.NODE_ENV === 'development'){
   // Ping local services, similar to service list
} else {
  // Ping a downstream schema registry
}

For custom-managed, I think this solves what I'd be looking to do which is effectively have control over where I get the schema from, and allow it to be "pinged" on an interval.

I like the direction this is going. :) THANK YOU for listening to the community feedback on this one!

@trevor-scheer
Copy link
Member Author

Fixed via #1246 and available at @apollo/gateway@0.46.0-alpha.0 or @apollo/gateway@alpha

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants