这是一个很长的问题,所以请提前道歉。我们使用Redis来跟踪每家公司的客户状态。跟踪客户状态是因为我们的客户在我们网站的标签中按州分开。
每当保存客户实例(after_save)时,我们都会查看已更改的内容并增加/减少相关计数。出于某种原因,计数很频繁,我们无法弄清楚原因。我们编写测试并验证所有逻辑看起来都正确。
有一点需要注意的是,我们经常使用sidekiq进行后台处理,不确定是否会影响它。
我们正在计算3个计数:my_customers_length(具有user_id的客户,这意味着他们被“分配给”该用户),all_customers_length(“开放”或“已分配”的客户,打开意味着aasm_state ='open'并且他们没有user_id)和open_customers_length(aasm_state ='open'并且没有user_id的客户)。
这是递增/递减Redis计数器的逻辑(customer.rb中的after_save):
def reset_stats
if user_id_changed?
if user_id_was == nil # open => assigned
# update company and user
user.redis_increment_my_customers_length
company.redis_decrement_open_customers_length
elsif user_id_was != nil && user_id != nil # assigned => assigned
# update users (assigner and assignee)
user_was = User.find(user_id_was)
user.redis_increment_my_customers_length
user_was.redis_decrement_my_customers_length
elsif user_id_was != nil && user_id == nil # assigned => closed
# update company and user
user_was = User.find(user_id_was)
user_was.redis_decrement_my_customers_length
company.redis_decrement_all_customers_length
end
else
if aasm_state_was == 'closed' && aasm_state == 'open' # closed => open
# update company
company.redis_increment_all_customers_length
company.redis_increment_open_customers_length
elsif aasm_state_was == 'open' && aasm_state == 'closed' # open => closed
# update company
company.redis_decrement_all_customers_length
company.redis_decrement_open_customers_length
end
end
end
以下是redis函数:
user.rb:
def redis_length_key
"my_customers_length_for_#{id}"
end
def set_my_customers_length(l)
RED.set(redis_length_key, l)
l
end
def redis_increment_my_customers_length
set_my_customers_length(my_customers_length.to_i + 1)
end
def redis_decrement_my_customers_length
set_my_customers_length(my_customers_length.to_i - 1)
end
和company.rb:
def open_customers
customers.open
end
def redis_open_length_key
"open_customers_length_for_#{id}"
end
def set_open_customers_length(l)
RED.set(redis_open_length_key, l)
l
end
def redis_increment_open_customers_length
set_open_customers_length(open_customers_length.to_i + 1)
end
def redis_decrement_open_customers_length
set_open_customers_length(open_customers_length.to_i - 1)
end
def open_customers_length(reset = false)
l = RED.get(redis_open_length_key)
if l.present? && reset == false && l.to_i >=0
l
else
set_open_customers_length(open_customers.length)
end
end
def redis_all_length_key
"all_customers_length_for_#{id}"
end
def set_all_customers_length(l)
RED.set(redis_all_length_key, l)
l
end
def all_customers_length
RED.get(redis_all_length_key)
end
def redis_increment_all_customers_length
set_all_customers_length(all_customers_length.to_i + 1)
end
def redis_decrement_all_customers_length
set_all_customers_length(open_or_claimed_customers_length.to_i - 1)
end
def open_or_claimed_customers_length(reset = false)
l = RED.get(redis_all_length_key)
if l.present? && reset == false && l.to_i >=0
l
else
set_all_customers_length(open_or_claimed_customers.length)
end
end
def open_or_claimed_customers
customers.open_or_claimed
end
我们注意到,通过点击数据库,Redis中的“开放”客户数量总是小于实际数量。对于“我的客户数量”和“所有客户数量”,Redis通常较低但并非总是如此。
我们的逻辑错了吗?我们错过了什么吗?这可能是Redis问题吗? Sidekiq问题?
答案 0 :(得分:3)
你的Redis逻辑不是原子的,它在redis_increment_open_customers_length
中有一个巨大的竞争条件。使用INCR
命令代替GET
+ SET
。