Module: KSUID
- Defined in:
- lib/ksuid.rb,
lib/ksuid/type.rb,
lib/ksuid/utils.rb,
lib/ksuid/base62.rb,
lib/ksuid/version.rb,
lib/ksuid/prefixed.rb,
lib/ksuid/configuration.rb
Overview
The K-Sortable Unique IDentifier (KSUID)
Distributed systems require unique identifiers to track events throughout their subsystems. Many algorithms for generating unique identifiers, like the Snowflake ID system, require coordination with a central authority. This is an unacceptable constraint in the face of systems that run on client devices, yet we still need to be able to generate event identifiers and roughly sort them for processing.
The KSUID optimizes this problem into a roughly sortable identifier with a high possibility space to reduce the chance of collision. KSUID uses a 32-bit timestamp with second-level precision combined with 128 bytes of random data for the “payload”. The timestamp is based on the Unix epoch, but with its base shifted forward from 1970-01-01 00:00:00 UTC to 2014-05-13 16:532:20 UTC. This is to extend the useful life of the ID format to over 100 years.
Because KSUID timestamps use seconds as their unit of precision, they are unsuitable to tasks that require extreme levels of precision. If you need microsecond-level precision, a format like ULID may be more suitable for your use case.
KSUIDs are “roughly sorted”. Practically, this means that for any given event stream, there may be some events that are ordered in a slightly different way than they actually happened. There are two reasons for this. Firstly, the format is precise to the second. This means that two events that are generated in the same second will be sorted together, but the KSUID with the smaller payload value will be sorted first. Secondly, the format is generated on the client device using its clock, so KSUID is susceptible to clock shift as well. The result of sorting the identifiers is that they will be sorted into groups of identifiers that happened in the same second according to their generating device.
Defined Under Namespace
Modules: Base62, Utils Classes: Configuration, Prefixed, Type
Constant Summary collapse
- EPOCH_TIME =
The shift in the Unix epoch time between the standard and the KSUID base
1_400_000_000
- BYTES =
The number of bytes that are used to represent each part of a KSUID
{ base62: 27, payload: 16, timestamp: 4, total: 20 }.freeze
- STRING_LENGTH =
The number of characters in a base 62-encoded KSUID
27
- MAX_STRING_ENCODED =
The maximum KSUID as a base 62-encoded string.
'aWgEPTl1tmebfsQzFP4bxwgy80V'
- VERSION =
The version of the KSUID gem
'1.0.0'
Class Method Summary collapse
-
.call(ksuid) ⇒ KSUID::Type
Converts a KSUID-compatible value into an actual KSUID.
-
.config ⇒ KSUID::Configuration
private
The configuration for creating new KSUIDs.
-
.configure {|config| ... } ⇒ KSUID::Configuration
Configures the KSUID gem by passing a block.
-
.from_base62(string) ⇒ KSUID::Type
Converts a base 62-encoded string into a KSUID.
-
.from_bytes(bytes) ⇒ KSUID::Type
Converts a byte string or byte array into a KSUID.
-
.max ⇒ KSUID::Type
Generates the maximum KSUID as a KSUID type.
-
.new(payload: nil, time: Time.now) ⇒ KSUID::Type
Instantiates a new KSUID.
-
.prefixed(prefix, payload: nil, time: Time.now) ⇒ KSUID::Prefixed
Instantiates a new Prefixed.
-
.string(payload: nil, time: Time.now) ⇒ String
Generates a KSUID string.
Class Method Details
.call(ksuid) ⇒ KSUID::Type
Converts a KSUID-compatible value into an actual KSUID
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/ksuid.rb', line 95 def self.call(ksuid) return unless ksuid case ksuid when KSUID::Prefixed then ksuid.to_ksuid when KSUID::Type then ksuid when Array then KSUID.from_bytes(ksuid) when String then cast_string(ksuid) else raise ArgumentError, "Cannot convert #{ksuid.inspect} to KSUID" end end |
.config ⇒ KSUID::Configuration
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
The configuration for creating new KSUIDs
113 114 115 |
# File 'lib/ksuid.rb', line 113 def self.config @config ||= KSUID::Configuration.new end |
.configure {|config| ... } ⇒ KSUID::Configuration
Configures the KSUID gem by passing a block
132 133 134 135 |
# File 'lib/ksuid.rb', line 132 def self.configure yield config if block_given? config end |
.from_base62(string) ⇒ KSUID::Type
Converts a base 62-encoded string into a KSUID
146 147 148 149 150 151 152 |
# File 'lib/ksuid.rb', line 146 def self.from_base62(string) string = string.rjust(STRING_LENGTH, Base62::CHARSET[0]) if string.length < STRING_LENGTH int = Base62.decode(string) bytes = Utils.int_to_bytes(int, 160) from_bytes(bytes) end |
.from_bytes(bytes) ⇒ KSUID::Type
Converts a byte string or byte array into a KSUID
163 164 165 166 167 168 169 170 |
# File 'lib/ksuid.rb', line 163 def self.from_bytes(bytes) bytes = bytes.bytes if bytes.is_a?(String) = Utils.int_from_bytes(bytes.first(BYTES[:timestamp])) payload = Utils.byte_string_from_array(bytes.last(BYTES[:payload])) KSUID::Type.new(payload: payload, time: Time.at( + EPOCH_TIME)) end |
.max ⇒ KSUID::Type
Generates the maximum KSUID as a KSUID type
180 181 182 |
# File 'lib/ksuid.rb', line 180 def self.max from_bytes([255] * BYTES[:total]) end |
.new(payload: nil, time: Time.now) ⇒ KSUID::Type
Instantiates a new KSUID
197 198 199 |
# File 'lib/ksuid.rb', line 197 def self.new(payload: nil, time: Time.now) Type.new(payload: payload, time: time) end |
.prefixed(prefix, payload: nil, time: Time.now) ⇒ KSUID::Prefixed
Instantiates a new Prefixed
216 217 218 |
# File 'lib/ksuid.rb', line 216 def self.prefixed(prefix, payload: nil, time: Time.now) Prefixed.new(prefix, payload: payload, time: time) end |