f6e182ac23
This PR handles SSE event stream edge cases. - When a pod restarts, the front clients have to reconnect to SSE - When the dev server restarts or is hot reloaded, the front client has to reconnect to SSE - When redis server restarts or the redis key is cleared for any reason, the server has to recreate the event stream in redis, this can happen when navigating for example. - Log in / log out flow With this PR we avoid error messages in the front end due to TTL or pod crash, we implement a resilient way of reconnecting silently. To avoid DDoSing our servers if pods crash or a full restart of the cluster is made, we evenly space retry attempts to reconnect from all the clients, to avoid n clients reconnection at the same time, we use a random wait time between 0 and a constant max wait time (set to 2 mins for now). This is the cheapest and most effective solution, clients who want to force reconnect have to refresh or navigate to another page. Fixes https://github.com/twentyhq/core-team-issues/issues/2045
39 lines
1.2 KiB
TypeScript
39 lines
1.2 KiB
TypeScript
import { type MessageDescriptor } from '@lingui/core';
|
|
import { msg } from '@lingui/core/macro';
|
|
import { assertUnreachable } from 'twenty-shared/utils';
|
|
|
|
import { CustomException } from 'src/utils/custom-exception';
|
|
|
|
export enum EventStreamExceptionCode {
|
|
EVENT_STREAM_ALREADY_EXISTS = 'EVENT_STREAM_ALREADY_EXISTS',
|
|
NOT_AUTHORIZED = 'NOT_AUTHORIZED',
|
|
EVENT_STREAM_DOES_NOT_EXIST = 'EVENT_STREAM_DOES_NOT_EXIST',
|
|
}
|
|
|
|
const getEventStreamExceptionUserFriendlyMessage = (
|
|
code: EventStreamExceptionCode,
|
|
) => {
|
|
switch (code) {
|
|
case EventStreamExceptionCode.EVENT_STREAM_ALREADY_EXISTS:
|
|
case EventStreamExceptionCode.EVENT_STREAM_DOES_NOT_EXIST:
|
|
return msg`Failed to receive real time updates.`;
|
|
case EventStreamExceptionCode.NOT_AUTHORIZED:
|
|
return msg`You are not authorized to perform this action.`;
|
|
default:
|
|
assertUnreachable(code);
|
|
}
|
|
};
|
|
|
|
export class EventStreamException extends CustomException<EventStreamExceptionCode> {
|
|
constructor(
|
|
message: string,
|
|
code: EventStreamExceptionCode,
|
|
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
|
|
) {
|
|
super(message, code, {
|
|
userFriendlyMessage:
|
|
userFriendlyMessage ?? getEventStreamExceptionUserFriendlyMessage(code),
|
|
});
|
|
}
|
|
}
|