Class: Cloudtasker::RedisClient

Inherits:
Object
  • Object
show all
Defined in:
lib/cloudtasker/redis_client.rb

Overview

A wrapper with helper methods for redis

Constant Summary collapse

LOCK_KEY_PREFIX =

Suffix added to cache keys when locking them

'cloudtasker/lock'
LOCK_DURATION =

seconds

2
LOCK_WAIT_DURATION =

seconds

0.03
DEFAULT_POOL_SIZE =

Default pool size used for Redis

ENV.fetch('RAILS_MAX_THREADS', 25)
DEFAULT_POOL_TIMEOUT =
5

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, **kwargs, &block) ⇒ Any

Delegate all methods to the redis client. Ruby 3 delegation method style.

Parameters:

  • name (String, Symbol)

    The method to delegate.

  • *args (Array<any>)

    The list of method positional arguments.

  • *kwargs (Hash<any>)

    The list of method keyword arguments.

  • &block (Proc)

    Block passed to the method.

Returns:

  • (Any)

    The method return value



147
148
149
150
151
152
153
# File 'lib/cloudtasker/redis_client.rb', line 147

def method_missing(name, *args, &block)
  if Redis.method_defined?(name)
    client.with { |c| c.send(name, *args, &block) }
  else
    super
  end
end

Class Method Details

.clientObject



18
19
20
21
22
23
24
25
26
# File 'lib/cloudtasker/redis_client.rb', line 18

def self.client
  @client ||= begin
    pool_size = Cloudtasker.config.redis&.dig(:pool_size) || DEFAULT_POOL_SIZE
    pool_timeout = Cloudtasker.config.redis&.dig(:pool_timeout) || DEFAULT_POOL_TIMEOUT
    ConnectionPool.new(size: pool_size, timeout: pool_timeout) do
      Redis.new(Cloudtasker.config.redis || {})
    end
  end
end

Instance Method Details

#clearInteger

Clear all redis keys

Returns:

  • (Integer)

    The number of keys deleted



103
104
105
106
107
108
109
# File 'lib/cloudtasker/redis_client.rb', line 103

def clear
  all_keys = keys
  return 0 if all_keys.empty?

  # Delete all keys
  del(*all_keys)
end

#clientRedis

Return the underlying redis client.

Returns:

  • (Redis)

    The redis client.



33
34
35
# File 'lib/cloudtasker/redis_client.rb', line 33

def client
  @client ||= self.class.client
end

#fetch(key) ⇒ Hash, Array

Get a cache entry and parse it as JSON.

Parameters:

  • key (String, Symbol)

    The cache key to fetch.

Returns:

  • (Hash, Array)

    The content of the cache key, parsed as JSON.



44
45
46
47
48
49
50
# File 'lib/cloudtasker/redis_client.rb', line 44

def fetch(key)
  return nil unless (val = get(key.to_s))

  JSON.parse(val, symbolize_names: true)
rescue JSON::ParserError
  nil
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Check if the class respond to a certain method.

Parameters:

  • name (String, Symbol)

    The name of the method.

  • include_private (Boolean) (defaults to: false)

    Whether to check private methods or not. Default to false.

Returns:

  • (Boolean)

    Return true if the class respond to this method.



182
183
184
# File 'lib/cloudtasker/redis_client.rb', line 182

def respond_to_missing?(name, include_private = false)
  Redis.method_defined?(name) || super
end

#search(pattern) ⇒ Array<String>

Return all keys matching the provided patterns.

Parameters:

  • pattern (String)

    A redis compatible pattern.

Returns:

  • (Array<String>)

    The list of matching keys



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/cloudtasker/redis_client.rb', line 118

def search(pattern)
  # Initialize loop variables
  cursor = nil
  list = []

  # Scan and capture matching keys
  client.with do |conn|
    while cursor != 0
      scan = conn.scan(cursor || 0, match: pattern)
      list += scan[1]
      cursor = scan[0].to_i
    end
  end

  list
end

#with_lock(cache_key, max_wait: nil) ⇒ Object

Acquire a lock on a cache entry.

Locks are enforced to be short-lived (2s). The yielded block should limit its logic to short operations (e.g. redis get/set).

Examples:

redis = RedisClient.new
redis.with_lock('foo')
  content = redis.fetch('foo')
  redis.set(content.merge(bar: 'bar).to_json)
end

Parameters:

  • cache_key (String)

    The cache key to access.

  • max_wait (Integer) (defaults to: nil)

    The number of seconds after which the lock will be cleared anyway.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/cloudtasker/redis_client.rb', line 80

def with_lock(cache_key, max_wait: nil)
  return nil unless cache_key

  # Set max wait
  max_wait = (max_wait || LOCK_DURATION).to_i

  # Wait to acquire lock
  lock_key = [LOCK_KEY_PREFIX, cache_key].join('/')
  client.with do |conn|
    sleep(LOCK_WAIT_DURATION) until conn.set(lock_key, true, nx: true, ex: max_wait)
  end

  # yield content
  yield
ensure
  del(lock_key)
end

#write(key, content) ⇒ String

Write a cache entry as JSON.

Parameters:

  • key (String, Symbol)

    The cache key to write.

  • content (Hash, Array)

    The content to write.

Returns:

  • (String)

    Redis response code.



60
61
62
# File 'lib/cloudtasker/redis_client.rb', line 60

def write(key, content)
  set(key.to_s, content.to_json)
end