Backend Development 9 min read

Solving Duplicate Order Number Issues in High-Concurrency Java Applications

After encountering duplicate order IDs during a system upgrade, the author analyzes the shortcomings of the original timestamp-and-random-based generator, demonstrates concurrency tests revealing collisions, and presents a revised Java implementation using atomic counters, shortened timestamps, and IP suffixes to reliably produce unique order numbers even under high load.

Architecture Digest
Architecture Digest
Architecture Digest
Solving Duplicate Order Number Issues in High-Concurrency Java Applications

During a production incident the system generated two identical order numbers with different contents, and queries by order number threw errors, prompting a redesign of the order‑number generation logic.

The original implementation built the order ID by concatenating a timestamp formatted as yyMMddHHmmssSSS with a merchant ID and a two‑digit random number, resulting in a 22‑character string. In high‑concurrency tests (100 parallel calls) 13 duplicates were observed.

Analysis showed that the two‑digit random component and the millisecond precision were insufficient to guarantee uniqueness under load.

A new approach was devised: remove the merchant ID, keep only three digits of milliseconds, and use a thread‑safe AtomicInteger counter that increments for each request. The counter is reset when it approaches its limit.

The revised generator also appends a suffix derived from the host’s IP address to differentiate instances in a clustered environment. The final method looks like:

private static final AtomicInteger SEQ = new AtomicInteger(1000);
private static final DateTimeFormatter DF_FMT_PREFIX = DateTimeFormatter.ofPattern("yyMMddHHmmssSS");
private static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai");

public static String generateOrderNo() {
    LocalDateTime now = LocalDateTime.now(ZONE_ID);
    if (SEQ.intValue() > 9990) {
        SEQ.getAndSet(1000);
    }
    return now.format(DF_FMT_PREFIX) + getLocalIpSuffix() + SEQ.getAndIncrement();
}

The helper getLocalIpSuffix() obtains the last octet of the container’s IP address (or a fallback random value) and caches it for subsequent calls.

Stress testing with 8 000 parallel invocations confirmed zero duplicates, demonstrating that the combination of a high‑resolution timestamp, an instance‑specific IP suffix, and an atomic counter reliably produces unique order numbers even in a multi‑instance Docker deployment.

Additional recommendations include relying on the atomic counter’s CAS semantics instead of explicit locks, using the double‑checked locking pattern for the IP suffix, and always performing thorough concurrency testing before releasing changes.

Javabackend developmentconcurrencyunique identifierAtomicIntegerorder ID
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.