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

Instrumentation methods #58

Open
sgrove opened this issue Jul 20, 2017 · 6 comments
Open

Instrumentation methods #58

sgrove opened this issue Jul 20, 2017 · 6 comments

Comments

@sgrove
Copy link

sgrove commented Jul 20, 2017

I'd like to have enter optional function argument to pass in on query execution that'll be called for each resolver with:

  • the current context
  • the path to the resolver (from the query)
  • the type of the parent resolver (necessary for some of the instrumentation we're looking to add)
  • the resolver arguments

Also, an exit optional function that would get the same as above, but also the return value of the resolver

This would let use collect telemetry on each resolver in a cross-cutting manner, and would be generally very useful.

@andreas
Copy link
Owner

andreas commented Jul 20, 2017

Thanks for the input! Adding middleware seems like a generally useful feature. Getting the types right might be challenge. Can you elaborate on the types you would expect for "path to the resolver", "type of the parent resolver" and "resolver arguments"?

@andreas
Copy link
Owner

andreas commented Jul 21, 2017

To expand on my previous comment...

The type of the function enter (or exit for that matter), will need to work for all types of fields. Since only the context type 'ctx is identical for a query execution over a schema, this is the only part of a field which can easily be exposed to enter. Loosely typed data can easily be exposed as well e.g. exposing the value of arguments as Yojson.Basic.json. Exposing 'src or 'args is possible, but enter will not be able to make any assumptions about their nature, so it will be hard to use for anything practical. Please share your use cases though, then I can more easily understand what you need.

@martijnwalraven
Copy link

@andreas: For context, I'm part of the Apollo team, and we've been working on a new design for exposing trace data to Apollo Optics. Currently, this relies on an agent running in your server that collects, aggregates, and batches up data to send to the Optics backend. In the new design, trace data for an individual request is exposed under extensions as part of the GraphQL response, and a separate proxy process (provided by us, written in Go) is responsible for filtering out the trace data and performing the aggregation and batching. Our hope is that this will make it much easier to use Optics with every GraphQL server, not just with Node and Ruby (the only platforms for which we currently provide agents).

We're getting ready for early access at the moment, and we expect to make changes based on feedback, so the format is preliminary. But for an idea of what this looks like, see here. Happy to answer any questions you might have. Would be exciting to get support for instrumentation and the Apollo tracing format in OCaml!

@andreas
Copy link
Owner

andreas commented Jul 23, 2017

@martijnwalraven: Thanks, that looks interesting. What does the startOffset refer to? Do you have an example of a returned extensions-value and how it's interpreted/visualized?

@martijnwalraven
Copy link

martijnwalraven commented Jul 24, 2017

I plan on writing up a short spec for this soon, but startOffset refers to the start time of a resolver call in nanoseconds, relative to the startTime of the request.

One motivation for the format is to send data to Apollo Optics and visualize both individual traces and aggregated statistics there, but we would also like to add support to GraphiQL to show trace data for a single request.

To give an example of the format, for this query:

query {
  hero {
    name
    friends {
      name
    }
  }
}

The result would look like this:

{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  },
  "extensions": {
    "tracing": {
      "version": 1,
      "startTime": "2017-07-24T14:03:16.008Z",
      "endTime": "2017-07-24T14:03:16.009Z",
      "duration": 1332446,
      "execution": {
        "resolvers": [
          {
            "path": [
              "hero"
            ],
            "parentType": "Query",
            "fieldName": "hero",
            "returnType": "Character",
            "startOffset": 1108856,
            "duration": 24201
          },
          {
            "path": [
              "hero",
              "name"
            ],
            "parentType": "Droid",
            "fieldName": "name",
            "returnType": "String!",
            "startOffset": 1181364,
            "duration": 7007
          },
          {
            "path": [
              "hero",
              "friends"
            ],
            "parentType": "Droid",
            "fieldName": "friends",
            "returnType": "[Character]",
            "startOffset": 1196580,
            "duration": 116086
          },
          {
            "path": [
              "hero",
              "friends",
              0,
              "name"
            ],
            "parentType": "Human",
            "fieldName": "name",
            "returnType": "String!",
            "startOffset": 1284820,
            "duration": 2516
          },
          {
            "path": [
              "hero",
              "friends",
              1,
              "name"
            ],
            "parentType": "Human",
            "fieldName": "name",
            "returnType": "String!",
            "startOffset": 1297121,
            "duration": 1373
          },
          {
            "path": [
              "hero",
              "friends",
              2,
              "name"
            ],
            "parentType": "Human",
            "fieldName": "name",
            "returnType": "String!",
            "startOffset": 1305854,
            "duration": 1286
          }
        ]
      }
    }
  }
}

As you can see, the overhead of the trace data is pretty significant, but it compresses quite well, so the recommendation is to enable gzip.

@martijnwalraven
Copy link

martijnwalraven commented Jul 29, 2017

@andreas: Just a heads up that I just published a description of the proposed Apollo Tracing format. Would be great to get your feedback and see how we can add support for this to the OCaml server!

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

No branches or pull requests

3 participants