Class: Tina4::CacheBackends::RedisBackend

Inherits:
BaseBackend
  • Object
show all
Defined in:
lib/tina4/cache_backends/redis_backend.rb

Overview

Redis / Valkey backend (parity with Python _RedisBackend). Uses the ‘redis` gem if it is installed, otherwise falls back to the raw RESP protocol over a TCP socket — so it works with zero runtime dependencies.

URL form: scheme://[user@]host[/db]. Credentials may also be supplied via TINA4_CACHE_USERNAME / TINA4_CACHE_PASSWORD (parity with TINA4_DATABASE_USERNAME / TINA4_DATABASE_PASSWORD). Wrong credentials cause available? to return false, so the factory falls back to file.

Direct Known Subclasses

ValkeyBackend

Constant Summary collapse

PREFIX =
"tina4:cache:"

Instance Method Summary collapse

Constructor Details

#initialize(url: "redis://localhost:6379", max_entries: 1000, name: "redis") ⇒ RedisBackend

Returns a new instance of RedisBackend.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/tina4/cache_backends/redis_backend.rb', line 21

def initialize(url: "redis://localhost:6379", max_entries: 1000, name: "redis")
  @max_entries = max_entries
  @name = name
  @hits = 0
  @misses = 0
  @client = nil
  @use_raw = false

  parse_url(url)

  # Try the redis gem first.
  begin
    require "redis"
    kwargs = { host: @host, port: @port, db: @db, timeout: 5 }
    kwargs[:password] = @password if @password
    kwargs[:username] = @username if @username
    @client = Redis.new(**kwargs)
    @client.ping
    @available = true
  rescue LoadError, StandardError
    @client = nil
    @use_raw = true
    # No gem — usable only if the server answers (and authenticates).
    @available = probe
  end
end

Instance Method Details

#available?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/tina4/cache_backends/redis_backend.rb', line 48

def available?
  @available
end

#clearObject



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/tina4/cache_backends/redis_backend.rb', line 112

def clear
  @hits = 0
  @misses = 0
  if @client
    begin
      keys = @client.keys("#{PREFIX}*")
      @client.del(*keys) unless keys.empty?
    rescue StandardError
    end
  elsif @use_raw
    # Raw RESP doesn't support pattern delete easily; rely on TTL.
  end
end

#delete(key) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/tina4/cache_backends/redis_backend.rb', line 97

def delete(key)
  full_key = PREFIX + key
  if @client
    begin
      @client.del(full_key) > 0
    rescue StandardError
      false
    end
  elsif @use_raw
    resp_command("DEL", full_key) == "1"
  else
    false
  end
end

#get(key) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/tina4/cache_backends/redis_backend.rb', line 52

def get(key)
  full_key = PREFIX + key
  raw = if @client
          begin
            @client.get(full_key)
          rescue StandardError
            nil
          end
        elsif @use_raw
          resp_command("GET", full_key)
        end

  if raw.nil?
    @misses += 1
    return nil
  end
  @hits += 1
  begin
    JSON.parse(raw)
  rescue JSON::ParserError, TypeError
    raw
  end
end

#nameObject



137
138
139
# File 'lib/tina4/cache_backends/redis_backend.rb', line 137

def name
  @name
end

#set(key, value, ttl) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/tina4/cache_backends/redis_backend.rb', line 76

def set(key, value, ttl)
  full_key = PREFIX + key
  serialized = JSON.generate(value)
  if @client
    begin
      if ttl > 0
        @client.setex(full_key, ttl, serialized)
      else
        @client.set(full_key, serialized)
      end
    rescue StandardError
    end
  elsif @use_raw
    if ttl > 0
      resp_command("SETEX", full_key, ttl.to_s, serialized)
    else
      resp_command("SET", full_key, serialized)
    end
  end
end

#statsObject



126
127
128
129
130
131
132
133
134
135
# File 'lib/tina4/cache_backends/redis_backend.rb', line 126

def stats
  size = 0
  if @client
    begin
      size = @client.keys("#{PREFIX}*").size
    rescue StandardError
    end
  end
  { hits: @hits, misses: @misses, size: size, backend: @name }
end