Skip to content

The Lambda worker

2 min read · main part only; folded sections are optional

After this you can answer: How does a message in a queue become a Lambda call, and what is a partial batch failure?

flowchart TB
Q["SQS jobs queue"] -->|"poll"| M["event source mapping<br/>batch 10, max 5 at once"]
M -->|"call with batch"| F["worker Lambda<br/>256 MB, 10 s"]
F -->|"batchItemFailures: [ids]"| M
M -->|"delete the rest"| Q
  1. Poll. AWS’s pollers long-poll the jobs queue and collect up to 10 messages.
  2. Call. They call the function with the batch. At most 5 batches run at the same time (maximumConcurrency).
  3. Handle one by one. For each message: is the job already done (DynamoDB)? If not, send the email (SES), then record it as done.
  4. Answer. The handler returns { batchItemFailures }, listing only the failed message ids. Before answering, it hides each failed message for 10 s, 40 s or 120 s.
  5. Delete the rest. Because the mapping has ReportBatchItemFailures, Lambda deletes every message not in the list. The failed ones come back later.
Worker (email)Tickets
Functionminitickets-dev-workerminitickets-dev-tickets
Entry pointlambda.handlerticket-lambda.handler
Memory / timeout256 MB / 10 s512 MB / 30 s
Batch / parallel10 / 51 / 2

Both are container images (arm64) from the worker repository, and both run outside the VPC: they call only public AWS APIs, so they need no NAT gateway. A Lambda has ONE execution role, used by the Lambda service and our code alike; trust: lambda.amazonaws.com.

Measured locally (under the Lambda runtime emulator): the first start took 302 ms, a warm batch 23 ms, and a failed message came back after 10.0 s.

Who may assume Worker Lambda execution role: Worker Lambda (Principal lambda.amazonaws.com; only someone with iam:PassRole on it can attach it to a function)

What it may do:

OnActionsPolicyWhy only this
Jobs SQS queuesqs:ReceiveMessagesqs:DeleteMessagesqs:ChangeMessageVisibilitysqs:GetQueueAttributesidentity ReadJobsQueue createWorkerRoleThe event source mapping receives and deletes with this role; the code's backoff changes the visibility. No SendMessage, no DLQ: SQS itself moves failed messages.
Processed-jobs tabledynamodb:GetItemdynamodb:PutItemidentity ProcessedJobsTable createWorkerRoleisDone is a GetItem and markDone a conditional PutItem. No Scan, Query or Delete: TTL deletes for free.
SES email identitiesour domain identity ARN and the test-address identity ARNses:SendEmailif ses:FromAddress = no-reply@minitickets.theversehub.inidentity SendBookingEmails createWorkerRoleThe policy restricts the sender to our configured address; it does not constrain email content.
CloudWatch log groups/minitickets/dev/worker:* onlylogs:CreateLogStreamlogs:PutLogEventsidentity WriteWorkerLogs createWorkerRoleNo logs:CreateLogGroup: Pulumi creates the group with a 7-day retention.
See the worker and its role on the map →

Lambda → Functions → minitickets-dev-worker → Configuration → Triggers, and Monitor.

SymptomCauseWhere to look
Failed messages disappear after a normal returnReportBatchItemFailures is missing, so Lambda ignores the failed ids and deletes the whole batchLambda → Triggers
Messages move slowly; Throttles above 0The account’s available concurrency is exhaustedLambda → Monitor; check the current account quota
“Task timed out after 10.00 seconds”A slow SES or DynamoDB call; undeleted messages become visible when their visibility timeout expires/minitickets/dev/worker
Why maximumConcurrency and not reserved concurrency?

With only a reservation, the pollers can fetch more batches than may run. Those calls are throttled, and repeated receives can send healthy messages to the DLQ. maximumConcurrency caps this queue’s concurrent invocations at five. It reserves no capacity: other functions can still exhaust the account quota and throttle it.

Why outside the VPC?

Inside, it would need a NAT gateway or VPC endpoints to reach SES, SQS and DynamoDB. It never talks to Postgres: the message carries everything the email needs.

Container image or zip?

One image pipeline for all three apps and a 10 GB uncompressed image limit. A larger deployment can add initialization work; measure cold starts rather than assuming one format is faster.

Can good messages still be retried?

Yes. An uncaught exception or timeout fails the whole invocation, even with partial batch reporting. Some emails may already have been sent. The completed-job check skips recorded jobs; a send before its completion record can still be repeated.

Advanced: Details: the tickets Lambda and both roles

The tickets function signs up to four tickets with an HMAC key it reads from Secrets Manager at run time, writes QR PNGs under tickets/ in S3 and the ticket items to DynamoDB. It uses the queue’s visibility timeout as its retry delay. The worker’s log group is /minitickets/dev/worker, not Lambda’s default /aws/lambda/…, so the 7-day retention applies. AWS documents the partial batch response rules and the 10 GB image limit. Its pricing page explains request and duration billing.

Who may assume Tickets Lambda execution role: Tickets Lambda (Principal lambda.amazonaws.com)

What it may do:

OnActionsPolicyWhy only this
Tickets SQS queuesqs:ReceiveMessagesqs:DeleteMessagesqs:GetQueueAttributesidentity createTicketRoleConsume the tickets queue through the event source mapping.
Tickets tabledynamodb:GetItemdynamodb:PutItemdynamodb:UpdateItemidentity createTicketRoleReserve, write and complete the ticket items of one booking.
Ticket assets buckettickets/* onlys3:PutObjectidentity createTicketRoleWrite the QR PNGs. It cannot read or touch posters.
Ticket signing secretsecretsmanager:GetSecretValueidentity createTicketRoleRead the HMAC key that signs tickets. The api never gets this key.
CloudWatch log groups/minitickets/dev/tickets:* onlylogs:CreateLogStreamlogs:PutLogEventsidentity createTicketRoleWrite its own log lines, nowhere else.

Answer these without scrolling up. If one feels hard, reread that section.

  1. What does the event source mapping do for us?
  2. What does the handler return, and what does Lambda do with it?
  3. What happens without ReportBatchItemFailures?
  4. Why does the worker run outside the VPC?
  5. Name two differences between the worker and the tickets Lambda.

Redraw it from memory: jobs queue → event source mapping (batch 10, max 5) → worker → batchItemFailures → delete the rest

Try this: on the map, select the worker Lambda execution role and see what its statements light up

Read this book online: https://book.theversehub.in/