⚡ Performance Optimization Guide
📋 Overview
This guide provides comprehensive strategies for optimizing Partner Services API integration performance, including caching, connection pooling, request batching, and monitoring techniques.
Optimization Areas:
- ✅ Response Time - Target <200ms API responses
- ✅ Caching Strategy - Reduce redundant API calls
- ✅ Connection Pooling - Efficient HTTP connections
- ✅ Request Batching - Minimize API calls
- ✅ Error Recovery - Fast failover mechanisms
🎯 Performance Targets
Current Benchmarks
- API Response Time: <200ms (95th percentile)
- Authentication Overhead: <5ms
- Charge Calculation: <150ms
- Partner Availability Check: <100ms
- Database Query Performance: <50ms
Scaling Goals
- Concurrent Users: 500+ supported
- Daily Shipments: 10,000+ capacity
- Rate Limits: 1000 requests/minute
- Cache Hit Rate: >80%
💾 Caching Strategies
1. Multi-Level Caching Architecture
javascript
class MultiLevelCache {
constructor() {
this.memoryCache = new Map();
this.redisCache = new Redis(process.env.REDIS_URL);
this.cacheConfig = {
partnerData: { ttl: 1800, levels: ["memory", "redis"] }, // 30 min
chargeCalculation: { ttl: 900, levels: ["memory", "redis"] }, // 15 min
zoneData: { ttl: 3600, levels: ["memory", "redis"] }, // 1 hour
pincodeData: { ttl: 7200, levels: ["memory", "redis"] }, // 2 hours
};
}
async get(key, type = "default") {
const config = this.cacheConfig[type] || { ttl: 300, levels: ["memory"] };
// Try memory cache first
if (config.levels.includes("memory")) {
const memoryResult = this.memoryCache.get(key);
if (memoryResult && !this.isExpired(memoryResult)) {
console.log(`✅ Memory cache hit: ${key}`);
return memoryResult.data;
}
}
// Try Redis cache
if (config.levels.includes("redis")) {
try {
const redisResult = await this.redisCache.get(key);
if (redisResult) {
const parsed = JSON.parse(redisResult);
if (!this.isExpired(parsed)) {
console.log(`✅ Redis cache hit: ${key}`);
// Backfill memory cache
if (config.levels.includes("memory")) {
this.memoryCache.set(key, parsed);
}
return parsed.data;
}
}
} catch (error) {
console.warn("Redis cache error:", error.message);
}
}
console.log(`❌ Cache miss: ${key}`);
return null;
}
async set(key, data, type = "default") {
const config = this.cacheConfig[type] || { ttl: 300, levels: ["memory"] };
const cacheEntry = {
data,
timestamp: Date.now(),
ttl: config.ttl * 1000,
};
// Set in memory cache
if (config.levels.includes("memory")) {
this.memoryCache.set(key, cacheEntry);
}
// Set in Redis cache
if (config.levels.includes("redis")) {
try {
await this.redisCache.setex(
key,
config.ttl,
JSON.stringify(cacheEntry)
);
} catch (error) {
console.warn("Redis cache set error:", error.message);
}
}
console.log(`💾 Cached: ${key} (TTL: ${config.ttl}s)`);
}
isExpired(cacheEntry) {
return Date.now() - cacheEntry.timestamp > cacheEntry.ttl;
}
}
// Usage
const cache = new MultiLevelCache();
// Cache partner data
await cache.set(`partner:${partnerId}`, partnerData, "partnerData");
// Retrieve cached data
const cachedPartner = await cache.get(`partner:${partnerId}`, "partnerData");
2. Smart Cache Keys & Invalidation
javascript
class SmartCacheManager {
constructor(cache) {
this.cache = cache;
this.dependencies = new Map();
}
// Generate hierarchical cache keys
generateCacheKey(type, identifiers) {
const baseKey = `${type}:${Object.values(identifiers).join(":")}`;
const hash = this.generateHash(identifiers);
return `${baseKey}:${hash}`;
}
// Cache with dependency tracking
async cacheWithDependencies(key, data, dependencies = [], ttl = 300) {
await this.cache.set(key, data, ttl);
// Track dependencies
dependencies.forEach((dep) => {
if (!this.dependencies.has(dep)) {
this.dependencies.set(dep, new Set());
}
this.dependencies.get(dep).add(key);
});
}
// Invalidate cache and all dependents
async invalidateWithDependencies(pattern) {
const keysToInvalidate = new Set();
// Find direct matches
const directKeys = await this.findCacheKeys(pattern);
directKeys.forEach((key) => keysToInvalidate.add(key));
// Find dependent keys
if (this.dependencies.has(pattern)) {
this.dependencies
.get(pattern)
.forEach((key) => keysToInvalidate.add(key));
}
// Invalidate all keys
for (const key of keysToInvalidate) {
await this.cache.delete(key);
console.log(`🗑️ Invalidated cache: ${key}`);
}
console.log(
`🧹 Invalidated ${keysToInvalidate.size} cache entries for pattern: ${pattern}`
);
}
generateHash(obj) {
return require("crypto")
.createHash("md5")
.update(JSON.stringify(obj))
.digest("hex")
.substring(0, 8);
}
}
🔗 Connection Pooling & HTTP Optimization
1. HTTP Connection Pool Configuration
javascript
const https = require("https");
const axios = require("axios");
// Optimized HTTPS agent with connection pooling
const httpsAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 30000, // 30 seconds
maxSockets: 50, // Per host
maxFreeSockets: 10, // Keep alive when idle
timeout: 60000, // 60 seconds
freeSocketTimeout: 30000, // 30 seconds
socketActiveTTL: 0, // Disable socket TTL
});
// Configure axios with optimized settings
const apiClient = axios.create({
httpsAgent,
timeout: 30000,
maxRedirects: 3,
maxContentLength: 50 * 1024 * 1024, // 50MB
headers: {
Connection: "keep-alive",
"Keep-Alive": "timeout=30, max=1000",
},
});
// Connection pool monitoring
setInterval(() => {
console.log("🔗 Connection Pool Stats:", {
totalSockets: httpsAgent.totalSocketCount,
freeSockets: httpsAgent.freeSockets,
requests: httpsAgent.requests,
});
}, 60000); // Every minute
2. Request Compression & Optimization
javascript
// Request/Response compression
apiClient.interceptors.request.use((config) => {
// Enable compression for large payloads
if (config.data && JSON.stringify(config.data).length > 1024) {
config.headers["Content-Encoding"] = "gzip";
config.headers["Accept-Encoding"] = "gzip, deflate, br";
}
return config;
});
// Response decompression
apiClient.interceptors.response.use((response) => {
// Handle compressed responses
if (response.headers["content-encoding"]) {
console.log(
`📦 Compressed response: ${response.headers["content-encoding"]}`
);
}
return response;
});
📦 Request Batching & Optimization
1. Intelligent Request Batching
javascript
class RequestBatcher {
constructor(client, options = {}) {
this.client = client;
this.batchSize = options.batchSize || 10;
this.flushInterval = options.flushInterval || 100; // ms
this.queue = [];
this.pendingFlush = null;
}
async batchRequest(method, path, body, options = {}) {
return new Promise((resolve, reject) => {
this.queue.push({
method,
path,
body,
options,
resolve,
reject,
timestamp: Date.now(),
});
this.scheduleFlush();
});
}
scheduleFlush() {
if (this.pendingFlush) return;
this.pendingFlush = setTimeout(() => {
this.flush();
}, this.flushInterval);
// Immediate flush if batch is full
if (this.queue.length >= this.batchSize) {
clearTimeout(this.pendingFlush);
this.pendingFlush = null;
this.flush();
}
}
async flush() {
if (this.queue.length === 0) return;
const batch = this.queue.splice(0, this.batchSize);
this.pendingFlush = null;
console.log(`🚀 Flushing batch of ${batch.length} requests`);
// Group similar requests
const grouped = this.groupRequests(batch);
for (const [key, requests] of grouped.entries()) {
try {
if (requests.length === 1) {
// Single request
const req = requests[0];
const result = await this.client.makeRequest(
req.method,
req.path,
req.body
);
req.resolve(result);
} else {
// Bulk request
await this.executeBulkRequest(key, requests);
}
} catch (error) {
// Reject all requests in this group
requests.forEach((req) => req.reject(error));
}
}
// Schedule next flush if queue has items
if (this.queue.length > 0) {
this.scheduleFlush();
}
}
groupRequests(batch) {
const groups = new Map();
batch.forEach((request) => {
const key = `${request.method}:${request.path.split("?")[0]}`;
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(request);
});
return groups;
}
async executeBulkRequest(key, requests) {
// Handle specific bulk operations
if (key.includes("availability/check")) {
await this.bulkAvailabilityCheck(requests);
} else if (key.includes("calculate-charges")) {
await this.bulkChargeCalculation(requests);
} else {
// Execute individually for unsupported bulk operations
for (const req of requests) {
try {
const result = await this.client.makeRequest(
req.method,
req.path,
req.body
);
req.resolve(result);
} catch (error) {
req.reject(error);
}
}
}
}
}
2. Parallel Request Execution
javascript
class ParallelRequestManager {
constructor(maxConcurrency = 10) {
this.maxConcurrency = maxConcurrency;
this.activeRequests = 0;
this.requestQueue = [];
}
async executeParallel(requests) {
const results = new Array(requests.length);
const promises = requests.map((request, index) =>
this.queueRequest(() => this.executeRequest(request), index)
);
const settled = await Promise.allSettled(promises);
settled.forEach((result, index) => {
if (result.status === "fulfilled") {
results[index] = result.value;
} else {
results[index] = { error: result.reason };
}
});
return results;
}
async queueRequest(requestFn, index) {
while (this.activeRequests >= this.maxConcurrency) {
await new Promise((resolve) => {
this.requestQueue.push(resolve);
});
}
this.activeRequests++;
try {
const result = await requestFn();
return { index, result };
} finally {
this.activeRequests--;
if (this.requestQueue.length > 0) {
const nextResolve = this.requestQueue.shift();
nextResolve();
}
}
}
}
📊 Performance Monitoring
1. Real-time Performance Metrics
javascript
class PerformanceMonitor {
constructor() {
this.metrics = {
requests: new Map(),
responseTime: [],
errors: [],
cacheHits: 0,
cacheMisses: 0,
};
this.startTime = Date.now();
}
startRequest(requestId, endpoint) {
this.metrics.requests.set(requestId, {
endpoint,
startTime: Date.now(),
startMemory: process.memoryUsage(),
});
}
endRequest(requestId, success = true, responseSize = 0) {
const request = this.metrics.requests.get(requestId);
if (!request) return;
const endTime = Date.now();
const duration = endTime - request.startTime;
const endMemory = process.memoryUsage();
// Record response time
this.metrics.responseTime.push({
endpoint: request.endpoint,
duration,
success,
responseSize,
memoryDelta: endMemory.heapUsed - request.startMemory.heapUsed,
timestamp: endTime,
});
// Clean up
this.metrics.requests.delete(requestId);
// Log slow requests
if (duration > 1000) {
console.warn(
`🐌 Slow request detected: ${request.endpoint} took ${duration}ms`
);
}
}
recordCacheHit() {
this.metrics.cacheHits++;
}
recordCacheMiss() {
this.metrics.cacheMisses++;
}
getStats() {
const now = Date.now();
const recentRequests = this.metrics.responseTime.filter(
(req) => now - req.timestamp < 60000
); // Last minute
const avgResponseTime =
recentRequests.length > 0
? recentRequests.reduce((sum, req) => sum + req.duration, 0) /
recentRequests.length
: 0;
const successRate =
recentRequests.length > 0
? (recentRequests.filter((req) => req.success).length /
recentRequests.length) *
100
: 100;
const cacheHitRate =
this.metrics.cacheHits + this.metrics.cacheMisses > 0
? (this.metrics.cacheHits /
(this.metrics.cacheHits + this.metrics.cacheMisses)) *
100
: 0;
return {
uptime: now - this.startTime,
activeRequests: this.metrics.requests.size,
avgResponseTime: Math.round(avgResponseTime),
successRate: Math.round(successRate * 100) / 100,
cacheHitRate: Math.round(cacheHitRate * 100) / 100,
totalRequests: this.metrics.responseTime.length,
recentRequests: recentRequests.length,
};
}
generateReport() {
const stats = this.getStats();
console.log("\n📊 Performance Report:");
console.log(`Uptime: ${Math.round(stats.uptime / 1000)}s`);
console.log(`Active Requests: ${stats.activeRequests}`);
console.log(`Avg Response Time: ${stats.avgResponseTime}ms`);
console.log(`Success Rate: ${stats.successRate}%`);
console.log(`Cache Hit Rate: ${stats.cacheHitRate}%`);
console.log(`Total Requests: ${stats.totalRequests}`);
console.log(`Recent Requests (1m): ${stats.recentRequests}\n`);
return stats;
}
}
2. Automated Performance Alerts
javascript
class PerformanceAlerting {
constructor(monitor, options = {}) {
this.monitor = monitor;
this.thresholds = {
avgResponseTime: options.avgResponseTime || 500, // ms
successRate: options.successRate || 95, // %
cacheHitRate: options.cacheHitRate || 70, // %
...options,
};
this.alertCooldown = new Map();
this.cooldownPeriod = 300000; // 5 minutes
}
checkThresholds() {
const stats = this.monitor.getStats();
// Check average response time
if (stats.avgResponseTime > this.thresholds.avgResponseTime) {
this.sendAlert("HIGH_RESPONSE_TIME", {
current: stats.avgResponseTime,
threshold: this.thresholds.avgResponseTime,
});
}
// Check success rate
if (stats.successRate < this.thresholds.successRate) {
this.sendAlert("LOW_SUCCESS_RATE", {
current: stats.successRate,
threshold: this.thresholds.successRate,
});
}
// Check cache hit rate
if (stats.cacheHitRate < this.thresholds.cacheHitRate) {
this.sendAlert("LOW_CACHE_HIT_RATE", {
current: stats.cacheHitRate,
threshold: this.thresholds.cacheHitRate,
});
}
}
sendAlert(type, data) {
const now = Date.now();
const lastAlert = this.alertCooldown.get(type);
if (lastAlert && now - lastAlert < this.cooldownPeriod) {
return; // Skip due to cooldown
}
this.alertCooldown.set(type, now);
console.warn(`🚨 Performance Alert: ${type}`, data);
// Send to monitoring system
this.sendToMonitoringSystem(type, data);
}
async sendToMonitoringSystem(type, data) {
// Implement integration with monitoring systems
// (Datadog, New Relic, CloudWatch, etc.)
}
}
🔄 Performance Best Practices
1. API Call Optimization Checklist
[ ] Reduce API Calls
- [ ] Implement comprehensive caching
- [ ] Use bulk operations when available
- [ ] Cache partner data for 30+ minutes
- [ ] Cache zone/pincode data for hours
[ ] Connection Optimization
- [ ] Enable HTTP keep-alive
- [ ] Use connection pooling
- [ ] Configure proper timeouts
- [ ] Enable request/response compression
[ ] Request Batching
- [ ] Batch similar requests together
- [ ] Implement request queuing
- [ ] Use parallel execution for independent calls
- [ ] Optimize batch sizes based on API limits
[ ] Error Handling
- [ ] Implement circuit breakers
- [ ] Use exponential backoff
- [ ] Cache error responses appropriately
- [ ] Provide fallback mechanisms
2. Memory Management
javascript
class MemoryOptimizer {
constructor() {
this.gcInterval = setInterval(() => {
if (global.gc) {
global.gc();
console.log("🧹 Manual garbage collection triggered");
}
}, 300000); // Every 5 minutes
}
monitorMemoryUsage() {
const usage = process.memoryUsage();
const thresholds = {
heapUsed: 500 * 1024 * 1024, // 500MB
external: 100 * 1024 * 1024, // 100MB
};
if (usage.heapUsed > thresholds.heapUsed) {
console.warn(
"⚠️ High heap usage:",
Math.round(usage.heapUsed / 1024 / 1024) + "MB"
);
}
if (usage.external > thresholds.external) {
console.warn(
"⚠️ High external memory usage:",
Math.round(usage.external / 1024 / 1024) + "MB"
);
}
return usage;
}
cleanup() {
if (this.gcInterval) {
clearInterval(this.gcInterval);
}
}
}
🎯 Performance Testing
Load Testing Example
javascript
class LoadTester {
async runLoadTest(concurrency = 10, duration = 60000) {
console.log(
`🧪 Starting load test: ${concurrency} concurrent users, ${duration}ms duration`
);
const startTime = Date.now();
const results = [];
const promises = [];
for (let i = 0; i < concurrency; i++) {
promises.push(this.runUserSimulation(startTime + duration, results));
}
await Promise.all(promises);
return this.analyzeResults(results);
}
async runUserSimulation(endTime, results) {
while (Date.now() < endTime) {
const startRequest = Date.now();
try {
await this.simulateTypicalWorkflow();
results.push({
success: true,
duration: Date.now() - startRequest,
timestamp: startRequest,
});
} catch (error) {
results.push({
success: false,
duration: Date.now() - startRequest,
timestamp: startRequest,
error: error.message,
});
}
// Wait between requests (simulate user think time)
await new Promise((resolve) =>
setTimeout(resolve, Math.random() * 2000 + 1000)
);
}
}
async simulateTypicalWorkflow() {
// Simulate a typical user workflow
await client.makeRequest("GET", "/health");
await client.makeRequest("GET", "/partners/comprehensive-data");
await client.makeRequest("POST", "/partners/availability/check", {
pickupPincode: "110001",
deliveryPincode: "400001",
weight: 2500,
});
await client.makeRequest("POST", "/shipments/calculate-charges", {
partnerId: "test_partner",
pickupPincode: "110001",
deliveryPincode: "400001",
weight: 2500,
});
}
analyzeResults(results) {
const successful = results.filter((r) => r.success);
const failed = results.filter((r) => !r.success);
const avgResponseTime =
successful.length > 0
? successful.reduce((sum, r) => sum + r.duration, 0) / successful.length
: 0;
return {
totalRequests: results.length,
successfulRequests: successful.length,
failedRequests: failed.length,
successRate: (successful.length / results.length) * 100,
avgResponseTime: Math.round(avgResponseTime),
p95ResponseTime: this.calculatePercentile(
successful.map((r) => r.duration),
95
),
p99ResponseTime: this.calculatePercentile(
successful.map((r) => r.duration),
99
),
};
}
calculatePercentile(values, percentile) {
const sorted = values.sort((a, b) => a - b);
const index = Math.ceil((percentile / 100) * sorted.length) - 1;
return sorted[index] || 0;
}
}
🎯 Next Steps
- Error Handling - Robust error management
- Main System Flow - Complete implementation
- Authentication - Secure performance
- API Examples - Performance testing strategies
Optimized performance ensures your integration can handle high loads while maintaining excellent user experience! ⚡