Description
Problem Statement
A popular strategy for server-side logging is to:
A. Compute the normal request
B. Return the result to the client
C. On the same process, take the telemetry information collected in (A) and send it to the APM (e.g. Sentry).
Because (C) requires an async call to another (possibly distant) server, it can be slow, perhaps even slower than (A). Also, (C) must start after (A) completes as (C) describes the performance of (A).
Ideally, we would execute (B) before beginning (C). This can be done in Express because res.send
is just an ordinary command that terminates the communication stream with the client but not the express handler (Stackoverflow). However, this cannot be accomplished in Vercel because it does not support these "fire-and-forget" background tasks:
Vercel does not support streaming responses from Serverless Functions due to an upstream limitation from AWS.
This means that background tasks, also known as "fire-and-forget" is not supported. Once the Serverless Function returns the response payload, it stops processing including any pending background tasks.
Therefore, in a Vercel + Sentry stack, (B) cannot happen until after (C), creating a significant and unnecessary performance penalty. Indeed, the sentry nextjs package has to monkeypatch wrap nextjs's .end
to wait until the sentry analytics have returned before returning to the client.
Solution Brainstorm
Proxy Function:
I propose that sentry/nextjs be architected as four steps:
A. Compute the normal request
B. Return the result to the client
C. On the same process, take the telemetry information collected in (A) and send it to a next serverless function (proxy function)
D. The nextjs serverless function sends the data to to APM (e.g. Sentry).
While (B) must occur after (C), (C) is now likely much faster. Indeed, it is likely that it will be sent sent to a function on the same computer, certainly in the same data center. The slow step of crossing to a potentially distant server (D), will no longer block (B), thus improving performance. The new proxy function can be very simple (e.g. takes the body and headers and forwards them to Sentry).
This solution may be useful.
Send and don't wait for acknowledgement
In Step (C) (either with with or without the proxy function), use net.Socket
to send the data without waiting for Sentry to acknowledge the result (see this). Instead of completing the full http
protocol, just send the header and body and stop listening for a reply. For many analytics applications, "best-effort" may be preferable if there is an accompanying speed boost.