Lambda in a data pipeline

Lesson 2 of 4 in Automating Data Processing.

Lambda is excellent at four jobs in a data pipeline and bad at a fifth, and the fifth is the one people reach for.

It is excellent as glue between events and services — an object landed, so start a workflow, tag the object, write a catalog entry. Excellent as a per-record transformer on a stream, through Firehose's transformation hook or an event source mapping on a stream or queue. Excellent as a small-batch processor, where "small" means something that fits comfortably in one invocation's memory and finishes well inside fifteen minutes. And excellent as an API or control-plane caller — the step in a workflow that calls three services and decides something.

It is bad as an ETL engine. Not because it cannot technically do it, but because the limits are hard and they arrive without warning: a maximum timeout of 15 minutes, memory from 128 MB to 10,240 MB, ephemeral /tmp from 512 MB to 10,240 MB, a 6 MB request and response payload on a synchronous invoke, and no shuffle, no spill, no cluster. A function that processes a 200 MB file today processes a 2 GB file in six months by timing out at minute fifteen, having written a third of its output. Spark exists because that failure mode is unacceptable.

The useful mental test is not "is this data big" — it is "what happens to this code when the input is ten times larger?" If the answer is "it runs ten times longer", Lambda has a cliff and Glue does not. If the answer is "there are ten times as many invocations", Lambda is exactly right, because that is the dimension it scales in.

Two more things about Lambda in a pipeline are worth stating precisely, because both are asked as scenarios rather than as facts.

Concurrency is a downstream problem, not a Lambda problem. The regional pool defaults to 1,000 concurrent executions, and a burst of S3 events will happily use all of it — which is marvellous until the target is a relational database with two hundred connections, at which point your pipeline is a denial-of-service attack on your own warehouse. Reserved concurrency is the fix, and it does two things from one number: it guarantees the function that much and it caps the function at that much. Set it to what the downstream can survive and let the queue hold the rest. Setting it to zero throttles the function entirely, which is the documented emergency stop for a runaway consumer without deleting its trigger.

Firehose transformation is its own shape, with its own contract. A Firehose delivery stream can invoke a Lambda function on each buffered batch, and your function must return every record it was given with a result of Ok, Dropped, or ProcessingFailed — returning fewer records than you received is an error, not a filter. Records you mark as failed go to an error prefix in the destination bucket rather than vanishing. This is the right place for per-record parsing, enrichment and PII redaction on the way into the lake, and the wrong place for anything that needs to see more than one batch, because batches are exactly as independent as they look.

Lambda, or something else?

Interactive decision tree — outcomes:

  • Lambda with an event source mapping

    The shape Lambda was built for. Tune BatchSize, the batching window and ParallelizationFactor; enable partial batch responses so one bad record does not re-run 199 good ones; and set the retry limit, record age and on-failure destination before you need them, not after.

  • A Firehose transformation function

    Per-record parsing, enrichment or redaction on the way into the lake. Return every record you were handed with Ok, Dropped or ProcessingFailed — returning fewer is an error. Failed records land in an error prefix, so nothing disappears silently.

  • Lambda, with reserved concurrency sized to the downstream

    The function is right; its concurrency is the hazard. Reserve the number of simultaneous executions the database or API can survive — the same setting both guarantees and caps — and let the queue absorb the burst. Zero is the emergency stop.

  • Lambda, and write down why it is safe

    A file-level operation that will not grow — re-tagging an object, writing a catalog partition, validating a header — is a fine Lambda. Record the assumption in the code, and put a CloudWatch alarm on duration approaching the timeout so the cliff announces itself before it arrives.

  • AWS Glue, not Lambda

    Once the unit of work grows with the data, you need an engine that scales across workers and can spill to disk. Glue (or EMR, if you need cluster control or a non-Spark framework) has no 15-minute wall and no 10 GB memory ceiling, and a failed run leaves a known state rather than a third of an output file.

  • Step Functions Distributed Map over many small Lambdas

    If the whole is too big but each partition is small, do not grow the function — multiply it. Distributed Map iterates an S3 listing as child executions, up to ten thousand concurrent, with a tolerated failure percentage and per-item results in S3. This is also the standard shape for a backfill.

  • A Step Functions task, often with no Lambda at all

    If the work is "call these services and decide", Step Functions can call most AWS APIs directly through its SDK integrations, with retries and catch per state. A Lambda whose body is three SDK calls and an if is a function you now have to patch, deploy and monitor for no benefit.

Key terms: Lambda event source mapping, Reserved concurrency, Dead-letter queue, Distributed Map

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.