@@ -18,6 +18,8 @@ const (
1818 MAX12BITS = 4095
1919 // MAX41BITS is the maximum value for a 41 bits number
2020 MAX41BITS = 2199023255551
21+ // CLOCK_DRIFT_TOLERANCE_MS is the tolerance for clock drift in milliseconds
22+ CLOCK_DRIFT_TOLERANCE_MS = 10
2123)
2224
2325// Spaceflake represents a Spaceflake
@@ -162,13 +164,15 @@ type Worker struct {
162164 Sequence uint64
163165 // ID is the worker ID that the Spaceflake generator will use for the next 5 bits
164166 ID uint64
165-
166- increment uint64
167- mutex * sync.Mutex
167+ // used to prevent clockdrift
168+ lastTimestamp uint64
169+ increment uint64
170+ mutex * sync.Mutex
168171}
169172
170173// GenerateSpaceflake generates a Spaceflake
171174func (w * Worker ) GenerateSpaceflake () (* Spaceflake , error ) {
175+
172176 if w .Node == nil {
173177 return nil , fmt .Errorf ("node is not set" )
174178 }
@@ -197,6 +201,16 @@ func (w *Worker) GenerateSpaceflake() (*Spaceflake, error) {
197201 milliseconds := uint64 (math .Floor (microTime () * 1000 ))
198202 milliseconds -= w .BaseEpoch
199203
204+ if delta := w .lastTimestamp - milliseconds ; milliseconds < w .lastTimestamp {
205+ if delta >= CLOCK_DRIFT_TOLERANCE_MS {
206+ return nil , fmt .Errorf ("clock moved backwards by %dms" , delta )
207+ }
208+ time .Sleep (time .Duration (delta + 1 ) * time .Millisecond )
209+ milliseconds = uint64 (math .Floor (microTime ()* 100 )) - w .BaseEpoch
210+ }
211+
212+ w .lastTimestamp = milliseconds
213+
200214 base := stringPadLeft (decimalBinary (milliseconds ), 41 , "0" )
201215 nodeID := stringPadLeft (decimalBinary (w .Node .ID ), 5 , "0" )
202216 workerID := stringPadLeft (decimalBinary (w .ID ), 5 , "0" )
@@ -250,6 +264,10 @@ func (w *Worker) GenerateSpaceflakeAt(at time.Time) (*Spaceflake, error) {
250264 milliseconds := uint64 (math .Floor (microTime * 1000 ))
251265 milliseconds -= w .BaseEpoch
252266
267+ if milliseconds < w .lastTimestamp {
268+ return nil , fmt .Errorf ("cannot generate Spaceflake: Detected clock drift. The time you want to generate the Spaceflake at is before the last generated Spaceflake time" )
269+ }
270+
253271 base := stringPadLeft (decimalBinary (milliseconds ), 41 , "0" )
254272 nodeID := stringPadLeft (decimalBinary (w .Node .ID ), 5 , "0" )
255273 workerID := stringPadLeft (decimalBinary (w .ID ), 5 , "0" )
0 commit comments