-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(batch): add batch processing utility (#1625)
* chore: init workspace * chore: init workspace * feat(batch): Implementation of base batch processing classes (#1588) * chore: init workspace * chore: init workspace * Initial base class implementation * Added BatchProcessor implementation, attempted fix for async * Added unit tests * Refactoring unit tests * Lint fix, updated docstrings * Added response and identifier typings * test(idempotency): improve integration tests for utility (#1591) * docs: new name * chore: rename e2e files * tests(idempotency): expand integration tests * chore(idempotency): remove unreachable code * Removed unnecessary type casting * Moved exports for handlers and factories * Updated imports, refactored randomization in factories * Refactored EventType to be const instead of enum * Refactored and added documentation for errors * Removed debugging line * chore(ci): add canary to layer deployment (#1593) * docs(idempotency): write utility docs (#1592) * docs: base docs * wip * chore: added paths to snippets tsconfig * chore: added page to docs menu * docs(idempotency): utility docs * highlights * chore: remove CDK mention * build(internal): bump semver from 5.7.1 to 5.7.2 (#1594) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](npm/node-semver@v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(idempotency): mark the utility ready public beta (#1595) * chore(idempotency): mark utility as public beta * chore: manually increment version in commons * docs(internal): update AWS SDK links to new docs (#1597) * chore(maintenance): remove parameters utility from layer bundling and layers e2e tests (#1599) * remove parameter from e2e tests * remove parameters from canary stack as well * chore(release): v1.11.1 [skip ci] * fix canary deploy in ci with correct workspace name (#1601) * chore: update layer ARN on documentation --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Andrea Amorosi <dreamorosi@gmail.com> Co-authored-by: Alexander Schueren <amelnyk@amazon.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Release bot[bot] <aws-devax-open-source@amazon.com> * feat(batch): Batch processing wrapper function (#1605) * Refactored some types, added function wrapper and base test * Added record check and tests, renamed factories * Refactored type check logic in function * Refactor test to remove error ignore * feat(batch): Implement SQS FIFO processor class (#1606) * Added SQS FIFO processor and unit tests * Added docstring for pbatch processing function * feat(batch): Support for Lambda context access in batch processing (#1609) * Added types and parameter for lambda context, added unit tests * Refactor parameter checking * Added test for malformed context handling * docs: created utility docs * docs: fixed white spaces * feat(batch): add async processor (#1616) * feat(batch): add async processor * tests: improved unit tests * chore: removed docstring + edited test handler * chore: fix typos * docs: added README * chore: added package to beta release * chore: marked package as public * chore: added new batch page to docs * chore: added utility to lerna workspace * chore: added utility to main readme * chore: added utility to CI --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Erika Yao <71943596+erikayao93@users.noreply.github.com> Co-authored-by: Alexander Schueren <amelnyk@amazon.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Release bot[bot] <aws-devax-open-source@amazon.com>
- Loading branch information
1 parent
0b575a1
commit c4e6b19
Showing
55 changed files
with
3,367 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { | ||
BatchProcessor, | ||
EventType, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
SQSEvent, | ||
SQSRecord, | ||
Context, | ||
SQSBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new BatchProcessor(EventType.SQS); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: SQSRecord, lambdaContext?: Context): void => { | ||
const payload = record.body; | ||
if (payload) { | ||
const item = JSON.parse(payload); | ||
logger.info('Processed item', { item }); | ||
} | ||
if (lambdaContext) { | ||
logger.info('Remaining time', { | ||
time: lambdaContext.getRemainingTimeInMillis(), | ||
}); | ||
} | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { BatchProcessor, EventType } from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
SQSEvent, | ||
SQSRecord, | ||
Context, | ||
SQSBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new BatchProcessor(EventType.SQS); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: SQSRecord): void => { | ||
const payload = record.body; | ||
if (payload) { | ||
const item = JSON.parse(payload); | ||
logger.info('Processed item', { item }); | ||
} | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
const batch = event.Records; | ||
|
||
processor.register(batch, recordHandler, { context }); | ||
const processedMessages = processor.process(); | ||
|
||
for (const message of processedMessages) { | ||
const status: 'success' | 'fail' = message[0]; | ||
const record = message[2]; | ||
|
||
logger.info('Processed record', { status, record }); | ||
} | ||
|
||
return processor.response(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { randomInt } from 'node:crypto'; | ||
import { | ||
DynamoDBClient, | ||
BatchWriteItemCommand, | ||
} from '@aws-sdk/client-dynamodb'; | ||
import { marshall } from '@aws-sdk/util-dynamodb'; | ||
import { | ||
BasePartialProcessor, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import type { | ||
SuccessResponse, | ||
FailureResponse, | ||
EventSourceType, | ||
} from '@aws-lambda-powertools/batch'; | ||
import type { SQSEvent, Context, SQSBatchResponse } from 'aws-lambda'; | ||
|
||
const tableName = process.env.TABLE_NAME || 'table-not-found'; | ||
|
||
class MyPartialProcessor extends BasePartialProcessor { | ||
#tableName: string; | ||
#client?: DynamoDBClient; | ||
|
||
public constructor(tableName: string) { | ||
super(); | ||
this.#tableName = tableName; | ||
} | ||
|
||
public async asyncProcessRecord( | ||
_record: EventSourceType | ||
): Promise<SuccessResponse | FailureResponse> { | ||
throw new Error('Not implemented'); | ||
} | ||
|
||
/** | ||
* It's called once, **after** processing the batch. | ||
* | ||
* Here we are writing all the processed messages to DynamoDB. | ||
*/ | ||
public clean(): void { | ||
// We know that the client is defined because clean() is called after prepare() | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
this.#client!.send( | ||
new BatchWriteItemCommand({ | ||
RequestItems: { | ||
[this.#tableName]: this.successMessages.map((message) => ({ | ||
PutRequest: { | ||
Item: marshall(message), | ||
}, | ||
})), | ||
}, | ||
}) | ||
); | ||
} | ||
|
||
/** | ||
* It's called once, **before** processing the batch. | ||
* | ||
* It initializes a new client and cleans up any existing data. | ||
*/ | ||
public prepare(): void { | ||
this.#client = new DynamoDBClient({}); | ||
this.successMessages = []; | ||
} | ||
|
||
/** | ||
* It handles how your record is processed. | ||
* | ||
* Here we are keeping the status of each run, `this.handler` is | ||
* the function that is passed when calling `processor.register()`. | ||
*/ | ||
public processRecord( | ||
record: EventSourceType | ||
): SuccessResponse | FailureResponse { | ||
try { | ||
const result = this.handler(record); | ||
|
||
return this.successHandler(record, result); | ||
} catch (error) { | ||
return this.failureHandler(record, error as Error); | ||
} | ||
} | ||
} | ||
|
||
const processor = new MyPartialProcessor(tableName); | ||
|
||
const recordHandler = (): number => { | ||
return Math.floor(randomInt(1, 10)); | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; | ||
import { | ||
BatchProcessor, | ||
EventType, | ||
FailureResponse, | ||
EventSourceType, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
SQSEvent, | ||
SQSRecord, | ||
Context, | ||
SQSBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
class MyProcessor extends BatchProcessor { | ||
#metrics: Metrics; | ||
|
||
public constructor(eventType: keyof typeof EventType) { | ||
super(eventType); | ||
this.#metrics = new Metrics({ namespace: 'test' }); | ||
} | ||
|
||
public failureHandler( | ||
record: EventSourceType, | ||
error: Error | ||
): FailureResponse { | ||
this.#metrics.addMetric('BatchRecordFailures', MetricUnits.Count, 1); | ||
|
||
return super.failureHandler(record, error); | ||
} | ||
} | ||
|
||
const processor = new MyProcessor(EventType.SQS); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: SQSRecord): void => { | ||
const payload = record.body; | ||
if (payload) { | ||
const item = JSON.parse(payload); | ||
logger.info('Processed item', { item }); | ||
} | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { | ||
AsyncBatchProcessor, | ||
EventType, | ||
asyncProcessPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import axios from 'axios'; // axios is an external dependency | ||
import type { | ||
SQSEvent, | ||
SQSRecord, | ||
Context, | ||
SQSBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new AsyncBatchProcessor(EventType.SQS); | ||
|
||
const recordHandler = async (record: SQSRecord): Promise<number> => { | ||
const res = await axios.post('https://httpbin.org/anything', { | ||
message: record.body, | ||
}); | ||
|
||
return res.status; | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
return await asyncProcessPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { | ||
BatchProcessor, | ||
EventType, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
DynamoDBStreamEvent, | ||
DynamoDBRecord, | ||
Context, | ||
DynamoDBBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new BatchProcessor(EventType.DynamoDBStreams); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: DynamoDBRecord): void => { | ||
if (record.dynamodb && record.dynamodb.NewImage) { | ||
logger.info('Processing record', { record: record.dynamodb.NewImage }); | ||
const message = record.dynamodb.NewImage.Message.S; | ||
if (message) { | ||
const payload = JSON.parse(message); | ||
logger.info('Processed item', { item: payload }); | ||
} | ||
} | ||
}; | ||
|
||
export const handler = async ( | ||
event: DynamoDBStreamEvent, | ||
context: Context | ||
): Promise<DynamoDBBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { | ||
BatchProcessor, | ||
EventType, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
KinesisStreamEvent, | ||
KinesisStreamRecord, | ||
Context, | ||
KinesisStreamBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new BatchProcessor(EventType.KinesisDataStreams); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: KinesisStreamRecord): void => { | ||
logger.info('Processing record', { record: record.kinesis.data }); | ||
const payload = JSON.parse(record.kinesis.data); | ||
logger.info('Processed item', { item: payload }); | ||
}; | ||
|
||
export const handler = async ( | ||
event: KinesisStreamEvent, | ||
context: Context | ||
): Promise<KinesisStreamBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { | ||
BatchProcessor, | ||
EventType, | ||
processPartialResponse, | ||
} from '@aws-lambda-powertools/batch'; | ||
import { Logger } from '@aws-lambda-powertools/logger'; | ||
import type { | ||
SQSEvent, | ||
SQSRecord, | ||
Context, | ||
SQSBatchResponse, | ||
} from 'aws-lambda'; | ||
|
||
const processor = new BatchProcessor(EventType.SQS); | ||
const logger = new Logger(); | ||
|
||
const recordHandler = (record: SQSRecord): void => { | ||
const payload = record.body; | ||
if (payload) { | ||
const item = JSON.parse(payload); | ||
logger.info('Processed item', { item }); | ||
} | ||
}; | ||
|
||
export const handler = async ( | ||
event: SQSEvent, | ||
context: Context | ||
): Promise<SQSBatchResponse> => { | ||
return processPartialResponse(event, recordHandler, processor, { | ||
context, | ||
}); | ||
}; | ||
export { processor }; |
Oops, something went wrong.