mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 15:25:35 +08:00
Fix Redis command errors when trying to start app with a readonly Redis.
This commit is contained in:
parent
3538b63305
commit
9bc3038728
|
@ -48,6 +48,8 @@ class GlobalSetting
|
||||||
end
|
end
|
||||||
token
|
token
|
||||||
end
|
end
|
||||||
|
rescue Redis::CommandError => e
|
||||||
|
@safe_secret_key_base = SecureRandom.hex(64) if e.message =~ /READONLY/
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.load_defaults
|
def self.load_defaults
|
||||||
|
|
|
@ -19,7 +19,9 @@ if defined?(Rack::MiniProfiler)
|
||||||
# raw_connection means results are not namespaced
|
# raw_connection means results are not namespaced
|
||||||
#
|
#
|
||||||
# namespacing gets complex, cause mini profiler is in the rack chain way before multisite
|
# namespacing gets complex, cause mini profiler is in the rack chain way before multisite
|
||||||
Rack::MiniProfiler.config.storage_instance = Rack::MiniProfiler::RedisStore.new(connection: DiscourseRedis.raw_connection)
|
Rack::MiniProfiler.config.storage_instance = Rack::MiniProfiler::RedisStore.new(
|
||||||
|
connection: DiscourseRedis.new(nil, namespace: false)
|
||||||
|
)
|
||||||
|
|
||||||
skip = [
|
skip = [
|
||||||
/^\/message-bus/,
|
/^\/message-bus/,
|
||||||
|
|
|
@ -135,9 +135,10 @@ class DiscourseRedis
|
||||||
options.dup.merge!(host: options[:slave_host], port: options[:slave_port])
|
options.dup.merge!(host: options[:slave_host], port: options[:slave_port])
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(config = nil)
|
def initialize(config = nil, namespace: true)
|
||||||
@config = config || DiscourseRedis.config
|
@config = config || DiscourseRedis.config
|
||||||
@redis = DiscourseRedis.raw_connection(@config)
|
@redis = DiscourseRedis.raw_connection(@config)
|
||||||
|
@namespace = namespace
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.fallback_handler
|
def self.fallback_handler
|
||||||
|
@ -183,29 +184,35 @@ class DiscourseRedis
|
||||||
:sunion, :ttl, :type, :watch, :zadd, :zcard, :zcount, :zincrby, :zrange, :zrangebyscore, :zrank, :zrem, :zremrangebyrank,
|
:sunion, :ttl, :type, :watch, :zadd, :zcard, :zcount, :zincrby, :zrange, :zrangebyscore, :zrank, :zrem, :zremrangebyrank,
|
||||||
:zremrangebyscore, :zrevrange, :zrevrangebyscore, :zrevrank, :zrangebyscore].each do |m|
|
:zremrangebyscore, :zrevrange, :zrevrangebyscore, :zrevrank, :zrangebyscore].each do |m|
|
||||||
define_method m do |*args|
|
define_method m do |*args|
|
||||||
args[0] = "#{namespace}:#{args[0]}"
|
args[0] = "#{namespace}:#{args[0]}" if @namespace
|
||||||
DiscourseRedis.ignore_readonly { @redis.send(m, *args) }
|
DiscourseRedis.ignore_readonly { @redis.send(m, *args) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mget(*args)
|
def mget(*args)
|
||||||
args.map! { |a| "#{namespace}:#{a}" }
|
args.map! { |a| "#{namespace}:#{a}" } if @namespace
|
||||||
DiscourseRedis.ignore_readonly { @redis.mget(*args) }
|
DiscourseRedis.ignore_readonly { @redis.mget(*args) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def del(k)
|
def del(k)
|
||||||
DiscourseRedis.ignore_readonly do
|
DiscourseRedis.ignore_readonly do
|
||||||
k = "#{namespace}:#{k}"
|
k = "#{namespace}:#{k}" if @namespace
|
||||||
@redis.del k
|
@redis.del k
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def keys(pattern = nil)
|
def keys(pattern = nil)
|
||||||
DiscourseRedis.ignore_readonly do
|
DiscourseRedis.ignore_readonly do
|
||||||
len = namespace.length + 1
|
pattern = pattern || '*'
|
||||||
@redis.keys("#{namespace}:#{pattern || '*'}").map {
|
pattern = "#{namespace}:#{pattern}" if @namespace
|
||||||
|k| k[len..-1]
|
keys = @redis.keys(pattern)
|
||||||
}
|
|
||||||
|
if @namespace
|
||||||
|
len = namespace.length + 1
|
||||||
|
keys.map! { |k| k[len..-1] }
|
||||||
|
end
|
||||||
|
|
||||||
|
keys
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,70 @@ describe DiscourseRedis do
|
||||||
|
|
||||||
let(:fallback_handler) { DiscourseRedis::FallbackHandler.instance }
|
let(:fallback_handler) { DiscourseRedis::FallbackHandler.instance }
|
||||||
|
|
||||||
|
describe 'redis commands' do
|
||||||
|
let(:raw_redis) { Redis.new(DiscourseRedis.config) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
raw_redis.flushall
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
raw_redis.flushall
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when namespace is enabled' do
|
||||||
|
let(:redis) { DiscourseRedis.new }
|
||||||
|
|
||||||
|
it 'should append namespace to the keys' do
|
||||||
|
redis.set('key', 1)
|
||||||
|
|
||||||
|
expect(raw_redis.get('default:key')).to eq('1')
|
||||||
|
expect(redis.keys).to eq(['key'])
|
||||||
|
|
||||||
|
redis.del('key')
|
||||||
|
|
||||||
|
expect(raw_redis.get('default:key')).to eq(nil)
|
||||||
|
|
||||||
|
raw_redis.set('default:key1', '1')
|
||||||
|
raw_redis.set('default:key2', '2')
|
||||||
|
|
||||||
|
expect(redis.mget('key1', 'key2')).to eq(['1', '2'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when namespace is disabled' do
|
||||||
|
let(:redis) { DiscourseRedis.new(nil, namespace: false) }
|
||||||
|
|
||||||
|
it 'should not append any namespace to the keys' do
|
||||||
|
redis.set('key', 1)
|
||||||
|
|
||||||
|
expect(raw_redis.get('key')).to eq('1')
|
||||||
|
expect(redis.keys).to eq(['key'])
|
||||||
|
|
||||||
|
redis.del('key')
|
||||||
|
|
||||||
|
expect(raw_redis.get('key')).to eq(nil)
|
||||||
|
|
||||||
|
raw_redis.set('key1', '1')
|
||||||
|
raw_redis.set('key2', '2')
|
||||||
|
|
||||||
|
expect(redis.mget('key1', 'key2')).to eq(['1', '2'])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should noop a readonly redis' do
|
||||||
|
expect(Discourse.recently_readonly?).to eq(false)
|
||||||
|
|
||||||
|
redis.without_namespace
|
||||||
|
.expects(:set)
|
||||||
|
.raises(Redis::CommandError.new("READONLY"))
|
||||||
|
|
||||||
|
redis.set('key', 1)
|
||||||
|
|
||||||
|
expect(Discourse.recently_readonly?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context '.slave_host' do
|
context '.slave_host' do
|
||||||
it 'should return the right config' do
|
it 'should return the right config' do
|
||||||
slave_config = DiscourseRedis.slave_config(config)
|
slave_config = DiscourseRedis.slave_config(config)
|
||||||
|
@ -22,9 +86,9 @@ describe DiscourseRedis do
|
||||||
it 'should check the status of the master server' do
|
it 'should check the status of the master server' do
|
||||||
begin
|
begin
|
||||||
fallback_handler.master = false
|
fallback_handler.master = false
|
||||||
$redis.without_namespace.expects(:get).raises(Redis::CommandError.new("READONLY"))
|
$redis.without_namespace.expects(:set).raises(Redis::CommandError.new("READONLY"))
|
||||||
fallback_handler.expects(:verify_master).once
|
fallback_handler.expects(:verify_master).once
|
||||||
$redis.get('test')
|
$redis.set('test', '1')
|
||||||
ensure
|
ensure
|
||||||
fallback_handler.master = true
|
fallback_handler.master = true
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user