snowflake_id_generator 1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/snowflake_id_generator.rb +127 -0
  3. metadata +45 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 564f24abd439144a45be9dd151cdedb2d3e35562a7808fa6544bd164b9561367
4
+ data.tar.gz: e1f3f6aa33fa2027b3c22d31f1b373f0b999c7a6e2710b20e39d22757e50dfa9
5
+ SHA512:
6
+ metadata.gz: 1cdb79ad56bba69f83372453b9b1b4e2efcb68e81fbe49598bf3b2536ffbe8930e449338c05c47bebf30f93a8001395c2a05dea48a2b6b09b34b84d19b0a6dfa
7
+ data.tar.gz: fa467ee85a046b61c03a9a7db1d7020c4318f60c50c5b616b6c1b60dcc739164cbbc2a9ba61eaf1e57c6f29a63cc3f4f5f3e4b97e0c5678cfcc8e74179343b6b
@@ -0,0 +1,127 @@
1
+ module Snowflake
2
+ EPOCH = 1288834974657 # Custom epoch (January 1, 2024)
3
+ WORKER_ID_BITS = 5
4
+ DATACENTER_ID_BITS = 5
5
+ SEQUENCE_BITS = 12
6
+
7
+ MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS)
8
+ MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)
9
+ MAX_SEQUENCE = -1 ^ (-1 << SEQUENCE_BITS)
10
+
11
+ WORKER_ID_SHIFT = SEQUENCE_BITS
12
+ DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
13
+ TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS
14
+
15
+ class IdGenerator
16
+
17
+ attr_reader :datacenter_id, :worker_id, :sequence, :last_timestamp
18
+
19
+ def initialize(datacenter_id, worker_id)
20
+ if datacenter_id > Snowflake::MAX_DATACENTER_ID || datacenter_id < 0
21
+ raise ArgumentError, "datacenter_id must be between 0 and #{Snowflake::MAX_DATACENTER_ID}"
22
+ end
23
+
24
+ if worker_id > Snowflake::MAX_WORKER_ID || worker_id < 0
25
+ raise ArgumentError, "worker_id must be between 0 and #{Snowflake::MAX_WORKER_ID}"
26
+ end
27
+
28
+ @datacenter_id = datacenter_id
29
+ @worker_id = worker_id
30
+ @sequence = 0
31
+ @last_timestamp = -1
32
+ end
33
+
34
+ def next_id
35
+ timestamp = current_time_millis
36
+
37
+ if timestamp < @last_timestamp
38
+ raise "Clock moved backwards. Refusing to generate id for #{@last_timestamp - timestamp} milliseconds"
39
+ end
40
+
41
+ if @last_timestamp == timestamp
42
+ @sequence = (@sequence + 1) & Snowflake::MAX_SEQUENCE
43
+ if @sequence == 0
44
+ timestamp = wait_for_next_millis(@last_timestamp)
45
+ end
46
+ else
47
+ @sequence = 0
48
+ end
49
+
50
+ @last_timestamp = timestamp
51
+
52
+ p "timestamp - EPOCH: #{timestamp - Snowflake::EPOCH}"
53
+
54
+ ((timestamp - Snowflake::EPOCH) << Snowflake::TIMESTAMP_SHIFT) |
55
+ (@datacenter_id << Snowflake::DATACENTER_ID_SHIFT) |
56
+ (@worker_id << Snowflake::WORKER_ID_SHIFT) |
57
+ @sequence
58
+ end
59
+
60
+ private
61
+
62
+ def current_time_millis
63
+ (Time.now.to_f * 1000).to_i
64
+ end
65
+
66
+ def wait_for_next_millis(last_timestamp)
67
+ timestamp = current_time_millis
68
+ while timestamp <= last_timestamp
69
+ timestamp = current_time_millis
70
+ end
71
+ timestamp
72
+ end
73
+ end
74
+
75
+ class IdAnalyzer
76
+ def initialize(id)
77
+ @id = id.to_i
78
+ @binary_id = @id.to_s(2).rjust(64, '0')
79
+ end
80
+
81
+ def analyze
82
+ {
83
+ timestamp: extract_timestamp,
84
+ datacenter_id: extract_datacenter_id,
85
+ worker_id: extract_worker_id,
86
+ sequence: extract_sequence,
87
+ timestamp_utc: timestamp_to_utc(extract_timestamp)
88
+ }
89
+ end
90
+
91
+ private
92
+
93
+ def extract_timestamp
94
+ # Extract timestamp bits from the binary ID
95
+ timestamp_bits = @binary_id[0..41]
96
+ # Convert timestamp bits to integer
97
+ timestamp = timestamp_bits.to_i(2)
98
+ # Add the dynamic epoch (based on the first 41 bits of the ID)
99
+ timestamp += Snowflake::EPOCH
100
+ timestamp
101
+ end
102
+
103
+ def extract_datacenter_id
104
+ @binary_id[42..46].to_i(2)
105
+ end
106
+
107
+ def extract_worker_id
108
+ @binary_id[47..51].to_i(2)
109
+ end
110
+
111
+ def extract_sequence
112
+ @binary_id[52..63].to_i(2)
113
+ end
114
+
115
+ def timestamp_to_utc(timestamp)
116
+ Time.at(timestamp / 1000.0).utc
117
+ end
118
+ end
119
+ end
120
+
121
+
122
+ # datacenter_id = ENV['DATACENTER_ID'].to_i
123
+ # worker_id = ENV['WORKER_ID'].to_i
124
+ # @generator = Snowflake::IdGenerator.new(datacenter_id, worker_id)
125
+ # p id = @generator.next_id
126
+
127
+ # p analyzer = Snowflake::IdAnalyzer.new(id).analyze
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: snowflake_id_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.3'
5
+ platform: ruby
6
+ authors:
7
+ - kokorolx
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-05-24 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Generate unique Snowflake IDs across multiple data centers and workers
14
+ simultaneously using a distributed system.
15
+ email:
16
+ - kokoro.lehoang@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/snowflake_id_generator.rb
22
+ homepage: https://github.com/kokorolx/snowflake_id_generator
23
+ licenses:
24
+ - MIT
25
+ metadata: {}
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirements: []
41
+ rubygems_version: 3.4.10
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: Snowflake ID generator and analyzer service.
45
+ test_files: []