diff --git a/src/types.ts b/src/types.ts index 52a4f1f..fc795df 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import { Lambda as CFLambda, SQS as CFSQS } from "cloudform-types"; +import { Worker } from "./worker"; export type Integration = Omit< Partial, @@ -16,3 +17,7 @@ export type Lambda = Omit, "Properties"> & { }; export type SQS = Pick & { id: string }; + +export interface Advisor { + apply: (worker: Worker) => string[]; +} diff --git a/src/visibility-timeout-advisor.ts b/src/visibility-timeout-advisor.ts new file mode 100644 index 0000000..73cfa81 --- /dev/null +++ b/src/visibility-timeout-advisor.ts @@ -0,0 +1,16 @@ +import { Advisor } from "./types"; +import { Worker } from "./worker"; + +export class VisibilityTimeoutAdvisor implements Advisor { + apply(worker: Worker) { + const suggestions: string[] = []; + const visibilityTimeout = Number(worker.sqs.Properties.VisibilityTimeout); + const timeout = Number(worker.lambda.Properties.Timeout); + if (visibilityTimeout < timeout * 6) { + suggestions.push( + "SQS VisibilityTimeout must be greater than Lambda Timeout * 6", + ); + } + return suggestions; + } +} diff --git a/src/worker.ts b/src/worker.ts index 6538df6..946388b 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -21,10 +21,27 @@ export class Worker { } analyze() { - console.log(this.id); - console.log(this.lambda); - console.log(this.integration); - console.log(this.sqs); - return []; + const suggestions = []; + if ( + this.integration.Properties.ScalingConfig?.MaximumConcurrency === + undefined + ) { + suggestions.push( + `Messages production rate should be less than ${ + Number(this.integration.Properties.BatchSize) / + Number(this.lambda.Properties.Timeout) + } message(s) per seconds * executions count`, + ); + suggestions.push("Specify MaximumConcurrency to prevent throttle"); + } else { + suggestions.push( + `Messages production rate should be less than ${ + Number(this.integration.Properties.ScalingConfig.MaximumConcurrency) * + (Number(this.integration.Properties.BatchSize) / + Number(this.lambda.Properties.Timeout)) + } message(s) per seconds`, + ); + } + return suggestions; } } diff --git a/test/visibility-timeout-advisor.test.ts b/test/visibility-timeout-advisor.test.ts new file mode 100644 index 0000000..3d0e621 --- /dev/null +++ b/test/visibility-timeout-advisor.test.ts @@ -0,0 +1,54 @@ +import { VisibilityTimeoutAdvisor } from "../src/visibility-timeout-advisor"; +import { Worker } from "../src/worker"; + +describe("App", () => { + it("should contain suggestion 'SQS VisibilityTimeout must be greater than Lambda Timeout * 6'", () => { + const worker = new Worker({ + id: "worker", + lambda: { id: "lambda", Properties: { Timeout: 1 } }, + sqs: { id: "sqs", Properties: { VisibilityTimeout: 1 } }, + integration: { + id: "integration", + Properties: { + BatchSize: 1, + ScalingConfig: { MaximumConcurrency: 1 }, + }, + }, + }); + expect(new VisibilityTimeoutAdvisor().apply(worker)).toEqual( + expect.arrayContaining([ + "SQS VisibilityTimeout must be greater than Lambda Timeout * 6", + ]), + ); + }); + it("should not contain suggestion 'SQS VisibilityTimeout must be greater than Lambda Timeout * 6'", () => { + const worker = new Worker({ + id: "worker", + lambda: { id: "lambda", Properties: { Timeout: 1 } }, + sqs: { id: "sqs", Properties: { VisibilityTimeout: 6 } }, + integration: { + id: "integration", + Properties: { + BatchSize: 1, + ScalingConfig: { MaximumConcurrency: 1 }, + }, + }, + }); + expect(new VisibilityTimeoutAdvisor().apply(worker)).toHaveLength(0); + }); + it("should not contain suggestion 'SQS VisibilityTimeout must be greater than Lambda Timeout * 6'", () => { + const worker = new Worker({ + id: "worker", + lambda: { id: "lambda", Properties: { Timeout: 1 } }, + sqs: { id: "sqs", Properties: { VisibilityTimeout: 7 } }, + integration: { + id: "integration", + Properties: { + BatchSize: 1, + ScalingConfig: { MaximumConcurrency: 1 }, + }, + }, + }); + expect(new VisibilityTimeoutAdvisor().apply(worker)).toHaveLength(0); + }); +}); diff --git a/test/worker.test.ts b/test/worker.test.ts index 7830d76..fb988ee 100644 --- a/test/worker.test.ts +++ b/test/worker.test.ts @@ -18,3 +18,73 @@ it("should instantiate a worker correctly", () => { expect(worker.integration.id).toBe("integration"); expect(worker.integration.Properties.BatchSize).toBe(10); }); + +describe("worker with different options", () => { + const analyze = ( + Timeout: number, + BatchSize: number, + MaximumConcurrency?: number, + ) => + new Worker({ + id: "worker", + lambda: { id: "lambda", Properties: { Timeout } }, + sqs: { id: "sqs", Properties: {} }, + integration: { + id: "integration", + Properties: { BatchSize, ScalingConfig: { MaximumConcurrency } }, + }, + }).analyze(); + + // analyze(Timeout, BatchSize, MaxConcurrency) + describe("when MaximumConcurrency is undefined", () => { + it("should satisfy the options", () => { + expect(analyze(1, 1)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 1 message(s) per seconds * executions count", + "Specify MaximumConcurrency to prevent throttle", + ]), + ); + expect(analyze(10, 1)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 0.1 message(s) per seconds * executions count", + "Specify MaximumConcurrency to prevent throttle", + ]), + ); + expect(analyze(1, 10)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 10 message(s) per seconds * executions count", + "Specify MaximumConcurrency to prevent throttle", + ]), + ); + }); + }); + describe("when MaximumConcurrency is a positive number greater or equal than 2", () => { + it("should satisfy the options", () => { + expect(analyze(1, 1, 2)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 2 message(s) per seconds", + ]), + ); + expect(analyze(10, 1, 2)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 0.2 message(s) per seconds", + ]), + ); + expect(analyze(1, 10, 2)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 20 message(s) per seconds", + ]), + ); + expect(analyze(10, 1, 10)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 1 message(s) per seconds", + ]), + ); + expect(analyze(1, 10, 10)).toEqual( + expect.arrayContaining([ + "Messages production rate should be less than 100 message(s) per seconds", + ]), + ); + }); + }); +});