Skip to content

Commit

Permalink
feat: keep alive when dispatch fails (#524)
Browse files Browse the repository at this point in the history
* feat: disableOnFail

* doc: add config doc

* Revert "doc: add config doc"

This reverts commit ec1f74f.

* Revert "feat: disableOnFail"

This reverts commit 67ed7b3.

* feat: only disable RUM when dispatch fails with 403 or 404

* chore: add unit tests

* feat: add 401
  • Loading branch information
williazz committed Mar 29, 2024
1 parent 87f4fb2 commit 1ba5ee0
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 14 deletions.
11 changes: 7 additions & 4 deletions src/dispatch/Dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class Dispatch {
private dispatchTimerId: number | undefined;
private buildClient: ClientBuilder;
private config: Config;
private disableCodes = ['401', '403', '404'];

constructor(
region: string,
Expand Down Expand Up @@ -232,10 +233,12 @@ export class Dispatch {
}

private handleReject = (e: any): { response: HttpResponse } => {
// The handler has run out of retries. We adhere to our convention to
// fail safe by disabling dispatch. This ensures that we will not
// continue to attempt requests when the problem is not recoverable.
this.disable();
if (e instanceof Error && this.disableCodes.includes(e.message)) {
// RUM disables only when dispatch fails and we are certain
// that subsequent attempts will not succeed, such as when
// credentials are invalid or the app monitor does not exist.
this.disable();
}
throw e;
};

Expand Down
138 changes: 128 additions & 10 deletions src/dispatch/__tests__/Dispatch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,15 +419,41 @@ describe('Dispatch tests', () => {
);
});

test('when a fetch request is rejected then dispatch is disabled', async () => {
test('when a fetch request is rejected with 429 then dispatch is NOT disabled', async () => {
// Init
const ERROR = 'Something went wrong.';
const sendFetch = jest.fn(() => Promise.reject(ERROR));
(DataPlaneClient as any).mockImplementation(() => {
return {
sendFetch
};
});
(DataPlaneClient as any).mockImplementationOnce(() => ({
sendFetch: () => Promise.reject(new Error('429'))
}));

const eventCache: EventCache =
Utils.createDefaultEventCacheWithEvents();

const dispatch = new Dispatch(
Utils.AWS_RUM_REGION,
Utils.AWS_RUM_ENDPOINT,
eventCache,
{
...DEFAULT_CONFIG,
...{ dispatchInterval: Utils.AUTO_DISPATCH_OFF, retries: 0 }
}
);
dispatch.setAwsCredentials(Utils.createAwsCredentials());

// Run
eventCache.recordEvent('com.amazon.rum.event1', {});

// Assert
await expect(dispatch.dispatchFetch()).rejects.toEqual(
new Error('429')
);
expect((dispatch as unknown as any).enabled).toBe(true);
});

test('when a fetch request is rejected with 500 then dispatch is NOT disabled', async () => {
// Init
(DataPlaneClient as any).mockImplementationOnce(() => ({
sendFetch: () => Promise.reject(new Error('500'))
}));

const eventCache: EventCache =
Utils.createDefaultEventCacheWithEvents();
Expand All @@ -444,11 +470,103 @@ describe('Dispatch tests', () => {
dispatch.setAwsCredentials(Utils.createAwsCredentials());

// Run
await expect(dispatch.dispatchFetch()).rejects.toEqual(ERROR);
eventCache.recordEvent('com.amazon.rum.event1', {});

// Assert
await expect(dispatch.dispatchFetch()).resolves.toEqual(undefined);
await expect(dispatch.dispatchFetch()).rejects.toEqual(
new Error('500')
);
expect((dispatch as unknown as any).enabled).toBe(true);
});

test('when a fetch request is rejected with 401 then dispatch is disabled', async () => {
// Init
(DataPlaneClient as any).mockImplementationOnce(() => ({
sendFetch: () => Promise.reject(new Error('401'))
}));

const eventCache: EventCache =
Utils.createDefaultEventCacheWithEvents();

const dispatch = new Dispatch(
Utils.AWS_RUM_REGION,
Utils.AWS_RUM_ENDPOINT,
eventCache,
{
...DEFAULT_CONFIG,
...{ dispatchInterval: Utils.AUTO_DISPATCH_OFF, retries: 0 }
}
);
dispatch.setAwsCredentials(Utils.createAwsCredentials());

// Run
eventCache.recordEvent('com.amazon.rum.event1', {});

// Assert
await expect(dispatch.dispatchFetch()).rejects.toEqual(
new Error('401')
);
expect((dispatch as unknown as any).enabled).toBe(false);
});

test('when a fetch request is rejected with 403 then dispatch is disabled', async () => {
// Init
(DataPlaneClient as any).mockImplementationOnce(() => ({
sendFetch: () => Promise.reject(new Error('403'))
}));

const eventCache: EventCache =
Utils.createDefaultEventCacheWithEvents();

const dispatch = new Dispatch(
Utils.AWS_RUM_REGION,
Utils.AWS_RUM_ENDPOINT,
eventCache,
{
...DEFAULT_CONFIG,
...{ dispatchInterval: Utils.AUTO_DISPATCH_OFF, retries: 0 }
}
);
dispatch.setAwsCredentials(Utils.createAwsCredentials());

// Run
eventCache.recordEvent('com.amazon.rum.event1', {});

// Assert
await expect(dispatch.dispatchFetch()).rejects.toEqual(
new Error('403')
);
expect((dispatch as unknown as any).enabled).toBe(false);
});

test('when a fetch request is rejected with 404 then dispatch is disabled', async () => {
// Init
(DataPlaneClient as any).mockImplementationOnce(() => ({
sendFetch: () => Promise.reject(new Error('404'))
}));

const eventCache: EventCache =
Utils.createDefaultEventCacheWithEvents();

const dispatch = new Dispatch(
Utils.AWS_RUM_REGION,
Utils.AWS_RUM_ENDPOINT,
eventCache,
{
...DEFAULT_CONFIG,
...{ dispatchInterval: Utils.AUTO_DISPATCH_OFF, retries: 0 }
}
);
dispatch.setAwsCredentials(Utils.createAwsCredentials());

// Run
eventCache.recordEvent('com.amazon.rum.event1', {});

// Assert
await expect(dispatch.dispatchFetch()).rejects.toEqual(
new Error('404')
);
expect((dispatch as unknown as any).enabled).toBe(false);
});

test('when signing is disabled then credentials are not needed for dispatch', async () => {
Expand Down

0 comments on commit 1ba5ee0

Please sign in to comment.