@simpleapps-com/augur-api
Version:
TypeScript client library for Augur microservices API endpoints
816 lines (687 loc) • 24.7 kB
Markdown
# Performance Optimization Guide
**Maximize your application's performance** with proven strategies for edge caching, batch operations, and pagination optimization.
## Edge Cache Strategy Decision Tree
**Choose the optimal cache duration** based on your data characteristics:
```typescript
/**
* 🌐 EDGE CACHE DECISION TREE
*
* Cloudflare CDN caches responses globally for specified hours.
* Choose duration based on data volatility and business impact.
*/
// 🟢 STATIC REFERENCE DATA → Cache: 8 hours
// Changes rarely, high business value when cached
const categories = await api.items.categories.list({ edgeCache: 8 });
const distributors = await api.vmi.distributors.list({ edgeCache: 8 });
const userGroups = await api.joomla.userGroups.list({ edgeCache: 8 });
// 🟡 MODERATE VOLATILITY → Cache: 3-6 hours
// Changes daily/weekly, good cache hit potential
const products = await api.opensearch.itemSearch.search({
q: 'electrical supplies',
edgeCache: 6 // Product catalogs change infrequently
});
const warehouses = await api.vmi.warehouses.list({
customerId: 12345,
edgeCache: 4 // Warehouse data moderately stable
});
const recommendations = await api.commerce.alsoBought.get({
itemId: 'WIRE-123',
edgeCache: 4 // Recommendations can be stale for hours
});
// 🟠 HIGH VOLATILITY → Cache: 1-3 hours
// Changes hourly, short cache acceptable
const pricing = await api.pricing.getPrice({
customerId: 12345,
itemId: 'STANDARD-ITEM',
quantity: 10,
edgeCache: 3 // Standard pricing changes less frequently
});
const users = await api.joomla.users.list({
limit: 50,
edgeCache: 2 // User lists change regularly
});
// 🔴 VOLATILE DATA → Cache: 1 hour maximum
// Changes frequently, short cache only
const cart = await api.commerce.cartHeaders.list({
userId: 123,
edgeCache: 1 // Cart contents change frequently
});
const inventory = await api.vmi.invProfileHdr.checkAvailability(warehouseId, {
edgeCache: 1 // Inventory levels change constantly
});
// ⛔ REAL-TIME DATA → No caching
// Must be current, never cache
const auth = await api.joomla.users.verifyPassword({
username: 'user',
password: 'password'
// No edgeCache - authentication must be real-time
});
const checkout = await api.commerce.checkout.create({
cartHdrUid: 12345
// No edgeCache - checkout must be real-time
});
```
## Smart Caching Strategies by Service
**Optimize cache duration** based on business context and data patterns:
```typescript
class SmartCacheStrategy {
/**
* Get optimal cache duration for any service operation
*/
static getOptimalDuration(
service: string,
operation: string,
context?: Record<string, any>
): number | undefined {
const strategies = {
// 🏪 JOOMLA SERVICE - Content & Users
joomla: {
content: context?.isStaticPage ? 8 : 4, // Articles/pages
users: context?.isLargeOrg ? 4 : 2, // User management
userGroups: 8, // Group structure
tags: 6, // Tag taxonomy
menu: 8, // Site navigation
authentication: undefined // Never cache auth
},
// 🛒 COMMERCE SERVICE - E-commerce
commerce: {
cart: 1, // User shopping carts
alsoBought: 6, // Product recommendations
checkout: undefined, // Real-time checkout
categories: 8 // Product categories
},
// 💰 PRICING SERVICE - Dynamic Pricing
pricing: {
standardPricing: context?.isStandardItem ? 4 : 2, // Standard vs custom
customerPricing: context?.isPremiumCustomer ? 3 : 1, // Premium gets longer cache
jobPricing: 6, // Job-based pricing
taxCalculation: 2 // Tax calculations
},
// 📦 VMI SERVICE - Inventory Management
vmi: {
warehouses: 6, // Warehouse data
inventory: context?.isHighVolume ? 1 : 2, // Inventory levels
distributors: 8, // Distributor info
products: 4, // Product catalog
replenishment: 3 // Replenishment data
},
// 🔍 OPENSEARCH SERVICE - Product Search
opensearch: {
search: context?.isPopularQuery ? 4 : 2, // Search results
facets: 6, // Search facets
suggestions: 4, // Search suggestions
itemDetails: 3 // Individual items
},
// 🏷️ ITEMS SERVICE - Product Information
items: {
categories: 8, // Category hierarchy
attributes: 6, // Product attributes
brands: 8, // Brand information
products: 4 // Product details
},
// 👥 CUSTOMERS SERVICE - Customer Data
customers: {
profiles: 3, // Customer profiles
addresses: 4, // Shipping addresses
orders: 2, // Order history
invoices: 6, // Invoice history
contacts: 4 // Contact information
},
// 📋 ORDERS SERVICE - Order Management
orders: {
lookup: 2, // Order lookups
documents: 4, // Order documents
purchaseOrders: 3, // Purchase orders
salesRep: 2 // Sales rep data
},
// 💳 PAYMENTS SERVICE - Payment Processing
payments: {
validation: undefined, // Never cache validation
setup: undefined, // Never cache setup
cardInfo: 1 // Brief card info cache
}
};
return strategies[service]?.[operation];
}
/**
* Auto-configure cache for any API call
*/
static applyCaching<T>(
apiCall: () => Promise<T>,
service: string,
operation: string,
context?: Record<string, any>
): () => Promise<T> {
const duration = this.getOptimalDuration(service, operation, context);
return () => {
if (duration) {
// Modify the API call to include optimal caching
return apiCall().then(result => {
console.log(`✅ Cached ${service}.${operation} for ${duration} hours`);
return result;
});
}
return apiCall();
};
}
}
// Usage Examples
const cachedPricing = SmartCacheStrategy.applyCaching(
() => api.pricing.getPrice({ customerId: 12345, itemId: 'STANDARD' }),
'pricing',
'standardPricing',
{ isStandardItem: true }
);
const cachedUsers = SmartCacheStrategy.applyCaching(
() => api.joomla.users.list({ limit: 50 }),
'joomla',
'users',
{ isLargeOrg: true }
);
```
## Batch Operations by Service
**Optimize performance** with service-specific batching patterns:
```typescript
/**
* 🚀 SERVICE-SPECIFIC BATCH PATTERNS
*
* Each service has optimal batching strategies based on:
* - Data relationships
* - Performance characteristics
* - Business workflows
*/
// 🏪 JOOMLA BATCH PATTERNS
class JoomlaBatchOperations {
static async loadUserDashboard(userIds: string[]) {
// Batch user data with related information
const [users, userGroups, permissions] = await Promise.allSettled([
// Primary user data
Promise.all(userIds.map(id =>
api.joomla.users.get(id, { edgeCache: 2 })
)),
// User group memberships (batch load)
api.joomla.userGroups.list({ edgeCache: 8 }),
// Permission data (if needed)
Promise.all(userIds.map(id =>
api.joomla.users.groups.list(id, { edgeCache: 4 })
))
]);
return this.combineUserData(users, userGroups, permissions);
}
static async loadContentBundle(categoryIds: string[]) {
// Batch content loading with metadata
return Promise.allSettled([
// Content by categories
Promise.all(categoryIds.map(catId =>
api.joomla.content.list({
categoryIdList: catId,
limit: 20,
edgeCache: 6
})
)),
// Tag information
api.joomla.tags.list({ edgeCache: 6 }),
// Category metadata
Promise.all(categoryIds.map(catId =>
api.joomla.content.get(catId, { edgeCache: 8 })
))
]);
}
}
// 🛒 COMMERCE BATCH PATTERNS
class CommerceBatchOperations {
static async loadShoppingContext(userId: number, customerId: number) {
// Complete e-commerce context in one batch
const [cart, recommendations, pricing, inventory] = await Promise.allSettled([
// User's current cart
api.commerce.cartHeaders.lookup({
userId,
customerId,
edgeCache: 1
}),
// Product recommendations (multiple items)
Promise.all([
api.commerce.alsoBought.get({
itemId: 'POPULAR-ITEM-1',
customerId,
edgeCache: 6
}),
api.commerce.alsoBought.get({
itemId: 'POPULAR-ITEM-2',
customerId,
edgeCache: 6
})
]),
// Batch pricing for common items
Promise.all(['ITEM-1', 'ITEM-2', 'ITEM-3'].map(itemId =>
api.pricing.getPrice({
customerId,
itemId,
quantity: 1,
edgeCache: 3
})
)),
// Inventory availability
api.vmi.invProfileHdr.checkAvailability(123, {
q: 'featured',
edgeCache: 2
})
]);
return this.buildShoppingContext(cart, recommendations, pricing, inventory);
}
}
// 📦 VMI BATCH PATTERNS
class VMIBatchOperations {
static async loadWarehouseSnapshot(warehouseIds: number[]) {
// Complete warehouse picture with parallel loading
return Promise.allSettled([
// Warehouse basic info
Promise.all(warehouseIds.map(id =>
api.vmi.warehouses.get(id, { edgeCache: 6 })
)),
// Inventory levels for all warehouses
Promise.all(warehouseIds.map(id =>
api.vmi.invProfileHdr.checkAvailability(id, { edgeCache: 2 })
)),
// Distributor information
api.vmi.distributors.list({
customerId: 12345,
edgeCache: 8
}),
// Product catalog
api.vmi.products.list({
customerId: 12345,
limit: 100,
edgeCache: 4
})
]);
}
static async loadReplenishmentPlan(warehouseId: number) {
// Replenishment analysis batch
const [availability, replenishmentInfo, distributors] = await Promise.allSettled([
api.vmi.invProfileHdr.checkAvailability(warehouseId, { edgeCache: 1 }),
api.vmi.invProfileHdr.getReplenishmentInfo(warehouseId, {
distributorsUid: 1,
edgeCache: 3
}),
api.vmi.distributors.list({
customerId: 12345,
edgeCache: 8
})
]);
return this.analyzeReplenishment(availability, replenishmentInfo, distributors);
}
}
// 🔍 SEARCH BATCH PATTERNS
class SearchBatchOperations {
static async loadSearchResults(queries: string[]) {
// Multi-query search with facet loading
return Promise.allSettled([
// Parallel searches
Promise.all(queries.map(q =>
api.opensearch.itemSearch.search({
q,
searchType: 'query',
size: 20,
edgeCache: 4
})
)),
// Search facets/attributes
api.opensearch.itemSearch.getAttributes({
q: queries[0], // Use first query for facets
edgeCache: 6
}),
// Category structure
api.items.categories.list({ edgeCache: 8 })
]);
}
}
```
## Pagination Optimization Examples
**Handle large datasets efficiently** with smart pagination strategies:
```typescript
/**
* 📄 ADVANCED PAGINATION PATTERNS
*
* Optimize large dataset handling with intelligent pagination,
* predictive loading, and cache-aware strategies.
*/
// 🎯 SMART PAGINATION CONTROLLER
class SmartPagination {
private cache = new Map<string, any>();
private prefetchBuffer = 2; // Pages to prefetch ahead
async loadPage<T>(
fetcher: (offset: number, limit: number) => Promise<T>,
page: number,
pageSize: number = 50,
enablePrefetch: boolean = true
): Promise<T> {
const offset = page * pageSize;
const cacheKey = `page_${page}_${pageSize}`;
// Check cache first
if (this.cache.has(cacheKey)) {
console.log(`📄 Cache hit for page ${page}`);
return this.cache.get(cacheKey);
}
// Load current page
const result = await fetcher(offset, pageSize);
this.cache.set(cacheKey, result);
// Prefetch next pages if enabled
if (enablePrefetch) {
this.prefetchNextPages(fetcher, page, pageSize);
}
return result;
}
private async prefetchNextPages<T>(
fetcher: (offset: number, limit: number) => Promise<T>,
currentPage: number,
pageSize: number
): Promise<void> {
// Prefetch next 2 pages in background
for (let i = 1; i <= this.prefetchBuffer; i++) {
const nextPage = currentPage + i;
const cacheKey = `page_${nextPage}_${pageSize}`;
if (!this.cache.has(cacheKey)) {
// Non-blocking prefetch
setTimeout(async () => {
try {
const offset = nextPage * pageSize;
const result = await fetcher(offset, pageSize);
this.cache.set(cacheKey, result);
console.log(`🔮 Prefetched page ${nextPage}`);
} catch (error) {
console.log(`❌ Prefetch failed for page ${nextPage}`);
}
}, 100 * i); // Stagger prefetch requests
}
}
}
}
// 📋 SERVICE-SPECIFIC PAGINATION EXAMPLES
// Users with intelligent prefetching
class UserPagination extends SmartPagination {
async loadUserPage(page: number, searchQuery?: string) {
return this.loadPage(
(offset, limit) => api.joomla.users.list({
offset,
limit,
q: searchQuery,
orderBy: 'username|ASC',
edgeCache: 2 // Cache user pages for 2 hours
}),
page,
50, // 50 users per page
true // Enable prefetching
);
}
// Load all pages for export/analysis
async loadAllUsers(maxPages: number = 10): Promise<any[]> {
const allUsers: any[] = [];
const pageSize = 100; // Larger pages for bulk operations
for (let page = 0; page < maxPages; page++) {
try {
const result = await api.joomla.users.list({
offset: page * pageSize,
limit: pageSize,
edgeCache: 4 // Longer cache for bulk operations
});
allUsers.push(...result.data);
// Stop if we've loaded all users
if (result.data.length < pageSize) {
console.log(`✅ Loaded all users in ${page + 1} pages`);
break;
}
// Rate limiting for bulk operations
if (page % 5 === 0) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
} catch (error) {
console.error(`❌ Failed to load page ${page}:`, error);
break;
}
}
return allUsers;
}
}
// Product search with faceted pagination
class ProductSearchPagination extends SmartPagination {
async loadSearchPage(
query: string,
page: number,
filters?: Record<string, any>
) {
return this.loadPage(
(offset, limit) => api.opensearch.itemSearch.search({
q: query,
searchType: 'query',
size: limit,
from: offset, // OpenSearch uses 'from' instead of 'offset'
filters: filters ? JSON.stringify(filters) : undefined,
edgeCache: 4 // Cache search results for 4 hours
}),
page,
20, // 20 products per page for search
true // Prefetch for smooth browsing
);
}
// Infinite scroll pattern
async* infiniteScroll(query: string, filters?: Record<string, any>) {
let page = 0;
let hasMore = true;
while (hasMore) {
const result = await this.loadSearchPage(query, page, filters);
yield result.data.items;
// Check if there are more results
hasMore = result.data.items.length === 20; // Full page means more might exist
page++;
// Safety limit
if (page > 100) {
console.log('⚠️ Reached pagination safety limit');
break;
}
}
}
}
// VMI inventory with streaming pagination
class InventoryPagination extends SmartPagination {
async streamInventory(
warehouseId: number,
onBatch: (items: any[]) => Promise<void>
): Promise<void> {
let page = 0;
const pageSize = 100; // Large batches for processing
while (true) {
const result = await api.vmi.invProfileHdr.checkAvailability(warehouseId, {
offset: page * pageSize,
limit: pageSize,
edgeCache: 1 // Short cache for inventory data
});
if (result.data.length === 0) {
console.log('✅ Finished streaming inventory');
break;
}
// Process batch
await onBatch(result.data);
page++;
// Rate limiting for large datasets
await new Promise(resolve => setTimeout(resolve, 500));
}
}
}
// 🎮 USAGE EXAMPLES
// Smart user pagination with prefetching
const userPagination = new UserPagination();
// Load page with automatic prefetching
const page1 = await userPagination.loadUserPage(0, 'john');
const page2 = await userPagination.loadUserPage(1, 'john'); // Likely cached from prefetch
// Infinite scroll search
const searchPagination = new ProductSearchPagination();
for await (const items of searchPagination.infiniteScroll('electrical wire')) {
console.log(`Loaded ${items.length} products`);
// Process items
items.forEach(item => {
console.log(`${item.item_id}: ${item.item_desc}`);
});
// Could break based on user interaction
if (items.length < 20) break; // No more results
}
// Stream large inventory dataset
const inventoryPagination = new InventoryPagination();
await inventoryPagination.streamInventory(123, async (items) => {
// Process each batch
console.log(`Processing ${items.length} inventory items`);
// Example: Update database, generate reports, etc.
for (const item of items) {
// Process individual items
await processInventoryItem(item);
}
});
```
## Performance Monitoring & Analytics
**Track and optimize** your API performance with comprehensive monitoring:
```typescript
// 📊 PERFORMANCE ANALYTICS SYSTEM
class AdvancedPerformanceMonitor {
private metrics = new Map<string, {
requests: number;
totalTime: number;
errors: number;
cacheHits: number;
cacheMisses: number;
lastUsed: number;
}>();
createMonitoredAPI(config: AugurAPIConfig): AugurAPI {
return new AugurAPI({
...config,
onRequest: (config) => {
const endpoint = this.getEndpointKey(config);
(config as any).startTime = Date.now();
(config as any).endpoint = endpoint;
this.incrementMetric(endpoint, 'requests');
return config;
},
onResponse: (response) => {
const startTime = (response.config as any)?.startTime;
const endpoint = (response.config as any)?.endpoint;
if (startTime && endpoint) {
const duration = Date.now() - startTime;
this.addTiming(endpoint, duration);
// Track cache performance
const cacheStatus = response.headers?.['cf-cache-status'];
if (cacheStatus === 'HIT') {
this.incrementMetric(endpoint, 'cacheHits');
} else if (cacheStatus === 'MISS') {
this.incrementMetric(endpoint, 'cacheMisses');
}
}
return response;
},
onError: (error) => {
const endpoint = (error.config as any)?.endpoint;
if (endpoint) {
this.incrementMetric(endpoint, 'errors');
}
throw error;
}
});
}
// Get comprehensive performance report
getPerformanceReport(): Record<string, any> {
const report: Record<string, any> = {};
for (const [endpoint, metrics] of this.metrics.entries()) {
const avgResponseTime = metrics.requests > 0
? metrics.totalTime / metrics.requests
: 0;
const errorRate = metrics.requests > 0
? (metrics.errors / metrics.requests) * 100
: 0;
const totalCacheRequests = metrics.cacheHits + metrics.cacheMisses;
const cacheHitRate = totalCacheRequests > 0
? (metrics.cacheHits / totalCacheRequests) * 100
: 0;
report[endpoint] = {
requests: metrics.requests,
avgResponseTime: Math.round(avgResponseTime),
errorRate: Math.round(errorRate * 100) / 100,
cacheHitRate: Math.round(cacheHitRate * 100) / 100,
totalErrors: metrics.errors,
lastUsed: new Date(metrics.lastUsed).toISOString()
};
}
return report;
}
// Get optimization suggestions
getOptimizationSuggestions(): string[] {
const suggestions: string[] = [];
const report = this.getPerformanceReport();
for (const [endpoint, metrics] of Object.entries(report)) {
// Slow endpoints
if (metrics.avgResponseTime > 2000) {
suggestions.push(`⚡ ${endpoint}: Avg response time ${metrics.avgResponseTime}ms - consider increasing cache duration`);
}
// High error rates
if (metrics.errorRate > 5) {
suggestions.push(`❌ ${endpoint}: Error rate ${metrics.errorRate}% - check endpoint reliability`);
}
// Poor cache performance
if (metrics.cacheHitRate < 50 && metrics.requests > 10) {
suggestions.push(`🔄 ${endpoint}: Cache hit rate ${metrics.cacheHitRate}% - consider longer cache duration`);
}
// Unused endpoints
const daysSinceLastUse = (Date.now() - new Date(metrics.lastUsed).getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceLastUse > 30) {
suggestions.push(`🗑️ ${endpoint}: Not used in ${Math.round(daysSinceLastUse)} days - consider removal`);
}
}
return suggestions;
}
private getEndpointKey(config: any): string {
const url = config.url || '';
const method = config.method?.toUpperCase() || 'GET';
return `${method} ${url}`;
}
private incrementMetric(endpoint: string, metric: keyof typeof this.metrics.values): void {
if (!this.metrics.has(endpoint)) {
this.metrics.set(endpoint, {
requests: 0,
totalTime: 0,
errors: 0,
cacheHits: 0,
cacheMisses: 0,
lastUsed: Date.now()
});
}
const endpointMetrics = this.metrics.get(endpoint)!;
endpointMetrics[metric]++;
endpointMetrics.lastUsed = Date.now();
}
private addTiming(endpoint: string, duration: number): void {
const endpointMetrics = this.metrics.get(endpoint);
if (endpointMetrics) {
endpointMetrics.totalTime += duration;
endpointMetrics.lastUsed = Date.now();
}
}
}
// 📈 USAGE EXAMPLE
const monitor = new AdvancedPerformanceMonitor();
const api = monitor.createMonitoredAPI({
siteId: 'your-site-id',
bearerToken: 'your-token'
});
// Use API normally, monitoring happens automatically
await api.joomla.users.list({ limit: 50, edgeCache: 2 });
await api.pricing.getPrice({ customerId: 12345, itemId: 'ITEM-1' });
await api.vmi.invProfileHdr.checkAvailability(123);
// Get performance insights
setTimeout(() => {
console.log('📊 Performance Report:', monitor.getPerformanceReport());
console.log('💡 Optimization Suggestions:', monitor.getOptimizationSuggestions());
}, 60000);
```
## Next Steps
- **Implementation**: Apply the caching strategies that match your data patterns
- **Monitoring**: Set up performance monitoring to track optimization impact
- **Scaling**: Use batch operations and pagination for large-scale applications
- **Analytics**: Regular review of performance metrics and optimization suggestions
For integration examples and enterprise patterns, see [Enterprise Integration Guide](./ENTERPRISE.md).