Skip to content

Hapi performance spans are off #11213

Closed as not planned
Closed as not planned

Description

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/node

SDK Version

7.107

Framework Version

No response

Link to Sentry event

No response

SDK Setup

  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    tracesSampleRate: 0.2,
    integrations: excludeFalsy([
      new Sentry.Integrations.Anr({ captureStackTrace: true }),
      new Sentry.Integrations.Hapi({ server }),
      new Sentry.Integrations.Postgres(),
    ]),
  });

Steps to Reproduce

  1. Enable Sentry Hapi integration.
  2. Let our Hapi service handle some async requests (e.g., database queries).

Expected Result

Database queries are correctly correlated with their HTTP requests.

Actual Result

Requests may be mismatched.

Sentry's hapiTracingPlugin uses onPreHandler and onPostHandler events to try to narrow the scope of the Sentry span to just a single Hapi request. However, that doesn't ensure handling of async operations within the request handler itself.

I tried figuring out a solution to this. If I'm understanding Sentry's API correctly, it expects you to use withScope or withIsolationScope to wrap the request handler. However, I don't see how to make that work with hapiTracingPlugin's design of onPreHandler and onPostHandler events. If Sentry had some sort of startIsolationScope and endIsoluationScope method, then pre- and post- events could probably work. Otherwise, the only solution I can think of would be to more invasively patch / instrument a Hapi application to wrap the request handlers themselves. (I believe that's the approach used by OpenTelemetry's Hapi instrumentation.)

Test case: The following demo starts a Hapi service on a random port, with an asynchronous route handler, and fires off 4 requests to the service, separated by 100ms intervals. Each request should have its own timestamp and span ID, and none of the requests should include an HTTP status (because the requests haven't finished at the time they're logged), but they're all the same.

'use strict';

const Sentry = require('@sentry/node');
const Hapi = require('@hapi/hapi');
const { setTimeout } = require('node:timers/promises');

const init = async () => {
    const server = Hapi.server({
        host: 'localhost'
    });

    Sentry.init({
        // Enable Spotlight to allow running without a DSN.
        spotlight: true,

        integrations: [
            new Sentry.Integrations.Hapi({ server }),
        ]
    })

    server.route({
        method: 'GET',
        path: '/',
        handler: async () => {
            await setTimeout(2000);
            console.log(Sentry.getActiveSpan().toJSON());
            return 'Hello World!';
        }
    });

    await server.start();
    console.log('Server running on %s', server.info.uri);

    return server.info.uri;
};

async function main() {
  const url = await init();

  for (let i = 0; i < 4; i++) {
    fetch(url).then((res) => res.text()).then(console.log);
    await setTimeout(100);
  }
}

main();

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

Type

Projects

  • Status

    Waiting for: Community
  • Status

    Waiting for: Community

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions