-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Rethink middleware #186
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
Comments
Here's a sketch of instrumentation: |
Here's an important point after chatting with the GitHub GraphQL team: middlewares add a lot of overhead. They get called for every field resolution, even for fields where they make no sense or where we could know ahead of time that they don't apply. The next implementation needs to be less wasteful, for example, allow fields to opt in to certain wrappers? |
We could have "instrumenters" that have an "attach" method and we attach For instance, a "instrumenter" that instruments usage of deprecated fields We would need different instrumenter types for each instrumentable thing, Would that work? On Friday, 16 September 2016, Robert Mosolgo notifications@github.com
|
Oh, that's a really cool idea! If I understand correctly:
Does that sound right? If so, it's a definite improvement over functional composition-style instrumentation, which I was leaning towards: resolve_func = -> (obj, args, ctx) { ... }
# ...
# attach an instrumenter by hand:
field :something, resolve: instrument(:my_instrumenter, resolve_proc) An approach like that ☝️ one is simple (and works already), but it leads to "shotgun surgery" when modifying your instrumentation. Better to have something that doesn't interfere with schema definition directly, but rather "wraps" items on an opt-in basis. |
Correct! That's exactly what I meant. :) The advantage is that you don't need to define it on every field BUT you Would love to collaborate on adding this! On Saturday, 17 September 2016, Robert Mosolgo <notifications@github.com
|
👍 🎉 😄 There are a couple other "events" that need coverage, how could those fit into a flow like this?
|
For begin_query / end_query we might be able to keep a similar syntax as On Saturday, 17 September 2016, Robert Mosolgo notifications@github.com
|
For the benefit of any watchers, we had some back-and-forth about API & implementation here: https://gist.github.com/cjoudrey/1c85ed1312403e9d73a4cc714cafc8f9 And later, cjoudrey suggested a nice option for attaching instrumenters in a non-destructive way: you could maintain a schema-level map of I think that's a very promising option for a few reasons:
There's one more question, how to support "events" like {
:begin_query => [...],
#<GraphQL::Field> => [...],
:begin_batch => [...],
:end_batch => [...],
} † This doesn't actually work yet, but I want it too, or at least to keep the door open to it. There are some cases ( |
I was thinking about this some more, let's say you want a robust API for opt-in instrumentation, what are the ways you can imagine attaching handlers? I can see something like this: Schema.define do
# Attach based on properties of the field:
instrument_with(RescueHandler, field_return_type: PersonType)
instrument_with(IdAccessLogger, field_name: "id")
# Attach to named events:
instrument_with(TimingHandler, event_name: [:query_begin, :query_end])
# Somehow support third-party events:
instrument_with(TimingHandler, event_name: [:batch_begin, :batch_end])
# Attach based on arbitrary code:
instrument_with(ConnectionLogger) { |target| target.is_a?(GraphQL::Field) && target.metadata[:relay_connection_field] }
end Perhaps under the hood, they're all implemented like the last example: you check the possible instrumentation targets and if the conditions return true, add an entry to the instrumentation table. |
Basic setup in #354, lots of room to grow there. |
Uh oh!
There was an error while loading. Please reload this page.
Middleware was designed with a simple, Rack-like API, but turns out that's a bug, not a feature:
Similar issues affect
graphql-ruby
. For example, if you use graphql-batch, the actual processing is done "out of bounds", not withinnext_middleware.call
. WithDeferredExecution
, the problem is even worse: there's no way to detect the actual end of the query, since any deferred fields are executed outside the root node's middleware call.So, we need a system that:
graphql-batch
)Whatever happens, it will be important to support existing middleware somehow. I imagine that will be simple enough, wrapping an existing middleware to port it to the new API, perhaps
Some ideas
Make it like query analyzers
before_query
returns an initial memonext_middleware.call
. This implementation requires the return value to bememo
, how can you intercept a field call?Make it an event listener
Extend the current definition
initial_value
etc)Make it like Rails instrumentation
In this API, code can be run inside an instrumentation block. Handlers can respond to incoming data in some way.
Some questions here:
instrument
?The text was updated successfully, but these errors were encountered: