Programming Concepts for Data Operations
Intermediate⏱60 minutesπŸ“–45 min readLesson 6 of 16

Programming Concepts for Data Operations

Turn DEA-C01 Domain 1 Task 1.4 into decisions you can defend on exam day: which language-agnostic programming concepts are actually testable and where language-specific syntax stops, how pandas on a single node differs from PySpark across partitions, why lazy evaluation, partition counts and the UDF cost ladder decide whether a Glue job is fast or ruinous, the SQL patterns the exam keeps re-asking (CTEs, window functions, joins, PIVOT and JSON parsing in Athena and Redshift), how AWS Lambda behaves as a data-processing primitive through its event sources, concurrency pool, 900-second ceiling and memory-coupled CPU, how to choose between Lambda, Glue Python shell, Glue Spark and AWS Batch on size, duration, frequency and runtime limits, and how idempotency, watermarking, at-least-once versus exactly-once effects and poison-pill handling keep a replay from corrupting a table, plus nested-data flattening, schema-on-read versus schema-on-write, a sourced October-2026 update box, two AWS customer case studies and twelve exam-style questions.

Every pipeline you have studied so far moves and reshapes data. This lesson asks the question underneath: what code runs, and why does it behave the way it does? DEA-C01 answers that in Domain 1, Task 1.4, and the task statement is deliberately narrow. The exam guide lists "language-agnostic programming concepts" as in scope and "programming language-specific syntax" as out of scope, so no item will ask you to recall a decorator, a list comprehension or a package import. Instead the stem hands you a pipeline problem (rank these rows, deduplicate this replay, parse this nested field, pick a runtime) and expects the right construct or service back (DEA-C01 exam guide, accessed Oct 2026).

plaintext
==================================================================== DEA-C01 DOMAIN 1 - TASK 1.4: APPLY PROGRAMMING CONCEPTS ==================================================================== Domain 1 weight ....... 34% of scored content IN SCOPE .............. language-agnostic programming concepts, SQL + query optimization, IaC, Git, CI/CD, distributed computing, data structures and algorithms, Lambda concurrency/performance, AWS SAM, mounting volumes in Lambda OUT OF SCOPE .......... "programming language-specific syntax" --------------------------------------------------------------------- CONCEPT -> THE SHAPE IT TAKES IN A DATA PIPELINE functions / types -> Lambda handler, Glue resolveChoice / cast: variables / scope -> local vs 4 KB Lambda environment variables conditionals -> CASE WHEN, Step Functions Choice, filters loops -> GROUP BY, Map state, repartition fan-out OOP basics -> Lambda layers mounted at /opt (max 5, zip) APIs -> pagination, 429 backoff, SDK / CLI access serialization -> CSV / JSON / Avro / Parquet / ORC choice --------------------------------------------------------------------- RUNTIME ENVELOPE (as of Oct 2026) Lambda ........ 1-900 s Β· 128-10,240 MB Β· 1,769 MB ~ 1 vCPU Glue Spark .... 2-100 DPU Β· 1 DPU = 4 vCPU + 16 GB Β· default 10 Glue PyShell .. 0.0625 or 1 DPU Β· ~10 GB data Β· far beyond 15 min AWS Batch ..... container image Β· queue + compute env Β· hours ====================================================================

In this lesson you will:

  • separate language-agnostic concepts from language-specific syntax, and map each concept to the construct the exam expects;
  • read a Lambda handler and a Glue job configuration as programs, not as trivia;
  • choose between pandas on one node and PySpark across partitions, and name the crossover honestly;
  • apply lazy evaluation: transformations build a plan, an action triggers compute, and a shuffle is where the cost lands;
  • size partitions, pick coalesce or repartition, and explain why gzip files hurt;
  • climb the UDF cost ladder from built-in SQL down to plain Python, and know when a UDF is justified;
  • write window functions in Spark SQL, Redshift and pandas, and know why PARTITION BY changes parallelism;
  • use CTEs, joins, PIVOT and JSON parsing as the SQL patterns the exam re-asks;
  • configure AWS Lambda for data work: event sources versus event source mappings, the concurrency pool, the 900-second ceiling, memory-coupled CPU and layers;
  • apply the Lambda vs Glue vs Batch decision matrix on size, duration, frequency and runtime limits;
  • distinguish at-least-once delivery from an exactly-once effect, and build idempotent writes;
  • use watermarks to bound late data, and climb the poison-pill handling ladder;
  • flatten nested JSON and choose between schema-on-read and schema-on-write;
  • read the October 2026 update box, study two AWS customer case studies, and practise with 12 exam-style questions plus interactive checks.
  • expanded scope of this edition: the lesson now carries five AWS customer case studies (EMX, AppsFlyer, Hearst, Nasdaq and PayU), fourteen practice questions (dea-06-q1 through dea-06-q14) and three interactive checks: matching, fill-in-the-blank and drag-and-drop.

1. Programming concepts the exam actually tests

1.1 The scope boundary, in the guide's own words

Two phrases do all the work:

In scope: "language-agnostic programming concepts" Β· Out of scope: "programming language-specific syntax"

Everything in this section therefore has to survive a test: could I ask this without naming a language? If yes, it is examinable. "What does def handler(event, context) mean as an entry point?" is examinable, the shape is the same in Python and the concept of an entry point is universal. "What is the exact argument order of pd.merge()?" is not, that is syntax.

Task 1.4's skill list makes the same point from the other side. The named skills are optimise runtime performance, Lambda concurrency and performance, languages and frameworks (Python, SQL, Scala, R, Java, Bash, PowerShell), version control, testing, logging and monitoring, infrastructure as code, AWS SAM, mounting storage volumes in Lambda, CloudFormation/CDK, CI/CD, distributed computing and data structures and algorithms: a list of capabilities, with Git questions kept generic after AWS removed CodeCommit from the in-scope list at exam-guide v1.1 (2025-12-12) (DEA-C01 exam guide revisions page, accessed Oct 2026).

1.2 The concept β†’ construct map

This table is the whole of Section 1 compressed into what a question can actually test:

Programming concept
The shape it takes in a data pipeline
What the exam asks
Functions and types
A Lambda handler is the entry point; Glue jobs declare resolveChoice and cast: to coerce types before an aggregation
Name the entry point, or which type a column must be cast to
Variables and scope
Local variables live one invocation; Lambda environment variables are an 4 KB aggregate (as of Oct 2026)
Where a secret must not be stored
Conditionals
CASE WHEN in SQL Β· Choice state in Step Functions Β· filter() in Spark
Branch on data, not on infrastructure
Loops
GROUP BY aggregates a loop away Β· Map state fans out Β· foreach/repartition parallelise
Recognise when iteration must be serial vs parallel
Object-oriented basics
Composition and reuse β†’ Lambda layers mounted at /opt, maximum 5, zip-only (as of Oct 2026)
Reuse shared code without copying it into every package
APIs
Paginated list calls, 429/throttle backoff, SDK and CLI programmatic access
Choose programmatic access and handle throttling correctly
Serialization
CSV β†’ JSON/NDJSON β†’ Avro β†’ Parquet/ORC
Pick the format from the access pattern, not from habit
Distributed computing
One partition = one task; shuffles are stage boundaries
Where parallelism starts and where cost lands
Data structures and algorithms
Hash keys for dedupe, sorted windows for ranking, joins as hash/broadcast
Choose the structure that makes the operation cheap

1.3 Worked example E1: reading a handler, not memorising syntax

The exam gives you a shape like this and asks what it does or what to change. Read it as a control-flow diagram:

python
# Illustrative - the exam tests the SHAPE, not the boto3 calls. def handler(event, context): """Entry point the platform invokes; the name and arity are fixed.""" batch = event.get("records", []) # variable holding a typed collection ok, bad = [], [] # two accumulators = two code paths for record in batch: # loop over the batch, not over the table if record.get("amount") is None: # conditional on DATA bad.append(record) # route poison records off the hot path elif record["amount"] < 0: bad.append(record) else: ok.append(record) if len(ok) > 1_000: # a conditional that changes STRATEGY write_partition_overwrite(ok) # bulk, deterministic, replayable else: write_rows(ok) # small, row-at-a-time return {"ok": len(ok), "bad": len(bad)}

Three examinable facts fall out of it: the entry point is a function with a fixed signature; conditionals route records rather than retry them; and the strategy change at a size threshold is exactly the "optimise runtime performance" skill: small sets go row-wise, large sets go bulk.

1.4 Conditionals and loops prefer set-based SQL

The single most common Domain 1 pattern is a stem describing a loop over rows, with the correct answer being an aggregation or a window function:

Imperative idea
Set-based answer
Why the exam prefers it
Loop and count per customer
GROUP BY customer_id
The engine does the loop in parallel
Loop keeping the newest row per key
ROW_NUMBER() OVER (PARTITION BY k ORDER BY ts DESC)
Ranking without a self-join
Loop with an if/else that writes different columns
CASE WHEN … END inside the select list
One pass, no branch in application code
Loop that fans out over files
Map state / repartition / a Glue DynamicFrame map
Parallelism declared, not hand-rolled
Loop that waits for a condition
Step Functions Choice + Wait, or a watermark
Durable waiting instead of a busy loop

1.5 Object orientation shows up as layers, not as classes

Class hierarchies are not examinable. Reuse is. AWS's answer to "my team has three Lambda functions that all need the same helper library" is a layer: a zip archive mounted under /opt, with a hard maximum of 5 layers per function, and, this is the detail questions reach for, layers are zip-only, so a container image function cannot use them at all (the image bakes its dependencies instead, up to 10 GB) (AWS Lambda limits and layers documentation, accessed Oct 2026).

1.6 APIs show up as pagination and throttling

A stem describing "call a service that returns 100 records at a time until exhausted" is testing pagination; a stem describing "the pipeline fails every few minutes with HTTP 429" is testing backoff with jitter, not a specific retry library. Both are language-agnostic, and both map to the same service-side levers you met in Lesson 05: retry policies, DLQs and idempotent writes.

1.7 Serialization is a decision, not a syntax

Format
Orientation
Schema
Exam signature
CSV
row
none
Interchange only: no pruning, no nested types
JSON / NDJSON
row
inferred at read
Nesting + schema-on-read; heavy to parse
Avro
row
in the header, splittable
Move and evolve it: streaming and schema evolution
Parquet / ORC
columnar
in the footer
Read it: pruning, min/max pushdown, compression

The rule AWS's own guidance reduces to: Avro/JSON to move it, Parquet/ORC to read it. Because Athena bills bytes scanned, the format is the cost: a columnar file that lets the engine skip untouched columns is cheaper than a row format that cannot (AWS Athena and AWS Glue documentation, accessed Oct 2026).

Matching Question
How to answer

Matching exercise with 6 pairs. Match each item on the left with its answer on the right.

Match each language-agnostic programming concept to the construct the DEA-C01 exam expects you to name:

Conditional on data
Drop here
Loop over rows
Drop here
Reuse of shared code
Drop here
Where a secret lives
Drop here
Paged, throttled API access
Drop here
Serialization format choice
Drop here

Drag answers from here:

CASE WHEN in SQL, or a Choice state in Step Functions: branch on the DATA, never on the infrastructure
GROUP BY, a window function, or a Map state: set-based or parallel, never a hand-written row loop
Lambda layers mounted at /opt: maximum 5, zip-only, so container-image functions cannot use them
Secrets Manager or SSM Parameter Store with KMS - Lambda environment variables are capped at a 4 KB aggregate
Pagination with backoff and jitter on 429: language-agnostic, so no client library is ever named
Avro/JSON to move it, Parquet/ORC to read it: chosen from the ACCESS PATTERN, not from habit
  • πŸ“š Did you know? AWS's DEA-C01 guide changed how it presents this topic rather than what it contains: at v1.1 (2025-12-12) the separate "Knowledge of" and "Skills in" lists were consolidated into one skills list per task, adding 8 skills and removing none. That is why current study material quotes a flat numbered list like "1.4.1 optimise runtime performance" while older material quotes a Knowledge/Skills pair for the same content (DEA-C01 revisions page, accessed Oct 2026).

2. Python and PySpark patterns for data operations

2.1 Single node versus distributed: pick before you code

The first fork in every data task is where the loop runs. Getting it wrong is expensive in both directions: a distributed cluster for a 40 MB file wastes startup and idle capacity, while a single node for a 200 GB join runs out of memory.

Single node
Distributed
Service
AWS Glue Python shell Β· AWS Lambda Β· a Glue job's driver
AWS Glue Spark (ETL) Β· Amazon EMR Β· EMR Serverless
Capacity
PyShell 0.0625 or 1 DPU, about 10 GB of data Β· Lambda 128 to 10,240 MB and ≀900 s
Glue Spark 2 to 100 DPU (minimum 2 DPU), 1 DPU = 4 vCPU + 16 GB
Execution
One interpreter, one heap
One partition = one task across workers
Startup
Seconds (Lambda), small (PyShell)
Glue Spark jobs commonly pay ~2 to 3 minutes of startup before the first task
Cost shape
Per request + GB-s (Lambda), or a fraction of a DPU
Whole DPU-hour while the job runs, including idle workers
Fails when
It runs out of memory or hits its timeout
Skew, too many small files, or a bad shuffle

Crossover rule of thumb: roughly 10 million rows / 5 to 10 GB is where people stop reaching for pandas on one node, and you must state its provenance honestly. It is a 2017 third-party rule of thumb, never an AWS-published figure; the exam-grade facts are the service limits above, which are AWS-documented (as of Oct 2026).

⚠️Warning

⚠️ Un-rewritten pandas on a Glue Spark job raises your bill. AWS's Glue Python-shell migration guidance notes that a Glue Spark job allocates at least two Spark workers, so code that still runs pandas serially in the driver leaves the workers idle and charged. The fix is either to move to a Python shell job (single node, cheap) or to move the logic to pandas-on-Spark / PySpark (distributed). Choosing "Glue Spark because it is bigger" is a cost bug, not a default.

2.2 Lazy evaluation: plan first, compute once

Apache Spark transformations are lazy: reading, filtering, mapping and joining build a plan, and nothing runs until an action (write, count, collect, toPandas) forces execution (AWS Prescriptive Guidance, Tuning AWS Glue for Apache Spark, accessed Oct 2026).

100%

Two consequences the exam builds questions on:

  1. A transformed dataset is recomputed on every action unless you cache()/persist() it. Two actions on the same lineage pay for the lineage twice.
  2. Chain your transformations, then act once. Ten cheap actions beat one expensive action only if they share no recomputed lineage; which they usually do.
python
# PySpark - illustrative shape, not a memorisation exercise from pyspark.sql import functions as F df = spark.read.parquet("s3://lake/raw/orders/") # plan: source scan declared clean = (df .filter(F.col("amount") > 0) # transformation - no compute .withColumn("order_day", F.to_date("ts")))# transformation - no compute ranked = clean.withColumn( "rn", F.row_number().over( Window.partitionBy("customer_id", "order_day") .orderBy(F.col("amount").desc()))) # transformation - no compute top = ranked.filter(F.col("rn") <= 3) # still no compute top.write.mode("overwrite").partitionBy("order_day") \ .parquet("s3://lake/curated/top_orders/") # ACTION - the plan executes

2.3 Partitions: the unit of parallelism and of cost

Fact
Value
Source and date
Granularity
1 partition = 1 task
AWS Prescriptive Guidance, Oct 2026
Target count
2 to 3Γ— cores for Glue; 2 to 4 per CPU for Spark
AWS Prescriptive Guidance / Spark RDD guide, Oct 2026
Default shuffle partitions
200
AWS Glue SQL performance tuning, Oct 2026
Skew advisory threshold
128 MB per partition (Spark SQL advisory: 64 MB)
AWS Glue tuning, Oct 2026
maxResultSize on a large shuffle
1 GB
AWS Glue tuning, Oct 2026
Gzip
Unsplittable: one file, one task
AWS Glue parallelize-tasks guidance, Oct 2026
Adaptive query execution
Coalesces small partitions automatically on Glue β‰₯4.0 / Spark β‰₯3.2
AWS Glue, Oct 2026

Worked example E2: partition math

You have 120 GB of Parquet on a 32-core Glue job:

plaintext
Target partitions = 2 to 3 x cores = 64 to 96 Take 96 -> 120 GB / 96 = 1.25 GB per partition If the source has 120,000 tiny files: 120,000 tasks on a 32-core job = driver OOM + scheduler thrash fix: coalesce(96) on read, or repartition(96) before the write

The two functions are not interchangeable: coalesce merges partitions without a full shuffle (use it to go down), while repartition performs a full shuffle to reach an exact count (use it to go up, or to spread skew across a key).

  • πŸ“š Did you know? "More partitions = faster" is the most expensive misconception in Spark. Past about 2 to 3Γ— cores, extra partitions do not add parallelism; they add scheduler overhead, small-file writes and GC pressure. The opposite error is just as bad: one 60 GB gzip partition is one task on one core, and because gzip is unsplittable the engine cannot break it up. The habit worth building is to repartition by the partition columns before the write, so the file layout you land is the layout the reader will prune on (AWS Glue parallelize-tasks guidance, accessed Oct 2026).

2.4 The UDF cost ladder

A user-defined function is the escape hatch for third-party parsers, model scoring and stateful row logic. Everything else should stay in built-in SQL. AWS's own performance guidance places the options on a ladder, and the top rung is roughly five times faster than the bottom one:

Rung
Implementation
Why it costs what it costs
β‘  Cheapest
Built-in Spark SQL functions
Runs on the JVM, stays visible to the Catalyst optimiser, keeps predicate pushdown
β‘‘
pandas / Arrow vectorised UDF
Batched Arrow batches: AWS measures it at about 5Γ— a plain Python UDF
β‘’
Plain Python UDF
Pickles every row across the JVM↔Python boundary; the optimiser treats it as a black box
β‘£ Most expensive
Scala / Java UDF
No Python boundary, but the least portable and still opaque to some optimisations
python
# Ladder rung 3 - AVOID on hot paths: one pickle round trip PER ROW @F.udf(returnType="string") def normalise_slow(raw): # Catalyst cannot push this predicate down return raw.strip().lower() # Ladder rung 1 - PREFERRED: vectorised built-in, stays in the JVM clean = df.withColumn("sku", F.upper(F.trim(F.col("raw_sku")))) # Ladder rung 2 - only when there is genuinely no built-in: # a pandas UDF batches whole Arrow record batches instead of single rows

Exam rule: reach for a UDF only when no built-in can do it. On a 1-billion-row job, rung β‘’ versus rung β‘  is the difference between minutes and hours, and it is a performance optimisation question, which is Task 1.4 skill "optimise runtime performance" (AWS Glue Optimize user-defined functions, accessed Oct 2026).

2.5 Window functions: three stems, three answers

Stem
Construct
"latest / top N per key"
ROW_NUMBER() OVER (PARTITION BY key ORDER BY ts DESC)
"running total, period-over-period delta"
SUM() OVER (ORDER BY …) Β· LAG(col) OVER (ORDER BY …)
"share of total"
RATIO_TO_REPORT() OVER () (Redshift) or SUM() OVER () as a divisor
sql
-- Redshift / Spark SQL: top 3 orders per customer per day SELECT customer_id, order_date, amount, ROW_NUMBER() OVER ( PARTITION BY customer_id, order_date ORDER BY amount DESC ) AS rn FROM orders QUALIFY rn <= 3;

Parallelism hinge (a favourite distractor): in Amazon Redshift, PARTITION BY is what lets the window function run across slices in parallel; without it the window is computed serially on a single slice. And window functions are legal only in a SELECT list or in the final ORDER BY: not in a WHERE clause (Redshift window-function documentation, accessed Oct 2026).

In pandas, the same job is groupby().transform() or groupby().cumcount(): correct, but single-node. Past the crossover size it collects everything onto one process, which is why df.toPandas() / collect() inside a Glue Spark job is the canonical out-of-memory bug: it pulls the whole distributed frame to the driver.


3. SQL patterns the exam keeps re-asking

3.1 CTEs stage the logic so each step is readable

A common table expression (WITH) is how you express "first deduplicate, then filter, then rank" without three separate statements, and how you make a query reviewable. It is also the natural home for the dedupe step in an idempotent pipeline (Section 6).

sql
-- Stage 1: keep one row per (order_id, version) - the replay-safe row WITH ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY order_id ORDER BY updated_at DESC, version DESC ) AS rn FROM raw_orders WHERE dt = '2026-10-09' ), -- Stage 2: only the surviving rows join the aggregation latest AS ( SELECT order_id, customer_id, amount FROM ranked WHERE rn = 1 ) SELECT customer_id, COUNT(*) AS orders, SUM(amount) AS revenue FROM latest GROUP BY customer_id ORDER BY revenue DESC;

3.2 Joins: qualify names, broadcast the small side

Engine
Join trap the exam tests
Correct habit
Athena
Two tables with a same-named column produce an ambiguous-reference error
Qualify: FROM a JOIN b ON a.id = b.customer_id
Spark / Glue
A join is the main shuffle bottleneck
Broadcast the small side (/*+ BROADCAST(small) */ or broadcast()); target the small table under the advisory threshold
Redshift
Join order and distribution keys decide whether the query re-distributes data
Prefer DISTKEY/SORTKEY alignment and ANALYZE before big joins
Any engine
A join with no predicate is a cross join
Always state the join keys explicitly

3.3 PIVOT, and the portable fallback

Amazon Redshift supports PIVOT, with hard limits worth memorising:

sql
-- Redshift PIVOT: only COUNT, SUM, MIN, MAX, AVG are allowed as aggregates SELECT region, pivot_alias FROM sales PIVOT (SUM(amount) FOR region IN ('EMEA', 'AMER', 'APAC')) AS p;
Redshift PIVOT rule
Consequence
Aggregates limited to COUNT, SUM, MIN, MAX, AVG
Anything else needs the portable form
No JOIN, no recursive CTE, no SUPER UNNEST in the input
Pre-stage those in a CTE
The IN list takes literals
Dynamic pivot needs dynamic SQL or the portable form

The portable, engine-independent form; which is the answer whenever a question asks for a pivot that PIVOT cannot express:

sql
SELECT order_date, SUM(CASE WHEN region = 'EMEA' THEN amount ELSE 0 END) AS emea, SUM(CASE WHEN region = 'AMER' THEN amount ELSE 0 END) AS amer, SUM(CASE WHEN region = 'APAC' THEN amount ELSE 0 END) AS apac FROM sales GROUP BY order_date;

3.4 JSON parsing: two engines, two families

Amazon Athena (Presto/Trino dialect) separates extracting a JSON value from extracting a scalar:

sql
-- Athena: json_extract returns JSON (quoted); json_extract_scalar returns text SELECT json_extract_scalar(payload, '$.customer.id') AS customer_id, json_extract_scalar(payload, '$.items[0].sku') AS first_sku FROM events WHERE dt = '2026-10-09'; -- Nested arrays are flattened, then crossed with the parent row SELECT e.order_id, i.sku, i.qty FROM events e CROSS JOIN UNNEST(e.items) AS i (sku, qty);

Amazon Redshift parses JSON once into the SUPER type and then queries it with PartiQL-style paths: JSON_PARSE(payload) β†’ SUPER, dot and bracket access, UNNEST, UNPIVOT. AWS is explicit that JSON is not a good fit for large datasets because it is not columnar; parse it into SUPER once, or land it as Parquet/SUPER columns (Redshift JSON and SUPER documentation, accessed Oct 2026).

πŸ“ŒImportant

Athena has no OFFSET-free deep paging story. ORDER BY … LIMIT … OFFSET is supported, but ORDER BY + LIMIT tops out at 2,147,483,647 rows, and Athena bills bytes scanned, so deep OFFSET paging both hits a ceiling and re-reads the same bytes. The exam-friendly answer is keyset pagination (WHERE id >:last_id ORDER BY id LIMIT n) over partitioned Parquet, not big offsets over CSV.

3.5 Bulk load: one COPY, not a thousand INSERTs

Amazon Redshift's own guidance is unambiguous: use COPY for large loads: individual INSERTs "might be prohibitively slow". Sizing rules: one COPY per table/prefix (concurrent COPYs to the same table serialize), non-splittable gzip/JSON files between 1 MB and 1 GB, count β‰ˆ number of slices, and CSV/Parquet files β‰₯128 MB are auto-split (Redshift COPY best practices, accessed Oct 2026).

Worked example E3: COPY sizing

plaintext
Source: 640 GB of gzip CSV Cluster: 128 slices Target = a multiple of 128 files, each between 1 MB and 1 GB 128 x 5 GB -> over 1 GB, split it 1,024 x 640 MB -> in range, 8 files per slice -> good Then ONE COPY for the whole prefix: concurrent COPYs to the same table serialize, so parallelism comes from FILE_COUNT inside one COPY.

4. AWS Lambda for data tasks

4.1 Two ways an event arrives: push (trigger) versus pull (event source mapping)

This distinction is the spine of every Lambda data question. A trigger means the service pushes the event to your function; an event source mapping (ESM) means Lambda polls the stream or queue on your behalf and batches records for you (AWS Lambda event-source-mapping documentation, accessed Oct 2026).

100%
Lever
Values (as of Oct 2026)
What it trades
BatchSize (Kinesis)
default 100, max 10,000
Throughput per invocation vs latency
Batching window
0 s (Kinesis/DDB/SQS), 500 ms (MSK/Kafka/MQ), max 300 s
Latency vs batching efficiency
ParallelizationFactor
1 to 10
More concurrency per shard vs ordering pressure
MaximumRetryAttempts
βˆ’1 (infinite) to 10,000
Retention of poison records vs stall time
MaximumRecordAgeInSeconds
βˆ’1 to 604,800 (7 days)
How long a shard keeps trying
ReportBatchItemFailures
on/off
Partial-batch success vs whole-batch retry
⚠️Warning

⚠️ The ESM failure mode is the whole batch and the whole shard. On failure, Lambda reprocesses the entire batch and pauses the shard; which is why the first symptom of one bad record is a climbing IteratorAge, not an error rate. Defaults are infinite, so a single poison record can block a DynamoDB stream for up to one day and a Kinesis shard for up to one week (AWS Lambda DynamoDB/Kinesis error documentation, accessed Oct 2026).

4.2 Concurrency: reserved versus provisioned

AWS gives every account a pool of 1,000 concurrent executions per Region, shared across all functions, with an unreserved floor of 100, and a sustainable ingestion rate of roughly 10 requests/second per unit of concurrency (as of Oct 2026).

Unreserved
Reserved
Provisioned
What it is
Whatever is left of the 1,000 pool
A hard minimum and maximum for one function
Pre-warmed execution environments
Price
:
Free
Charged (per GB-s and per hour, 1 ms billing)
Applies to
:
Any function
Function versions and aliases only
Canonical use
Best-effort traffic
Guarantee a pipeline's capacity, or cap a function so a small database pool is not overwhelmed
Interactive latency: shaving cold starts

Sizing arithmetic: required concurrency β‰ˆ requests/second Γ— duration in seconds, and AWS's provisioning guidance adds ~10% headroom. Provisioned concurrency is not the answer to "my async pipeline should start quickly": AWS states async pipelines usually do not need it (AWS Lambda concurrency documentation, accessed Oct 2026).

Worked example E4: concurrency sizing

plaintext
Peak: 200 concurrent invocations, average duration 250 ms Warm environments for provisioned concurrency: 200 x 0.250 s = 50 concurrent executions +10% headroom -> provision 55 Account pool is 1,000 per Region: reserved for this function <= 1,000 - 100 (unreserved floor) = 900 provisioned concurrency must sit inside the reserved envelope Requests per second ceiling at 200 concurrency: 10 x 200 = 2,000 requests/second (as of Oct 2026)

4.3 Timeout and the memory β†’ CPU coupling

There is no vCPU knob. CPU, network and GPU scale with the memory setting, which is the single most testable Lambda performance fact:

Setting
Value (as of Oct 2026)
Timeout
1 to 900 s, console default 3 s, hard ceiling 900 s (15 min)
Memory
128 to 10,240 MB in 1 MB increments
1,769 MB
β‰ˆ 1 vCPU
10,240 MB
β‰ˆ 6 vCPUs
/tmp
512 MB free, then billed, up to 10,240 MB
Environment variables
4 KB aggregate
Deployment package
50 MB zipped / 250 MB unzipped, including up to 5 layers
Container image
10 GB
Code storage
300 GB unzipped, not increasable

Worked example E5: memory, CPU and unit cost

plaintext
Job needs about 1 vCPU. Current setting 1,024 MB (~0.6 vCPU) is too slow. Fix: raise memory to 1,769 MB - there is no separate CPU setting. Unit cost of ONE invocation at 1,769 MB x 500 ms: 1.769 GB x 0.5 s = 0.8845 GB-s 0.8845 x $0.0000166667 (x86 tier 1) = $0.0000147 per invocation Monthly at 10 million invocations: duration 10,000,000 x 0.8845 GB-s = 8,845,000 GB-s less free 400,000 GB-s = 8,445,000 billable GB-s 8,445,000 x $0.0000166667 = $140.75 requests 10,000,000 - 1,000,000 free = 9,000,000 9,000,000 / 1,000,000 x $0.20 = $1.80 total ~ $142.55 / month (prices as of Oct 2026 - verify current pricing before use)

4.4 Layers, environment variables and /tmp

  • Layers are the object-oriented reuse story (Section 1.5): ≀5, zip-only, mounted at /opt. A container-image function cannot use layers.
  • Environment variables are capped at 4 KB in aggregate and are readable through the API, so they are configuration, not a secret store. Secrets belong in AWS Secrets Manager or Systems Manager Parameter Store, encrypted with KMS.
  • /tmp is transient and per-execution-environment: it survives warm invocations of the same environment and disappears when the environment is recycled. It is a cache, never durable state, and the first 512 MB is free (as of Oct 2026).
  • Mounting storage: Task 1.4 explicitly names mounting storage volumes in Lambda: Amazon EFS is the durable, multi-environment answer; /tmp is the ephemeral one.

Worked example E6: is Lambda the right runtime at all?

plaintext
Requirement: a nightly Python merge of three CSV files, single node, 45 minutes, no distributed joins. Lambda : hard 900 s ceiling -> impossible at any price Glue Spark : 2-3 min startup + 2 DPU min -> a cluster for a loop Glue Python shell: 0.0625 or 1 DPU, timeout far beyond 15 min -> THE ANSWER Selection logic: "single node + longer than 15 min = Python shell", regardless of the exact byte count (the ~10 GB figure is the working- set guidance that tells you when the shell stops being enough).

5. The Lambda vs Glue vs Batch decision matrix

5.1 The matrix

This table is the highest-yield single artefact in Task 1.4. Every row is a sourced limit as of Oct 2026:

Signal in the stem
Pick
Why, with the limit
Event transform, seconds to minutes, GB-scale
AWS Lambda
900 s ceiling, 10,240 MB memory, billed per request + GB-s, no idle capacity
Single-node pandas, longer than 15 min or around 10 GB
AWS Glue Python shell
0.0625 / 1 DPU, about 10 GB, timeout far longer than Lambda's hard 15 minutes
Distributed ETL, joins, GB: TB, needs the Data Catalog
AWS Glue Spark (ETL)
2 to 100 DPU (default 10), 1 DPU = 4 vCPU + 16 GB; expect ~2 to 3 min startup
Container image, hours, queue + Spot capacity
AWS Batch
Docker job on a queue and compute environment; scheduling overhead means binpack jobs to 3 to 5 minutes or more
Existing Spark/Hadoop estate, petabyte scale
Amazon EMR / EMR Serverless
You bring the framework version and cluster config
SQL-only transformation already in the warehouse
Amazon Redshift ELT
Window functions, PIVOT, SUPER and COPY stay inside the engine
100%

5.2 Worked example E7: the same job on two runtimes

plaintext
Job: 1.44 million invocations per month, 300 ms each, 512 MB Lambda: GB-s 1,440,000 x 0.512 GB x 0.3 s = 221,184 GB-s free tier 400,000 GB-s -> duration cost $0 requests (1,440,000 - 1,000,000) / 1,000,000 x $0.20 = $0.088 total ~ $0.09 per month Same work as a Glue Spark job: 10 minutes a day on 4 DPU 4 DPU x (10/60) h = 0.667 DPU-h per day 0.667 x 30 days = 20 DPU-h per month 20 x $0.44/DPU-hour = $8.80 per month (Glue ETL $0.44 per DPU-hour as of Oct 2026 - verify current pricing before use; Lambda prices as of Oct 2026) Delta: about 98x cheaper on Lambda - because the work is INVOCATION-SHAPED, not JOB-SHAPED.

The lesson is not "Lambda is always cheaper"; it is that pricing follows the shape of the work. A 40-minute nightly merge cannot run on Lambda at any price (the 900 s ceiling), and a 2-second job cannot justify AWS Batch, where scheduling overhead can exceed the runtime.

  • πŸ“š Did you know? AWS's own Glue guidance puts the startup cost in writing: Glue Spark jobs commonly spend 2 to 3 minutes before the first task runs, which is more runtime than the job itself for sub-100 MB payloads. That is why the exam's "small, frequent, event-driven" stems point at Lambda and its "big, scheduled, join-heavy" stems point at Glue, and why a stem that says "frequent, small files arriving every few seconds" is usually telling you to reject both Glue and Batch (AWS Glue and AWS Batch best-practice documentation, accessed Oct 2026).

6. Idempotency, watermarks and poison pills

6.1 At-least-once delivery, exactly-once effect

Streams and queues, Kinesis, SQS, DynamoDB Streams, MSK, deliver at-least-once by default. There is no end-to-end exactly-once transport. What pipelines build instead is an exactly-once effect: at-least-once delivery + an idempotent side effect (AWS Big Data Blog, Build a fault-tolerant serverless data aggregation pipeline with exactly-once processing, 2021-11-01).

Toolbox
Mechanism
When
Deterministic keys
Hash or business key, never a fresh UUID
Any dedupe or upsert
Partition overwrite
INSERT OVERWRITE PARTITION (dt=…)
Whole-day batch re-runs
MERGE / upsert
MERGE INTO … ON key
CDC and late-arriving changes
INSERT … ON CONFLICT DO NOTHING
Database-enforced uniqueness
Append-only event tables
Conditional writes
attribute_not_exists(key) / ConditionExpression
DynamoDB, version guards
Hash-of-batch key
SHA-256 over the batch as the idempotency key
Batch-level dedupe
ClientRequestToken
Client-supplied token, valid ≀10 min
Repeat-safe API calls
Run ledger
A pipeline_runs table checked before work starts
Whole-pipeline idempotency
python
# Idempotent sink - illustrative shape import hashlib def batch_key(records): """Deterministic: the SAME batch always produces the SAME key.""" ids = sorted(r["order_id"] for r in records) return hashlib.sha256("|".join(ids).encode()).hexdigest() def sink(records, dt): # 1. Guard: has this exact batch already been committed? if runs.exists(batch_key(records), dt): return "skipped" # replay is a no-op, not a duplicate # 2. Write deterministically: overwrite the partition, or upsert by key. # NEVER append with uuid4() keys - that is the classic duplicate bug. write_partition_overwrite(records, dt=dt) runs.mark_complete(batch_key(records), dt) return "written"
⚠️Warning

⚠️ A fresh UUID per row destroys idempotency. If each replay generates new keys, every retry looks like new data, and duplicates accumulate silently. The rule is: deterministic business key + partition overwrite / MERGE / conditional write. Assume every foreachBatch, Lambda retry and DLQ redrive runs at least twice.

6.2 Watermarking: how long you wait for late data

A watermark is max event time βˆ’ allowed lateness. It bounds how long a streaming job waits for late records before it closes a window and releases the state behind it. Move it forward only: too generous and state grows without bound; too tight and late records are dropped silently (AWS and community streaming guidance, accessed Oct 2026).

In an incremental Glue job the same idea appears as a stored high-water mark plus a sliding safe window:

plaintext
Stored high-water mark : last_event_ts = 2026-02-01 Allowed lateness : 2 days Safe-window re-read : WHERE event_ts > 2026-01-30 (2-day overlap) Dedupe : dropDuplicates(["order_id"]) (or MERGE on the key) Advance the mark : last_event_ts = max(old, new) - forward only Residual risk : records later than the window -> periodic batch reconciliation, not a bigger watermark
100%

Worked example E8: the safe window

plaintext
Watermark at 2026-02-01, allowed lateness 2 days. Record arrives on 2026-02-02 with event_ts 2026-02-01 23:40 -> inside the watermark: ACCEPTED, because the safe window re-read covers > 2026-01-30 and dropDuplicates(MERGE) removes anything already written. Record arrives on 2026-02-09 with event_ts 2026-02-08 -> BEHIND the watermark: silently dropped by a streaming source; recovered only by the scheduled batch reconciliation pass. Cost of widening the window to 7 days: every run re-reads 7 days of data and holds 7 days of dedupe state - a latency/storage trade, never a free win.
Fill in the Blanks
How to answer

Fill-in-the-blank exercise with 5 blanks. Type the missing words, then check your answer.

Complete the reliability statements using the exact vocabulary the exam uses:

Streams and queues deliver blank by default, and an blank effect is produced only by combining that delivery with an blank write. A blank equals max event time minus allowed lateness and may only move forward, while a record that always throws is a blank that pauses the shard until retries and record age are bounded.

Word Bank, drag words into the blanks above:

at-least-once
exactly-once
idempotent
watermark
poison pill
at-most-once
effectively-once
nondeterministic
event-time skew
dead-letter record
batching window
shard lease
checkpoint offset

6.3 Poison-pill handling: the ladder

A poison pill is a record that always throws. Because an ESM retries the whole batch and pauses the shard, one bad record can stop an entire stream, and the defaults are infinite:

100%
Rung
Lever
Effect (as of Oct 2026)
1
ReportBatchItemFailures
Report per-record failure; the good records are not retried
2
BisectBatchOnFunctionError
Repeatedly halve the failing batch to isolate the offender
3
MaximumRetryAttempts (βˆ’1…10,000) + MaximumRecordAgeInSeconds (βˆ’1…604,800)
Turns infinite retry into a bounded stall
4
OnFailure destination (SQS/SNS/S3/Kafka) or a DLQ
Park the bad records for inspection and replay
SQS variant
maxReceiveCount β†’ DLQ
The SQS-native version of rung 4
Pre-filter
FilterCriteria
Drop non-matching events before they reach the function
Ordering Question
How to answer

Ordering exercise with 5 items. Move them into the correct order, then check your answer.

Put the poison-pill handling ladder in the order a data engineer applies it, from the first lever that costs nothing to the last line of defence:

ReportBatchItemFailures: succeed the batch and report only the record that failed, so good records are not retried
BisectBatchOnFunctionError: halve the failing batch repeatedly until the offending record is isolated
Bound MaximumRetryAttempts (0 to 10,000) and MaximumRecordAgeInSeconds (up to 604,800 s) so infinite retry becomes a bounded stall
OnFailure destination or an SQS dead-letter queue: park the bad records for inspection instead of blocking the shard
Deliberate replay from the DLQ: safe ONLY because the sink was made idempotent in Section 6.1

Worked example E9: from a one-week stall to a one-hour stall

plaintext
BEFORE (defaults): one bad Kinesis record retries = infinite record age = infinite result = shard blocked for UP TO ONE WEEK (DynamoDB Streams: up to ONE DAY) AFTER (configured): MaximumRetryAttempts = 5 MaximumRecordAgeInSeconds = 3,600 BisectBatchOnFunctionError = true OnFailure -> SQS DLQ result: about 1 hour of bounded retry, 5 attempts, the bad records parked in an SQS queue, shard released. Replay from the DLQ is safe ONLY because Section 6.1 made the sink idempotent - otherwise replaying is how you double the table.
  • πŸ“š Did you know? The three retry owners on this exam are different, and questions routinely mix them up: for synchronous invocations the caller retries; for asynchronous invocations Lambda retries the function twice (at 1 and 2 minutes) before an on-failure destination; and for event source mappings the mapping itself retries the batch and pauses the shard. The same 429 in a stem therefore has three different answers depending on how the function was invoked (AWS Lambda asynchronous and ESM documentation, accessed Oct 2026).

7. Nested data: flattening, and schema-on-read versus schema-on-write

7.1 Two schema philosophies

Schema-on-write
Schema-on-read
When the schema is applied
At load, define and enforce before data lands
At query time, land raw, apply the schema when you read
Typical service shape
Amazon Redshift table definitions, constraints
Amazon S3 + AWS Glue Data Catalog + Amazon Athena
Query performance
Faster, types and layouts are already fixed
Slower per query, parsing and coercion happen at read
Cost of a mistake
Reject the load, fix upstream
Drift surfaces as consumer errors
Flexibility
Low, contract up front
High, new consumers, new shapes
Exam answer
Warehouses and curated serving layers
Data lakes and raw/bronze zones
πŸ“ŒImportant

Schema-on-read does not mean "no schema work". AWS's own architecture guidance is to keep a raw layer stored as-is and put a standardized layer with schema validation and evolution control above it: a contract layer. Without it, schema drift in the raw zone becomes a consumer-side outage, which is the failure mode a question will describe when it wants this answer (AWS Prescriptive Guidance, Modern data architecture, accessed Oct 2026).

7.2 Flattening nested data, per engine

Engine
Mechanism
AWS Glue
UnnestFrame / Relationalize on a DynamicFrame
Amazon Athena
flatten() for arrays Β· CROSS JOIN UNNEST … WITH ORDINALITY for struct/array expansion
Amazon Redshift
SUPER type with UNNEST / UNPIVOT and PartiQL paths
Apache Spark
explode() / explode_outer() to turn an array column into rows
sql
-- Athena: one row in, one row per array element out SELECT e.order_id, i.sku, i.qty, i.price FROM orders e CROSS JOIN UNNEST(e.items) AS i (sku, qty, price); -- A NULL array yields NO rows with CROSS JOIN UNNEST; -- use LEFT JOIN UNNEST (or explode_outer) when you must keep the parent.

Two consequences worth memorising: a NULL array produces no rows under a cross-unnest (silently shrinking your result), and an explicit schema at read time beats schema inference: inference is convenient once and unpredictable forever.

7.3 Format follows the access pattern, and format is cost

plaintext
raw landing : JSON or CSV, stored as-is (schema-on-read, cheap to write) | v AWS Glue ETL or Athena CTAS curated : Snappy-Parquet, Hive layout year=/month=/day= | v serving : Athena / Redshift Spectrum / QuickSight - column pruning skips untouched columns - min/max statistics prune whole row groups - Athena bills BYTES SCANNED, so pruning IS the bill

Worked example E10: the format decision, priced

plaintext
Scenario: 1 TB table, a query touching 3 of 30 columns. Athena list price: $5.00 per TB scanned (as of Oct 2026 - verify current pricing before use), 10 MB minimum per query. CSV, no pruning : scans ~1 TB -> about $5.00 per run Parquet, columnar : scans only the 3 needed columns plus metadata -> typically an order of magnitude less Ten runs a day x 30 days: CSV 300 x $5.00 = $1,500 / month Parquet 300 x ~$0.50 = ~$150 / month (illustrative) The conversion (CTAS or a Glue job) is a ONE-TIME cost against a RECURRING saving - which is why "convert to Parquet" is almost always the right answer in an Athena cost stem.
  • πŸ“š Did you know? The lineage question loves the four-format contrast because it is purely shape: CSV has no schema and no pruning, JSON nests but must be parsed whole, Avro carries its schema in the header and is splittable, which is exactly why streaming and schema-evolution jobs prefer it, and Parquet/ORC are columnar, which is why analytics engines do. The one-line memory is Avro/JSON to move it, Parquet/ORC to read it (AWS Glue and Athena format guidance, accessed Oct 2026).

2026 Updates (as of October 2026)

ℹ️Note

What moved for programming and data operations between 2024 and October 2026: every line is checked against an AWS primary source in October 2026, and each is examinable because the exam tests current behaviour:

  • The exam guide itself was rebuilt: v1.1 (2025-12-12) consolidated the separate "Knowledge of" and "Skills in" lists into one skills list per task, adding 8 skills and removing none; Cloud9, CodeCommit and AWS Schema Conversion Tool left the in-scope list, so Git questions stay generic (clone, commit, branch, merge) and never name a specific AWS Git service: DEA-C01 exam guide revisions and in-scope pages, accessed Oct 2026.
  • Glue runtime generations advanced: Glue 5.1 became the default for new jobs on 2025-11-26 (Spark 3.5.6), and Glue 6.0 went GA on 2026-08-21 with Spark 4.1.1, Python 3.13, Scala 2.13 and a 30% price reduction; Glue 0.9/1.0/2.0 reached end of life on 2026-04-01, and Python Shell 3.6 can no longer be created after 2026-03-31: AWS Glue release notes, version support policy and What's New, accessed Oct 2026. A stem pinning an EOL Glue version is a wrong answer.
  • Glue 6.0 removes two compatibility paths: EMRFS is gone (S3A is the only S3 filesystem) and the AWS SDK for Java v1 is removed: AWS Glue Migrating to version 6.0, accessed Oct 2026. Any option relying on fs.s3.consistent.* or com.amazonaws.* imports is stale.
  • Redshift Python UDFs are being retired: no new scalar Python UDFs after 2025-10-30 and they are unsupported after 2026-06-30: Amazon Redshift behaviour-changes page, accessed Oct 2026. This directly reshapes the UDF ladder of Section 2.4: on Redshift the exam-friendly answer moves to SQL, stored procedures and ML, not to new Python UDFs.
  • Athena's cost floor dropped: managed query results (2025-06-03) are encrypted, service-managed and cost nothing extra: no result bucket required, and Capacity Reservations now start at 4 DPU for 1 minute (was 24 DPU / 60 min) as of 2026-02-11: AWS What's New and Athena capacity-control blog, accessed Oct 2026.
  • Kinesis Data Streams now has three capacity modes: Provisioned, On-demand Standard and On-demand Advantage (2025-11-04), which removes the per-stream hourly charge but applies an account-wide 25 MB/s ingest + 25 MB/s retrieval floor; from 2026-08-28 a stream can be materialised directly into Iceberg on S3 Tables: AWS What's New, accessed Oct 2026. "Kinesis has two capacity modes" is now a stale statement.
  • EMR Serverless workers grew: up to 32 vCPU / 244 GB per worker as of 2026-07-07, with Spark-job local storage 200 GiB β†’ 1 TiB: AWS What's New, accessed Oct 2026.
  • Stale-fact warning, service-name fossils: the guide's in-scope list still prints "Amazon Kinesis Data Firehose", while the product has been Amazon Data Firehose since 2024-02-09 with no API, endpoint, CLI, policy or metric change. Either name is valid, never "correct" an option that uses the old one (AWS What's New, 2024-02-09).
  • Redshift now writes to the lake, not only reads it: Iceberg writes went GA on 2025-11-17 (CREATE/SHOW/DROP plus append-only INSERT, catalogued in the Glue Data Catalog), UPDATE / DELETE / MERGE followed on 2026-04-23 (including Amazon S3 Tables and Lake Formation permissions), and Iceberg materialized views arrived 2026-10-05, and the permissions tightened with them: on a Lake Formation table an Iceberg DELETE needs DELETE, UPDATE/MERGE need INSERT + DELETE, and every Iceberg DML statement also needs ALTER, Amazon Redshift What's New and behaviour-changes page (Patch 202), accessed Oct 2026. Any option saying Redshift can only query Iceberg is stale, and a lab grant of INSERT alone no longer runs a MERGE.
  • Step Functions is now a first-class fan-out engine for data jobs: Distributed Map gained Athena manifests and Parquet inputs plus S3-prefix iteration (LOAD_AND_FLATTEN) on 2025-09-18, and on 2026-03-26 AWS added 28 services / 1,100+ AWS SDK integrations, taking the total to over 220 AWS services (including Bedrock AgentCore and S3 Vectors). Memorise the split limits: Distributed Map ≀ 10,000 children with an S3 source of CSV/JSON/JSONL/Parquet, versus Inline Map ≀ 40 concurrent children, JSON array only, 256 KiB trigger payload: AWS Step Functions recent-launches and Map-state documentation, accessed Oct 2026. This is the "loops β†’ Map state" row of Section 1.4 made directly testable.
  • Cross-account data sharing became a one-share operation: AWS Lake Formation cross-account sharing v5 (2026-02-11) lets a single AWS RAM share carry hundreds of thousands of tables using wildcard patterns instead of per-resource associations, composes with cross-Region resource links for Athena/EMR/Glue ETL, keeps every existing share and API working, and is opt-in: AWS What's New, accessed Oct 2026. Pair it with the tightened Redshift Iceberg permissions above: sharing got easier while write permissions got stricter.
  • πŸ“š Did you know? Nothing in the runtime ranking changed across 2024 to 2026 even though almost every number around it did. Lambda still stops at 900 s with CPU tied to memory, Glue Python shell still runs single-node at 0.0625 or 1 DPU far beyond 15 minutes, and Glue Spark still starts at 2 DPU with ~2 to 3 minutes of startup; while what moved is everything around those ceilings (Glue 6.0 on 2026-08-21, EMR Serverless workers up to 32 vCPU / 244 GB, Athena Capacity Reservations down to 4 DPU for 1 minute). That is why the Section 5 decision matrix survives release-note churn: it is keyed on the shape of the work: duration, size, frequency, distribution: not on a version number (AWS What's New and service documentation, accessed Oct 2026).

Real-World Case Studies

AWS publishes what these abstractions look like in production. Every figure below is customer- or AWS-claimed and unaudited, with the source named so you can check it: the examinable point is the pattern (which programming decision was applied, which service did the work, which number moved), not the marketing.

Case A, EMX: a programmatic exchange rebuilt on S3 and Athena

Element
Detail
Customer
EMX, a programmatic media/advertising exchange handling more than 2 TB of raw data per hour
Challenge
Backend ETL and storage costs that grew linearly with ingest, on a workload that was mostly ad-hoc querying
Services
Amazon S3 as the store plus Amazon Athena as the query layer: no warehouse cluster in the serving path
Outcomes
85% saved on data storage costs; queries reported as "at least four times cheaper than other backend ETL tools" (customer claim, accessed Oct 2026)
Exam domain
Domain 1 Task 1.4 (choose the right runtime and format) with Domain 3 cost hooks
Source
AWS Big Data Blog, How EMX reduced data pipeline costs by 85% with Amazon Athena, 2021-02-02 (accessed Oct 2026)

Read EMX as Section 7.3 in production: the saving came from where the compute happens and how much of the file gets read, not from a discount. Athena bills bytes scanned, so landing columnar files and pruning them with partitions is a programming decision: the same decision as choosing a window function over a Python loop.

Case B, AppsFlyer: an interactive workload moved off HBase onto Athena

Element
Detail
Customer
AppsFlyer, mobile attribution and analytics platform
Challenge
An interactive workload running on Apache HBase whose cost and operational burden were outgrowing its value
Services
Amazon Athena over data in Amazon S3, replacing the HBase serving path for that workload
Outcomes
80% reduction in monthly cost for the interactive workload (customer claim, accessed Oct 2026)
Exam domain
Domain 1 Task 1.4 (runtime selection and query optimisation) with Domain 3 cost hooks
Source
AWS Big Data Blog, How AppsFlyer modernized their interactive workload by moving to Amazon Athena and saved 80% of costs, 2024-08-08 (accessed Oct 2026)

The examinable point is the selection logic, not the percentage: a low-latency, key-oriented store (HBase) was the wrong shape for an analytical, scan-and-filter workload, and the answer was to move the read path to a columnar lake plus a serverless SQL engine. That is the same fork as Section 5, pick the runtime from the shape of the work, and it is why "we already have a database, so query it" is a distractor in a Domain 1 cost stem.

Case C, Hearst: a 30 TB/day clickstream pipeline built on Kinesis

Element
Detail
Customer
Hearst, a media group spanning 250+ websites, 15 daily and 36 weekly newspapers, 300+ magazines and 31 television stations
Challenge
Clickstream from every property had to be collected continuously; the customer states that building the pipeline without Amazon Kinesis "would have involved many weeks of engineering"
Services
Amazon Kinesis Data Streams β†’ Amazon Data Firehose (then Kinesis Data Firehose) β†’ Spark streaming into Amazon S3 for BI
Outcomes
The pipeline ingests 30 TB of data per day; the customer credits the two services with making "the entire process extremely simple and reliable" (customer claim, accessed Oct 2026)
Exam domain
Domain 1 Task 1.4 (event sources, streaming versus batch, serialization) with Domain 2 ingestion
Source
AWS Kinesis Data Streams product/customer page, aws.amazon.com/kinesis/data-streams, accessed Oct 2026

Read Hearst as Section 4.1 in production. Choosing a stream over a nightly batch is a programming decision about event shape and latency, and Firehose's managed buffering and delivery is precisely what removes the "weeks of engineering" the customer describes. When a stem says "frequent, small files arriving every few seconds", Hearst is the shape it is describing, and the answer is a stream plus a Lambda or Kinesis consumer (an event source mapping that polls and batches), never a scheduled Glue job whose 2 to 3 minute startup dwarfs the work.

Case D, Nasdaq: an exchange decouples its write path from its read path

Element
Detail
Customer
Nasdaq, a national securities exchange
Challenge
Orders, quotes and trades were loaded in an overnight batch window from a legacy on-premises warehouse, and loading contended with querying for the same capacity
Services
Amazon S3 data lake as the write path (plus Amazon S3 Glacier archive and S3 Object Lock), with Amazon Redshift and Redshift Spectrum as the read path
Outcomes
Daily volume moved from 30 billion to 70 billion records a day (peak 113 billion, February 2020); 90% of the load completed 5 hours sooner; queries ran 32% faster; a 15 TB slice of the lake is queried in place (customer claim, accessed Oct 2026)
Exam domain
Domain 1 Task 1.4 (partitioning, format, decoupled storage and compute) with Domain 2 lake design
Source
AWS case study Nasdaq, aws.amazon.com/solutions/case-studies/nasdaq-case-study/, accessed Oct 2026

The engineering quote behind the case is about contention: with S3 holding the bytes and Redshift holding the compute, "data loading and querying" stop competing for the same resources. That is the Section 2.3 partition lesson at scale: the lake is written in a layout the reader can prune, and the query engine scales independently of the ingest window. Note also what the case is not: it is not a claim that Redshift was replaced. Spectrum keeps the warehouse as the SQL engine while S3 becomes the storage tier, which is exactly the lake house pattern the exam tests.

Case E, PayU: consolidation, data sharing, then demand rationalisation

Element
Detail
Customer
PayU, a fintech/payments business
Challenge
Slow queries and data siloed across roughly 40 production databases, with data freshness limited to once a day
Services
AWS Glue ETL β†’ Amazon S3 β†’ Amazon Redshift, plus Redshift Data Sharing across 5 clusters (2 ETL, 3 consumer)
Outcomes
Queries went from 10 to 15 minutes to under 1 minute; freshness went from once a day to under 30 minutes; about $20,000 per month saved while scanning roughly 200 TB per day; monthly query volume fell from 150,000 to 35,000 (βˆ’77%) in a single month (customer claim, accessed Oct 2026)
Exam domain
Domain 1 Task 1.4 (query rationalisation) with Domain 3 cost optimisation
Source
AWS case study PayU, aws.amazon.com/solutions/case-studies/payu-redshift-case-study/, accessed Oct 2026

Two levers must be kept apart here, and the exam will try to blur them. The platform move (40 databases β†’ one warehouse plus S3, with Data Sharing isolating consumers without copying data) bought freshness and the $20,000/month saving; the demand move (150,000 β†’ 35,000 queries per month) is fewer queries, not cheaper queries. AWS quotes the customer: "In 1 month, we cut down queries by 77 percent, which would have been a 6-month exercise in the previous environment." The lesson for Task 1.4: rationalise the workload before you buy capacity: a rewritten CTE or a cached aggregate removes cost permanently, whereas right-sizing only moves the bill.

Case
Programming decision it demonstrates
Domain
EMX
Format and query engine are the cost: columnar + serverless SQL
D1 Task 1.4
AppsFlyer
Runtime selection follows the shape of the workload
D1 Task 1.4
Hearst
Event shape decides the runtime: a stream plus managed delivery beats a nightly batch at 30 TB/day
D1 Task 1.4
Nasdaq
Decouple the write path from the read path so load and query stop contending
D1 Task 1.4
PayU
Rationalise demand (150k β†’ 35k queries/month) before buying capacity; Data Sharing isolates consumers without copies
D1 Task 1.4
  • πŸ“š Did you know? Neither AWS case study names a single function, class or library: AWS publishes the outcomes and leaves the code to you. That mirrors the exam's own boundary: DEA-C01 asks you to recognise that 85% storage savings on an ad-hoc query workload implies a columnar format plus a bytes-scanned engine, and that 80% off an interactive workload implies a runtime re-selection, never to recall a customer's code (AWS Big Data Blog case studies, accessed Oct 2026).

  • πŸ“š Did you know? Amazon Kinesis Data Streams launched in 2013 and AWS calls it "the first cloud-native serverless streaming data service", which is why every customer story written before 2024 uses old service names. Kinesis Data Analytics became the Amazon Managed Service for Apache Flink on 2023-08-30, and Kinesis Data Firehose became Amazon Data Firehose on 2024-02-09 with no API change, the IAM action is still firehose:*, and the DEA in-scope list still prints the old Firehose name. Older case studies (Hearst, AGCO, Baqend) therefore read like a different stack from current documentation: map the names, never treat them as two services (AWS Kinesis retrospective, AWS What's New and DEA-C01 in-scope services page, accessed Oct 2026).


Practice Questions

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

Which of the following is OUT OF SCOPE for DEA-C01 Task 1.4?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Glue Spark job reads 120 GB of Parquet on a 32-core cluster and writes tiny files. Using AWS's guidance of 2-3 partitions per core, what is the target partition count and the resulting size per partition?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

You must normalise a string column across one billion rows in a Glue Spark job and no built-in function does the job. Which rung of the UDF cost ladder should you choose, and why?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Redshift query computes ROW_NUMBER() OVER (ORDER BY event_ts DESC) with no PARTITION BY over a large table and runs slowly. What is the correct diagnosis?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

You need to pivot Redshift sales data by quarter using MEDIAN as the aggregate. Which statement is correct?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

In Amazon Athena you need the plain text value of a nested JSON field to use in a WHERE clause. Which expression do you use?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Lambda function that parses records is CPU-bound at 1,024 MB. The team proposes setting a vCPU count. What is the correct action?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A scheduled Python job merges three CSV files on a single node and routinely runs 45 minutes. Which service best fits?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A data pipeline's Lambda function must never exceed 50 concurrent executions, because the backing relational database only tolerates 50 connections. Which configuration is correct?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A nightly job appends rows to a table using uuid4() as the primary key. After a retry, the table contains duplicates. What is the correct fix?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Kinesis event source mapping's IteratorAge is climbing steadily, the producer is healthy, and a single malformed record sits in the shard. Which set of actions addresses the root cause?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A data lake stores raw JSON on S3 with schema applied at query time. After a producer adds a required field, several downstream Athena queries fail. What does AWS guidance recommend?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Redshift team replays CDC into an Apache Iceberg table that lives on Amazon S3 Tables and is registered with AWS Lake Formation. the replay runs MERGE INTO ... ON key. Which permission set does current Redshift behaviour require?

Practice Question
How to answer

Multiple-choice question with 4 options. Select one option, then choose Check Answer.

A Step Functions state machine must fan out one child per Parquet file across roughly 6,000 objects under an S3 prefix, each child running a short Glue job. Which construct fits, and what is its limit?

⚠️Warning

⚠️ Exam-day traps for this lesson:

  • Language-agnostic β‰  no code; you must read handlers, SQL and pipelines; you must never memorise library syntax. An option that turns on an argument order, a decorator or an import path is testing the out-of-scope clause.
  • There is no vCPU knob on Lambda: CPU follows memory: 1,769 MB β‰ˆ 1 vCPU, 10,240 MB β‰ˆ 6 vCPUs; timeout is 1 to 900 s (default 3 s).
  • Reserved concurrency β‰  provisioned concurrency: reserved is free and is a hard min and max (cap or guarantee); provisioned is charged, applies to versions/aliases, and buys warmth for interactive latency.
  • 1,000 is the account pool, not your function's limit; it is shared per Region, with an unreserved floor of 100, and throughput runs about 10 requests/s per unit of concurrency.
  • Layers are zip-only and capped at 5: a container-image function cannot use them; /opt is the mount point.
  • 4 KB of environment variables is not a secret store: use Secrets Manager or SSM Parameter Store with KMS.
  • At-least-once is the default; exactly-once is an effect built from at-least-once plus an idempotent write: never claim a stream is exactly-once.
  • A fresh UUID per row destroys idempotency: deterministic business key + partition overwrite / MERGE / conditional write.
  • An ESM failure retries the whole batch and pauses the shard, and the defaults are infinite β†’ 1 day (DynamoDB Streams) / 1 week (Kinesis) blocked.
  • 1 partition = 1 task: target 2 to 3Γ— cores, coalesce down, repartition up, and gzip is unsplittable.
  • Built-in SQL beats vectorised UDF beats plain Python UDF (~5Γ—), and on Redshift, scalar Python UDFs are unsupported after 2026-06-30.
  • Pandas crossover (β‰ˆ10 M rows / 5 to 10 GB) is a 2017 third-party rule of thumb, never an AWS figure: the sourced facts are the service limits.
  • Format is cost: Athena bills bytes scanned, so Parquet/ORC pruning is a billing decision; Avro/JSON to move it, Parquet/ORC to read it.
  • Case-study numbers are unaudited customer claims: EMX's 85% and AppsFlyer's 80% are what those customers reported, never guarantees.
πŸ“ŒImportant

Comparative Verdict: how programming choices compare on exam day

  • Versus other clouds: the exam tests AWS services only: nothing in DEA-C01 compares Lambda with Azure Functions or Glue with another managed ETL, and the in-scope list is the entire universe of examinable names. Any option that pivots to a competitor's product, or to an unverified third-party benchmark (including the 2017 pandas crossover), is out of scope by construction; answer with an in-scope AWS service or an AWS-published limit.
  • Versus self-managed / on-premises: running Spark, a queue consumer or a scheduler yourself means operating the cluster, the shuffle tuning, the retry semantics and the upgrades. AWS's managed answers, Lambda (no servers, per-request billing), Glue (serverless Spark/Python shell with a Data Catalog), Batch (queue + managed compute environments), trade that control for ceilings: 900 s, 2 DPU minimum, and scheduling overhead. The exam prefers the managed option unless the requirement explicitly names a control the managed service removes.
  • Versus another AWS service: the honest boundaries are Lambda (event-shaped, ≀900 s, ≀10,240 MB, per-request + GB-s) vs Glue Python shell (single node, ~10 GB, far longer than 15 min, 0.0625/1 DPU) vs Glue Spark (distributed, 2 to 100 DPU, catalog-native, ~2 to 3 min startup) vs AWS Batch (container images, hours, queue + Spot, binpack 3 to 5 min) vs Amazon Redshift (SQL-only ELT inside the warehouse). Choose on the shape of the requirement, duration, size, frequency, distribution, never on habit.
  • Versus a manual, human process: every published customer outcome here, EMX's 85% storage saving, AppsFlyer's 80% interactive-workload reduction, is the result of patterns (columnar format plus bytes-scanned engine, runtime re-selected to fit the workload shape), not of a tool. Treat percentages as "the customer achieved", never as "AWS guarantees".
βœ…Success

Key Takeaways:

  1. Task 1.4 draws one line: language-agnostic concepts are in scope, language-specific syntax is out; you read handlers, SQL and pipelines, and you map functions/variables/conditionals/loops/OO/APIs/serialization onto pipeline constructs (handler entry point, CASE WHEN/Choice, Map, layers at /opt, pagination and 429 backoff, format choice).
  2. pandas is single-node (Glue Python shell 0.0625/1 DPU ~10 GB; Lambda ≀10,240 MB and ≀900 s); PySpark is distributed (Glue Spark 2 to 100 DPU, 1 DPU = 4 vCPU + 16 GB, ~2 to 3 min startup), and un-rewritten pandas on a Spark job leaves the minimum two workers idle and billed.
  3. Lazy evaluation: transformations build a plan, an action triggers compute, a dataset is recomputed on every action unless cached, and a shuffle is the expensive stage boundary; 1 partition = 1 task, target 2 to 3Γ— cores, default shuffle partitions 200, skew advisory 128 MB, maxResultSize 1 GB, gzip unsplittable, coalesce down / repartition up.
  4. The UDF cost ladder is built-in SQL β†’ pandas/Arrow vectorised (β‰ˆ5Γ— plain) β†’ plain Python (pickle per row) β†’ Scala/Java; use a UDF only for third-party parsers, model scoring or stateful row logic, and note Redshift scalar Python UDFs are unsupported after 2026-06-30.
  5. Window functions: latest-per-key β†’ ROW_NUMBER(); running total/delta β†’ SUM() OVER/LAG; share β†’ RATIO_TO_REPORT. In Redshift, PARTITION BY is what makes the window parallel across slices, and window functions belong only in SELECT or the final ORDER BY.
  6. SQL patterns: WITH CTEs stage dedupeβ†’filterβ†’rank; qualify same-named join columns in Athena; broadcast the small side in Spark; Redshift PIVOT allows only COUNT/SUM/MIN/MAX/AVG with literal IN lists (portable fallback: MAX(CASE WHEN …)); Athena uses json_extract_scalar + UNNEST, Redshift uses JSON_PARSE β†’ SUPER; bulk load with one COPY, never row INSERTs.
  7. Lambda event sources: triggers = the service pushes (S3, EventBridge, API Gateway, Step Functions, SNS); event source mappings = Lambda polls (Kinesis, DynamoDB Streams, SQS, MSK, Kafka, MQ, DocumentDB) with BatchSize, a 0 to 300 s window, ParallelizationFactor 1 to 10, bounded retries/age, BisectBatchOnFunctionError, OnFailure and ReportBatchItemFailures.
  8. Concurrency: account pool 1,000/Region, unreserved floor 100, ~10 rps per concurrency unit; reserved = free min+max (guarantee or throttle); provisioned = charged, version/alias only, sized as req/s Γ— duration s +10%: async pipelines usually do not need it.
  9. Lambda runtime envelope: timeout 1 to 900 s (default 3 s), memory 128 to 10,240 MB with 1,769 MB β‰ˆ 1 vCPU and 10,240 MB β‰ˆ 6, /tmp 512 MB free then billed, env vars 4 KB, package 50/250 MB with ≀5 layers at /opt (zip-only), images 10 GB, code storage 300 GB (as of Oct 2026).
  10. Decision matrix: seconds-to-minutes and event-shaped β†’ Lambda; single node >15 min or ~10 GB β†’ Glue Python shell; distributed GB: TB with catalog β†’ Glue Spark; container image, hours, queue + Spot β†’ AWS Batch (binpack 3 to 5 min); petabyte/existing Spark β†’ EMR; SQL-only β†’ Redshift.
  11. Reliability: streams are at-least-once; an exactly-once effect = at-least-once + idempotent write (deterministic keys, partition overwrite, MERGE/ON CONFLICT, conditional writes, hash-of-batch, ClientRequestToken ≀10 min, run ledger); the watermark = max event time βˆ’ allowed lateness, advanced forward only with a sliding safe window and dropDuplicates, plus batch reconciliation for the tail.
  12. Poison pills: an ESM retries the whole batch and pauses the shard; defaults are infinite β†’ blocked up to 1 day (DynamoDB Streams) / up to 1 week (Kinesis). Ladder: ReportBatchItemFailures β†’ BisectBatchOnFunctionError β†’ bound retries/age (max 10,000 / 604,800 s) β†’ OnFailure/DLQ; schema-on-read needs a standardized contract layer above raw, and Parquet/ORC for reads with Avro/JSON for movement.
Progress38%