Class: Cache

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

Overview

This is a bottom up implementation of ActiveSupport::Cache::Store this allows us to cleanly implement without using cache entries and version support which we do not use, in tern this makes the cache as fast as simply using ‘Discourse.redis.setex` with a more convenient API

It only implements a subset of ActiveSupport::Cache::Store as we make no use of large parts of the interface.

An additional advantage of this class is that all methods have named params Rails tends to use options hash for lots of stuff due to legacy reasons this makes it harder to reason about the API

Constant Summary collapse

MAX_CACHE_AGE =

nothing is cached for longer than 1 day EVER there is no reason to have data older than this clogging redis it is dangerous cause if we rename keys we will be stuck with pointless data

1.day

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(namespace: "_CACHE") ⇒ Cache

Returns a new instance of Cache.



32
33
34
# File 'lib/cache.rb', line 32

def initialize(namespace: "_CACHE")
  @namespace = namespace
end

Instance Attribute Details

#namespaceObject (readonly)

Returns the value of attribute namespace.



24
25
26
# File 'lib/cache.rb', line 24

def namespace
  @namespace
end

Class Method Details

.supports_cache_versioning?Boolean

we don’t need this feature, 1 day expiry is enough it makes lookups a tad cheaper

Returns:

  • (Boolean)


28
29
30
# File 'lib/cache.rb', line 28

def self.supports_cache_versioning?
  false
end

Instance Method Details

#clearObject



48
49
50
# File 'lib/cache.rb', line 48

def clear
  keys.each { |k| redis.del(k) }
end

#delete(name) ⇒ Object



71
72
73
# File 'lib/cache.rb', line 71

def delete(name)
  redis.del(normalize_key(name))
end

#exist?(name) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
# File 'lib/cache.rb', line 56

def exist?(name)
  key = normalize_key(name)
  redis.exists?(key)
end

#fetch(name, expires_in: nil, force: nil, &blk) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/cache.rb', line 75

def fetch(name, expires_in: nil, force: nil, &blk)
  if block_given?
    key = normalize_key(name)
    raw = nil

    raw = redis.get(key) if !force

    if raw
      begin
        Marshal.load(raw) # rubocop:disable Security/MarshalLoad
      rescue => e
        log_first_exception(e)
      end
    else
      val = blk.call
      write_entry(key, val, expires_in: expires_in)
      val
    end
  elsif force
    raise ArgumentError,
          "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
  else
    read(name)
  end
end

#keys(pattern = "*") ⇒ Object



44
45
46
# File 'lib/cache.rb', line 44

def keys(pattern = "*")
  redis.scan_each(match: "#{@namespace}:#{pattern}").to_a
end

#normalize_key(key) ⇒ Object



52
53
54
# File 'lib/cache.rb', line 52

def normalize_key(key)
  "#{@namespace}:#{key}"
end

#read(name) ⇒ Object

this removes a bunch of stuff we do not need like instrumentation and versioning



62
63
64
65
# File 'lib/cache.rb', line 62

def read(name)
  key = normalize_key(name)
  read_entry(key)
end

#reconnectObject



40
41
42
# File 'lib/cache.rb', line 40

def reconnect
  redis.reconnect
end

#redisObject



36
37
38
# File 'lib/cache.rb', line 36

def redis
  Discourse.redis
end

#write(name, value, expires_in: nil) ⇒ Object



67
68
69
# File 'lib/cache.rb', line 67

def write(name, value, expires_in: nil)
  write_entry(normalize_key(name), value, expires_in: expires_in)
end