mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 12:57:29 +08:00
FIX: add support for pipelined and multi redis commands (#16682)
Latest redis interoduces a block form of multi / pipelined, this was incorrectly passed through and not namespaced. Fix also updates logster, we held off on upgrading it due to missing functions
This commit is contained in:
parent
919f71537e
commit
2df3c65ba9
2
Gemfile
2
Gemfile
|
@ -230,7 +230,7 @@ gem 'cppjieba_rb', require: false
|
||||||
gem 'lograge', require: false
|
gem 'lograge', require: false
|
||||||
gem 'logstash-event', require: false
|
gem 'logstash-event', require: false
|
||||||
gem 'logstash-logger', require: false
|
gem 'logstash-logger', require: false
|
||||||
gem 'logster', '2.11.0'
|
gem 'logster'
|
||||||
|
|
||||||
# NOTE: later versions of sassc are causing a segfault, possibly dependent on processer architecture
|
# NOTE: later versions of sassc are causing a segfault, possibly dependent on processer architecture
|
||||||
# and until resolved should be locked at 2.0.1
|
# and until resolved should be locked at 2.0.1
|
||||||
|
|
|
@ -215,7 +215,7 @@ GEM
|
||||||
logstash-event (1.2.02)
|
logstash-event (1.2.02)
|
||||||
logstash-logger (0.26.1)
|
logstash-logger (0.26.1)
|
||||||
logstash-event (~> 1.2)
|
logstash-event (~> 1.2)
|
||||||
logster (2.11.0)
|
logster (2.11.2)
|
||||||
loofah (2.17.0)
|
loofah (2.17.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
|
@ -560,7 +560,7 @@ DEPENDENCIES
|
||||||
lograge
|
lograge
|
||||||
logstash-event
|
logstash-event
|
||||||
logstash-logger
|
logstash-logger
|
||||||
logster (= 2.11.0)
|
logster
|
||||||
loofah
|
loofah
|
||||||
lru_redux
|
lru_redux
|
||||||
lz4-ruby
|
lz4-ruby
|
||||||
|
@ -638,4 +638,4 @@ DEPENDENCIES
|
||||||
yaml-lint
|
yaml-lint
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.5
|
2.3.13
|
||||||
|
|
|
@ -181,10 +181,10 @@ class Site
|
||||||
json = MultiJson.dump(SiteSerializer.new(site, root: false, scope: guardian))
|
json = MultiJson.dump(SiteSerializer.new(site, root: false, scope: guardian))
|
||||||
|
|
||||||
if guardian.anonymous?
|
if guardian.anonymous?
|
||||||
Discourse.redis.multi do
|
Discourse.redis.multi do |transaction|
|
||||||
Discourse.redis.setex 'site_json', 1800, json
|
transaction.setex 'site_json', 1800, json
|
||||||
Discourse.redis.set 'site_json_seq', seq
|
transaction.set 'site_json_seq', seq
|
||||||
Discourse.redis.set 'site_json_version', Discourse.git_version
|
transaction.set 'site_json_version', Discourse.git_version
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,9 @@ class RandomTopicSelector
|
||||||
key = cache_key(category)
|
key = cache_key(category)
|
||||||
|
|
||||||
if results.present?
|
if results.present?
|
||||||
Discourse.redis.multi do
|
Discourse.redis.multi do |transaction|
|
||||||
Discourse.redis.rpush(key, results)
|
transaction.rpush(key, results)
|
||||||
Discourse.redis.expire(key, 2.days)
|
transaction.expire(key, 2.days)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,9 +56,9 @@ class RandomTopicSelector
|
||||||
|
|
||||||
return results if count < 1
|
return results if count < 1
|
||||||
|
|
||||||
results = Discourse.redis.multi do
|
results = Discourse.redis.multi do |transaction|
|
||||||
Discourse.redis.lrange(key, 0, count - 1)
|
transaction.lrange(key, 0, count - 1)
|
||||||
Discourse.redis.ltrim(key, count, -1)
|
transaction.ltrim(key, count, -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
if !results.is_a?(Array) # Redis is in readonly mode
|
if !results.is_a?(Array) # Redis is in readonly mode
|
||||||
|
|
|
@ -14,9 +14,9 @@ class DiscourseRedis
|
||||||
GlobalSetting.redis_config
|
GlobalSetting.redis_config
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(config = nil, namespace: true)
|
def initialize(config = nil, namespace: true, raw_redis: nil)
|
||||||
@config = config || DiscourseRedis.config
|
@config = config || DiscourseRedis.config
|
||||||
@redis = DiscourseRedis.raw_connection(@config.dup)
|
@redis = raw_redis || DiscourseRedis.raw_connection(@config.dup)
|
||||||
@namespace = namespace
|
@namespace = namespace
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,6 +150,26 @@ class DiscourseRedis
|
||||||
Cache.new
|
Cache.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def multi
|
||||||
|
if block_given?
|
||||||
|
@redis.multi do |transaction|
|
||||||
|
yield DiscourseRedis.new(@config, namespace: @namespace, raw_redis: transaction)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@redis.multi
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pipelined
|
||||||
|
if block_given?
|
||||||
|
@redis.pipelined do |transaction|
|
||||||
|
yield DiscourseRedis.new(@config, namespace: @namespace, raw_redis: transaction)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@redis.pipelined
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def remove_namespace(key)
|
def remove_namespace(key)
|
||||||
|
|
|
@ -93,9 +93,9 @@ class DistributedMutex
|
||||||
got_lock = false
|
got_lock = false
|
||||||
else
|
else
|
||||||
result =
|
result =
|
||||||
redis.multi do
|
redis.multi do |transaction|
|
||||||
redis.set key, expire_time.to_s
|
transaction.set key, expire_time.to_s
|
||||||
redis.expireat key, expire_time + 1
|
transaction.expireat key, expire_time + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
got_lock = !result.nil?
|
got_lock = !result.nil?
|
||||||
|
@ -112,9 +112,11 @@ class DistributedMutex
|
||||||
current_expire_time = redis.get key
|
current_expire_time = redis.get key
|
||||||
|
|
||||||
if current_expire_time == expire_time.to_s
|
if current_expire_time == expire_time.to_s
|
||||||
|
# MULTI is the way redis ensures the watched key
|
||||||
|
# has not changed by the time it is deleted
|
||||||
result =
|
result =
|
||||||
redis.multi do
|
redis.multi do |transaction|
|
||||||
redis.del key
|
transaction.del key
|
||||||
end
|
end
|
||||||
return !result.nil?
|
return !result.nil?
|
||||||
else
|
else
|
||||||
|
|
|
@ -14,9 +14,9 @@ class RedisSnapshot
|
||||||
keys = redis.keys
|
keys = redis.keys
|
||||||
|
|
||||||
values =
|
values =
|
||||||
redis.pipelined do
|
redis.pipelined do |batch|
|
||||||
keys.each do |key|
|
keys.each do |key|
|
||||||
redis.dump(key)
|
batch.dump(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ class RedisSnapshot
|
||||||
end
|
end
|
||||||
|
|
||||||
def restore(redis = Discourse.redis)
|
def restore(redis = Discourse.redis)
|
||||||
redis.pipelined do
|
redis.pipelined do |batch|
|
||||||
redis.flushdb
|
batch.flushdb
|
||||||
|
|
||||||
@dump.each do |key, value|
|
@dump.each do |key, value|
|
||||||
redis.restore(key, 0, value)
|
batch.restore(key, 0, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,13 @@ task 'redis:clean_up' => ['environment'] do
|
||||||
cursor, keys = redis.scan(cursor)
|
cursor, keys = redis.scan(cursor)
|
||||||
cursor = cursor.to_i
|
cursor = cursor.to_i
|
||||||
|
|
||||||
redis.multi do
|
redis.multi do |transaction|
|
||||||
keys.each do |key|
|
keys.each do |key|
|
||||||
if match = key.match(regexp)
|
if match = key.match(regexp)
|
||||||
db_name = match[:message_bus] || match[:namespace]
|
db_name = match[:message_bus] || match[:namespace]
|
||||||
|
|
||||||
if !dbs.include?(db_name)
|
if !dbs.include?(db_name)
|
||||||
redis.del(key)
|
transaction.del(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,45 @@ describe DiscourseRedis do
|
||||||
raw_redis.flushdb
|
raw_redis.flushdb
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'pipelined / multi' do
|
||||||
|
let(:redis) { DiscourseRedis.new }
|
||||||
|
|
||||||
|
it 'should support multi commands' do
|
||||||
|
val = redis.multi do |transaction|
|
||||||
|
transaction.set 'foo', 'bar'
|
||||||
|
transaction.set 'bar', 'foo'
|
||||||
|
transaction.get 'bar'
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(raw_redis.get('foo')).to eq(nil)
|
||||||
|
expect(raw_redis.get('bar')).to eq(nil)
|
||||||
|
expect(redis.get('foo')).to eq('bar')
|
||||||
|
expect(redis.get('bar')).to eq('foo')
|
||||||
|
|
||||||
|
expect(val).to eq(["OK", "OK", "foo"])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should support pipelined commands' do
|
||||||
|
set, incr = nil
|
||||||
|
val = redis.pipelined do |pipeline|
|
||||||
|
set = pipeline.set "foo", "baz"
|
||||||
|
incr = pipeline.incr "baz"
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(val).to eq(["OK", 1])
|
||||||
|
|
||||||
|
expect(set.value).to eq("OK")
|
||||||
|
expect(incr.value).to eq(1)
|
||||||
|
|
||||||
|
expect(raw_redis.get('foo')).to eq(nil)
|
||||||
|
expect(raw_redis.get('baz')).to eq(nil)
|
||||||
|
|
||||||
|
expect(redis.get('foo')).to eq("baz")
|
||||||
|
expect(redis.get('baz')).to eq("1")
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'when namespace is enabled' do
|
describe 'when namespace is enabled' do
|
||||||
let(:redis) { DiscourseRedis.new }
|
let(:redis) { DiscourseRedis.new }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user