mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 21:21:19 +08:00
Correct flaky distributed cache test
make distributed cache more testable
This commit is contained in:
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Like a hash, just does its best to stay in sync across the farm
|
# Like a hash, just does its best to stay in sync across the farm
|
||||||
# On boot all instances are blank, but they populate as various processes
|
# On boot all instances are blank, but they populate as various processes
|
||||||
# fill it up
|
# fill it up
|
||||||
@ -6,17 +8,21 @@ require 'weakref'
|
|||||||
require 'base64'
|
require 'base64'
|
||||||
|
|
||||||
class DistributedCache
|
class DistributedCache
|
||||||
|
|
||||||
|
class Manager
|
||||||
|
|
||||||
|
def initialize(message_bus = nil)
|
||||||
@subscribers = []
|
@subscribers = []
|
||||||
@subscribed = false
|
@subscribed = false
|
||||||
@lock = Mutex.new
|
@lock = Mutex.new
|
||||||
|
@message_bus = message_bus || MessageBus
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :key
|
def subscribers
|
||||||
|
|
||||||
def self.subscribers
|
|
||||||
@subscribers
|
@subscribers
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.process_message(message)
|
def process_message(message)
|
||||||
i = @subscribers.length - 1
|
i = @subscribers.length - 1
|
||||||
|
|
||||||
payload = message.data
|
payload = message.data
|
||||||
@ -45,15 +51,15 @@ class DistributedCache
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.channel_name
|
def channel_name
|
||||||
"/distributed_hash".freeze
|
"/distributed_hash".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_subscribe!
|
def ensure_subscribe!
|
||||||
return if @subscribed
|
return if @subscribed
|
||||||
@lock.synchronize do
|
@lock.synchronize do
|
||||||
return if @subscribed
|
return if @subscribed
|
||||||
MessageBus.subscribe(channel_name) do |message|
|
@message_bus.subscribe(channel_name) do |message|
|
||||||
@lock.synchronize do
|
@lock.synchronize do
|
||||||
process_message(message)
|
process_message(message)
|
||||||
end
|
end
|
||||||
@ -62,40 +68,50 @@ class DistributedCache
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.publish(hash, message)
|
def publish(hash, message)
|
||||||
message[:origin] = hash.identity
|
message[:origin] = hash.identity
|
||||||
message[:hash_key] = hash.key
|
message[:hash_key] = hash.key
|
||||||
message[:discourse_version] = Discourse.git_version
|
message[:discourse_version] = Discourse.git_version
|
||||||
MessageBus.publish(channel_name, message, user_ids: [-1])
|
@message_bus.publish(channel_name, message, user_ids: [-1])
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set(hash, key, value)
|
def set(hash, key, value)
|
||||||
# special support for set
|
# special support for set
|
||||||
marshal = (Set === value || Hash === value)
|
marshal = (Set === value || Hash === value)
|
||||||
value = Base64.encode64(Marshal.dump(value)) if marshal
|
value = Base64.encode64(Marshal.dump(value)) if marshal
|
||||||
publish(hash, op: :set, key: key, value: value, marshalled: marshal)
|
publish(hash, op: :set, key: key, value: value, marshalled: marshal)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.delete(hash, key)
|
def delete(hash, key)
|
||||||
publish(hash, op: :delete, key: key)
|
publish(hash, op: :delete, key: key)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.clear(hash)
|
def clear(hash)
|
||||||
publish(hash, op: :clear)
|
publish(hash, op: :clear)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.register(hash)
|
def register(hash)
|
||||||
@lock.synchronize do
|
@lock.synchronize do
|
||||||
@subscribers << WeakRef.new(hash)
|
@subscribers << WeakRef.new(hash)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(key)
|
@default_manager = Manager.new
|
||||||
DistributedCache.ensure_subscribe!
|
|
||||||
DistributedCache.register(self)
|
|
||||||
|
|
||||||
|
def self.default_manager
|
||||||
|
@default_manager
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :key
|
||||||
|
|
||||||
|
def initialize(key, manager = nil)
|
||||||
@key = key
|
@key = key
|
||||||
@data = {}
|
@data = {}
|
||||||
|
@manager = manager || DistributedCache.default_manager
|
||||||
|
|
||||||
|
@manager.ensure_subscribe!
|
||||||
|
@manager.register(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def identity
|
def identity
|
||||||
@ -105,7 +121,7 @@ class DistributedCache
|
|||||||
|
|
||||||
def []=(k, v)
|
def []=(k, v)
|
||||||
k = k.to_s if Symbol === k
|
k = k.to_s if Symbol === k
|
||||||
DistributedCache.set(self, k, v)
|
@manager.set(self, k, v)
|
||||||
hash[k] = v
|
hash[k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -116,12 +132,12 @@ class DistributedCache
|
|||||||
|
|
||||||
def delete(k)
|
def delete(k)
|
||||||
k = k.to_s if Symbol === k
|
k = k.to_s if Symbol === k
|
||||||
DistributedCache.delete(self, k)
|
@manager.delete(self, k)
|
||||||
hash.delete(k)
|
hash.delete(k)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear
|
def clear
|
||||||
DistributedCache.clear(self)
|
@manager.clear(self)
|
||||||
hash.clear
|
hash.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -4,20 +4,30 @@ require 'distributed_cache'
|
|||||||
describe DistributedCache do
|
describe DistributedCache do
|
||||||
|
|
||||||
before :all do
|
before :all do
|
||||||
$redis.flushall
|
@bus = MessageBus::Instance.new
|
||||||
|
@bus.configure(backend: :memory)
|
||||||
|
@manager = DistributedCache::Manager.new(@bus)
|
||||||
|
end
|
||||||
|
|
||||||
|
after :all do
|
||||||
|
@bus.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache(name)
|
||||||
|
DistributedCache.new(name, @manager)
|
||||||
end
|
end
|
||||||
|
|
||||||
let! :cache1 do
|
let! :cache1 do
|
||||||
DistributedCache.new("test")
|
cache("test")
|
||||||
end
|
end
|
||||||
|
|
||||||
let! :cache2 do
|
let! :cache2 do
|
||||||
DistributedCache.new("test")
|
cache("test")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows us to store Set' do
|
it 'allows us to store Set' do
|
||||||
c1 = DistributedCache.new("test1")
|
c1 = cache("test1")
|
||||||
c2 = DistributedCache.new("test1")
|
c2 = cache("test1")
|
||||||
|
|
||||||
set = Set.new
|
set = Set.new
|
||||||
set << 1
|
set << 1
|
||||||
@ -45,8 +55,8 @@ describe DistributedCache do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'does not leak state across caches' do
|
it 'does not leak state across caches' do
|
||||||
c2 = DistributedCache.new("test1")
|
c2 = cache("test1")
|
||||||
c3 = DistributedCache.new("test1")
|
c3 = cache("test1")
|
||||||
c2["hi"] = "hi"
|
c2["hi"] = "hi"
|
||||||
wait_for do
|
wait_for do
|
||||||
c3["hi"] == "hi"
|
c3["hi"] == "hi"
|
||||||
|
Reference in New Issue
Block a user