What each AWS data service is for and where its limits are, as asked in real loops: what S3 guarantees, what a Glue job bookmark remembers, why Redshift has no partitions, how Athena bills you and what a Kinesis shard gives you.
What S3 promises and what it does not, how keys and storage classes decide cost and speed, and how Glue turns a prefix full of files into a table.
Glue jobs, bookmarks & orchestration
4
What a Glue job is configured with, what a bookmark actually stores, what a connection can reach, and what starts a job running.
Redshift & Athena
8
How Redshift places rows and skips blocks, how data gets in, how it copes with many users, and how Athena turns bytes scanned into a bill.
Streams, functions, CDC & access
4
What a Kinesis shard gives you, where Lambda stops, what DMS reads from a source database, and how a job gets permission to touch a bucket.
Evergreen · asked verbatim
5
The flat definitions interviewers open with — what Glue is, what Kinesis is, the Redshift distribution styles — each answered with the detail that separates a user from a reader.
01 / 25
Reported · 2S3 layout, partitioning & consistency
What does Amazon S3 guarantee about durability and consistency today, and why does that still not make it a file system for a Spark job's output?
Why they ask this
Half the candidates still say S3 is eventually consistent, and the other half say that since it is consistent it behaves like HDFS. Both answers lead to broken output commits.
Say this
Since December 2020 S3 gives strong read-after-write consistency: a successful PUT or DELETE is seen by the next GET or LIST. It is still an object store with no directories, no append and no atomic rename, so a job that commits output by renaming a folder is neither fast nor atomic on S3.
The reasoning
S3 Standard is designed for **99.999999999% (eleven nines) durability** by storing objects redundantly across devices in several Availability Zones. On consistency, AWS documents **strong read-after-write consistency for PUT and DELETE**: once the write returns success, any subsequent read or list sees it. The old advice to wait, or to keep a consistency layer such as EMRFS consistent view, dates from before December 2020 and is no longer needed.
What S3 does not have is file-system semantics. The namespace is flat: `s3://lake/events/dt=2026-09-14/` is a **key prefix**, not a directory. There is no append — changing an object means rewriting it. There is **no rename**: moving a "folder" is a copy of every object followed by a delete of every object, and a thousand-object move can fail halfway. There are no locks across objects, and concurrent writers to the same key are resolved by the last write.
That is what bites Spark. Hadoop's classic output committer writes to a temporary directory and renames it into place, which on HDFS is one metadata operation. On S3 it is a slow copy, and a reader listing the prefix mid-commit can see half the files. EMR and Glue ship S3-optimised committers that avoid the rename for common formats, but they still do not make a multi-file write atomic to readers.
The durable fixes are to publish differently. Either write to a staging prefix and flip a pointer that readers follow (a manifest, a `_SUCCESS` marker readers check, or a Glue table location swap), or use a table format — **Apache Iceberg, Delta Lake or Hudi** — whose commit is a single atomic metadata write, so readers see all of a write or none of it.
The answer most people give
"S3 is eventually consistent, so you sleep for a minute before reading what you wrote." That was true for some operations before December 2020 and is wrong now — and the real problem, the missing atomic rename, is left unaddressed.
They’ll ask next
Two Spark jobs overwrite the same S3 partition at the same moment. What does a reader see, and what would stop it?
How should a data lake's keys be laid out in S3 — prefixes and Hive-style partitions — given S3's request-rate limits per prefix?
Why they ask this
Layout decides three things at once: how fast writers can go, how much a query engine can skip, and how many files each query opens. Interviewers want all three, not just "partition by date".
Say this
S3 supports at least 3,500 writes and 5,500 reads per second per partitioned prefix, with no limit on the number of prefixes, so spreading load across `dt=`/`hour=` prefixes also spreads request rate. Choose partition columns that match the filters queries use, keep them low-cardinality, and keep files large.
The reasoning
AWS documents that an application can achieve **at least 3,500 PUT/COPY/POST/DELETE and 5,500 GET/HEAD requests per second per partitioned prefix**, and that there is no limit to the number of prefixes in a bucket. Scaling to a new, higher request rate is gradual, and while it happens clients can receive **503 Slow Down** errors, which the SDKs retry with backoff. The old advice to put random hashes at the front of keys dates from before AWS raised these rates in 2018; a date-partitioned layout spreads load well enough for most lakes.
Hive-style keys — `s3://lake/clean/orders/dt=2026-09-14/region=eu/part-0001.parquet` — do double duty. Each partition is its own prefix, which spreads request rate, and Athena, Glue, EMR and Redshift Spectrum all understand `key=value` paths as partition columns, so `WHERE dt = '2026-09-14'` reads one prefix instead of the table.
The failure is over-partitioning. Partitioning by `customer_id` or by minute creates millions of tiny prefixes and tiny files; every query then pays per-file open and list costs, and a crawler or `MSCK REPAIR TABLE` takes hours. Partition by the column queries actually filter on, with a cardinality in the hundreds or low thousands per year, and aim for files in the **hundreds of megabytes** in a columnar format such as Parquet with Snappy or ZSTD compression.
Split zones — raw, clean, curated — by bucket or by top-level prefix, because IAM policies, lifecycle rules and replication all attach to buckets and prefixes. Raw data you must keep for audit then gets its own lifecycle, and analysts can be granted `curated/` without ever seeing `raw/`.
Millions of prefixes and tiny files; listing and file-open overhead swamps the scan.
The answer most people give
"Put a random hash at the start of every key so S3 does not throttle." That breaks partition pruning for every query engine and solves a limit that has not bound most workloads since 2018.
They’ll ask next
A backfill writes 40,000 objects a second into one `dt=` prefix and starts getting 503s. What do you change?
What are the S3 storage classes, and what does a lifecycle policy do — including the minimum storage durations and the 128 KB rule?
Why they ask this
Storage classes are where lake cost is won or lost, and the minimums are what make a sensible-looking lifecycle rule cost more than it saves.
Say this
Classes trade storage price against retrieval cost and minimum duration: Standard, Intelligent-Tiering, Standard-IA and One Zone-IA, then the three Glacier classes. A lifecycle policy moves or expires objects by age, prefix, tag or size — but the IA and Glacier classes carry minimum durations and a 128 KB minimum billable size that small or short-lived objects pay anyway.
The reasoning
**S3 Standard** is for hot data. **Intelligent-Tiering** charges a small monitoring fee per object and moves objects between access tiers on its own — to Infrequent Access after 30 days without access and to Archive Instant Access after 90 — with **no retrieval fees**; objects under 128 KB are not monitored. **Standard-IA** and **One Zone-IA** are cheaper to store and charge per GB retrieved; they bill a **30-day minimum** and a **128 KB minimum object size**, and One Zone-IA keeps data in a single Availability Zone.
The archive classes: **Glacier Instant Retrieval** (millisecond access, 90-day minimum), **Glacier Flexible Retrieval** (minutes to hours to restore, 90-day minimum) and **Glacier Deep Archive** (the cheapest, restores in hours, 180-day minimum). Deleting, overwriting or transitioning an object before its minimum is charged as if it had stayed.
A **lifecycle configuration** has transition actions and expiration actions, filtered by prefix, tag or object size. Transitions follow a waterfall — Standard to IA to Glacier, not back up. Since September 2024, objects **smaller than 128 KB are not transitioned by default**, because each transition is a billed request and a tiny object in IA is billed as 128 KB anyway. Lifecycle rules should also expire noncurrent versions in versioned buckets and abort incomplete multipart uploads, which are two quiet sources of storage cost.
For a lake the usual shape is: raw landing data to IA after 30 days and to Deep Archive after 180 if audit requires keeping it; curated Parquet stays in Standard or Intelligent-Tiering because query engines read it unpredictably; scratch and staging prefixes expire after 7 days.
The answer most people give
"Move everything to Glacier after 30 days to save money." Glacier Flexible Retrieval has a 90-day minimum and hours-long restores, so queries break and early deletes are billed as though the data had stayed the full 90 days.
They’ll ask next
Your lake holds 300 million objects averaging 60 KB. Does a transition-to-IA rule save money? Work it out.
How does AWS Glue fetch the metadata of data sources, such as Parquet or CSV files stored in an S3 bucket?
Why they ask this
A crawler is magic until it produces forty tables instead of one, or types every CSV column as string. The question checks whether you know what it actually reads.
Say this
A Glue crawler runs classifiers over the files under an S3 path: for Parquet it reads the schema from the file footer, for CSV and JSON it samples rows and infers types. It groups paths with compatible schemas into one table, turns `key=value` folders into partition columns, and writes the result to the Glue Data Catalog.
The reasoning
A **crawler** is pointed at one or more include paths and runs a list of **classifiers** in order. Built-in classifiers cover Parquet, ORC, Avro, JSON, CSV and others; custom classifiers (grok, XML, JSON path, CSV with a declared header) run first when present. Self-describing formats are cheap: Parquet and ORC carry the schema in the file, so the crawler reads it. Text formats are sampled, which is why a CSV column that is empty in the sample comes out as `string`.
The crawler then decides **table boundaries**. Folders whose files share a compatible schema become one table, and the folders below the table root become partitions — named after the key when the path is Hive-style (`dt=2026-09-14`), or `partition_0`, `partition_1` otherwise. When schemas differ between folders it may create one table per folder instead; the crawler option to **create a single schema for each S3 path** stops that.
What it does on later runs is configurable: a **schema change policy** (update the table, or only log the change), a **deletion behaviour** for vanished objects, and a **recrawl policy** — crawl everything, or only new folders (incremental crawls that add new partitions). Crawls are billed by DPU time, so a full crawl over a million objects every hour is a real cost.
A crawler is not the only way to get metadata. You can declare the table with `CREATE EXTERNAL TABLE` in Athena, have the Glue job update the catalog as it writes (`enableUpdateCatalog`), or skip partition registration with Athena partition projection. Many teams crawl once to bootstrap, then own the DDL.
The answer most people give
"The crawler copies the data into the Glue Catalog." It copies nothing but metadata — table name, columns, types, location and partitions. The data stays in S3, and a wrong inferred type is fixed in the catalog, not in the files.
They’ll ask next
The crawler created one table per day folder instead of one partitioned table. Why, and what do you change?
What does a Glue job bookmark actually remember for an S3 source and for a JDBC source, and what must the job script do for it to work?
Why they ask this
Bookmarks are the standard answer to "how do you load incrementally in Glue", and most candidates cannot say what state is kept — which is exactly what they need when a job reprocesses or skips data.
Say this
For S3 sources the bookmark keeps the last-modified times of objects already processed, so new or modified files are picked up. For JDBC sources it keeps the highest value of the bookmark key — the primary key by default — which must increase monotonically. The script must pass a `transformation_ctx` on each source and call `job.commit()`, or nothing is saved.
The reasoning
A bookmark is persisted **state per job, per `transformation_ctx`**. For **Amazon S3** input, AWS documents that bookmarks check the **last modified time** of objects to decide what needs processing; a file that is modified after it was processed is reprocessed in full on the next run. For **JDBC** sources the bookmark stores the last value of the **bookmark keys**: by default the primary key, which is expected to be sequential, or user-defined `jobBookmarkKeys` that must be **strictly monotonically increasing or decreasing**. Gaps are fine; updates to old rows are invisible, because their key does not change.
The script has to cooperate. Each source that should be tracked needs a `transformation_ctx` name, the job must be started with `--job-bookmark-option job-bookmark-enable`, and it must end with **`job.commit()`** — the state is only saved on commit, so a job that fails before it reprocesses the same input next time. The other options are `job-bookmark-disable` (always read everything) and `job-bookmark-pause` (read from the last bookmark without advancing it, with optional `job-bookmark-from` / `job-bookmark-to` run ids).
The sharp edges are documented. **Resetting or rewinding a bookmark does not clean the target** — Glue does not track outputs — so a reset job appends a second copy unless the write is an overwrite or a merge. **Changing a source path while keeping the same `transformation_ctx`** makes Glue apply the old state to the new path and skip files it thinks it has seen. And for a partition with a very large number of files, bookmark listing can run the driver out of memory; AWS points at the Glue S3 file lister for that case.
So a bookmark is a watermark, not exactly-once delivery. Make the write idempotent — partition overwrite or `MERGE` on a business key — and a rerun or reset becomes safe rather than a duplicate-cleanup exercise.
The formulations
Tracked source plus commitship
job.init(args["JOB_NAME"], args)
orders = glueContext.create_dynamic_frame.from_catalog(
database="raw", table_name="orders", transformation_ctx="orders_src")
# ... transform and write ...
job.commit() # bookmark state is saved only here
Named source and an explicit commit: the two things the bookmark cannot work without.
Bookmarks are enabled on the job, yet this source is read in full on every run.
The answer most people give
"The bookmark guarantees each file is processed exactly once." It records what was read. A failure after the write but before `job.commit()` reprocesses the batch, and a reset replays everything into a target Glue never cleaned.
They’ll ask next
A source file is corrected and re-uploaded under the same key a week later. What does the next bookmarked run do with it?
Reported · 2Glue ETL jobs & job bookmarksCost optimisation
What configuration does a Glue job need, and what are connectors and connections in the Glue service?
Why they ask this
It tests whether you have created a Glue job rather than read about one: worker types, DPUs, the role, the version and how the job reaches a database in a VPC.
Say this
A Glue job needs an IAM role, a Glue version, a worker type and count, a script, and options such as bookmarks, timeout, retries and auto scaling. A connection is a Data Catalog object holding the endpoint, credentials and VPC details for one data store; a connector is the driver or code a connection is built on.
The reasoning
**Role and version.** The job runs as an IAM role that needs access to the script location, the data and the temporary directory. **Glue 5.1** (Spark 3.5.6, Python 3.11) is the default for jobs created without a version; older versions stay selectable, and features differ between them.
**Capacity.** Capacity is measured in DPUs — one **DPU is 4 vCPUs and 16 GB of memory**. Worker types map onto it: **G.1X** is 1 DPU, **G.2X** 2 DPU, **G.4X** 4 DPU, and **G.025X** a quarter DPU for low-volume streaming jobs. Spark jobs need a minimum of 2 DPUs, and AWS bills per DPU-hour, per second, with a **1-minute minimum** on Glue 2.0 and later. Auto scaling lets a job add and remove workers up to the configured maximum, and the **Flex** execution class runs non-urgent jobs on spare capacity at a lower rate.
**Behaviour.** The job bookmark option, timeout, maximum retries, maximum concurrent runs, job parameters (`--TempDir`, `--additional-python-modules`, `--conf`), and the Spark UI and continuous logging settings used for debugging.
**Connections and connectors.** AWS defines a **connection** as a Data Catalog object that stores login credentials, URI strings and VPC information for a particular data store, reusable across crawlers and jobs. A **connector** is what the connection is created from: the native JDBC connectors, AWS Marketplace connectors, or a custom one. For a database inside a VPC, Glue attaches network interfaces in the connection's subnet with its security group, so that group needs a self-referencing inbound rule and the subnet needs a route to S3 — usually an S3 gateway endpoint. Keep the password in Secrets Manager and reference it from the connection.
The answer most people give
"Glue is serverless, so there is nothing to configure." You still choose the worker type and count, which set both runtime and cost, and a job that has to reach a private RDS instance fails until the connection's subnet and security group are right.
They’ll ask next
The job times out connecting to a private RDS instance even though the credentials are right. What do you check first?
Reported · 2Glue ETL jobs & job bookmarksGlue Data Catalog & crawlers
Can you connect to tables in different AWS databases, such as RDS and Redshift, using a single connection in AWS Glue?
Why they ask this
It checks whether you know what a connection is scoped to — one data store — and what the realistic alternatives are when a job needs two.
Say this
No: a Glue connection describes one data store — one endpoint, one set of credentials, one network placement. One connection can reach every table that login can see inside that database; for RDS and Redshift together you attach two connections to the job, or move the join somewhere that already reaches both.
The reasoning
A connection holds a single JDBC URL (or a Redshift or other native endpoint), its credentials and its VPC settings. What one connection **can** cover is many tables in the same database: a crawler with the include path `sales/%` catalogues every table in the `sales` database, and one job can read ten tables through the same connection.
RDS and Redshift are two data stores, so the job gets **two connections**. Each carries its own subnet and security group, and the practical constraint is network reachability: the job's network interfaces must be able to reach both endpoints, so the simplest arrangement puts both in one VPC or routes between them. Credentials belong in Secrets Manager, one secret per store.
Two alternatives are often better than a Glue join across two databases. The Glue **Redshift connector** reads and writes Redshift through an S3 temporary directory (`UNLOAD` out, `COPY` in), which is much faster than row-by-row JDBC for large tables. And **Redshift federated query** lets Redshift query RDS or Aurora PostgreSQL and MySQL tables live, so the join can run inside the warehouse with no Glue job at all.
If the question is really "one connection to manage", the answer is the Data Catalog: crawl both stores once, and every downstream job refers to catalog tables by name rather than by endpoint.
The answer most people give
"Yes — the connection is to your AWS account, so it can see all the databases." A connection is scoped to one data store and one network placement, and the job fails the moment it tries a second endpoint through it.
They’ll ask next
The Redshift table is 2 TB. Why is reading it through a plain JDBC connection a bad idea, and what does the Glue connector do instead?
What are the different types of triggers in AWS Glue and in AWS Step Functions, and when would you orchestrate with one rather than the other?
Why they ask this
Every AWS pipeline needs something to start it and something to chain it. Knowing what each service offers — and where Glue triggers run out — is the orchestration question in AWS form.
Say this
Glue has four trigger types: scheduled, conditional (on the state of other jobs or crawlers), on-demand and EventBridge event. Step Functions has no trigger object: executions are started by EventBridge rules or schedules, the API, or another state machine, and inside them you chain Glue, Lambda, EMR and more with retries and error handling.
The reasoning
**Glue triggers** come in four types. **Scheduled** runs on a cron expression. **Conditional** fires when watched jobs or crawlers reach a state — `SUCCEEDED`, `FAILED`, `TIMEOUT` — with ANY or ALL logic, which is how one Glue job chains after another. **On-demand** fires when started manually or by API. **Event** fires on EventBridge events, optionally batched by count or time window. Glue **workflows** group triggers, jobs and crawlers into a DAG you can monitor as a unit.
**Step Functions** starts executions from outside — an EventBridge rule or EventBridge Scheduler, a `StartExecution` API call, API Gateway, or another state machine. The value is inside: service integrations call Glue, EMR, EMR Serverless, Lambda, Athena and the Redshift Data API, and the **`.sync`** integration pattern waits for a Glue job to finish before the next state. `Retry` and `Catch` give per-step error handling. **Standard** workflows run up to **one year** with exactly-once state transitions; **Express** workflows run up to **five minutes** with at-least-once semantics, for high-volume short work.
The decision is usually made on scope. If every step is a Glue job or crawler, Glue workflows and conditional triggers are enough and cost nothing extra. As soon as the chain mixes services, needs branching, human approval or careful failure handling, **Step Functions** is the AWS-native choice. **MWAA** (managed Airflow, with Airflow 2.10, 2.11 and 3.x versions supported) wins when the team already writes Airflow DAGs, needs backfills over logical dates, or orchestrates systems outside AWS.
The answer most people give
"Use a Lambda on a cron to start each job, and have each job start the next." That rebuilds orchestration by hand, with no retry policy, no view of the whole run and no clean way to rerun one failed step.
They’ll ask next
A Step Functions state starts a Glue job without `.sync`. What does the next state see, and why is that a bug?
What are the pros and cons of using Amazon Redshift over other data warehousing solutions such as Oracle or MySQL?
Why they ask this
Amazon asks it on the phone screen to see whether you understand why a columnar MPP engine is fast for analytics — and what it gives up to be fast.
Say this
Redshift stores data by column, compresses it and spreads it over nodes that scan in parallel, so aggregations over billions of rows are fast and storage scales separately on RA3 and RG nodes. It gives up what an OLTP database is for: fast single-row writes, enforced constraints and indexes.
The reasoning
**Architecture.** A leader node plans queries and compute nodes execute them; each compute node is divided into **slices** that each own part of every table. Storage is **columnar and compressed**, in 1 MB blocks with min/max metadata (zone maps), so a query reads only the columns it names and skips blocks outside its filter. That is why `SUM(amount) GROUP BY region` over two billion rows is seconds in Redshift and minutes in a row store.
**Pros.** Massively parallel scans and joins; **RA3** and the newer **RG** node types separate compute from managed storage, so storage grows without adding nodes; querying S3 in place (Spectrum, or the engine built into RG nodes); concurrency scaling for bursts; Serverless for spiky workloads; and it is a managed service, with automatic vacuum, analyse and table optimisation.
**Cons.** It is not an OLTP database. Single-row `INSERT`s and `UPDATE`s are slow and are meant to be batched through `COPY` and staging tables. **Primary, unique and foreign keys are informational and not enforced**, so duplicates are your problem. There are no indexes; performance depends on distribution and sort keys, which `AUTO` helps with but does not replace. Concurrency is far lower than an OLTP database's, and a provisioned cluster costs money while idle.
MySQL is a row store built for transactions and scales up, not out; Oracle can do analytics but licensing and scale-up hardware are the price. The honest framing: keep OLTP in RDS or Aurora, replicate into Redshift, and do analytics there.
The answer most people give
"Redshift is just a bigger, faster PostgreSQL." It speaks PostgreSQL-flavoured SQL, but it has no indexes, does not enforce keys and performs badly on the single-row workload PostgreSQL is built for.
They’ll ask next
Your load inserted the same order twice and Redshift accepted it despite a primary key. Why, and how do you stop it?
For a query that finds which product sold the most in 2020 from an orders table in Redshift, how many times is the orders table scanned, and would partitioning the table improve performance?
Why they ask this
It is the follow-up Amazon attaches to a SQL question. The trap is that Redshift local tables have no partitions — the answer is sort keys and zone maps.
Say this
Written as one GROUP BY with a filter on the order date, the orders table is scanned once; `EXPLAIN` shows it. Redshift tables have no partitions — the equivalent is a sort key on the order date, whose zone maps let the scan skip every block outside 2020.
The reasoning
Count the scans from the plan, not the SQL. `SELECT product_id, SUM(qty) FROM orders WHERE order_date >= '2020-01-01' AND order_date < '2021-01-01' GROUP BY 1 ORDER BY 2 DESC LIMIT 1` scans `orders` **once**. Written as "compute totals, then find the max, then join back to find which product has it", the plan can show two scans of the same table — which is what the interviewer wants you to spot. `EXPLAIN` lists each `Seq Scan` step.
**Redshift local tables have no `PARTITION BY`.** The mechanism that plays the same role is the **sort key**. Redshift stores each column in 1 MB blocks and records each block's minimum and maximum value — the **zone map**. With `order_date` as the leading sort-key column, the filter above lets the scan skip every block whose range lies outside 2020, which on ten years of orders removes about 90% of the I/O. Without a sort key, those blocks are read and discarded.
The **distribution key** matters too once the query joins `orders` to `products`: if both are distributed on `product_id`, or `products` is small enough for `DISTSTYLE ALL`, the join needs no redistribution.
Partitions do exist for **external tables** — Spectrum tables over S3 declare partition columns in the Glue Catalog, and a filter on them prunes whole S3 prefixes. So the full answer is: sort keys for local data, partitions for data left in S3.
Zone maps on order_date skip every block outside the requested year.
Try to partition like Hiveavoid
CREATE TABLE orders (...) PARTITIONED BY (order_year INT);
Not valid for Redshift local tables; partitions belong to external (Spectrum) tables.
The answer most people give
"Yes, partition the orders table by year." Redshift local tables cannot be partitioned. The candidate who says this has usually only used Hive or BigQuery, and the interviewer moves on to sort keys to find out.
They’ll ask next
Most queries filter on `order_date` but a few filter only on `customer_id`. What does a compound sort key do for those?
What are the best practices to improve query performance in Amazon Redshift?
Why they ask this
Asked in almost every AWS loop that uses Redshift. A list of words passes nothing; the interviewer wants each practice tied to the mechanism it works through.
Say this
Design the table so queries touch less data and move less of it: sort keys for the filters, distribution for the joins, compressed columns. Then load in bulk, keep statistics current, manage concurrency with WLM, and read `EXPLAIN` for redistribution steps before changing anything.
The reasoning
**Table design.** Put a **timestamp as the leading sort-key column** when queries filter on time ranges, so zone maps skip blocks; if a table is frequently joined, a sort key on the join column lets the planner use a merge join instead of a hash join. Distribute large tables with **`DISTKEY`** on their most frequent large join column so joins are co-located, give small dimensions **`DISTSTYLE ALL`**, and let **`AUTO`** decide where there is no clear choice. Leave column compression to `ENCODE AUTO`.
**Loading and maintenance.** Load with `COPY` from multiple files, not row-by-row `INSERT`. Make sure statistics are current (`ANALYZE`, which runs automatically but can lag after a large load) and that deleted rows are reclaimed and data re-sorted (automatic `VACUUM`). Use materialised views with auto refresh for expensive repeated aggregations.
**Workload.** Automatic WLM with query priorities, short query acceleration for quick queries, and concurrency scaling for bursts of queued queries. Result caching serves repeated identical queries without re-running them.
**Diagnose before tuning.** `EXPLAIN` shows join redistribution: `DS_DIST_NONE` and `DS_DIST_ALL_NONE` are good, while `DS_BCAST_INNER` and `DS_DIST_BOTH` mean data moves between nodes at query time. `SYS_QUERY_HISTORY` finds the slow queries, `STL_ALERT_EVENT_LOG` flags missing statistics and nested loops, and Redshift Advisor recommends sort and distribution keys from the actual workload.
The answer most people give
"Add indexes on the columns in the WHERE clause." Redshift has no indexes. The equivalent levers are sort keys and distribution, and a candidate who reaches for indexes has not tuned a columnar warehouse.
They’ll ask next
`EXPLAIN` shows `DS_BCAST_INNER` on a join to a 30-million-row table. Is `DISTSTYLE ALL` the fix?
What are the steps to load data from a CSV file in S3 into Amazon Redshift?
Why they ask this
It separates people who have loaded a warehouse from people who have queried one. The detail interviewers listen for is parallelism: file count, slices and COPY.
Say this
Stage the file in S3, split into several compressed files; give the cluster an IAM role that can read the bucket; create the table; run `COPY` with the CSV options and the role. Check the load-error tables, and for updates load into a staging table and merge in one transaction.
The reasoning
**Stage.** Put the data in S3 split into multiple files of similar size — AWS recommends files between **1 MB and 1 GB after compression**, and a file count that is a **multiple of the number of slices**, so every slice loads in parallel. One 20 GB CSV loads on one slice.
**Permissions.** Associate an IAM role with the cluster or workgroup that allows `s3:GetObject` and `s3:ListBucket` on the prefix, plus `kms:Decrypt` if the objects use SSE-KMS.
**Load.** Create the target table, then run `COPY` with the format options — `FORMAT AS CSV`, `IGNOREHEADER 1`, `DATEFORMAT`, `GZIP` — and `IAM_ROLE`. A **manifest** file pins the exact set of files to load, which makes reruns deterministic. `COPY` is parallel across slices; `INSERT` statements go one by one and are far slower.
**Verify and merge.** Failures land in `STL_LOAD_ERRORS` (`SYS_LOAD_ERROR_DETAIL` on Serverless); `MAXERROR` decides how many bad rows are tolerated. Because keys are not enforced, a load that must update existing rows goes into a **staging table** first, followed by `MERGE` (or delete-then-insert) into the target inside one transaction.
The formulations
COPY from a manifestship
COPY stage.orders
FROM 's3://lake/exports/orders/2026-09-14/manifest.json'
IAM_ROLE 'arn:aws:iam::123456789012:role/redshift-load'
FORMAT AS CSV IGNOREHEADER 1 GZIP MANIFEST;
Parallel across slices, and the manifest pins exactly which files this run loads.
INSERT row by rowavoid
INSERT INTO stage.orders VALUES (1, 'A', 10.50);
INSERT INTO stage.orders VALUES (2, 'B', 7.25); -- one statement per row
Each statement is a separate commit through the leader node; millions of rows take hours.
The answer most people give
"Read the CSV in Python and insert the rows with a database driver." It works for a few thousand rows and takes hours for millions, because it bypasses the parallel load path Redshift is built around.
They’ll ask next
The COPY fails on row 1,204,331 with an invalid date. How do you find the row and load the rest?
What is Redshift Spectrum, and when would you query S3 through Spectrum rather than Athena, or load the data with COPY?
Why they ask this
Three ways to query the same files, each billed differently. The interviewer wants a decision rule, not three definitions.
Say this
Spectrum lets a Redshift cluster query S3 data through external tables in the Glue Catalog, and join it to warehouse tables. Use it for large, cold history that joins to warehouse data; Athena for lake-only, ad hoc queries without a warehouse; COPY when data is queried hard and often.
The reasoning
**Spectrum** defines an external schema over a Glue Data Catalog database. A query that touches an external table sends the S3 part to the Spectrum layer, which scans the files, applies filters and partial aggregates, and returns only what the cluster needs; most of the data stays in S3. Spectrum is billed by bytes scanned, on top of the cluster. The newer **RG** node type includes a built-in data lake query engine for which AWS notes Spectrum is not required.
**Athena** is serverless SQL over the same catalog — no cluster, billed per byte scanned (**$5 per TB** at the us-east-1 list price as of September 2026, which can change) with a 10 MB minimum per query. It is the natural tool for exploration, lake-only reporting and teams without a warehouse.
**COPY** moves the data into Redshift managed storage, where it gets sort keys, distribution, compression and result caching. Pay the load once, then query cheaply and fast.
The rule: hot and joined every hour → COPY. Five years of clickstream joined monthly to warehouse dimensions → Spectrum, partitioned in Parquet. Analysts poking at raw lake files → Athena. All three read less, and cost less, when the files are Parquet and partitioned by the filter column.
The answer most people give
"Spectrum and Athena are the same thing, so use whichever." They share the catalog and similar pricing, but Spectrum runs from a cluster and can join warehouse tables; Athena cannot see Redshift local tables without a federated connector.
They’ll ask next
A Spectrum query over CSV costs more than the cluster that day. What two changes to the files fix it?
How does Redshift handle many users querying at the same time, and what is concurrency scaling?
Why they ask this
Dashboards at 9 a.m. are where Redshift clusters fall over. The question tests whether you know WLM, what concurrency scaling can and cannot offload, and what it costs.
Say this
Workload management queues and prioritises queries so short dashboard queries are not stuck behind long ETL. When queries queue, concurrency scaling adds transient capacity for eligible reads and common writes; each cluster earns up to one hour of free credit a day, and usage beyond that is billed per second.
The reasoning
**Workload management (WLM)** routes queries into queues. Automatic WLM decides memory and concurrency itself and honours **query priorities**; **short query acceleration** runs quick queries in a dedicated space so they do not wait behind hour-long ETL.
**Concurrency scaling** is turned on per WLM queue. When queries queue, Redshift sends eligible ones to transient scaling clusters. It supports read queries and commonly used write operations — ETL statements such as `COPY`, `INSERT`, `UPDATE` and `DELETE` — but **not most DDL** such as `CREATE TABLE`, and not writes to a table with `DISTSTYLE ALL`.
**Cost.** AWS pricing states that each cluster earns **up to one hour of free concurrency scaling credits per day**, and usage beyond the credits is charged at a per-second on-demand rate. A usage limit can cap it, so a runaway dashboard cannot run up an unbounded bill.
**Serverless** handles concurrency by scaling Redshift Processing Units. The default **base capacity is 128 RPUs**, settable from 4 to 512; a **maximum RPU** setting caps how far a burst can scale, and so how much it can cost.
The answer most people give
"Add nodes whenever users complain about slowness." A resize pays for peak capacity around the clock, when the problem is a two-hour morning peak that WLM priorities and concurrency scaling handle for far less.
They’ll ask next
Concurrency scaling is on, but the morning ETL still queues behind dashboards. Which queries were not eligible to scale?
How is Amazon Athena billed, and what do partition pruning and predicate pushdown do to the cost of a query?
Why they ask this
Athena cost is almost entirely a function of the files, not the SQL. The question checks that you can turn that into numbers.
Say this
Per-query Athena billing charges for bytes scanned, rounded up to the megabyte with a 10 MB minimum — $5 per TB at the us-east-1 list price as of September 2026. Partition pruning skips whole S3 prefixes and predicate pushdown skips Parquet row groups, so both cut the bill directly.
The reasoning
With per-query billing you pay for **bytes scanned per query, rounded up to the nearest megabyte, with a 10 MB minimum**; DDL such as `CREATE`, `ALTER` and `DROP TABLE` is free. The list price in us-east-1 is **$5 per TB** as of September 2026 and may change. **Capacity Reservations** are the alternative: dedicated DPUs billed hourly, for predictable heavy use.
AWS's own pricing example makes the levers concrete: a query over 3 TB of uncompressed text costs **$15**; GZIP at 3:1 brings the same query to **$5**; converted to a columnar format and reading one of four columns, it scans 0.25 TB and costs **$1.25**.
**Partition pruning** happens when the `WHERE` clause filters on a partition column: Athena reads only the matching prefixes. It only works on the partition column as stored — `WHERE dt = '2026-09-14'` prunes, while wrapping a timestamp column in a function scans everything. **Predicate pushdown** goes further inside the files: Parquet and ORC keep min/max statistics per row group, and row groups that cannot match are skipped. It works best when files are sorted or clustered on the filter column.
Two cheap guards: a workgroup **per-query data usage control** cancels any query that would scan more than a set amount, and large files (hundreds of MB) keep S3 request and file-open overheads down. `LIMIT` does not reduce what is scanned on an unsorted table.
The formulations
Filter on the partition columnship
SELECT count(*) FROM events
WHERE dt = '2026-09-14' AND event_type = 'checkout';
Reads one day's prefix; the event_type filter can then skip Parquet row groups.
Filter on a derived timestampavoid
SELECT count(*) FROM events
WHERE date(event_ts) = DATE '2026-09-14';
Same answer, but no partition is pruned, so every day in the table is scanned.
The answer most people give
"Athena is serverless, so it is cheap." It is cheap on partitioned Parquet and expensive on CSV. The same query can differ twelvefold in cost with no change to the SQL.
They’ll ask next
A dashboard runs the same Athena query every five minutes. What else, besides the file format, stops you paying for it 288 times a day?
Reported · 2Athena & query costGlue Data Catalog & crawlers
What is partition projection in Athena, and when is it better than MSCK REPAIR TABLE or partitions added by a crawler?
Why they ask this
A table with years of hourly partitions makes partition management a job of its own. Projection is the answer — with a catch other engines hit.
Say this
Partition projection lets Athena compute partition values and locations from table properties instead of looking them up in the Glue Catalog, so new partitions need no registration and planning is faster on highly partitioned tables. Its catch is that only Athena uses it; other engines still need registered partitions.
The reasoning
Normally Athena looks up partitions in the Glue Data Catalog, which means someone has to add them — a crawler, `MSCK REPAIR TABLE`, or `ALTER TABLE ADD PARTITION`. On a table with tens of thousands of partitions that lookup is slow, and a missing registration makes new data invisible.
With **partition projection** you declare in table properties how partitions are formed: a type for each partition column — **integer, date, enum or injected** — its range and format, and a storage location template. Athena then **projects** the partitions in memory. AWS documents that enabling projection makes Athena **ignore any partition metadata registered in the Glue Catalog** for that table.
`MSCK REPAIR TABLE` works only for Hive-style `key=value` paths and lists the whole table location, which is slow on large tables. Crawlers cost DPU time and can mis-infer. Projection needs neither.
The limits: projection is an **Athena** feature, so Spark on EMR or Glue reading the same catalog table still sees only registered partitions, and a projected partition with no data returns nothing rather than an error — a missing day looks like a quiet day unless something checks.
No partition registration ever again for Athena, and fast planning on thousands of days.
MSCK REPAIR after every loadworks
MSCK REPAIR TABLE events;
Keeps the catalog usable by every engine, but lists the whole location and slows as it grows.
The answer most people give
"Run a crawler every hour to add the new partitions." It works, costs DPU time on every run, and can change the schema or split the table in ways nobody asked for.
They’ll ask next
The same table is read by Athena and by a Glue Spark job. Projection is on. What does the Spark job see for yesterday's partition?
How does a Kinesis data stream scale — what does a shard give you, what does the partition key decide, and what changes in on-demand mode?
Why they ask this
Every Kinesis design question bottoms out in shard arithmetic. A candidate who cannot size a stream cannot say why a consumer is throttled.
Say this
In provisioned mode each shard takes up to 1 MB/s or 1,000 records/s of writes and serves 2 MB/s of reads shared by its standard consumers. The partition key is hashed to pick a shard, which gives ordering per key and caps a hot key at one shard. On-demand mode manages shards for you and scales to double the previous peak.
The reasoning
**Per shard, provisioned:** writes up to **1 MB/s or 1,000 records/s**; reads up to **2 MB/s**, shared by every standard consumer, with at most **five read transactions per second**, each returning up to 10,000 records or 10 MB. **Enhanced fan-out** gives each registered consumer its own **2 MB/s per shard**, pushed over HTTP/2 — up to 20 consumers per stream, or 50 with On-demand Advantage. Exceeding a limit returns `ProvisionedThroughputExceededException`.
**The partition key** is hashed (MD5) into the shard hash-key space. All records with one key go to one shard, which is what gives **ordering per key**; it also means one hot key can never go faster than one shard, whatever the shard count. Resharding splits or merges shards (`UpdateShardCount` does it uniformly).
**On-demand mode** removes shard planning. AWS documents that a new on-demand stream starts with **4 MB/s** of write capacity and accommodates up to **double its previous peak** write throughput, scaling within about 15 minutes; the ceiling is 10 GB/s of writes in the largest Regions and 200 MB/s elsewhere. A stream can switch between modes **twice in 24 hours**. The account-level **On-demand Advantage** mode changes pricing and allows pre-warming.
**Retention** defaults to 24 hours and can be raised to **8,760 hours (365 days)**. Records can now be up to **10 MiB**, but every stream, new or existing, defaults to a **1 MiB** maximum until you raise it with `UpdateMaxRecordSize`; AWS positions large records as occasional, alongside a baseline of records of 1 MiB or less.
The answer most people give
"Add shards until the throttling stops." If one partition key produces most of the traffic, every extra shard sits idle while the hot one still throttles. The fix is the key, not the count.
They’ll ask next
Three Lambda functions and a Firehose all read the same 10-shard stream and all fall behind. What is being shared, and what do you change?
Reported · 2Lambda in data pipelinesGlue ETL jobs & job bookmarks
What are the limitations of using Lambda functions, and when should AWS Lambda be used rather than AWS Glue?
Why they ask this
Lambda is the most over-used service in junior AWS designs. The question checks that you know its ceilings by number and can say where Glue takes over.
Say this
A standard Lambda invocation runs for at most 15 minutes with up to 10,240 MB of memory and a 6 MB synchronous payload, and has no distributed engine. Use Lambda for small, event-driven units — one file, one batch of stream records, one API call — and Glue for joins, large files and anything that needs Spark.
The reasoning
**The ceilings.** A standard invocation can run for up to **900 seconds (15 minutes)**; the exception is Lambda Managed Instances, where asynchronous and event-source-mapping invocations can run up to 90 minutes. Memory is **128 MB to 10,240 MB**, with CPU allocated in proportion. Synchronous request and response payloads are capped at **6 MB** each. Concurrency is limited per account and Region, and new accounts start with reduced quotas. Cold starts add latency, and state does not survive between invocations.
**Where Lambda fits.** Event-driven work where each unit is small and independent: an S3 `ObjectCreated` event that validates or converts one modest file, a Kinesis batch that is enriched and written on, an API that returns a metric, a small file-arrival hook that starts a Glue job or a Step Functions execution. It scales per event and costs nothing when idle.
**Where Glue fits.** Anything that needs Spark: joins between large datasets, files in the gigabytes, aggregations across partitions, job bookmarks, catalog integration. A Lambda that loads a 3 GB file into memory, or loops through S3 to stay under 15 minutes, is a Glue job written the hard way.
The two work well together: Lambda as the trigger and light transform, Glue (or EMR) as the engine.
Runs into the memory ceiling and the 15-minute timeout as soon as volume grows.
The answer most people give
"Lambda scales infinitely, so it can do any ETL." It scales out to many small invocations, not up to one big one. A single job still has a 15-minute ceiling and a 10 GB memory limit.
They’ll ask next
A Lambda triggered by S3 events sometimes processes the same file twice. Why, and what makes that harmless?
How does AWS DMS perform a full load followed by change data capture, and what has to be configured on the source database for CDC to work?
Why they ask this
DMS is the default AWS answer to "replicate this database". Interviewers probe what it reads from the source, because that is where DMS tasks fail.
Say this
A DMS task copies each table in full while caching changes that arrive during the copy, applies those, then keeps reading changes from the database's transaction log. That only works if the source keeps the log in a usable format for long enough — row-based binlogs on MySQL, logical replication on PostgreSQL, supplemental logging on Oracle.
The reasoning
DMS runs **tasks** on a replication instance (or DMS Serverless) between a source and a target endpoint. A task is **full load**, **CDC only**, or **full load plus CDC**. In the combined mode, changes made during each table's full load are **cached** and applied once that table's load completes, then the task moves to **ongoing replication**, reading the source's log through engine-specific APIs.
**Source prerequisites** are where tasks fail. On MySQL, CDC needs `binlog_format = ROW` and `binlog_row_image = FULL`, and on RDS the binary logs must be kept long enough — for example `call mysql.rds_set_configuration('binlog retention hours', 24);` — or a task that falls behind finds its position purged. PostgreSQL needs logical replication (on RDS, `rds.logical_replication = 1`), which creates a replication slot. Oracle needs supplemental logging. Updates and deletes need a primary key to be applied correctly.
AWS is explicit that **DMS CDC is not real-time** and has **no latency SLA**; latency depends on source load, instance size and the target. Watch the `CDCLatencySource` and `CDCLatencyTarget` CloudWatch metrics.
Targets shape the rest. Writing to S3, DMS produces CSV or Parquet files with an **operation column (`I`, `U`, `D`)**, batched by settings such as `cdcMaxBatchInterval` and `cdcMinFileSize`; something downstream must merge those into a current-state table. Large objects need a LOB mode chosen deliberately, and data validation can compare source and target row by row.
The answer most people give
"DMS queries the tables every few minutes for changed rows." That is timestamp polling, which misses deletes. DMS reads the transaction log, which is exactly why the log settings on the source matter.
They’ll ask next
The DMS task was stopped for a weekend and will not resume on Monday. What happened on the source?
What are IAM policies and roles, and how does a Glue job or a Redshift cluster get permission to read an S3 bucket?
Why they ask this
Every AWS pipeline failure eventually reads "Access Denied". Knowing how roles, trust policies, bucket policies and KMS key policies combine is what lets you fix it.
Say this
A policy is a JSON document of allowed or denied actions on resources; a role is an identity with policies attached that a service assumes to get temporary credentials. A Glue job runs as its job role and Redshift uses a role associated with the cluster; each needs S3 permissions, and a KMS key policy that allows decrypt if the objects are SSE-KMS.
The reasoning
**Users** are long-lived identities for people or legacy tools; **groups** attach policies to many users; **roles** have no long-term credentials — a principal **assumes** the role and receives temporary credentials from STS. A role's **trust policy** says who may assume it: `glue.amazonaws.com` for a Glue job role, `redshift.amazonaws.com` for a Redshift role.
**Policies** are identity-based (attached to a user, group or role) or resource-based (attached to the resource, such as an S3 bucket policy or KMS key policy). Evaluation: an **explicit deny always wins**; otherwise the request needs an allow from an identity or a resource policy (within one account); organisation SCPs and permission boundaries can only narrow further.
**In practice:** the Glue job role gets `s3:GetObject` on `arn:aws:s3:::lake/raw/*` and `s3:ListBucket` on `arn:aws:s3:::lake` limited to that prefix, plus Glue Catalog permissions. Redshift `COPY` names the role with `IAM_ROLE`. If the bucket uses **SSE-KMS**, the role also needs `kms:Decrypt` in the key policy — the most common cause of "Access Denied" on a bucket whose policy looks right. Cross-account access needs both the bucket policy and the role policy to allow it.
Least privilege means prefix-scoped resources, no `s3:*`, and no access keys in job code. For table- and column-level control over the lake, Lake Formation adds a grant layer on top of IAM.
Makes the error go away and hands the job delete rights on every bucket in the account.
The answer most people give
"Create an IAM user and put its access keys in the Glue job parameters." Long-lived keys in job configuration leak through logs and consoles; services should assume roles.
They’ll ask next
The Glue role has `s3:GetObject` on the prefix but the read still fails with Access Denied. The bucket uses SSE-KMS. What is missing?
EvergreenReported · 2Glue ETL jobs & job bookmarksGlue Data Catalog & crawlers
What is AWS Glue, what are its components, and how does it work?
Why they ask this
The opener of most AWS data engineering rounds. A good answer names the components and what each is for in two minutes, then stops.
Say this
AWS Glue is a serverless data integration service: a Data Catalog holding table metadata, crawlers that fill it, and Spark, Python shell and streaming jobs that transform data. Triggers and workflows run the jobs, and you pay per DPU-hour, billed per second.
The reasoning
**Data Catalog** — the central metastore of databases, tables, schemas, locations and partitions. Athena, Redshift Spectrum, EMR and Glue jobs all read it, which is why it is the part of Glue used most widely.
**Crawlers** scan S3 and JDBC sources, infer schemas with classifiers and create or update catalog tables. **Connections** hold endpoints, credentials and VPC settings for data stores.
**Jobs** — Spark (Python or Scala, via `GlueContext` and DynamicFrames or plain DataFrames), Spark Streaming, and Python shell for small tasks; Glue 5.1 is the default version. Capacity is set in workers of a chosen type, measured in DPUs (4 vCPUs and 16 GB each). **Job bookmarks** track what has been processed. **Glue Studio** builds jobs visually.
**Triggers and workflows** start and chain jobs and crawlers. Around them sit Data Quality rules, the Schema Registry for streaming schemas, and DataBrew for no-code preparation.
The answer most people give
"Glue is AWS's ETL tool." True and not enough — it leaves out the Data Catalog, which is the part of Glue that Athena, Spectrum and EMR all depend on even when no Glue job exists.
They’ll ask next
What is a DynamicFrame, and when would you convert it to a Spark DataFrame?
EvergreenReported · 3Redshift architecture & distribution
What are the distribution styles in Amazon Redshift, and when do you use each?
Why they ask this
Asked verbatim in nearly every Redshift round. The follow-ups — skew, co-location, what AUTO does — are where it is decided.
Say this
There are four: AUTO, EVEN, KEY and ALL. KEY places rows with the same key value on the same slice so joins on that key need no data movement; ALL copies a small table to every node; EVEN spreads rows round-robin; AUTO, the default, lets Redshift choose and change the style as the table grows.
The reasoning
**KEY** hashes one column to choose the slice. Two large tables distributed on the column they are joined by are **co-located**, so the join needs no redistribution. A table can have only **one** distribution key, so pick the join that matters most and a high-cardinality column — a key with a few dominant values puts most rows on a few slices (skew).
**ALL** keeps a full copy on every node. Right for small, slowly changing dimensions joined to everything; wrong for large or frequently updated tables, because every write happens once per node.
**EVEN** spreads rows round-robin. Right when a table is not joined, or when no key is clearly better.
**AUTO** is the default when no style is specified. AWS documents that Redshift starts a small table as ALL, may switch it to KEY (choosing a primary-key column) as it grows, and to EVEN if no column suits; `SVV_ALTER_TABLE_RECOMMENDATIONS` shows what it proposes. `EXPLAIN` confirms the effect: `DS_DIST_NONE` means co-located, `DS_BCAST_INNER` and `DS_DIST_BOTH` mean data moved at query time.
The answer most people give
"Always use KEY on the primary key." Distributing a fact table on its own `order_id` spreads it evenly but co-locates nothing, because nothing joins to the fact on `order_id`.
They’ll ask next
How do you check whether a KEY-distributed table is skewed, and what do you do if it is?
EvergreenReported · 2Kinesis Data Streams & Firehose
What is AWS Kinesis, and what are its main services?
Why they ask this
The streaming opener. Interviewers want Data Streams and Firehose told apart in the first sentence, because the rest of the conversation depends on it.
Say this
Kinesis is AWS's family of managed streaming services. Kinesis Data Streams is a durable, sharded log that your consumers read at their own pace; Amazon Data Firehose (formerly Kinesis Data Firehose) is a delivery service with no consumer code that buffers records and writes them to S3, Redshift, OpenSearch, Iceberg tables and other destinations.
The reasoning
**Kinesis Data Streams** stores records in shards for 24 hours by default (up to 365 days). Producers write with a partition key, and any number of consumers — Lambda, the Kinesis Client Library, Flink, Firehose — read independently and can replay. Use it when something must process the data, in order per key, possibly in several ways.
**Amazon Data Firehose** has no read API. It takes records from producers or from a Kinesis stream, optionally transforms them with Lambda, converts JSON to Parquet or ORC, partitions them dynamically by fields in the data, buffers them, and delivers them. For S3 the buffer is configurable from **1 to 128 MiB** (default 5) and **0 to 900 seconds** (default 300); whichever fills first triggers a delivery.
The family also includes **Managed Service for Apache Flink** (formerly Kinesis Data Analytics) for stateful stream processing, and Kinesis Video Streams.
The decision rule: Streams when data has to be **processed**, Firehose when it only has to **land**. Many designs use both — Streams for the real-time consumers, Firehose reading the same stream to archive to S3.
The answer most people give
"Kinesis is AWS's Kafka." Data Streams is comparable to a Kafka topic, but Firehose is nothing like Kafka, and Amazon MSK is AWS's actual managed Kafka. Mixing the three up is the first thing interviewers notice.
They’ll ask next
Firehose delivers to S3 every 300 seconds and produces thousands of small files a day. Which settings change that?
What is the equivalent of Azure Data Factory in AWS?
Why they ask this
Asked of candidates who move between clouds. It checks that you can map concepts rather than names — ADF is both an integration tool and an orchestrator.
Say this
AWS Glue is the nearest equivalent: its jobs, Glue Studio and connections cover ADF's copy and data-flow work, and its triggers and workflows cover simple scheduling. For richer orchestration, the equivalents of ADF pipelines are Step Functions or Amazon MWAA.
The reasoning
**ADF pipelines with Copy activities and Mapping Data Flows** map to **Glue jobs** — Spark jobs written in code or built visually in Glue Studio. ADF **linked services** map to **Glue connections**, and ADF **datasets** roughly to **Glue Data Catalog tables**.
ADF's **self-hosted integration runtime**, which reaches on-premises systems, has no single AWS equivalent: a Glue connection in a VPC reaches on-premises databases over Direct Connect or VPN, and DMS or DataSync handle bulk movement.
ADF **triggers** (schedule, tumbling window, event) map to Glue triggers or EventBridge rules. ADF's role as an orchestrator of many services maps to **Step Functions**, or to **MWAA** when the team works in Airflow. The older service literally called AWS Data Pipeline is in maintenance mode and is not the answer for new work.
The answer most people give
"AWS Data Pipeline, because the name matches." It is the legacy service, not where AWS points new integration work — the answer is Glue for integration, and Step Functions or MWAA for orchestration.
They’ll ask next
An ADF pipeline uses tumbling-window triggers with dependencies between windows. How do you reproduce that on AWS?
EvergreenReported · 1Lambda in data pipelinesMonitoring & failure handling
Explain how Lambda asynchronous invocation works.
Why they ask this
S3 and EventBridge invoke Lambda asynchronously, so retries and lost events in file-driven pipelines come straight from this model.
Say this
The caller hands the event to Lambda, which queues it and returns immediately; Lambda then invokes the function from that internal queue. On a function error it retries twice more, and events that still fail — or that grow too old — go to a failure destination or dead-letter queue if one is configured, and are otherwise dropped.
The reasoning
With asynchronous invocation — how **S3 event notifications, SNS and EventBridge** call Lambda — the service places the event on an **internal queue** and returns success to the caller straight away. The caller never sees the function's result or its errors.
**Retries.** If the function returns an error, Lambda retries **up to two more times** by default, with waits between attempts. If it is throttled, Lambda keeps retrying for up to the **maximum event age** (six hours by default). Both the retry count (0–2) and the maximum event age are configurable.
**Failures.** Configure an **on-failure destination** (SQS, SNS, EventBridge, another Lambda, or S3) or a dead-letter queue to capture events that exhaust their retries; without one, they are discarded, and a file-triggered pipeline silently skips a file. On-success destinations can record completions.
**Consequence.** Because of retries, a function can see the same event more than once, so the handler must be idempotent — for example, write to a deterministic output key.
The answer most people give
"Asynchronous means Lambda runs it later, and if it fails the uploader gets an error." The uploader already got success when the event was queued; a failure after that is visible only through the function's logs, metrics and failure destination.
They’ll ask next
An S3-triggered Lambda loaded one file twice into the warehouse. Walk through how that happened.