System Design Interview Questions with Answers
Most Asked System Design Interview Questions for Software Engineer Roles
Introduction
System design is the process of designing scalable, reliable, secure, and maintainable software systems that can handle real-world business and technical requirements. This comprehensive guide presents 100+ carefully curated System Design interview questions and answers, covering everything from fundamental concepts to advanced distributed-system architecture. You'll learn about scalability, availability, reliability, load balancing, caching, databases, database sharding, replication, partitioning, message queues, API design, microservices, monolithic architecture, distributed systems, CAP theorem, consistency, fault tolerance, rate limiting, authentication, authorization, storage systems, CDNs, monitoring, and system performance. Whether you're preparing for a Software Engineer, Senior Software Engineer, Backend Developer, Full Stack Developer, DevOps Engineer, or System Design interview at a product-based company, this question bank will help you understand how to approach real-world architecture problems. You'll learn how to analyze requirements, estimate system capacity, identify bottlenecks, choose the right technologies, and design systems that can scale efficiently. From designing URL shorteners, notification systems, chat applications, social media platforms, payment systems, file storage services, search systems, and ride-sharing applications to handling millions of users and requests, these System Design interview questions will strengthen your architectural thinking and prepare you for real-world technical interviews. Start practicing now and build the confidence to design scalable systems.
Why Learn System Design?
- Essential skill for senior software engineering and backend development interviews
- Learn how to design scalable, reliable, and high-performance software systems
- Understand core concepts such as load balancing, caching, replication, sharding, and partitioning
- Master distributed systems concepts including CAP theorem, consistency, availability, and fault tolerance
- Learn how to choose databases, message queues, storage systems, and other components based on system requirements
- Develop the ability to analyze scalability, performance, security, reliability, and system bottlenecks
- Prepare for real-world system design problems commonly discussed in technical interviews
- Improve architectural thinking and make better technology and design decisions
Most Asked System Design Interview Questions
System Design is the process of defining the architecture, components, modules, interfaces, and data for a system to satisfy specified requirements.
- Scalability: Handle growth in users and data
- Reliability: System availability and fault tolerance
- Performance: Response time and throughput
- Security: Protect data and resources
- Cost: Infrastructure and operational costs
// System Design - Load Balancer Example
// Nginx configuration for load balancing
http {
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=2;
server backend3.example.com weight=1;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
// Round Robin Load Balancer implementation
class LoadBalancer {
private servers: string[];
private currentIndex: number = 0;
constructor(servers: string[]) {
this.servers = servers;
}
getNextServer(): string {
const server = this.servers[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.servers.length;
return server;
}
}Load balancing distributes incoming network traffic across multiple servers to ensure reliability, availability, and performance.
- Round Robin: Distributes requests sequentially
- Least Connections: Sends to server with fewest connections
- IP Hash: Consistent routing based on client IP
- Weighted: Assigns weights to servers
- Health checks: Monitor server health
// System Design - Database Sharding
// Consistent hashing for sharding
class ConsistentHash {
private ring: SortedMap<number, string>;
private virtualNodes: number;
constructor(nodes: string[], virtualNodes: number = 150) {
this.ring = new SortedMap();
this.virtualNodes = virtualNodes;
for (const node of nodes) {
this.addNode(node);
}
}
addNode(node: string): void {
for (let i = 0; i < this.virtualNodes; i++) {
const hash = this.hash(`${node}-${i}`);
this.ring.set(hash, node);
}
}
getNode(key: string): string {
const hash = this.hash(key);
const node = this.ring.ceilingEntry(hash);
return node ? node.value : this.ring.firstEntry().value;
}
private hash(key: string): number {
// Hash function implementation
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash;
}
return hash;
}
}Caching stores frequently accessed data in high-speed storage to reduce latency and improve performance.
- Cache-Aside: Application manages cache
- Read-Through: Cache reads from database
- Write-Through: Writes to cache and database
- Write-Behind: Asynchronous write
- Cache invalidation: Removing stale data
// System Design - Caching Strategy
// Cache aside pattern implementation
class CacheAside<K, V> {
private cache: Map<K, V>;
private db: Database;
private ttl: number;
constructor(db: Database, ttl: number = 3600) {
this.cache = new Map();
this.db = db;
this.ttl = ttl;
}
async get(key: K): Promise<V | null> {
// Check cache first
if (this.cache.has(key)) {
return this.cache.get(key);
}
// Cache miss - get from DB
const value = await this.db.get(key);
if (value) {
this.cache.set(key, value);
setTimeout(() => this.cache.delete(key), this.ttl * 1000);
}
return value;
}
async set(key: K, value: V): Promise<void> {
await this.db.set(key, value);
this.cache.set(key, value);
}
invalidate(key: K): void {
this.cache.delete(key);
}
}
// Write-through cache
class WriteThroughCache<K, V> {
private cache: Map<K, V>;
private db: Database;
constructor(db: Database) {
this.cache = new Map();
this.db = db;
}
async get(key: K): Promise<V | null> {
return this.cache.get(key) || null;
}
async set(key: K, value: V): Promise<void> {
await this.db.set(key, value);
this.cache.set(key, value);
}
}A message queue enables asynchronous communication between services, providing decoupling and reliability.
- Producer: Sends messages
- Consumer: Receives messages
- Queue: Stores messages
- Publish/Subscribe: Broadcast messages
- Dead Letter Queue: Failed message handling
// System Design - Message Queue
// Simple message queue implementation
class MessageQueue {
private queues: Map<string, Queue>;
private subscribers: Map<string, ((message: any) => void)[]>;
constructor() {
this.queues = new Map();
this.subscribers = new Map();
}
createQueue(name: string): void {
this.queues.set(name, {
messages: [],
consumers: []
});
this.subscribers.set(name, []);
}
publish(queueName: string, message: any): void {
const queue = this.queues.get(queueName);
if (!queue) {
throw new Error(`Queue ${queueName} not found`);
}
queue.messages.push(message);
this.notifySubscribers(queueName, message);
// Notify consumers
for (const consumer of queue.consumers) {
consumer(message);
}
}
subscribe(queueName: string, callback: (message: any) => void): void {
const subscribers = this.subscribers.get(queueName);
if (subscribers) {
subscribers.push(callback);
}
}
private notifySubscribers(queueName: string, message: any): void {
const subscribers = this.subscribers.get(queueName);
if (subscribers) {
for (const subscriber of subscribers) {
subscriber(message);
}
}
}
}
// Kafka-like message broker
class MessageBroker {
private topics: Map<string, Topic>;
constructor() {
this.topics = new Map();
}
createTopic(name: string, partitions: number): void {
this.topics.set(name, {
partitions: Array.from({ length: partitions }, (_, i) => ({
id: i,
messages: [],
offset: 0
})),
consumers: []
});
}
publish(topicName: string, key: string, message: any): void {
const topic = this.topics.get(topicName);
if (!topic) {
throw new Error(`Topic ${topicName} not found`);
}
const partition = this.getPartition(key, topic.partitions.length);
topic.partitions[partition].messages.push({
key,
value: message,
timestamp: Date.now()
});
}
private getPartition(key: string, numPartitions: number): number {
return Math.abs(this.hash(key)) % numPartitions;
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash;
}
return hash;
}
}A distributed lock coordinates access to shared resources across multiple nodes in a distributed system.
- Redlock: Redis-based distributed lock
- ZooKeeper: Coordination service
- Lease: Time-based lock
- Fencing token: Prevent conflicting writes
- TTL: Time-to-live for locks
// System Design - Distributed Lock
// Distributed lock using Redis
class DistributedLock {
private redis: Redis;
private lockKey: string;
private lockValue: string;
private ttl: number;
constructor(redis: Redis, lockKey: string, ttl: number = 30000) {
this.redis = redis;
this.lockKey = lockKey;
this.lockValue = `${process.pid}-${Date.now()}`;
this.ttl = ttl;
}
async acquire(): Promise<boolean> {
const result = await this.redis.set(
this.lockKey,
this.lockValue,
'NX',
'PX',
this.ttl
);
return result === 'OK';
}
async release(): Promise<void> {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
await this.redis.eval(script, 1, this.lockKey, this.lockValue);
}
async execute<T>(fn: () => Promise<T>): Promise<T | null> {
const acquired = await this.acquire();
if (!acquired) {
return null;
}
try {
return await fn();
} finally {
await this.release();
}
}
}
// Redlock algorithm implementation
class Redlock {
private redisInstances: Redis[];
private retryCount: number;
private retryDelay: number;
constructor(redisInstances: Redis[], retryCount: number = 3, retryDelay: number = 100) {
this.redisInstances = redisInstances;
this.retryCount = retryCount;
this.retryDelay = retryDelay;
}
async lock(resource: string, ttl: number): Promise<Lock | null> {
const lockValue = `${process.pid}-${Date.now()}`;
let successCount = 0;
const quorum = Math.floor(this.redisInstances.length / 2) + 1;
for (let attempt = 0; attempt < this.retryCount; attempt++) {
const results = await Promise.all(
this.redisInstances.map(async (redis) => {
const result = await redis.set(
resource,
lockValue,
'NX',
'PX',
ttl
);
return result === 'OK';
})
);
successCount = results.filter(r => r).length;
if (successCount >= quorum) {
return new Lock(resource, lockValue, ttl, this.redisInstances);
}
await this.sleep(this.retryDelay);
}
return null;
}
}Rate limiting controls the amount of incoming and outgoing traffic to prevent system overload and ensure fair usage.
- Token Bucket: Tokens refill over time
- Leaky Bucket: Constant rate output
- Sliding Window: Request count per time window
- Fixed Window: Count per fixed time period
- Distributed: Rate limiting across nodes
// System Design - Rate Limiter
// Token bucket rate limiter
class TokenBucketRateLimiter {
private capacity: number;
private tokens: number;
private refillRate: number;
private lastRefill: number;
constructor(capacity: number, refillRate: number) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate;
this.lastRefill = Date.now();
}
allowRequest(): boolean {
this.refill();
if (this.tokens >= 1) {
this.tokens -= 1;
return true;
}
return false;
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
const tokensToAdd = elapsed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
// Sliding window rate limiter
class SlidingWindowRateLimiter {
private windowSize: number;
private maxRequests: number;
private requests: number[];
constructor(windowSize: number, maxRequests: number) {
this.windowSize = windowSize;
this.maxRequests = maxRequests;
this.requests = [];
}
allowRequest(): boolean {
const now = Date.now();
this.requests = this.requests.filter(
timestamp => now - timestamp < this.windowSize
);
if (this.requests.length < this.maxRequests) {
this.requests.push(now);
return true;
}
return false;
}
}
// Distributed rate limiter using Redis
class DistributedRateLimiter {
private redis: Redis;
private key: string;
private maxRequests: number;
private windowSize: number;
constructor(redis: Redis, key: string, maxRequests: number, windowSize: number) {
this.redis = redis;
this.key = key;
this.maxRequests = maxRequests;
this.windowSize = windowSize;
}
async allowRequest(): Promise<boolean> {
const now = Date.now();
const windowStart = now - this.windowSize;
// Remove old requests
await this.redis.zremrangebyscore(this.key, 0, windowStart);
// Count requests in current window
const count = await this.redis.zcard(this.key);
if (count < this.maxRequests) {
await this.redis.zadd(this.key, now.toString(), now.toString());
await this.redis.expire(this.key, this.windowSize / 1000);
return true;
}
return false;
}
}An API Gateway acts as a single entry point for client applications, providing routing, authentication, rate limiting, and other cross-cutting concerns.
- Routing: Forward requests to services
- Authentication: JWT, OAuth2 verification
- Rate Limiting: Throttle requests
- Caching: Cache responses
- Monitoring: Logging and metrics
// System Design - API Gateway
// API Gateway implementation
class APIGateway {
private routes: Map<string, Route>;
private authService: AuthService;
private rateLimiter: RateLimiter;
constructor(authService: AuthService, rateLimiter: RateLimiter) {
this.routes = new Map();
this.authService = authService;
this.rateLimiter = rateLimiter;
}
registerRoute(path: string, service: Service): void {
this.routes.set(path, {
path,
service,
methods: ['GET', 'POST', 'PUT', 'DELETE']
});
}
async handleRequest(request: Request): Promise<Response> {
// Authentication
if (!await this.authService.authenticate(request)) {
return new Response('Unauthorized', { status: 401 });
}
// Rate limiting
if (!await this.rateLimiter.allowRequest(request.ip)) {
return new Response('Too Many Requests', { status: 429 });
}
// Route matching
const route = this.matchRoute(request.url);
if (!route) {
return new Response('Not Found', { status: 404 });
}
// Request transformation
const transformedRequest = this.transformRequest(request);
// Forward to service
const response = await route.service.handle(transformedRequest);
// Response transformation
return this.transformResponse(response);
}
private matchRoute(url: string): Route | undefined {
for (const [path, route] of this.routes) {
if (url.startsWith(path)) {
return route;
}
}
return undefined;
}
private transformRequest(request: Request): Request {
// Add headers, modify body, etc.
return request;
}
private transformResponse(response: Response): Response {
// Modify response, add headers, etc.
return response;
}
}
// Service registry
class ServiceRegistry {
private services: Map<string, ServiceInstance[]>;
private healthCheckInterval: number;
constructor(healthCheckInterval: number = 30000) {
this.services = new Map();
this.healthCheckInterval = healthCheckInterval;
}
register(serviceName: string, instance: ServiceInstance): void {
if (!this.services.has(serviceName)) {
this.services.set(serviceName, []);
}
this.services.get(serviceName).push(instance);
}
deregister(serviceName: string, instanceId: string): void {
const instances = this.services.get(serviceName);
if (instances) {
this.services.set(
serviceName,
instances.filter(i => i.id !== instanceId)
);
}
}
getService(serviceName: string): ServiceInstance | null {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
return null;
}
// Load balancing - round robin
return instances[0]; // Simplified
}
healthCheck(): void {
// Check health of all instances
for (const [name, instances] of this.services) {
const healthyInstances = instances.filter(i => i.isHealthy());
if (healthyInstances.length === 0) {
// All instances unhealthy
console.warn(`No healthy instances for service: ${name}`);
}
this.services.set(name, healthyInstances);
}
}
}A circuit breaker prevents cascading failures by stopping requests to a failing service, allowing it to recover.
- Closed: Normal operation
- Open: Failing, requests fail fast
- Half-Open: Testing recovery
- Timeout: Reset after timeout
- Fallback: Alternative response
// System Design - Circuit Breaker
// Circuit breaker pattern implementation
class CircuitBreaker {
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN';
private failureThreshold: number;
private timeout: number;
private failures: number;
private lastFailureTime: number;
private retryTimeout: number;
constructor(failureThreshold: number = 5, timeout: number = 30000, retryTimeout: number = 1000) {
this.state = 'CLOSED';
this.failureThreshold = failureThreshold;
this.timeout = timeout;
this.retryTimeout = retryTimeout;
this.failures = 0;
this.lastFailureTime = 0;
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
if (this.state === 'HALF_OPEN') {
this.state = 'CLOSED';
this.failures = 0;
}
}
private onFailure(): void {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
this.scheduleRetry();
}
}
private scheduleRetry(): void {
setTimeout(() => {
this.state = 'HALF_OPEN';
}, this.retryTimeout);
}
}
// Circuit breaker with fallback
class CircuitBreakerWithFallback<T> {
private breaker: CircuitBreaker;
private fallback: () => T;
constructor(breaker: CircuitBreaker, fallback: () => T) {
this.breaker = breaker;
this.fallback = fallback;
}
async execute(fn: () => Promise<T>): Promise<T> {
try {
return await this.breaker.execute(fn);
} catch (error) {
return this.fallback();
}
}
}Event sourcing stores all changes to application state as a sequence of events, enabling audit trails and state reconstruction.
- Event Store: Append-only log
- Event Replay: Reconstruct state
- Snapshots: Performance optimization
- Event Versioning: Handle schema evolution
- CQRS: Command Query Responsibility Segregation
// System Design - Event Sourcing
// Event sourcing implementation
interface Event {
id: string;
type: string;
data: any;
timestamp: number;
version: number;
}
class EventStore {
private events: Event[];
private aggregates: Map<string, any>;
constructor() {
this.events = [];
this.aggregates = new Map();
}
appendEvent(aggregateId: string, event: Event): void {
this.events.push(event);
this.applyEvent(aggregateId, event);
}
getEvents(aggregateId: string): Event[] {
return this.events.filter(e => e.aggregateId === aggregateId);
}
getAggregateState(aggregateId: string): any {
return this.aggregates.get(aggregateId);
}
private applyEvent(aggregateId: string, event: Event): void {
let aggregate = this.aggregates.get(aggregateId) || {};
switch (event.type) {
case 'CREATED':
aggregate = { ...aggregate, ...event.data };
break;
case 'UPDATED':
aggregate = { ...aggregate, ...event.data };
break;
case 'DELETED':
aggregate = null;
break;
default:
break;
}
this.aggregates.set(aggregateId, aggregate);
}
}
// Snapshot implementation
class SnapshotStore {
private snapshots: Map<string, { state: any, version: number, timestamp: number }>;
private snapshotFrequency: number;
constructor(snapshotFrequency: number = 100) {
this.snapshots = new Map();
this.snapshotFrequency = snapshotFrequency;
}
saveSnapshot(aggregateId: string, state: any, version: number): void {
this.snapshots.set(aggregateId, {
state,
version,
timestamp: Date.now()
});
}
getSnapshot(aggregateId: string): { state: any; version: number } | null {
const snapshot = this.snapshots.get(aggregateId);
if (!snapshot) {
return null;
}
return {
state: snapshot.state,
version: snapshot.version
};
}
shouldSnapshot(version: number): boolean {
return version % this.snapshotFrequency === 0;
}
}Publish-Subscribe is a messaging pattern where publishers send messages to topics, and subscribers receive messages from topics they subscribe to.
- Publisher: Sends messages to topics
- Subscriber: Receives messages from topics
- Topic: Channel for messages
- Fan-out: One-to-many communication
- Filtering: Subscribe to specific messages
// System Design - Pub/Sub System
// Publish/Subscribe system implementation
class PubSubSystem {
private topics: Map<string, Topic>;
private subscribers: Map<string, Subscriber[]>;
constructor() {
this.topics = new Map();
this.subscribers = new Map();
}
createTopic(name: string): void {
this.topics.set(name, new Topic(name));
this.subscribers.set(name, []);
}
subscribe(topicName: string, subscriber: Subscriber): void {
const subscribers = this.subscribers.get(topicName);
if (subscribers) {
subscribers.push(subscriber);
subscriber.onSubscribe(topicName);
}
}
unsubscribe(topicName: string, subscriber: Subscriber): void {
const subscribers = this.subscribers.get(topicName);
if (subscribers) {
this.subscribers.set(
topicName,
subscribers.filter(s => s.id !== subscriber.id)
);
subscriber.onUnsubscribe(topicName);
}
}
publish(topicName: string, message: Message): void {
const topic = this.topics.get(topicName);
if (!topic) {
throw new Error(`Topic ${topicName} not found`);
}
topic.addMessage(message);
const subscribers = this.subscribers.get(topicName) || [];
for (const subscriber of subscribers) {
subscriber.onMessage(topicName, message);
}
}
getTopic(name: string): Topic | undefined {
return this.topics.get(name);
}
}
class Topic {
name: string;
messages: Message[];
constructor(name: string) {
this.name = name;
this.messages = [];
}
addMessage(message: Message): void {
this.messages.push(message);
}
getMessages(startIndex: number = 0): Message[] {
return this.messages.slice(startIndex);
}
}
interface Message {
id: string;
data: any;
timestamp: number;
}
interface Subscriber {
id: string;
onMessage(topic: string, message: Message): void;
onSubscribe(topic: string): void;
onUnsubscribe(topic: string): void;
}Load balancing algorithms determine how traffic is distributed across servers based on different strategies.
- Round Robin: Sequential distribution
- Weighted Round Robin: Weighted sequential
- Least Connections: Server with fewest connections
- Least Response Time: Fastest response
- Consistent Hashing: Client-based routing
// System Design - Load Balancer Algorithms
// Weighted Round Robin
class WeightedRoundRobin {
private servers: Server[];
private currentIndex: number = 0;
private currentWeight: number = 0;
constructor(servers: Server[]) {
this.servers = servers;
}
getNextServer(): Server {
while (true) {
this.currentIndex = (this.currentIndex + 1) % this.servers.length;
if (this.currentIndex === 0) {
this.currentWeight = this.currentWeight - 1;
if (this.currentWeight <= 0) {
this.currentWeight = this.getMaxWeight();
}
}
if (this.servers[this.currentIndex].weight >= this.currentWeight) {
return this.servers[this.currentIndex];
}
}
}
private getMaxWeight(): number {
return Math.max(...this.servers.map(s => s.weight));
}
}
// Least Connections
class LeastConnections {
private servers: Server[];
constructor(servers: Server[]) {
this.servers = servers;
}
getNextServer(): Server {
return this.servers.reduce((min, server) =>
server.connections < min.connections ? server : min
);
}
}
// Consistent Hashing Load Balancer
class ConsistentHashLoadBalancer {
private ring: SortedMap<number, Server>;
private virtualNodes: number;
constructor(servers: Server[], virtualNodes: number = 100) {
this.ring = new SortedMap();
this.virtualNodes = virtualNodes;
for (const server of servers) {
this.addServer(server);
}
}
addServer(server: Server): void {
for (let i = 0; i < this.virtualNodes; i++) {
const hash = this.hash(`${server.id}-${i}`);
this.ring.set(hash, server);
}
}
getServer(key: string): Server {
const hash = this.hash(key);
const entry = this.ring.ceilingEntry(hash);
return entry ? entry.value : this.ring.firstEntry().value;
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash;
}
return hash;
}
}Database replication copies data from one database server to another, providing redundancy, high availability, and load distribution.
- Master-Slave: One master, multiple slaves
- Master-Master: Multi-master replication
- Synchronous: Immediate replication
- Asynchronous: Delayed replication
- Conflict Resolution: Handle write conflicts
// System Design - Database Replication
// Master-slave replication
class MasterSlaveReplication {
private master: Database;
private slaves: Database[];
private replicationLog: LogEntry[];
constructor(master: Database, slaves: Database[]) {
this.master = master;
this.slaves = slaves;
this.replicationLog = [];
}
async write(data: any): Promise<void> {
// Write to master
await this.master.write(data);
// Log the operation
this.replicationLog.push({
timestamp: Date.now(),
data: data,
type: 'WRITE'
});
// Replicate to slaves asynchronously
this.replicateToSlaves(data);
}
async read(): Promise<any> {
// Read from a random slave for load balancing
const slave = this.slaves[Math.floor(Math.random() * this.slaves.length)];
return await slave.read();
}
private async replicateToSlaves(data: any): Promise<void> {
for (const slave of this.slaves) {
try {
await slave.write(data);
} catch (error) {
console.error(`Failed to replicate to slave: ${error}`);
// Log failure for retry
this.replicationLog.push({
timestamp: Date.now(),
data: data,
type: 'REPLICATION_FAILED',
slave: slave.id
});
}
}
}
async syncSlave(slaveId: string): Promise<void> {
const slave = this.slaves.find(s => s.id === slaveId);
if (!slave) {
throw new Error(`Slave ${slaveId} not found`);
}
// Sync missing data from master
const missingData = await this.getMissingData(slave);
for (const data of missingData) {
await slave.write(data);
}
}
private async getMissingData(slave: Database): Promise<any[]> {
// Get data missing from slave
return [];
}
}Microservices communicate using various patterns including synchronous (REST, gRPC) and asynchronous (message queues, events).
- REST API: Synchronous HTTP communication
- gRPC: High-performance RPC
- Message Queue: Asynchronous communication
- Event-driven: Event-based communication
- Service Mesh: Infrastructure layer for communication
// System Design - Microservices Communication
// Service communication patterns
// Synchronous communication (HTTP/REST)
class RestClient {
private baseUrl: string;
private timeout: number;
constructor(baseUrl: string, timeout: number = 5000) {
this.baseUrl = baseUrl;
this.timeout = timeout;
}
async get(endpoint: string): Promise<any> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
signal: AbortSignal.timeout(this.timeout)
});
return response.json();
}
async post(endpoint: string, data: any): Promise<any> {
const response = await fetch(`${this.baseUrl}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
signal: AbortSignal.timeout(this.timeout)
});
return response.json();
}
}
// Asynchronous communication (Message Queue)
class AsyncCommunication {
private messageQueue: MessageQueue;
private serviceName: string;
constructor(messageQueue: MessageQueue, serviceName: string) {
this.messageQueue = messageQueue;
this.serviceName = serviceName;
}
async sendMessage(service: string, message: any): Promise<void> {
await this.messageQueue.publish(service, {
from: this.serviceName,
data: message,
timestamp: Date.now()
});
}
async receiveMessages(callback: (message: any) => Promise<void>): Promise<void> {
await this.messageQueue.subscribe(this.serviceName, async (message) => {
try {
await callback(message);
} catch (error) {
console.error(`Error processing message: ${error}`);
// Send to dead letter queue
await this.messageQueue.publish('dlq', message);
}
});
}
}
// Circuit breaker pattern
class CircuitBreakerService {
private circuitBreaker: CircuitBreaker;
private service: Service;
private fallback: () => any;
constructor(service: Service, fallback: () => any) {
this.circuitBreaker = new CircuitBreaker();
this.service = service;
this.fallback = fallback;
}
async call(request: any): Promise<any> {
try {
return await this.circuitBreaker.execute(
() => this.service.handle(request)
);
} catch (error) {
return this.fallback();
}
}
}Data partitioning distributes data across multiple servers or shards to improve performance and scalability.
- Range Partitioning: Based on value ranges
- Hash Partitioning: Based on hash function
- List Partitioning: Based on predefined lists
- Composite Partitioning: Multiple partitioning strategies
- Consistent Hashing: Minimal redistribution on changes
// System Design - Data Partitioning Strategies
// Range partitioning
class RangePartitioner {
private partitions: Partition[];
private partitionKey: string;
constructor(partitions: Partition[], partitionKey: string) {
this.partitions = partitions;
this.partitionKey = partitionKey;
}
getPartition(data: any): Partition {
const value = data[this.partitionKey];
for (const partition of this.partitions) {
if (value >= partition.start && value < partition.end) {
return partition;
}
}
return this.partitions[this.partitions.length - 1];
}
}
// Hash partitioning
class HashPartitioner {
private numPartitions: number;
private partitions: Partition[];
constructor(numPartitions: number) {
this.numPartitions = numPartitions;
this.partitions = [];
for (let i = 0; i < numPartitions; i++) {
this.partitions.push({ id: i });
}
}
getPartition(key: string): Partition {
const hash = this.hash(key);
const partitionId = hash % this.numPartitions;
return this.partitions[partitionId];
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash);
}
}
// List partitioning
class ListPartitioner {
private partitionMap: Map<string, Partition>;
constructor(partitionMap: Map<string, Partition>) {
this.partitionMap = partitionMap;
}
getPartition(key: string): Partition | undefined {
return this.partitionMap.get(key);
}
}
// Composite partitioning
class CompositePartitioner {
private partitioners: Partitioner[];
constructor(partitioners: Partitioner[]) {
this.partitioners = partitioners;
}
getPartition(data: any): Partition {
let partition = data;
for (const partitioner of this.partitioners) {
partition = partitioner.getPartition(partition);
}
return partition;
}
}Replication strategies define how data is copied and synchronized across multiple nodes in a distributed system.
- Single-Leader: One primary node
- Multi-Leader: Multiple primary nodes
- Leaderless: No designated leader
- Quorum-based: Majority consensus
- Conflict-free: CRDTs (Conflict-free Replicated Data Types)
// System Design - Replication Strategies
// Multi-master replication
class MultiMasterReplication {
private nodes: Node[];
private conflictResolution: ConflictResolutionStrategy;
constructor(nodes: Node[], conflictResolution: ConflictResolutionStrategy) {
this.nodes = nodes;
this.conflictResolution = conflictResolution;
}
async write(data: any): Promise<void> {
const writes = this.nodes.map(async (node) => {
try {
await node.write(data);
return { success: true, node: node.id };
} catch (error) {
return { success: false, node: node.id, error };
}
});
const results = await Promise.all(writes);
const failed = results.filter(r => !r.success);
if (failed.length > 0) {
// Handle failures
console.warn(`Writes failed on nodes: ${failed.map(f => f.node).join(', ')}`);
}
}
async read(key: string): Promise<any> {
const reads = this.nodes.map(async (node) => {
try {
return await node.read(key);
} catch (error) {
return null;
}
});
const results = await Promise.all(reads);
const validResults = results.filter(r => r !== null);
if (validResults.length === 0) {
throw new Error('No data available');
}
// Resolve conflicts
return this.conflictResolution.resolve(validResults);
}
}
// Quorum-based replication
class QuorumReplication {
private nodes: Node[];
private readQuorum: number;
private writeQuorum: number;
constructor(nodes: Node[], readQuorum: number, writeQuorum: number) {
this.nodes = nodes;
this.readQuorum = readQuorum;
this.writeQuorum = writeQuorum;
}
async write(data: any): Promise<void> {
const writes = this.nodes.map(node => node.write(data));
const results = await Promise.allSettled(writes);
const successCount = results.filter(r => r.status === 'fulfilled').length;
if (successCount < this.writeQuorum) {
throw new Error(`Write quorum not met: ${successCount} of ${this.writeQuorum}`);
}
}
async read(key: string): Promise<any> {
const reads = this.nodes.map(node => node.read(key));
const results = await Promise.allSettled(reads);
const successfulReads = results
.filter(r => r.status === 'fulfilled')
.map(r => (r as PromiseFulfilledResult<any>).value);
if (successfulReads.length < this.readQuorum) {
throw new Error(`Read quorum not met: ${successfulReads.length} of ${this.readQuorum}`);
}
return this.resolveReads(successfulReads);
}
private resolveReads(reads: any[]): any {
// Implement read resolution (e.g., version-based)
return reads[0];
}
}API Gateway patterns define how the gateway handles routing, authentication, rate limiting, and other cross-cutting concerns.
- Gateway Routing: Route requests to services
- Gateway Aggregation: Combine multiple responses
- Gateway Authentication: JWT/OAuth validation
- Gateway Rate Limiting: Throttle requests
- Gateway Caching: Cache responses
// System Design - API Gateway Patterns
// Gateway routing pattern
class GatewayRouter {
private routes: Route[];
private loadBalancer: LoadBalancer;
constructor(routes: Route[], loadBalancer: LoadBalancer) {
this.routes = routes;
this.loadBalancer = loadBalancer;
}
async route(request: Request): Promise<Response> {
const route = this.matchRoute(request);
if (!route) {
return new Response('Not Found', { status: 404 });
}
const service = this.loadBalancer.getService(route.serviceName);
if (!service) {
return new Response('Service Unavailable', { status: 503 });
}
const response = await service.handle(request);
return response;
}
private matchRoute(request: Request): Route | undefined {
for (const route of this.routes) {
if (request.url.pathname.startsWith(route.path)) {
return route;
}
}
return undefined;
}
}
// Authentication gateway
class AuthGateway {
private authService: AuthService;
private allowedPaths: string[];
constructor(authService: AuthService, allowedPaths: string[]) {
this.authService = authService;
this.allowedPaths = allowedPaths;
}
async authenticate(request: Request): Promise<boolean> {
// Skip authentication for public paths
if (this.isPublicPath(request.url.pathname)) {
return true;
}
const token = this.extractToken(request);
if (!token) {
return false;
}
return await this.authService.validateToken(token);
}
private isPublicPath(path: string): boolean {
return this.allowedPaths.some(p => path.startsWith(p));
}
private extractToken(request: Request): string | null {
const authHeader = request.headers.get('Authorization');
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
return null;
}
}
// Rate limiting gateway
class RateLimitGateway {
private rateLimiter: RateLimiter;
private config: RateLimitConfig;
constructor(rateLimiter: RateLimiter, config: RateLimitConfig) {
this.rateLimiter = rateLimiter;
this.config = config;
}
async allowRequest(request: Request): Promise<boolean> {
const clientId = this.getClientId(request);
return await this.rateLimiter.allowRequest(clientId);
}
private getClientId(request: Request): string {
// Use IP address or API key
return request.ip || request.headers.get('X-API-Key') || 'unknown';
}
}Caching patterns define how and when to cache data to optimize performance and reduce latency.
- Cache-Aside: Application manages cache
- Read-Through: Cache reads from database
- Write-Through: Writes to cache and database
- Write-Behind: Asynchronous write to database
- Cache Invalidation: Remove stale data
// System Design - Caching Patterns
// Cache-Aside (Lazy Loading)
class CacheAsidePattern<K, V> {
private cache: Cache<K, V>;
private dataStore: DataStore<K, V>;
constructor(cache: Cache<K, V>, dataStore: DataStore<K, V>) {
this.cache = cache;
this.dataStore = dataStore;
}
async get(key: K): Promise<V | null> {
// Try cache first
let value = await this.cache.get(key);
if (value !== null) {
return value;
}
// Cache miss - get from data store
value = await this.dataStore.get(key);
if (value !== null) {
// Store in cache
await this.cache.set(key, value);
}
return value;
}
async set(key: K, value: V): Promise<void> {
await this.dataStore.set(key, value);
await this.cache.set(key, value);
}
async invalidate(key: K): Promise<void> {
await this.cache.delete(key);
}
}
// Write-Through Cache
class WriteThroughCache<K, V> {
private cache: Cache<K, V>;
private dataStore: DataStore<K, V>;
constructor(cache: Cache<K, V>, dataStore: DataStore<K, V>) {
this.cache = cache;
this.dataStore = dataStore;
}
async get(key: K): Promise<V | null> {
return await this.cache.get(key);
}
async set(key: K, value: V): Promise<void> {
// Write to both cache and data store
await Promise.all([
this.cache.set(key, value),
this.dataStore.set(key, value)
]);
}
async delete(key: K): Promise<void> {
await Promise.all([
this.cache.delete(key),
this.dataStore.delete(key)
]);
}
}
// Write-Behind Cache
class WriteBehindCache<K, V> {
private cache: Cache<K, V>;
private dataStore: DataStore<K, V>;
private writeQueue: Queue<{key: K, value: V}>;
constructor(cache: Cache<K, V>, dataStore: DataStore<K, V>) {
this.cache = cache;
this.dataStore = dataStore;
this.writeQueue = new Queue();
this.processQueue();
}
async get(key: K): Promise<V | null> {
return await this.cache.get(key);
}
async set(key: K, value: V): Promise<void> {
await this.cache.set(key, value);
this.writeQueue.enqueue({ key, value });
}
private async processQueue(): Promise<void> {
while (true) {
const item = await this.writeQueue.dequeue();
if (item) {
await this.dataStore.set(item.key, item.value);
}
}
}
}Eventual consistency guarantees that all replicas will converge to the same state, but not necessarily immediately after a write.
- Vector Clocks: Version tracking
- Conflict Resolution: Handle concurrent updates
- Quorum: Read/Write quorum for consistency
- Asynchronous Replication: Delay between writes and reads
- CRDTs: Conflict-free Replicated Data Types
// System Design - Eventual Consistency
// Eventual consistency implementation
class EventualConsistency {
private nodes: Node[];
private replicationQueue: Queue<Update>;
private conflictResolver: ConflictResolver;
constructor(nodes: Node[], conflictResolver: ConflictResolver) {
this.nodes = nodes;
this.conflictResolver = conflictResolver;
this.replicationQueue = new Queue();
}
async update(key: string, value: any): Promise<void> {
// Update local node
const localNode = this.nodes[0];
await localNode.update(key, value);
// Queue for replication
this.replicationQueue.enqueue({
key,
value,
timestamp: Date.now(),
source: localNode.id
});
// Start replication process
this.processReplication();
}
async read(key: string): Promise<any> {
// Read from a random node
const node = this.nodes[Math.floor(Math.random() * this.nodes.length)];
return await node.read(key);
}
private async processReplication(): Promise<void> {
while (true) {
const update = await this.replicationQueue.dequeue();
if (update) {
await this.replicate(update);
}
}
}
private async replicate(update: Update): Promise<void> {
for (const node of this.nodes) {
if (node.id !== update.source) {
try {
await node.update(update.key, update.value);
} catch (error) {
console.error(`Failed to replicate to node ${node.id}: ${error}`);
// Retry later
this.replicationQueue.enqueue(update);
}
}
}
}
// Vector clock for versioning
private vectorClock: Map<string, number> = new Map();
incrementClock(nodeId: string): void {
const current = this.vectorClock.get(nodeId) || 0;
this.vectorClock.set(nodeId, current + 1);
}
getClock(): Map<string, number> {
return new Map(this.vectorClock);
}
}Distributed transactions coordinate operations across multiple services or databases, ensuring consistency and atomicity.
- Two-Phase Commit: Prepare and commit phases
- Saga Pattern: Compensation transactions
- Compensation: Rollback operations
- Idempotency: Handle duplicate operations
- Distributed ACID: Atomicity, Consistency, Isolation, Durability
// System Design - Distributed Transactions
// Two-phase commit
class TwoPhaseCommit {
private participants: Participant[];
private coordinator: Coordinator;
constructor(participants: Participant[]) {
this.participants = participants;
this.coordinator = new Coordinator(participants);
}
async execute(transaction: Transaction): Promise<boolean> {
// Phase 1: Prepare
const prepareResults = await this.coordinator.prepare(transaction);
if (prepareResults.every(r => r === 'ready')) {
// Phase 2: Commit
await this.coordinator.commit(transaction);
return true;
} else {
// Rollback
await this.coordinator.rollback(transaction);
return false;
}
}
}
// Saga pattern
class SagaOrchestrator {
private steps: SagaStep[];
private compensationSteps: SagaStep[];
constructor(steps: SagaStep[], compensationSteps: SagaStep[]) {
this.steps = steps;
this.compensationSteps = compensationSteps;
}
async execute(data: any): Promise<void> {
let completedSteps = 0;
try {
for (let i = 0; i < this.steps.length; i++) {
await this.steps[i].execute(data);
completedSteps++;
}
} catch (error) {
// Compensate for completed steps
for (let i = completedSteps - 1; i >= 0; i--) {
await this.compensationSteps[i].execute(data);
}
throw error;
}
}
}
// Compensation transaction
class CompensationTransaction {
private operations: Operation[];
constructor(operations: Operation[]) {
this.operations = operations;
}
async execute(): Promise<void> {
for (const operation of this.operations) {
try {
await operation.execute();
} catch (error) {
// Log failure and continue
console.error(`Compensation failed: ${error}`);
}
}
}
}Service discovery enables services to dynamically discover and communicate with each other without hardcoded addresses.
- Service Registry: Store service instances
- Health Checks: Monitor service health
- Load Balancing: Distribute across instances
- DNS-based: Service discovery via DNS
- Consul/etcd: Service discovery tools
// System Design - Service Discovery
// Service discovery implementation
class ServiceDiscovery {
private services: Map<string, ServiceInstance[]>;
private healthCheck: HealthCheck;
private loadBalancer: LoadBalancer;
constructor(healthCheck: HealthCheck, loadBalancer: LoadBalancer) {
this.services = new Map();
this.healthCheck = healthCheck;
this.loadBalancer = loadBalancer;
this.startHealthCheck();
}
register(serviceName: string, instance: ServiceInstance): void {
if (!this.services.has(serviceName)) {
this.services.set(serviceName, []);
}
this.services.get(serviceName).push(instance);
console.log(`Service ${serviceName} registered: ${instance.id}`);
}
deregister(serviceName: string, instanceId: string): void {
const instances = this.services.get(serviceName);
if (instances) {
this.services.set(
serviceName,
instances.filter(i => i.id !== instanceId)
);
console.log(`Service ${serviceName} deregistered: ${instanceId}`);
}
}
discover(serviceName: string): ServiceInstance | null {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
return null;
}
// Load balance and return a healthy instance
const healthyInstances = instances.filter(i => i.isHealthy());
if (healthyInstances.length === 0) {
return null;
}
return this.loadBalancer.select(healthyInstances);
}
private startHealthCheck(): void {
setInterval(async () => {
for (const [serviceName, instances] of this.services) {
const healthyInstances = await Promise.all(
instances.map(async (instance) => {
const isHealthy = await this.healthCheck.check(instance);
instance.setHealth(isHealthy);
return instance;
})
);
this.services.set(
serviceName,
healthyInstances.filter(i => i.isHealthy())
);
}
}, 30000);
}
}
// Health check implementation
class HealthCheck {
async check(instance: ServiceInstance): Promise<boolean> {
try {
const response = await fetch(`${instance.url}/health`, {
timeout: 5000
});
return response.status === 200;
} catch (error) {
return false;
}
}
}
// Load balancer for service discovery
class ServiceLoadBalancer {
private strategy: LoadBalancingStrategy;
constructor(strategy: LoadBalancingStrategy) {
this.strategy = strategy;
}
select(instances: ServiceInstance[]): ServiceInstance {
return this.strategy.select(instances);
}
}A distributed cache stores data across multiple nodes, providing high availability, scalability, and fault tolerance.
- Consistent Hashing: Data distribution
- Replication: Copy data across nodes
- Cache Invalidation: Remove stale data
- Cache Coherency: Consistency across nodes
- Cache Eviction: LRU, LFU, TTL
// System Design - Distributed Cache
// Distributed cache implementation
class DistributedCache {
private nodes: CacheNode[];
private consistentHash: ConsistentHash;
private replicationFactor: number;
constructor(nodes: CacheNode[], replicationFactor: number = 2) {
this.nodes = nodes;
this.replicationFactor = replicationFactor;
this.consistentHash = new ConsistentHash(nodes.map(n => n.id));
}
async get(key: string): Promise<any> {
const primaryNode = this.getNode(key);
const value = await primaryNode.get(key);
if (value === null && this.replicationFactor > 1) {
// Try replica nodes
const replicaNodes = this.getReplicaNodes(key);
for (const node of replicaNodes) {
const value = await node.get(key);
if (value !== null) {
// Write back to primary
await primaryNode.set(key, value);
return value;
}
}
}
return value;
}
async set(key: string, value: any): Promise<void> {
const primaryNode = this.getNode(key);
await primaryNode.set(key, value);
// Replicate to other nodes
const replicaNodes = this.getReplicaNodes(key);
for (const node of replicaNodes) {
try {
await node.set(key, value);
} catch (error) {
console.error(`Failed to replicate to node ${node.id}: ${error}`);
}
}
}
private getNode(key: string): CacheNode {
const nodeId = this.consistentHash.getNode(key);
return this.nodes.find(n => n.id === nodeId);
}
private getReplicaNodes(key: string): CacheNode[] {
const replicas = [];
let currentKey = key;
for (let i = 0; i < this.replicationFactor - 1; i++) {
currentKey = `${currentKey}-replica`;
const nodeId = this.consistentHash.getNode(currentKey);
const node = this.nodes.find(n => n.id === nodeId);
if (node && !replicas.includes(node)) {
replicas.push(node);
}
}
return replicas;
}
}A job queue manages background tasks and long-running operations, providing retries, scheduling, and failure handling.
- Producer: Enqueues jobs
- Consumer: Processes jobs
- Retry: Automatic retry on failure
- Backoff: Exponential backoff for retries
- Dead Letter Queue: Failed job handling
// System Design - Job Queue
// Job queue implementation
class JobQueue {
private queues: Map<string, Queue>;
private workers: Worker[];
private maxRetries: number;
constructor(maxRetries: number = 3) {
this.queues = new Map();
this.workers = [];
this.maxRetries = maxRetries;
}
createQueue(name: string): void {
this.queues.set(name, new Queue(name));
}
enqueue(queueName: string, job: Job): void {
const queue = this.queues.get(queueName);
if (!queue) {
throw new Error(`Queue ${queueName} not found`);
}
queue.enqueue(job);
this.processQueue(queueName);
}
private async processQueue(queueName: string): Promise<void> {
const queue = this.queues.get(queueName);
if (!queue) return;
while (queue.hasJobs()) {
const job = queue.dequeue();
if (job) {
await this.executeJob(job);
}
}
}
private async executeJob(job: Job): Promise<void> {
let attempts = 0;
let success = false;
while (attempts < this.maxRetries && !success) {
try {
await job.execute();
success = true;
job.status = 'completed';
} catch (error) {
attempts++;
job.retries = attempts;
job.status = 'failed';
console.error(`Job ${job.id} failed (attempt ${attempts}): ${error}`);
if (attempts < this.maxRetries) {
// Exponential backoff
const delay = Math.pow(2, attempts) * 1000;
await this.sleep(delay);
}
}
}
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
class Queue {
private name: string;
private jobs: Job[];
constructor(name: string) {
this.name = name;
this.jobs = [];
}
enqueue(job: Job): void {
job.status = 'pending';
this.jobs.push(job);
}
dequeue(): Job | null {
return this.jobs.shift() || null;
}
hasJobs(): boolean {
return this.jobs.length > 0;
}
}Realtime communication enables instant message delivery between clients and servers using WebSockets, Server-Sent Events, or other protocols.
- WebSockets: Bi-directional communication
- Server-Sent Events: Server to client streaming
- WebRTC: Peer-to-peer communication
- Room Management: Group communication
- Presence: Online/offline status
// System Design - Realtime Communication
// WebSocket server implementation
class WebSocketServer {
private clients: Map<string, WebSocket>;
private rooms: Map<string, Set<string>>;
constructor() {
this.clients = new Map();
this.rooms = new Map();
}
onConnection(ws: WebSocket, clientId: string): void {
this.clients.set(clientId, ws);
ws.on('message', (message) => {
this.handleMessage(clientId, message);
});
ws.on('close', () => {
this.handleDisconnect(clientId);
});
}
private handleMessage(clientId: string, message: any): void {
const data = JSON.parse(message);
switch (data.type) {
case 'join':
this.joinRoom(clientId, data.room);
break;
case 'leave':
this.leaveRoom(clientId, data.room);
break;
case 'message':
this.broadcastMessage(data.room, {
from: clientId,
data: data.payload
});
break;
}
}
private joinRoom(clientId: string, room: string): void {
if (!this.rooms.has(room)) {
this.rooms.set(room, new Set());
}
this.rooms.get(room).add(clientId);
this.sendToClient(clientId, {
type: 'joined',
room: room
});
}
private leaveRoom(clientId: string, room: string): void {
const clients = this.rooms.get(room);
if (clients) {
clients.delete(clientId);
}
}
private broadcastMessage(room: string, message: any): void {
const clients = this.rooms.get(room);
if (!clients) return;
for (const clientId of clients) {
this.sendToClient(clientId, message);
}
}
private sendToClient(clientId: string, message: any): void {
const ws = this.clients.get(clientId);
if (ws) {
ws.send(JSON.stringify(message));
}
}
private handleDisconnect(clientId: string): void {
this.clients.delete(clientId);
for (const [room, clients] of this.rooms) {
clients.delete(clientId);
}
}
}Monitoring and observability provide insights into system behavior, performance, and health through metrics, logs, and traces.
- Metrics: Performance indicators
- Logs: Event records
- Traces: Request flows
- Alerts: Notification on anomalies
- Dashboards: Visualization
// System Design - Monitoring and Observability
// Metrics collection
class MetricsCollector {
private metrics: Map<string, Metric>;
private registry: Registry;
constructor(registry: Registry) {
this.metrics = new Map();
this.registry = registry;
}
counter(name: string, labels?: Record<string, string>): Counter {
const key = this.getMetricKey(name, labels);
if (!this.metrics.has(key)) {
const counter = new Counter(name, labels);
this.metrics.set(key, counter);
this.registry.register(counter);
}
return this.metrics.get(key) as Counter;
}
gauge(name: string, labels?: Record<string, string>): Gauge {
const key = this.getMetricKey(name, labels);
if (!this.metrics.has(key)) {
const gauge = new Gauge(name, labels);
this.metrics.set(key, gauge);
this.registry.register(gauge);
}
return this.metrics.get(key) as Gauge;
}
histogram(name: string, labels?: Record<string, string>): Histogram {
const key = this.getMetricKey(name, labels);
if (!this.metrics.has(key)) {
const histogram = new Histogram(name, labels);
this.metrics.set(key, histogram);
this.registry.register(histogram);
}
return this.metrics.get(key) as Histogram;
}
private getMetricKey(name: string, labels?: Record<string, string>): string {
if (!labels) return name;
const labelString = Object.entries(labels)
.map(([k, v]) => `${k}=${v}`)
.join(',');
return `${name}{${labelString}}`;
}
}
// Logging system
class LoggingSystem {
private loggers: Logger[];
private logLevel: LogLevel;
constructor(logLevel: LogLevel = 'info') {
this.loggers = [];
this.logLevel = logLevel;
}
addLogger(logger: Logger): void {
this.loggers.push(logger);
}
log(level: LogLevel, message: string, metadata?: any): void {
if (this.shouldLog(level)) {
const logEntry = {
timestamp: new Date().toISOString(),
level,
message,
metadata,
traceId: this.getTraceId()
};
for (const logger of this.loggers) {
logger.log(logEntry);
}
}
}
info(message: string, metadata?: any): void {
this.log('info', message, metadata);
}
error(message: string, metadata?: any): void {
this.log('error', message, metadata);
}
debug(message: string, metadata?: any): void {
this.log('debug', message, metadata);
}
warn(message: string, metadata?: any): void {
this.log('warn', message, metadata);
}
private shouldLog(level: LogLevel): boolean {
const levels = ['debug', 'info', 'warn', 'error'];
return levels.indexOf(level) >= levels.indexOf(this.logLevel);
}
private getTraceId(): string {
// Get trace ID from context
return 'trace-' + Date.now();
}
}Distributed tracing tracks requests across multiple services, providing visibility into the flow and performance of distributed systems.
- Spans: Individual operations
- Trace ID: Unique request identifier
- Context Propagation: Pass tracing context
- Instrumentation: Code instrumentation
- Jaeger/Zipkin: Tracing tools
// System Design - Distributed Tracing
// Distributed tracing implementation
class Tracer {
private spans: Span[];
private activeSpans: Map<string, Span>;
constructor() {
this.spans = [];
this.activeSpans = new Map();
}
startSpan(operationName: string, parentSpanId?: string): Span {
const spanId = this.generateSpanId();
const span = new Span(spanId, operationName, parentSpanId);
this.activeSpans.set(spanId, span);
return span;
}
finishSpan(spanId: string): void {
const span = this.activeSpans.get(spanId);
if (span) {
span.finish();
this.spans.push(span);
this.activeSpans.delete(spanId);
}
}
getTrace(traceId: string): Span[] {
return this.spans.filter(s => s.traceId === traceId);
}
private generateSpanId(): string {
return `span-${Date.now()}-${Math.random()}`;
}
}
class Span {
id: string;
traceId: string;
operationName: string;
parentSpanId: string;
startTime: number;
endTime: number;
tags: Map<string, any>;
logs: LogEntry[];
constructor(id: string, operationName: string, parentSpanId?: string) {
this.id = id;
this.traceId = this.generateTraceId();
this.operationName = operationName;
this.parentSpanId = parentSpanId || '';
this.startTime = Date.now();
this.endTime = 0;
this.tags = new Map();
this.logs = [];
}
setTag(key: string, value: any): void {
this.tags.set(key, value);
}
log(message: string, fields?: any): void {
this.logs.push({
timestamp: Date.now(),
message,
fields
});
}
finish(): void {
this.endTime = Date.now();
}
private generateTraceId(): string {
return `trace-${Date.now()}-${Math.random()}`;
}
}A data pipeline moves and transforms data from sources to destinations, enabling data processing, analysis, and integration.
- Sources: Data origins
- Processors: Data transformation
- Sinks: Data destinations
- Streaming: Real-time processing
- Batch: Periodic processing
// System Design - Data Pipeline
// Data pipeline implementation
class DataPipeline {
private stages: Stage[];
private errorHandlers: ErrorHandler[];
constructor(stages: Stage[]) {
this.stages = stages;
this.errorHandlers = [];
}
async process(data: any): Promise<any> {
let currentData = data;
for (const stage of this.stages) {
try {
currentData = await stage.process(currentData);
} catch (error) {
await this.handleError(error, stage, currentData);
throw error;
}
}
return currentData;
}
private async handleError(error: Error, stage: Stage, data: any): Promise<void> {
for (const handler of this.errorHandlers) {
try {
await handler.handle(error, stage, data);
} catch (e) {
console.error(`Error handler failed: ${e}`);
}
}
}
}
// Streaming pipeline
class StreamingPipeline {
private sources: Source[];
private processors: Processor[];
private sinks: Sink[];
constructor() {
this.sources = [];
this.processors = [];
this.sinks = [];
}
addSource(source: Source): void {
this.sources.push(source);
}
addProcessor(processor: Processor): void {
this.processors.push(processor);
}
addSink(sink: Sink): void {
this.sinks.push(sink);
}
async start(): Promise<void> {
for (const source of this.sources) {
source.onData(async (data) => {
let processed = data;
for (const processor of this.processors) {
processed = await processor.process(processed);
}
for (const sink of this.sinks) {
await sink.write(processed);
}
});
await source.start();
}
}
}A data warehouse is a centralized repository that stores structured data from multiple sources for analysis and reporting.
- ETL: Extract, Transform, Load
- OLAP: Online Analytical Processing
- Star Schema: Fact and dimension tables
- Data Marts: Departmental data
- Historical Data: Time-based data
// System Design - Data Warehouse
// Data warehouse implementation
class DataWarehouse {
private tables: Map<string, Table>;
private partitions: Map<string, Partition>;
private indices: Map<string, Index>;
constructor() {
this.tables = new Map();
this.partitions = new Map();
this.indices = new Map();
}
createTable(name: string, schema: Schema): void {
this.tables.set(name, new Table(name, schema));
}
createPartition(tableName: string, partitionKey: string): void {
const table = this.tables.get(tableName);
if (!table) {
throw new Error(`Table ${tableName} not found`);
}
this.partitions.set(`${tableName}.${partitionKey}`, new Partition(table, partitionKey));
}
createIndex(tableName: string, column: string): void {
const table = this.tables.get(tableName);
if (!table) {
throw new Error(`Table ${tableName} not found`);
}
this.indices.set(`${tableName}.${column}`, new Index(table, column));
}
async query(sql: string): Promise<any[]> {
// Parse SQL and execute query
const parsed = this.parseSQL(sql);
const table = this.tables.get(parsed.table);
if (!table) {
throw new Error(`Table ${parsed.table} not found`);
}
return table.query(parsed);
}
private parseSQL(sql: string): ParsedQuery {
// Simple SQL parser
return { table: 'users', columns: ['*'], where: {} };
}
}
// ETL process
class ETLProcess {
private extractors: Extractor[];
private transformers: Transformer[];
private loaders: Loader[];
constructor() {
this.extractors = [];
this.transformers = [];
this.loaders = [];
}
addExtractor(extractor: Extractor): void {
this.extractors.push(extractor);
}
addTransformer(transformer: Transformer): void {
this.transformers.push(transformer);
}
addLoader(loader: Loader): void {
this.loaders.push(loader);
}
async run(): Promise<void> {
for (const extractor of this.extractors) {
const data = await extractor.extract();
let transformed = data;
for (const transformer of this.transformers) {
transformed = await transformer.transform(transformed);
}
for (const loader of this.loaders) {
await loader.load(transformed);
}
}
}
}A CDN is a distributed network of servers that delivers web content to users based on their geographic location, improving performance and availability.
- Edge Locations: Geographic distribution
- Caching: Content storage at edge
- Origin: Primary content source
- DNS Routing: Geographic routing
- DDoS Protection: Security features
// System Design - Content Delivery Network
// CDN implementation
class CDN {
private edgeLocations: EdgeLocation[];
private originServer: OriginServer;
private cache: Cache;
constructor(originServer: OriginServer, edgeLocations: EdgeLocation[]) {
this.originServer = originServer;
this.edgeLocations = edgeLocations;
this.cache = new Cache();
}
async serveContent(request: Request): Promise<Response> {
// Find closest edge location
const edgeLocation = this.getClosestEdge(request.ip);
// Check cache
const cacheKey = this.getCacheKey(request);
const cached = await this.cache.get(cacheKey);
if (cached) {
return cached;
}
// Fetch from origin
const response = await this.originServer.fetch(request);
// Cache response
if (response.status === 200) {
await this.cache.set(cacheKey, response);
}
return response;
}
private getClosestEdge(ip: string): EdgeLocation {
// Geo-location based routing
const location = this.getGeoLocation(ip);
return this.edgeLocations
.sort((a, b) => this.getDistance(location, a.location))
[0];
}
private getCacheKey(request: Request): string {
return `${request.url.pathname}${request.url.query || ''}`;
}
private getGeoLocation(ip: string): { lat: number; lon: number } {
// IP to location mapping
return { lat: 40.7128, lon: -74.0060 };
}
private getDistance(loc1: { lat: number; lon: number }, loc2: { lat: number; lon: number }): number {
// Haversine formula for distance calculation
return 0;
}
}A search engine indexes and searches documents, providing relevant results based on user queries using various ranking algorithms.
- Inverted Index: Word-to-document mapping
- Tokenization: Text processing
- Ranking: Relevance scoring
- Query Parsing: Understand search intent
- Elasticsearch/Solr: Search engines
// System Design - Search Engine
// Search engine implementation
class SearchEngine {
private index: InvertedIndex;
private tokenizer: Tokenizer;
private ranker: Ranker;
constructor() {
this.index = new InvertedIndex();
this.tokenizer = new Tokenizer();
this.ranker = new Ranker();
}
indexDocument(doc: Document): void {
const tokens = this.tokenizer.tokenize(doc.content);
for (const token of tokens) {
this.index.addToken(token, doc.id);
}
}
search(query: string): SearchResult[] {
const tokens = this.tokenizer.tokenize(query);
const results = this.index.search(tokens);
return this.ranker.rank(results, query);
}
}
// Inverted index implementation
class InvertedIndex {
private index: Map<string, Set<string>>;
constructor() {
this.index = new Map();
}
addToken(token: string, docId: string): void {
if (!this.index.has(token)) {
this.index.set(token, new Set());
}
this.index.get(token).add(docId);
}
search(tokens: string[]): SearchResult[] {
const resultSets = tokens
.map(token => this.index.get(token) || new Set());
// Intersection of sets
const intersection = resultSets.reduce(
(acc, set) => new Set([...acc].filter(x => set.has(x))),
resultSets[0] || new Set()
);
return Array.from(intersection).map(docId => ({
docId,
score: 0
}));
}
}Real-time analytics processes and analyzes data as it arrives, enabling immediate insights and decision-making.
- Stream Processing: Continuous data processing
- Sliding Window: Time-based analysis
- Aggregations: Real-time metrics
- Dashboards: Real-time visualization
- Alerting: Immediate notifications
// System Design - Real-time Analytics
// Real-time analytics implementation
class RealTimeAnalytics {
private aggregators: Map<string, Aggregator>;
private windowSize: number;
private slidingWindow: SlidingWindow;
constructor(windowSize: number = 60) {
this.aggregators = new Map();
this.windowSize = windowSize;
this.slidingWindow = new SlidingWindow(windowSize);
}
addEvent(event: AnalyticsEvent): void {
this.slidingWindow.add(event);
this.updateAggregations(event);
}
private updateAggregations(event: AnalyticsEvent): void {
for (const [key, aggregator] of this.aggregators) {
aggregator.process(event);
}
}
getMetrics(metric: string): Metric {
const aggregator = this.aggregators.get(metric);
if (!aggregator) {
throw new Error(`Metric ${metric} not found`);
}
return aggregator.getMetric();
}
addAggregator(name: string, aggregator: Aggregator): void {
this.aggregators.set(name, aggregator);
}
}
// Sliding window for events
class SlidingWindow {
private windowSize: number;
private events: Event[];
constructor(windowSize: number) {
this.windowSize = windowSize;
this.events = [];
}
add(event: Event): void {
this.events.push(event);
this.cleanup();
}
getEvents(): Event[] {
this.cleanup();
return this.events;
}
private cleanup(): void {
const cutoff = Date.now() - this.windowSize * 1000;
this.events = this.events.filter(e => e.timestamp >= cutoff);
}
}CAP theorem states that a distributed system can only provide two of three guarantees: Consistency, Availability, and Partition Tolerance.
- Consistency: All nodes see same data
- Availability: Every request receives a response
- Partition Tolerance: System continues despite network partitions
- CP Systems: Consistency + Partition Tolerance
- AP Systems: Availability + Partition Tolerance
// System Design - CAP Theorem
// CAP theorem implementation examples
// Consistency (Strong)
class StrongConsistency {
private nodes: Node[];
private version: Map<string, number>;
async write(key: string, value: any): Promise<void> {
const writes = this.nodes.map(node => node.write(key, value));
await Promise.all(writes);
}
async read(key: string): Promise<any> {
const reads = this.nodes.map(node => node.read(key));
const results = await Promise.all(reads);
return results[0];
}
}
// Availability (Eventual)
class EventualConsistency2 {
private nodes: Node[];
private replication: Replication;
async write(key: string, value: any): Promise<void> {
await this.nodes[0].write(key, value);
this.replication.asyncReplicate(key, value);
}
async read(key: string): Promise<any> {
return await this.nodes[0].read(key);
}
}
// Partition tolerance
class PartitionTolerance {
private nodes: Node[];
private partitionStrategy: PartitionStrategy;
async handleNetworkPartition(): Promise<void> {
// Split nodes into partitions
const partitions = this.partitionStrategy.partition(this.nodes);
for (const partition of partitions) {
this.processPartition(partition);
}
}
private processPartition(partition: Node[]): void {
// Process requests within partition
}
}ACID transactions ensure reliable processing of database operations with Atomicity, Consistency, Isolation, and Durability.
- Atomicity: All or nothing
- Consistency: Valid state after transaction
- Isolation: Concurrent execution isolation
- Durability: Persistence after commit
- Isolation Levels: Read Uncommitted, Read Committed, Repeatable Read, Serializable
// System Design - ACID Transactions
// ACID transaction implementation
class ACIDTransaction {
private transactions: Transaction[];
private log: TransactionLog;
private lockManager: LockManager;
async begin(): Promise<Transaction> {
const tx = new Transaction();
this.transactions.push(tx);
this.log.write('BEGIN', tx.id);
return tx;
}
async commit(tx: Transaction): Promise<void> {
// Commit all changes
for (const change of tx.changes) {
await change.apply();
}
this.log.write('COMMIT', tx.id);
tx.status = 'committed';
}
async rollback(tx: Transaction): Promise<void> {
// Rollback all changes
for (const change of tx.changes) {
await change.undo();
}
this.log.write('ROLLBACK', tx.id);
tx.status = 'aborted';
}
}
// Isolation levels
class IsolationLevels {
async serializable(operations: Operation[]): Promise<void> {
// Strictest isolation level
const lockManager = new LockManager();
for (const op of operations) {
await lockManager.acquire(op.key, 'exclusive');
await op.execute();
await lockManager.release(op.key);
}
}
async readCommitted(operations: Operation[]): Promise<void> {
// Write locks only
const lockManager = new LockManager();
for (const op of operations) {
if (op.type === 'write') {
await lockManager.acquire(op.key, 'exclusive');
await op.execute();
await lockManager.release(op.key);
} else {
await op.execute();
}
}
}
}BASE properties describe the behavior of distributed systems: Basically Available, Soft state, Eventual consistency.
- Basically Available: System remains available
- Soft State: State may change over time
- Eventual Consistency: System converges over time
- Trade-offs: Consistency vs Availability
- Use cases: NoSQL databases, distributed systems
// System Design - BASE Properties
// BASE (Basically Available, Soft state, Eventually consistent)
class BASESystem {
private nodes: Node[];
private consistencyLevel: ConsistencyLevel;
constructor(consistencyLevel: ConsistencyLevel = 'eventual') {
this.nodes = nodes;
this.consistencyLevel = consistencyLevel;
}
async write(key: string, value: any): Promise<void> {
// Write to local node
await this.nodes[0].write(key, value);
// Asynchronously replicate
this.asyncReplicate(key, value);
}
async read(key: string): Promise<any> {
// Read from any available node
const node = this.getAvailableNode();
return await node.read(key);
}
private async asyncReplicate(key: string, value: any): Promise<void> {
for (const node of this.nodes.slice(1)) {
try {
await node.write(key, value);
} catch (error) {
// Log failure, continue with other nodes
console.error(`Replication failed: ${error}`);
}
}
}
private getAvailableNode(): Node {
for (const node of this.nodes) {
if (node.isAvailable()) {
return node;
}
}
return this.nodes[0];
}
}Quorum consensus ensures data consistency in distributed systems by requiring a majority of nodes to agree on operations.
- Read Quorum: Minimum nodes for read
- Write Quorum: Minimum nodes for write
- Quorum Formula: R + W > N
- Consistency Levels: Strong, Eventual
- Trade-offs: Performance vs Consistency
// System Design - Quorum Consensus
// Quorum consensus implementation
class QuorumConsensus {
private nodes: Node[];
private readQuorum: number;
private writeQuorum: number;
constructor(nodes: Node[], readQuorum: number, writeQuorum: number) {
this.nodes = nodes;
this.readQuorum = readQuorum;
this.writeQuorum = writeQuorum;
}
async write(key: string, value: any): Promise<void> {
const writes = this.nodes.map(node => node.write(key, value));
const results = await Promise.allSettled(writes);
const successCount = results.filter(r => r.status === 'fulfilled').length;
if (successCount < this.writeQuorum) {
throw new Error('Write quorum not met');
}
}
async read(key: string): Promise<any> {
const reads = this.nodes.map(node => node.read(key));
const results = await Promise.allSettled(reads);
const values = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
if (values.length < this.readQuorum) {
throw new Error('Read quorum not met');
}
return this.resolveVersion(values);
}
private resolveVersion(values: any[]): any {
// Return the value with the highest version
return values[0];
}
}Distributed ID generation creates unique identifiers across multiple nodes without centralized coordination.
- Snowflake: Twitter's ID generator
- UUID: Universally Unique Identifier
- Sequential: Ordered IDs
- ZooKeeper: Coordination service
- Distributed counters: Atomic increments
// System Design - Distributed ID Generation
// Snowflake ID generator
class SnowflakeIDGenerator {
private workerId: number;
private datacenterId: number;
private sequence: number = 0;
private lastTimestamp: number = -1;
private readonly workerIdBits = 5;
private readonly datacenterIdBits = 5;
private readonly maxWorkerId = -1 ^ (-1 << this.workerIdBits);
private readonly maxDatacenterId = -1 ^ (-1 << this.datacenterIdBits);
private readonly sequenceBits = 12;
private readonly workerIdShift = this.sequenceBits;
private readonly datacenterIdShift = this.sequenceBits + this.workerIdBits;
private readonly timestampShift = this.sequenceBits + this.workerIdBits + this.datacenterIdBits;
private readonly epoch = 1609459200000; // 2021-01-01
constructor(workerId: number, datacenterId: number) {
if (workerId > this.maxWorkerId || workerId < 0) {
throw new Error(`Worker ID must be between 0 and ${this.maxWorkerId}`);
}
if (datacenterId > this.maxDatacenterId || datacenterId < 0) {
throw new Error(`Datacenter ID must be between 0 and ${this.maxDatacenterId}`);
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
nextId(): bigint {
let timestamp = this.timestamp();
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards');
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
if (this.sequence === 0) {
timestamp = this.waitForNextMillisecond(this.lastTimestamp);
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
return (BigInt(timestamp - this.epoch) << BigInt(this.timestampShift)) |
(BigInt(this.datacenterId) << BigInt(this.datacenterIdShift)) |
(BigInt(this.workerId) << BigInt(this.workerIdShift)) |
BigInt(this.sequence);
}
private timestamp(): number {
return Date.now();
}
private waitForNextMillisecond(lastTimestamp: number): number {
let timestamp = this.timestamp();
while (timestamp <= lastTimestamp) {
timestamp = this.timestamp();
}
return timestamp;
}
}Data replication with failover ensures high availability by automatically switching to a replica when the primary fails.
- Master-Slave: Primary and backup
- Failover Detection: Health monitoring
- Automatic Failover: Automated recovery
- Promotion: Slave to master
- Recovery: Restore after failure
// System Design - Data Replication
// Master-slave replication with failover
class ReplicationWithFailover {
private master: Node;
private slaves: Node[];
private failoverHandler: FailoverHandler;
constructor(master: Node, slaves: Node[]) {
this.master = master;
this.slaves = slaves;
this.failoverHandler = new FailoverHandler();
}
async write(data: any): Promise<void> {
try {
await this.master.write(data);
this.replicateToSlaves(data);
} catch (error) {
await this.handleWriteFailure(data);
}
}
async read(): Promise<any> {
// Read from a random slave for load balancing
const slave = this.slaves[Math.floor(Math.random() * this.slaves.length)];
return await slave.read();
}
private async replicateToSlaves(data: any): Promise<void> {
for (const slave of this.slaves) {
try {
await slave.write(data);
} catch (error) {
console.error(`Failed to replicate: ${error}`);
}
}
}
private async handleWriteFailure(data: any): Promise<void> {
// Promote a slave to master
const newMaster = await this.failoverHandler.failover(this.master, this.slaves);
this.master = newMaster;
await this.master.write(data);
}
}Leader election is the process of selecting a single node as the leader among a group of nodes in a distributed system.
- Raft: Consensus algorithm
- Bully Algorithm: Highest ID wins
- Election Timeout: Timer for election
- Heartbeat: Leader communication
- Term: Election rounds
// System Design - Leader Election
// Leader election using Raft algorithm
class RaftLeaderElection {
private nodes: Node[];
private currentLeader: Node | null;
private electionTimeout: number;
constructor(nodes: Node[]) {
this.nodes = nodes;
this.currentLeader = null;
this.electionTimeout = 5000;
}
async startElection(): Promise<void> {
this.currentLeader = null;
this.nodes.forEach(node => node.resetVote());
const candidate = this.nodes[0];
candidate.incrementTerm();
candidate.requestVotes(this.nodes);
const votes = await this.collectVotes(candidate);
if (votes > this.nodes.length / 2) {
candidate.becomeLeader();
this.currentLeader = candidate;
console.log(`Leader elected: ${candidate.id}`);
}
}
private async collectVotes(candidate: Node): Promise<number> {
const votePromises = this.nodes.map(node => node.voteFor(candidate));
const votes = await Promise.all(votePromises);
return votes.filter(v => v).length;
}
}Distributed consensus algorithms ensure multiple nodes agree on a single value, enabling coordination and consistency.
- Paxos: Classic consensus algorithm
- Raft: Understandable consensus
- ZAB: ZooKeeper Atomic Broadcast
- Quorum: Majority agreement
- Term: Logical time periods
// System Design - Distributed Consensus
// Paxos consensus algorithm
class PaxosConsensus {
private nodes: Node[];
private proposals: Proposal[];
constructor(nodes: Node[]) {
this.nodes = nodes;
this.proposals = [];
}
async propose(value: any): Promise<void> {
const proposal = new Proposal(value);
this.proposals.push(proposal);
// Phase 1: Prepare
const prepareResponses = await this.prepare(proposal);
if (prepareResponses.length < this.nodes.length / 2) {
throw new Error('Prepare phase failed');
}
// Phase 2: Accept
const acceptResponses = await this.accept(proposal);
if (acceptResponses.length < this.nodes.length / 2) {
throw new Error('Accept phase failed');
}
// Phase 3: Commit
await this.commit(proposal);
}
private async prepare(proposal: Proposal): Promise<any[]> {
const responses = this.nodes.map(node => node.prepare(proposal));
return Promise.all(responses);
}
private async accept(proposal: Proposal): Promise<any[]> {
const responses = this.nodes.map(node => node.accept(proposal));
return Promise.all(responses);
}
private async commit(proposal: Proposal): Promise<void> {
await Promise.all(this.nodes.map(node => node.commit(proposal)));
}
}A service mesh is a dedicated infrastructure layer for handling service-to-service communication, providing observability, security, and reliability.
- Sidecar Proxy: Per-service proxy
- Control Plane: Configuration management
- Data Plane: Traffic routing
- Istio/Linkerd: Service mesh tools
- Traffic Management: Routing, load balancing
// System Design - Service Mesh
// Service mesh implementation
class ServiceMesh {
private sidecarProxies: SidecarProxy[];
private controlPlane: ControlPlane;
private dataPlane: DataPlane;
constructor() {
this.sidecarProxies = [];
this.controlPlane = new ControlPlane();
this.dataPlane = new DataPlane();
}
addService(service: Service): void {
const proxy = new SidecarProxy(service);
this.sidecarProxies.push(proxy);
this.controlPlane.registerService(service);
this.dataPlane.addRoute(service);
}
async routeRequest(request: Request): Promise<Response> {
const service = this.controlPlane.getService(request.service);
const proxy = this.sidecarProxies.find(p => p.service.id === service.id);
if (!proxy) {
throw new Error('Service not found');
}
return proxy.handle(request);
}
}
// Traffic management
class TrafficManagement {
private routes: Route[];
private loadBalancer: LoadBalancer;
constructor(routes: Route[], loadBalancer: LoadBalancer) {
this.routes = routes;
this.loadBalancer = loadBalancer;
}
async route(request: Request): Promise<Response> {
const route = this.findRoute(request);
if (!route) {
return new Response('Not Found', { status: 404 });
}
const service = this.loadBalancer.select(route.services);
return service.handle(request);
}
private findRoute(request: Request): Route | null {
for (const route of this.routes) {
if (request.url.startsWith(route.path)) {
return route;
}
}
return null;
}
}Data migration is the process of moving data from one system to another, often involving transformation and validation.
- ETL: Extract, Transform, Load
- Blue-Green: Zero-downtime migration
- Validation: Data integrity checks
- Rollback: Revert on failure
- Migration Strategies: Big Bang, Trickle
// System Design - Data Migration
// Data migration strategies
class DataMigration {
private source: DataSource;
private destination: DataDestination;
private migrationStrategy: MigrationStrategy;
constructor(source: DataSource, destination: DataDestination, strategy: MigrationStrategy) {
this.source = source;
this.destination = destination;
this.migrationStrategy = strategy;
}
async migrate(): Promise<void> {
await this.migrationStrategy.migrate(this.source, this.destination);
}
}
// Blue-green deployment for database migration
class BlueGreenMigration {
private blueEnvironment: Database;
private greenEnvironment: Database;
private activeEnvironment: 'blue' | 'green';
constructor(blue: Database, green: Database) {
this.blueEnvironment = blue;
this.greenEnvironment = green;
this.activeEnvironment = 'blue';
}
async migrate(): Promise<void> {
// Prepare green environment
await this.prepareGreen();
// Switch traffic
await this.switchTraffic('green');
// Verify green
if (await this.verifyGreen()) {
// Decommission blue
await this.decommissionBlue();
} else {
// Rollback to blue
await this.rollback();
}
}
private async prepareGreen(): Promise<void> {
// Prepare green environment
await this.greenEnvironment.migrate();
}
private async switchTraffic(environment: 'blue' | 'green'): Promise<void> {
this.activeEnvironment = environment;
// Switch traffic to active environment
}
private async verifyGreen(): Promise<boolean> {
// Verify green environment
return true;
}
private async decommissionBlue(): Promise<void> {
// Decommission blue environment
await this.blueEnvironment.decommission();
}
private async rollback(): Promise<void> {
// Rollback to blue
this.activeEnvironment = 'blue';
}
}Load shedding selectively drops requests when system resources are overloaded to prevent complete failure.
- Priority-based: Drop low-priority requests
- Graceful Degradation: Partial functionality
- Backpressure: Signal overload
- Rate Limiting: Control request rate
- Circuit Breaking: Stop failing requests
// System Design - Load Shedding
// Load shedding implementation
class LoadShedder {
private thresholds: Map<string, number>;
private currentLoad: number;
constructor(thresholds: Map<string, number>) {
this.thresholds = thresholds;
this.currentLoad = 0;
}
shouldShed(): boolean {
return this.currentLoad > this.thresholds.get('cpu') ||
this.currentLoad > this.thresholds.get('memory');
}
shed(request: Request): boolean {
if (!this.shouldShed()) {
return true;
}
// Priority-based shedding
const priority = this.getPriority(request);
return priority <= this.thresholds.get('priorityThreshold');
}
private getPriority(request: Request): number {
// Determine request priority
return 0;
}
}Backpressure is a mechanism to prevent system overload by signaling upstream components to slow down or stop sending data.
- Flow Control: Regulate data flow
- Reactive Streams: Backpressure protocol
- Buffering: Temporary storage
- Dropping: Discard excess data
- Blocking: Block producer
// System Design - Backpressure
// Backpressure implementation
class BackpressureHandler {
private queues: Queue[];
private maxQueueSize: number;
constructor(maxQueueSize: number) {
this.queues = [];
this.maxQueueSize = maxQueueSize;
}
async process(data: any): Promise<void> {
const queue = this.getQueue(data);
if (queue.size() >= this.maxQueueSize) {
// Backpressure: wait or reject
await this.waitForSpace(queue);
}
await queue.push(data);
this.processQueue(queue);
}
private async waitForSpace(queue: Queue): Promise<void> {
while (queue.size() >= this.maxQueueSize) {
await this.sleep(100);
}
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}Circuit breaker with retry combines failure detection with automatic retry mechanisms to improve reliability.
- Retry Policy: Number of retries
- Backoff: Exponential backoff
- Timeout: Operation timeout
- Circuit State: Closed, Open, Half-Open
- Fallback: Alternative response
// System Design - Circuit Breaker with Retry
// Circuit breaker with retry logic
class CircuitBreakerWithRetry {
private maxRetries: number;
private backoffMultiplier: number;
private breaker: CircuitBreaker;
constructor(maxRetries: number = 3, backoffMultiplier: number = 2) {
this.maxRetries = maxRetries;
this.backoffMultiplier = backoffMultiplier;
this.breaker = new CircuitBreaker();
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
let retries = 0;
let delay = 1000;
while (retries < this.maxRetries) {
try {
return await this.breaker.execute(fn);
} catch (error) {
retries++;
if (retries < this.maxRetries) {
await this.sleep(delay);
delay *= this.backoffMultiplier;
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}Health checks monitor the status and availability of system components, enabling automated recovery and load balancing.
- Liveness Probe: Is the service running?
- Readiness Probe: Is the service ready?
- Startup Probe: Is the service started?
- Grace Period: Time to recover
- Health Endpoints: /health, /ready
// System Design - Health Check
// Health check implementation
class HealthChecker {
private services: Service[];
private checkInterval: number;
private healthStatus: Map<string, boolean>;
constructor(services: Service[], checkInterval: number = 30000) {
this.services = services;
this.checkInterval = checkInterval;
this.healthStatus = new Map();
this.startHealthCheck();
}
async checkHealth(service: Service): Promise<boolean> {
try {
const response = await fetch(`${service.url}/health`, {
timeout: 5000
});
const status = response.status === 200;
this.healthStatus.set(service.id, status);
return status;
} catch (error) {
this.healthStatus.set(service.id, false);
return false;
}
}
isHealthy(serviceId: string): boolean {
return this.healthStatus.get(serviceId) || false;
}
private startHealthCheck(): void {
setInterval(async () => {
for (const service of this.services) {
await this.checkHealth(service);
}
}, this.checkInterval);
}
}A dead letter queue stores messages that cannot be processed successfully, allowing for later analysis and recovery.
- Failed Messages: Unprocessable messages
- Retry Exhaustion: Max retries reached
- Manual Processing: Human intervention
- Analysis: Debugging failures
- Replay: Reprocess messages
// System Design - Dead Letter Queue
// Dead letter queue implementation
class DeadLetterQueue {
private dlq: Queue;
private maxRetries: number;
constructor(maxRetries: number = 3) {
this.dlq = new Queue('dlq');
this.maxRetries = maxRetries;
}
async process(message: Message): Promise<void> {
try {
await this.processMessage(message);
} catch (error) {
message.retries = (message.retries || 0) + 1;
if (message.retries >= this.maxRetries) {
await this.dlq.enqueue(message);
console.error(`Message moved to DLQ: ${message.id}`);
} else {
// Re-queue with delay
await this.reQueue(message);
}
}
}
private async processMessage(message: Message): Promise<void> {
// Process message
}
private async reQueue(message: Message): Promise<void> {
// Re-queue with exponential backoff
const delay = Math.pow(2, message.retries) * 1000;
setTimeout(async () => {
await this.process(message);
}, delay);
}
async processDLQ(): Promise<void> {
const messages = await this.dlq.dequeueAll();
for (const message of messages) {
try {
await this.processMessage(message);
} catch (error) {
console.error(`DLQ processing failed: ${error}`);
}
}
}
}The bulkhead pattern isolates failures by partitioning resources into separate pools, preventing cascading failures.
- Resource Isolation: Separate resource pools
- Circuit Breaker: Failure isolation
- Bulkhead: Partition by use case
- Concurrency Limits: Max concurrent requests
- Thread Pools: Isolated thread pools
// System Design - Bulkhead Pattern
// Bulkhead pattern implementation
class Bulkhead {
private pools: Map<string, Pool>;
private maxConcurrent: number;
constructor(maxConcurrent: number) {
this.pools = new Map();
this.maxConcurrent = maxConcurrent;
}
async execute<T>(poolName: string, fn: () => Promise<T>): Promise<T> {
if (!this.pools.has(poolName)) {
this.pools.set(poolName, new Pool(this.maxConcurrent));
}
const pool = this.pools.get(poolName);
return await pool.execute(fn);
}
}
class Pool {
private maxConcurrent: number;
private current: number = 0;
private queue: Function[];
constructor(maxConcurrent: number) {
this.maxConcurrent = maxConcurrent;
this.queue = [];
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.current < this.maxConcurrent) {
this.current++;
try {
return await fn();
} finally {
this.current--;
this.processQueue();
}
} else {
return new Promise((resolve, reject) => {
this.queue.push(() => {
this.execute(fn).then(resolve).catch(reject);
});
});
}
}
private processQueue(): void {
if (this.queue.length > 0 && this.current < this.maxConcurrent) {
const next = this.queue.shift();
next();
}
}
}Exponential backoff is a retry strategy where the delay between retries increases exponentially to reduce system load.
- Delay: Increasing wait time
- Jitter: Add randomness
- Max Retries: Limit retry attempts
- Backoff Factors: 2x, 4x, 8x
- Use case: API calls, network operations
// System Design - Retry with Exponential Backoff
// Exponential backoff implementation
class ExponentialBackoff {
private maxRetries: number;
private baseDelay: number;
private maxDelay: number;
constructor(maxRetries: number = 5, baseDelay: number = 100, maxDelay: number = 10000) {
this.maxRetries = maxRetries;
this.baseDelay = baseDelay;
this.maxDelay = maxDelay;
}
async execute<T>(fn: () => Promise<T>): Promise<T> {
let retries = 0;
let delay = this.baseDelay;
while (retries < this.maxRetries) {
try {
return await fn();
} catch (error) {
if (retries === this.maxRetries - 1) {
throw error;
}
await this.sleep(delay);
delay = Math.min(delay * 2, this.maxDelay);
retries++;
}
}
throw new Error('Max retries exceeded');
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}Rate limiting algorithms control the rate of requests to prevent system overload and ensure fair usage.
- Token Bucket: Tokens refill over time
- Leaky Bucket: Constant rate output
- Fixed Window: Count per time window
- Sliding Window: Rolling time window
- Sliding Log: Request timestamps
// System Design - Rate Limiting Algorithms
// Token bucket algorithm
class TokenBucket {
private capacity: number;
private tokens: number;
private refillRate: number;
private lastRefill: number;
constructor(capacity: number, refillRate: number) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate;
this.lastRefill = Date.now();
}
allow(): boolean {
this.refill();
if (this.tokens >= 1) {
this.tokens--;
return true;
}
return false;
}
private refill(): void {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
}
// Leaky bucket algorithm
class LeakyBucket {
private capacity: number;
private leakRate: number;
private water: number;
private lastLeak: number;
constructor(capacity: number, leakRate: number) {
this.capacity = capacity;
this.leakRate = leakRate;
this.water = 0;
this.lastLeak = Date.now();
}
allow(): boolean {
this.leak();
if (this.water < this.capacity) {
this.water++;
return true;
}
return false;
}
private leak(): void {
const now = Date.now();
const elapsed = (now - this.lastLeak) / 1000;
this.water = Math.max(0, this.water - elapsed * this.leakRate);
this.lastLeak = now;
}
}Leader-follower replication designates one node as the leader for writes and replicates changes to followers for reads.
- Leader: Writes allowed
- Followers: Read-only replicas
- Replication Log: Write operations
- Read Consistency: From leader or followers
- Failover: Promote follower to leader
// System Design - Leader-Follower Replication
// Leader-follower replication implementation
class LeaderFollowerReplication {
private leader: Node;
private followers: Node[];
private replicationLog: Log[];
constructor(leader: Node, followers: Node[]) {
this.leader = leader;
this.followers = followers;
this.replicationLog = [];
}
async write(data: any): Promise<void> {
// Write to leader
await this.leader.write(data);
this.replicationLog.push({
timestamp: Date.now(),
data,
type: 'WRITE'
});
// Replicate to followers asynchronously
this.replicateToFollowers(data);
}
async read(): Promise<any> {
// Read from leader for consistency
return await this.leader.read();
}
private async replicateToFollowers(data: any): Promise<void> {
for (const follower of this.followers) {
try {
await follower.write(data);
} catch (error) {
console.error(`Failed to replicate to follower: ${error}`);
}
}
}
}Multi-Paxos extends the Paxos consensus algorithm to handle multiple instances of consensus, enabling state machine replication.
- Multiple Instances: Many consensus rounds
- Leaders: Single leader per instance
- State Machine: Deterministic operations
- Replication: Replicate state machine
- Stable Leaders: Reduce overhead
// System Design - Multi-Paxos
// Multi-Paxos implementation
class MultiPaxos {
private nodes: Node[];
private currentLeader: Node | null;
private proposals: Map<number, Proposal>;
private instanceId: number;
constructor(nodes: Node[]) {
this.nodes = nodes;
this.currentLeader = null;
this.proposals = new Map();
this.instanceId = 0;
}
async propose(value: any): Promise<void> {
if (!this.currentLeader) {
await this.electLeader();
}
const proposal = new Proposal(++this.instanceId, value);
this.proposals.set(proposal.id, proposal);
await this.currentLeader.propose(proposal);
}
private async electLeader(): Promise<void> {
// Leader election
for (const node of this.nodes) {
const votes = await node.requestVotes();
if (votes > this.nodes.length / 2) {
this.currentLeader = node;
node.becomeLeader();
break;
}
}
}
}A distributed queue is a messaging system that stores and processes messages across multiple nodes, providing scalability and fault tolerance.
- Partitions: Data sharding
- Consumer Groups: Load balancing
- Offsets: Message position
- Replication: Data redundancy
- Delivery Guarantees: At-least-once, At-most-once
// System Design - Distributed Queue
// Distributed queue implementation
class DistributedQueue {
private partitions: Partition[];
private consumerGroups: Map<string, ConsumerGroup>;
constructor(partitions: Partition[]) {
this.partitions = partitions;
this.consumerGroups = new Map();
}
async publish(key: string, message: any): Promise<void> {
const partition = this.getPartition(key);
await partition.append(message);
}
async subscribe(groupId: string, callback: (message: any) => void): Promise<void> {
if (!this.consumerGroups.has(groupId)) {
this.consumerGroups.set(groupId, new ConsumerGroup(groupId));
}
const group = this.consumerGroups.get(groupId);
group.addConsumer(callback);
// Start consuming
this.consume(group);
}
private getPartition(key: string): Partition {
const hash = this.hash(key);
return this.partitions[hash % this.partitions.length];
}
private async consume(group: ConsumerGroup): Promise<void> {
// Consumer loop
while (true) {
for (const partition of this.partitions) {
const messages = await partition.poll();
for (const message of messages) {
group.distribute(message);
}
}
}
}
private hash(key: string): number {
let hash = 0;
for (let i = 0; i < key.length; i++) {
hash = (hash << 5) - hash + key.charCodeAt(i);
hash = hash & hash;
}
return Math.abs(hash);
}
}Distributed lock with TTL (Time-To-Live) automatically releases locks after a timeout to prevent deadlocks.
- TTL: Time-based expiration
- Renewal: Extend lock before expiration
- Fencing Token: Prevent stale locks
- Redlock: Redis-based lock
- Watchdog: Automatic renewal
// System Design - Distributed Locking with TTL
// Distributed lock with TTL and renewal
class DistributedLockWithTTL {
private redis: Redis;
private lockKey: string;
private lockValue: string;
private ttl: number;
private renewalInterval: number;
constructor(redis: Redis, lockKey: string, ttl: number = 30000, renewalInterval: number = 10000) {
this.redis = redis;
this.lockKey = lockKey;
this.lockValue = `${process.pid}-${Date.now()}`;
this.ttl = ttl;
this.renewalInterval = renewalInterval;
}
async acquire(): Promise<boolean> {
const result = await this.redis.set(
this.lockKey,
this.lockValue,
'NX',
'PX',
this.ttl
);
if (result === 'OK') {
this.startRenewal();
return true;
}
return false;
}
async release(): Promise<void> {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
await this.redis.eval(script, 1, this.lockKey, this.lockValue);
this.stopRenewal();
}
private startRenewal(): void {
this.renewalIntervalId = setInterval(async () => {
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("pexpire", KEYS[1], ARGV[2])
else
return 0
end
`;
await this.redis.eval(script, 1, this.lockKey, this.lockValue, this.ttl);
}, this.renewalInterval);
}
private stopRenewal(): void {
if (this.renewalIntervalId) {
clearInterval(this.renewalIntervalId);
}
}
}Read-through cache automatically loads data from the database into the cache when a cache miss occurs.
- Cache Miss: Data not in cache
- Auto-load: Load from database
- Cache Fill: Populate cache
- TTL: Cache expiration
- Consistency: Cache invalidation
// System Design - Read-Through Cache
// Read-through cache implementation
class ReadThroughCache<K, V> {
private cache: Cache<K, V>;
private dataStore: DataStore<K, V>;
constructor(cache: Cache<K, V>, dataStore: DataStore<K, V>) {
this.cache = cache;
this.dataStore = dataStore;
}
async get(key: K): Promise<V | null> {
let value = await this.cache.get(key);
if (value === null) {
value = await this.dataStore.get(key);
if (value !== null) {
await this.cache.set(key, value);
}
}
return value;
}
async set(key: K, value: V): Promise<void> {
await this.dataStore.set(key, value);
await this.cache.set(key, value);
}
async invalidate(key: K): Promise<void> {
await this.cache.delete(key);
}
}Conflict resolution handles concurrent updates to the same data in distributed systems using various strategies.
- Last Write Wins: Latest timestamp wins
- Version Vectors: Track versions
- Merge: Combine conflicting values
- Application-specific: Custom resolution
- CRDTs: Conflict-free data types
// System Design - Data Replication with Conflict Resolution
// Conflict resolution strategies
class ConflictResolver {
resolve(versions: Version[]): Version {
// Last write wins
return this.lastWriteWins(versions);
}
private lastWriteWins(versions: Version[]): Version {
return versions.reduce((latest, current) => {
return current.timestamp > latest.timestamp ? current : latest;
});
}
private highestVersionWins(versions: Version[]): Version {
return versions.reduce((latest, current) => {
return current.version > latest.version ? current : latest;
});
}
private mergeValues(versions: Version[]): Version {
// Merge values from all versions
// This is application-specific
return versions[0];
}
}
// Vector clock conflict resolution
class VectorClockResolver {
resolve(versions: VectorClockVersion[]): VectorClockVersion {
// Find versions with concurrent updates
const concurrentVersions = this.findConcurrent(versions);
if (concurrentVersions.length === 1) {
return concurrentVersions[0];
}
// Merge concurrent updates
return this.merge(concurrentVersions);
}
private findConcurrent(versions: VectorClockVersion[]): VectorClockVersion[] {
// Find concurrent versions
return versions;
}
private merge(versions: VectorClockVersion[]): VectorClockVersion {
// Merge concurrent versions
return versions[0];
}
}Distributed configuration manages configuration settings across multiple services and environments in a centralized way.
- Centralized Store: Single source of truth
- Dynamic Updates: Change without restart
- Versioning: Configuration history
- Watch/Notify: Configuration change detection
- Security: Access control
// System Design - Distributed Configuration
// Distributed configuration management
class DistributedConfig {
private config: Map<string, any>;
private watchers: Map<string, ((value: any) => void)[]>;
private storage: ConfigStorage;
constructor(storage: ConfigStorage) {
this.config = new Map();
this.watchers = new Map();
this.storage = storage;
this.loadConfig();
}
async get(key: string): Promise<any> {
return this.config.get(key);
}
async set(key: string, value: any): Promise<void> {
this.config.set(key, value);
await this.storage.save(key, value);
this.notifyWatchers(key, value);
}
watch(key: string, callback: (value: any) => void): void {
if (!this.watchers.has(key)) {
this.watchers.set(key, []);
}
this.watchers.get(key).push(callback);
}
private async loadConfig(): Promise<void> {
const keys = await this.storage.getAllKeys();
for (const key of keys) {
const value = await this.storage.get(key);
this.config.set(key, value);
}
}
private notifyWatchers(key: string, value: any): void {
const watchers = this.watchers.get(key);
if (watchers) {
for (const watcher of watchers) {
watcher(value);
}
}
}
}Feature flags enable dynamic feature toggling without code deployment, supporting A/B testing and gradual rollouts.
- Toggle: Enable/disable features
- Targeting: User-based rollout
- Percentage Rollout: Gradual release
- Kill Switch: Emergency disable
- Control Plane: Flag management
// System Design - Feature Flags
// Feature flag system
class FeatureFlags {
private flags: Map<string, FeatureFlag>;
private storage: FlagStorage;
private contexts: Map<string, any>;
constructor(storage: FlagStorage) {
this.flags = new Map();
this.storage = storage;
this.contexts = new Map();
this.loadFlags();
}
async isEnabled(flagName: string, context?: any): Promise<boolean> {
const flag = this.flags.get(flagName);
if (!flag) {
return false;
}
// Check if flag is active
if (!flag.active) {
return false;
}
// Check targeting
if (flag.targeting) {
return this.evaluateTargeting(flag.targeting, context);
}
// Rollout percentage
if (flag.rolloutPercentage !== undefined) {
return this.evaluateRollout(flag.rolloutPercentage, context);
}
return flag.defaultValue || false;
}
async setFlag(flag: FeatureFlag): Promise<void> {
this.flags.set(flag.name, flag);
await this.storage.save(flag);
}
private evaluateTargeting(targeting: Targeting, context: any): boolean {
// Evaluate targeting rules
return true;
}
private evaluateRollout(percentage: number, context: any): boolean {
// Evaluate rollout percentage
return Math.random() * 100 < percentage;
}
private async loadFlags(): Promise<void> {
const flags = await this.storage.getAll();
for (const flag of flags) {
this.flags.set(flag.name, flag);
}
}
}Service discovery with health checks dynamically registers and deregisters services based on their health status.
- Health Check: Monitor service health
- Registration: Register healthy services
- Deregistration: Remove unhealthy services
- Load Balancing: Select healthy instances
- Consul/etcd: Service discovery tools
// System Design - Service Discovery with Health Checks
// Service discovery with health checks
class ServiceDiscoveryWithHealth {
private services: Map<string, ServiceInstance[]>;
private healthCheck: HealthCheck;
private loadBalancer: LoadBalancer;
private deregisterTimeout: number;
constructor(healthCheck: HealthCheck, loadBalancer: LoadBalancer, deregisterTimeout: number = 30000) {
this.services = new Map();
this.healthCheck = healthCheck;
this.loadBalancer = loadBalancer;
this.deregisterTimeout = deregisterTimeout;
this.startHealthCheck();
}
register(serviceName: string, instance: ServiceInstance): void {
if (!this.services.has(serviceName)) {
this.services.set(serviceName, []);
}
this.services.get(serviceName).push(instance);
}
deregister(serviceName: string, instanceId: string): void {
const instances = this.services.get(serviceName);
if (instances) {
this.services.set(
serviceName,
instances.filter(i => i.id !== instanceId)
);
}
}
discover(serviceName: string): ServiceInstance | null {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
return null;
}
// Filter healthy instances
const healthyInstances = instances.filter(i => i.isHealthy());
if (healthyInstances.length === 0) {
// Try to revive unhealthy instances
this.tryRevive(serviceName);
return null;
}
return this.loadBalancer.select(healthyInstances);
}
private startHealthCheck(): void {
setInterval(async () => {
for (const [serviceName, instances] of this.services) {
const updatedInstances = await Promise.all(
instances.map(async (instance) => {
const isHealthy = await this.healthCheck.check(instance);
instance.setHealth(isHealthy);
return instance;
})
);
// Keep only healthy instances or those that can be revived
const validInstances = updatedInstances.filter(
instance => instance.isHealthy() ||
(Date.now() - instance.lastCheck < this.deregisterTimeout)
);
this.services.set(serviceName, validInstances);
}
}, 10000);
}
private async tryRevive(serviceName: string): Promise<void> {
const instances = this.services.get(serviceName);
if (!instances) return;
for (const instance of instances) {
if (await this.healthCheck.check(instance)) {
instance.setHealth(true);
break;
}
}
}
}API versioning allows multiple versions of an API to coexist, enabling backward-compatible evolution.
- URL Path: /api/v1/users
- Headers: Accept-Version: v1
- Query Parameters: ?version=v1
- Media Type: Content-Type: application/vnd.api.v1+json
- Versioning Strategy: Semantic versioning
// System Design - API Versioning
// API versioning strategies
class APIVersioning {
private versions: Map<string, APIVersion>;
private defaultVersion: string;
constructor(defaultVersion: string = 'v1') {
this.versions = new Map();
this.defaultVersion = defaultVersion;
}
registerVersion(version: APIVersion): void {
this.versions.set(version.name, version);
}
getVersion(request: Request): APIVersion {
// URL path versioning: /api/v1/users
const pathVersion = this.extractPathVersion(request.url);
if (pathVersion && this.versions.has(pathVersion)) {
return this.versions.get(pathVersion);
}
// Header versioning: Accept-Version: v1
const headerVersion = this.extractHeaderVersion(request.headers);
if (headerVersion && this.versions.has(headerVersion)) {
return this.versions.get(headerVersion);
}
// Query parameter versioning: ?version=v1
const queryVersion = this.extractQueryVersion(request.url);
if (queryVersion && this.versions.has(queryVersion)) {
return this.versions.get(queryVersion);
}
return this.versions.get(this.defaultVersion);
}
private extractPathVersion(url: string): string | null {
const match = url.match(//api/(vd+)/);
return match ? match[1] : null;
}
private extractHeaderVersion(headers: Headers): string | null {
return headers.get('Accept-Version') || null;
}
private extractQueryVersion(url: string): string | null {
const params = new URLSearchParams(url);
return params.get('version') || null;
}
}API documentation provides comprehensive information about API endpoints, parameters, responses, and usage examples.
- OpenAPI/Swagger: Specification format
- Endpoints: Paths and methods
- Parameters: Input requirements
- Responses: Output formats
- Examples: Usage examples
// System Design - API Documentation
// API documentation generation
class APIDocumentation {
private endpoints: Endpoint[];
private schemas: Schema[];
constructor() {
this.endpoints = [];
this.schemas = [];
}
addEndpoint(endpoint: Endpoint): void {
this.endpoints.push(endpoint);
}
addSchema(schema: Schema): void {
this.schemas.push(schema);
}
generateOpenAPI(): OpenAPIDocument {
return {
openapi: '3.0.0',
info: {
title: 'API Documentation',
version: '1.0.0'
},
paths: this.generatePaths(),
components: {
schemas: this.generateSchemas()
}
};
}
private generatePaths(): Paths {
const paths: Paths = {};
for (const endpoint of this.endpoints) {
paths[endpoint.path] = {
[endpoint.method]: {
summary: endpoint.summary,
description: endpoint.description,
parameters: endpoint.parameters,
responses: endpoint.responses
}
};
}
return paths;
}
private generateSchemas(): Schemas {
const schemas: Schemas = {};
for (const schema of this.schemas) {
schemas[schema.name] = {
type: schema.type,
properties: schema.properties,
required: schema.required
};
}
return schemas;
}
}Rate limiting with Redis uses Redis data structures to implement distributed rate limiting across multiple nodes.
- Sorted Sets: Track requests by timestamp
- Atomic Operations: Redis Lua scripts
- Sliding Window: Time-based window
- Token Bucket: Redis-based token bucket
- Distributed: Rate limiting across nodes
// System Design - API Rate Limiting with Redis
// Rate limiting with Redis
class RedisRateLimiter {
private redis: Redis;
private maxRequests: number;
private windowSize: number;
constructor(redis: Redis, maxRequests: number, windowSize: number) {
this.redis = redis;
this.maxRequests = maxRequests;
this.windowSize = windowSize;
}
async allowRequest(key: string): Promise<boolean> {
const now = Date.now();
const windowStart = now - this.windowSize;
const redisKey = `rate_limit:${key}`;
// Remove old requests
await this.redis.zremrangebyscore(redisKey, 0, windowStart);
// Count requests in current window
const count = await this.redis.zcard(redisKey);
if (count < this.maxRequests) {
await this.redis.zadd(redisKey, now, now.toString());
await this.redis.expire(redisKey, this.windowSize / 1000);
return true;
}
return false;
}
}API gateway with authentication handles user authentication and authorization before routing requests to services.
- JWT: JSON Web Token validation
- OAuth2: Authorization framework
- API Keys: Simple authentication
- User Context: Pass user info to services
- Role-based: Access control
// System Design - API Gateway with Authentication
// API gateway with JWT authentication
class JWTGateway {
private secret: string;
private routes: Route[];
constructor(secret: string, routes: Route[]) {
this.secret = secret;
this.routes = routes;
}
async handle(request: Request): Promise<Response> {
// Extract JWT token
const token = this.extractToken(request);
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
// Verify JWT
try {
const payload = await this.verifyToken(token);
// Add user info to request context
request.user = payload;
// Route request
return await this.routeRequest(request);
} catch (error) {
return new Response('Invalid token', { status: 401 });
}
}
private extractToken(request: Request): string | null {
const authHeader = request.headers.get('Authorization');
if (authHeader && authHeader.startsWith('Bearer ')) {
return authHeader.substring(7);
}
return null;
}
private async verifyToken(token: string): Promise<any> {
// Verify JWT token
return { userId: '123', roles: ['user'] };
}
private async routeRequest(request: Request): Promise<Response> {
// Route to appropriate service
return new Response('OK');
}
}API gateway with caching improves performance by caching responses at the gateway level.
- Cache Key: Based on request
- TTL: Cache expiration
- Cache Invalidation: Remove stale cache
- Response Caching: Cache API responses
- Cache Control: Cache headers
// System Design - API Gateway with Caching
// API gateway with caching
class CachingGateway {
private cache: Cache;
private routes: Route[];
private ttl: number;
constructor(cache: Cache, routes: Route[], ttl: number = 300) {
this.cache = cache;
this.routes = routes;
this.ttl = ttl;
}
async handle(request: Request): Promise<Response> {
// Check cache
const cacheKey = this.getCacheKey(request);
const cachedResponse = await this.cache.get(cacheKey);
if (cachedResponse) {
return cachedResponse;
}
// Route request
const response = await this.routeRequest(request);
// Cache successful responses
if (response.status === 200) {
await this.cache.set(cacheKey, response, this.ttl);
}
return response;
}
private getCacheKey(request: Request): string {
return `${request.method}:${request.url}`;
}
private async routeRequest(request: Request): Promise<Response> {
// Route to appropriate service
return new Response('OK');
}
}A GraphQL gateway provides a single GraphQL endpoint that combines multiple underlying GraphQL or REST services.
- Schema Federation: Combine schemas
- Query Planning: Optimize queries
- Federation: Apollo Federation
- Delegation: Forward subqueries
- Batching: Batch multiple requests
// System Design - GraphQL Gateway
// GraphQL gateway implementation
class GraphQLGateway {
private schemas: Schema[];
private resolvers: Resolvers;
constructor(schemas: Schema[], resolvers: Resolvers) {
this.schemas = schemas;
this.resolvers = resolvers;
}
async execute(query: string, variables: any): Promise<any> {
// Parse and validate query
const parsed = this.parseQuery(query);
this.validateQuery(parsed);
// Execute query
return await this.executeQuery(parsed, variables);
}
private parseQuery(query: string): ParsedQuery {
// Parse GraphQL query
return { operation: 'query', fields: [] };
}
private validateQuery(query: ParsedQuery): void {
// Validate against schema
}
private async executeQuery(query: ParsedQuery, variables: any): Promise<any> {
// Execute resolvers
const result = {};
for (const field of query.fields) {
const resolver = this.resolvers[field.name];
if (resolver) {
result[field.name] = await resolver(variables);
}
}
return result;
}
}A gRPC gateway enables RESTful HTTP/JSON access to gRPC services, providing interoperability.
- Protobuf: Protocol buffers
- HTTP/JSON: REST API access
- Transcoding: Convert between formats
- Interceptors: Middleware support
- Load Balancing: Distribute requests
// System Design - gRPC Gateway
// gRPC gateway implementation
class GRPCGateway {
private services: Service[];
private interceptors: Interceptor[];
constructor(services: Service[], interceptors: Interceptor[] = []) {
this.services = services;
this.interceptors = interceptors;
}
async call(serviceName: string, method: string, request: any): Promise<any> {
const service = this.services.find(s => s.name === serviceName);
if (!service) {
throw new Error(`Service ${serviceName} not found`);
}
const methodHandler = service.methods[method];
if (!methodHandler) {
throw new Error(`Method ${method} not found`);
}
// Apply interceptors
let context = { request };
for (const interceptor of this.interceptors) {
context = await interceptor(context);
}
// Execute method
return await methodHandler(context.request);
}
}A WebSocket gateway manages WebSocket connections, providing real-time bidirectional communication.
- Connection Management: Handle WebSocket connections
- Authentication: Secure WebSocket
- Room Management: Group communication
- Message Broadcasting: Send to multiple clients
- Scalability: Horizontal scaling
// System Design - WebSocket Gateway
// WebSocket gateway implementation
class WebSocketGateway {
private connections: Map<string, WebSocket>;
private rooms: Map<string, Set<string>>;
private authentication: AuthService;
constructor(authentication: AuthService) {
this.connections = new Map();
this.rooms = new Map();
this.authentication = authentication;
}
async handleConnection(ws: WebSocket, token: string): Promise<void> {
// Authenticate
const user = await this.authentication.authenticate(token);
if (!user) {
ws.close(1008, 'Unauthorized');
return;
}
const clientId = user.id;
this.connections.set(clientId, ws);
ws.on('message', (message) => {
this.handleMessage(clientId, message);
});
ws.on('close', () => {
this.handleDisconnect(clientId);
});
}
private handleMessage(clientId: string, message: any): void {
const data = JSON.parse(message);
switch (data.type) {
case 'join':
this.joinRoom(clientId, data.room);
break;
case 'leave':
this.leaveRoom(clientId, data.room);
break;
case 'message':
this.broadcast(data.room, {
from: clientId,
data: data.payload
});
break;
}
}
private joinRoom(clientId: string, room: string): void {
if (!this.rooms.has(room)) {
this.rooms.set(room, new Set());
}
this.rooms.get(room).add(clientId);
}
private leaveRoom(clientId: string, room: string): void {
const clients = this.rooms.get(room);
if (clients) {
clients.delete(clientId);
}
}
private broadcast(room: string, message: any): void {
const clients = this.rooms.get(room);
if (!clients) return;
for (const clientId of clients) {
const ws = this.connections.get(clientId);
if (ws) {
ws.send(JSON.stringify(message));
}
}
}
private handleDisconnect(clientId: string): void {
this.connections.delete(clientId);
for (const [room, clients] of this.rooms) {
clients.delete(clientId);
}
}
}Server-Sent Events enable servers to push real-time updates to clients over HTTP connections.
- Event Stream: Continuous updates
- Auto-reconnect: Automatic reconnection
- Event Types: Named events
- Last-Event-ID: Resume from last event
- Use cases: Notifications, live updates
// System Design - Server-Sent Events
// SSE implementation
class SSEHandler {
private clients: Map<string, Client>;
private events: Event[];
constructor() {
this.clients = new Map();
this.events = [];
}
subscribe(clientId: string, response: Response): void {
const client = {
id: clientId,
response,
lastEventId: 0
};
this.clients.set(clientId, client);
this.sendInitialEvents(client);
}
unsubscribe(clientId: string): void {
this.clients.delete(clientId);
}
emit(event: Event): void {
this.events.push(event);
this.broadcast(event);
}
private sendInitialEvents(client: Client): void {
const events = this.events.slice(client.lastEventId);
for (const event of events) {
this.sendEvent(client, event);
}
client.lastEventId = this.events.length;
}
private sendEvent(client: Client, event: Event): void {
const data = `event: ${event.type}\ndata: ${JSON.stringify(event.data)}\n\n`;
client.response.write(data);
}
private broadcast(event: Event): void {
for (const [id, client] of this.clients) {
this.sendEvent(client, event);
client.lastEventId = this.events.length;
}
}
}Data streaming enables continuous processing of data as it arrives, supporting real-time analytics and event-driven applications.
- Streams: Continuous data flow
- Partitions: Parallel processing
- Consumer Groups: Load balancing
- Exactly-Once: Delivery guarantees
- Kafka/Kinesis: Streaming platforms
// System Design - Data Streaming
// Data streaming implementation
class DataStream {
private streams: Map<string, Stream>;
private consumers: Map<string, Consumer>;
private partitions: Map<string, Partition>;
constructor() {
this.streams = new Map();
this.consumers = new Map();
this.partitions = new Map();
}
createStream(name: string, partitions: number): void {
this.streams.set(name, new Stream(name, partitions));
}
produce(streamName: string, key: string, value: any): void {
const stream = this.streams.get(streamName);
if (!stream) {
throw new Error(`Stream ${streamName} not found`);
}
stream.produce(key, value);
}
consume(streamName: string, consumerGroup: string): Consumer {
const stream = this.streams.get(streamName);
if (!stream) {
throw new Error(`Stream ${streamName} not found`);
}
const consumer = new Consumer(consumerGroup, stream);
this.consumers.set(consumer.id, consumer);
return consumer;
}
}A data lake is a centralized repository that stores vast amounts of raw data in its native format for future analysis.
- Raw Data: Unprocessed data
- Schema-on-Read: Apply schema when reading
- Scalability: Petabyte-scale storage
- Data Catalog: Data discovery
- Use cases: Big data, analytics
// System Design - Data Lake
// Data lake implementation
class DataLake {
private storage: Storage;
private catalog: Catalog;
private partitions: Partition[];
constructor(storage: Storage, catalog: Catalog) {
this.storage = storage;
this.catalog = catalog;
this.partitions = [];
}
async write(path: string, data: any): Promise<void> {
await this.storage.write(path, data);
this.catalog.addEntry(path, data);
}
async read(path: string): Promise<any> {
return await this.storage.read(path);
}
async query(query: string): Promise<any[]> {
// Query data lake
const entries = this.catalog.search(query);
const results = [];
for (const entry of entries) {
const data = await this.storage.read(entry.path);
results.push(data);
}
return results;
}
addPartition(partition: Partition): void {
this.partitions.push(partition);
}
}Data validation ensures data quality by checking data against predefined rules and schemas.
- Schema Validation: Structure validation
- Type Validation: Data type checks
- Range Validation: Value bounds
- Format Validation: Pattern matching
- Business Rules: Domain-specific validation
// System Design - Data Validation
// Data validation framework
class DataValidator {
private schemas: Map<string, Schema>;
constructor() {
this.schemas = new Map();
}
registerSchema(name: string, schema: Schema): void {
this.schemas.set(name, schema);
}
validate(data: any, schemaName: string): ValidationResult {
const schema = this.schemas.get(schemaName);
if (!schema) {
return { valid: false, errors: [`Schema ${schemaName} not found`] };
}
return this.validateAgainstSchema(data, schema);
}
private validateAgainstSchema(data: any, schema: Schema): ValidationResult {
const errors = [];
// Validate required fields
for (const field of schema.required) {
if (!data[field]) {
errors.push(`Missing required field: ${field}`);
}
}
// Validate types
for (const [field, type] of Object.entries(schema.properties)) {
if (data[field] !== undefined) {
if (!this.validateType(data[field], type)) {
errors.push(`Invalid type for field: ${field}`);
}
}
}
return {
valid: errors.length === 0,
errors
};
}
private validateType(value: any, type: string): boolean {
switch (type) {
case 'string':
return typeof value === 'string';
case 'number':
return typeof value === 'number';
case 'boolean':
return typeof value === 'boolean';
case 'array':
return Array.isArray(value);
case 'object':
return typeof value === 'object' && !Array.isArray(value);
default:
return false;
}
}
}Data encryption protects sensitive data by converting it into an unreadable format using cryptographic algorithms.
- Symmetric Encryption: Same key for encrypt/decrypt
- Asymmetric Encryption: Public/private keys
- AES: Advanced Encryption Standard
- RSA: Public-key cryptography
- TLS/SSL: Transport layer security
// System Design - Data Encryption
// Data encryption implementation
class DataEncryption {
private algorithm: string;
private key: Buffer;
private iv: Buffer;
constructor(algorithm: string = 'aes-256-cbc') {
this.algorithm = algorithm;
this.key = Buffer.from(process.env.ENCRYPTION_KEY || 'default-key-32-characters-long!', 'utf-8');
this.iv = Buffer.from(process.env.IV || 'default-iv-16chars', 'utf-8');
}
encrypt(text: string): string {
const cipher = crypto.createCipheriv(this.algorithm, this.key, this.iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
decrypt(encrypted: string): string {
const decipher = crypto.createDecipheriv(this.algorithm, this.key, this.iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
encryptObject(obj: any): string {
return this.encrypt(JSON.stringify(obj));
}
decryptObject(encrypted: string): any {
return JSON.parse(this.decrypt(encrypted));
}
}Data compression reduces the size of data to save storage space and bandwidth using various algorithms.
- Lossless: Perfect reconstruction
- Lossy: Some data loss
- gzip: DEFLATE algorithm
- Zstandard: High compression ratio
- Use cases: Storage optimization, network transfer
// System Design - Data Compression
// Data compression implementation
class DataCompression {
compress(data: any): Buffer {
const json = JSON.stringify(data);
return zlib.gzipSync(json);
}
decompress(compressed: Buffer): any {
const decompressed = zlib.gunzipSync(compressed);
return JSON.parse(decompressed.toString());
}
async compressStream(stream: Readable): Promise<Buffer> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
const gzip = zlib.createGzip();
stream.pipe(gzip);
gzip.on('data', (chunk) => chunks.push(chunk));
gzip.on('end', () => resolve(Buffer.concat(chunks)));
gzip.on('error', reject);
});
}
async decompressStream(compressed: Buffer): Promise<Readable> {
const decompressed = zlib.gunzipSync(compressed);
return Readable.from(decompressed);
}
}Data masking protects sensitive data by replacing it with fictional but realistic data for non-production environments.
- Redaction: Remove sensitive data
- Partial Masking: Show part of data
- Format Preserving: Same format, different values
- Encryption: Encrypt sensitive data
- Use cases: Testing, development, analytics
// System Design - Data Masking
// Data masking implementation
class DataMasker {
private rules: Map<string, MaskingRule>;
constructor() {
this.rules = new Map();
}
addRule(field: string, rule: MaskingRule): void {
this.rules.set(field, rule);
}
mask(data: any): any {
const masked = { ...data };
for (const [field, rule] of this.rules) {
if (masked[field] !== undefined) {
masked[field] = this.applyRule(masked[field], rule);
}
}
return masked;
}
private applyRule(value: any, rule: MaskingRule): any {
switch (rule.type) {
case 'redact':
return '***';
case 'partial':
return this.partialMask(value, rule);
case 'replace':
return rule.replacement || '***';
case 'encrypt':
return this.encrypt(value);
default:
return value;
}
}
private partialMask(value: string, rule: MaskingRule): string {
const visible = rule.visible || 4;
if (value.length <= visible * 2) {
return '***';
}
return value.substring(0, visible) + '***' + value.substring(value.length - visible);
}
private encrypt(value: string): string {
// Basic encryption for masking
return Buffer.from(value).toString('base64');
}
}Data retention policies define how long data should be kept and when it should be deleted based on business and regulatory requirements.
- Time-based: Retention period
- Size-based: Storage limits
- Compliance: Regulatory requirements
- Automated Cleanup: Scheduled deletion
- Archiving: Move to cold storage
// System Design - Data Retention
// Data retention policy
class DataRetention {
private policies: Map<string, RetentionPolicy>;
private storage: Storage;
private cleanupSchedule: number;
constructor(storage: Storage, cleanupSchedule: number = 86400000) { // 24 hours
this.policies = new Map();
this.storage = storage;
this.cleanupSchedule = cleanupSchedule;
this.startCleanup();
}
addPolicy(collection: string, policy: RetentionPolicy): void {
this.policies.set(collection, policy);
}
async applyRetention(): Promise<void> {
for (const [collection, policy] of this.policies) {
await this.cleanupCollection(collection, policy);
}
}
private async cleanupCollection(collection: string, policy: RetentionPolicy): Promise<void> {
const cutoff = Date.now() - policy.duration;
const query = {
timestamp: { $lt: cutoff }
};
if (policy.maxSize) {
const count = await this.storage.count(collection);
if (count > policy.maxSize) {
const deleteCount = count - policy.maxSize;
await this.storage.deleteOldest(collection, deleteCount);
}
}
await this.storage.delete(collection, query);
}
private startCleanup(): void {
setInterval(async () => {
await this.applyRetention();
}, this.cleanupSchedule);
}
}Data archiving moves infrequently accessed data to long-term storage while keeping it accessible if needed.
- Cold Storage: Low-cost storage
- Compression: Space optimization
- Indexing: Searchable archives
- Retention: Long-term preservation
- Restoration: Access archived data
// System Design - Data Archiving
// Data archiving implementation
class DataArchiver {
private storage: Storage;
private archiveStorage: Storage;
private archivePolicy: ArchivePolicy;
constructor(storage: Storage, archiveStorage: Storage, archivePolicy: ArchivePolicy) {
this.storage = storage;
this.archiveStorage = archiveStorage;
this.archivePolicy = archivePolicy;
}
async archive(): Promise<void> {
const cutoff = Date.now() - this.archivePolicy.duration;
const query = {
timestamp: { $lt: cutoff }
};
const data = await this.storage.find(query);
if (data.length > 0) {
await this.archiveStorage.save(data);
await this.storage.delete(query);
console.log(`Archived ${data.length} records`);
}
}
async restore(archiveId: string): Promise<void> {
const data = await this.archiveStorage.findById(archiveId);
if (data) {
await this.storage.save(data);
await this.archiveStorage.delete(archiveId);
}
}
}Data backup creates copies of data to protect against loss, corruption, or disaster.
- Full Backup: Complete data copy
- Incremental: Changes since last backup
- Differential: Changes since full backup
- Off-site: Remote backup storage
- Restore: Recover from backup
// System Design - Data Backup
// Data backup implementation
class DataBackup {
private storage: Storage;
private backupStorage: Storage;
private backupInterval: number;
constructor(storage: Storage, backupStorage: Storage, backupInterval: number = 86400000) {
this.storage = storage;
this.backupStorage = backupStorage;
this.backupInterval = backupInterval;
this.startBackup();
}
async createBackup(): Promise<string> {
const backupId = `backup-${Date.now()}`;
const data = await this.storage.getAll();
await this.backupStorage.save(backupId, data);
return backupId;
}
async restoreBackup(backupId: string): Promise<void> {
const data = await this.backupStorage.get(backupId);
if (data) {
await this.storage.restore(data);
}
}
private startBackup(): void {
setInterval(async () => {
try {
await this.createBackup();
console.log('Backup created successfully');
} catch (error) {
console.error('Backup failed:', error);
}
}, this.backupInterval);
}
}Data recovery restores data from backups or recovery points after data loss or corruption.
- Recovery Point: Restore point
- RPO: Recovery Point Objective
- RTO: Recovery Time Objective
- Restore: Data restoration
- Recovery Testing: Regular testing
// System Design - Data Recovery
// Data recovery implementation
class DataRecovery {
private recoveryPoints: Map<string, RecoveryPoint>;
private storage: Storage;
constructor(storage: Storage) {
this.recoveryPoints = new Map();
this.storage = storage;
}
createRecoveryPoint(): string {
const id = `recovery-${Date.now()}`;
const data = this.storage.getState();
this.recoveryPoints.set(id, {
id,
timestamp: Date.now(),
data
});
return id;
}
recover(id: string): boolean {
const recoveryPoint = this.recoveryPoints.get(id);
if (!recoveryPoint) {
return false;
}
this.storage.restore(recoveryPoint.data);
return true;
}
listRecoveryPoints(): RecoveryPoint[] {
return Array.from(this.recoveryPoints.values());
}
}Data auditing tracks and logs data access and modifications for compliance, security, and troubleshooting.
- Audit Log: Track changes
- User Activity: Who accessed what
- Compliance: Regulatory requirements
- Filtering: Focus on specific events
- Reporting: Audit reports
// System Design - Data Auditing
// Data auditing implementation
class DataAuditor {
private logs: AuditLog[];
private storage: Storage;
private filters: AuditFilter[];
constructor(storage: Storage) {
this.logs = [];
this.storage = storage;
this.filters = [];
}
logAction(action: AuditAction): void {
const log: AuditLog = {
id: `audit-${Date.now()}`,
action,
timestamp: Date.now(),
user: action.user,
details: action.details
};
this.logs.push(log);
this.storage.save('audit', log);
}
addFilter(filter: AuditFilter): void {
this.filters.push(filter);
}
query(startDate: Date, endDate: Date): AuditLog[] {
let results = this.logs.filter(log => {
return log.timestamp >= startDate.getTime() && log.timestamp <= endDate.getTime();
});
for (const filter of this.filters) {
results = filter.apply(results);
}
return results;
}
}Data governance ensures data quality, security, compliance, and proper management across the organization.
- Policies: Rules and standards
- Compliance: Regulatory adherence
- Data Quality: Accuracy and completeness
- Data Catalog: Data discovery
- Lineage: Data origin and flow
// System Design - Data Governance
// Data governance implementation
class DataGovernance {
private policies: Map<string, GovernancePolicy>;
private compliance: ComplianceFramework;
private dataCatalog: DataCatalog;
constructor(compliance: ComplianceFramework, dataCatalog: DataCatalog) {
this.policies = new Map();
this.compliance = compliance;
this.dataCatalog = dataCatalog;
}
addPolicy(name: string, policy: GovernancePolicy): void {
this.policies.set(name, policy);
}
async enforcePolicies(data: any): Promise<boolean> {
for (const policy of this.policies.values()) {
if (!await policy.validate(data)) {
return false;
}
}
return true;
}
async classifyData(data: any): Promise<DataClassification> {
return await this.compliance.classify(data);
}
async getLineage(dataId: string): Promise<DataLineage> {
return await this.dataCatalog.getLineage(dataId);
}
}Data privacy protects personal and sensitive information, ensuring proper handling and consent management.
- Consent: User permission
- Anonymization: Remove PII
- Pseudonymization: Replace identifiers
- Compliance: GDPR, CCPA
- Data Subject Rights: Access, deletion
// System Design - Data Privacy
// Data privacy implementation
class DataPrivacy {
private privacyPolicies: PrivacyPolicy[];
private consentManager: ConsentManager;
constructor(consentManager: ConsentManager) {
this.privacyPolicies = [];
this.consentManager = consentManager;
}
addPolicy(policy: PrivacyPolicy): void {
this.privacyPolicies.push(policy);
}
async processData(data: any, userId: string): Promise<any> {
// Check consent
const consent = await this.consentManager.getConsent(userId);
if (!consent) {
throw new Error('User consent not provided');
}
// Apply privacy policies
let processedData = data;
for (const policy of this.privacyPolicies) {
processedData = await policy.apply(processedData, consent);
}
return processedData;
}
async anonymize(data: any): Promise<any> {
// Remove PII (Personally Identifiable Information)
const anonymized = { ...data };
delete anonymized.name;
delete anonymized.email;
delete anonymized.phone;
return anonymized;
}
}Data federation provides a unified view of data from multiple sources without physically moving the data.
- Virtualization: Logical view
- Query Federation: Query across sources
- Data Sources: Multiple databases
- Transformation: Data normalization
- Performance: Query optimization
// System Design - Data Federation
// Data federation implementation
class DataFederation {
private dataSources: Map<string, DataSource>;
private queryEngine: QueryEngine;
constructor(queryEngine: QueryEngine) {
this.dataSources = new Map();
this.queryEngine = queryEngine;
}
addDataSource(name: string, source: DataSource): void {
this.dataSources.set(name, source);
}
async query(query: string): Promise<any[]> {
const parsedQuery = this.queryEngine.parse(query);
const results = [];
for (const source of this.dataSources.values()) {
if (this.isRelevantSource(source, parsedQuery)) {
const result = await source.query(parsedQuery);
results.push(result);
}
}
return this.queryEngine.merge(results);
}
private isRelevantSource(source: DataSource, query: ParsedQuery): boolean {
return source.supports(query);
}
}Data warehousing design involves creating structures for efficient analytical querying and reporting.
- Fact Tables: Measure data
- Dimension Tables: Descriptive attributes
- Star Schema: Central fact table
- Snowflake Schema: Normalized dimensions
- OLAP Cubes: Multi-dimensional analysis
// System Design - Data Warehousing
// Data warehouse design
class DataWarehouseDesign {
private factTables: FactTable[];
private dimensionTables: DimensionTable[];
private aggregations: Aggregation[];
constructor() {
this.factTables = [];
this.dimensionTables = [];
this.aggregations = [];
}
addFactTable(fact: FactTable): void {
this.factTables.push(fact);
}
addDimensionTable(dimension: DimensionTable): void {
this.dimensionTables.push(dimension);
}
addAggregation(aggregation: Aggregation): void {
this.aggregations.push(aggregation);
}
async query(sql: string): Promise<any[]> {
// Execute OLAP query
return [];
}
}A data lakehouse combines the flexibility of data lakes with the structure and performance of data warehouses.
- ACID Transactions: Data consistency
- Schema Enforcement: Data quality
- Time Travel: Historical data access
- Open Format: Parquet, ORC
- Delta Lake: Lakehouse implementation
// System Design - Data Lakehouse
// Data lakehouse implementation
class DataLakehouse {
private storage: Storage;
private catalog: Catalog;
private compute: ComputeEngine;
constructor(storage: Storage, catalog: Catalog, compute: ComputeEngine) {
this.storage = storage;
this.catalog = catalog;
this.compute = compute;
}
async write(path: string, data: any): Promise<void> {
await this.storage.write(path, data);
await this.catalog.index(path, data);
}
async read(path: string): Promise<any> {
return await this.storage.read(path);
}
async query(sql: string): Promise<any[]> {
const plan = await this.compute.optimize(sql);
return await this.compute.execute(plan);
}
}Data mesh is a decentralized data architecture that treats data as a product, with domain-oriented ownership.
- Domain Ownership: Data owned by domains
- Data Products: Self-contained data services
- Self-serve: Data infrastructure
- Federated Governance: Distributed governance
- Data-as-a-Product: Data product mindset
// System Design - Data Mesh
// Data mesh implementation
class DataMesh {
private domains: Domain[];
private dataProducts: DataProduct[];
private governance: DataGovernance;
constructor(governance: DataGovernance) {
this.domains = [];
this.dataProducts = [];
this.governance = governance;
}
addDomain(domain: Domain): void {
this.domains.push(domain);
}
addDataProduct(product: DataProduct): void {
this.dataProducts.push(product);
}
async query(domainName: string, query: string): Promise<any[]> {
const domain = this.domains.find(d => d.name === domainName);
if (!domain) {
throw new Error(`Domain ${domainName} not found`);
}
return await domain.query(query);
}
async discover(query: string): Promise<DataProduct[]> {
return this.dataProducts.filter(p => p.matches(query));
}
}Data integration combines data from different sources into a unified view, enabling comprehensive analysis.
- ETL: Extract, Transform, Load
- ELT: Extract, Load, Transform
- Data Pipeline: Automated data flow
- Data Quality: Validation and cleaning
- Batch/Streaming: Processing modes
// System Design - Data Integration
// Data integration implementation
class DataIntegration {
private sources: DataSource[];
private targets: DataTarget[];
private transformations: Transformation[];
private schedule: Schedule;
constructor(schedule: Schedule) {
this.sources = [];
this.targets = [];
this.transformations = [];
this.schedule = schedule;
this.start();
}
addSource(source: DataSource): void {
this.sources.push(source);
}
addTarget(target: DataTarget): void {
this.targets.push(target);
}
addTransformation(transformation: Transformation): void {
this.transformations.push(transformation);
}
async run(): Promise<void> {
for (const source of this.sources) {
let data = await source.extract();
for (const transformation of this.transformations) {
data = await transformation.transform(data);
}
for (const target of this.targets) {
await target.load(data);
}
}
}
private start(): void {
this.schedule.run(async () => {
await this.run();
});
}
}Data synchronization ensures data consistency across multiple systems by propagating changes in real-time or near-real-time.
- Change Data Capture: Detect changes
- Conflict Resolution: Handle conflicts
- Bidirectional: Two-way sync
- Polling: Periodic sync
- Event-driven: Sync on events
// System Design - Data Synchronization
// Data synchronization implementation
class DataSync {
private sources: DataSource[];
private target: DataTarget;
private conflictResolver: ConflictResolver;
private syncStrategy: SyncStrategy;
constructor(target: DataTarget, conflictResolver: ConflictResolver, syncStrategy: SyncStrategy) {
this.sources = [];
this.target = target;
this.conflictResolver = conflictResolver;
this.syncStrategy = syncStrategy;
}
addSource(source: DataSource): void {
this.sources.push(source);
}
async sync(): Promise<void> {
const changes = [];
for (const source of this.sources) {
const sourceChanges = await source.getChanges();
changes.push(...sourceChanges);
}
const resolved = await this.conflictResolver.resolve(changes);
const merged = await this.syncStrategy.merge(resolved);
await this.target.apply(merged);
}
}Data quality ensures data is accurate, complete, consistent, and fit for its intended purpose.
- Accuracy: Correct data
- Completeness: All required data
- Consistency: Uniform data
- Timeliness: Up-to-date data
- Validity: Data meets rules
// System Design - Data Quality
// Data quality framework
class DataQuality {
private rules: QualityRule[];
private metrics: QualityMetric[];
private reporter: QualityReporter;
constructor(reporter: QualityReporter) {
this.rules = [];
this.metrics = [];
this.reporter = reporter;
}
addRule(rule: QualityRule): void {
this.rules.push(rule);
}
addMetric(metric: QualityMetric): void {
this.metrics.push(metric);
}
async assess(data: any): Promise<QualityReport> {
const results = [];
// Apply rules
for (const rule of this.rules) {
const result = await rule.check(data);
results.push(result);
}
// Calculate metrics
const metrics = {};
for (const metric of this.metrics) {
metrics[metric.name] = await metric.calculate(data);
}
return {
results,
metrics,
timestamp: Date.now()
};
}
}Data observability monitors and alerts on data quality, health, and anomalies in data pipelines and systems.
- Data Monitoring: Track data quality
- Alerting: Notify on issues
- Data Lineage: Track data flow
- Dashboard: Visualize metrics
- Anomaly Detection: Identify outliers
// System Design - Data Observability
// Data observability implementation
class DataObservability {
private monitors: DataMonitor[];
private alerts: Alert[];
private dashboard: Dashboard;
constructor(dashboard: Dashboard) {
this.monitors = [];
this.alerts = [];
this.dashboard = dashboard;
}
addMonitor(monitor: DataMonitor): void {
this.monitors.push(monitor);
}
addAlert(alert: Alert): void {
this.alerts.push(alert);
}
async check(): Promise<void> {
for (const monitor of this.monitors) {
const status = await monitor.check();
if (status === 'critical') {
for (const alert of this.alerts) {
await alert.trigger(monitor);
}
}
this.dashboard.update(monitor, status);
}
}
}Data lineage tracks the flow of data from its origin to its destination, showing transformations and dependencies.
- Data Flow: Movement of data
- Transformations: Data changes
- Dependencies: Data relationships
- Impact Analysis: Change effects
- Audit: Data provenance
// System Design - Data Lineage
// Data lineage implementation
class DataLineage {
private nodes: LineageNode[];
private edges: LineageEdge[];
private graph: Graph;
constructor() {
this.nodes = [];
this.edges = [];
this.graph = new Graph();
}
addNode(node: LineageNode): void {
this.nodes.push(node);
this.graph.addNode(node);
}
addEdge(edge: LineageEdge): void {
this.edges.push(edge);
this.graph.addEdge(edge);
}
getLineage(dataId: string): LineageGraph {
const path = this.graph.findPath(dataId);
return {
nodes: path.nodes,
edges: path.edges
};
}
}A data catalog organizes and manages metadata, enabling data discovery and understanding across the organization.
- Metadata: Data about data
- Search: Data discovery
- Tagging: Data classification
- Data Dictionary: Field descriptions
- Collaboration: Data sharing
// System Design - Data Catalog
// Data catalog implementation
class DataCatalog {
private assets: DataAsset[];
private tags: Map<string, string[]>;
private searchEngine: SearchEngine;
constructor(searchEngine: SearchEngine) {
this.assets = [];
this.tags = new Map();
this.searchEngine = searchEngine;
}
addAsset(asset: DataAsset): void {
this.assets.push(asset);
this.searchEngine.index(asset);
}
addTag(assetId: string, tag: string): void {
if (!this.tags.has(assetId)) {
this.tags.set(assetId, []);
}
this.tags.get(assetId).push(tag);
}
search(query: string): DataAsset[] {
return this.searchEngine.search(query);
}
getAsset(id: string): DataAsset | undefined {
return this.assets.find(a => a.id === id);
}
}A data marketplace enables data providers to publish and monetize data, and consumers to discover and purchase data.
- Data Providers: Data publishers
- Data Consumers: Data buyers
- Pricing: Data pricing models
- Access Control: Data access management
- Billing: Usage-based billing
// System Design - Data Marketplace
// Data marketplace implementation
class DataMarketplace {
private providers: DataProvider[];
private consumers: DataConsumer[];
private products: DataProduct[];
private pricing: PricingEngine;
private billing: BillingSystem;
constructor(pricing: PricingEngine, billing: BillingSystem) {
this.providers = [];
this.consumers = [];
this.products = [];
this.pricing = pricing;
this.billing = billing;
}
registerProvider(provider: DataProvider): void {
this.providers.push(provider);
}
registerConsumer(consumer: DataConsumer): void {
this.consumers.push(consumer);
}
publishProduct(product: DataProduct): void {
this.products.push(product);
}
async purchase(consumerId: string, productId: string): Promise<void> {
const product = this.products.find(p => p.id === productId);
if (!product) {
throw new Error('Product not found');
}
const price = await this.pricing.calculate(product);
await this.billing.charge(consumerId, price);
await this.provideAccess(consumerId, product);
}
private async provideAccess(consumerId: string, product: DataProduct): Promise<void> {
// Provide access to the data
}
}A data API provides programmatic access to data, enabling integration with applications and services.
- REST API: HTTP-based API
- GraphQL: Query language
- Rate Limiting: Usage control
- Authentication: Access control
- Documentation: API documentation
// System Design - Data API
// Data API implementation
class DataAPI {
private endpoints: APIEndpoint[];
private authentication: AuthService;
private rateLimit: RateLimiter;
private cache: Cache;
constructor(authentication: AuthService, rateLimit: RateLimiter, cache: Cache) {
this.endpoints = [];
this.authentication = authentication;
this.rateLimit = rateLimit;
this.cache = cache;
}
addEndpoint(endpoint: APIEndpoint): void {
this.endpoints.push(endpoint);
}
async handle(request: Request): Promise<Response> {
// Authenticate
const user = await this.authentication.authenticate(request);
if (!user) {
return new Response('Unauthorized', { status: 401 });
}
// Rate limit
if (!await this.rateLimit.allow(user.id)) {
return new Response('Too Many Requests', { status: 429 });
}
// Route to endpoint
const endpoint = this.findEndpoint(request);
if (!endpoint) {
return new Response('Not Found', { status: 404 });
}
// Cache
const cacheKey = this.getCacheKey(request);
const cached = await this.cache.get(cacheKey);
if (cached) {
return cached;
}
// Execute
const response = await endpoint.execute(request);
await this.cache.set(cacheKey, response);
return response;
}
private findEndpoint(request: Request): APIEndpoint | undefined {
return this.endpoints.find(e => e.path === request.url.pathname);
}
private getCacheKey(request: Request): string {
return `${request.method}:${request.url}`;
}
}Data export extracts data from a system into various formats for analysis, reporting, or integration.
- Formats: CSV, JSON, Excel
- Batch: Scheduled export
- Streaming: Real-time export
- Filtering: Selective export
- Compression: Space optimization
// System Design - Data Export
// Data export implementation
class DataExporter {
private formats: Map<string, ExportFormat>;
private storage: Storage;
constructor(storage: Storage) {
this.formats = new Map();
this.storage = storage;
}
addFormat(name: string, format: ExportFormat): void {
this.formats.set(name, format);
}
async exportData(data: any, format: string, options?: ExportOptions): Promise<string> {
const exporter = this.formats.get(format);
if (!exporter) {
throw new Error(`Format ${format} not supported`);
}
const exported = await exporter.export(data, options);
const path = `exports/${Date.now()}.${format}`;
await this.storage.write(path, exported);
return path;
}
}Data import loads data from external sources into a system, with validation and transformation.
- Formats: CSV, JSON, Excel
- Validation: Data validation
- Transformation: Data mapping
- Batch: Bulk import
- Error Handling: Failed records
// System Design - Data Import
// Data import implementation
class DataImporter {
private parsers: Map<string, DataParser>;
private validators: DataValidator[];
private transformers: DataTransformer[];
constructor() {
this.parsers = new Map();
this.validators = [];
this.transformers = [];
}
addParser(format: string, parser: DataParser): void {
this.parsers.set(format, parser);
}
addValidator(validator: DataValidator): void {
this.validators.push(validator);
}
addTransformer(transformer: DataTransformer): void {
this.transformers.push(transformer);
}
async importData(file: Buffer, format: string): Promise<any> {
const parser = this.parsers.get(format);
if (!parser) {
throw new Error(`Format ${format} not supported`);
}
let data = await parser.parse(file);
for (const validator of this.validators) {
if (!await validator.validate(data)) {
throw new Error('Data validation failed');
}
}
for (const transformer of this.transformers) {
data = await transformer.transform(data);
}
return data;
}
}Data transformation converts data from one format or structure to another, enabling integration and analysis.
- Mapping: Field mapping
- Cleaning: Data cleansing
- Enrichment: Add data
- Aggregation: Summarize data
- Normalization: Standardize format
// System Design - Data Transformation
// Data transformation pipeline
class DataTransformationPipeline {
private stages: TransformationStage[];
constructor() {
this.stages = [];
}
addStage(stage: TransformationStage): void {
this.stages.push(stage);
}
async transform(data: any): Promise<any> {
let current = data;
for (const stage of this.stages) {
current = await stage.process(current);
}
return current;
}
}Data enrichment enhances data by adding additional information from internal or external sources.
- External Data: Third-party data
- Geocoding: Location data
- Demographic: Demographic data
- Real-time: Live enrichment
- Batch: Periodic enrichment
// System Design - Data Enrichment
// Data enrichment implementation
class DataEnricher {
private sources: EnrichmentSource[];
private cache: Cache;
private batchSize: number;
constructor(cache: Cache, batchSize: number = 100) {
this.sources = [];
this.cache = cache;
this.batchSize = batchSize;
}
addSource(source: EnrichmentSource): void {
this.sources.push(source);
}
async enrich(data: any[]): Promise<any[]> {
const enriched = [];
const batches = this.chunk(data, this.batchSize);
for (const batch of batches) {
const results = await Promise.all(
batch.map(item => this.enrichItem(item))
);
enriched.push(...results);
}
return enriched;
}
private async enrichItem(item: any): Promise<any> {
let enriched = { ...item };
for (const source of this.sources) {
const key = this.getKey(item, source);
const cached = await this.cache.get(key);
if (cached) {
enriched = { ...enriched, ...cached };
} else {
const result = await source.get(item);
if (result) {
await this.cache.set(key, result);
enriched = { ...enriched, ...result };
}
}
}
return enriched;
}
private getKey(item: any, source: EnrichmentSource): string {
return `${source.name}:${item.id}`;
}
private chunk<T>(arr: T[], size: number): T[][] {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}
}Data purging permanently deletes data that is no longer needed, freeing up storage and improving performance.
- Retention Policy: Keep criteria
- Compliance: Regulatory compliance
- Performance: Improve performance
- Archive: Move to archive first
- Secure Delete: Irreversible deletion
// System Design - Data Purging
// Data purging implementation
class DataPurger {
private policies: Map<string, PurgePolicy>;
private storage: Storage;
constructor(storage: Storage) {
this.policies = new Map();
this.storage = storage;
}
addPolicy(collection: string, policy: PurgePolicy): void {
this.policies.set(collection, policy);
}
async purge(): Promise<void> {
for (const [collection, policy] of this.policies) {
await this.purgeCollection(collection, policy);
}
}
private async purgeCollection(collection: string, policy: PurgePolicy): Promise<void> {
const cutoff = Date.now() - policy.duration;
const query = {
timestamp: { $lt: cutoff }
};
await this.storage.delete(collection, query);
console.log(`Purged records from ${collection}`);
}
}Data normalization organizes data to reduce redundancy and improve integrity, following normal forms.
- 1NF: Atomic values
- 2NF: Remove partial dependencies
- 3NF: Remove transitive dependencies
- BCNF: Boyce-Codd Normal Form
- Denormalization: Performance optimization
// System Design - Data Normalization
// Data normalization implementation
class DataNormalizer {
private rules: NormalizationRule[];
constructor() {
this.rules = [];
}
addRule(rule: NormalizationRule): void {
this.rules.push(rule);
}
normalize(data: any): any {
let normalized = { ...data };
for (const rule of this.rules) {
normalized = this.applyRule(normalized, rule);
}
return normalized;
}
private applyRule(data: any, rule: NormalizationRule): any {
const result = { ...data };
switch (rule.type) {
case 'format':
if (data[rule.field]) {
result[rule.field] = this.formatValue(data[rule.field], rule.format);
}
break;
case 'convert':
if (data[rule.field]) {
result[rule.field] = this.convertValue(data[rule.field], rule.targetType);
}
break;
case 'transform':
if (data[rule.field]) {
result[rule.field] = rule.transformation(data[rule.field]);
}
break;
case 'validate':
if (!rule.validation(data[rule.field])) {
result[rule.field] = rule.defaultValue || null;
}
break;
}
return result;
}
private formatValue(value: any, format: string): any {
// Apply formatting
return value;
}
private convertValue(value: any, targetType: string): any {
// Convert to target type
return value;
}
}Data deduplication removes duplicate copies of data to save storage space and improve efficiency.
- Hash-based: Identify duplicates by hash
- Block-level: Deduplicate at block level
- File-level: Deduplicate entire files
- In-line: Real-time deduplication
- Post-process: Batch deduplication
// System Design - Data Deduplication
// Data deduplication implementation
class DataDeduplicator {
private storage: Storage;
private hashAlgorithm: string;
private cache: Cache;
constructor(storage: Storage, cache: Cache, hashAlgorithm: string = 'sha256') {
this.storage = storage;
this.cache = cache;
this.hashAlgorithm = hashAlgorithm;
}
async deduplicate(data: any): Promise<any> {
const hash = this.hash(data);
const existing = await this.cache.get(hash);
if (existing) {
return existing;
}
await this.storage.save(data);
await this.cache.set(hash, data);
return data;
}
private hash(data: any): string {
const json = JSON.stringify(data);
return crypto.createHash(this.hashAlgorithm).update(json).digest('hex');
}
}Data versioning tracks and manages different versions of data, enabling rollback and historical analysis.
- Version Control: Track changes
- Snapshot: Point-in-time capture
- Rollback: Revert to previous version
- Audit Trail: Change history
- Conflict Resolution: Handle concurrent updates
// System Design - Data Versioning
// Data versioning implementation
class DataVersioning {
private versions: Map<string, Version[]>;
private storage: Storage;
private maxVersions: number;
constructor(storage: Storage, maxVersions: number = 10) {
this.versions = new Map();
this.storage = storage;
this.maxVersions = maxVersions;
}
async save(id: string, data: any): Promise<void> {
const version: Version = {
id: `v${Date.now()}`,
data,
timestamp: Date.now()
};
if (!this.versions.has(id)) {
this.versions.set(id, []);
}
const versions = this.versions.get(id);
versions.push(version);
if (versions.length > this.maxVersions) {
const oldest = versions.shift();
await this.storage.archive(id, oldest);
}
await this.storage.save(id, data);
}
async get(id: string, versionId?: string): Promise<any> {
if (versionId) {
const versions = this.versions.get(id);
if (!versions) return null;
const version = versions.find(v => v.id === versionId);
return version ? version.data : null;
}
return await this.storage.get(id);
}
async getVersions(id: string): Promise<Version[]> {
return this.versions.get(id) || [];
}
}Data migration strategy defines how data is moved from source to target systems with minimal disruption and risk.
- Big Bang: One-time migration
- Trickle: Incremental migration
- Parallel Run: Run both systems
- Cutover: Switch to new system
- Validation: Data integrity checks
// System Design - Data Migration Strategy
// Data migration strategy implementation
class DataMigrationStrategy {
private stages: MigrationStage[];
private validators: MigrationValidator[];
private rollbackStrategy: RollbackStrategy;
constructor(rollbackStrategy: RollbackStrategy) {
this.stages = [];
this.validators = [];
this.rollbackStrategy = rollbackStrategy;
}
addStage(stage: MigrationStage): void {
this.stages.push(stage);
}
addValidator(validator: MigrationValidator): void {
this.validators.push(validator);
}
async migrate(): Promise<void> {
let currentStage = 0;
try {
for (let i = 0; i < this.stages.length; i++) {
currentStage = i;
await this.stages[i].execute();
// Validate after each stage
for (const validator of this.validators) {
if (!await validator.validate()) {
throw new Error(`Validation failed at stage ${i}`);
}
}
}
} catch (error) {
console.error(`Migration failed at stage ${currentStage}: ${error}`);
await this.rollbackStrategy.rollback(currentStage);
throw error;
}
}
}