| name | dsa-system-design |
| description | Scalability fundamentals, data partitioning, caching strategies, rate limiting, distributed systems patterns, and microservices data design — bridging classical DSA concepts to real production architecture decisions. Use when: designing or reviewing distributed system components, selecting a Cosmos DB partition key, implementing rate limiting or throttling policies, choosing caching strategies, analysing microservice communication patterns, or explaining CAP theorem trade-offs in the context of the Biotrackr architecture. |
DSA System Design
When to Use
- Selecting a partition key strategy for Cosmos DB containers
- Designing or reviewing rate limiting and throttling policies
- Explaining horizontal vs vertical scaling decisions
- Analysing caching strategies (in-memory, distributed, CDN)
- Reviewing microservice communication topology for bottlenecks
- Teaching CAP theorem and consistency trade-offs in the context of production systems
- Connecting classical data structure choices (heap, hash map) to distributed systems decisions
Scalability Fundamentals
Vertical vs Horizontal Scaling
| Approach | Mechanism | Limit |
|---|
| Vertical | Add CPU/RAM to one machine | Hardware ceiling |
| Horizontal | Add more machines / replicas | Near-unlimited with stateless design |
Biotrackr context: Azure Container Apps scales horizontally — new container replicas
are spun up under load. Services must be stateless (no local in-memory state that needs
to be shared) for horizontal scaling to work correctly. Cosmos DB handles horizontal scaling
via partitioning.
Latency Reference Numbers
| Operation | Approximate Latency |
|---|
| L1 cache read | ~1 ns |
| Memory (RAM) read | ~100 ns |
| SSD read | ~100 µs |
| Network round-trip (same region) | ~1 ms |
| Cosmos DB query (indexed, right partition) | ~5–10 ms |
| Cross-region replication lag | ~50–150 ms |
Teaching angle: A cache hit that avoids a Cosmos DB query saves ~5–10 ms per call.
At 100 calls/sec, that is 500–1000 ms of latency removed from the system per second.
Data Partitioning (Cosmos DB Context)
Why Partitioning Matters
Cosmos DB distributes data across physical partitions by hash of the partition key. All
requests for a given key are routed to the same physical partition — O(1) dispatch.
Partition Key Selection Criteria
| Criterion | Goal |
|---|
| High cardinality | Spread data evenly across partitions |
| Even access distribution | Avoid hot partitions (one partition gets all traffic) |
| Aligns with query patterns | Most queries filter by the partition key |
| Does not change after write | Partition key is immutable once written |
Biotrackr Partition Key Examples
| Container | Partition Key | Rationale |
|---|
| Records | /documentType | Biotrackr services share one records container; partitioning by document type groups related documents and matches the current container design |
| Conversations | /sessionId | Each chat session is isolated; session-scoped reads and deletes naturally target a single partition |
Teaching angle: In Biotrackr, the shared records container is partitioned by
/documentType, not /date. That reflects the current service design, where activity,
sleep, food, and vitals documents are separated by type inside one container. For
conversations, /sessionId is the right partition key because history is loaded,
appended, and deleted per session. If query patterns or tenant boundaries change in the
future, re-evaluate the partition key against actual access patterns and hot-partition risk
rather than assuming a date-based strategy.
Hot Partition Anti-Pattern
A hot partition occurs when a large fraction of requests target one partition key value.
Symptoms: throttling on one partition key despite low overall RU consumption.
Fix: Increase partition key cardinality by appending a suffix (synthetic sharding):
date_shard where shard = hash(record_id) % N.
Caching Strategies
Cache Patterns
| Pattern | Description | Use Case |
|---|
| Cache-aside (lazy) | App checks cache first; on miss, loads DB and populates cache | Read-heavy, tolerable staleness |
| Write-through | Write to cache and DB simultaneously | Read-heavy, strong consistency needed |
| Write-behind | Write to cache; async flush to DB | Write-heavy, eventual consistency acceptable |
| Read-through | Cache handles DB load on miss transparently | Simpler app logic |
Biotrackr Anchor — CachingMcpToolWrapper
CachingMcpToolWrapper in Biotrackr.Chat.Api implements cache-aside for MCP tool
calls. Cache key = tool name + parameter hash; TTL varies by tool type.
Teaching angle: The cache is a hash map with expiry. Cache-aside is the most common
pattern because it is simple and degrades gracefully: on cache miss, the application falls
back to the source of truth. Write-through and write-behind add complexity only when
write performance is the bottleneck.
Cache Invalidation Strategies
- TTL (time-to-live): simplest — data expires after a fixed duration
- Event-driven invalidation: write to DB triggers cache eviction (more complex)
- Versioned keys: append a version/hash to the key; old entries become unreachable
Rate Limiting and Throttling
Rate Limiting Algorithms
| Algorithm | Description | Burst Handling |
|---|
| Token bucket | Bucket refills at rate r; each request consumes 1 token | Allows bursts up to bucket size |
| Leaky bucket | Requests exit at fixed rate regardless of input rate | Smooths bursts |
| Fixed window counter | Count requests per time window; reset at window boundary | Boundary burst problem |
| Sliding window log | Track request timestamps; evict expired | Accurate, memory-intensive |
Biotrackr Anchor — MCP Server Rate Limiting
Biotrackr.Mcp.Server enforces 100 requests/minute per IP, queue size 10 using ASP.NET
Core rate limiting middleware. The service configures a global partitioned rate limiter
with a fixed window counter per client IP: each IP can make up to 100 requests in a
one-minute window, the counter resets at the next window boundary, and up to 10
additional requests can wait in the queue.
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
{
var ipAddress = httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
return RateLimitPartition.GetFixedWindowLimiter(
partitionKey: ipAddress,
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
QueueLimit = 10
});
});
Biotrackr Anchor — APIM Throttling Policies
Azure API Management applies quota and rate limit policies at the gateway layer before
requests reach the backend services. This is a distributed rate limiter: APIM counts
requests centrally and rejects early, protecting backend services from overload.
Teaching angle: A rate limiter is a leaky bucket or token bucket implemented as
a counter with a TTL. In a distributed system, the counter must be stored in a shared
store (Redis, APIM internal store) rather than in-process memory.
Microservices Data Patterns
Database-per-Service
Biotrackr follows a modified database-per-service pattern: Activity, Sleep, Food, and
Vitals share a single Cosmos DB records container within a shared account rather
than provisioning one container per domain. The container is partitioned by
/documentType, and each domain writes and queries its own document types. This
gives logical domain isolation within the shared container.
Trade-offs:
- Reduces infrastructure overhead by reusing one container for multiple domains
- Keeps domain records separated logically through
documentType-based partitioning
- Cross-domain queries require explicit filtering because unrelated document types
coexist in the same container
- Domains share container-level throughput and storage characteristics instead of scaling
completely independently
Eventual Consistency and the CAP Theorem
CAP theorem: a distributed system can guarantee at most two of:
- Consistency — every read sees the most recent write
- Availability — every request gets a response (possibly stale)
- Partition tolerance — the system continues despite network splits
Cosmos DB supports multiple consistency levels, from strong through eventual,
letting systems choose the trade-off between read freshness, latency, and availability
characteristics. Biotrackr is configured to use session consistency — reads see
writes from the same logical session, providing a practical balance of performance and
correctness for user-scoped workloads.
Event-Driven Data Flow
Biotrackr's ingest services (Activity.Svc, Sleep.Svc, Food.Svc, Vitals.Svc) write to
Cosmos DB asynchronously. Downstream APIs read from these containers. This is an
event-sourcing-lite pattern: the service writes the fact, and readers query when needed.
API Gateway Pattern (APIM)
APIM acts as the single entry point for all external requests. From a DSA perspective,
APIM is a routing table — an O(1) lookup from request path to backend service — plus a
policy pipeline (a linked list of processing steps: auth → rate-limit → transform → route).
Container Apps Scaling Triggers
Azure Container Apps scales on:
- HTTP concurrency — scale out when concurrent requests exceed threshold
- CPU/memory — scale out on resource saturation
- KEDA-based — scale on queue depth, Cosmos DB change feed lag, custom metrics
Teaching angle: Scaling triggers are threshold functions — binary decisions based
on a measured value crossing a boundary. The correct threshold depends on the latency
target and per-replica capacity, which requires profiling (load testing) rather than guessing.