{"record":{"id":"d454b950784e88f9","repo":"shuzheng/zheng","slug":"clock-moved-backwards-refusing-to-generate-id-fo","errorCode":null,"errorMessage":"Clock moved backwards.  Refusing to generate id for %d milliseconds","messagePattern":"Clock moved backwards\\.  Refusing to generate id for (.+?) milliseconds","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"zheng-common/src/main/java/com/zheng/common/util/key/SnowflakeIdWorker.java","lineNumber":118,"sourceCode":"\t\t\tthrow new IllegalArgumentException(String.format(\"datacenter Id can't be greater than %d or less than 0\", maxDatacenterId));\n\t\t}\n\t\tthis.workerId = workerId;\n\t\tthis.datacenterId = datacenterId;\n\t}\n\n\t// ==============================Methods==========================================\n\n\t/**\n\t * 获得下一个ID (该方法是线程安全的)\n\t *\n\t * @return SnowflakeId\n\t */\n\tpublic synchronized long nextId() {\n\t\tlong timestamp = timeGen();\n\n\t\t//如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常\n\t\tif (timestamp < lastTimestamp) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\tString.format(\"Clock moved backwards.  Refusing to generate id for %d milliseconds\", lastTimestamp - timestamp));\n\t\t}\n\n\t\t//如果是同一时间生成的，则进行毫秒内序列\n\t\tif (lastTimestamp == timestamp) {\n\t\t\tsequence = (sequence + 1) & sequenceMask;\n\t\t\t//毫秒内序列溢出\n\t\t\tif (sequence == 0) {\n\t\t\t\t//阻塞到下一个毫秒,获得新的时间戳\n\t\t\t\ttimestamp = tilNextMillis(lastTimestamp);\n\t\t\t}\n\t\t}\n\t\t//时间戳改变，毫秒内序列重置\n\t\telse {\n\t\t\tsequence = 0L;\n\t\t}\n\n\t\t//上次生成ID的时间截","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/shuzheng/zheng/blob/7005c0a775e6d014d1dc8a8a809f7b1c13bf785a/zheng-common/src/main/java/com/zheng/common/util/key/SnowflakeIdWorker.java#L100-L136","documentation":"nextId() compares the current timestamp with lastTimestamp; if time has gone backwards it throws RuntimeException refusing to generate ids for the negative interval, because reusing a timestamp could produce duplicate snowflake ids. The message includes how many milliseconds the clock moved backwards.","triggerScenarios":"Calling nextId() after the system clock was set back (manual change, NTP correction, VM snapshot restore, leap adjustment) so timeGen() returns a value smaller than the previously used lastTimestamp.","commonSituations":"VM/host restored from snapshot; NTP daemon stepping the clock backwards; container migrated to a host with skewed clock; operator manually correcting a too-fast clock.","solutions":["Fix the underlying clock source and let it catch up; the service will resume once current time exceeds lastTimestamp.","Enable gradual (slew) NTP corrections instead of step changes (e.g. ntpd without -g steps, chrony slew mode).","Restart the service only if the clock regression is permanent and ensure workerId/datacenterId differs, or persist lastTimestamp and reject duplicates on restart.","Wrap id generation so the RuntimeException is handled: queue requests briefly or fail over to another node whose clock is monotonic."],"exampleFix":"// before\nlong id = idWorker.nextId(); // throws after clock rollback\n// after\ntry {\n    long id = idWorker.nextId();\n} catch (RuntimeException e) {\n    if (e.getMessage().startsWith(\"Clock moved backwards\")) {\n        // wait for clock to catch up or fail over\n    } else {\n        throw e;\n    }\n}","handlingStrategy":"retry","validationCode":"// pre-check before generating\nlong ts = System.currentTimeMillis();\n// if a previously persisted lastTimestamp is newer than ts, clock regressed\nif (ts < lastKnownTimestamp) {\n    log.warn(\"Clock moved backwards by {} ms; delaying id generation\", lastKnownTimestamp - ts);\n}","typeGuard":"boolean clockIsMonotonic(long lastTimestamp) {\n    return System.currentTimeMillis() >= lastTimestamp;\n}","tryCatchPattern":"long id;\nwhile (true) {\n    try { id = idWorker.nextId(); break; }\n    catch (RuntimeException e) {\n        if (e.getMessage() != null && e.getMessage().startsWith(\"Clock moved backwards\")) {\n            Thread.sleep(100); // wait for clock to catch up\n        } else throw e;\n    }\n}","preventionTips":["Run NTP/chrony in slew mode so clocks never step backwards.","Never restore VM snapshots of machines running id generators without resetting worker state.","Persist lastTimestamp and refuse to serve ids after rollback until time catches up.","Monitor clock offsets (e.g. chrony tracking) and alert on negative adjustments.","Isolate id generation on nodes with stable clocks (dedicated, non-preemptible VMs)."],"tags":["clock-skew","id-generation","snowflake","ntp"],"backgroundTag":"clock-moved-backwards","analyzedSha":"7005c0a775e6d014d1dc8a8a809f7b1c13bf785a","analyzedAt":"2026-09-04T16:58:32.852Z","contentChangedAt":"2026-09-04T16:58:32.852Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}