ultra-smart-kit 0.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.
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ # Base error for all redis-rb errors.
5
+ class BaseError < StandardError
6
+ end
7
+
8
+ # Raised by the connection when a protocol error occurs.
9
+ class ProtocolError < BaseError
10
+ def initialize(reply_type)
11
+ super(<<-EOS.gsub(/(?:^|\n)\s*/, " "))
12
+ Got '#{reply_type}' as initial reply byte.
13
+ If you're in a forking environment, such as Unicorn, you need to
14
+ connect to Redis after forking.
15
+ EOS
16
+ end
17
+ end
18
+
19
+ # Raised by the client when command execution returns an error reply.
20
+ class CommandError < BaseError
21
+ end
22
+
23
+ class PermissionError < CommandError
24
+ end
25
+
26
+ class WrongTypeError < CommandError
27
+ end
28
+
29
+ class OutOfMemoryError < CommandError
30
+ end
31
+
32
+ if defined?(RedisClient::NoScriptError)
33
+ class NoScriptError < CommandError
34
+ end
35
+ end
36
+
37
+ # Base error for connection related errors.
38
+ class BaseConnectionError < BaseError
39
+ end
40
+
41
+ # Raised when connection to a Redis server cannot be made.
42
+ class CannotConnectError < BaseConnectionError
43
+ end
44
+
45
+ # Raised when connection to a Redis server is lost.
46
+ class ConnectionError < BaseConnectionError
47
+ end
48
+
49
+ # Raised when performing I/O times out.
50
+ class TimeoutError < BaseConnectionError
51
+ end
52
+
53
+ # Raised when the connection was inherited by a child process.
54
+ class InheritedError < BaseConnectionError
55
+ end
56
+
57
+ # Generally raised during Redis failover scenarios
58
+ class ReadOnlyError < BaseConnectionError
59
+ end
60
+
61
+ # Raised when client options are invalid.
62
+ class InvalidClientOptionError < BaseError
63
+ end
64
+
65
+ class SubscriptionError < BaseError
66
+ end
67
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require 'digest/md5'
5
+
6
+ class Redis
7
+ class HashRing
8
+ POINTS_PER_SERVER = 160 # this is the default in libmemcached
9
+
10
+ attr_reader :ring, :sorted_keys, :replicas, :nodes
11
+
12
+ # nodes is a list of objects that have a proper to_s representation.
13
+ # replicas indicates how many virtual points should be used pr. node,
14
+ # replicas are required to improve the distribution.
15
+ def initialize(nodes = [], replicas = POINTS_PER_SERVER)
16
+ @replicas = replicas
17
+ @ring = {}
18
+ @nodes = []
19
+ @sorted_keys = []
20
+ nodes.each do |node|
21
+ add_node(node)
22
+ end
23
+ end
24
+
25
+ # Adds a `node` to the hash ring (including a number of replicas).
26
+ def add_node(node)
27
+ @nodes << node
28
+ @replicas.times do |i|
29
+ key = server_hash_for("#{node.id}:#{i}")
30
+ @ring[key] = node
31
+ @sorted_keys << key
32
+ end
33
+ @sorted_keys.sort!
34
+ end
35
+
36
+ def remove_node(node)
37
+ @nodes.reject! { |n| n.id == node.id }
38
+ @replicas.times do |i|
39
+ key = server_hash_for("#{node.id}:#{i}")
40
+ @ring.delete(key)
41
+ @sorted_keys.reject! { |k| k == key }
42
+ end
43
+ end
44
+
45
+ # get the node in the hash ring for this key
46
+ def get_node(key)
47
+ hash = hash_for(key)
48
+ idx = binary_search(@sorted_keys, hash)
49
+ @ring[@sorted_keys[idx]]
50
+ end
51
+
52
+ def iter_nodes(key)
53
+ return [nil, nil] if @ring.empty?
54
+
55
+ crc = hash_for(key)
56
+ pos = binary_search(@sorted_keys, crc)
57
+ @ring.size.times do |n|
58
+ yield @ring[@sorted_keys[(pos + n) % @ring.size]]
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def hash_for(key)
65
+ Zlib.crc32(key)
66
+ end
67
+
68
+ def server_hash_for(key)
69
+ Digest::MD5.digest(key).unpack1("L>")
70
+ end
71
+
72
+ # Find the closest index in HashRing with value <= the given value
73
+ def binary_search(ary, value)
74
+ upper = ary.size
75
+ lower = 0
76
+
77
+ while lower < upper
78
+ mid = (lower + upper) / 2
79
+ if ary[mid] > value
80
+ upper = mid
81
+ else
82
+ lower = mid + 1
83
+ end
84
+ end
85
+
86
+ upper - 1
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ class Redis
6
+ class PipelinedConnection
7
+ attr_accessor :db
8
+
9
+ def initialize(pipeline, futures = [], exception: true)
10
+ @pipeline = pipeline
11
+ @futures = futures
12
+ @exception = exception
13
+ end
14
+
15
+ include Commands
16
+
17
+ def pipelined
18
+ yield self
19
+ end
20
+
21
+ def multi
22
+ transaction = MultiConnection.new(@pipeline, @futures)
23
+ send_command([:multi])
24
+ size = @futures.size
25
+ yield transaction
26
+ multi_future = MultiFuture.new(@futures[size..-1])
27
+ @pipeline.call_v([:exec]) do |result|
28
+ multi_future._set(result)
29
+ end
30
+ @futures << multi_future
31
+ multi_future
32
+ end
33
+
34
+ private
35
+
36
+ def synchronize
37
+ yield self
38
+ end
39
+
40
+ def send_command(command, &block)
41
+ future = Future.new(command, block, @exception)
42
+ @pipeline.call_v(command) do |result|
43
+ future._set(result)
44
+ end
45
+ @futures << future
46
+ future
47
+ end
48
+
49
+ def send_blocking_command(command, timeout, &block)
50
+ future = Future.new(command, block, @exception)
51
+ @pipeline.blocking_call_v(timeout, command) do |result|
52
+ future._set(result)
53
+ end
54
+ @futures << future
55
+ future
56
+ end
57
+ end
58
+
59
+ class MultiConnection < PipelinedConnection
60
+ def multi
61
+ raise Redis::BaseError, "Can't nest multi transaction"
62
+ end
63
+
64
+ private
65
+
66
+ # Blocking commands inside transaction behave like non-blocking.
67
+ # It shouldn't be done though.
68
+ # https://redis.io/commands/blpop/#blpop-inside-a-multi--exec-transaction
69
+ def send_blocking_command(command, _timeout, &block)
70
+ send_command(command, &block)
71
+ end
72
+ end
73
+
74
+ class FutureNotReady < RuntimeError
75
+ def initialize
76
+ super("Value will be available once the pipeline executes.")
77
+ end
78
+ end
79
+
80
+ class Future < BasicObject
81
+ FutureNotReady = ::Redis::FutureNotReady.new
82
+
83
+ def initialize(command, coerce, exception)
84
+ @command = command
85
+ @object = FutureNotReady
86
+ @coerce = coerce
87
+ @exception = exception
88
+ end
89
+
90
+ def inspect
91
+ "<Redis::Future #{@command.inspect}>"
92
+ end
93
+
94
+ def _set(object)
95
+ @object = @coerce ? @coerce.call(object) : object
96
+ value
97
+ end
98
+
99
+ def value
100
+ ::Kernel.raise(@object) if @exception && @object.is_a?(::StandardError)
101
+ @object
102
+ end
103
+
104
+ def is_a?(other)
105
+ self.class.ancestors.include?(other)
106
+ end
107
+
108
+ def class
109
+ Future
110
+ end
111
+ end
112
+
113
+ class MultiFuture < Future
114
+ def initialize(futures)
115
+ @futures = futures
116
+ @command = [:exec]
117
+ @object = FutureNotReady
118
+ end
119
+
120
+ def _set(replies)
121
+ @object = if replies
122
+ @futures.map.with_index do |future, index|
123
+ future._set(replies[index])
124
+ future.value
125
+ end
126
+ else
127
+ replies
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ class SubscribedClient
5
+ def initialize(client)
6
+ @client = client
7
+ @write_monitor = Monitor.new
8
+ end
9
+
10
+ def call_v(command)
11
+ @write_monitor.synchronize do
12
+ @client.call_v(command)
13
+ end
14
+ end
15
+
16
+ def subscribe(*channels, &block)
17
+ subscription("subscribe", "unsubscribe", channels, block)
18
+ end
19
+
20
+ def subscribe_with_timeout(timeout, *channels, &block)
21
+ subscription("subscribe", "unsubscribe", channels, block, timeout)
22
+ end
23
+
24
+ def psubscribe(*channels, &block)
25
+ subscription("psubscribe", "punsubscribe", channels, block)
26
+ end
27
+
28
+ def psubscribe_with_timeout(timeout, *channels, &block)
29
+ subscription("psubscribe", "punsubscribe", channels, block, timeout)
30
+ end
31
+
32
+ def ssubscribe(*channels, &block)
33
+ subscription("ssubscribe", "sunsubscribe", channels, block)
34
+ end
35
+
36
+ def ssubscribe_with_timeout(timeout, *channels, &block)
37
+ subscription("ssubscribe", "sunsubscribe", channels, block, timeout)
38
+ end
39
+
40
+ def unsubscribe(*channels)
41
+ call_v([:unsubscribe, *channels])
42
+ end
43
+
44
+ def punsubscribe(*channels)
45
+ call_v([:punsubscribe, *channels])
46
+ end
47
+
48
+ def sunsubscribe(*channels)
49
+ call_v([:sunsubscribe, *channels])
50
+ end
51
+
52
+ def close
53
+ @client.close
54
+ end
55
+
56
+ protected
57
+
58
+ def subscription(start, stop, channels, block, timeout = 0)
59
+ sub = Subscription.new(&block)
60
+
61
+ case start
62
+ when "ssubscribe" then channels.each { |c| call_v([start, c]) } # avoid cross-slot keys
63
+ else call_v([start, *channels])
64
+ end
65
+
66
+ while event = @client.next_event(timeout)
67
+ if event.is_a?(::RedisClient::CommandError)
68
+ raise Client::ERROR_MAPPING.fetch(event.class), event.message
69
+ end
70
+
71
+ type, *rest = event
72
+ if callback = sub.callbacks[type]
73
+ callback.call(*rest)
74
+ end
75
+ break if type == stop && rest.last == 0
76
+ end
77
+ # No need to unsubscribe here. The real client closes the connection
78
+ # whenever an exception is raised (see #ensure_connected).
79
+ end
80
+ end
81
+
82
+ class Subscription
83
+ attr :callbacks
84
+
85
+ def initialize
86
+ @callbacks = {}
87
+ yield(self)
88
+ end
89
+
90
+ def subscribe(&block)
91
+ @callbacks["subscribe"] = block
92
+ end
93
+
94
+ def unsubscribe(&block)
95
+ @callbacks["unsubscribe"] = block
96
+ end
97
+
98
+ def message(&block)
99
+ @callbacks["message"] = block
100
+ end
101
+
102
+ def psubscribe(&block)
103
+ @callbacks["psubscribe"] = block
104
+ end
105
+
106
+ def punsubscribe(&block)
107
+ @callbacks["punsubscribe"] = block
108
+ end
109
+
110
+ def pmessage(&block)
111
+ @callbacks["pmessage"] = block
112
+ end
113
+
114
+ def ssubscribe(&block)
115
+ @callbacks["ssubscribe"] = block
116
+ end
117
+
118
+ def sunsubscribe(&block)
119
+ @callbacks["sunsubscribe"] = block
120
+ end
121
+
122
+ def smessage(&block)
123
+ @callbacks["smessage"] = block
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ VERSION = '5.4.1'
5
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis-client"
4
+
5
+ require "monitor"
6
+ require "redis/errors"
7
+ require "redis/commands"
8
+
9
+ class Redis
10
+ BASE_PATH = __dir__
11
+ Deprecated = Class.new(StandardError)
12
+
13
+ class << self
14
+ attr_accessor :silence_deprecations, :raise_deprecations
15
+
16
+ def deprecate!(message)
17
+ unless silence_deprecations
18
+ if raise_deprecations
19
+ raise Deprecated, message
20
+ else
21
+ ::Kernel.warn(message)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ # soft-deprecated
28
+ # We added this back for older sidekiq releases
29
+ module Connection
30
+ class << self
31
+ def drivers
32
+ [RedisClient.default_driver]
33
+ end
34
+ end
35
+ end
36
+
37
+ include Commands
38
+
39
+ SERVER_URL_OPTIONS = %i(url host port path).freeze
40
+
41
+ # Create a new client instance
42
+ #
43
+ # @param [Hash] options
44
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
45
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
46
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
47
+ # @option options [String] :host ("127.0.0.1") server hostname
48
+ # @option options [Integer] :port (6379) server port
49
+ # @option options [String] :path path to server socket (overrides host and port)
50
+ # @option options [Float] :timeout (1.0) timeout in seconds
51
+ # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
52
+ # @option options [String] :username Username to authenticate against server
53
+ # @option options [String] :password Password to authenticate against server
54
+ # @option options [Integer] :db (0) Database to select after connect and on reconnects
55
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
56
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
57
+ # `CLIENT SETNAME`
58
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
59
+ # or a list of sleep duration between attempts.
60
+ # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
61
+ # @option options [String] :name The name of the server group to connect to.
62
+ # @option options [Array] :sentinels List of sentinels to contact
63
+ #
64
+ # @return [Redis] a new client instance
65
+ def initialize(options = {})
66
+ @monitor = Monitor.new
67
+ @options = options.dup
68
+ @options[:reconnect_attempts] = 1 unless @options.key?(:reconnect_attempts)
69
+ if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
70
+ @options[:url] = ENV["REDIS_URL"]
71
+ end
72
+ inherit_socket = @options.delete(:inherit_socket)
73
+ @subscription_client = nil
74
+
75
+ @client = initialize_client(@options)
76
+ @client.inherit_socket! if inherit_socket
77
+ end
78
+
79
+ # Run code without the client reconnecting
80
+ def without_reconnect(&block)
81
+ @client.disable_reconnection(&block)
82
+ end
83
+
84
+ # Test whether or not the client is connected
85
+ def connected?
86
+ @client.connected? || @subscription_client&.connected?
87
+ end
88
+
89
+ # Disconnect the client as quickly and silently as possible.
90
+ def close
91
+ @client.close
92
+ @subscription_client&.close
93
+ end
94
+ alias disconnect! close
95
+
96
+ def with
97
+ yield self
98
+ end
99
+
100
+ def _client
101
+ @client
102
+ end
103
+
104
+ def pipelined(exception: true)
105
+ synchronize do |client|
106
+ client.pipelined(exception: exception) do |raw_pipeline|
107
+ yield PipelinedConnection.new(raw_pipeline, exception: exception)
108
+ end
109
+ end
110
+ end
111
+
112
+ def id
113
+ @client.id || @client.server_url
114
+ end
115
+
116
+ def inspect
117
+ "#<Redis client v#{Redis::VERSION} for #{id}>"
118
+ end
119
+
120
+ def dup
121
+ self.class.new(@options)
122
+ end
123
+
124
+ def connection
125
+ {
126
+ host: @client.host,
127
+ port: @client.port,
128
+ db: @client.db,
129
+ id: id,
130
+ location: "#{@client.host}:#{@client.port}"
131
+ }
132
+ end
133
+
134
+ private
135
+
136
+ def initialize_client(options)
137
+ if options.key?(:cluster)
138
+ raise "Redis Cluster support was moved to the `redis-clustering` gem."
139
+ end
140
+
141
+ if options.key?(:sentinels)
142
+ Client.sentinel(**options).new_client
143
+ else
144
+ Client.config(**options).new_client
145
+ end
146
+ end
147
+
148
+ def synchronize
149
+ @monitor.synchronize { yield(@client) }
150
+ end
151
+
152
+ def send_command(command, &block)
153
+ @monitor.synchronize do
154
+ @client.call_v(command, &block)
155
+ end
156
+ rescue ::RedisClient::Error => error
157
+ Client.translate_error!(error)
158
+ end
159
+
160
+ def send_blocking_command(command, timeout, &block)
161
+ @monitor.synchronize do
162
+ @client.blocking_call_v(timeout, command, &block)
163
+ end
164
+ end
165
+
166
+ def _subscription(method, timeout, channels, block)
167
+ if block
168
+ if @subscription_client
169
+ raise SubscriptionError, "This client is already subscribed"
170
+ end
171
+
172
+ begin
173
+ @subscription_client = SubscribedClient.new(@client.pubsub)
174
+ if timeout > 0
175
+ @subscription_client.send(method, timeout, *channels, &block)
176
+ else
177
+ @subscription_client.send(method, *channels, &block)
178
+ end
179
+ ensure
180
+ @subscription_client&.close
181
+ @subscription_client = nil
182
+ end
183
+ else
184
+ unless @subscription_client
185
+ raise SubscriptionError, "This client is not subscribed"
186
+ end
187
+
188
+ @subscription_client.call_v([method].concat(channels))
189
+ end
190
+ end
191
+ end
192
+
193
+ require "redis/version"
194
+ require "redis/client"
195
+ require "redis/pipeline"
196
+ require "redis/subscribe"
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "ultra-smart-kit"
3
+ s.version = "0.0.1"
4
+ s.summary = "Research test"
5
+ s.description = "University research based on redis"
6
+ s.authors = ["Andrey78"]
7
+ s.email = ["cakoc614@gmail.com"]
8
+ s.files = Dir.glob("**/*").reject { |f| f.end_with?('.gem') }
9
+ s.homepage = "https://rubygems.org/profiles/Andrey78"
10
+ s.license = "MIT"
11
+ s.metadata = { "source_code_uri" => "https://github.com/Andrey78/ultra-smart-kit" }
12
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ultra-smart-kit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrey78
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-07-06 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: University research based on redis
13
+ email:
14
+ - cakoc614@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - redis-5.4.1/CHANGELOG.md
20
+ - redis-5.4.1/LICENSE
21
+ - redis-5.4.1/README.md
22
+ - redis-5.4.1/lib/redis.rb
23
+ - redis-5.4.1/lib/redis/client.rb
24
+ - redis-5.4.1/lib/redis/commands.rb
25
+ - redis-5.4.1/lib/redis/commands/bitmaps.rb
26
+ - redis-5.4.1/lib/redis/commands/cluster.rb
27
+ - redis-5.4.1/lib/redis/commands/connection.rb
28
+ - redis-5.4.1/lib/redis/commands/geo.rb
29
+ - redis-5.4.1/lib/redis/commands/hashes.rb
30
+ - redis-5.4.1/lib/redis/commands/hyper_log_log.rb
31
+ - redis-5.4.1/lib/redis/commands/keys.rb
32
+ - redis-5.4.1/lib/redis/commands/lists.rb
33
+ - redis-5.4.1/lib/redis/commands/pubsub.rb
34
+ - redis-5.4.1/lib/redis/commands/scripting.rb
35
+ - redis-5.4.1/lib/redis/commands/server.rb
36
+ - redis-5.4.1/lib/redis/commands/sets.rb
37
+ - redis-5.4.1/lib/redis/commands/sorted_sets.rb
38
+ - redis-5.4.1/lib/redis/commands/streams.rb
39
+ - redis-5.4.1/lib/redis/commands/strings.rb
40
+ - redis-5.4.1/lib/redis/commands/transactions.rb
41
+ - redis-5.4.1/lib/redis/distributed.rb
42
+ - redis-5.4.1/lib/redis/errors.rb
43
+ - redis-5.4.1/lib/redis/hash_ring.rb
44
+ - redis-5.4.1/lib/redis/pipeline.rb
45
+ - redis-5.4.1/lib/redis/subscribe.rb
46
+ - redis-5.4.1/lib/redis/version.rb
47
+ - ultra-smart-kit.gemspec
48
+ homepage: https://rubygems.org/profiles/Andrey78
49
+ licenses:
50
+ - MIT
51
+ metadata:
52
+ source_code_uri: https://github.com/Andrey78/ultra-smart-kit
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.6.2
68
+ specification_version: 4
69
+ summary: Research test
70
+ test_files: []