Skip to content

Commit

Permalink
Add flushSafe to avoid crashing API endpoint when flushing fails
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadorequest committed Jun 24, 2021
1 parent 493e16f commit 5e21464
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 24 deletions.
26 changes: 26 additions & 0 deletions src/modules/core/sentry/universal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { createLogger } from '@/modules/core/logging/logger';
import { FLUSH_TIMEOUT } from '@/modules/core/sentry/config';
import { UserSession } from '@/modules/core/userSession/useUserSession';
import * as Sentry from '@sentry/node';

const fileLabel = 'modules/core/sentry/universal';
const logger = createLogger({
fileLabel,
});

/**
* Configure Sentry tags related to the current user.
*
Expand Down Expand Up @@ -35,3 +42,22 @@ export const configureSentryI18n = (lang: string, locale: string): void => {
});
}
};

/**
* Flushes Sentry queue in a safe way.
*
* It's necessary to flush all Sentry events on the server, because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
* If you don't flush, then it's possible the Sentry events won't be sent.
* This helper is meant to be used for backend-only usage. (not frontend)
*
* There is a potential bug in Sentry that throws an exception when flushing times out, causing API endpoints to fail.
* @see https://github.com/getsentry/sentry/issues/26870
*/
export const flushSafe = async (): Promise<boolean> => {
try {
return await Sentry.flush(FLUSH_TIMEOUT);
} catch (e) {
logger.error(`[flushSafe] An exception was thrown while running Sentry.flush()`, e);
return false;
}
};
8 changes: 3 additions & 5 deletions src/pages/_error.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FLUSH_TIMEOUT } from '@/modules/core/sentry/config';
import { flushSafe } from '@/modules/core/sentry/universal';
import * as Sentry from '@sentry/node';
import { NextPageContext } from 'next';
import NextError, { ErrorProps as NextErrorProps } from 'next/error';
Expand Down Expand Up @@ -126,8 +126,7 @@ ErrorPage.getInitialProps = async (props: NextPageContext): Promise<ErrorProps>
if (err) {
Sentry.captureException(err);

// It's necessary to flush all events when running on the server, because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

return errorInitialProps;
}
Expand Down Expand Up @@ -155,8 +154,7 @@ ErrorPage.getInitialProps = async (props: NextPageContext): Promise<ErrorProps>
new Error(`_error.js getInitialProps missing data at path: ${asPath}`),
);

// It's necessary to flush all events when running on the server, because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

return errorInitialProps;
};
Expand Down
5 changes: 2 additions & 3 deletions src/pages/api/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
AMPLITUDE_EVENTS,
} from '@/modules/core/amplitude/events';
import { createLogger } from '@/modules/core/logging/logger';
import { FLUSH_TIMEOUT } from '@/modules/core/sentry/config';
import Sentry from '@/modules/core/sentry/init';
import { configureReq } from '@/modules/core/sentry/server';
import { flushSafe } from '@/modules/core/sentry/universal';
import {
NextApiRequest,
NextApiResponse,
Expand Down Expand Up @@ -40,8 +40,7 @@ export const error = async (req: NextApiRequest, res: NextApiResponse): Promise<
Sentry.captureException(e);
logger.error(e.message);

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.json({
error: true,
Expand Down
8 changes: 3 additions & 5 deletions src/pages/api/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
} from '@/modules/core/amplitude/events';
import { filterExternalAbsoluteUrl } from '@/modules/core/js/url';
import { createLogger } from '@/modules/core/logging/logger';
import { FLUSH_TIMEOUT } from '@/modules/core/sentry/config';
import Sentry from '@/modules/core/sentry/init';
import { configureReq } from '@/modules/core/sentry/server';
import { flushSafe } from '@/modules/core/sentry/universal';
import appendQueryParameter from 'append-query';
import {
NextApiRequest,
Expand Down Expand Up @@ -103,17 +103,15 @@ export const preview = async (req: EndpointRequest, res: NextApiResponse): Promi
Sentry.captureMessage('Preview mode is not allowed in production', Sentry.Severity.Warning);
}

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.writeHead(307, { Location: safeRedirectUrl });
res.end();
} catch (e) {
Sentry.captureException(e);
logger.error(e.message);

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.json({
error: true,
Expand Down
5 changes: 2 additions & 3 deletions src/pages/api/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
AMPLITUDE_EVENTS,
} from '@/modules/core/amplitude/events';
import { createLogger } from '@/modules/core/logging/logger';
import { FLUSH_TIMEOUT } from '@/modules/core/sentry/config';
import Sentry from '@/modules/core/sentry/init';
import { configureReq } from '@/modules/core/sentry/server';
import { flushSafe } from '@/modules/core/sentry/universal';
import {
NextApiRequest,
NextApiResponse,
Expand Down Expand Up @@ -63,8 +63,7 @@ export const status = async (req: NextApiRequest, res: NextApiResponse): Promise
Sentry.captureException(e);
logger.error(e.message);

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.json({
error: true,
Expand Down
12 changes: 4 additions & 8 deletions src/pages/api/webhooks/deploymentCompleted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {
} from '@/modules/core/amplitude/events';
import { convertRequestBodyToJSObject } from '@/modules/core/api/convertRequestBodyToJSObject';
import { createLogger } from '@/modules/core/logging/logger';
import {
ALERT_TYPES,
FLUSH_TIMEOUT,
} from '@/modules/core/sentry/config';
import { ALERT_TYPES } from '@/modules/core/sentry/config';
import Sentry from '@/modules/core/sentry/init';
import { configureReq } from '@/modules/core/sentry/server';
import { flushSafe } from '@/modules/core/sentry/universal';
import {
NextApiRequest,
NextApiResponse,
Expand Down Expand Up @@ -120,17 +118,15 @@ export const deploymentCompleted = async (req: EndpointRequest, res: NextApiResp
});
});

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.status(200);
res.end();
} catch (e) {
Sentry.captureException(e);
logger.error(e.message);

// It's necessary to flush all events because Vercel runs on AWS Lambda, see https://vercel.com/docs/platform/limits#streaming-responses
await Sentry.flush(FLUSH_TIMEOUT);
await flushSafe();

res.status(500);
res.end();
Expand Down

0 comments on commit 5e21464

Please sign in to comment.