Based on the SAP-C02 exam guideReport an error
When microservices exceed a certain scale, the data contract between client and server becomes a major operational cost. When a mobile app needs to display "order details + user + shipping address + payment method" on one screen, REST requires 4-5 simultaneous API calls that the client must combine. As screens change, backend APIs change too. The answer to breaking this coupling is GraphQL, and AWS's managed service for it is AppSync.
Meanwhile, inter-service messaging long used SQS, SNS, and Kinesis as standards. They seem similar but are fundamentally different. SQS is "single-consume queue," SNS is "fan-out pub/sub," and Kinesis is "replayable stream." Exam scenarios distinguishing these three appear across domains. Today we examine both AppSync's GraphQL model and the internal models of these three messaging services.
Two chronic REST API problems:
GraphQL (Facebook, released 2015) solves this as a client query language. When clients declare needed fields and their relationships, the server returns exactly that much.
query GetOrderScreen($id: ID!) {
order(id: $id) {
id
items { product { name price } quantity }
customer { name email }
shippingAddress { street city }
payment { method last4 }
}
}One query combines 5 resources in one response. When clients change screens, only the query changes; backend APIs stay the same.
💡 Related Theory: GraphQL defines type systems via SDL (Schema Definition Language), and queries are declarative traversals of those types. Essentially a dependency-typed query language. Trade-offs: REST's simple HTTP caching (URL-based) doesn't work; query complexity attacks (clients paralyzing backends with expensive queries) require depth limits and cost analysis. Apollo's Production-Ready GraphQL (O'Reilly) is the best operational reference.
AppSync provides a managed GraphQL server while mapping resolvers directly to AWS data sources (DynamoDB, Lambda, HTTP, RDS Aurora Serverless, OpenSearch, EventBridge). Resolvers are written in VTL (Velocity Template Language) or JavaScript.
Key features:
Traditional BFF (Backend For Frontend) is GraphQL server → Lambda → DynamoDB (3-hop). AppSync direct resolver cuts it to GraphQL server → DynamoDB (1-hop). Lambda cost, cold start, and operational burden vanish.
// JavaScript resolver (2022~)
export function request(ctx) {
return {
operation: "GetItem",
key: util.dynamodb.toMapValues({ id: ctx.args.id })
};
}
export function response(ctx) {
return ctx.result;
}This resolver calls DynamoDB GetItem directly. Response within 100ms.
🔍 Deeper Dive: AppSync resolvers historically used VTL (Velocity Template Language), Apache Velocity-based old template language with tough debugging and high learning curve. In 2022, APPSYNC_JS runtime added support for ES6 JavaScript, and nearly all new projects now use JS resolvers. VTL maintained only for legacy compatibility.
AppSync subscriptions implement GraphQL's standard subscription type over WebSocket. When clients issue subscription onOrderUpdated($id: ID!) queries, WebSocket connections persist, and servers auto-push on mutation.
subscription OnOrderUpdate($id: ID!) {
onOrderUpdated(id: $id) {
id
status
updatedAt
}
}On mutation, AppSync pushes to all matching subscribers. Standard pattern for mobile chat, real-time alerts, collaborative tools (Figma-style).
📚 Case Study: 2020-2022 during COVID, food delivery app GrubHub implemented "real-time order tracking" with AppSync subscriptions. When delivery drivers update location, customer apps receive instantly over WebSocket. Previously long polling + Lambda cost exploded with 1M concurrent users. AppSync has very low per-WebSocket cost, achieving 80% savings.
| Mode | Use Case |
|---|---|
| API Key | Public or simple demo, with expiration (max 1 year) |
| IAM | Service-to-service calls, SigV4 signing |
| Cognito User Pool | User authentication + group-based authorization |
| OIDC | External IdP (Google, Okta, etc.) |
| Lambda Authorizer | Custom auth (legacy JWT, etc.) |
Multiple modes simultaneously on one API enabled. Example: anonymous queries use API Key, user data changes use Cognito.
⚠️ Trap: AppSync API Key is identification not authentication. Useful for throttling and analytics, but don't treat as security boundary. Production nearly always requires Cognito or IAM.
SQS is the simplest message queue. Publishers put messages in a queue; consumers poll, process, and delete. One message typically processes by one consumer (even when N workers poll, one message goes to one worker).
| Characteristic | Standard | FIFO |
|---|---|---|
| Throughput | Nearly unlimited (tens of thousands TPS) | 300 TPS (3,000 with batching) |
| Order Guarantee | None (best-effort) | Guaranteed within MessageGroupId |
| Duplicates | Possible (at-least-once) | 5-min deduplication (exactly-once-ish) |
| Use Case | General task queue | Payments, accounting, order-critical |
| Naming | Any | Must end with .fifo |
FIFO's throughput limit (default 300) extends via High Throughput FIFO option with partition-level scaling to thousands TPS. SAP exams map the distinction and "exactly once" keyword.
💡 Related Theory: In distributed systems, "exactly-once delivery" is theoretically impossible due to FLP impossibility (1985) + Two Generals problem. SQS FIFO's "exactly-once processing" precisely means "idempotent processing within 5-min deduplication window." If a consumer dies right before ack, redelivery causes duplicate. Application-level idempotency (e.g.,
idempotencyKey) still required.
When a consumer receives a message, visibility timeout (default 30 seconds) hides it from other consumers. If processing completes and delete is called, it leaves the queue; on failure, after timeout, it reappears for other workers.
Baseline: average message processing time × 1.5–2. Too short causes processing duplicates; too long delays retry of failed messages.
Default ReceiveMessage is short polling; empty queue returns immediately. Workers polling every second = 86,400 empty calls daily. SQS charges per API call, costing tens of dollars monthly.
Long polling (WaitTimeSeconds=20) waits up to 20 seconds for messages. Empty calls drop to 1/20, message arrival latency nearly zero (instant push). Production standard.
Messages exceeding maxReceiveCount move to DLQ (Dead Letter Queue). Usually set to 5-10. DLQ is separate SQS, and operators analyze, reprocess, or delete.
Causes of DLQ messages:
📚 Case Study: In 2018, a music streaming company received alert "5,000 messages in payment processing SQS DLQ." Investigation found a days-old payment gateway SDK update that rejected a specific card number format. After fix deployment, redrive moved all 5,000 from DLQ to main queue for normal processing. Without DLQ, messages would have been lost forever. Common SAP exam pattern: "isolate failed messages + maxReceiveCount" → DLQ.
SNS Publishes to a Topic, instantly pushing to all Subscribers. One message reaches N recipients simultaneously.
Topic-published messages don't reach all subscribers; filter policies route portions to each.
// SQS subscription FilterPolicy
{ "type": ["high_value"], "region": ["us-east-1"] }This subscription receives only messages with type=high_value AND region=us-east-1. Multiple subscribers on same Topic with different filters provide both fan-out and selective routing.
Added 2020. SQS FIFO subscribers only; MessageGroupId guarantees order + deduplication while fanning out.
[Publisher FIFO] → [SNS FIFO Topic] ─┬─► SQS FIFO A (analytics)
├─► SQS FIFO B (accounting)
└─► SQS FIFO C (audit)
Scenarios needing order and fan-out in payment/accounting domains.
SNS Topic policy permitting other account ARNs enables cross-account subscriptions. Similar to EventBridge for multi-account fan-out. Cross-region automatic if subscriber endpoint differs.
⚠️ Trap: "SNS doesn't persist messages" appears frequently in exams. SNS publishes immediately and doesn't store. Subscribers temporarily down trigger SNS retry, but after retry limit, message loss occurs. For persistence, SNS → SQS subscription buffers to SQS, or EventBridge Archive preserves.
[Publisher]
│ Publish
▼
[SNS Topic: order-events]
│
├──Filter:type=high──► [SQS high-value-queue] → Worker (5 retries) → DLQ
├──Filter:type=low──── [SQS low-value-queue] → Worker
└──Lambda(analytics) direct subscription
SQS subscriptions fan-out because:
🎯 Scenario: "An OTT service emits video upload complete event triggering 4 downstream: (1) transcoding (2) thumbnail (3) search indexing (4) audit log. Best architecture?" — Answer: SNS Topic + 4 SQS subscriptions. Each downstream polls at its pace + failed messages isolated to DLQ. EventBridge possible but SNS cheapest for simple 4-way fan-out.
Kinesis fundamentally differs from SQS. SQS: "consume message once, then delete." Kinesis: "keep stream flowing time-ordered; multiple consumers each read by offset."
Streams divide into Shards (partitions). Each shard:
Insufficient throughput → split shards; excess → merge. Or use On-Demand mode (2021) for auto-scaling.
Producers specify partition key when sending records. Same key → same shard → order guaranteed within shard. E.g., user_id as key guarantees one user's events ordered.
🔍 Deeper Dive: Partition key choice is critical. Poor selection creates hot shard. Example:
regionas key concentrates us-east-1 traffic in one shard. Generally pick keys with high cardinality like user_id, session_id. AWS MD5-hashes partition keys to shard mapping; uniform key distribution auto-balances.
Multiple consumers simultaneously reading same stream → EFO. Biggest Kinesis/SQS difference.
| Dimension | SQS | Kinesis |
|---|---|---|
| Model | Queue (single consume) | Stream (replayable) |
| Retention | 14 days (default 4) | 24h~365d |
| Ordering | FIFO only | Guaranteed within shard |
| Multiple Consumers | No (one message, one worker) | Yes (each reads by offset) |
| Replay | No (deletes after consume) | Yes (reread possible) |
| Use Case | Task queue, background work | Real-time analytics, time-series, multi-consume |
| Pricing | Per request | Shard-hour + PUT payload unit |
🎯 Scenario: "Ad platform receives user click events for real-time analytics (hourly aggregation) AND retains 7 days raw data for ML retraining, replayable. Best?" — Answer: Kinesis Data Streams (7-day retention) + Lambda consumer (real-time aggregation) + Firehose consumer (S3 storage, EFO). SQS can't do multi-consumer and replay. EFO lets consumers run without throughput impact.
Firehose sounds like Kinesis Data Streams but differs fundamentally. It auto-delivers to destination.
Firehose lacks shard management, partition key, offset concepts. Just receives data → buffer + transform + deliver. ELT pipeline standard entry.
Managed Apache Kafka version. Standard Kafka clients usable. Kinesis fork:
MSK Serverless (2022) even manages cluster. Throughput-based billing.
Connect SQS, Kinesis, DDB Streams, MQ, MSK as Lambda sources. Lambda handles own poll + invoke.
Key parameters:
⚠️ Trap: ESM's ReportBatchItemFailures appears frequently in exams. Default: "one failure in batch → retry full batch." Without idempotence, causes duplicate processing. Enable ReportBatchItemFailures; function returns failed message ID list, Lambda retries only those and acks others.
Modern EDA rarely ends with one tool. Common combo:
[Mobile App] ─GraphQL─► [AppSync]
│ Mutation
▼
[DynamoDB] ─Streams─► [EventBridge Pipes]
│ Filter
▼
[EventBridge Bus]
│ Rule
┌────────────────────┼────────────────────┐
▼ ▼ ▼
[Step Functions] [SNS Topic] [Kinesis Firehose]
(workflow) (fan-out) (S3 storage, ML)
│
┌───────┴───────┐
▼ ▼
[SQS analytics] [SQS alerts] → Worker
AppSync is client interface, DDB Streams is source-of-truth change event, EventBridge distributes, Step Functions/SNS/Kinesis each handle roles.
📚 Case Study: In 2023, Goldman Sachs' internal trading platform presented AppSync receiving client GraphQL, mutations flowing DDB Streams → EventBridge to fan-out risk engine, audit, downstream alerts. Transitioning from monolithic REST + direct call chains to microservice EDA doubled new feature velocity.
GraphQL, SQS, SNS, Kinesis, MSK, Firehose all move data but differ fundamentally:
Exam keyword mapping:
Next day (week recap) consolidates week 8 with 10 comprehensive scenarios.
Click a choice to reveal the answer and explanation.
Question 1
A mobile app shows 4 resources — user, order, shipping address, and payment — on one screen. The client must receive only the fields it needs and also receive real-time order status pushes. Cognito authentication is required. What is the most appropriate backend?
Question 2
A payment processing queue — ordering guarantee + deduplication (to prevent processing the same payment twice) + up to 1,000 TPS. What is the most appropriate configuration?
Question 3
One event is fanned out to 5 downstream consumers (analytics, CRM, shipping, notifications, audit), each processing at its own speed. Each downstream needs retry + isolation on failure. What is the most appropriate configuration?
Question 4
Messages that fail processing are isolated permanently after 5 attempts so that operators can analyze them. What configuration should you use?
Question 5
An advertising platform aggregates a clickstream in real time while retaining 7 days of raw data for ML training, with reprocessing possible. Two consumers must process simultaneously without affecting each other's latency.
Question 6
All messages are published to a single SNS Topic, and each subscriber should receive only a specific attribute (type=high). Without writing code.
Question 7
In a Lambda Event Source Mapping (SQS source) with batch size = 10, the default behavior when one message fails is to retry the whole batch. How can you retry only the failed messages?