redis 4.8.1 → 5.4.0
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/CHANGELOG.md +82 -0
- data/README.md +125 -162
- data/lib/redis/client.rb +82 -616
- data/lib/redis/commands/bitmaps.rb +14 -4
- data/lib/redis/commands/cluster.rb +1 -18
- data/lib/redis/commands/connection.rb +5 -10
- data/lib/redis/commands/geo.rb +3 -3
- data/lib/redis/commands/hashes.rb +13 -6
- data/lib/redis/commands/hyper_log_log.rb +1 -1
- data/lib/redis/commands/keys.rb +27 -23
- data/lib/redis/commands/lists.rb +74 -25
- data/lib/redis/commands/pubsub.rb +34 -25
- data/lib/redis/commands/server.rb +15 -15
- data/lib/redis/commands/sets.rb +35 -40
- data/lib/redis/commands/sorted_sets.rb +128 -18
- data/lib/redis/commands/streams.rb +48 -21
- data/lib/redis/commands/strings.rb +18 -17
- data/lib/redis/commands/transactions.rb +7 -31
- data/lib/redis/commands.rb +11 -12
- data/lib/redis/distributed.rb +136 -72
- data/lib/redis/errors.rb +15 -50
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +47 -222
- data/lib/redis/subscribe.rb +50 -14
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +77 -184
- metadata +10 -57
- data/lib/redis/cluster/command.rb +0 -79
- data/lib/redis/cluster/command_loader.rb +0 -33
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -120
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -34
- data/lib/redis/cluster/option.rb +0 -100
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -46
- data/lib/redis/cluster.rb +0 -315
- data/lib/redis/connection/command_helper.rb +0 -41
- data/lib/redis/connection/hiredis.rb +0 -68
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -437
- data/lib/redis/connection/synchrony.rb +0 -148
- data/lib/redis/connection.rb +0 -11
    
        data/lib/redis/cluster.rb
    DELETED
    
    | @@ -1,315 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require_relative 'errors'
         | 
| 4 | 
            -
            require_relative 'client'
         | 
| 5 | 
            -
            require_relative 'cluster/command'
         | 
| 6 | 
            -
            require_relative 'cluster/command_loader'
         | 
| 7 | 
            -
            require_relative 'cluster/key_slot_converter'
         | 
| 8 | 
            -
            require_relative 'cluster/node'
         | 
| 9 | 
            -
            require_relative 'cluster/node_key'
         | 
| 10 | 
            -
            require_relative 'cluster/node_loader'
         | 
| 11 | 
            -
            require_relative 'cluster/option'
         | 
| 12 | 
            -
            require_relative 'cluster/slot'
         | 
| 13 | 
            -
            require_relative 'cluster/slot_loader'
         | 
| 14 | 
            -
             | 
| 15 | 
            -
            class Redis
         | 
| 16 | 
            -
              # Redis Cluster client
         | 
| 17 | 
            -
              #
         | 
| 18 | 
            -
              # @see https://github.com/antirez/redis-rb-cluster POC implementation
         | 
| 19 | 
            -
              # @see https://redis.io/topics/cluster-spec Redis Cluster specification
         | 
| 20 | 
            -
              # @see https://redis.io/topics/cluster-tutorial Redis Cluster tutorial
         | 
| 21 | 
            -
              #
         | 
| 22 | 
            -
              # Copyright (C) 2013 Salvatore Sanfilippo <antirez@gmail.com>
         | 
| 23 | 
            -
              class Cluster
         | 
| 24 | 
            -
                def initialize(options = {})
         | 
| 25 | 
            -
                  @option = Option.new(options)
         | 
| 26 | 
            -
                  @node, @slot = fetch_cluster_info!(@option)
         | 
| 27 | 
            -
                  @command = fetch_command_details(@node)
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                def id
         | 
| 31 | 
            -
                  @node.map(&:id).sort.join(' ')
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                # db feature is disabled in cluster mode
         | 
| 35 | 
            -
                def db
         | 
| 36 | 
            -
                  0
         | 
| 37 | 
            -
                end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                # db feature is disabled in cluster mode
         | 
| 40 | 
            -
                def db=(_db); end
         | 
| 41 | 
            -
             | 
| 42 | 
            -
                def timeout
         | 
| 43 | 
            -
                  @node.first.timeout
         | 
| 44 | 
            -
                end
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def connected?
         | 
| 47 | 
            -
                  @node.any?(&:connected?)
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                def disconnect
         | 
| 51 | 
            -
                  @node.each(&:disconnect)
         | 
| 52 | 
            -
                  true
         | 
| 53 | 
            -
                end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                def connection_info
         | 
| 56 | 
            -
                  @node.sort_by(&:id).map do |client|
         | 
| 57 | 
            -
                    {
         | 
| 58 | 
            -
                      host: client.host,
         | 
| 59 | 
            -
                      port: client.port,
         | 
| 60 | 
            -
                      db: client.db,
         | 
| 61 | 
            -
                      id: client.id,
         | 
| 62 | 
            -
                      location: client.location
         | 
| 63 | 
            -
                    }
         | 
| 64 | 
            -
                  end
         | 
| 65 | 
            -
                end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                def with_reconnect(val = true, &block)
         | 
| 68 | 
            -
                  try_send(@node.sample, :with_reconnect, val, &block)
         | 
| 69 | 
            -
                end
         | 
| 70 | 
            -
             | 
| 71 | 
            -
                def call(command, &block)
         | 
| 72 | 
            -
                  send_command(command, &block)
         | 
| 73 | 
            -
                end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                def call_loop(command, timeout = 0, &block)
         | 
| 76 | 
            -
                  node = assign_node(command)
         | 
| 77 | 
            -
                  try_send(node, :call_loop, command, timeout, &block)
         | 
| 78 | 
            -
                end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                def call_pipeline(pipeline)
         | 
| 81 | 
            -
                  node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
         | 
| 82 | 
            -
                  if node_keys.size > 1
         | 
| 83 | 
            -
                    raise(CrossSlotPipeliningError,
         | 
| 84 | 
            -
                          pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
         | 
| 85 | 
            -
                  end
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                  try_send(find_node(node_keys.first), :call_pipeline, pipeline)
         | 
| 88 | 
            -
                end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                def call_with_timeout(command, timeout, &block)
         | 
| 91 | 
            -
                  node = assign_node(command)
         | 
| 92 | 
            -
                  try_send(node, :call_with_timeout, command, timeout, &block)
         | 
| 93 | 
            -
                end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                def call_without_timeout(command, &block)
         | 
| 96 | 
            -
                  call_with_timeout(command, 0, &block)
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                def process(commands, &block)
         | 
| 100 | 
            -
                  if commands.size == 1 &&
         | 
| 101 | 
            -
                     %w[unsubscribe punsubscribe].include?(commands.first.first.to_s.downcase) &&
         | 
| 102 | 
            -
                     commands.first.size == 1
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                    # Node is indeterminate. We do just a best-effort try here.
         | 
| 105 | 
            -
                    @node.process_all(commands, &block)
         | 
| 106 | 
            -
                  else
         | 
| 107 | 
            -
                    node = assign_node(commands.first)
         | 
| 108 | 
            -
                    try_send(node, :process, commands, &block)
         | 
| 109 | 
            -
                  end
         | 
| 110 | 
            -
                end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                private
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                def fetch_cluster_info!(option)
         | 
| 115 | 
            -
                  node = Node.new(option.per_node_key)
         | 
| 116 | 
            -
                  available_slots = SlotLoader.load(node)
         | 
| 117 | 
            -
                  node_flags = NodeLoader.load_flags(node)
         | 
| 118 | 
            -
                  option.update_node(available_slots.keys.map { |k| NodeKey.optionize(k) })
         | 
| 119 | 
            -
                  [Node.new(option.per_node_key, node_flags, option.use_replica?),
         | 
| 120 | 
            -
                   Slot.new(available_slots, node_flags, option.use_replica?)]
         | 
| 121 | 
            -
                ensure
         | 
| 122 | 
            -
                  node&.each(&:disconnect)
         | 
| 123 | 
            -
                end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                def fetch_command_details(nodes)
         | 
| 126 | 
            -
                  details = CommandLoader.load(nodes)
         | 
| 127 | 
            -
                  Command.new(details)
         | 
| 128 | 
            -
                end
         | 
| 129 | 
            -
             | 
| 130 | 
            -
                def send_command(command, &block)
         | 
| 131 | 
            -
                  cmd = command.first.to_s.downcase
         | 
| 132 | 
            -
                  case cmd
         | 
| 133 | 
            -
                  when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
         | 
| 134 | 
            -
                    @node.call_all(command, &block).first
         | 
| 135 | 
            -
                  when 'flushall', 'flushdb'
         | 
| 136 | 
            -
                    @node.call_master(command, &block).first
         | 
| 137 | 
            -
                  when 'wait'     then @node.call_master(command, &block).reduce(:+)
         | 
| 138 | 
            -
                  when 'keys'     then @node.call_slave(command, &block).flatten.sort
         | 
| 139 | 
            -
                  when 'dbsize'   then @node.call_slave(command, &block).reduce(:+)
         | 
| 140 | 
            -
                  when 'scan'     then _scan(command, &block)
         | 
| 141 | 
            -
                  when 'lastsave' then @node.call_all(command, &block).sort
         | 
| 142 | 
            -
                  when 'role'     then @node.call_all(command, &block)
         | 
| 143 | 
            -
                  when 'config'   then send_config_command(command, &block)
         | 
| 144 | 
            -
                  when 'client'   then send_client_command(command, &block)
         | 
| 145 | 
            -
                  when 'cluster'  then send_cluster_command(command, &block)
         | 
| 146 | 
            -
                  when 'readonly', 'readwrite', 'shutdown'
         | 
| 147 | 
            -
                    raise OrchestrationCommandNotSupported, cmd
         | 
| 148 | 
            -
                  when 'memory'   then send_memory_command(command, &block)
         | 
| 149 | 
            -
                  when 'script'   then send_script_command(command, &block)
         | 
| 150 | 
            -
                  when 'pubsub'   then send_pubsub_command(command, &block)
         | 
| 151 | 
            -
                  when 'discard', 'exec', 'multi', 'unwatch'
         | 
| 152 | 
            -
                    raise AmbiguousNodeError, cmd
         | 
| 153 | 
            -
                  else
         | 
| 154 | 
            -
                    node = assign_node(command)
         | 
| 155 | 
            -
                    try_send(node, :call, command, &block)
         | 
| 156 | 
            -
                  end
         | 
| 157 | 
            -
                end
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                def send_config_command(command, &block)
         | 
| 160 | 
            -
                  case command[1].to_s.downcase
         | 
| 161 | 
            -
                  when 'resetstat', 'rewrite', 'set'
         | 
| 162 | 
            -
                    @node.call_all(command, &block).first
         | 
| 163 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 164 | 
            -
                  end
         | 
| 165 | 
            -
                end
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                def send_memory_command(command, &block)
         | 
| 168 | 
            -
                  case command[1].to_s.downcase
         | 
| 169 | 
            -
                  when 'stats' then @node.call_all(command, &block)
         | 
| 170 | 
            -
                  when 'purge' then @node.call_all(command, &block).first
         | 
| 171 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 172 | 
            -
                  end
         | 
| 173 | 
            -
                end
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                def send_client_command(command, &block)
         | 
| 176 | 
            -
                  case command[1].to_s.downcase
         | 
| 177 | 
            -
                  when 'list' then @node.call_all(command, &block).flatten
         | 
| 178 | 
            -
                  when 'pause', 'reply', 'setname'
         | 
| 179 | 
            -
                    @node.call_all(command, &block).first
         | 
| 180 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 181 | 
            -
                  end
         | 
| 182 | 
            -
                end
         | 
| 183 | 
            -
             | 
| 184 | 
            -
                def send_cluster_command(command, &block)
         | 
| 185 | 
            -
                  subcommand = command[1].to_s.downcase
         | 
| 186 | 
            -
                  case subcommand
         | 
| 187 | 
            -
                  when 'addslots', 'delslots', 'failover', 'forget', 'meet', 'replicate',
         | 
| 188 | 
            -
                       'reset', 'set-config-epoch', 'setslot'
         | 
| 189 | 
            -
                    raise OrchestrationCommandNotSupported, 'cluster', subcommand
         | 
| 190 | 
            -
                  when 'saveconfig' then @node.call_all(command, &block).first
         | 
| 191 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 192 | 
            -
                  end
         | 
| 193 | 
            -
                end
         | 
| 194 | 
            -
             | 
| 195 | 
            -
                def send_script_command(command, &block)
         | 
| 196 | 
            -
                  case command[1].to_s.downcase
         | 
| 197 | 
            -
                  when 'debug', 'kill'
         | 
| 198 | 
            -
                    @node.call_all(command, &block).first
         | 
| 199 | 
            -
                  when 'flush', 'load'
         | 
| 200 | 
            -
                    @node.call_master(command, &block).first
         | 
| 201 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 202 | 
            -
                  end
         | 
| 203 | 
            -
                end
         | 
| 204 | 
            -
             | 
| 205 | 
            -
                def send_pubsub_command(command, &block)
         | 
| 206 | 
            -
                  case command[1].to_s.downcase
         | 
| 207 | 
            -
                  when 'channels' then @node.call_all(command, &block).flatten.uniq.sort
         | 
| 208 | 
            -
                  when 'numsub'
         | 
| 209 | 
            -
                    @node.call_all(command, &block).reject(&:empty?).map { |e| Hash[*e] }
         | 
| 210 | 
            -
                         .reduce({}) { |a, e| a.merge(e) { |_, v1, v2| v1 + v2 } }
         | 
| 211 | 
            -
                  when 'numpat' then @node.call_all(command, &block).reduce(:+)
         | 
| 212 | 
            -
                  else assign_node(command).call(command, &block)
         | 
| 213 | 
            -
                  end
         | 
| 214 | 
            -
                end
         | 
| 215 | 
            -
             | 
| 216 | 
            -
                # @see https://redis.io/topics/cluster-spec#redirection-and-resharding
         | 
| 217 | 
            -
                #   Redirection and resharding
         | 
| 218 | 
            -
                def try_send(node, method_name, *args, retry_count: 3, &block)
         | 
| 219 | 
            -
                  node.public_send(method_name, *args, &block)
         | 
| 220 | 
            -
                rescue CommandError => err
         | 
| 221 | 
            -
                  if err.message.start_with?('MOVED')
         | 
| 222 | 
            -
                    raise if retry_count <= 0
         | 
| 223 | 
            -
             | 
| 224 | 
            -
                    node = assign_redirection_node(err.message)
         | 
| 225 | 
            -
                    retry_count -= 1
         | 
| 226 | 
            -
                    retry
         | 
| 227 | 
            -
                  elsif err.message.start_with?('ASK')
         | 
| 228 | 
            -
                    raise if retry_count <= 0
         | 
| 229 | 
            -
             | 
| 230 | 
            -
                    node = assign_asking_node(err.message)
         | 
| 231 | 
            -
                    node.call(%i[asking])
         | 
| 232 | 
            -
                    retry_count -= 1
         | 
| 233 | 
            -
                    retry
         | 
| 234 | 
            -
                  else
         | 
| 235 | 
            -
                    raise
         | 
| 236 | 
            -
                  end
         | 
| 237 | 
            -
                rescue CannotConnectError
         | 
| 238 | 
            -
                  update_cluster_info!
         | 
| 239 | 
            -
                  raise
         | 
| 240 | 
            -
                end
         | 
| 241 | 
            -
             | 
| 242 | 
            -
                def _scan(command, &block)
         | 
| 243 | 
            -
                  input_cursor = Integer(command[1])
         | 
| 244 | 
            -
             | 
| 245 | 
            -
                  client_index = input_cursor % 256
         | 
| 246 | 
            -
                  raw_cursor = input_cursor >> 8
         | 
| 247 | 
            -
             | 
| 248 | 
            -
                  clients = @node.scale_reading_clients
         | 
| 249 | 
            -
             | 
| 250 | 
            -
                  client = clients[client_index]
         | 
| 251 | 
            -
                  return ['0', []] unless client
         | 
| 252 | 
            -
             | 
| 253 | 
            -
                  command[1] = raw_cursor.to_s
         | 
| 254 | 
            -
             | 
| 255 | 
            -
                  result_cursor, result_keys = client.call(command, &block)
         | 
| 256 | 
            -
                  result_cursor = Integer(result_cursor)
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                  if result_cursor == 0
         | 
| 259 | 
            -
                    client_index += 1
         | 
| 260 | 
            -
                  end
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                  [((result_cursor << 8) + client_index).to_s, result_keys]
         | 
| 263 | 
            -
                end
         | 
| 264 | 
            -
             | 
| 265 | 
            -
                def assign_redirection_node(err_msg)
         | 
| 266 | 
            -
                  _, slot, node_key = err_msg.split(' ')
         | 
| 267 | 
            -
                  slot = slot.to_i
         | 
| 268 | 
            -
                  @slot.put(slot, node_key)
         | 
| 269 | 
            -
                  find_node(node_key)
         | 
| 270 | 
            -
                end
         | 
| 271 | 
            -
             | 
| 272 | 
            -
                def assign_asking_node(err_msg)
         | 
| 273 | 
            -
                  _, _, node_key = err_msg.split(' ')
         | 
| 274 | 
            -
                  find_node(node_key)
         | 
| 275 | 
            -
                end
         | 
| 276 | 
            -
             | 
| 277 | 
            -
                def assign_node(command)
         | 
| 278 | 
            -
                  node_key = find_node_key(command)
         | 
| 279 | 
            -
                  find_node(node_key)
         | 
| 280 | 
            -
                end
         | 
| 281 | 
            -
             | 
| 282 | 
            -
                def find_node_key(command, primary_only: false)
         | 
| 283 | 
            -
                  key = @command.extract_first_key(command)
         | 
| 284 | 
            -
                  return if key.empty?
         | 
| 285 | 
            -
             | 
| 286 | 
            -
                  slot = KeySlotConverter.convert(key)
         | 
| 287 | 
            -
                  return unless @slot.exists?(slot)
         | 
| 288 | 
            -
             | 
| 289 | 
            -
                  if @command.should_send_to_master?(command) || primary_only
         | 
| 290 | 
            -
                    @slot.find_node_key_of_master(slot)
         | 
| 291 | 
            -
                  else
         | 
| 292 | 
            -
                    @slot.find_node_key_of_slave(slot)
         | 
| 293 | 
            -
                  end
         | 
| 294 | 
            -
                end
         | 
| 295 | 
            -
             | 
| 296 | 
            -
                def find_node(node_key)
         | 
| 297 | 
            -
                  return @node.sample if node_key.nil?
         | 
| 298 | 
            -
             | 
| 299 | 
            -
                  @node.find_by(node_key)
         | 
| 300 | 
            -
                rescue Node::ReloadNeeded
         | 
| 301 | 
            -
                  update_cluster_info!(node_key)
         | 
| 302 | 
            -
                  @node.find_by(node_key)
         | 
| 303 | 
            -
                end
         | 
| 304 | 
            -
             | 
| 305 | 
            -
                def update_cluster_info!(node_key = nil)
         | 
| 306 | 
            -
                  unless node_key.nil?
         | 
| 307 | 
            -
                    host, port = NodeKey.split(node_key)
         | 
| 308 | 
            -
                    @option.add_node(host, port)
         | 
| 309 | 
            -
                  end
         | 
| 310 | 
            -
             | 
| 311 | 
            -
                  @node.map(&:disconnect)
         | 
| 312 | 
            -
                  @node, @slot = fetch_cluster_info!(@option)
         | 
| 313 | 
            -
                end
         | 
| 314 | 
            -
              end
         | 
| 315 | 
            -
            end
         | 
| @@ -1,41 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            class Redis
         | 
| 4 | 
            -
              module Connection
         | 
| 5 | 
            -
                module CommandHelper
         | 
| 6 | 
            -
                  COMMAND_DELIMITER = "\r\n"
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                  def build_command(args)
         | 
| 9 | 
            -
                    command = [nil]
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                    args.each do |i|
         | 
| 12 | 
            -
                      if i.is_a? Array
         | 
| 13 | 
            -
                        i.each do |j|
         | 
| 14 | 
            -
                          j = j.to_s
         | 
| 15 | 
            -
                          j = j.encoding == Encoding::BINARY ? j : j.b
         | 
| 16 | 
            -
                          command << "$#{j.bytesize}"
         | 
| 17 | 
            -
                          command << j
         | 
| 18 | 
            -
                        end
         | 
| 19 | 
            -
                      else
         | 
| 20 | 
            -
                        i = i.to_s
         | 
| 21 | 
            -
                        i = i.encoding == Encoding::BINARY ? i : i.b
         | 
| 22 | 
            -
                        command << "$#{i.bytesize}"
         | 
| 23 | 
            -
                        command << i
         | 
| 24 | 
            -
                      end
         | 
| 25 | 
            -
                    end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    command[0] = "*#{(command.length - 1) / 2}"
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                    # Trailing delimiter
         | 
| 30 | 
            -
                    command << ""
         | 
| 31 | 
            -
                    command.join(COMMAND_DELIMITER)
         | 
| 32 | 
            -
                  end
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  protected
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                  def encode(string)
         | 
| 37 | 
            -
                    string.force_encoding(Encoding.default_external)
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
                end
         | 
| 40 | 
            -
              end
         | 
| 41 | 
            -
            end
         | 
| @@ -1,68 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require "redis/connection/registry"
         | 
| 4 | 
            -
            require "redis/errors"
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            require "hiredis/connection"
         | 
| 7 | 
            -
            require "timeout"
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            class Redis
         | 
| 10 | 
            -
              module Connection
         | 
| 11 | 
            -
                class Hiredis
         | 
| 12 | 
            -
                  def self.connect(config)
         | 
| 13 | 
            -
                    connection = ::Hiredis::Connection.new
         | 
| 14 | 
            -
                    connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                    if config[:scheme] == "unix"
         | 
| 17 | 
            -
                      connection.connect_unix(config[:path], connect_timeout)
         | 
| 18 | 
            -
                    elsif config[:scheme] == "rediss" || config[:ssl]
         | 
| 19 | 
            -
                      raise NotImplementedError, "SSL not supported by hiredis driver"
         | 
| 20 | 
            -
                    else
         | 
| 21 | 
            -
                      connection.connect(config[:host], config[:port], connect_timeout)
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    instance = new(connection)
         | 
| 25 | 
            -
                    instance.timeout = config[:read_timeout]
         | 
| 26 | 
            -
                    instance
         | 
| 27 | 
            -
                  rescue Errno::ETIMEDOUT
         | 
| 28 | 
            -
                    raise TimeoutError
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                  def initialize(connection)
         | 
| 32 | 
            -
                    @connection = connection
         | 
| 33 | 
            -
                  end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                  def connected?
         | 
| 36 | 
            -
                    @connection&.connected?
         | 
| 37 | 
            -
                  end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                  def timeout=(timeout)
         | 
| 40 | 
            -
                    # Hiredis works with microsecond timeouts
         | 
| 41 | 
            -
                    @connection.timeout = Integer(timeout * 1_000_000)
         | 
| 42 | 
            -
                  end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                  def disconnect
         | 
| 45 | 
            -
                    @connection.disconnect
         | 
| 46 | 
            -
                    @connection = nil
         | 
| 47 | 
            -
                  end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                  def write(command)
         | 
| 50 | 
            -
                    @connection.write(command.flatten(1))
         | 
| 51 | 
            -
                  rescue Errno::EAGAIN
         | 
| 52 | 
            -
                    raise TimeoutError
         | 
| 53 | 
            -
                  end
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                  def read
         | 
| 56 | 
            -
                    reply = @connection.read
         | 
| 57 | 
            -
                    reply = CommandError.new(reply.message) if reply.is_a?(RuntimeError)
         | 
| 58 | 
            -
                    reply
         | 
| 59 | 
            -
                  rescue Errno::EAGAIN
         | 
| 60 | 
            -
                    raise TimeoutError
         | 
| 61 | 
            -
                  rescue RuntimeError => err
         | 
| 62 | 
            -
                    raise ProtocolError, err.message
         | 
| 63 | 
            -
                  end
         | 
| 64 | 
            -
                end
         | 
| 65 | 
            -
              end
         | 
| 66 | 
            -
            end
         | 
| 67 | 
            -
             | 
| 68 | 
            -
            Redis::Connection.drivers << Redis::Connection::Hiredis
         | 
| @@ -1,13 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            class Redis
         | 
| 4 | 
            -
              module Connection
         | 
| 5 | 
            -
                # Store a list of loaded connection drivers in the Connection module.
         | 
| 6 | 
            -
                # Redis::Client uses the last required driver by default, and will be aware
         | 
| 7 | 
            -
                # of the loaded connection drivers if the user chooses to override the
         | 
| 8 | 
            -
                # default connection driver.
         | 
| 9 | 
            -
                def self.drivers
         | 
| 10 | 
            -
                  @drivers ||= []
         | 
| 11 | 
            -
                end
         | 
| 12 | 
            -
              end
         | 
| 13 | 
            -
            end
         |