Understanding Scalability
Scalability is the ability of a system to handle growing amounts of work by adding resources to the system. In 2025, with cloud-native technologies and modern infrastructure, choosing the right scalability pattern is crucial for cost-effective performance.
Scalability Spectrum:
Vertical Scaling (Scale Up):
Single Server → More Powerful Server
2 cores → 8 cores → 32 cores → 128 cores
8 GB RAM → 32 GB RAM → 128 GB RAM → 512 GB RAM
Horizontal Scaling (Scale Out):
Multiple Servers → More Servers
1 server → 3 servers → 10 servers → 100 servers
+------------+ +------------+ +------------+
| Server 1 | | Server 2 | | Server 3 |
| (small) | | (small) | | (small) |
+------------+ +------------+ +------------+
| | |
+------------------+------------------+
|
Load Balancer
|
ClientVertical Scaling Deep Dive
When to Use Vertical Scaling
Vertical Scaling Best Use Cases:
1. Simple applications without distribution complexity
2. Monolithic databases (initial stages)
3. Applications with high single-threaded performance needs
4. Memory-intensive workloads (in-memory databases)
5. Development and testing environments
Advantages:
- Simple to implement
- No code changes required
- Lower operational complexity
- No distributed system challenges
- Consistent performance
Disadvantages:
- Upper limit on hardware
- Single point of failure
- Expensive at scale
- Vendor lock-in
- Downtime during upgrades
Real-World Example: High-Performance Database
class VerticalScalingStrategy {
// Analyze if vertical scaling is appropriate
async shouldScaleVertically(metrics: SystemMetrics): Promise<boolean> {
// CPU is not the bottleneck
if (metrics.cpuUsage < 70) {
// Memory is the constraint
if (metrics.memoryUsage > 90) {
// Database benefits from more memory
if (this.isDatabase()) {
return true;
}
// Application can use larger cache
if (this.isCacheIntensive()) {
return true;
}
}
}
// Single-threaded workload
if (metrics.singleThreaded && metrics.cpuUsage > 90) {
// Better CPU will help
return true;
}
return false;
}
// Calculate optimal instance size
calculateOptimalSize(metrics: SystemMetrics): InstanceSpec {
// Memory sizing
const currentMemory = metrics.totalMemory;
const memoryUtilization = metrics.memoryUsage / 100;
// Target 70% utilization
const targetMemory = metrics.usedMemory / 0.7;
// Round up to available instance sizes
const memorySizes = [8, 16, 32, 64, 128, 256, 512]; // GB
const optimalMemory = memorySizes.find(
size => size >= targetMemory
) || memorySizes[memorySizes.length - 1];
// CPU sizing
const cpuCores = this.calculateCPUCores(metrics, optimalMemory);
return {
memory: optimalMemory,
cpuCores,
storage: this.calculateStorage(metrics)
};
}
private calculateCPUCores(metrics: SystemMetrics, memory: number): number {
// Ratio based on instance type
const cpuMemoryRatio = {
'general-purpose': 4, // 1 CPU per 4GB RAM
'compute-optimized': 2, // 1 CPU per 2GB RAM
'memory-optimized': 8 // 1 CPU per 8GB RAM
};
const workloadType = this.analyzeWorkloadType(metrics);
const ratio = cpuMemoryRatio[workloadType];
return Math.ceil(memory / ratio);
}
}Vertical Scaling Implementation
typescript
class AWSVerticalScaler {
async scaleDatabase(
instanceId: string,
targetInstanceType: string
): Promise<void> {
const rds = new AWS.RDS();
// Get current instance
const currentInstance = await rds.describeDBInstances({
DBInstanceIdentifier: instanceId
}).promise();
const currentType = currentInstance.DBInstances[0].DBInstanceClass;
console.log(`Scaling ${instanceId} from ${currentType} to ${targetInstanceType}`);
// Modify instance
await rds.modifyDBInstance({
DBInstanceIdentifier: instanceId,
DBInstanceClass: targetInstanceType,
ApplyImmediately: true,
AllocatedStorage: this.calculateNewStorage(currentInstance, targetInstanceType),
Iops: this.calculateNewIOPS(currentInstance, targetInstanceType)
}).promise();
// Wait for modification to complete
await this.waitForModification(instanceId);
console.log('Scaling complete');
}
private calculateNewStorage(
instance: any,
instanceType: string
): number {
// Storage scales with instance type
const storageRatios: Record<string, number> = {
'db.t3.micro': 20,
'db.t3.small': 30,
'db.t3.medium': 50,
'db.m5.large': 100,
'db.m5.xlarge': 200,
'db.m5.2xlarge': 400,
'db.m5.4xlarge': 800,
'db.r5.large': 150,
'db.r5.xlarge': 300,
'db.r5.2xlarge': 600,
'db.r5.4xlarge': 1200
};
const ratio = storageRatios[instanceType] || 100;
const currentStorage = instance.AllocatedStorage;
return Math.max(currentStorage, ratio);
}
private calculateNewIOPS(
instance: {
AllocatedStorage: number;
StorageType: string;
},
instanceType: string
): number {
// IOPS scale with storage for io1
if (instance.StorageType === 'io1') {
// 50 IOPS per GB, max 64,000
return Math.min(instance.AllocatedStorage * 50, 64000);
}
// General Purpose SSD (gp3) gets baseline IOPS
return 3000; // Baseline for gp3
}
private async waitForModification(instanceId: string): Promise<void> {
const rds = new AWS.RDS();
return new Promise((resolve, reject) => {
const checkStatus = async () => {
const response = await rds.describeDBInstances({
DBInstanceIdentifier: instanceId
}).promise();
const instance = response.DBInstances[0];
const status = instance.DBInstanceStatus;
if (status === 'available') {
resolve();
} else if (status === 'modifying') {
setTimeout(checkStatus, 30000); // Check every 30 seconds
} else {
reject(new Error(`Unexpected status: ${status}`));
}
};
checkStatus();
});
}
}Horizontal Scaling Deep Dive
When to Use Horizontal Scaling
Horizontal Scaling Best Use Cases:
1. Stateless applications (web servers, APIs)
2. High availability requirements
3. Geographic distribution
4. Variable/ unpredictable workloads
5. Cost-effective scaling at scale
Advantages:
- No upper limit
- Better fault tolerance
- Cost-effective at scale
- Geographic distribution
- Flexible scaling (up and down)
Disadvantages:
- Increased complexity
- Requires stateless design
- Network overhead
- Consistency challenges
- Load balancing required
Real-World Example: Web Application
class HorizontalScalingStrategy {
async shouldScaleHorizontally(metrics: SystemMetrics): Promise<boolean> {
// Stateful workloads not suitable
if (this.isStateful()) {
return false;
}
// CPU scaling
if (metrics.cpuUsage > 70) {
return true;
}
// Request queue increasing
if (metrics.requestQueueDepth > 100) {
return true;
}
// Response time degrading
if (metrics.averageResponseTime > 1000) { // 1 second
return true;
}
return false;
}
// Calculate optimal instance count
async calculateInstanceCount(
metrics: SystemMetrics,
targetUtilization: number = 70
): Promise<number> {
const currentInstances = metrics.instanceCount;
const currentLoad = metrics.cpuUsage;
// Target utilization percentage
const targetLoad = targetUtilization;
// Calculate required instances
const requiredInstances = Math.ceil(
(currentInstances * currentLoad) / targetLoad
);
// Add buffer for spikes
const buffer = Math.ceil(requiredInstances * 0.2); // 20% buffer
const totalInstances = requiredInstances + buffer;
// Respect min/max limits
return Math.min(
Math.max(totalInstances, this.minInstances),
this.maxInstances
);
}
// Auto-scaling implementation
async autoScale(metrics: SystemMetrics): Promise<void> {
const currentCount = metrics.instanceCount;
const desiredCount = await this.calculateInstanceCount(metrics);
if (desiredCount !== currentCount) {
const action = desiredCount > currentCount ? 'Scaling out' : 'Scaling in';
console.log(`${action} from ${currentCount} to ${desiredCount} instances`);
await this.updateInstanceCount(desiredCount);
// Wait for instances to be healthy
if (desiredCount > currentCount) {
await this.waitForInstancesHealthy(desiredCount);
}
}
}
}Auto-Scaling Implementation
typescript
import { AutoScaling, CloudWatch } from '@aws-sdk/client-auto-scaling';
class AWSAutoScaler {
private autoscaling: AutoScaling;
private cloudwatch: CloudWatch;
constructor() {
this.autoscaling = new AutoScaling({ region: 'us-east-1' });
this.cloudwatch = new CloudWatch({ region: 'us-east-1' });
}
async setupAutoScaling(
groupName: string,
config: ScalingPolicy
): Promise<void> {
// Create scaling policies
await this.createScaleOutPolicy(groupName, config);
await this.createScaleInPolicy(groupName, config);
// Create CloudWatch alarms
await this.createHighCPUAlarm(groupName, config);
await this.createLowCPUAlarm(groupName, config);
await this.createHighMemoryAlarm(groupName, config);
console.log(`Auto-scaling configured for ${groupName}`);
}
private async createScaleOutPolicy(
groupName: string,
config: ScalingPolicy
): Promise<void> {
await this.autoscaling.putScalingPolicy({
AutoScalingGroupName: groupName,
PolicyName: `${groupName}-scale-out`,
PolicyType: 'TargetTrackingScaling',
TargetTrackingConfiguration: {
PredefinedMetricSpecification: {
PredefinedMetricType: 'ASGAverageCPUUtilization'
},
TargetValue: config.targetCPUUtilization,
ScaleOutCooldown: config.scaleOutCooldown,
ScaleInCooldown: config.scaleInCooldown
}
}).promise();
}
private async createScaleInPolicy(
groupName: string,
config: ScalingPolicy
): Promise<void> {
await this.autoscaling.putScalingPolicy({
AutoScalingGroupName: groupName,
PolicyName: `${groupName}-scale-in`,
PolicyType: 'SimpleScaling',
AdjustmentType: 'ChangeInCapacity',
ScalingAdjustment: -1,
Cooldown: config.scaleInCooldown
}).promise();
}
private async createHighCPUAlarm(
groupName: string,
config: ScalingPolicy
): Promise<void> {
await this.cloudwatch.putMetricAlarm({
AlarmName: `${groupName}-high-cpu`,
AlarmDescription: 'Alert on CPU > 80%',
Namespace: 'AWS/EC2',
MetricName: 'CPUUtilization',
Dimensions: [{
Name: 'AutoScalingGroupName',
Value: groupName
}],
Statistic: 'Average',
Period: 300, // 5 minutes
EvaluationPeriods: 2,
Threshold: 80,
ComparisonOperator: 'GreaterThanThreshold'
}).promise();
}
private async createLowCPUAlarm(
groupName: string,
config: ScalingPolicy
): Promise<void> {
await this.cloudwatch.putMetricAlarm({
AlarmName: `${groupName}-low-cpu`,
AlarmDescription: 'Alert on CPU < 30%',
Namespace: 'AWS/EC2',
MetricName: 'CPUUtilization',
Dimensions: [{
Name: 'AutoScalingGroupName',
Value: groupName
}],
Statistic: 'Average',
Period: 300,
EvaluationPeriods: 4, // 20 minutes
Threshold: 30,
ComparisonOperator: 'LessThanThreshold'
}).promise();
}
async manualScale(groupName: string, desiredCount: number): Promise<void> {
await this.autoscaling.setDesiredCapacity({
AutoScalingGroupName: groupName,
DesiredCapacity: desiredCount,
HonorCooldown: false
}).promise();
console.log(`Scaled ${groupName} to ${desiredCount} instances`);
}
async scheduleScaling(
groupName: string,
schedules: ScheduledScaling[]
): Promise<void> {
for (const schedule of schedules) {
await this.autoscaling.putScheduledUpdateGroupAction({
AutoScalingGroupName: groupName,
ScheduledActionName: schedule.name,
StartTime: schedule.startTime,
EndTime: schedule.endTime,
Recurrence: schedule.recurrence,
MinSize: schedule.minSize,
MaxSize: schedule.maxSize,
DesiredCapacity: schedule.desiredCapacity
}).promise();
}
console.log(`Scheduled scaling configured for ${groupName}`);
}
}Load Balancing Strategies
Load Balancer Algorithms
typescript
enum LoadBalancingAlgorithm {
ROUND_ROBIN,
LEAST_CONNECTIONS,
IP_HASH,
LEAST_RESPONSE_TIME,
WEIGHTED_ROUND_ROBIN,
RANDOM
}
class LoadBalancer {
private servers: Server[];
private algorithm: LoadBalancingAlgorithm;
private connections: Map<string, number>;
constructor(servers: Server[], algorithm: LoadBalancingAlgorithm) {
this.servers = servers;
this.algorithm = algorithm;
this.connections = new Map();
servers.forEach(s => this.connections.set(s.id, 0));
}
selectServer(request: Request): Server {
switch (this.algorithm) {
case LoadBalancingAlgorithm.ROUND_ROBIN:
return this.roundRobin();
case LoadBalancingAlgorithm.LEAST_CONNECTIONS:
return this.leastConnections();
case LoadBalancingAlgorithm.IP_HASH:
return this.ipHash(request);
case LoadBalancingAlgorithm.LEAST_RESPONSE_TIME:
return this.leastResponseTime();
case LoadBalancingAlgorithm.WEIGHTED_ROUND_ROBIN:
return this.weightedRoundRobin();
case LoadBalancingAlgorithm.RANDOM:
return this.random();
default:
return this.roundRobin();
}
}
private roundRobin(): Server {
const server = this.servers[this.currentRoundRobinIndex];
this.currentRoundRobinIndex =
(this.currentRoundRobinIndex + 1) % this.servers.length;
return server;
}
private leastConnections(): Server {
let minConnections = Infinity;
let selectedServer: Server = this.servers[0];
for (const server of this.servers) {
const connections = this.connections.get(server.id) || 0;
if (connections < minConnections) {
minConnections = connections;
selectedServer = server;
}
}
return selectedServer;
}
private ipHash(request: Request): Server {
const clientIP = request.ip;
const hash = this.hashCode(clientIP);
const index = Math.abs(hash) % this.servers.length;
return this.servers[index];
}
private leastResponseTime(): Server {
let minTime = Infinity;
let selectedServer: Server = this.servers[0];
for (const server of this.servers) {
if (server.averageResponseTime < minTime) {
minTime = server.averageResponseTime;
selectedServer = server;
}
}
return selectedServer;
}
private weightedRoundRobin(): Server {
// Weighted round robin based on server capacity
const totalWeight = this.servers.reduce(
(sum, s) => sum + s.weight,
0
);
let random = Math.random() * totalWeight;
for (const server of this.servers) {
random -= server.weight;
if (random <= 0) {
return server;
}
}
return this.servers[this.servers.length - 1];
}
private random(): Server {
const index = Math.floor(Math.random() * this.servers.length);
return this.servers[index];
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash;
}
recordConnection(serverId: string): void {
const current = this.connections.get(serverId) || 0;
this.connections.set(serverId, current + 1);
}
releaseConnection(serverId: string): void {
const current = this.connections.get(serverId) || 0;
this.connections.set(serverId, Math.max(0, current - 1));
}
}Session Persistence
typescript
class SessionAwareLoadBalancer extends LoadBalancer {
private sessions: Map<string, string>; // sessionID -> serverID
private stickySessions: boolean;
constructor(
servers: Server[],
algorithm: LoadBalancingAlgorithm,
stickySessions: boolean = true
) {
super(servers, algorithm);
this.stickySessions = stickySessions;
this.sessions = new Map();
}
selectServer(request: Request): Server {
// Check for existing session
if (this.stickySessions) {
const sessionId = request.cookies['session_id'];
if (sessionId && this.sessions.has(sessionId)) {
const serverId = this.sessions.get(sessionId);
const server = this.servers.find(s => s.id === serverId);
// Verify server is healthy
if (server && server.isHealthy) {
return server;
} else {
// Server unhealthy, remove session mapping
this.sessions.delete(sessionId);
}
}
}
// No session or unhealthy server, select new server
const server = super.selectServer(request);
// Store session mapping
if (this.stickySessions && request.cookies['session_id']) {
this.sessions.set(
request.cookies['session_id'],
server.id
);
}
return server;
}
}
// Redis-based session sharing
class RedisSessionStore {
private redis: Redis;
constructor(redis: Redis) {
this.redis = redis;
}
async setSession(
sessionId: string,
data: SessionData,
ttl: number = 3600
): Promise<void> {
await this.redis.setex(
`session:${sessionId}`,
ttl,
JSON.stringify(data)
);
}
async getSession(sessionId: string): Promise<SessionData | null> {
const data = await this.redis.get(`session:${sessionId}`);
if (data) {
return JSON.parse(data);
}
return null;
}
async deleteSession(sessionId: string): Promise<void> {
await this.redis.del(`session:${sessionId}`);
}
// Session affinity across multiple load balancers
async getServerForSession(sessionId: string): Promise<string | null> {
const data = await this.getSession(sessionId);
return data?.serverId || null;
}
async setServerForSession(
sessionId: string,
serverId: string
): Promise<void> {
const data = await this.getSession(sessionId) || {};
data.serverId = serverId;
await this.setSession(sessionId, data);
}
}Database Scaling Patterns
Read Replicas
Read Replicas Architecture:
Write
|
+-------+-------+
| Primary |
| Database |
+-------+-------+
|
+-------+-------+-------+
| | | |
Read Read Read Read
| | | |
+-------+ +-------+ +-------+ +-------+
|Replica| |Replica| |Replica| |Replica|
+-------+ +-------+ +-------+ +-------+
Benefits:
- Offload read traffic
- Geographic distribution
- Improved read performance
- Backup/recoverytypescript
class ReadReplicaManager {
private primary: DatabaseConnection;
private replicas: DatabaseConnection[];
private readWriteRatio: number;
constructor(
primary: DatabaseConnection,
replicas: DatabaseConnection[],
readWriteRatio: number = 0.8 // 80% reads, 20% writes
) {
this.primary = primary;
this.replicas = replicas;
this.readWriteRatio = readWriteRatio;
}
async query(sql: string, params?: any[]): Promise<any> {
const isWrite = this.isWriteQuery(sql);
if (isWrite) {
// All writes go to primary
return await this.primary.query(sql, params);
} else {
// Reads go to replicas
const replica = this.selectReplica();
return await replica.query(sql, params);
}
}
private selectReplica(): DatabaseConnection {
// Random replica for load distribution
const index = Math.floor(
Math.random() * this.replicas.length
);
return this.replicas[index];
}
private isWriteQuery(sql: string): boolean {
const writeKeywords = [
'INSERT', 'UPDATE', 'DELETE', 'CREATE',
'ALTER', 'DROP', 'TRUNCATE', 'REPLACE'
];
const upperSQL = sql.trim().toUpperCase();
return writeKeywords.some(keyword =>
upperSQL.startsWith(keyword)
);
}
async addReplica(connectionString: string): Promise<void> {
const replica = new DatabaseConnection(connectionString);
await replica.connect();
this.replicas.push(replica);
console.log('Replica added successfully');
}
async removeReplica(replicaId: string): Promise<void> {
const index = this.replicas.findIndex(
r => r.id === replicaId
);
if (index !== -1) {
const replica = this.replicas[index];
await replica.disconnect();
this.replicas.splice(index, 1);
console.log(`Replica ${replicaId} removed`);
}
}
async getReplicationLag(): Promise<number> {
const lags = await Promise.all(
this.replicas.map(async replica => {
const result = await replica.query(
'SHOW SLAVE STATUS'
);
return result[0].Seconds_Behind_Master;
})
);
// Return average lag
const avgLag = lags.reduce((a, b) => a + b, 0) / lags.length;
return avgLag;
}
}Database Sharding
typescript
class DatabaseShardingManager {
private shards: Map<string, DatabaseConnection>;
private shardingStrategy: ShardingStrategy;
constructor(shardingStrategy: ShardingStrategy) {
this.shards = new Map();
this.shardingStrategy = shardingStrategy;
}
addShard(shardId: string, connection: DatabaseConnection): void {
this.shards.set(shardId, connection);
}
async query(
sql: string,
params?: any[],
shardKey?: string
): Promise<any> {
// Determine target shard
const shardId = shardKey
? this.shardingStrategy.getShard(shardKey)
: this.shardingStrategy.getDefaultShard();
const shard = this.shards.get(shardId);
if (!shard) {
throw new Error(`Shard ${shardId} not found`);
}
return await shard.query(sql, params);
}
async broadcastQuery(sql: string, params?: any[]): Promise<any[]> {
// Send query to all shards
const results = await Promise.all(
Array.from(this.shards.values()).map(
shard => shard.query(sql, params)
)
);
return results.flat();
}
}
class HashShardingStrategy implements ShardingStrategy {
private shardCount: number;
constructor(shardCount: number) {
this.shardCount = shardCount;
}
getShard(key: string): string {
const hash = this.hashCode(key);
const shardIndex = hash % this.shardCount;
return `shard${shardIndex}`;
}
getDefaultShard(): string {
return 'shard0';
}
private hashCode(str: string): number {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash);
}
}
class RangeShardingStrategy implements ShardingStrategy {
private ranges: Map<string, Range>;
constructor() {
this.ranges = new Map();
}
addRange(shardId: string, range: Range): void {
this.ranges.set(shardId, range);
}
getShard(key: string): string {
const value = this.extractValue(key);
for (const [shardId, range] of this.ranges) {
if (value >= range.min && value < range.max) {
return shardId;
}
}
throw new Error(`No shard found for key: ${key}`);
}
getDefaultShard(): string {
return 'shard0';
}
private extractValue(key: string): number {
// Extract numeric value from key
const match = key.match(/\d+/);
return match ? parseInt(match[0]) : 0;
}
}
interface Range {
min: number;
max: number;
}
interface ShardingStrategy {
getShard(key: string): string;
getDefaultShard(): string;
}Caching Strategies
Multi-Level Caching
typescript
class MultiLevelCache {
private l1: InMemoryCache; // Local cache (fastest)
private l2: Redis; // Distributed cache
private l3: Database; // Persistent storage
constructor(l1: InMemoryCache, l2: Redis, l3: Database) {
this.l1 = l1;
this.l2 = l2;
this.l3 = l3;
}
async get(key: string): Promise<any> {
// L1: Check local cache
const l1Data = await this.l1.get(key);
if (l1Data !== null) {
console.log('L1 cache hit');
return l1Data;
}
// L2: Check Redis
const l2Data = await this.l2.get(key);
if (l2Data !== null) {
console.log('L2 cache hit');
// Populate L1 cache
await this.l1.set(key, l2Data, 60); // 1 minute
return l2Data;
}
// L3: Query database
console.log('Cache miss, querying database');
const l3Data = await this.l3.get(key);
// Populate caches
await this.l1.set(key, l3Data, 60);
await this.l2.set(key, l3Data, 3600); // 1 hour
return l3Data;
}
async set(key: string, value: any): Promise<void> {
// Write to all levels
await this.l1.set(key, value, 60);
await this.l2.set(key, value, 3600);
await this.l3.set(key, value);
}
async invalidate(key: string): Promise<void> {
// Invalidate at all levels
await this.l1.delete(key);
await this.l2.del(key);
await this.l3.delete(key);
}
async invalidatePattern(pattern: string): Promise<void> {
// Redis pattern deletion
const keys = await this.l2.keys(pattern);
if (keys.length > 0) {
await this.l2.del(...keys);
}
// Invalidate L1 cache for affected keys
await this.l1.clear();
console.log(`Invalidated ${keys.length} keys matching ${pattern}`);
}
}
class CacheWarmer {
private cache: MultiLevelCache;
constructor(cache: MultiLevelCache) {
this.cache = cache;
}
async warmCache(keys: string[]): Promise<void> {
console.log(`Warming cache for ${keys.length} keys`);
// Parallel warming
await Promise.all(
keys.map(async key => {
try {
await this.cache.get(key); // Will populate cache
} catch (error) {
console.error(`Failed to warm cache for key ${key}:`, error);
}
})
);
console.log('Cache warming complete');
}
// Warm based on access patterns
async warmPopularKeys(limit: number = 1000): Promise<void> {
// Get most accessed keys from analytics
const popularKeys = await this.getPopularKeys(limit);
await this.warmCache(popularKeys);
}
private async getPopularKeys(limit: number): Promise<string[]> {
// Query analytics or logs for popular keys
// This is implementation-specific
return [
'user:12345',
'product:67890',
// ... more keys
];
}
}2025 Scalability Trends
Kubernetes Native Scaling
yaml
# Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60Serverless Auto-Scaling
typescript
class ServerlessScaler {
// AWS Lambda automatically scales
// No configuration needed for concurrency
async processEvent(event: Event): Promise<void> {
// Lambda handles scaling automatically
// Up to 1000 concurrent executions per region
await Promise.all(
event.records.map(record =>
this.processRecord(record)
)
);
}
// Provisioned Concurrency for cold start reduction
async configureProvisionedConcurrency(
functionName: string,
concurrency: number
): Promise<void> {
const lambda = new AWS.Lambda();
await lambda.putProvisionedConcurrencyConfig({
FunctionName: functionName,
ProvisionedConcurrentExecutions: concurrency
}).promise();
console.log(
`Configured ${concurrency} provisioned concurrency for ${functionName}`
);
}
// Scheduled concurrency warming
async scheduleConcurrencyWarming(
functionName: string,
schedule: string
): Promise<void> {
const lambda = new AWS.Lambda();
const events = new AWS.EventBridge();
// Create lambda function for warming
await lambda.putFunctionCode({
FunctionName: `${functionName}-warmer`,
Code: {
ZipFile: this.createWarmerCode(functionName)
}
}).promise();
// Schedule warming
await events.putRule({
Name: `${functionName}-warming-schedule`,
ScheduleExpression: schedule
}).promise();
}
}Scalability Checklist
1. Define Requirements:
- Expected traffic patterns
- Growth projections
- Performance SLAs
- Budget constraints
2. Choose Scaling Strategy:
- Vertical for simple workloads
- Horizontal for stateless apps
- Hybrid for complex systems
3. Design for Scale:
- Stateless application design
- Database scaling strategy
- Caching at all levels
- CDN for static content
4. Implement Auto-Scaling:
- Configure scaling policies
- Set up monitoring and alerts
- Define scaling thresholds
- Test scaling scenarios
5. Monitor and Optimize:
- Track scaling events
- Measure cost per request
- Identify bottlenecks
- Continuous optimization
6. Plan for Failures:
- Circuit breakers
- Retry logic
- Fallback mechanisms
- Graceful degradation
7. Test Scaling:
- Load testing
- Chaos engineering
- Performance testing
- Disaster recovery
Scale wisely for cost-effective performance!