# Exercise 3: Lambda Consumer, Partial Batch Failures and Scaling

A Lambda function `resize-image` consumes the `thumbnails` queue from Exercise 1 via an event source mapping.

## Facts

- Average processing: 2 s per message; p99 8 s. Batch size 10.
- Traffic: normally 5 messages/s; a nightly import enqueues 300,000 messages within 10 minutes.
- Downstream S3 writes are fine at any rate, but the function also calls an internal metadata API that tolerates at most **100 concurrent requests**.
- Occasionally one image in a batch is corrupt and throws.

## Deliverables

1. **Visibility timeout** for the queue given the Lambda timeout you choose (state both, using AWS's guidance for Lambda + SQS).
2. **Partial batch responses**: write the handler skeleton (TypeScript) that returns `batchItemFailures` for failed messages only, and enable `ReportBatchItemFailures` on the mapping. Explain what happens to the 9 good messages without it.
3. **Concurrency control**: configure the event source mapping's **maximum concurrency** (or reserved concurrency) to respect the metadata API's limit. Show the numbers.
4. **Backlog math**: with your concurrency cap, how long does the nightly import take to drain? Is that acceptable if thumbnails must be ready within 2 hours?
5. **Monitoring**: which two SQS metrics and one Lambda metric would you alarm on, with thresholds?
6. **Poison handling**: how do the corrupt images end up in the DLQ, and how many times is each retried?

## Requirements Checklist

- [ ] Lambda timeout > p99 (e.g. 30 s) and queue visibility timeout ≥ 6 × the Lambda timeout (e.g. 180 s).
- [ ] Handler catches per-record errors and returns `{ batchItemFailures: [{ itemIdentifier: messageId }] }`; without it, the whole batch is retried, re-processing the 9 good images.
- [ ] Maximum concurrency set so concurrent API calls ≤ 100 (e.g. maximum concurrency ≈ 100 if each invocation makes one call at a time).
- [ ] Drain time computed: 300,000 messages × 2 s / 100 concurrent ≈ 6,000 s ≈ 100 minutes → within 2 hours (show the math and note it's tight).
- [ ] Alarms: `ApproximateAgeOfOldestMessage` (lag), DLQ `ApproximateNumberOfMessagesVisible` ≥ 1, Lambda `Errors`/`Throttles`.
- [ ] Corrupt messages fail repeatedly until `maxReceiveCount` (5) is exceeded → moved to the DLQ.
