E-commerce Backend That Handles Black Friday Traffic

Build scalable e-commerce backend architecture that survives Black Friday traffic spikes. Complete guide with real-world patterns and performance optimizations.

11 minutes
Advanced
2025-01-22

What You'll Accomplish

Handle 100x traffic spikes without system failure
Implement proven e-commerce scalability patterns
Build resilient payment and inventory systems
Optimize for peak shopping performance

Remember the last time a major e-commerce site crashed during Black Friday? The revenue lost in those crucial hours? The customer trust damaged when checkout pages wouldn't load? The engineering team working around the clock to restore service?

Scalable e-commerce architecture prevents that disaster. It's like building a highway system that still flows smoothly when everyone tries to drive at the same time.

Here's how to build an e-commerce backend that thrives under the heaviest traffic loads.

The Reality of E-commerce Traffic Patterns

Understanding Peak Load Characteristics

Black Friday traffic patterns:

  • Normal day: 10,000 concurrent users, 500 orders/hour
  • Black Friday: 1,000,000+ concurrent users, 50,000+ orders/hour
  • Flash sale: 2,000,000+ users in first 15 minutes
  • Mobile vs desktop: 70% mobile traffic with different performance expectations
  • Geographic distribution: Traffic concentrated in specific time zones

System failure points under load:

  1. Database bottlenecks - Write-heavy operations (orders, inventory updates)
  2. Session management - Memory exhaustion from concurrent user sessions
  3. Payment processing - Third-party API rate limits and timeouts
  4. Image/asset serving - CDN overwhelm and bandwidth constraints
  5. Search functionality - Complex queries failing under concurrent load
  6. Cache invalidation - Stampeding herd problems during high cache misses

The Cost of Downtime During Peak Sales

Revenue impact analysis:

  • Amazon: Estimated $220,000 lost revenue per minute of downtime
  • Mid-size retailer ($10M annual revenue): $2,000-5,000 per minute during peak hours
  • Small e-commerce ($1M annual revenue): $200-500 per minute during sales events

Beyond immediate revenue loss:

  • Customer acquisition cost increase: 300-500% higher costs to re-acquire lost customers
  • Brand damage: 65% of customers won't return after a poor experience
  • Competitive advantage loss: Customers discover and switch to competitors
  • Team burnout: Emergency firefighting affects long-term productivity

High-Traffic E-commerce Architecture Patterns

Pattern 1: Event-Driven Architecture with CQRS

Separate read and write operations for optimal scaling:

// Command side - Write operations (orders, inventory updates)
interface OrderCommand {
  type: 'CREATE_ORDER' | 'UPDATE_ORDER' | 'CANCEL_ORDER';
  orderId: string;
  customerId: string;
  items: OrderItem[];
  timestamp: Date;
}

class OrderCommandHandler {
  async handle(command: OrderCommand): Promise<void> {
    switch (command.type) {
      case 'CREATE_ORDER':
        // Write to command database (optimized for writes)
        await this.orderRepository.create(command);
        
        // Publish events for other services
        await this.eventBus.publish({
          type: 'OrderCreated',
          data: command,
          timestamp: new Date()
        });
        break;
        
      case 'UPDATE_ORDER':
        // Handle order updates
        await this.orderRepository.update(command);
        await this.eventBus.publish({
          type: 'OrderUpdated', 
          data: command
        });
        break;
    }
  }
}

// Query side - Read operations (product search, order history)
class ProductQueryHandler {
  constructor(private readDB: ReadOptimizedDatabase) {}
  
  async searchProducts(query: ProductSearchQuery): Promise<ProductResult[]> {
    // Use read-optimized database with materialized views
    return await this.readDB.query(`
      SELECT p.*, ps.score, i.quantity
      FROM products_search_view p
      JOIN product_scores ps ON p.id = ps.product_id  
      JOIN inventory_view i ON p.id = i.product_id
      WHERE p.search_vector @@ plainto_tsquery($1)
      AND i.quantity > 0
      ORDER BY ps.score DESC, p.popularity DESC
      LIMIT $2
    `, [query.term, query.limit]);
  }
}

// Event handlers update read models asynchronously
class ProductSearchProjection {
  async handleProductUpdated(event: ProductUpdatedEvent): Promise<void> {
    // Update search-optimized read model
    await this.searchDB.updateProductIndex({
      productId: event.productId,
      name: event.name,
      description: event.description,
      tags: event.tags,
      searchVector: this.generateSearchVector(event)
    });
  }
}

Benefits:

  • Read scalability: Read replicas can scale independently
  • Write performance: Write operations optimized for transaction speed
  • Resilience: Read operations continue even if write services are degraded
  • Evolution: Read and write models can evolve independently

Pattern 2: Inventory Management with Eventual Consistency

Handle high-concurrency inventory updates:

// Pessimistic vs Optimistic inventory management
class InventoryManager:
    def __init__(self, redis_client, db_connection):
        self.redis = redis_client
        self.db = db_connection
    
    async def reserve_inventory_optimistic(self, product_id: str, quantity: int, order_id: str):
        """
        Optimistic approach - assume success, handle conflicts
        Better for high-traffic scenarios with low conflict rates
        """
        try:
            # Check current inventory (eventual consistency is acceptable)
            current_inventory = await self.get_inventory_estimate(product_id)
            
            if current_inventory < quantity:
                raise InsufficientInventoryError(f"Only {current_inventory} available")
            
            # Attempt reservation without locking
            reservation = {
                'product_id': product_id,
                'quantity': quantity,
                'order_id': order_id,
                'expires_at': datetime.utcnow() + timedelta(minutes=15),
                'status': 'pending'
            }
            
            # Store reservation with atomic check
            success = await self.create_inventory_reservation(reservation)
            
            if not success:
                # Handle conflict by checking actual inventory
                actual_inventory = await self.get_exact_inventory(product_id)
                if actual_inventory >= quantity:
                    # Retry once
                    return await self.reserve_inventory_with_retry(product_id, quantity, order_id)
                else:
                    raise InsufficientInventoryError("Inventory unavailable")
            
            # Async inventory update (eventual consistency)
            await self.update_inventory_async(product_id, -quantity)
            
            return reservation
            
        except Exception as e:
            # Log for monitoring
            await self.log_inventory_conflict(product_id, quantity, str(e))
            raise
    
    async def confirm_inventory_reservation(self, reservation_id: str):
        """
        Convert pending reservation to confirmed allocation
        """
        reservation = await self.get_reservation(reservation_id)
        
        if reservation['status'] != 'pending':
            raise InvalidReservationError("Reservation not in pending state")
        
        # Confirm the reservation
        await self.update_reservation_status(reservation_id, 'confirmed')
        
        # Update inventory tracking
        await self.record_inventory_allocation(
            reservation['product_id'], 
            reservation['quantity'],
            reservation['order_id']
        )
    
    async def handle_inventory_oversell(self, product_id: str):
        """
        Handle oversell situations gracefully
        """
        # Get all pending reservations for product
        pending_reservations = await self.get_pending_reservations(product_id)
        actual_inventory = await self.get_exact_inventory(product_id)
        
        if sum(r['quantity'] for r in pending_reservations) <= actual_inventory:
            return  # No oversell situation
        
        # Prioritize reservations (FIFO, customer tier, etc.)
        prioritized_reservations = self.prioritize_reservations(pending_reservations)
        
        allocated_quantity = 0
        for reservation in prioritized_reservations:
            if allocated_quantity + reservation['quantity'] <= actual_inventory:
                await self.confirm_inventory_reservation(reservation['id'])
                allocated_quantity += reservation['quantity']
            else:
                # Cancel this reservation and notify customer
                await self.cancel_reservation_with_alternatives(
                    reservation['id'], 
                    product_id
                )

Oversell management strategies:

  • Pre-order queue: Convert oversold items to pre-orders with delivery dates
  • Alternative suggestions: Recommend similar available products
  • Backorder notifications: Notify when items are back in stock
  • Tier-based prioritization: VIP customers get inventory priority

Pattern 3: Payment Processing Resilience

Handle payment gateway failures and high concurrency:

// Payment processing with circuit breaker and retry logic
class PaymentProcessor {
  constructor() {
    this.gateways = [
      { name: 'stripe', handler: new StripeHandler(), priority: 1 },
      { name: 'paypal', handler: new PayPalHandler(), priority: 2 },
      { name: 'adyen', handler: new AdyenHandler(), priority: 3 }
    ];
    
    this.circuitBreakers = new Map();
    this.gateways.forEach(gateway => {
      this.circuitBreakers.set(gateway.name, new CircuitBreaker({
        timeout: 30000,
        errorThresholdPercentage: 50,
        resetTimeout: 60000
      }));
    });
  }
  
  async processPayment(paymentRequest) {
    const orderId = paymentRequest.orderId;
    const amount = paymentRequest.amount;
    
    // Check for duplicate payment attempts
    const existingPayment = await this.checkDuplicatePayment(orderId);
    if (existingPayment) {
      return this.handleDuplicatePayment(existingPayment, paymentRequest);
    }
    
    // Try gateways in order of priority and health
    const sortedGateways = this.getSortedHealthyGateways();
    
    let lastError = null;
    for (const gateway of sortedGateways) {
      try {
        const circuitBreaker = this.circuitBreakers.get(gateway.name);
        
        const paymentResult = await circuitBreaker.fire(async () => {
          return await gateway.handler.processPayment({
            ...paymentRequest,
            idempotencyKey: `${orderId}_${gateway.name}_${Date.now()}`
          });
        });
        
        // Payment successful
        await this.recordSuccessfulPayment(orderId, gateway.name, paymentResult);
        return {
          success: true,
          gateway: gateway.name,
          transactionId: paymentResult.transactionId,
          amount: paymentResult.amount
        };
        
      } catch (error) {
        lastError = error;
        
        // Log gateway failure
        await this.logPaymentFailure(orderId, gateway.name, error);
        
        // Check if this is a permanent failure (expired card, insufficient funds)
        if (this.isPermanentFailure(error)) {
          throw error;
        }
        
        // Continue to next gateway for temporary failures
        continue;
      }
    }
    
    // All gateways failed
    throw new PaymentProcessingError(
      'All payment gateways unavailable',
      lastError
    );
  }
  
  getSortedHealthyGateways() {
    return this.gateways
      .filter(gateway => {
        const circuit = this.circuitBreakers.get(gateway.name);
        return circuit.state !== 'OPEN';
      })
      .sort((a, b) => {
        // Sort by health score and priority
        const healthA = this.getGatewayHealth(a.name);
        const healthB = this.getGatewayHealth(b.name);
        
        if (healthA !== healthB) {
          return healthB - healthA;  // Higher health first
        }
        
        return a.priority - b.priority;  // Lower priority number first
      });
  }
  
  async handleDuplicatePayment(existingPayment, newRequest) {
    if (existingPayment.status === 'completed') {
      // Payment already successful
      return {
        success: true,
        duplicate: true,
        transactionId: existingPayment.transactionId
      };
    }
    
    if (existingPayment.status === 'processing') {
      // Payment in progress, wait for completion
      return await this.waitForPaymentCompletion(existingPayment.id);
    }
    
    // Previous payment failed, allow retry
    return null;
  }
}

// Async payment status reconciliation
class PaymentReconciliationService {
  async reconcilePayments() {
    // Find payments that might be in inconsistent state
    const pendingPayments = await this.getPendingPayments();
    
    for (const payment of pendingPayments) {
      try {
        const gatewayStatus = await this.checkPaymentStatusWithGateway(
          payment.gateway,
          payment.gatewayTransactionId
        );
        
        if (gatewayStatus.status !== payment.status) {
          await this.updatePaymentStatus(payment.id, gatewayStatus);
          
          // Update order status if needed
          if (gatewayStatus.status === 'completed') {
            await this.confirmOrder(payment.orderId);
          }
        }
      } catch (error) {
        await this.logReconciliationError(payment.id, error);
      }
    }
  }
}

Pattern 4: Caching Strategy for E-commerce

Multi-layer caching optimized for shopping patterns:

# Redis cluster configuration for e-commerce caching
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cache-config
data:
  redis.conf: |
    # Memory optimization for e-commerce workloads
    maxmemory 8gb
    maxmemory-policy allkeys-lru
    
    # Persistence for critical cache data
    save 900 1      # Save if at least 1 key changed in 900 seconds
    save 300 10     # Save if at least 10 keys changed in 300 seconds
    save 60 10000   # Save if at least 10000 keys changed in 60 seconds
    
    # Network optimization
    tcp-keepalive 60
    timeout 300
    
    # Memory efficiency
    hash-max-ziplist-entries 512
    hash-max-ziplist-value 64
    list-max-ziplist-size -2
    set-max-intset-entries 512
    zset-max-ziplist-entries 128
// Multi-layer caching strategy
class EcommerceCacheManager:
    def __init__(self):
        // Layer 1: Application memory cache (fastest)
        self.memory_cache = TTLCache(maxsize=10000, ttl=300)  // 5 min TTL
        
        // Layer 2: Redis cache (fast, distributed)
        self.redis = redis.RedisCluster.from_url(os.environ['REDIS_CLUSTER_URL'])
        
        // Layer 3: Database with prepared statements (slower but authoritative)
        self.db = DatabasePool()
        
        // Cache warming scheduler
        self.cache_warmer = CacheWarmer()
    
    async def get_product(self, product_id: str) -> Product:
        cache_key = f"product:{product_id}"
        
        // Layer 1: Check memory cache
        product = self.memory_cache.get(cache_key)
        if product:
            return product
        
        // Layer 2: Check Redis cache
        cached_product = await self.redis.get(cache_key)
        if cached_product:
            product = json.loads(cached_product)
            // Warm memory cache
            self.memory_cache[cache_key] = product
            return product
        
        // Layer 3: Database query
        product = await self.db.query_product(product_id)
        if product:
            // Cache in both layers
            await self.redis.setex(
                cache_key, 
                3600,  // 1 hour TTL
                json.dumps(product, default=str)
            )
            self.memory_cache[cache_key] = product
        
        return product
    
    async def update_product_inventory(self, product_id: str, new_quantity: int):
        """
        Update inventory and handle cache invalidation carefully
        """
        // Update database first
        await self.db.update_inventory(product_id, new_quantity)
        
        // Invalidate caches to ensure consistency
        cache_key = f"product:{product_id}"
        
        // Remove from memory cache
        self.memory_cache.pop(cache_key, None)
        
        // Remove from Redis with pipeline for efficiency
        pipe = self.redis.pipeline()
        pipe.delete(cache_key)
        pipe.delete(f"product_list:category:{await self.get_product_category(product_id)}")
        pipe.delete("products:featured")
        pipe.delete("products:bestsellers")
        await pipe.execute()
        
        // Optional: Pre-warm cache with new data
        await self.get_product(product_id)  // This will cache the updated product
    
    async def warm_critical_caches(self):
        """
        Pre-warm caches before traffic spikes
        """
        critical_data = [
            'products:bestsellers',
            'products:featured', 
            'products:new_arrivals',
            'categories:main_nav'
        ]
        
        for cache_key in critical_data:
            if not await self.redis.exists(cache_key):
                await self.generate_and_cache_data(cache_key)
    
    async def handle_cache_stampede(self, cache_key: str, data_generator_func):
        """
        Prevent multiple processes from regenerating expensive cache data
        """
        lock_key = f"lock:{cache_key}"
        
        // Try to acquire lock for 30 seconds
        async with self.redis.lock(lock_key, timeout=30):
            // Double-check cache after acquiring lock
            cached_data = await self.redis.get(cache_key)
            if cached_data:
                return json.loads(cached_data)
            
            // Generate new data
            fresh_data = await data_generator_func()
            
            // Cache with extended TTL during high traffic
            await self.redis.setex(cache_key, 7200, json.dumps(fresh_data, default=str))
            
            return fresh_data

Step-by-Step Implementation

Step 1: Design Database Architecture for Scale (60 minutes)

Optimize database structure for high-traffic e-commerce:

-- Products table optimized for read performance
CREATE TABLE products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    sku VARCHAR(100) UNIQUE NOT NULL,
    name VARCHAR(500) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL,
    category_id UUID REFERENCES categories(id),
    brand_id UUID REFERENCES brands(id),
    status VARCHAR(20) DEFAULT 'active',
    search_vector tsvector,
    popularity_score INTEGER DEFAULT 0,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Separate inventory table for high-write operations
CREATE TABLE inventory (
    product_id UUID PRIMARY KEY REFERENCES products(id),
    quantity INTEGER NOT NULL DEFAULT 0,
    reserved_quantity INTEGER NOT NULL DEFAULT 0,
    reorder_point INTEGER DEFAULT 10,
    max_stock INTEGER DEFAULT 1000,
    last_restocked TIMESTAMP,
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Inventory reservations for order processing
CREATE TABLE inventory_reservations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    product_id UUID REFERENCES products(id),
    order_id UUID,
    quantity INTEGER NOT NULL,
    expires_at TIMESTAMP NOT NULL,
    status VARCHAR(20) DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT NOW()
);

-- Orders table with partitioning by date
CREATE TABLE orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    customer_id UUID REFERENCES customers(id),
    status VARCHAR(50) NOT NULL DEFAULT 'pending',
    subtotal DECIMAL(10,2) NOT NULL,
    tax_amount DECIMAL(10,2) DEFAULT 0,
    shipping_amount DECIMAL(10,2) DEFAULT 0,
    total_amount DECIMAL(10,2) NOT NULL,
    payment_status VARCHAR(50) DEFAULT 'pending',
    shipping_address JSONB,
    billing_address JSONB,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (created_at);

-- Create monthly partitions for better performance
CREATE TABLE orders_2025_01 PARTITION OF orders
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

CREATE TABLE orders_2025_02 PARTITION OF orders  
    FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');

-- Indexes optimized for e-commerce queries
CREATE INDEX idx_products_category_status ON products(category_id, status) WHERE status = 'active';
CREATE INDEX idx_products_search_vector ON products USING GIN(search_vector);
CREATE INDEX idx_products_popularity ON products(popularity_score DESC, created_at DESC);
CREATE INDEX idx_inventory_quantity ON inventory(quantity) WHERE quantity > 0;
CREATE INDEX idx_orders_customer_date ON orders(customer_id, created_at DESC);
CREATE INDEX idx_orders_status_date ON orders(status, created_at) WHERE status IN ('pending', 'processing');

-- Materialized view for fast product listing
CREATE MATERIALIZED VIEW product_catalog_view AS
SELECT 
    p.id,
    p.sku,
    p.name,
    p.price,
    p.category_id,
    c.name as category_name,
    b.name as brand_name,
    i.quantity,
    p.popularity_score,
    p.created_at
FROM products p
JOIN categories c ON p.category_id = c.id  
JOIN brands b ON p.brand_id = b.id
JOIN inventory i ON p.id = i.product_id
WHERE p.status = 'active' 
AND i.quantity > 0;

-- Index on materialized view
CREATE INDEX idx_product_catalog_category ON product_catalog_view(category_id, popularity_score DESC);
CREATE INDEX idx_product_catalog_search ON product_catalog_view(name, brand_name);

-- Refresh materialized view every 5 minutes
CREATE OR REPLACE FUNCTION refresh_product_catalog()
RETURNS void AS $$
BEGIN
    REFRESH MATERIALIZED VIEW CONCURRENTLY product_catalog_view;
END;
$$ LANGUAGE plpgsql;

-- Schedule automatic refresh
SELECT cron.schedule('refresh-product-catalog', '*/5 * * * *', 'SELECT refresh_product_catalog();');

Step 2: Implement Session Management for Scale (45 minutes)

Handle millions of concurrent user sessions:

// Distributed session management
class SessionManager {
  constructor() {
    this.redis = new Redis.Cluster([
      { host: 'session-redis-1', port: 6379 },
      { host: 'session-redis-2', port: 6379 },
      { host: 'session-redis-3', port: 6379 }
    ]);
    
    this.sessionTTL = 24 * 60 * 60; // 24 hours
    this.cartTTL = 7 * 24 * 60 * 60; // 7 days
  }
  
  async createSession(userId = null) {
    const sessionId = this.generateSessionId();
    
    const sessionData = {
      sessionId,
      userId: userId || null,
      createdAt: new Date().toISOString(),
      lastActivity: new Date().toISOString(),
      cart: {
        items: [],
        subtotal: 0,
        estimatedTax: 0,
        estimatedShipping: 0
      },
      preferences: {
        currency: 'USD',
        language: 'en',
        timezone: 'America/New_York'
      }
    };
    
    // Store session with automatic expiration
    await this.redis.setex(
      `session:${sessionId}`,
      this.sessionTTL,
      JSON.stringify(sessionData)
    );
    
    return sessionData;
  }
  
  async getSession(sessionId) {
    const sessionData = await this.redis.get(`session:${sessionId}`);
    
    if (!sessionData) {
      return null;
    }
    
    const session = JSON.parse(sessionData);
    
    // Update last activity
    session.lastActivity = new Date().toISOString();
    await this.updateSession(sessionId, session);
    
    return session;
  }
  
  async addToCart(sessionId, productId, quantity, price) {
    const session = await this.getSession(sessionId);
    
    if (!session) {
      throw new Error('Session not found');
    }
    
    // Check if item already in cart
    const existingItemIndex = session.cart.items.findIndex(
      item => item.productId === productId
    );
    
    if (existingItemIndex >= 0) {
      // Update quantity
      session.cart.items[existingItemIndex].quantity += quantity;
      session.cart.items[existingItemIndex].subtotal = 
        session.cart.items[existingItemIndex].quantity * price;
    } else {
      // Add new item
      session.cart.items.push({
        productId,
        quantity,
        price,
        subtotal: quantity * price,
        addedAt: new Date().toISOString()
      });
    }
    
    // Recalculate cart totals
    session.cart.subtotal = session.cart.items.reduce(
      (total, item) => total + item.subtotal, 0
    );
    
    // Update session with extended cart TTL
    await this.updateSession(sessionId, session, this.cartTTL);
    
    return session.cart;
  }
  
  async transferGuestCartToUser(guestSessionId, userId) {
    const guestSession = await this.getSession(guestSessionId);
    
    if (!guestSession || guestSession.cart.items.length === 0) {
      return;
    }
    
    // Find or create user session
    let userSession = await this.getUserSession(userId);
    
    if (!userSession) {
      userSession = await this.createSession(userId);
    }
    
    // Merge carts (user cart takes precedence for duplicates)
    const mergedItems = [...userSession.cart.items];
    
    for (const guestItem of guestSession.cart.items) {
      const existingIndex = mergedItems.findIndex(
        item => item.productId === guestItem.productId
      );
      
      if (existingIndex >= 0) {
        // Add quantities together
        mergedItems[existingIndex].quantity += guestItem.quantity;
        mergedItems[existingIndex].subtotal = 
          mergedItems[existingIndex].quantity * mergedItems[existingIndex].price;
      } else {
        mergedItems.push(guestItem);
      }
    }
    
    userSession.cart.items = mergedItems;
    userSession.cart.subtotal = mergedItems.reduce(
      (total, item) => total + item.subtotal, 0
    );
    
    await this.updateSession(userSession.sessionId, userSession);
    
    // Clean up guest session
    await this.redis.del(`session:${guestSessionId}`);
    
    return userSession;
  }
  
  // Cleanup expired sessions and abandoned carts
  async cleanupExpiredSessions() {
    const script = `
      local keys = redis.call('SCAN', 0, 'MATCH', 'session:*', 'COUNT', 1000)
      local expired = {}
      
      for i=1,#keys[2] do
        local ttl = redis.call('TTL', keys[2][i])
        if ttl == -1 or ttl > 86400 then
          table.insert(expired, keys[2][i])
        end
      end
      
      if #expired > 0 then
        redis.call('DEL', unpack(expired))
      end
      
      return #expired
    `;
    
    return await this.redis.eval(script, 0);
  }
}

Step 3: Build Search Infrastructure (90 minutes)

High-performance product search that scales:

// Elasticsearch configuration for e-commerce search
import { Elasticsearch } from 'elasticsearch';
import { bulk } from 'elasticsearch/api/helpers';
import asyncio from 'asyncio';

class ProductSearchService {
    constructor() {
        this.es = new Elasticsearch([
            { host: 'elasticsearch-1', port: 9200 },
            { host: 'elasticsearch-2', port: 9200 },
            { host: 'elasticsearch-3', port: 9200 }
        ]);
        
        this.index_name = 'products'
        this.setup_index()
    }
    
    setup_index() {
        /**
        Create optimized index for e-commerce search
        */
        const index_config = {
            settings: {
                number_of_shards: 3,
                number_of_replicas: 1,
                analysis: {
                    analyzer: {
                        product_analyzer: {
                            type: 'custom',
                            tokenizer: 'standard',
                            filter: [
                                'lowercase',
                                'stop',
                                'snowball',
                                'product_synonym'
                            ]
                        }
                    },
                    filter: {
                        product_synonym: {
                            type: 'synonym',
                            synonyms: [
                                'laptop,notebook,computer',
                                'phone,mobile,smartphone',
                                'tv,television'
                            ]
                        }
                    }
                }
            },
            mappings: {
                properties: {
                    id: { type: 'keyword' },
                    sku: { type: 'keyword' },
                    name: {
                        type: 'text',
                        analyzer: 'product_analyzer',
                        fields: {
                            keyword: { type: 'keyword' },
                            suggest: { type: 'completion' }
                        }
                    },
                    description: {
                        type: 'text',
                        analyzer: 'product_analyzer'
                    },
                    category: {
                        type: 'text',
                        fields: {
                            keyword: { type: 'keyword' }
                        }
                    },
                    brand: {
                        type: 'text',
                        fields: {
                            keyword: { type: 'keyword' }
                        }
                    },
                    price: { type: 'float' },
                    sale_price: { type: 'float' },
                    inventory_quantity: { type: 'integer' },
                    popularity_score: { type: 'integer' },
                    rating_average: { type: 'float' },
                    rating_count: { type: 'integer' },
                    created_at: { type: 'date' },
                    attributes: {
                        type: 'nested',
                        properties: {
                            name: { type: 'keyword' },
                            value: { type: 'keyword' }
                        }
                    },
                    tags: { type: 'keyword' },
                    availability: { type: 'keyword' }
                }
            }
        }
        
        if (!this.es.indices.exists(index=this.index_name)) {
            this.es.indices.create(index=this.index_name, body=index_config)
        }
    }
    
    async search_products(query_params) {
        /**
        Advanced product search with filters, facets, and personalization
        */
        const search_query = this.build_search_query(query_params)
        
        try {
            const response = await this.es.search(
                index=this.index_name,
                body=search_query,
                timeout='30s'
            )
            
            return this.format_search_results(response, query_params)
            
        } catch (e) {
            // Fallback to database search if Elasticsearch fails
            return await this.fallback_database_search(query_params)
        }
    }
    
    build_search_query(params) {
        /**
        Build complex Elasticsearch query with filters and boosting
        */
        const query = {
            size: params.get('size', 20),
            from: params.get('from', 0),
            query: {
                bool: {
                    must: [],
                    filter: [],
                    should: [],
                    must_not: []
                }
            },
            sort: [],
            aggs: {},
            _source: [
                'id', 'sku', 'name', 'price', 'sale_price', 
                'category', 'brand', 'rating_average', 'rating_count',
                'inventory_quantity', 'availability'
            ]
        }
        
        // Text search
        if (params.get('q')) {
            query.query.bool.must.push({
                multi_match: {
                    query: params['q'],
                    fields: [
                        'name^3',           // Boost name matches
                        'description^2',    // Boost description matches  
                        'brand^2',          // Boost brand matches
                        'category',
                        'tags'
                    ],
                    type: 'cross_fields',
                    minimum_should_match: '70%'
                }
            })
        }
        
        // Category filter
        if (params.get('category')) {
            query.query.bool.filter.push({
                term: { 'category.keyword': params['category'] }
            })
        }
        
        // Price range filter
        if (params.get('min_price') || params.get('max_price')) {
            const price_range = {}
            if (params.get('min_price')) {
                price_range['gte'] = parseFloat(params['min_price'])
            }
            if (params.get('max_price')) {
                price_range['lte'] = parseFloat(params['max_price'])
            }
            
            query.query.bool.filter.push({
                range: { 'price': price_range }
            })
        }
        
        // Availability filter
        query.query.bool.filter.push({
            term: { 'availability': 'in_stock' }
        })
        
        // Sorting
        const sort_param = params.get('sort', 'relevance')
        if (sort_param === 'price_low') {
            query.sort = [{ 'price': 'asc' }]
        } else if (sort_param === 'price_high') {  
            query.sort = [{ 'price': 'desc' }]
        } else if (sort_param === 'popularity') {
            query.sort = [{ 'popularity_score': 'desc' }]
        } else if (sort_param === 'newest') {
            query.sort = [{ 'created_at': 'desc' }]
        } else if (sort_param === 'rating') {
            query.sort = [{ 'rating_average': 'desc' }]
        } else {
            // Default relevance sort with popularity boost
            query.query.bool.should.push({
                function_score: {
                    field_value_factor: {
                        field: 'popularity_score',
                        factor: 0.1,
                        modifier: 'log1p'
                    }
                }
            })
        }
        
        // Facet aggregations
        query.aggs = {
            categories: {
                terms: { field: 'category.keyword', size: 20 }
            },
            brands: {
                terms: { field: 'brand.keyword', size: 20 }  
            },
            price_ranges: {
                range: {
                    field: 'price',
                    ranges: [
                        { to: 25 },
                        { from: 25, to: 50 },
                        { from: 50, to: 100 },
                        { from: 100, to: 250 },
                        { from: 250 }
                    ]
                }
            }
        }
        
        return query
    }
    
    format_search_results(response, params) {
        /**
        Format Elasticsearch response for API consumption
        */
        const products = []
        for (const hit of response.hits.hits) {
            const product = hit._source
            product.relevance_score = hit._score
            products.push(product)
        }
        
        const facets = {}
        if ('aggregations' in response) {
            facets = {
                categories: [
                    { name: bucket.key, count: bucket.doc_count }
                    for (const bucket of response.aggregations.categories.buckets)
                ],
                brands: [
                    { name: bucket.key, count: bucket.doc_count }
                    for (const bucket of response.aggregations.brands.buckets)
                ],
                price_ranges: [
                    {
                        min: bucket.from,
                        max: bucket.to,
                        count: bucket.doc_count
                    }
                    for (const bucket of response.aggregations.price_ranges.buckets)
                ]
            }
        }
        
        return {
            products: products,
            total: response.hits.total.value,
            took: response.took,
            facets: facets,
            pagination: {
                page: (params.get('from', 0) // params.get('size', 20)) + 1,
                size: params.get('size', 20),
                total_pages: (response.hits.total.value + params.get('size', 20) - 1) // params.get('size', 20)
            }
        }
    }
    
    async update_product_index(product_data) {
        /**
        Update product in search index
        */
        try {
            await this.es.index({
                index: this.index_name,
                id: product_data.id,
                body: product_data,
                refresh: 'wait_for'
            })
        } catch (e) {
            // Log error but don't fail the main operation
            logger.error(`Failed to update search index for product ${product_data.id}: ${e}`);
        }
    }

Step 4: Implement Load Balancing and Auto-Scaling (75 minutes)

Auto-scaling configuration for traffic spikes:

# Kubernetes auto-scaling configuration
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ecommerce-api
spec:
  replicas: 10  # Base replica count
  selector:
    matchLabels:
      app: ecommerce-api
  template:
    metadata:
      labels:
        app: ecommerce-api
    spec:
      containers:
      - name: api
        image: ecommerce-api:latest
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
          limits:
            cpu: 2000m
            memory: 4Gi
        env:
        - name: DB_POOL_SIZE
          value: "20"
        - name: REDIS_POOL_SIZE
          value: "10"
        - name: MAX_CONCURRENT_REQUESTS
          value: "1000"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

---
# Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ecommerce-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ecommerce-api
  minReplicas: 10
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
      - type: Pods
        value: 20
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

---
# Load balancer configuration
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: ecommerce-load-balancer
spec:
  hosts:
  - api.ecommerce.com
  http:
  - match:
    - uri:
        prefix: /api/search
    route:
    - destination:
        host: ecommerce-search-service
        subset: stable
      weight: 90
    - destination:
        host: ecommerce-search-service
        subset: canary
      weight: 10
    timeout: 10s
    retries:
      attempts: 3
      perTryTimeout: 3s
  - match:
    - uri:
        prefix: /api/checkout
    route:
    - destination:
        host: ecommerce-checkout-service
    timeout: 30s
    retries:
      attempts: 2
      perTryTimeout: 10s
  - route:
    - destination:
        host: ecommerce-api
    timeout: 15s
    retries:
      attempts: 3
      perTryTimeout: 5s

---
# Service mesh traffic management
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: ecommerce-circuit-breaker
spec:
  host: ecommerce-api
  trafficPolicy:
    outlierDetection:
      consecutiveErrors: 5
      interval: 30s
      baseEjectionTime: 30s
      maxEjectionPercent: 50
    connectionPool:
      tcp:
        maxConnections: 100
      http:
        http1MaxPendingRequests: 50
        http2MaxRequests: 100
        maxRequestsPerConnection: 10
        maxRetries: 3

Step 5: Monitor and Alert for Performance Issues (45 minutes)

Comprehensive monitoring for e-commerce systems:

// Custom metrics collection for e-commerce
import time from 'time';
import { Counter, Histogram, Gauge } from 'prometheus_client';

class EcommerceMetrics {
    constructor() {
        // Business metrics
        this.orders_created = new Counter(
            'ecommerce_orders_created_total',
            'Total orders created',
            ['status', 'payment_method']
        )
        
        this.revenue = new Counter(
            'ecommerce_revenue_total',
            'Total revenue in dollars',
            ['currency', 'category']
        )
        
        this.cart_actions = new Counter(
            'ecommerce_cart_actions_total',
            'Cart interactions',
            ['action', 'product_category']
        )
        
        // Performance metrics
        this.response_time = new Histogram(
            'ecommerce_response_time_seconds',
            'Response time for requests',
            ['endpoint', 'method'],
            [0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
        )
        
        this.concurrent_users = new Gauge(
            'ecommerce_concurrent_users',
            'Number of active concurrent users'
        )
        
        // Inventory metrics
        this.inventory_updates = new Counter(
            'ecommerce_inventory_updates_total',
            'Inventory update operations',
            ['operation', 'status']
        )
        
        this.low_stock_alerts = new Gauge(
            'ecommerce_low_stock_products',
            'Number of products with low stock'
        )
        
        // Error metrics
        this.payment_failures = new Counter(
            'ecommerce_payment_failures_total',
            'Payment processing failures',
            ['gateway', 'error_type']
        )
    }
    
    track_order_created(order_data) {
        /**Track successful order creation*/
        this.orders_created.labels(
            status=order_data.status,
            payment_method=order_data.payment_method
        ).inc()
        
        this.revenue.labels(
            currency=order_data.currency,
            category=order_data.main_category
        ).inc(order_data.total_amount)
    }
    
    track_performance_degradation() {
        /**Alert on performance issues*/
        const current_time = Date.now();
        
        // Track if response times are above thresholds
        if (this.last_response_times && this.last_response_times.length > 0) {
            const avg_response_time = this.last_response_times.reduce((sum, t) => sum + t, 0) / this.last_response_times.length;
            
            if (avg_response_time > 2.0) {  // 2 second threshold
                this.send_alert(
                    level='warning',
                    message=`Average response time: ${avg_response_time.toFixed(2)}s`,
                    metric='response_time_degradation'
                )
            }
            
            if (avg_response_time > 5.0) {  // 5 second critical threshold
                this.send_alert(
                    level='critical',
                    message=`Critical response time: ${avg_response_time.toFixed(2)}s`,
                    metric='response_time_critical'
                )
            }
        }
    }

// Alerting configuration
const alerting_rules = `
groups:
- name: ecommerce.rules
  rules:
  # High error rate alert
  - alert: HighErrorRate
    expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "High error rate detected"
      description: "Error rate is {{ $value | humanizePercentage }} for the last 5 minutes"

  # Response time alert
  - alert: SlowResponseTime
    expr: histogram_quantile(0.95, rate(ecommerce_response_time_seconds_bucket[5m])) > 2.0
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Slow response times"
      description: "95th percentile response time is {{ $value }}s"

  # Low inventory alert
  - alert: LowInventory
    expr: ecommerce_low_stock_products > 100
    for: 1m
    labels:
      severity: warning
    annotations:
      summary: "High number of low stock products"
      description: "{{ $value }} products are running low on inventory"

  # Payment failure spike
  - alert: PaymentFailureSpike
    expr: rate(ecommerce_payment_failures_total[5m]) > 0.1
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "Payment failure rate spike"
      description: "Payment failures increased to {{ $value }} per second"

  # Revenue drop alert
  - alert: RevenueDrop
    expr: rate(ecommerce_revenue_total[1h]) < 0.5 * rate(ecommerce_revenue_total[1h] offset 1d)
    for: 10m
    labels:
      severity: warning
    annotations:
      summary: "Revenue significantly below normal"
      description: "Current hourly revenue is {{ $value }} vs normal {{ rate(ecommerce_revenue_total[1h] offset 1d) }}"

  # Concurrent user capacity alert
  - alert: HighConcurrentUsers
    expr: ecommerce_concurrent_users > 800000
    for: 1m
    labels:
      severity: warning
    annotations:
      summary: "High concurrent user load"
      description: "Currently serving {{ $value }} concurrent users - approaching capacity limits"
`;

Real-World Example: Fashion Retailer's Black Friday Success

What they did: Transformed their e-commerce infrastructure to handle 50x traffic increase during Black Friday

Before Black Friday 2024:

  • Peak capacity: 25,000 concurrent users before performance degradation
  • Database: Single MySQL instance with read replicas
  • Caching: Basic Redis caching for sessions only
  • Search: Database-based product search with full-text indexing
  • Payment: Single payment gateway (Stripe) with basic retry logic
  • Inventory: Real-time inventory updates causing lock contention
  • Previous Black Friday: 45 minutes of downtime, $2.1M in lost sales

Architecture transformation (6 months before Black Friday):

  1. Database re-architecture:

    • Migrated to PostgreSQL with read replicas and connection pooling
    • Implemented table partitioning for orders and analytics
    • Separated inventory management into dedicated microservice
  2. Caching strategy overhaul:

    • Multi-layer caching (memory + Redis + CDN)
    • Cache warming automation 2 hours before traffic spikes
    • Intelligent cache invalidation to prevent stampeding herd
  3. Search infrastructure:

    • Implemented Elasticsearch cluster for product search
    • Added auto-suggest and faceted search capabilities
    • Search performance improved from 800ms to 45ms average
  4. Payment resilience:

    • Integrated 3 payment gateways with automatic failover
    • Implemented payment queue system for high-volume processing
    • Added payment reconciliation service for consistency
  5. Auto-scaling implementation:

    • Kubernetes-based auto-scaling from 10 to 200 pods
    • Geographic load distribution across 3 regions
    • Circuit breakers and bulkhead patterns for service isolation

Black Friday 2024 results:

  • Peak traffic: 1.2M concurrent users (48x normal traffic)
  • Orders processed: 847,000 orders in 24 hours vs. previous record of 89,000
  • System availability: 99.97% uptime (only 2.6 minutes of degraded performance)
  • Revenue: $47.3M (312% increase over previous Black Friday)
  • Performance: Average response time stayed under 200ms throughout peak hours
  • Zero downtime: No service interruptions or emergency interventions needed

Key architectural decisions that made the difference:

  • Eventual consistency: Accepted 5-15 second delays for inventory updates to prevent system bottlenecks
  • Read-heavy optimization: 90% of traffic served from cache layers
  • Graceful degradation: Non-critical features (recommendations, reviews) disabled during peak to preserve core shopping functionality
  • Regional failover: Automatic traffic routing away from overloaded regions

Post-Black Friday insights:

  • Cost efficiency: Infrastructure costs increased 40% but revenue increased 312%
  • Team confidence: Zero emergency calls or weekend work required
  • Customer experience: Customer satisfaction scores increased 23% due to consistent performance
  • Competitive advantage: Gained market share as competitors experienced outages

Quote from CTO: "The transformation wasn't just technical - it was cultural. We went from fearing Black Friday to confidently planning flash sales throughout the year. Our infrastructure became a business enabler instead of a constraint."

Tools and Resources

Infrastructure and Scaling

Container Orchestration:

  • Kubernetes (Free) - Container orchestration with auto-scaling
  • Google GKE ($0.10 per cluster hour) - Managed Kubernetes with auto-scaling
  • Amazon EKS ($0.10 per cluster hour) - Managed Kubernetes on AWS
  • Azure AKS (Free cluster management) - Managed Kubernetes on Azure

Load Balancing and Traffic Management:

  • NGINX Plus ($2,500/year) - High-performance load balancer
  • HAProxy (Free + Enterprise support) - Reliable load balancing solution
  • Istio (Free) - Service mesh for traffic management and observability
  • AWS Application Load Balancer ($0.0225 per hour) - Managed load balancing

Database and Caching

Database Solutions:

  • PostgreSQL with Citus (Free + Cloud pricing) - Distributed PostgreSQL for scale
  • Amazon RDS (Variable pricing) - Managed database with read replicas
  • Google Cloud SQL (Variable pricing) - Managed database services
  • CockroachDB (Free + Enterprise) - Distributed SQL database

Caching and Session Management:

  • Redis Cluster (Free + managed services) - Distributed caching
  • Memcached (Free) - High-performance distributed memory caching
  • Amazon ElastiCache ($0.017/hour per node) - Managed Redis and Memcached
  • Google Cloud Memorystore (Variable pricing) - Managed Redis service

Search and Analytics

Search Solutions:

  • Elasticsearch (Free + paid features) - Full-text search and analytics
  • Amazon CloudSearch ($0.12/hour per instance) - Managed search service
  • Algolia ($0.50 per 1,000 requests) - Managed search API
  • Swiftype ($250/month starter) - Enterprise search solutions

Analytics and Monitoring:

  • Prometheus + Grafana (Free) - Open-source monitoring and visualization
  • Datadog ($15/host/month) - Comprehensive monitoring and analytics
  • New Relic ($25/month per user) - Application performance monitoring
  • Elastic APM (Free + paid features) - Application performance monitoring

Measuring E-commerce Success

Business Performance Indicators

Revenue Metrics:

  • Orders per minute during peak traffic
  • Conversion rate under different load levels
  • Average order value during high-traffic periods
  • Revenue per concurrent user

Customer Experience Metrics:

  • Page load time percentiles (P50, P95, P99)
  • Checkout completion rate under load
  • Search result response time
  • Customer satisfaction scores during peak events

System Performance Metrics:

  • Concurrent user capacity before degradation
  • Database query response time
  • Cache hit ratio during peak traffic
  • Payment processing success rate

Success Benchmarks

30-Day Targets:

  • Handle 10x normal traffic without performance degradation
  • <2 second page load times during peak traffic
  • 99% uptime during planned sale events

  • <1% error rate during high-traffic periods

Peak Season Targets:

  • Support 50-100x normal concurrent users
  • Maintain <500ms API response times under full load
  • 99.9% payment processing success rate

  • Zero revenue-impacting downtime during major sales events

Ready to Get Started?

Here's your e-commerce scaling action plan:

  1. Today: Audit your current infrastructure capacity and identify bottlenecks
  2. This week: Implement database read replicas and basic caching layer
  3. Next week: Set up monitoring and alerting for key performance metrics
  4. Next month: Deploy auto-scaling infrastructure and load testing environment

Reality check: Building truly scalable e-commerce architecture takes 3-6 months of focused effort, but the ROI during peak traffic periods can be 500-1000%. Most retailers see the investment pay for itself during their first major sale event.

The truth: In e-commerce, your infrastructure is your competitive advantage. When competitors' sites crash under load, customers come to you. Build systems that scale, and turn traffic spikes into revenue opportunities.

Scale your e-commerce backend today - your Black Friday revenue depends on it.

Topics Covered

Scalable E-Commerce BackendBlack Friday ArchitectureHigh Traffic E-CommerceE-Commerce ScalabilityBackend Performance OptimizationE-Commerce System Design

Ready for More?

Explore our comprehensive collection of guides and tutorials to accelerate your tech journey.

Explore All Guides
Weekly Tech Insights

Stay Ahead of the Curve

Join thousands of tech professionals getting weekly insights on AI automation, software architecture, and modern development practices.

No spam, unsubscribe anytimeReal tech insights weekly