Correct flaky distributed cache test

make distributed cache more testable
This commit is contained in:
Sam
2017-08-01 16:11:48 -04:00
parent 513b7bf706
commit 71ad3a48c2
2 changed files with 121 additions and 95 deletions

View File

@ -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

View File

@ -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"