redis_int52_autoincrement 1.0.1
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 +7 -0
- data/CHANGELOG +2 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +60 -0
- data/Rakefile +41 -0
- data/lib/redis_int52_autoincrement.rb +58 -0
- data/redis_int52_autoincrement.gemspec +20 -0
- data/test/test_redis_int52_autoincrement.rb +20 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: be347a0e6721c196136980f15f5fcb895cb1770955795a7e802dcdb8d80a8f3a
|
4
|
+
data.tar.gz: 4d7ed637481c0369ef22d169f71a5a8f5c9bb230c662fc6910fd979d410e56f1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 55b7e45781f9e8eafe092e9350e3a85c9c6198882fa94e7e222f7a8c6c2089d2a0ab891bc54c2d7daa09dd479fbb6edd6cf5f1d3553970ac9f567ee65ff4bb74
|
7
|
+
data.tar.gz: c0fb76237341a72b1ae47417c572499568a752c9122c0a56a8b4707082fcdbc0efa384cdc375a02ebae1fd1bf4f9d2ca8c149c40f8c2e698eb25aefafa10f3a5
|
data/CHANGELOG
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2023-2024 Andrew Chernuha
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
= Int64 UUID (UInt52) autoincremented via redis-server
|
2
|
+
|
3
|
+
Why UInt52? Becouse JavaScript have problems with usege of Int64 values, that less than UInt54 max value.
|
4
|
+
So I create this library for be sure that migration to the UUID64 will not brake front side with rounding JavaScript bug.
|
5
|
+
|
6
|
+
What bug? Please open in your browser console and put into it
|
7
|
+
|
8
|
+
11111111111111111+2
|
9
|
+
|
10
|
+
What you see? Yes! It is 11111111111111114.
|
11
|
+
|
12
|
+
But it is not all. Please insert in this console 11111111111111111 and press enter. What you see? Yes, it is 11111111111111112.
|
13
|
+
|
14
|
+
Why it happens: becouse JavaScript use Double for store and working with Integers. Yes, i know, it is really bad idea :(.
|
15
|
+
|
16
|
+
So UInt54 is the maximum int that JavaScript can process without this bug.
|
17
|
+
|
18
|
+
If you do not want search and fix all places in your project with this bug and you need UUID64 - use this lib.
|
19
|
+
|
20
|
+
= What this gem provide:
|
21
|
+
Generates universally unique identifiers (UUID52) with unsigned Int64 last 52 bits (so it will be UInt52)
|
22
|
+
for use in distributed applications.
|
23
|
+
Based on:
|
24
|
+
1) possibility autoincrement value in redis-server
|
25
|
+
2) unix timestamps with microseconds
|
26
|
+
3) expire key possibility in redis database.
|
27
|
+
4) this value UInt52 can be used by JavaScript without lags.
|
28
|
+
|
29
|
+
|
30
|
+
== Generating UInt52 Ids
|
31
|
+
|
32
|
+
Call #generate to generate a new UUID64. The method returns a Int64 unique value less than UINT52 max value.
|
33
|
+
|
34
|
+
For example in rails:
|
35
|
+
|
36
|
+
before_create do |record|
|
37
|
+
record.id ||= RedisInt52Autoincrement.generate(Rails.cache.redis || Redis.new(url: "redis://localhost:6379/0"))
|
38
|
+
end
|
39
|
+
|
40
|
+
== Why so hard?
|
41
|
+
|
42
|
+
We have an issue where the servers have different times on each server and during the time the server syncs the time we have a shift of a few seconds.
|
43
|
+
So this lib have +-50(UUID52_MAX_SECONDS_EXPIRE) seconds maximum for delta shift.
|
44
|
+
|
45
|
+
== Latest and Greatest
|
46
|
+
|
47
|
+
Source code and documentation hosted on Github: http://github.com/trumenov/redis_int52_autoincrement
|
48
|
+
|
49
|
+
To get UUID52 from source:
|
50
|
+
|
51
|
+
git clone git://github.com/trumenov/redis_int52_autoincrement.git
|
52
|
+
|
53
|
+
|
54
|
+
== License
|
55
|
+
|
56
|
+
This package is licensed under the MIT license and/or the Creative
|
57
|
+
Commons Attribution-ShareAlike.
|
58
|
+
|
59
|
+
:include: MIT-LICENSE
|
60
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
#require 'rake/rdoctask'
|
3
|
+
|
4
|
+
|
5
|
+
spec = Gem::Specification.load(File.expand_path("redis_int52_autoincrement.gemspec", File.dirname(__FILE__)))
|
6
|
+
|
7
|
+
desc "Default Task"
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
|
11
|
+
desc "Run all test cases"
|
12
|
+
Rake::TestTask.new do |test|
|
13
|
+
test.verbose = true
|
14
|
+
test.test_files = ['test/*.rb']
|
15
|
+
test.warning = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Create the documentation.
|
19
|
+
#Rake::RDocTask.new do |rdoc|
|
20
|
+
# rdoc.rdoc_files.include "README.rdoc", "lib/**/*.rb"
|
21
|
+
# rdoc.options = spec.rdoc_options
|
22
|
+
#end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
desc "Push new release to rubyforge and git tag"
|
27
|
+
task :push do
|
28
|
+
sh "git push"
|
29
|
+
puts "Tagging version #{spec.version} .."
|
30
|
+
sh "git tag v#{spec.version}"
|
31
|
+
sh "git push --tag"
|
32
|
+
puts "Building and pushing gem .."
|
33
|
+
sh "gem build #{spec.name}.gemspec"
|
34
|
+
sh "gem push #{spec.name}-#{spec.version}.gem"
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Install #{spec.name} locally"
|
38
|
+
task :install do
|
39
|
+
sh "gem build #{spec.name}.gemspec"
|
40
|
+
sh "gem install #{spec.name}-#{spec.version}.gem"
|
41
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class RedisInt52Autoincrement
|
2
|
+
|
3
|
+
# Why 30 seconds? Easy to debug and more than enouth for sync time delta shift.
|
4
|
+
# You can use 10 or 5 seconds - and all must working normal too.
|
5
|
+
# 30seconds was chosen by me without any explanation. Just took this value, i do not know why.
|
6
|
+
REDIS_KEY_TTL_SECONDS = 30
|
7
|
+
|
8
|
+
MAX_BUFFER_BITS_CNT = 52
|
9
|
+
TIMESTAMP_BITS_CNT = 34.freeze # 34bits for 150+ years more than enougth
|
10
|
+
TIMESTAMP_MICROSECONDS_BITS_CNT = 20.freeze # 0..999_999 - require 20bits
|
11
|
+
TIMESTAMP_MICROSECONDS_STORE_BITS_CNT = 9.freeze # from max value 999_999 we will take only first 9 high bits.
|
12
|
+
TIMESTAMP_MICROSECONDS_BITS_SHL = TIMESTAMP_MICROSECONDS_BITS_CNT - TIMESTAMP_MICROSECONDS_STORE_BITS_CNT
|
13
|
+
INCREMENTED_ID_BITS_CNT = 7.freeze # 7bits for 0..127 INCREMENTED_UINT8_ID
|
14
|
+
SERVER_ID_BITS_CNT = 2.freeze
|
15
|
+
# So in result: 34 + 9 + 7 + 2 = 52bits(MAX_BUFFER_BITS_CNT).
|
16
|
+
|
17
|
+
TIMESTAMP_SECONDS_SHIFT_LEFT_CNT = (TIMESTAMP_MICROSECONDS_STORE_BITS_CNT + INCREMENTED_ID_BITS_CNT + SERVER_ID_BITS_CNT).freeze # 18 bits
|
18
|
+
TIMESTAMP_MICROSECONDS_SHIFT_LEFT_CNT = (INCREMENTED_ID_BITS_CNT + SERVER_ID_BITS_CNT).freeze # 9 bits
|
19
|
+
INCREMENTED_ID_MAX_VAL = ((1 << INCREMENTED_ID_BITS_CNT) - 1).freeze # 1111111b=127
|
20
|
+
UINT52_MAX_VAL = 4503599627370496 # 2^52 = 4503599627370496
|
21
|
+
LIB_FIRST_VAL = 447450850315268 # First id was generated 02.02.2024 at ~18:00 (Kiev +2:00)
|
22
|
+
|
23
|
+
# Version number.
|
24
|
+
module Version
|
25
|
+
version = Gem::Specification.load(File.expand_path("../redis_int52_autoincrement.gemspec", File.dirname(__FILE__))).version.to_s.split(".").map { |i| i.to_i }
|
26
|
+
MAJOR = version[1]
|
27
|
+
MINOR = version[0]
|
28
|
+
PATCH = version[1]
|
29
|
+
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
30
|
+
end
|
31
|
+
|
32
|
+
VERSION = Version::STRING
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def generate(redis, namespace = 'default', options = {})
|
36
|
+
server_id = options[:server_id] || 0
|
37
|
+
time_redis = options[:time_redis] || redis
|
38
|
+
raise("Wrong server_id[#{server_id}]. Allow only 0..3 server_id") unless server_id.between?(0, 3)
|
39
|
+
time_arr = time_redis.time
|
40
|
+
raise("Wrong time type[#{time_arr.inspect}]") unless time_arr.count.eql?(2)
|
41
|
+
unix_seconds = time_arr.first.to_i
|
42
|
+
first_part = unix_seconds << TIMESTAMP_SECONDS_SHIFT_LEFT_CNT
|
43
|
+
|
44
|
+
microseconds = time_arr[1].to_i
|
45
|
+
raise("Wrong microseconds [#{microseconds}]") unless microseconds.between?(0, 999_999)
|
46
|
+
second_part = (microseconds >> TIMESTAMP_MICROSECONDS_BITS_SHL) << TIMESTAMP_MICROSECONDS_SHIFT_LEFT_CNT
|
47
|
+
|
48
|
+
key = "ai64:#{namespace}:#{unix_seconds}:#{microseconds}"
|
49
|
+
incremented_val = redis.incrby(key, 1)
|
50
|
+
redis.expire(key, REDIS_KEY_TTL_SECONDS)
|
51
|
+
raise("Wrong incremented_val [#{incremented_val}]") unless incremented_val.between?(0, INCREMENTED_ID_MAX_VAL)
|
52
|
+
third_part = incremented_val << SERVER_ID_BITS_CNT
|
53
|
+
result = ((first_part | second_part) | third_part) | server_id
|
54
|
+
raise("Wrong result[#{result}]") unless result.between?(LIB_FIRST_VAL, UINT52_MAX_VAL)
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'redis_int52_autoincrement'
|
3
|
+
s.version = '1.0.1'
|
4
|
+
s.summary = "Int64 (UInt52) autoincrement via redis-servers"
|
5
|
+
s.description = <<-EOF
|
6
|
+
Int64 (UInt52) autoincrement via redis-servers with timestamp in microseconds, autoincerent via redis key.
|
7
|
+
Support 1-5 redis servers in one system (1 - timestamp + 4 servers - autoincrementers).
|
8
|
+
EOF
|
9
|
+
|
10
|
+
s.authors << 'Trumenov' << 'Chaky'
|
11
|
+
s.email = 'chaky22222222@gmail.com'
|
12
|
+
s.homepage = 'http://github.com/trumenov/redis_int52_autoincrement'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.files = Dir['{bin,test,lib,docs}/**/*'] + ['README.rdoc', 'MIT-LICENSE', 'Rakefile', 'CHANGELOG', 'redis_int52_autoincrement.gemspec']
|
16
|
+
|
17
|
+
s.rdoc_options << '--main' << 'README.rdoc' << '--title' << 'Int64 UInt52 redis autoincrementer' << '--line-numbers'
|
18
|
+
'--webcvs' << 'http://github.com/trumenov/redis_int52_autoincrement'
|
19
|
+
s.extra_rdoc_files = ['README.rdoc', 'MIT-LICENSE']
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# Author:: Andrew Chernuha chaky22222222@gmail.com
|
3
|
+
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
4
|
+
# run test with command: rake
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'rubygems'
|
8
|
+
require 'redis_int52_autoincrement'
|
9
|
+
require "redis"
|
10
|
+
|
11
|
+
class TestRedisInt52Autoincrement < Test::Unit::TestCase
|
12
|
+
|
13
|
+
def test_check_generated_placed_in_target_region
|
14
|
+
redis = Redis.new(url: "redis://localhost:6379/0")
|
15
|
+
left_part = Time.now.to_i << (RedisInt52Autoincrement::MAX_BUFFER_BITS_CNT - RedisInt52Autoincrement::TIMESTAMP_BITS_CNT)
|
16
|
+
new_id = RedisInt52Autoincrement.generate(redis)
|
17
|
+
right_part = (Time.now.to_i + 1) << (RedisInt52Autoincrement::MAX_BUFFER_BITS_CNT - RedisInt52Autoincrement::TIMESTAMP_BITS_CNT)
|
18
|
+
assert new_id.between?(left_part, right_part)
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis_int52_autoincrement
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trumenov
|
8
|
+
- Chaky
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2024-02-02 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: |
|
15
|
+
Int64 (UInt52) autoincrement via redis-servers with timestamp in microseconds, autoincerent via redis key.
|
16
|
+
Support 1-5 redis servers in one system (1 - timestamp + 4 servers - autoincrementers).
|
17
|
+
email: chaky22222222@gmail.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files:
|
21
|
+
- README.rdoc
|
22
|
+
- MIT-LICENSE
|
23
|
+
files:
|
24
|
+
- CHANGELOG
|
25
|
+
- MIT-LICENSE
|
26
|
+
- README.rdoc
|
27
|
+
- Rakefile
|
28
|
+
- lib/redis_int52_autoincrement.rb
|
29
|
+
- redis_int52_autoincrement.gemspec
|
30
|
+
- test/test_redis_int52_autoincrement.rb
|
31
|
+
homepage: http://github.com/trumenov/redis_int52_autoincrement
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- "--main"
|
38
|
+
- README.rdoc
|
39
|
+
- "--title"
|
40
|
+
- Int64 UInt52 redis autoincrementer
|
41
|
+
- "--line-numbers"
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.3.26
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Int64 (UInt52) autoincrement via redis-servers
|
59
|
+
test_files: []
|