Class: ThreeScale::Backend::StorageAsync::Client

Inherits:
Object
  • Object
show all
Includes:
Configurable
Defined in:
lib/3scale/backend/storage_async/client.rb

Overview

This is a wrapper for the Async-Redis client (github.com/socketry/async-redis). This class overrides some methods to provide the same interface that the redis-rb client provides. This is done to avoid modifying all the model classes which assume that the Storage instance behaves likes the redis-rb client.

Class Attribute Summary collapse

Instance Method Summary collapse

Methods included from Configurable

#configuration, #configuration=, included

Constructor Details

#initialize(opts) ⇒ Client

Returns a new instance of Client.



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/3scale/backend/storage_async/client.rb', line 43

def initialize(opts)
  host, port = opts[:url].match(HOST_PORT_REGEX).captures if opts[:url]
  host ||= DEFAULT_HOST
  port ||= DEFAULT_PORT

  endpoint = Async::IO::Endpoint.tcp(host, port)
  @redis_async = Async::Redis::Client.new(
    endpoint, limit: opts[:max_connections]
  )
  @building_pipeline = false
end

Class Attribute Details

.instance(reset = false) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/3scale/backend/storage_async/client.rb', line 29

def instance(reset = false)
  if reset || @instance.nil?
    @instance = new(
        Storage::Helpers.config_with(
            configuration.redis,
            options: { default_url: "#{DEFAULT_HOST}:#{DEFAULT_PORT}" }
        )
    )
  else
    @instance
  end
end

Instance Method Details

#blpop(*args) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/3scale/backend/storage_async/client.rb', line 126

def blpop(*args)
  call_args = ['BLPOP'] + args

  # redis-rb accepts a Hash as last arg that can contain :timeout.
  if call_args.last.is_a? Hash
    timeout = call_args.pop[:timeout]
    call_args << timeout
  end

  @redis_async.call(*call_args.flatten)
end

#closeObject



200
201
202
# File 'lib/3scale/backend/storage_async/client.rb', line 200

def close
  @redis_async.close
end

#pipelined(&block) ⇒ Object

This method allows us to send pipelines like this: storage.pipelined do

storage.get('a')
storage.get('b')

end



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/3scale/backend/storage_async/client.rb', line 170

def pipelined(&block)
  # This replaces the client with a Pipeline that accumulates the Redis
  # commands run in a block and sends all of them in a single request.
  #
  # There's an important limitation: this assumes that the fiber will
  # not yield in the block.

  # When running a nested pipeline, we just need to continue
  # accumulating commands.
  if @building_pipeline
    block.call
    return
  end

  @building_pipeline = true

  original = @redis_async
  pipeline = Pipeline.new
  @redis_async = pipeline

  begin
    block.call
  ensure
    @redis_async = original
    @building_pipeline = false
  end

  pipeline.run(original)
end

#scan(cursor, opts = {}) ⇒ Object



156
157
158
159
160
161
162
163
# File 'lib/3scale/backend/storage_async/client.rb', line 156

def scan(cursor, opts = {})
  args = ['SCAN', cursor]

  args += ['MATCH', opts[:match]] if opts[:match]
  args += ['COUNT', opts[:count]] if opts[:count]

  @redis_async.call(*args)
end

#set(key, val, opts = {}) ⇒ Object



138
139
140
141
142
143
144
145
# File 'lib/3scale/backend/storage_async/client.rb', line 138

def set(key, val, opts = {})
  args = ['SET', key, val]

  args += ['EX', opts[:ex]] if opts[:ex]
  args << 'NX' if opts[:nx]

  @redis_async.call(*args)
end

#sscan(key, cursor, opts = {}) ⇒ Object



147
148
149
150
151
152
153
154
# File 'lib/3scale/backend/storage_async/client.rb', line 147

def sscan(key, cursor, opts = {})
  args = ['SSCAN', key, cursor]

  args += ['MATCH', opts[:match]] if opts[:match]
  args += ['COUNT', opts[:count]] if opts[:count]

  @redis_async.call(*args)
end