Skip to main content

Overview

Every distributed system has inherent challenges. This section consolidates the hard problems encountered in the User Service design and their solutions.

Challenge Summary


Data Layer Challenges

Email Uniqueness

The Problem

DynamoDB has no native UNIQUE constraint. Two concurrent requests could add the same email address.
Solution: GSI + Conditional Writes
Race Condition Handling: Even with the query, a race condition exists between check and write. The conditional expression catches this:

Primary Email Invariant

The Problem

Exactly one email must be primary per user. Changing primary requires updating two records atomically.
Solution: TransactWriteItems
Why TransactWriteItems?
  • All-or-nothing: If any operation fails, none are applied
  • Condition checks: Can verify preconditions atomically
  • Up to 100 items per transaction

Concurrent Updates

The Problem

Two requests update the same user simultaneously, causing lost updates.
Solution: Optimistic Locking

Authentication Challenges

Cognito-DynamoDB Sync

The Problem

User registers in Cognito, but DynamoDB record creation fails. User exists in Cognito but not in application.
Solution: Post-Confirmation Trigger with Retry
Compensating Action: A separate Lambda processes the DLQ and retries user creation:

Token Revocation

The Problem

JWT access tokens cannot be revoked. A suspended user’s token remains valid until expiry.
Solution: Layered Defense
1

Short TTL

Access tokens expire in 1 hour, limiting exposure window
2

Status Check

For sensitive operations, verify user status in DynamoDB
3

Global Sign-Out

Invalidate all refresh tokens when user is suspended

Event Layer Challenges

Event Ordering

The Problem

Events may arrive out of order. user.updated could arrive before user.created.
Solution: Timestamps + Idempotent Consumers
Alternative: SQS FIFO For strict ordering requirements:
  • Use SQS FIFO queue with MessageGroupId = userId
  • Trade-off: Lower throughput (3,000 messages/second with batching)

Guaranteed Delivery

The Problem

DynamoDB write succeeds, but EventBridge publish fails. Event is lost.
Solution: Transactional Outbox Pattern View full diagram
The outbox pattern adds complexity. Only use when event delivery is business-critical. For many use cases, at-least-once delivery with idempotent consumers is sufficient.

Failure Mode Analysis

A comprehensive view of what can go wrong, how we detect it, and how we recover.

Blast Radius Analysis

View full diagram

Recovery Runbooks

Symptoms: CloudWatch alarm for DLQ depth > 0Steps:
  1. Check DLQ messages for error patterns
  2. If transient (network, throttle): Redrive messages to source queue
  3. If persistent (code bug): Fix code, deploy, then redrive
  4. Monitor for successful processing
Symptoms: Users report they registered but can’t access the appSteps:
  1. Check CloudWatch for post-confirmation trigger errors
  2. Query DynamoDB for user by Cognito sub
  3. If missing: Manually create DynamoDB record or trigger reconciliation job
  4. Investigate root cause (DynamoDB throttling, code bug)
Symptoms: Security incident requiring immediate logout of all usersSteps:
  1. Cognito: AdminUserGlobalSignOut for affected users (invalidates refresh tokens)
  2. Access tokens remain valid until expiry (1 hour)
  3. For immediate block: Deploy Lambda change to check user status on every request
  4. Consider reducing access token TTL for future incidents

What Makes This “Good”

Defense in Depth

Multiple layers of protection: JWT validation, status checks, conditional writes

Explicit Trade-offs

Each solution documents what we gain and what we sacrifice

Failure Handling

Every failure mode has a recovery path: retries, DLQ, compensation

Observable

Structured logging, tracing, and metrics at every decision point

Questions to Ask

When reviewing this design, consider:
  1. What’s the blast radius? If X fails, what else breaks?
  2. Can we recover? For every failure, is there a path back to consistency?
  3. What’s the latency impact? Extra DB reads, transaction overhead, network hops
  4. Is it worth it? Does the complexity match the business criticality?