maintainx-performance-tuning
Optimize MaintainX API integration performance. Use when experiencing slow API responses, optimizing data fetching, or improving integration throughput with MaintainX. Trigger with phrases like "maintainx performance", "maintainx slow", "optimize maintainx", "maintainx caching", "maintainx faster".
- License 许可
- MIT
- 最近更新
- 2026-11-13
- Stars
- ★ 2.0k
- Forks
- 233
01 前言 Front Matter
- name
- maintainx-performance-tuning
- author
- Jeremy Longshore <jeremy@intentsolutions.io>
- license
- MIT
- version
- 1.0.0
- description
- Optimize MaintainX API integration performance. Use when experiencing slow API responses, optimizing data fetching, or improving integration throughput with MaintainX. Trigger with phrases like "maintainx performance", "maintainx slow", "optimize maintainx", "maintainx caching", "maintainx faster".
- allowed-tools
- Read, Write, Edit, Bash(npm:*)
03 SKILL.md 正文
MaintainX Performance Tuning
Overview
Optimize your MaintainX integration for maximum performance with caching, efficient queries, and connection pooling.
Prerequisites
- MaintainX integration working
- Redis or in-memory cache available
- Performance metrics baseline
Performance Metrics Baseline
| Operation | Target | Acceptable |
|---|---|---|
| List work orders | <500ms | <1000ms |
| Get single work order | <200ms | <500ms |
| Create work order | <500ms | <1000ms |
| Paginated fetch (100 items) | <1000ms | <2000ms |
Instructions
Step 1: Response Caching
// src/cache/cache-manager.ts
import { Redis } from 'ioredis';
interface CacheConfig {
ttlSeconds: number;
prefix: string;
}
class CacheManager {
private redis: Redis;
private prefix: string;
constructor(redisUrl: string, prefix = 'maintainx') {
this.redis = new Redis(redisUrl);
this.prefix = prefix;
}
private key(key: string): string {
return `${this.prefix}:${key}`;
}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(this.key(key));
if (!data) return null;
return JSON.parse(data) as T;
}
async set<T>(key: string, value: T, ttlSeconds = 300): Promise<void> {
await this.redis.set(
this.key(key),
JSON.stringify(value),
'EX',
ttlSeconds
);
}
async delete(key: string): Promise<void> {
await this.redis.del(this.key(key));
}
async invalidatePattern(pattern: string): Promise<void> {
const keys = await this.redis.keys(this.key(pattern));
if (keys.length > 0) {
await this.redis.del(...keys);
}
}
}
// Cached API client wrapper
class CachedMaintainXClient {
private client: MaintainXClient;
private cache: CacheManager;
constructor(client: MaintainXClient, cache: CacheManager) {
this.client = client;
this.cache = cache;
}
async getWorkOrder(id: string): Promise<WorkOrder> {
const cacheKey = `workorder:${id}`;
// Check cache first
const cached = await this.cache.get<WorkOrder>(cacheKey);
if (cached) {
console.log(`Cache hit: ${cacheKey}`);
return cached;
}
// Fetch from API
const workOrder = await this.client.getWorkOrder(id);
// Cache for 5 minutes
await this.cache.set(cacheKey, workOrder, 300);
return workOrder;
}
async getWorkOrders(params?: WorkOrderQueryParams): Promise<WorkOrderListResponse> {
// Cache list queries for shorter duration
const cacheKey = `workorders:${JSON.stringify(params || {})}`;
const cached = await this.cache.get<WorkOrderListResponse>(cacheKey);
if (cached) {
return cached;
}
const response = await this.client.getWorkOrders(params);
await this.cache.set(cacheKey, response, 60); // 1 minute
return response;
}
async createWorkOrder(data: CreateWorkOrderInput): Promise<WorkOrder> {
const workOrder = await this.client.createWorkOrder(data);
// Invalidate list cache
await this.cache.invalidatePattern('workorders:*');
return workOrder;
}
// Invalidate cache when data changes
async invalidateWorkOrder(id: string): Promise<void> {
await this.cache.delete(`workorder:${id}`);
await this.cache.invalidatePattern('workorders:*');
}
}
Step 2: Connection Pooling
// src/api/connection-pool.ts
import axios, { AxiosInstance } from 'axios';
import https from 'https';
import http from 'http';
function createOptimizedClient(): AxiosInstance {
// HTTP agent with keep-alive
const httpsAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000,
});
const httpAgent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 30000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 30000,
});
return axios.create({
baseURL: 'https://api.getmaintainx.com/v1',
timeout: 30000,
httpsAgent,
httpAgent,
headers: {
'Authorization': `Bearer ${process.env.MAINTAINX_API_KEY}`,
'Content-Type': 'application/json',
'Connection': 'keep-alive',
},
// Response compression
decompress: true,
});
}
export { createOptimizedClient };
Step 3: Efficient Pagination
// src/utils/efficient-pagination.ts
interface EfficientPaginationOptions {
batchSize: number;
maxConcurrent: number;
delayBetweenBatches: number;
}
// Stream-based pagination for memory efficiency
async function* streamWorkOrders(
client: MaintainXClient,
params: WorkOrderQueryParams = {},
options: Partial<EfficientPaginationOptions> = {}
): AsyncGenerator<WorkOrder, void, unknown> {
const { batchSize = 100 } = options;
let cursor: string | undefined;
do {
const response = await client.getWorkOrders({
...params,
cursor,
limit: batchSize,
});
for (const workOrder of response.workOrders) {
yield workOrder;
}
cursor = response.nextCursor || undefined;
} while (cursor);
}
// Parallel fetch with cursor prefetch
async function fetchAllWorkOrdersOptimized(
client: MaintainXClient,
params: WorkOrderQueryParams = {}
): Promise<WorkOrder[]> {
const allWorkOrders: WorkOrder[] = [];
let cursor: string | undefined;
let prefetchPromise: Promise<WorkOrderListResponse> | null = null;
do {
// Use prefetched response if available
let response: WorkOrderListResponse;
if (prefetchPromise) {
response = await prefetchPromise;
} else {
response = await client.getWorkOrders({ ...params, cursor, limit: 100 });
}
allWorkOrders.push(...response.workOrders);
cursor = response.nextCursor || undefined;
// Prefetch next page while processing current
if (cursor) {
prefetchPromise = client.getWorkOrders({ ...params, cursor, limit: 100 });
} else {
prefetchPromise = null;
}
} while (cursor);
return allWorkOrders;
}
// Usage with memory-efficient streaming
async function processLargeDataset(client: MaintainXClient) {
let processed = 0;
for await (const workOrder of streamWorkOrders(client, { status: 'DONE' })) {
// Process one at a time - no memory buildup
await processWorkOrder(workOrder);
processed++;
if (processed % 100 === 0) {
console.log(`Processed ${processed} work orders`);
}
}
}
Step 4: Request Deduplication
// src/utils/deduplication.ts
class RequestDeduplicator {
private pending: Map<string, Promise<any>> = new Map();
async dedupe<T>(key: string, operation: () => Promise<T>): Promise<T> {
// Check for in-flight request
if (this.pending.has(key)) {
console.log(`Deduplicating request: ${key}`);
return this.pending.get(key) as Promise<T>;
}
// Create new request
const promise = operation().finally(() => {
this.pending.delete(key);
});
this.pending.set(key, promise);
return promise;
}
}
// Usage in client
class DeduplicatedMaintainXClient {
private client: MaintainXClient;
private deduper: RequestDeduplicator;
constructor(client: MaintainXClient) {
this.client = client;
this.deduper = new RequestDeduplicator();
}
async getWorkOrder(id: string): Promise<WorkOrder> {
return this.deduper.dedupe(
`workorder:${id}`,
() => this.client.getWorkOrder(id)
);
}
async getAsset(id: string): Promise<Asset> {
return this.deduper.dedupe(
`asset:${id}`,
() => this.client.getAsset(id)
);
}
}
Step 5: Batch Data Loading
// src/utils/dataloader.ts
import DataLoader from 'dataloader';
// Batch load work orders
const workOrderLoader = new DataLoader<string, WorkOrder>(
async (ids: readonly string[]) => {
console.log(`Batch loading ${ids.length} work orders`);
// Fetch all in parallel (or single batch request if API supports)
const workOrders = await Promise.all(
ids.map(id => client.getWorkOrder(id))
);
// Return in same order as input
return workOrders;
},
{
maxBatchSize: 20,
batchScheduleFn: (callback) => setTimeout(callback, 10), // 10ms batching window
cache: true,
}
);
// Batch load assets
const assetLoader = new DataLoader<string, Asset>(
async (ids: readonly string[]) => {
const assets = await Promise.all(
ids.map(id => client.getAsset(id))
);
return assets;
}
);
// Usage: multiple calls get batched
async function loadWorkOrdersWithAssets(workOrderIds: string[]) {
// These will be batched into fewer API calls
const workOrders = await Promise.all(
workOrderIds.map(id => workOrderLoader.load(id))
);
// Load associated assets
const assetIds = workOrders
.map(wo => wo.assetId)
.filter(Boolean) as string[];
const assets = await Promise.all(
assetIds.map(id => assetLoader.load(id))
);
return { workOrders, assets };
}
Step 6: Performance Monitoring
// src/monitoring/performance.ts
import { performance } from 'perf_hooks';
interface PerformanceMetric {
operation: string;
duration: number;
timestamp: Date;
success: boolean;
}
class PerformanceMonitor {
private metrics: PerformanceMetric[] = [];
async measure<T>(
operation: string,
fn: () => Promise<T>
): Promise<T> {
const start = performance.now();
let success = true;
try {
return await fn();
} catch (error) {
success = false;
throw error;
} finally {
const duration = performance.now() - start;
this.metrics.push({
operation,
duration,
timestamp: new Date(),
success,
});
// Log slow operations
if (duration > 1000) {
console.warn(`Slow operation: ${operation} took ${duration.toFixed(0)}ms`);
}
}
}
getStats(operation?: string): any {
const filtered = operation
? this.metrics.filter(m => m.operation === operation)
: this.metrics;
if (filtered.length === 0) return null;
const durations = filtered.map(m => m.duration);
return {
count: filtered.length,
avg: durations.reduce((a, b) => a + b, 0) / durations.length,
min: Math.min(...durations),
max: Math.max(...durations),
p95: percentile(durations, 95),
p99: percentile(durations, 99),
successRate: filtered.filter(m => m.success).length / filtered.length,
};
}
}
function percentile(arr: number[], p: number): number {
const sorted = [...arr].sort((a, b) => a - b);
const index = Math.ceil((p / 100) * sorted.length) - 1;
return sorted[index];
}
// Usage
const monitor = new PerformanceMonitor();
const workOrders = await monitor.measure(
'getWorkOrders',
() => client.getWorkOrders({ limit: 100 })
);
console.log('Performance stats:', monitor.getStats('getWorkOrders'));
Output
- Caching layer implemented
- Connection pooling configured
- Efficient pagination patterns
- Request deduplication
- DataLoader for batch loading
- Performance monitoring
Performance Checklist
- [ ] Response caching configured
- [ ] Connection pooling enabled
- [ ] Pagination uses streaming where appropriate
- [ ] Duplicate requests deduplicated
- [ ] Batch loading for related data
- [ ] Performance metrics collected
- [ ] Slow operations identified and optimized
Resources
Next Steps
For cost optimization, see maintainx-cost-tuning.
04 同仓库更多 Skills
web-artifacts-builder
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
webapp-testing
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
neuropixels-analysis
Neuropixels neural recording analysis. Load SpikeGLX/OpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen/IBL curation, AI-assisted visual analysis, for Neuropixels 1.0/2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.
skill-creator
Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
notebooklm
Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.
slack-gif-creator
Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."