Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Architecting High-Concurrency Flash Sale Systems: Core Strategies and Solutions

Tech Oct 25 1

Technology Stack and Core Frameworks

The foundation of a robust flash sale system relies on Spring Boot for rapid development, Thymeleaf for server-side rendering (if not fully decoupled), MyBatis for ORM, and MySQL with Druid connection pooling. Caching and distributed sessions are handled via Redis using the Jedis client and Fast for object serialization. Security measures include MD5 hashing via Apache Commons and request validation using JSR 303.

Component Scanning and Thread Safety

In Spring, @Component auto-registers classes into the IoC container, whereas @Bean is typically used to register third-party library instances. By default, Spring controllers are singletons, making them thread-unsafe if they contain mutable state. To achieve thread safety, either change the scope to prototype or encapsulate state variables within ThreadLocal.

Authentication and Distributed Session Management

Redis Key Design via Template Pattern

To prevent key collisions and manage expiration logically, Redis keys should follow a structured naming convention. Implementing the Template Pattern ensures consistency:

public interface CacheKeyPrefix {
    int getExpiration();
    String getNamespace();
}

public abstract class AbstractCachePrefix implements CacheKeyPrefix {
    private int timeToLive;
    private String prefix;

    protected AbstractCachePrefix(int timeToLive, String prefix) {
        this.timeToLive = timeToLive;
        this.prefix = prefix;
    }

    protected AbstractCachePrefix(String prefix) {
        this(0, prefix); // 0 indicates no expiration
    }

    @Override
    public int getExpiration() {
        return timeToLive;
    }

    @Override
    public String getNamespace() {
        return this.getClass().getSimpleName() + ":" + prefix;
    }
}

public class UserCachePrefix extends AbstractCachePrefix {
    public UserCachePrefix(String prefix) {
        super(prefix);
    }
    public static UserCachePrefix BY_ID = new UserCachePrefix("id");
}

Double MD5 Password Encryption

Passwords are hashed twice to mitigate different attack vectors. The client hashes the plaintext with a fixed salt to prevent plaintext transmission over the network. The server then hashes the result again with a random salt before storing it, protecting against database leakage and rainbow table attacks.

Parameter Validation and Global Exception Handling

Instead of manually validating request parameters in every controller, JSR 303 annotations (like @NotNull, @Length) are applied to View Objects (VOs). A global exception handler decorated with @ControllerAdvice intercepts validation errors and business logic exceptions, returning standardized error responses and keeping the codebase clean.

Distributed Session and User Resolution

In a multi-instance deployment, session synchronization is inefficient. Storing session data in Redis allows all application instances to share the state. Upon successful login, the server generates a UUID token, stores the user session in Redis, and returns the token to the client. For subsequent requests, a custom HandlerMethodArgumentResolver extracts the token from the HTTP header, retrieves the user object from Redis, and injects it directly into the controller method parameters.

Flash Sale Data Modeling and Logic

Database Schema

Flash sale items require a dedicated table containing sale-specific attributes like discount price, available stock, start time, and end time. Similarly, flash sale orders should be stored in a separate table from standard orders. Primary keys should utilize the Snowflake algorithm rather than auto-incrementing IDs to ensure distributed uniqueness.

Client vs. Server Responsibilities

The server is responsible for providing the sale status (not started, in progress, ended) and the precise start time. The client handles the countdown timer logic, ensuring users see a real-time countdown without continuously polling the server.

System Benchmarking and Bottlenecks

Performance testing using tools like JMeter involves measuring Response Time (RT), Throughput, and Queries Per Second (QPS). Distributed testing can be executed via JMeter CLI on Linux. The primary bottleneck in high-concurrency systems is typically the relational database; while web servers can scale out easily, database sharding introduces significant complexity.

Page-Level Performance Optimization

Multi-Level Caching

Page/URL Caching: Entire rendered HTML pages are cached in Redis with a short Time-To-Live (e.g., 30 seconds) to absorb read spikes.
Object Caching: Fine-grained caching of user profiles or order details. Unlike URL caching, object caches require strict consistency management; updates to the database must trigger corresponding cache invalidations or updates.

Frontend-Backend Separation (Staticization)

Pages are delivered as static HTML. The client uses AJAX to fetch dynamic data asynchronously. This shifts rendering load to the browser and leverages client-side caching. To maximize browser cache efficiency, the server should set Cache-Control: max-age headers, preventing unnecessary 304 Not Modified validation requests.

Preventing Overselling

Overselling occurs when concurrent requests deplete stock below zero or a user secures multiple orders for a single-item limit.

  • Negative Stock: Rely on database row-level locks during updates:
    UPDATE flash_sale_item SET available_stock = available_stock - 1 WHERE item_id = #{itemId} AND available_stock > 0;
  • Duplicate Orders: Create a unique composite index on the order table combining user_id and item_id. The database will reject duplicate inserts.

Service-Level Interface Optimization

Directly hitting the database during a traffic spike guarantees system failure. The optimized flow decouples order creation:

  1. Stock Pre-loading: Load available stock into Redis upon system initialization using InitializingBean.
  2. Redis Pre-deduction: Decrement stock in Redis atomically. If stock falls below zero, return an "out of stock" error immediately.
  3. Local Memory Flags: Maintain a ConcurrentHashMap indicating whether an item is sold out. This prevents redundant network calls to Redis for already-depleted items.
  4. Asynchronous Ordering: Send the request to a RabbitMQ queue. Return a "queuing" response to the client instantly.
  5. Database Processing: Consumers dequeue messages and execute the transactional database operations (creating orders, deducting stock).
  6. Client Polling: The frontend polls a status endpoint to check if the order was successfully created.

Security Mechanisms

Dynamic URL Hiding

The actual flash sale endpoint URL is not static. The client must first request a dynamic path containing a time-sensitive, randomized token (Path Variable) valid only for that user and session.

CAPTCHA Integration

Requiring users to solve a math CAPTCHA before obtaining the sale URL serves two purposes: blocking automated bot scripts and intentionally staggering user request timestamps to flatten traffic peaks.

Rate Limiting via Interceptors

A custom annotation @AccessLimit marks protected endpoints. A Spring interceptor extracts the user ID and request URI, tracking access counts in Redis within a sliding time window. If requests exceed the defined threshold, the interceptor blocks the request immediately.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
    int seconds();
    int maxRequests();
    boolean requireLogin() default true;
}

Infrastructure and Server Tuning

Tomcat Optimization

  • Memory: Configure JVM heap sizes (-Xms, -Xmx) and garbage collection strategies in CATALINA_OPTS.
  • Concurrency: Adjust maxThreads, maxConnections, and acceptCount in server.xml to handle higher concurrent requests.
  • APR: Switch from NIO to the Apache Portable Runtime (APR) using JNI for native-level I/O performance.

Load Balancing and High Availability

LVS operates at Layer 4 (Network) for raw throughput, while Nginx operates at Layer 7 (Application) for intelligent routing and static file caching. For extreme scale, traffic flows from LVS to multiple Nginx instances. Keepalived is deployed alongside LVS/Nginx to ensure high availability through VRRP failover mechanisms.

Advanced Scenarios and Edge Cases

Handling Multi-Item Flash Sales

When a sale allows purchasing more than one unit, the dequeue logic must calculate the remaining quota for the user and cap the deduction accordingly:

transaction.start();
try {
    int currentStock = selectStockForUpdate(itemId);
    if (currentStock < requestedAmount) return "Insufficient Stock";
    
    int userOwned = selectSumPurchased(userId, itemId);
    if (userOwned >= maxAllowed) return "Limit Exceeded";
    
    int allowableAmount = Math.min(maxAllowed - userOwned, requestedAmount);
    
    updateStockDeduction(itemId, allowableAmount);
    insertNewOrder(userId, itemId, allowableAmount);
    
    transaction.commit();
} catch (Exception ex) {
    transaction.rollback();
}

Resilience Strategies

If Redis pre-deduction succeeds but the database transaction fails, a compensation mechanism or manual reconciliation process is required. If Redis crashes mid-sale, fallback logic or local caching limits must activate to prevent overwhelming the database. For unpaid successful orders, a scheduled job should monitor order creation timestamps and roll back stock if payment is not received within a specified window.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.