Architecture Design and Implementation Built for the Cloud · Topic 2 of 11
Stateless Design, Caching and Multi-Tenancy
Externalize session state to DynamoDB, S3 or ElastiCache, choose ElastiCache or DAX caching strategies, and isolate SaaS tenants with silo or pool models.
- 5 min
- Stateless compute plus external state is the default for anything that auto scales.
- Lazy loading caches on demand and can serve stale data. Write-through keeps the cache fresh at the cost of extra writes.
- DAX is for DynamoDB only and accelerates eventually consistent reads. ElastiCache fronts any data source.
What it is
A stateless application keeps no user or session data on the compute that serves a request. Any instance, container or function can handle any request, so the fleet scales out, scales in and replaces failed nodes freely.
The state still exists, but it lives in a shared store. Caching then makes that store fast, and multi-tenancy decides how one platform keeps many customers apart.
How it works
Where to put the state
| State | Store | Why |
|---|---|---|
| Web session data | ElastiCache (Valkey or Redis OSS) or DynamoDB | Fast key lookups, with TTL to expire old sessions |
| User uploads and generated files | Amazon S3 | Durable object storage shared by every node |
| Shopping carts, user profiles | DynamoDB | Scales with no servers and supports TTL and conditional writes |
| Workflow progress | Step Functions or a database | Survives node loss mid process |
Sticky sessions on a load balancer pin a user to one node. They are a short-term fix for legacy apps, but they unbalance load and lose the session when the node dies, so the exam prefers external state.
Caching strategies
Lazy loading (cache aside) reads the cache first and fills it from the database only on a miss. It caches only what is requested, but the first read is slow and data can go stale.
Write-through updates the cache every time the database is written. Reads are always fresh, but the cache holds data that may never be read. Combine either strategy with a TTL to bound staleness and memory use.
| Need | Choose |
|---|---|
| Microsecond reads for a DynamoDB table with no code changes to query logic | DynamoDB Accelerator (DAX) |
| Cache for RDS or Aurora, session store, leaderboards, rich data types | ElastiCache for Valkey or Redis OSS |
| Simple multithreaded cache with no persistence or replication | ElastiCache for Memcached |
| Cache with no node sizing | ElastiCache Serverless |
| Static and API responses close to users | CloudFront or API Gateway caching |
DAX serves eventually consistent reads from its cache. Strongly consistent reads pass through to DynamoDB, so DAX does not help a workload that needs strongly consistent reads.
Multi-tenant isolation models
| Model | What is shared | Strength | Trade-off |
|---|---|---|---|
| Silo | Nothing: an account, VPC or database per tenant | Strongest isolation, simple per-tenant cost | Highest cost and operational overhead at scale |
| Pool | Everything: shared compute and tables with a tenant ID | Best cost efficiency and agility | Isolation must be enforced in code and policy, noisy neighbor risk |
| Bridge | Mix: shared compute with siloed data, or by tier | Balances both | More moving parts |
Many SaaS designs put premium tenants in silos and smaller tenants in a pool.
Enforcing isolation in a pool
- Tenant context from identity: store the tenant ID as a custom attribute in Amazon Cognito so it appears in the token. The backend reads the tenant from the verified token, never from a request parameter.
- Scoped credentials: assume an IAM role with session tags or a policy that limits DynamoDB access to items whose partition key matches the tenant, using the
dynamodb:LeadingKeyscondition key. - Row-level security: PostgreSQL row-level security on RDS or Aurora filters every query by a tenant setting on the session.
- Tenant-specific encryption: a separate AWS KMS key per tenant lets you revoke one tenant's access and gives a clear audit trail per tenant.
- Noisy neighbors: API Gateway usage plans and throttling per tenant protect shared capacity.
How the exam asks about this
- "Users are logged out when Auto Scaling terminates instances" -> move session state to ElastiCache or DynamoDB and remove the dependence on sticky sessions.
- "A read-heavy DynamoDB table needs microsecond latency with the least application change" -> put DAX in front of the table.
- "A SaaS provider must stop one tenant from reading another tenant's items in a shared DynamoDB table" -> derive the tenant ID from the Cognito token and use IAM policies with the
dynamodb:LeadingKeyscondition.
What to remember for the exam
- Stateless compute plus external state is the default for anything that auto scales.
- Lazy loading caches on demand and can serve stale data. Write-through keeps the cache fresh at the cost of extra writes.
- DAX is for DynamoDB only and accelerates eventually consistent reads. ElastiCache fronts any data source.
- Silo gives the strongest isolation, pool gives the lowest cost, bridge mixes them per tier or per layer.
- Take tenant context from the verified identity token, not from input the client controls.
- Per-tenant KMS keys support tenant-level revocation and audit.
Application code that adds "WHERE tenant_id = ?" is not isolation on its own. The exam expects an enforced control such as scoped IAM credentials or row-level security, so one bug cannot leak another tenant's data.
Common mistakes
Fixing lost sessions with sticky sessions. Stickiness hides the problem and still loses sessions when a node is replaced. External session storage fixes the cause.
Choosing DAX for strongly consistent reads. Those reads go straight to DynamoDB, so the cache adds cost without speed. Use DAX only where eventual consistency is acceptable.
Putting every tenant in its own account by default. Silos are simple to reason about but expensive to run for thousands of small tenants. A pool or bridge model is usually the cost-effective answer unless the scenario demands hard isolation.
Check yourself
Why do sticky sessions conflict with Auto Scaling?
A user's session lives on one node, so scale-in or a node failure loses it, and load is spread unevenly across the fleet.
When is write-through caching better than lazy loading?
When reads must not return stale data, because the cache is updated on every database write.
How can a pooled DynamoDB table stop tenants reading each other's data?
Use a role scoped by the tenant ID from the Cognito token with the dynamodb:LeadingKeys condition, so each request can only touch that tenant's partition keys.
Topic 2 of 11 in Architecture Design and Implementation Built for the Cloud
Test yourself on Architecture Design and Implementation Built for the Cloud
The full AWS SAP-C03 question bank has 850 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by AWS. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.