UNPKG

@simpleapps-com/augur-api

Version:

TypeScript client library for Augur microservices API endpoints

816 lines (687 loc) 24.7 kB
# 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).