__init__.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. import time
  2. import threading
  3. class SnowflakeIdWorker:
  4. def __init__(self, worker_id: int = 1, datacenter_id: int = 1, sequence: int = 0):
  5. self.worker_id = worker_id
  6. self.datacenter_id = datacenter_id
  7. self.sequence = sequence
  8. self.twepoch = 1288834974657
  9. self.worker_id_bits = 5
  10. self.datacenter_id_bits = 5
  11. self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
  12. self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
  13. self.sequence_bits = 12
  14. self.worker_id_shift = self.sequence_bits
  15. self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
  16. self.timestamp_left_shift = (
  17. self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
  18. )
  19. self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
  20. self.last_timestamp = -1
  21. self._lock = threading.Lock()
  22. def _gen_timestamp(self) -> int:
  23. return int(time.time() * 1000)
  24. def _til_next_millis(self, last_timestamp: int) -> int:
  25. ts = self._gen_timestamp()
  26. while ts <= last_timestamp:
  27. ts = self._gen_timestamp()
  28. return ts
  29. def get_id(self) -> int:
  30. with self._lock:
  31. ts = self._gen_timestamp()
  32. if ts < self.last_timestamp:
  33. raise RuntimeError(f"Clock moved backwards. Refusing to generate id for {self.last_timestamp - ts} milliseconds")
  34. if ts == self.last_timestamp:
  35. self.sequence = (self.sequence + 1) & self.sequence_mask
  36. if self.sequence == 0:
  37. ts = self._til_next_millis(self.last_timestamp)
  38. else:
  39. self.sequence = 0
  40. self.last_timestamp = ts
  41. return (
  42. ((ts - self.twepoch) << self.timestamp_left_shift)
  43. | (self.datacenter_id << self.datacenter_id_shift)
  44. | (self.worker_id << self.worker_id_shift)
  45. | self.sequence
  46. )
  47. snowflake = SnowflakeIdWorker()
  48. def next_id() -> int:
  49. return snowflake.get_id()