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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend Developmentunique 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

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.