@mytechtoday/augment-extensions 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +35 -3
- package/README.md +3 -3
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -0
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -0
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -0
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -0
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -0
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -0
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -0
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -0
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -0
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -0
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -0
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -0
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -0
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -0
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -0
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -0
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -0
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -0
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -0
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -0
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -0
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -0
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -0
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -0
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -0
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -0
- package/augment-extensions/workflows/beads/examples/complete-workflow-example.md +8 -8
- package/augment-extensions/workflows/beads/rules/best-practices.md +2 -2
- package/augment-extensions/workflows/beads/rules/file-format.md +4 -4
- package/augment-extensions/workflows/beads/rules/manual-setup.md +4 -4
- package/augment-extensions/workflows/beads/rules/workflow.md +3 -3
- package/modules.md +40 -3
- package/package.json +1 -1
package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md
ADDED
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
# Scalability and Performance
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers scalability and performance principles, patterns, and techniques for building systems that can handle growth and deliver fast response times.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Knowledge
|
|
10
|
+
|
|
11
|
+
### Scalability Fundamentals
|
|
12
|
+
|
|
13
|
+
#### Horizontal Scaling (Scale Out)
|
|
14
|
+
|
|
15
|
+
**Definition**
|
|
16
|
+
- Add more servers/instances to handle increased load
|
|
17
|
+
- Distribute load across multiple machines
|
|
18
|
+
- Preferred for cloud-native applications
|
|
19
|
+
- Enables elastic scaling
|
|
20
|
+
|
|
21
|
+
**Characteristics**
|
|
22
|
+
- Linear cost scaling (add servers as needed)
|
|
23
|
+
- Improved fault tolerance (redundancy)
|
|
24
|
+
- Requires stateless services
|
|
25
|
+
- Load balancing required
|
|
26
|
+
|
|
27
|
+
**Benefits**
|
|
28
|
+
- No theoretical limit to scaling
|
|
29
|
+
- Better fault tolerance
|
|
30
|
+
- Cost-effective (commodity hardware)
|
|
31
|
+
- Supports auto-scaling
|
|
32
|
+
|
|
33
|
+
**Challenges**
|
|
34
|
+
- Distributed system complexity
|
|
35
|
+
- Data consistency issues
|
|
36
|
+
- Network latency
|
|
37
|
+
- Load balancing overhead
|
|
38
|
+
|
|
39
|
+
#### Vertical Scaling (Scale Up)
|
|
40
|
+
|
|
41
|
+
**Definition**
|
|
42
|
+
- Add more resources (CPU, RAM, disk) to existing servers
|
|
43
|
+
- Increase capacity of individual machines
|
|
44
|
+
- Simpler than horizontal scaling
|
|
45
|
+
- Has physical limits
|
|
46
|
+
|
|
47
|
+
**Characteristics**
|
|
48
|
+
- Easier to implement (no code changes)
|
|
49
|
+
- Maintains data locality
|
|
50
|
+
- Limited by hardware constraints
|
|
51
|
+
- Single point of failure
|
|
52
|
+
|
|
53
|
+
**Benefits**
|
|
54
|
+
- Simpler architecture
|
|
55
|
+
- No distributed system complexity
|
|
56
|
+
- Better for stateful applications
|
|
57
|
+
- Lower network overhead
|
|
58
|
+
|
|
59
|
+
**Challenges**
|
|
60
|
+
- Physical limits (max CPU, RAM)
|
|
61
|
+
- Expensive (high-end hardware)
|
|
62
|
+
- Downtime during upgrades
|
|
63
|
+
- Single point of failure
|
|
64
|
+
|
|
65
|
+
### Performance Fundamentals
|
|
66
|
+
|
|
67
|
+
#### Latency
|
|
68
|
+
|
|
69
|
+
**Definition**
|
|
70
|
+
- Time delay between request and response
|
|
71
|
+
- Measured in milliseconds (ms)
|
|
72
|
+
- Critical for user experience
|
|
73
|
+
- Affected by network, processing, I/O
|
|
74
|
+
|
|
75
|
+
**Latency Numbers Every Programmer Should Know**
|
|
76
|
+
```
|
|
77
|
+
L1 cache reference: 0.5 ns
|
|
78
|
+
Branch mispredict: 5 ns
|
|
79
|
+
L2 cache reference: 7 ns
|
|
80
|
+
Mutex lock/unlock: 25 ns
|
|
81
|
+
Main memory reference: 100 ns
|
|
82
|
+
Compress 1KB with Snappy: 3,000 ns
|
|
83
|
+
Send 1KB over 1 Gbps: 10,000 ns
|
|
84
|
+
Read 4KB from SSD: 150,000 ns (0.15 ms)
|
|
85
|
+
Read 1MB sequentially: 1,000,000 ns (1 ms)
|
|
86
|
+
Disk seek: 10,000,000 ns (10 ms)
|
|
87
|
+
Read 1MB from network: 10,000,000 ns (10 ms)
|
|
88
|
+
Round trip in datacenter: 500,000 ns (0.5 ms)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Reducing Latency**
|
|
92
|
+
- Caching (reduce database queries)
|
|
93
|
+
- CDN (reduce network distance)
|
|
94
|
+
- Database indexing (faster queries)
|
|
95
|
+
- Asynchronous processing (non-blocking)
|
|
96
|
+
- Connection pooling (reuse connections)
|
|
97
|
+
|
|
98
|
+
#### Throughput
|
|
99
|
+
|
|
100
|
+
**Definition**
|
|
101
|
+
- Number of operations per unit time
|
|
102
|
+
- Measured in requests/second, transactions/second
|
|
103
|
+
- Indicates system capacity
|
|
104
|
+
- Can be increased by parallelism
|
|
105
|
+
|
|
106
|
+
**Improving Throughput**
|
|
107
|
+
- Horizontal scaling (more servers)
|
|
108
|
+
- Parallel processing (multi-threading)
|
|
109
|
+
- Batch processing (group operations)
|
|
110
|
+
- Asynchronous processing (queues)
|
|
111
|
+
- Resource optimization (efficient algorithms)
|
|
112
|
+
|
|
113
|
+
### Caching Strategies
|
|
114
|
+
|
|
115
|
+
#### Cache-Aside (Lazy Loading)
|
|
116
|
+
|
|
117
|
+
**Definition**
|
|
118
|
+
- Application checks cache first
|
|
119
|
+
- On miss, load from database and populate cache
|
|
120
|
+
- Application manages cache
|
|
121
|
+
- Most common pattern
|
|
122
|
+
|
|
123
|
+
**Flow**
|
|
124
|
+
1. Check cache for data
|
|
125
|
+
2. If found (cache hit), return data
|
|
126
|
+
3. If not found (cache miss), load from database
|
|
127
|
+
4. Store in cache for future requests
|
|
128
|
+
5. Return data
|
|
129
|
+
|
|
130
|
+
**Benefits**
|
|
131
|
+
- Only cache what's needed
|
|
132
|
+
- Cache failures don't break application
|
|
133
|
+
- Simple to implement
|
|
134
|
+
|
|
135
|
+
**Challenges**
|
|
136
|
+
- Cache miss penalty (extra latency)
|
|
137
|
+
- Stale data possible
|
|
138
|
+
- Cache warming needed
|
|
139
|
+
|
|
140
|
+
#### Read-Through Cache
|
|
141
|
+
|
|
142
|
+
**Definition**
|
|
143
|
+
- Cache sits between application and database
|
|
144
|
+
- Cache handles loading from database
|
|
145
|
+
- Transparent to application
|
|
146
|
+
- Cache manages itself
|
|
147
|
+
|
|
148
|
+
**Benefits**
|
|
149
|
+
- Simpler application code
|
|
150
|
+
- Consistent caching logic
|
|
151
|
+
- Automatic cache population
|
|
152
|
+
|
|
153
|
+
**Challenges**
|
|
154
|
+
- Requires cache infrastructure
|
|
155
|
+
- Cache miss penalty
|
|
156
|
+
- Less control over caching
|
|
157
|
+
|
|
158
|
+
#### Write-Through Cache
|
|
159
|
+
|
|
160
|
+
**Definition**
|
|
161
|
+
- Writes go through cache to database
|
|
162
|
+
- Cache and database updated synchronously
|
|
163
|
+
- Ensures cache consistency
|
|
164
|
+
- Higher write latency
|
|
165
|
+
|
|
166
|
+
**Benefits**
|
|
167
|
+
- Cache always consistent
|
|
168
|
+
- No stale data
|
|
169
|
+
- Simple consistency model
|
|
170
|
+
|
|
171
|
+
**Challenges**
|
|
172
|
+
- Higher write latency
|
|
173
|
+
- Unnecessary caching of rarely-read data
|
|
174
|
+
- Write amplification
|
|
175
|
+
|
|
176
|
+
#### Write-Behind Cache (Write-Back)
|
|
177
|
+
|
|
178
|
+
**Definition**
|
|
179
|
+
- Writes go to cache first
|
|
180
|
+
- Database updated asynchronously
|
|
181
|
+
- Lower write latency
|
|
182
|
+
- Risk of data loss
|
|
183
|
+
|
|
184
|
+
**Benefits**
|
|
185
|
+
- Fast writes
|
|
186
|
+
- Batch database updates
|
|
187
|
+
- Reduced database load
|
|
188
|
+
|
|
189
|
+
**Challenges**
|
|
190
|
+
- Data loss risk (cache failure)
|
|
191
|
+
- Complex consistency
|
|
192
|
+
- Eventual consistency
|
|
193
|
+
|
|
194
|
+
### Load Balancing
|
|
195
|
+
|
|
196
|
+
**Definition**
|
|
197
|
+
- Distribute traffic across multiple servers
|
|
198
|
+
- Improve availability and scalability
|
|
199
|
+
- Prevent server overload
|
|
200
|
+
- Enable horizontal scaling
|
|
201
|
+
|
|
202
|
+
**Load Balancing Algorithms**
|
|
203
|
+
|
|
204
|
+
**Round Robin**
|
|
205
|
+
- Distribute requests sequentially
|
|
206
|
+
- Simple and fair
|
|
207
|
+
- Doesn't consider server load
|
|
208
|
+
|
|
209
|
+
**Least Connections**
|
|
210
|
+
- Send to server with fewest active connections
|
|
211
|
+
- Better for long-lived connections
|
|
212
|
+
- Requires connection tracking
|
|
213
|
+
|
|
214
|
+
**Least Response Time**
|
|
215
|
+
- Send to server with fastest response
|
|
216
|
+
- Adapts to server performance
|
|
217
|
+
- Requires health monitoring
|
|
218
|
+
|
|
219
|
+
**IP Hash**
|
|
220
|
+
- Hash client IP to determine server
|
|
221
|
+
- Session affinity (sticky sessions)
|
|
222
|
+
- Uneven distribution possible
|
|
223
|
+
|
|
224
|
+
**Weighted Round Robin**
|
|
225
|
+
- Assign weights to servers
|
|
226
|
+
- More requests to powerful servers
|
|
227
|
+
- Handles heterogeneous servers
|
|
228
|
+
|
|
229
|
+
**Load Balancing Layers**
|
|
230
|
+
|
|
231
|
+
**Layer 4 (Transport Layer)**
|
|
232
|
+
- Based on IP and port
|
|
233
|
+
- Fast (no application inspection)
|
|
234
|
+
- Limited routing logic
|
|
235
|
+
|
|
236
|
+
**Layer 7 (Application Layer)**
|
|
237
|
+
- Based on HTTP headers, URL, cookies
|
|
238
|
+
- Flexible routing (path-based, host-based)
|
|
239
|
+
- Slower (application inspection)
|
|
240
|
+
|
|
241
|
+
### Database Scaling
|
|
242
|
+
|
|
243
|
+
#### Database Replication
|
|
244
|
+
|
|
245
|
+
**Master-Slave Replication**
|
|
246
|
+
- One master (writes), multiple slaves (reads)
|
|
247
|
+
- Scales read operations
|
|
248
|
+
- Eventual consistency
|
|
249
|
+
- Failover to slave on master failure
|
|
250
|
+
|
|
251
|
+
**Master-Master Replication**
|
|
252
|
+
- Multiple masters (writes and reads)
|
|
253
|
+
- Higher write availability
|
|
254
|
+
- Conflict resolution needed
|
|
255
|
+
- More complex
|
|
256
|
+
|
|
257
|
+
**Benefits**
|
|
258
|
+
- Improved read performance
|
|
259
|
+
- High availability
|
|
260
|
+
- Geographic distribution
|
|
261
|
+
- Backup and disaster recovery
|
|
262
|
+
|
|
263
|
+
**Challenges**
|
|
264
|
+
- Replication lag (eventual consistency)
|
|
265
|
+
- Conflict resolution (multi-master)
|
|
266
|
+
- Increased complexity
|
|
267
|
+
- Storage duplication
|
|
268
|
+
|
|
269
|
+
#### Database Sharding
|
|
270
|
+
|
|
271
|
+
**Definition**
|
|
272
|
+
- Partition data across multiple databases
|
|
273
|
+
- Each shard holds subset of data
|
|
274
|
+
- Horizontal partitioning
|
|
275
|
+
- Scales both reads and writes
|
|
276
|
+
|
|
277
|
+
**Sharding Strategies**
|
|
278
|
+
|
|
279
|
+
**Range-Based Sharding**
|
|
280
|
+
- Partition by value range (e.g., A-M, N-Z)
|
|
281
|
+
- Simple to implement
|
|
282
|
+
- Risk of uneven distribution (hotspots)
|
|
283
|
+
|
|
284
|
+
**Hash-Based Sharding**
|
|
285
|
+
- Hash key to determine shard
|
|
286
|
+
- Even distribution
|
|
287
|
+
- Difficult to add/remove shards
|
|
288
|
+
|
|
289
|
+
**Geographic Sharding**
|
|
290
|
+
- Partition by location
|
|
291
|
+
- Reduced latency (data locality)
|
|
292
|
+
- Regulatory compliance (data residency)
|
|
293
|
+
|
|
294
|
+
**Benefits**
|
|
295
|
+
- Scales reads and writes
|
|
296
|
+
- Improved performance (smaller datasets)
|
|
297
|
+
- Fault isolation (shard failure)
|
|
298
|
+
|
|
299
|
+
**Challenges**
|
|
300
|
+
- Complex queries (cross-shard joins)
|
|
301
|
+
- Rebalancing (adding/removing shards)
|
|
302
|
+
- Distributed transactions
|
|
303
|
+
- Application complexity
|
|
304
|
+
|
|
305
|
+
#### Database Indexing
|
|
306
|
+
|
|
307
|
+
**Definition**
|
|
308
|
+
- Data structure to speed up queries
|
|
309
|
+
- Trade-off: faster reads, slower writes
|
|
310
|
+
- Essential for query performance
|
|
311
|
+
- Requires careful design
|
|
312
|
+
|
|
313
|
+
**Index Types**
|
|
314
|
+
|
|
315
|
+
**B-Tree Index**
|
|
316
|
+
- Balanced tree structure
|
|
317
|
+
- Good for range queries
|
|
318
|
+
- Default in most databases
|
|
319
|
+
- Supports equality and range
|
|
320
|
+
|
|
321
|
+
**Hash Index**
|
|
322
|
+
- Hash table structure
|
|
323
|
+
- Fast equality lookups
|
|
324
|
+
- No range queries
|
|
325
|
+
- Memory-intensive
|
|
326
|
+
|
|
327
|
+
**Full-Text Index**
|
|
328
|
+
- Optimized for text search
|
|
329
|
+
- Supports natural language queries
|
|
330
|
+
- Larger storage overhead
|
|
331
|
+
|
|
332
|
+
**Composite Index**
|
|
333
|
+
- Index on multiple columns
|
|
334
|
+
- Order matters (leftmost prefix)
|
|
335
|
+
- Reduces index count
|
|
336
|
+
|
|
337
|
+
**Best Practices**
|
|
338
|
+
- Index foreign keys
|
|
339
|
+
- Index columns in WHERE, JOIN, ORDER BY
|
|
340
|
+
- Avoid over-indexing (write penalty)
|
|
341
|
+
- Monitor index usage
|
|
342
|
+
- Regular index maintenance
|
|
343
|
+
|
|
344
|
+
### Asynchronous Processing
|
|
345
|
+
|
|
346
|
+
**Definition**
|
|
347
|
+
- Decouple request from processing
|
|
348
|
+
- Non-blocking operations
|
|
349
|
+
- Improves responsiveness
|
|
350
|
+
- Enables background processing
|
|
351
|
+
|
|
352
|
+
**Message Queues**
|
|
353
|
+
- Producer sends messages to queue
|
|
354
|
+
- Consumer processes messages asynchronously
|
|
355
|
+
- Decouples components
|
|
356
|
+
- Enables retry and error handling
|
|
357
|
+
|
|
358
|
+
**Benefits**
|
|
359
|
+
- Improved responsiveness (fast response)
|
|
360
|
+
- Better resource utilization
|
|
361
|
+
- Fault tolerance (retry failed jobs)
|
|
362
|
+
- Load leveling (smooth traffic spikes)
|
|
363
|
+
|
|
364
|
+
**Use Cases**
|
|
365
|
+
- Email sending
|
|
366
|
+
- Image processing
|
|
367
|
+
- Report generation
|
|
368
|
+
- Data synchronization
|
|
369
|
+
- Batch operations
|
|
370
|
+
|
|
371
|
+
**Popular Message Queues**
|
|
372
|
+
- RabbitMQ
|
|
373
|
+
- Apache Kafka
|
|
374
|
+
- Amazon SQS
|
|
375
|
+
- Redis (pub/sub)
|
|
376
|
+
- Google Cloud Pub/Sub
|
|
377
|
+
|
|
378
|
+
### Content Delivery Network (CDN)
|
|
379
|
+
|
|
380
|
+
**Definition**
|
|
381
|
+
- Distributed network of servers
|
|
382
|
+
- Cache static content close to users
|
|
383
|
+
- Reduces latency and bandwidth
|
|
384
|
+
- Improves availability
|
|
385
|
+
|
|
386
|
+
**How CDN Works**
|
|
387
|
+
1. User requests content
|
|
388
|
+
2. CDN edge server checks cache
|
|
389
|
+
3. If cached (hit), return content
|
|
390
|
+
4. If not cached (miss), fetch from origin
|
|
391
|
+
5. Cache content at edge
|
|
392
|
+
6. Return content to user
|
|
393
|
+
|
|
394
|
+
**Benefits**
|
|
395
|
+
- Reduced latency (geographic proximity)
|
|
396
|
+
- Reduced bandwidth costs
|
|
397
|
+
- Improved availability (distributed)
|
|
398
|
+
- DDoS protection (distributed traffic)
|
|
399
|
+
|
|
400
|
+
**What to Cache**
|
|
401
|
+
- Static assets (images, CSS, JavaScript)
|
|
402
|
+
- Videos and media files
|
|
403
|
+
- API responses (with appropriate TTL)
|
|
404
|
+
- HTML pages (with cache invalidation)
|
|
405
|
+
|
|
406
|
+
**CDN Providers**
|
|
407
|
+
- Cloudflare
|
|
408
|
+
- Amazon CloudFront
|
|
409
|
+
- Akamai
|
|
410
|
+
- Fastly
|
|
411
|
+
- Google Cloud CDN
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Skills
|
|
416
|
+
|
|
417
|
+
### Implementing Caching
|
|
418
|
+
|
|
419
|
+
**In-Memory Cache with Redis**
|
|
420
|
+
```typescript
|
|
421
|
+
import Redis from 'ioredis';
|
|
422
|
+
|
|
423
|
+
class CacheService {
|
|
424
|
+
private redis: Redis;
|
|
425
|
+
|
|
426
|
+
constructor() {
|
|
427
|
+
this.redis = new Redis({
|
|
428
|
+
host: process.env.REDIS_HOST,
|
|
429
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
430
|
+
password: process.env.REDIS_PASSWORD,
|
|
431
|
+
retryStrategy: (times) => {
|
|
432
|
+
const delay = Math.min(times * 50, 2000);
|
|
433
|
+
return delay;
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async get<T>(key: string): Promise<T | null> {
|
|
439
|
+
const value = await this.redis.get(key);
|
|
440
|
+
return value ? JSON.parse(value) : null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
async set<T>(key: string, value: T, ttlSeconds?: number): Promise<void> {
|
|
444
|
+
const serialized = JSON.stringify(value);
|
|
445
|
+
|
|
446
|
+
if (ttlSeconds) {
|
|
447
|
+
await this.redis.setex(key, ttlSeconds, serialized);
|
|
448
|
+
} else {
|
|
449
|
+
await this.redis.set(key, serialized);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async delete(key: string): Promise<void> {
|
|
454
|
+
await this.redis.del(key);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async invalidatePattern(pattern: string): Promise<void> {
|
|
458
|
+
const keys = await this.redis.keys(pattern);
|
|
459
|
+
if (keys.length > 0) {
|
|
460
|
+
await this.redis.del(...keys);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Usage with cache-aside pattern
|
|
466
|
+
class ProductService {
|
|
467
|
+
constructor(
|
|
468
|
+
private productRepository: ProductRepository,
|
|
469
|
+
private cache: CacheService
|
|
470
|
+
) {}
|
|
471
|
+
|
|
472
|
+
async getProduct(id: string): Promise<Product> {
|
|
473
|
+
const cacheKey = `product:${id}`;
|
|
474
|
+
|
|
475
|
+
// Try cache first
|
|
476
|
+
const cached = await this.cache.get<Product>(cacheKey);
|
|
477
|
+
if (cached) {
|
|
478
|
+
return cached;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Cache miss - load from database
|
|
482
|
+
const product = await this.productRepository.findById(id);
|
|
483
|
+
|
|
484
|
+
if (product) {
|
|
485
|
+
// Cache for 1 hour
|
|
486
|
+
await this.cache.set(cacheKey, product, 3600);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return product;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async updateProduct(id: string, data: Partial<Product>): Promise<Product> {
|
|
493
|
+
const product = await this.productRepository.update(id, data);
|
|
494
|
+
|
|
495
|
+
// Invalidate cache
|
|
496
|
+
await this.cache.delete(`product:${id}`);
|
|
497
|
+
|
|
498
|
+
// Invalidate related caches
|
|
499
|
+
await this.cache.invalidatePattern(`products:category:${product.categoryId}:*`);
|
|
500
|
+
|
|
501
|
+
return product;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Implementing Load Balancing
|
|
507
|
+
|
|
508
|
+
**Application-Level Load Balancing**
|
|
509
|
+
```typescript
|
|
510
|
+
interface Server {
|
|
511
|
+
url: string;
|
|
512
|
+
weight: number;
|
|
513
|
+
activeConnections: number;
|
|
514
|
+
healthy: boolean;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
class LoadBalancer {
|
|
518
|
+
private servers: Server[];
|
|
519
|
+
private currentIndex = 0;
|
|
520
|
+
|
|
521
|
+
constructor(servers: Array<{ url: string; weight?: number }>) {
|
|
522
|
+
this.servers = servers.map(s => ({
|
|
523
|
+
url: s.url,
|
|
524
|
+
weight: s.weight || 1,
|
|
525
|
+
activeConnections: 0,
|
|
526
|
+
healthy: true
|
|
527
|
+
}));
|
|
528
|
+
|
|
529
|
+
// Start health checks
|
|
530
|
+
this.startHealthChecks();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Round-robin algorithm
|
|
534
|
+
getNextServerRoundRobin(): Server | null {
|
|
535
|
+
const healthyServers = this.servers.filter(s => s.healthy);
|
|
536
|
+
|
|
537
|
+
if (healthyServers.length === 0) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const server = healthyServers[this.currentIndex % healthyServers.length];
|
|
542
|
+
this.currentIndex++;
|
|
543
|
+
|
|
544
|
+
return server;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Least connections algorithm
|
|
548
|
+
getNextServerLeastConnections(): Server | null {
|
|
549
|
+
const healthyServers = this.servers.filter(s => s.healthy);
|
|
550
|
+
|
|
551
|
+
if (healthyServers.length === 0) {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return healthyServers.reduce((min, server) =>
|
|
556
|
+
server.activeConnections < min.activeConnections ? server : min
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Weighted round-robin algorithm
|
|
561
|
+
getNextServerWeighted(): Server | null {
|
|
562
|
+
const healthyServers = this.servers.filter(s => s.healthy);
|
|
563
|
+
|
|
564
|
+
if (healthyServers.length === 0) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Create weighted list
|
|
569
|
+
const weighted: Server[] = [];
|
|
570
|
+
for (const server of healthyServers) {
|
|
571
|
+
for (let i = 0; i < server.weight; i++) {
|
|
572
|
+
weighted.push(server);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const server = weighted[this.currentIndex % weighted.length];
|
|
577
|
+
this.currentIndex++;
|
|
578
|
+
|
|
579
|
+
return server;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async executeRequest<T>(
|
|
583
|
+
request: (url: string) => Promise<T>,
|
|
584
|
+
algorithm: 'round-robin' | 'least-connections' | 'weighted' = 'round-robin'
|
|
585
|
+
): Promise<T> {
|
|
586
|
+
let server: Server | null;
|
|
587
|
+
|
|
588
|
+
switch (algorithm) {
|
|
589
|
+
case 'least-connections':
|
|
590
|
+
server = this.getNextServerLeastConnections();
|
|
591
|
+
break;
|
|
592
|
+
case 'weighted':
|
|
593
|
+
server = this.getNextServerWeighted();
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
server = this.getNextServerRoundRobin();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (!server) {
|
|
600
|
+
throw new Error('No healthy servers available');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
server.activeConnections++;
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const result = await request(server.url);
|
|
607
|
+
return result;
|
|
608
|
+
} finally {
|
|
609
|
+
server.activeConnections--;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private startHealthChecks(): void {
|
|
614
|
+
setInterval(async () => {
|
|
615
|
+
for (const server of this.servers) {
|
|
616
|
+
try {
|
|
617
|
+
const response = await fetch(`${server.url}/health`, {
|
|
618
|
+
method: 'GET',
|
|
619
|
+
timeout: 5000
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
server.healthy = response.ok;
|
|
623
|
+
} catch (error) {
|
|
624
|
+
server.healthy = false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}, 10000); // Check every 10 seconds
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Usage
|
|
632
|
+
const loadBalancer = new LoadBalancer([
|
|
633
|
+
{ url: 'http://server1.example.com', weight: 2 },
|
|
634
|
+
{ url: 'http://server2.example.com', weight: 1 },
|
|
635
|
+
{ url: 'http://server3.example.com', weight: 1 }
|
|
636
|
+
]);
|
|
637
|
+
|
|
638
|
+
const result = await loadBalancer.executeRequest(
|
|
639
|
+
async (url) => {
|
|
640
|
+
const response = await fetch(`${url}/api/data`);
|
|
641
|
+
return response.json();
|
|
642
|
+
},
|
|
643
|
+
'weighted'
|
|
644
|
+
);
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Implementing Database Connection Pooling
|
|
648
|
+
|
|
649
|
+
**Connection Pool**
|
|
650
|
+
```typescript
|
|
651
|
+
import { Pool, PoolClient } from 'pg';
|
|
652
|
+
|
|
653
|
+
class DatabasePool {
|
|
654
|
+
private pool: Pool;
|
|
655
|
+
|
|
656
|
+
constructor() {
|
|
657
|
+
this.pool = new Pool({
|
|
658
|
+
host: process.env.DB_HOST,
|
|
659
|
+
port: parseInt(process.env.DB_PORT || '5432'),
|
|
660
|
+
database: process.env.DB_NAME,
|
|
661
|
+
user: process.env.DB_USER,
|
|
662
|
+
password: process.env.DB_PASSWORD,
|
|
663
|
+
|
|
664
|
+
// Pool configuration
|
|
665
|
+
min: 2, // Minimum connections
|
|
666
|
+
max: 10, // Maximum connections
|
|
667
|
+
idleTimeoutMillis: 30000, // Close idle connections after 30s
|
|
668
|
+
connectionTimeoutMillis: 2000, // Wait 2s for connection
|
|
669
|
+
|
|
670
|
+
// Connection validation
|
|
671
|
+
allowExitOnIdle: true
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Monitor pool events
|
|
675
|
+
this.pool.on('connect', () => {
|
|
676
|
+
console.log('New client connected to pool');
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
this.pool.on('error', (err) => {
|
|
680
|
+
console.error('Unexpected pool error', err);
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
async query<T>(sql: string, params?: any[]): Promise<T[]> {
|
|
685
|
+
const client = await this.pool.connect();
|
|
686
|
+
|
|
687
|
+
try {
|
|
688
|
+
const result = await client.query(sql, params);
|
|
689
|
+
return result.rows;
|
|
690
|
+
} finally {
|
|
691
|
+
client.release(); // Return connection to pool
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async transaction<T>(
|
|
696
|
+
callback: (client: PoolClient) => Promise<T>
|
|
697
|
+
): Promise<T> {
|
|
698
|
+
const client = await this.pool.connect();
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
await client.query('BEGIN');
|
|
702
|
+
const result = await callback(client);
|
|
703
|
+
await client.query('COMMIT');
|
|
704
|
+
return result;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
await client.query('ROLLBACK');
|
|
707
|
+
throw error;
|
|
708
|
+
} finally {
|
|
709
|
+
client.release();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async close(): Promise<void> {
|
|
714
|
+
await this.pool.end();
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Usage
|
|
719
|
+
const db = new DatabasePool();
|
|
720
|
+
|
|
721
|
+
// Simple query
|
|
722
|
+
const users = await db.query<User>(
|
|
723
|
+
'SELECT * FROM users WHERE active = $1',
|
|
724
|
+
[true]
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
// Transaction
|
|
728
|
+
await db.transaction(async (client) => {
|
|
729
|
+
await client.query(
|
|
730
|
+
'INSERT INTO orders (user_id, total) VALUES ($1, $2)',
|
|
731
|
+
[userId, total]
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
await client.query(
|
|
735
|
+
'UPDATE inventory SET quantity = quantity - $1 WHERE product_id = $2',
|
|
736
|
+
[quantity, productId]
|
|
737
|
+
);
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Implementing Asynchronous Processing
|
|
742
|
+
|
|
743
|
+
**Message Queue with Bull (Redis-based)**
|
|
744
|
+
```typescript
|
|
745
|
+
import Queue from 'bull';
|
|
746
|
+
|
|
747
|
+
// Define job data types
|
|
748
|
+
interface EmailJob {
|
|
749
|
+
to: string;
|
|
750
|
+
subject: string;
|
|
751
|
+
body: string;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
interface ImageProcessingJob {
|
|
755
|
+
imageUrl: string;
|
|
756
|
+
operations: Array<'resize' | 'compress' | 'watermark'>;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
class QueueService {
|
|
760
|
+
private emailQueue: Queue.Queue<EmailJob>;
|
|
761
|
+
private imageQueue: Queue.Queue<ImageProcessingJob>;
|
|
762
|
+
|
|
763
|
+
constructor() {
|
|
764
|
+
const redisConfig = {
|
|
765
|
+
host: process.env.REDIS_HOST,
|
|
766
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
767
|
+
password: process.env.REDIS_PASSWORD
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// Create queues
|
|
771
|
+
this.emailQueue = new Queue<EmailJob>('email', {
|
|
772
|
+
redis: redisConfig,
|
|
773
|
+
defaultJobOptions: {
|
|
774
|
+
attempts: 3,
|
|
775
|
+
backoff: {
|
|
776
|
+
type: 'exponential',
|
|
777
|
+
delay: 2000
|
|
778
|
+
},
|
|
779
|
+
removeOnComplete: true,
|
|
780
|
+
removeOnFail: false
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
this.imageQueue = new Queue<ImageProcessingJob>('image-processing', {
|
|
785
|
+
redis: redisConfig,
|
|
786
|
+
defaultJobOptions: {
|
|
787
|
+
attempts: 2,
|
|
788
|
+
timeout: 60000 // 1 minute timeout
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
// Set up processors
|
|
793
|
+
this.setupProcessors();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
private setupProcessors(): void {
|
|
797
|
+
// Email processor
|
|
798
|
+
this.emailQueue.process(5, async (job) => {
|
|
799
|
+
console.log(`Processing email job ${job.id}`);
|
|
800
|
+
|
|
801
|
+
const { to, subject, body } = job.data;
|
|
802
|
+
|
|
803
|
+
// Simulate email sending
|
|
804
|
+
await this.sendEmail(to, subject, body);
|
|
805
|
+
|
|
806
|
+
// Update progress
|
|
807
|
+
await job.progress(100);
|
|
808
|
+
|
|
809
|
+
return { sent: true, timestamp: new Date() };
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// Image processing processor
|
|
813
|
+
this.imageQueue.process(3, async (job) => {
|
|
814
|
+
console.log(`Processing image job ${job.id}`);
|
|
815
|
+
|
|
816
|
+
const { imageUrl, operations } = job.data;
|
|
817
|
+
|
|
818
|
+
let processedUrl = imageUrl;
|
|
819
|
+
|
|
820
|
+
for (let i = 0; i < operations.length; i++) {
|
|
821
|
+
const operation = operations[i];
|
|
822
|
+
processedUrl = await this.processImage(processedUrl, operation);
|
|
823
|
+
|
|
824
|
+
// Update progress
|
|
825
|
+
await job.progress(((i + 1) / operations.length) * 100);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return { processedUrl };
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Error handling
|
|
832
|
+
this.emailQueue.on('failed', (job, err) => {
|
|
833
|
+
console.error(`Email job ${job.id} failed:`, err);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
this.imageQueue.on('failed', (job, err) => {
|
|
837
|
+
console.error(`Image job ${job.id} failed:`, err);
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
async addEmailJob(data: EmailJob, priority?: number): Promise<string> {
|
|
842
|
+
const job = await this.emailQueue.add(data, {
|
|
843
|
+
priority: priority || 0
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
return job.id.toString();
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
async addImageProcessingJob(
|
|
850
|
+
data: ImageProcessingJob,
|
|
851
|
+
delay?: number
|
|
852
|
+
): Promise<string> {
|
|
853
|
+
const job = await this.imageQueue.add(data, {
|
|
854
|
+
delay: delay || 0
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
return job.id.toString();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
async getJobStatus(
|
|
861
|
+
queueName: 'email' | 'image-processing',
|
|
862
|
+
jobId: string
|
|
863
|
+
): Promise<any> {
|
|
864
|
+
const queue = queueName === 'email' ? this.emailQueue : this.imageQueue;
|
|
865
|
+
const job = await queue.getJob(jobId);
|
|
866
|
+
|
|
867
|
+
if (!job) {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
id: job.id,
|
|
873
|
+
state: await job.getState(),
|
|
874
|
+
progress: job.progress(),
|
|
875
|
+
data: job.data,
|
|
876
|
+
result: job.returnvalue,
|
|
877
|
+
failedReason: job.failedReason
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
private async sendEmail(to: string, subject: string, body: string): Promise<void> {
|
|
882
|
+
// Implement actual email sending
|
|
883
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private async processImage(url: string, operation: string): Promise<string> {
|
|
887
|
+
// Implement actual image processing
|
|
888
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
889
|
+
return `${url}-${operation}`;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Usage
|
|
894
|
+
const queueService = new QueueService();
|
|
895
|
+
|
|
896
|
+
// Add email job
|
|
897
|
+
const emailJobId = await queueService.addEmailJob({
|
|
898
|
+
to: 'user@example.com',
|
|
899
|
+
subject: 'Welcome',
|
|
900
|
+
body: 'Welcome to our service!'
|
|
901
|
+
}, 1); // High priority
|
|
902
|
+
|
|
903
|
+
// Add image processing job with delay
|
|
904
|
+
const imageJobId = await queueService.addImageProcessingJob({
|
|
905
|
+
imageUrl: 'https://example.com/image.jpg',
|
|
906
|
+
operations: ['resize', 'compress', 'watermark']
|
|
907
|
+
}, 5000); // Process after 5 seconds
|
|
908
|
+
|
|
909
|
+
// Check job status
|
|
910
|
+
const status = await queueService.getJobStatus('email', emailJobId);
|
|
911
|
+
console.log('Job status:', status);
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
916
|
+
## Examples
|
|
917
|
+
|
|
918
|
+
### Database Query Optimization
|
|
919
|
+
|
|
920
|
+
**N+1 Query Problem**
|
|
921
|
+
```typescript
|
|
922
|
+
// Bad: N+1 queries
|
|
923
|
+
async function getOrdersWithCustomers(): Promise<OrderWithCustomer[]> {
|
|
924
|
+
const orders = await db.query<Order>('SELECT * FROM orders');
|
|
925
|
+
|
|
926
|
+
const ordersWithCustomers = [];
|
|
927
|
+
for (const order of orders) {
|
|
928
|
+
// N additional queries!
|
|
929
|
+
const customer = await db.query<Customer>(
|
|
930
|
+
'SELECT * FROM customers WHERE id = $1',
|
|
931
|
+
[order.customerId]
|
|
932
|
+
);
|
|
933
|
+
|
|
934
|
+
ordersWithCustomers.push({
|
|
935
|
+
...order,
|
|
936
|
+
customer: customer[0]
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
return ordersWithCustomers;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Good: Single query with JOIN
|
|
944
|
+
async function getOrdersWithCustomers(): Promise<OrderWithCustomer[]> {
|
|
945
|
+
const rows = await db.query(`
|
|
946
|
+
SELECT
|
|
947
|
+
o.id as order_id,
|
|
948
|
+
o.total,
|
|
949
|
+
o.created_at,
|
|
950
|
+
c.id as customer_id,
|
|
951
|
+
c.name as customer_name,
|
|
952
|
+
c.email as customer_email
|
|
953
|
+
FROM orders o
|
|
954
|
+
JOIN customers c ON o.customer_id = c.id
|
|
955
|
+
`);
|
|
956
|
+
|
|
957
|
+
return rows.map(row => ({
|
|
958
|
+
id: row.order_id,
|
|
959
|
+
total: row.total,
|
|
960
|
+
createdAt: row.created_at,
|
|
961
|
+
customer: {
|
|
962
|
+
id: row.customer_id,
|
|
963
|
+
name: row.customer_name,
|
|
964
|
+
email: row.customer_email
|
|
965
|
+
}
|
|
966
|
+
}));
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Better: Use DataLoader for batching
|
|
970
|
+
import DataLoader from 'dataloader';
|
|
971
|
+
|
|
972
|
+
const customerLoader = new DataLoader(async (customerIds: string[]) => {
|
|
973
|
+
const customers = await db.query<Customer>(
|
|
974
|
+
'SELECT * FROM customers WHERE id = ANY($1)',
|
|
975
|
+
[customerIds]
|
|
976
|
+
);
|
|
977
|
+
|
|
978
|
+
// Return in same order as requested
|
|
979
|
+
const customerMap = new Map(customers.map(c => [c.id, c]));
|
|
980
|
+
return customerIds.map(id => customerMap.get(id) || null);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
async function getOrdersWithCustomers(): Promise<OrderWithCustomer[]> {
|
|
984
|
+
const orders = await db.query<Order>('SELECT * FROM orders');
|
|
985
|
+
|
|
986
|
+
// Batch load all customers in single query
|
|
987
|
+
const ordersWithCustomers = await Promise.all(
|
|
988
|
+
orders.map(async (order) => ({
|
|
989
|
+
...order,
|
|
990
|
+
customer: await customerLoader.load(order.customerId)
|
|
991
|
+
}))
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
return ordersWithCustomers;
|
|
995
|
+
}
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
**Database Indexing**
|
|
999
|
+
```sql
|
|
1000
|
+
-- Bad: No index on frequently queried column
|
|
1001
|
+
SELECT * FROM orders WHERE customer_id = '123';
|
|
1002
|
+
-- Full table scan!
|
|
1003
|
+
|
|
1004
|
+
-- Good: Add index
|
|
1005
|
+
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
|
|
1006
|
+
|
|
1007
|
+
-- Composite index for multiple columns
|
|
1008
|
+
CREATE INDEX idx_orders_customer_status ON orders(customer_id, status);
|
|
1009
|
+
|
|
1010
|
+
-- Partial index for specific conditions
|
|
1011
|
+
CREATE INDEX idx_orders_pending ON orders(created_at)
|
|
1012
|
+
WHERE status = 'pending';
|
|
1013
|
+
|
|
1014
|
+
-- Covering index (includes all needed columns)
|
|
1015
|
+
CREATE INDEX idx_orders_covering ON orders(customer_id, status, total, created_at);
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
### Caching Strategies
|
|
1019
|
+
|
|
1020
|
+
**Multi-Level Caching**
|
|
1021
|
+
```typescript
|
|
1022
|
+
class MultiLevelCache {
|
|
1023
|
+
constructor(
|
|
1024
|
+
private l1Cache: Map<string, any>, // In-memory (fast, small)
|
|
1025
|
+
private l2Cache: CacheService // Redis (slower, larger)
|
|
1026
|
+
) {}
|
|
1027
|
+
|
|
1028
|
+
async get<T>(key: string): Promise<T | null> {
|
|
1029
|
+
// Try L1 cache (in-memory)
|
|
1030
|
+
if (this.l1Cache.has(key)) {
|
|
1031
|
+
return this.l1Cache.get(key);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// Try L2 cache (Redis)
|
|
1035
|
+
const value = await this.l2Cache.get<T>(key);
|
|
1036
|
+
|
|
1037
|
+
if (value) {
|
|
1038
|
+
// Populate L1 cache
|
|
1039
|
+
this.l1Cache.set(key, value);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
return value;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
|
1046
|
+
// Set in both caches
|
|
1047
|
+
this.l1Cache.set(key, value);
|
|
1048
|
+
await this.l2Cache.set(key, value, ttl);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
async delete(key: string): Promise<void> {
|
|
1052
|
+
this.l1Cache.delete(key);
|
|
1053
|
+
await this.l2Cache.delete(key);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
**Cache Stampede Prevention**
|
|
1059
|
+
```typescript
|
|
1060
|
+
class CacheWithStampedePrevention {
|
|
1061
|
+
private locks = new Map<string, Promise<any>>();
|
|
1062
|
+
|
|
1063
|
+
constructor(private cache: CacheService) {}
|
|
1064
|
+
|
|
1065
|
+
async get<T>(
|
|
1066
|
+
key: string,
|
|
1067
|
+
loader: () => Promise<T>,
|
|
1068
|
+
ttl: number = 3600
|
|
1069
|
+
): Promise<T> {
|
|
1070
|
+
// Try cache first
|
|
1071
|
+
const cached = await this.cache.get<T>(key);
|
|
1072
|
+
if (cached) {
|
|
1073
|
+
return cached;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Check if another request is already loading this key
|
|
1077
|
+
if (this.locks.has(key)) {
|
|
1078
|
+
return this.locks.get(key);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Create lock (promise) for this key
|
|
1082
|
+
const loadPromise = (async () => {
|
|
1083
|
+
try {
|
|
1084
|
+
const value = await loader();
|
|
1085
|
+
await this.cache.set(key, value, ttl);
|
|
1086
|
+
return value;
|
|
1087
|
+
} finally {
|
|
1088
|
+
this.locks.delete(key);
|
|
1089
|
+
}
|
|
1090
|
+
})();
|
|
1091
|
+
|
|
1092
|
+
this.locks.set(key, loadPromise);
|
|
1093
|
+
|
|
1094
|
+
return loadPromise;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Usage
|
|
1099
|
+
const cache = new CacheWithStampedePrevention(cacheService);
|
|
1100
|
+
|
|
1101
|
+
// Multiple concurrent requests for same key will only trigger one database query
|
|
1102
|
+
const [result1, result2, result3] = await Promise.all([
|
|
1103
|
+
cache.get('user:123', () => db.getUserById('123')),
|
|
1104
|
+
cache.get('user:123', () => db.getUserById('123')),
|
|
1105
|
+
cache.get('user:123', () => db.getUserById('123'))
|
|
1106
|
+
]);
|
|
1107
|
+
// Only one database query executed!
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### Performance Monitoring
|
|
1111
|
+
|
|
1112
|
+
**Response Time Tracking**
|
|
1113
|
+
```typescript
|
|
1114
|
+
class PerformanceMonitor {
|
|
1115
|
+
private metrics = new Map<string, number[]>();
|
|
1116
|
+
|
|
1117
|
+
async measure<T>(
|
|
1118
|
+
operation: string,
|
|
1119
|
+
fn: () => Promise<T>
|
|
1120
|
+
): Promise<T> {
|
|
1121
|
+
const start = Date.now();
|
|
1122
|
+
|
|
1123
|
+
try {
|
|
1124
|
+
const result = await fn();
|
|
1125
|
+
const duration = Date.now() - start;
|
|
1126
|
+
|
|
1127
|
+
this.recordMetric(operation, duration);
|
|
1128
|
+
|
|
1129
|
+
return result;
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
const duration = Date.now() - start;
|
|
1132
|
+
this.recordMetric(`${operation}:error`, duration);
|
|
1133
|
+
throw error;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
private recordMetric(operation: string, duration: number): void {
|
|
1138
|
+
if (!this.metrics.has(operation)) {
|
|
1139
|
+
this.metrics.set(operation, []);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
this.metrics.get(operation)!.push(duration);
|
|
1143
|
+
|
|
1144
|
+
// Keep only last 1000 measurements
|
|
1145
|
+
const measurements = this.metrics.get(operation)!;
|
|
1146
|
+
if (measurements.length > 1000) {
|
|
1147
|
+
measurements.shift();
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
getStats(operation: string): PerformanceStats | null {
|
|
1152
|
+
const measurements = this.metrics.get(operation);
|
|
1153
|
+
|
|
1154
|
+
if (!measurements || measurements.length === 0) {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
const sorted = [...measurements].sort((a, b) => a - b);
|
|
1159
|
+
|
|
1160
|
+
return {
|
|
1161
|
+
count: measurements.length,
|
|
1162
|
+
min: sorted[0],
|
|
1163
|
+
max: sorted[sorted.length - 1],
|
|
1164
|
+
avg: measurements.reduce((a, b) => a + b, 0) / measurements.length,
|
|
1165
|
+
p50: sorted[Math.floor(sorted.length * 0.5)],
|
|
1166
|
+
p95: sorted[Math.floor(sorted.length * 0.95)],
|
|
1167
|
+
p99: sorted[Math.floor(sorted.length * 0.99)]
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
getAllStats(): Map<string, PerformanceStats> {
|
|
1172
|
+
const stats = new Map<string, PerformanceStats>();
|
|
1173
|
+
|
|
1174
|
+
for (const operation of this.metrics.keys()) {
|
|
1175
|
+
const operationStats = this.getStats(operation);
|
|
1176
|
+
if (operationStats) {
|
|
1177
|
+
stats.set(operation, operationStats);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
return stats;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
interface PerformanceStats {
|
|
1186
|
+
count: number;
|
|
1187
|
+
min: number;
|
|
1188
|
+
max: number;
|
|
1189
|
+
avg: number;
|
|
1190
|
+
p50: number;
|
|
1191
|
+
p95: number;
|
|
1192
|
+
p99: number;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// Usage
|
|
1196
|
+
const monitor = new PerformanceMonitor();
|
|
1197
|
+
|
|
1198
|
+
const user = await monitor.measure('db:getUser', async () => {
|
|
1199
|
+
return userRepository.findById(userId);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
// Get statistics
|
|
1203
|
+
const stats = monitor.getStats('db:getUser');
|
|
1204
|
+
console.log('Database query performance:', stats);
|
|
1205
|
+
// { count: 1000, min: 5, max: 250, avg: 25, p50: 20, p95: 50, p99: 100 }
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
---
|
|
1209
|
+
|
|
1210
|
+
## Understanding
|
|
1211
|
+
|
|
1212
|
+
### Scalability Patterns
|
|
1213
|
+
|
|
1214
|
+
**CQRS (Command Query Responsibility Segregation)**
|
|
1215
|
+
- Separate read and write models
|
|
1216
|
+
- Optimize each independently
|
|
1217
|
+
- Scale reads and writes separately
|
|
1218
|
+
- Eventual consistency between models
|
|
1219
|
+
|
|
1220
|
+
**Event Sourcing**
|
|
1221
|
+
- Store events instead of current state
|
|
1222
|
+
- Rebuild state from events
|
|
1223
|
+
- Complete audit trail
|
|
1224
|
+
- Enables time travel and replay
|
|
1225
|
+
|
|
1226
|
+
**Saga Pattern**
|
|
1227
|
+
- Manage distributed transactions
|
|
1228
|
+
- Compensating transactions for rollback
|
|
1229
|
+
- Eventual consistency
|
|
1230
|
+
- Complex but scalable
|
|
1231
|
+
|
|
1232
|
+
### Performance Testing
|
|
1233
|
+
|
|
1234
|
+
**Load Testing**
|
|
1235
|
+
- Test system under expected load
|
|
1236
|
+
- Identify performance bottlenecks
|
|
1237
|
+
- Validate scalability
|
|
1238
|
+
- Tools: Apache JMeter, k6, Gatling
|
|
1239
|
+
|
|
1240
|
+
**Stress Testing**
|
|
1241
|
+
- Test system beyond normal capacity
|
|
1242
|
+
- Find breaking point
|
|
1243
|
+
- Identify failure modes
|
|
1244
|
+
- Plan for peak loads
|
|
1245
|
+
|
|
1246
|
+
**Spike Testing**
|
|
1247
|
+
- Sudden increase in load
|
|
1248
|
+
- Test auto-scaling
|
|
1249
|
+
- Identify resource limits
|
|
1250
|
+
- Plan for traffic spikes
|
|
1251
|
+
|
|
1252
|
+
**Endurance Testing**
|
|
1253
|
+
- Sustained load over time
|
|
1254
|
+
- Identify memory leaks
|
|
1255
|
+
- Test resource cleanup
|
|
1256
|
+
- Validate long-term stability
|
|
1257
|
+
|
|
1258
|
+
### Best Practices
|
|
1259
|
+
|
|
1260
|
+
1. **Measure Before Optimizing**
|
|
1261
|
+
- Profile to find bottlenecks
|
|
1262
|
+
- Don't guess, measure
|
|
1263
|
+
- Focus on biggest impact
|
|
1264
|
+
- Avoid premature optimization
|
|
1265
|
+
|
|
1266
|
+
2. **Cache Strategically**
|
|
1267
|
+
- Cache expensive operations
|
|
1268
|
+
- Set appropriate TTLs
|
|
1269
|
+
- Implement cache invalidation
|
|
1270
|
+
- Monitor cache hit rates
|
|
1271
|
+
|
|
1272
|
+
3. **Design for Horizontal Scaling**
|
|
1273
|
+
- Stateless services
|
|
1274
|
+
- Externalize session state
|
|
1275
|
+
- Use load balancers
|
|
1276
|
+
- Enable auto-scaling
|
|
1277
|
+
|
|
1278
|
+
4. **Optimize Database Access**
|
|
1279
|
+
- Use indexes wisely
|
|
1280
|
+
- Avoid N+1 queries
|
|
1281
|
+
- Use connection pooling
|
|
1282
|
+
- Consider read replicas
|
|
1283
|
+
|
|
1284
|
+
5. **Implement Asynchronous Processing**
|
|
1285
|
+
- Decouple long-running tasks
|
|
1286
|
+
- Use message queues
|
|
1287
|
+
- Implement retry logic
|
|
1288
|
+
- Monitor queue depth
|
|
1289
|
+
|
|
1290
|
+
6. **Monitor and Alert**
|
|
1291
|
+
- Track key metrics (latency, throughput, errors)
|
|
1292
|
+
- Set up alerts for anomalies
|
|
1293
|
+
- Use distributed tracing
|
|
1294
|
+
- Implement health checks
|
|
1295
|
+
|
|
1296
|
+
### Common Pitfalls
|
|
1297
|
+
|
|
1298
|
+
1. **Premature Optimization**
|
|
1299
|
+
- Optimizing before measuring
|
|
1300
|
+
- Adding complexity without benefit
|
|
1301
|
+
- Focus on correctness first
|
|
1302
|
+
|
|
1303
|
+
2. **Over-Caching**
|
|
1304
|
+
- Caching everything
|
|
1305
|
+
- Stale data issues
|
|
1306
|
+
- Memory pressure
|
|
1307
|
+
- Cache invalidation complexity
|
|
1308
|
+
|
|
1309
|
+
3. **Ignoring Database Performance**
|
|
1310
|
+
- Missing indexes
|
|
1311
|
+
- N+1 queries
|
|
1312
|
+
- Large result sets
|
|
1313
|
+
- Inefficient queries
|
|
1314
|
+
|
|
1315
|
+
4. **Synchronous Processing**
|
|
1316
|
+
- Blocking on long operations
|
|
1317
|
+
- Poor user experience
|
|
1318
|
+
- Resource waste
|
|
1319
|
+
- Limited scalability
|
|
1320
|
+
|
|
1321
|
+
5. **Single Point of Failure**
|
|
1322
|
+
- No redundancy
|
|
1323
|
+
- No failover
|
|
1324
|
+
- Downtime risk
|
|
1325
|
+
- Scalability limit
|
|
1326
|
+
|
|
1327
|
+
6. **Insufficient Monitoring**
|
|
1328
|
+
- No visibility into performance
|
|
1329
|
+
- Can't identify bottlenecks
|
|
1330
|
+
- Reactive instead of proactive
|
|
1331
|
+
- Difficult troubleshooting
|
|
1332
|
+
|
|
1333
|
+
---
|
|
1334
|
+
|
|
1335
|
+
## References
|
|
1336
|
+
|
|
1337
|
+
- **Designing Data-Intensive Applications**: Martin Kleppmann
|
|
1338
|
+
- **High Performance Browser Networking**: Ilya Grigorik
|
|
1339
|
+
- **Site Reliability Engineering**: Google
|
|
1340
|
+
- **The Art of Scalability**: Martin L. Abbott, Michael T. Fisher
|
|
1341
|
+
- **Database Internals**: Alex Petrov
|
|
1342
|
+
- **Redis in Action**: Josiah L. Carlson
|
|
1343
|
+
- **CAP Theorem**: Eric Brewer
|
|
1344
|
+
- **Latency Numbers**: Jeff Dean (Google)
|
|
1345
|
+
|