snowflaked 0.2.0-x86_64-darwin → 0.3.0-x86_64-darwin
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.
- checksums.yaml +4 -4
- data/README.md +8 -9
- data/lib/snowflaked/3.3/snowflaked.bundle +0 -0
- data/lib/snowflaked/3.4/snowflaked.bundle +0 -0
- data/lib/snowflaked/4.0/snowflaked.bundle +0 -0
- data/lib/snowflaked/model_extensions.rb +5 -1
- data/lib/snowflaked/railtie.rb +3 -4
- data/lib/snowflaked/version.rb +1 -1
- data/lib/snowflaked.rb +67 -17
- metadata +18 -5
- data/lib/snowflaked/3.2/snowflaked.bundle +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c4b472c55a93ad9d23408576c10f5efe71fab97e18d4687115aa0b589a11c207
|
|
4
|
+
data.tar.gz: b9845f8ad1c9d2fd6641b5af6b224d844065c6c653dc1e77bfbc2eadafe994c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 549aef8a88663ba9838becf340936c7b43f1711f5e4bbf66d3b4c314f995cbd9aa942978c0423fcae0ed780239cf9e769f656ebda97b1d2f5fa48077d82a5aa5
|
|
7
|
+
data.tar.gz: 9a1fd304cafdc6913a513a38770fbc31b9a0146b62b7dcb4b8c1f24d249098bc1a090aad2026e1f5d2ac2203d94ae82c2bea3960e21d45a8ef8b1c1a991f5a89
|
data/README.md
CHANGED
|
@@ -83,14 +83,16 @@ end
|
|
|
83
83
|
```ruby
|
|
84
84
|
Snowflaked.configure do |config|
|
|
85
85
|
config.machine_id = 42
|
|
86
|
-
config.epoch = Time.utc(1989, 1, 3) #
|
|
86
|
+
config.epoch = Time.utc(1989, 1, 3) # Defaults to DEFAULT_EPOCH (2024-01-01) if not configured
|
|
87
87
|
end
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
Configuration is locked after `Snowflaked.configure` or the first generated/parsed ID. Set it during application boot, before request threads start.
|
|
91
|
+
|
|
90
92
|
### Machine ID
|
|
91
93
|
|
|
92
94
|
> [!TIP]
|
|
93
|
-
> For multi-process servers like Puma, it is recommended to **not** configure `machine_id` explicitly. The gem automatically calculates a
|
|
95
|
+
> For multi-process servers like Puma, it is recommended to **not** configure `machine_id` explicitly. The gem automatically calculates a process-local machine ID using `(hostname.hash ^ pid) % 1024`, so forked workers do not reuse the parent's cached machine ID.
|
|
94
96
|
|
|
95
97
|
If you must set `machine_id` explicitly, use environment variables that differ per worker process.
|
|
96
98
|
|
|
@@ -102,14 +104,12 @@ If `machine_id` is not explicitly configured, it resolves in this order:
|
|
|
102
104
|
2. `MACHINE_ID` environment variable
|
|
103
105
|
3. Auto-detected using the following formula: `(hostname.hash ^ pid) % 1024`
|
|
104
106
|
|
|
105
|
-
For Kubernetes deployments,
|
|
107
|
+
For Kubernetes deployments, set the machine ID to a numeric value between `0` and `1023`:
|
|
106
108
|
|
|
107
109
|
```yaml
|
|
108
110
|
env:
|
|
109
111
|
- name: SNOWFLAKED_MACHINE_ID
|
|
110
|
-
|
|
111
|
-
fieldRef:
|
|
112
|
-
fieldPath: metadata.name
|
|
112
|
+
value: "42"
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
Or use a StatefulSet ordinal for guaranteed unique values:
|
|
@@ -151,9 +151,8 @@ tl;dr: Snowflake IDs have a negligible performance impact compared to database-b
|
|
|
151
151
|
|
|
152
152
|
## Requirements
|
|
153
153
|
|
|
154
|
-
- Ruby >= 3.
|
|
155
|
-
- rustc >= 1.81.0
|
|
156
|
-
- cargo >= 1.81.0
|
|
154
|
+
- Ruby >= 3.3
|
|
155
|
+
- rustc / cargo >= 1.81.0 (development uses the toolchain pinned in `mise.toml`)
|
|
157
156
|
- Mise
|
|
158
157
|
|
|
159
158
|
## Development
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -20,10 +20,14 @@ module Snowflaked
|
|
|
20
20
|
def _snowflake_columns_from_comments
|
|
21
21
|
return @_snowflake_columns_from_comments if defined?(@_snowflake_columns_from_comments)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
return [] unless table_exists?
|
|
24
|
+
|
|
25
|
+
@_snowflake_columns_from_comments = columns.filter_map { |col| col.name.to_sym if col.comment == Snowflaked::SchemaDefinitions::COMMENT }
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def _snowflake_attributes_with_columns
|
|
29
|
+
return _snowflake_attributes unless table_exists?
|
|
30
|
+
|
|
27
31
|
@_snowflake_attributes_with_columns ||= (_snowflake_attributes | _snowflake_columns_from_comments)
|
|
28
32
|
end
|
|
29
33
|
end
|
data/lib/snowflaked/railtie.rb
CHANGED
|
@@ -42,10 +42,9 @@ module Snowflaked
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
initializer "snowflaked.model_extensions", after: "active_record.initialize_database" do
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
45
|
+
require "snowflaked/model_extensions"
|
|
46
|
+
|
|
47
|
+
ActiveSupport.on_load(:active_record) { include Snowflaked::ModelExtensions }
|
|
49
48
|
end
|
|
50
49
|
end
|
|
51
50
|
end
|
data/lib/snowflaked/version.rb
CHANGED
data/lib/snowflaked.rb
CHANGED
|
@@ -16,40 +16,81 @@ require_relative "snowflaked/railtie" if defined?(Rails::Railtie)
|
|
|
16
16
|
|
|
17
17
|
module Snowflaked
|
|
18
18
|
MAX_MACHINE_ID = 1023
|
|
19
|
+
DEFAULT_EPOCH = Time.utc(2024, 1, 1).freeze
|
|
19
20
|
|
|
20
21
|
class Error < StandardError; end
|
|
21
22
|
class ConfigurationError < Error; end
|
|
22
23
|
|
|
23
24
|
class Configuration
|
|
24
|
-
|
|
25
|
+
attr_reader :machine_id, :epoch
|
|
25
26
|
|
|
26
27
|
def initialize
|
|
27
28
|
@machine_id = nil
|
|
28
|
-
@epoch =
|
|
29
|
+
@epoch = DEFAULT_EPOCH
|
|
30
|
+
@sealed = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def machine_id=(value)
|
|
34
|
+
raise_if_sealed!(:machine_id)
|
|
35
|
+
|
|
36
|
+
@machine_id = value.nil? ? nil : checked_machine_id(value)
|
|
37
|
+
@machine_id_value = nil
|
|
38
|
+
@machine_id_value_pid = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def epoch=(value)
|
|
42
|
+
raise_if_sealed!(:epoch)
|
|
43
|
+
|
|
44
|
+
@epoch = value
|
|
45
|
+
@epoch_ms = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def seal!
|
|
49
|
+
@sealed = true
|
|
29
50
|
end
|
|
30
51
|
|
|
31
52
|
def machine_id_value
|
|
32
|
-
|
|
53
|
+
if @machine_id_value_pid != Process.pid
|
|
54
|
+
@machine_id_value = resolve_machine_id
|
|
55
|
+
@machine_id_value_pid = Process.pid
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@machine_id_value
|
|
33
59
|
end
|
|
34
60
|
|
|
35
61
|
def epoch_ms
|
|
36
62
|
return nil unless @epoch
|
|
37
63
|
|
|
38
|
-
(@epoch.
|
|
64
|
+
@epoch_ms ||= (@epoch.to_r * 1000).to_i
|
|
39
65
|
end
|
|
40
66
|
|
|
41
67
|
private
|
|
42
68
|
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
# Resolution order: explicit config, then env vars, then an auto fallback.
|
|
70
|
+
# Explicit and env values are range-checked; the fallback is always valid.
|
|
71
|
+
def resolve_machine_id
|
|
72
|
+
return @machine_id unless @machine_id.nil?
|
|
73
|
+
|
|
74
|
+
env = ENV["SNOWFLAKED_MACHINE_ID"] || ENV.fetch("MACHINE_ID", nil)
|
|
75
|
+
return checked_machine_id(env) if env
|
|
76
|
+
|
|
77
|
+
# Unique-enough per process without coordination: varies by host and by
|
|
78
|
+
# pid, so forked workers each get a different id. % keeps it in range.
|
|
79
|
+
(Socket.gethostname.hash ^ Process.pid) % (MAX_MACHINE_ID + 1)
|
|
45
80
|
end
|
|
46
81
|
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
# Coerce to Integer and ensure it fits in 0..MAX_MACHINE_ID, else raise.
|
|
83
|
+
def checked_machine_id(value)
|
|
84
|
+
id = Integer(value, exception: false)
|
|
85
|
+
return id if id&.between?(0, MAX_MACHINE_ID)
|
|
86
|
+
|
|
87
|
+
raise ConfigurationError, "machine_id must be an integer between 0 and #{MAX_MACHINE_ID}, got #{value.inspect}"
|
|
49
88
|
end
|
|
50
89
|
|
|
51
|
-
def
|
|
52
|
-
|
|
90
|
+
def raise_if_sealed!(attribute)
|
|
91
|
+
return unless @sealed
|
|
92
|
+
|
|
93
|
+
raise ConfigurationError, "#{attribute} cannot be changed after Snowflaked has been configured"
|
|
53
94
|
end
|
|
54
95
|
end
|
|
55
96
|
|
|
@@ -66,8 +107,8 @@ module Snowflaked
|
|
|
66
107
|
end
|
|
67
108
|
|
|
68
109
|
def id
|
|
69
|
-
|
|
70
|
-
Native.generate
|
|
110
|
+
ensure_initialized!
|
|
111
|
+
Native.generate
|
|
71
112
|
end
|
|
72
113
|
|
|
73
114
|
def parse(id)
|
|
@@ -78,10 +119,15 @@ module Snowflaked
|
|
|
78
119
|
def timestamp(id)
|
|
79
120
|
ensure_initialized!
|
|
80
121
|
seconds, milliseconds = Native.timestamp_ms(id).divmod(1000)
|
|
81
|
-
|
|
122
|
+
|
|
123
|
+
if defined?(Time.zone) && Time.zone
|
|
124
|
+
Time.zone.at(seconds, milliseconds * 1000, :usec)
|
|
125
|
+
else
|
|
126
|
+
Time.at(seconds, milliseconds * 1000, :usec).utc
|
|
127
|
+
end
|
|
82
128
|
end
|
|
83
129
|
|
|
84
|
-
def machine_id(id)
|
|
130
|
+
def machine_id(id) # rubocop:disable Rails/Delegate
|
|
85
131
|
Native.machine_id(id)
|
|
86
132
|
end
|
|
87
133
|
|
|
@@ -90,16 +136,20 @@ module Snowflaked
|
|
|
90
136
|
Native.timestamp_ms(id)
|
|
91
137
|
end
|
|
92
138
|
|
|
93
|
-
def sequence(id)
|
|
139
|
+
def sequence(id) # rubocop:disable Rails/Delegate
|
|
94
140
|
Native.sequence(id)
|
|
95
141
|
end
|
|
96
142
|
|
|
97
143
|
private
|
|
98
144
|
|
|
99
145
|
def ensure_initialized!
|
|
100
|
-
return if
|
|
146
|
+
return if @native_initialized_pid == Process.pid
|
|
147
|
+
|
|
148
|
+
config = configuration
|
|
149
|
+
config.seal!
|
|
101
150
|
|
|
102
|
-
Native.init_generator(
|
|
151
|
+
Native.init_generator(config.machine_id_value, config.epoch_ms)
|
|
152
|
+
@native_initialized_pid = Process.pid
|
|
103
153
|
end
|
|
104
154
|
end
|
|
105
155
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: snowflaked
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: x86_64-darwin
|
|
6
6
|
authors:
|
|
7
7
|
- Luiz Eduardo Kowalski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
12
|
-
dependencies:
|
|
11
|
+
date: 2026-07-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: railties
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '8.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '8.0'
|
|
13
27
|
description: A Ruby gem for generating Twitter Snowflake IDs using a high-performance
|
|
14
28
|
Rust backend. Thread-safe with configurable machine ID and custom epoch support.
|
|
15
29
|
email:
|
|
@@ -20,7 +34,6 @@ files:
|
|
|
20
34
|
- LICENSE.txt
|
|
21
35
|
- README.md
|
|
22
36
|
- lib/snowflaked.rb
|
|
23
|
-
- lib/snowflaked/3.2/snowflaked.bundle
|
|
24
37
|
- lib/snowflaked/3.3/snowflaked.bundle
|
|
25
38
|
- lib/snowflaked/3.4/snowflaked.bundle
|
|
26
39
|
- lib/snowflaked/4.0/snowflaked.bundle
|
|
@@ -47,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
47
60
|
requirements:
|
|
48
61
|
- - ">="
|
|
49
62
|
- !ruby/object:Gem::Version
|
|
50
|
-
version: '3.
|
|
63
|
+
version: '3.3'
|
|
51
64
|
- - "<"
|
|
52
65
|
- !ruby/object:Gem::Version
|
|
53
66
|
version: 4.1.dev
|
|
Binary file
|