MaintainX Performance Tuning
Overview
Optimize MaintainX integration performance with caching, connection pooling, efficient pagination, and request deduplication.
Prerequisites
- MaintainX integration working
- Node.js 18+
- Redis (recommended for production caching)
- Performance baseline measurements
Instructions
Step 1: Connection Pooling with Keep-Alive
// src/performance/pooled-client.ts
import axios from 'axios';
import http from 'node:http';
import https from 'node:https';
// Reuse TCP connections instead of opening new ones per request
const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 10 });
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 10 });
const client = axios.create({
baseURL: 'https://api.getmaintainx.com/v1',
headers: {
Authorization: `Bearer ${process.env.MAINTAINX_API_KEY}`,
'Content-Type': 'application/json',
},
httpAgent,
httpsAgent,
timeout: 30_000,
});
// Benefit: Eliminates TCP handshake + TLS negotiation per request
// Typical improvement: 100-200ms saved per request
Step 2: Multi-Level Caching
// src/performance/cache.ts
interface CacheLayer<T> {
get(key: string): Promise<T | undefined>;
set(key: string, value: T, ttlMs: number): Promise<void>;
}
// L1: In-memory (fastest, per-process)
class MemoryCache<T> implements CacheLayer<T> {
private store = new Map<string, { value: T; expiresAt: number }>();
async get(key: string) {
const entry = this.store.get(key);
if (entry && entry.expiresAt > Date.now()) return entry.value;
this.store.delete(key);
return undefined;
}
async set(key: string, value: T, ttlMs: number) {
this.store.set(key, { value, expiresAt: Date.now() + ttlMs });
}
}
// L2: Redis (shared across processes)
class RedisCache<T> implements CacheLayer<T> {
constructor(private redis: any) {}
async get(key: string) {
const data = await this.redis.get(`mx:${key}`);
return data ? JSON.parse(data) : undefined;
}
async set(key: string, value: T, ttlMs: number) {
await this.redis.setex(`mx:${key}`, Math.ceil(ttlMs / 1000), JSON.stringify(value));
}
}
// Multi-level cache: check L1 first, then L2, then fetch
class MultiCache<T> {
constructor(private l1: CacheLayer<T>, private l2: CacheLayer<T>) {}
async getOrFetch(key: string, ttlMs: number, fetcher: () => Promise<T>): Promise<T> {
// Check L1
let value = await this.l1.get(key);
if (value !== undefined) return value;
// Check L2
value = await this.l2.get(key);
if (value !== undefined) {
await this.l1.set(key, value, ttlMs / 2); // L1 shorter TTL
return value;
}
// Fetch from API
value = await fetcher();
await this.l1.set(key, value, ttlMs / 2);
await this.l2.set(key, value, ttlMs);
return value;
}
}