Backend / Cloud: AWS / _interview_essentials.md

AWS Interview Essentials — Cross-Service Decision Matrices

Updated 6 interview angles 10 min read source
On this page9
  1. 1. Messaging: SNS vs SQS vs EventBridge vs Kinesis vs MSK
  2. 2. Compute: Lambda vs Fargate vs ECS-on-EC2 vs EKS vs EC2 vs App Runner vs Batch
  3. 3. Datastore: RDS vs Aurora vs DynamoDB vs ElastiCache vs DocumentDB vs OpenSearch
  4. 4. Request flow walkthrough
  5. 5. The “Lambda + RDS” problem
  6. 6. IAM patterns for containerized + serverless apps
  7. 7. Cost gotchas reference
  8. 8. boto3 idioms
  9. Interview angle

AWS Interview Essentials — Cross-Service Decision Matrices

The cross-cutting content interviewers ask about most. Individual service files (Compute/, Databases/, etc.) have the depth; this file is the “which one and why” layer plus the patterns that span services.

1. Messaging: SNS vs SQS vs EventBridge vs Kinesis vs MSK

The first split is message versus stream: a message is consumed and gone, a stream is a log you read at your own offset and can re-read.

Message-based — SQS, SNS, EventBridge:

SQS SNS EventBridge
Pattern queue, one consumer pub/sub fan-out bus with routing
Consumers competing a copy each rule-matched targets
Ordering FIFO queues only FIFO topics only no
Replay no, acked is gone no archive and replay
Filtering no subscription policies rich event patterns
Throughput ~unlimited ~unlimited thousands/s
Cost per request per request per event published

Stream-based — Kinesis and MSK, both ordered with replay:

Kinesis Data Streams MSK (managed Kafka)
Consumers many, own offset many, own offset
Ordering per shard per partition
Retention up to 365 days configurable
Filtering consumer-side consumer-side
Throughput shards x 1MB/s in very high
Cost per shard-hour + payload per broker-hour

Latency is milliseconds for all five, so it is not the deciding factor. | Best for | work distribution, buffering | fan-out notifications | event routing across many types/targets, SaaS integration, replay | high-throughput streaming, multiple independent readers, ordered replay | Kafka-compatible streaming, existing Kafka tooling |

Decision shortcut:

  • Two services, reliable handoff → SQS.
  • Tell N services “this happened” → SNS.
  • Route many event types to many targets with filtering / archive-replay / partner SaaS → EventBridge.
  • High-throughput ordered stream, replay, multiple independent consumers → Kinesis (or MSK if you need Kafka APIs/tooling).
  • “Exactly once across DB + queue” → none of these alone; use transactional outbox + idempotent consumers.

See Application_Integration/ for SQS/SNS/EventBridge depth.

2. Compute: Lambda vs Fargate vs ECS-on-EC2 vs EKS vs EC2 vs App Runner vs Batch

The axis that decides it is who manages the capacity.

You manage nothing — it scales to zero and you pay per use:

Lambda Fargate App Runner
Model function, event-driven container container, web
Max duration 15 min unbounded unbounded
Cold start ms to s 30-60s task start yes
Scales to zero yes no, min 1 task yes
Cost per invocation + GB-s per task vCPU/RAM-s per request + compute
Best for spiky, event-driven, short services and workers a simple web app

You manage nodes — cheaper at steady volume, more to run:

ECS-on-EC2 EKS EC2
Model containers, your nodes Kubernetes plain VM
Cold start node + task node + pod minutes to boot
Ops burden medium high high
Cost EC2, you bin-pack EC2 + control plane per instance-hour
Best for steady volume, GPU K8s shops, multi-cloud full control, legacy

Batch sits outside both: queue-driven, scales to zero when the queue empties, and priced per job. Use it for scheduled or parallel work such as rendering and ETL rather than for anything serving requests.

Decision shortcut:

  • Event-driven, short, spiky → Lambda.
  • Long-running web service / worker, don’t want to manage nodes → Fargate.
  • Steady high-volume where per-CPU cost matters, or GPU → ECS-on-EC2 (Spot + Reserved).
  • Already K8s / multi-cloud / need the K8s ecosystem → EKS.
  • Simple “deploy my container as a web app” → App Runner.
  • Big parallel batch jobs → Batch.

See Compute/01_Compare_AWS_Compute_Services/README.md and individual files.

3. Datastore: RDS vs Aurora vs DynamoDB vs ElastiCache vs DocumentDB vs OpenSearch

Relational, when you need joins and transactions:

RDS Aurora
Type managed PG/MySQL AWS-built SQL engine
Scaling vertical + read replicas storage auto, 15 readers
Failover 60-120s Multi-AZ ~30s
Best for relational workloads the same, bigger and faster

Everything else, chosen by access pattern rather than by data model:

Access pattern Consistency
DynamoDB known key lookups eventual, strong opt-in
ElastiCache get/set, TTL n/a, it is a cache
DocumentDB document queries tunable
OpenSearch full-text, aggregations eventual

DynamoDB scales horizontally and gives single-digit millisecond latency, but only for access patterns you designed the keys around — ad-hoc queries are the thing it is bad at. ElastiCache is for the hot path: sessions, rate limits, cached reads. OpenSearch also serves vector search, which is why it appears in RAG designs on AWS.

Decision shortcut:

  • Relational, joins, transactions, moderate scale → RDS.
  • Same but need faster failover, more read replicas, or storage > 64 TB → Aurora.
  • Known access patterns, massive scale, predictable latency → DynamoDB.
  • Sub-ms cache in front of any of the above → ElastiCache.
  • You have MongoDB code → DocumentDB (or self-host / Atlas).
  • Full-text search, log analytics, vector search → OpenSearch.

4. Request flow walkthrough

A typical production request, with the layers identified:

text
User


Route 53            DNS resolution; latency/failover routing


CloudFront          edge cache; static assets served here; TLS termination
  │  (cache miss / dynamic)

AWS WAF             rate limiting, SQLi/XSS rules, geo / IP blocking


ALB  or  API Gateway
  │  ALB: L7 routing, ECS/EKS targets, OIDC auth
  │  API GW: REST/HTTP API, Cognito/Lambda authorizers, throttling, usage plans

Compute             ECS/Fargate task  OR  Lambda
  │  - auth already validated at the edge / gateway
  │  - app reads secrets from Secrets Manager / SSM at cold start

Data layer
  │  - RDS via RDS Proxy (Lambda) or direct pool (long-running)
  │  - DynamoDB direct
  │  - ElastiCache checked first (cache-aside)

Async side-effects  → SQS / SNS / EventBridge → workers

Where caching lives: CloudFront (edge), ElastiCache (app data), API Gateway cache (rarely worth it). Where auth lives: WAF (coarse), gateway authorizer (token validation), app (fine-grained authz). Observability: CloudWatch Logs/Metrics + X-Ray traces threaded through every hop.

5. The “Lambda + RDS” problem

The single most-asked AWS+backend interview question.

The problem: Lambda scales by spawning concurrent execution environments. Each one opens its own DB connections. 1000 concurrent Lambdas → 1000 Postgres connections → RDS hits max_connections, refuses new connections, the whole service fails. Postgres connections are expensive (a process + memory each); a db.t3.medium caps around ~340 connections.

Three solutions:

Solution How Trade-off
RDS Proxy managed connection pooler in front of RDS; Lambda connects to the proxy, proxy multiplexes a small pool to RDS best general fix; ~$0.015/h per vCPU of the DB; pinning — prepared statements, session-level SET, advisory locks, LISTEN/NOTIFY force a connection to “pin” and stop multiplexing
Careful concurrency control set Lambda reserved concurrency low enough that max concurrent × connections-per-Lambda ≤ RDS capacity; reuse the connection across invocations (open it module-level, outside the handler) caps throughput; brittle as the system grows
Move to DynamoDB DynamoDB has no connection concept — it’s an HTTPS API; scales with Lambda naturally only works if the access pattern fits a key-value/document model; not a drop-in for relational workloads

In an interview, name all three and the pinning gotcha on RDS Proxy. The “right” answer is usually RDS Proxy, with reserved concurrency as a stopgap and DynamoDB as the answer if the data model allows it.

6. IAM patterns for containerized + serverless apps

How code gets AWS permissions without long-lived keys:

Workload Pattern Mechanism
Lambda execution role role attached to the function; SDK picks it up automatically
ECS task task role (app identity) + execution role (ECS infra: pull image, fetch secrets, write logs) two distinct roles — common confusion point
EC2 instance profile role attached to the instance; SDK reads it from instance metadata
EKS pod IRSA or EKS Pod Identity ServiceAccount annotated with a role ARN; SDK assumes it via OIDC. Pod Identity (newer) is simpler — no OIDC provider per cluster
CI/CD (GitHub Actions) GitHub OIDC federation GitHub’s OIDC token is exchanged for AWS temp creds via an IAM role with a trust policy scoped to the repo; no long-lived AWS_ACCESS_KEY_ID secret
Cross-account assume-role role in account B with a trust policy allowing a principal in account A; sts:AssumeRole

The unifying principle: every workload gets a role, not keys. Keys (AWS_ACCESS_KEY_ID/SECRET) are a smell — they don’t rotate, they leak, they can’t be scoped per-request. See Security_Identity_and_Compliance/02_.../02_advanced_role_patterns.md.

7. Cost gotchas reference

The bills that surprise teams:

Gotcha Why it bites Mitigation
NAT Gateway data processing ~$0.045/GB processed on top of the hourly charge — a Lambda pushing TBs to S3 from a private subnet pays this VPC gateway endpoint for S3/DynamoDB (free); interface endpoints for other services
CloudWatch Logs ingestion ~$0.50/GB ingested — verbose logging dominates the CloudWatch bill, not storage log at INFO not DEBUG in prod; sample; set retention; ship high-volume logs elsewhere
CloudWatch custom metric cardinality each unique dimension combination is a separate metric (~$0.30/metric/mo) — user_id as a dimension = millions of metrics never put high-cardinality values in dimensions; use EMF + Logs Insights for that
Cross-AZ traffic ~$0.01/GB each direction between AZs — chatty multi-AZ services pay constantly AZ-aware routing; co-locate chatty components; it’s the price of HA
S3 request costs at scale GET/PUT are cheap individually but millions of small-object requests add up; LIST is pricier batch; use larger objects; CloudFront in front; S3 Inventory instead of LIST
KMS per-request charges ~$0.03 per 10k requests — a hot path calling Decrypt per request adds up use envelope encryption (decrypt the data key once, cache it); KMS data key caching
Idle NAT Gateway / NLB hourly ~$32/mo each just for existing, before any traffic delete unused ones; consolidate; question whether you need a NAT GW per AZ in dev
Provisioned concurrency / idle Aurora pay 24/7 even at zero traffic only for latency-critical paths; Aurora Serverless v2 scales down but not to zero

8. boto3 idioms

Python-specific patterns interviewers expect:

python
import boto3
from botocore.config import Config

# Client/session reuse — create once, not per request.
# In Lambda: module-level, outside the handler.
session = boto3.Session()
config = Config(
    # adaptive = client-side rate limiting
    retries={"max_attempts": 5, "mode": "adaptive"},
    connect_timeout=3,
    read_timeout=10,
)
s3 = session.client("s3", config=config)

# Paginators — never assume one response has everything
paginator = s3.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket="my-bucket", Prefix="logs/"):
    for obj in page.get("Contents", []):
        process(obj)

# Streaming — don't load whole objects into memory
obj = s3.get_object(Bucket="b", Key="big.json")
for line in obj["Body"].iter_lines():
    process(line)

s3.upload_fileobj(file_like, "b", "key")      # streams the upload
s3.download_fileobj("b", "key", file_like)    # streams the download

# Async — for async apps, use aioboto3 / aiobotocore
# import aioboto3
# async with aioboto3.Session().client("s3") as s3:
#     await s3.put_object(...)

Key points:

  • Reuse clients/sessions — client creation is expensive (loads service models). In Lambda, module-level.
  • retries.mode="adaptive" — client-side rate limiting that backs off when AWS throttles; better than the default “legacy” mode.
  • Always paginatelist_* APIs cap results; a paginator handles continuation tokens for you.
  • Stream large payloadsupload_fileobj/download_fileobj/iter_lines instead of reading whole objects.
  • Set timeouts via botocore.config.Config — defaults are generous; a hung AWS call shouldn’t hang your request.
  • Asyncaioboto3 (wraps aiobotocore) for asyncio apps; don’t call sync boto3 in an async handler without run_in_executor.

Interview angle 6

  • “SNS vs SQS vs EventBridge?” — SQS = queue/work distribution; SNS = pub/sub fan-out; EventBridge = routing many event types to many targets with filtering, archive/replay, and SaaS integration. Add Kinesis when you need ordered high-throughput streaming with replay.
  • “Lambda vs Fargate vs EKS?” — Lambda for event-driven/spiky/short; Fargate for long-running services without node management; EKS when you’re already K8s or need the ecosystem. Cost and ops burden rise left to right.
  • “How do you connect Lambda to RDS at scale?” — RDS Proxy (watch pinning), reserved concurrency as a cap, or DynamoDB if the data model fits. The connection-storm problem is the thing being tested.
  • “How does your container/CI get AWS permissions?” — roles, never keys: task role for ECS, IRSA/Pod Identity for EKS, GitHub OIDC for CI. Long-lived access keys are a red flag.
  • “What’s a surprising AWS bill?” — NAT Gateway data processing, CloudWatch Logs ingestion, custom-metric cardinality, cross-AZ traffic. Knowing these signals real operational experience.
  • “boto3 best practices?” — reuse clients, paginate everything, adaptive retries, stream large objects, set timeouts via Config, aioboto3 for async.