mirror of
https://github.com/discourse/discourse.git
synced 2024-12-02 06:54:15 +08:00
2a22a3b51d
Currently when a cache entry is corrupt, we log the event without doing anything else. It means the cache is still corrupt, and the proper value isn’t computed again. Normally, it’s very rare the cache becomes corrupt, but it can happen when upgrading Rails for example and the cache format changes. This is normally handled automatically by Rails but since we’re using a custom cache class, we have to do it ourselves. This patch takes the same approach the Rails team did, when a cache entry is corrupt, we treat it as a miss, recomputing the proper value and caching it in the new format.
126 lines
2.7 KiB
Ruby
126 lines
2.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "cache"
|
|
|
|
RSpec.describe Cache do
|
|
subject(:cache) { Cache.new }
|
|
|
|
it "supports exist?" do
|
|
cache.write("testing", 1.1)
|
|
expect(cache.exist?("testing")).to eq(true)
|
|
expect(cache.exist?(SecureRandom.hex)).to eq(false)
|
|
end
|
|
|
|
it "supports float" do
|
|
cache.write("float", 1.1)
|
|
expect(cache.read("float")).to eq(1.1)
|
|
end
|
|
|
|
it "supports fixnum" do
|
|
cache.write("num", 1)
|
|
expect(cache.read("num")).to eq(1)
|
|
end
|
|
|
|
it "supports hash" do
|
|
hash = { a: 1, b: [1, 2, 3] }
|
|
cache.write("hash", hash)
|
|
expect(cache.read("hash")).to eq(hash)
|
|
end
|
|
|
|
it "can be cleared" do
|
|
Discourse.redis.set("boo", "boo")
|
|
cache.write("hello0", "world")
|
|
cache.write("hello1", "world")
|
|
cache.clear
|
|
|
|
expect(Discourse.redis.get("boo")).to eq("boo")
|
|
expect(cache.read("hello0")).to eq(nil)
|
|
end
|
|
|
|
it "can delete correctly" do
|
|
cache.delete("key")
|
|
|
|
cache.fetch("key", expires_in: 1.minute) { "test" }
|
|
|
|
expect(cache.fetch("key")).to eq("test")
|
|
|
|
cache.delete("key")
|
|
expect(cache.fetch("key")).to eq(nil)
|
|
end
|
|
|
|
it "calls setex in redis" do
|
|
cache.delete("key")
|
|
cache.delete("bla")
|
|
|
|
key = cache.normalize_key("key")
|
|
|
|
cache.fetch("key", expires_in: 1.minute) { "bob" }
|
|
|
|
expect(Discourse.redis.ttl(key)).to be_within(2.seconds).of(1.minute)
|
|
|
|
# we always expire withing a day
|
|
cache.fetch("bla") { "hi" }
|
|
|
|
key = cache.normalize_key("bla")
|
|
expect(Discourse.redis.ttl(key)).to be_within(2.seconds).of(1.day)
|
|
end
|
|
|
|
it "can store and fetch correctly" do
|
|
cache.delete "key"
|
|
|
|
r =
|
|
cache.fetch "key" do
|
|
"bob"
|
|
end
|
|
|
|
expect(r).to eq("bob")
|
|
end
|
|
|
|
it "can fetch existing correctly" do
|
|
cache.write "key", "bill"
|
|
|
|
r =
|
|
cache.fetch "key" do
|
|
"bob"
|
|
end
|
|
expect(r).to eq("bill")
|
|
end
|
|
|
|
it "can fetch keys with pattern" do
|
|
cache.write "users:admins", "jeff"
|
|
cache.write "users:moderators", "bob"
|
|
|
|
expect(cache.keys("users:*").count).to eq(2)
|
|
end
|
|
|
|
it "can fetch namespace" do
|
|
expect(cache.namespace).to eq("_CACHE")
|
|
end
|
|
|
|
it "uses the defined expires_in" do
|
|
cache.write "foo:bar", "baz", expires_in: 3.minutes
|
|
|
|
expect(cache.redis.ttl("#{cache.namespace}:foo:bar")).to eq(180)
|
|
end
|
|
|
|
describe ".fetch" do
|
|
subject(:fetch_value) { cache.fetch("my_key") { "bob" } }
|
|
|
|
context "when the cache is corrupt" do
|
|
before do
|
|
cache.delete("my_key")
|
|
Discourse.redis.setex(cache.normalize_key("my_key"), described_class::MAX_CACHE_AGE, "")
|
|
end
|
|
|
|
it "runs and return the provided block" do
|
|
expect(fetch_value).to eq("bob")
|
|
end
|
|
|
|
it "generates a new cache entry" do
|
|
fetch_value
|
|
expect(cache.read("my_key")).to eq("bob")
|
|
end
|
|
end
|
|
end
|
|
end
|