Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: retry on payload too large error #993

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 52 additions & 10 deletions src/push/kibana_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
sendReqAndHandleError,
sendRequest,
APIMonitorError,
RequestPayloadTooLargeError,
} from './request';
import { generateURL } from './utils';

Expand All @@ -49,17 +50,58 @@ export async function bulkPutMonitors(
options: PushOptions,
schemas: MonitorSchema[]
) {
const resp = await sendReqAndHandleError<PutResponse>({
url: generateURL(options, 'bulk_update') + '/_bulk_update',
method: 'PUT',
auth: options.auth,
body: JSON.stringify({ monitors: schemas }),
});
const { failedMonitors } = resp;
if (failedMonitors && failedMonitors.length > 0) {
throw formatFailedMonitors(failedMonitors);
const url = generateURL(options, 'bulk_update') + '/_bulk_update';

async function sendRequest(payload: MonitorSchema[]): Promise<PutResponse> {
try {
const resp = await sendReqAndHandleError<PutResponse>({
url,
method: 'PUT',
auth: options.auth,
body: JSON.stringify({ monitors: payload }),
});

const { failedMonitors } = resp;
if (failedMonitors && failedMonitors.length > 0) {
throw formatFailedMonitors(failedMonitors);
}
return resp;
} catch (error) {
if (error instanceof RequestPayloadTooLargeError) {
// Split the payload into two halves
const mid = Math.ceil(payload.length / 2);
const firstHalf = payload.slice(0, mid);
const secondHalf = payload.slice(mid);

// Recursively send each half and combine the results
const [firstResult, secondResult] = await Promise.all([
sendRequest(firstHalf),
sendRequest(secondHalf),
]);

// Combine the results (if necessary, based on your API's behavior)
return {
createdMonitors: [
...(firstResult.createdMonitors ?? []),
...(secondResult.createdMonitors ?? []),
],
updatedMonitors: [
...(firstResult.updatedMonitors ?? []),
...(secondResult.updatedMonitors ?? []),
],
failedMonitors: [
...(firstResult.failedMonitors ?? []),
...(secondResult.failedMonitors ?? []),
],
} as PutResponse;
}

// Re-throw other errors
throw error;
}
}
return resp;

return sendRequest(schemas);
}

export type GetResponse = {
Expand Down
10 changes: 10 additions & 0 deletions src/push/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export async function sendReqAndHandleError<T>(
options: APIRequestOptions
): Promise<T> {
const { statusCode, body } = await sendRequest(options);
if (statusCode === 414) {
throw new RequestPayloadTooLargeError('Request payload too large');
}
return (
await handleError(statusCode, options.url, body)
).json() as Promise<T>;
Expand Down Expand Up @@ -145,3 +148,10 @@ export function formatStaleMonitors(errors: APIMonitorError[]) {
const heading = bold(`${symbols['warning']} Warnings\n`);
return yellow(heading + formatMonitorError(errors));
}

export class RequestPayloadTooLargeError extends Error {
constructor(message: string) {
super(message);
this.name = 'RequestPayloadTooLargeError';
}
}
Loading