The Clock That Ran Backwards — Java Bug Hunt

Inspired by Twitter's Snowflake ID generator — and the NTP clock-rollback duplicates it explicitly guards against.

  • Language: Java
  • Layer: Database
  • Difficulty: Hard
  • Concepts: IDs, Clocks
  • Modelled on: Twitter Snowflake
  • Visible tests: a new tick resets the sequence; the same tick increments the sequence; a backwards clock is refused
  • Reward: 50 XP for a complete fix

Briefing

Inspired by Twitter's Snowflake ID generator — and the NTP clock-rollback duplicates it explicitly guards against. IDs are (timestamp << 12) | sequence and must be strictly increasing; if the wall clock ever runs backwards, the generator must refuse rather than mint colliding IDs.

IdGen.java currently trusts the clock and never resets the sequence.

Bug report

BUG-SNOW · Priority: Critical · Reported by: storage

next(lastTimestamp, lastSeq, now):

  • now < lastTimestamp -> throw (clock moved backwards!)
  • now == lastTimestamp -> same tick: sequence + 1
  • now > lastTimestamp -> new tick: sequence resets to 0
  • returns id = now * 4096 + seq

Observed: duplicate IDs after an NTP correction, and sequences that never reset — overflowing the 12-bit field within a busy second.

Logs

[idgen] now=1699999999 < last=1700000042 -> id went BACKWARDS
[db] duplicate key: 6963200172032

The code as shipped

IdGen.java (editable)

class IdGen {
    // Returns { id, seqUsed } packed as long[2].
    static long[] next(long lastTimestamp, long lastSeq, long now) {
        long seq = lastSeq + 1;
        long id = now * 4096 + seq;
        return new long[] { id, seq };
    }
}

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More Java bug hunts.