似乎无法保持 redis 计数器正确
Can't seem to keep a redis counter correct
我有一些计数器存储在 REDIS 中,这些计数器在 customer.rb 中的状态变化时得到更新。我需要存储的东西是:
1) 与用户关联的客户数(用户 has_many 客户)
2) 状态为(使用 aasm_state)'open' 或 'claimed' 的客户数
3) 状态为(using aasm_state)'open
的客户数
每当客户的状态发生变化时,我都会 increment/decrement Redis 进行相应的计数。但是,无论我尝试过什么,计数似乎总是在一段时间后消失。
我正在使用 Sidekiq,但我认为这不是并发问题,因为 REDIS 不应该受到并发问题的影响,对吗?
这是我的计数更新方法:
def reset_stats
if aasm_state_was == 'open' && aasm_state == 'claimed' # open => assigned
# update company and user
user.redis_increment_my_customers_length
company.redis_decrement_open_customers_length
elsif user_id_changed? && aasm_state_was == 'claimed' && aasm_state == 'claimed' # 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 aasm_state_was == 'claimed' && aasm_state == 'closed' # 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
elsif aasm_state_was == 'closed' && aasm_state == 'claimed' # closed => assigned
# update company and user
user.redis_increment_my_customers_length
company.redis_increment_all_customers_length
elsif 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
并在 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.to_i
end
def redis_increment_my_customers_length
RED.get(redis_length_key) ? RED.incr(redis_length_key) : my_customers_length
end
def redis_decrement_my_customers_length
RED.get(redis_length_key) ? RED.decr(redis_length_key) : my_customers_length
end
def my_customers_length
if l = RED.get(redis_length_key)
l.to_i
else
set_my_customers_length(my_customers.length)
end
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.to_i
end
def redis_increment_open_customers_length
RED.get(redis_open_length_key) ? RED.incr(redis_open_length_key) : open_customers_length
end
def redis_decrement_open_customers_length
RED.get(redis_open_length_key) ? RED.decr(redis_open_length_key) : open_customers_length
end
def open_customers_length
if l = RED.get(redis_open_length_key)
return l.to_i
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 redis_increment_all_customers_length
RED.get(redis_all_length_key) ? RED.incr(redis_all_length_key) : all_customers_length
end
def redis_decrement_all_customers_length
RED.get(redis_all_length_key) ? RED.decr(redis_all_length_key) : all_customers_length
end
def all_customers_length
if l = RED.get(redis_all_length_key)
l.to_i
else
set_all_customers_length(open_or_claimed_customers.length)
end
end
def open_or_claimed_customers
customers.open_or_claimed
end
是否有更好的模式来实现我想要完成的目标?这非常令人沮丧,因为一段时间后计数似乎总是不正确。请帮忙!
您在调用 set_my_customers_length(my_customers_length + 1)
和调用 RED.set(redis_open_length_key, l)
之间存在竞争条件。
- 两个进程启动。
当第一次调用两个进程时,- my_customers_length 为 5。
- 第一个进程进行第二次调用并将 Redis 设置为 6。
- 第二个进程进行第二次调用并再次将 Redis 设置为 6。
- Redis 值实际上应该是 7。
考虑使用 Redis 的 INCR 和 DECR 函数自动更新值。
你这里有一个竞争条件:
RED.get(redis_all_length_key) ? RED.incr(redis_all_length_key) : all_customers_length
你不能在读取和写入 Redis 之间做任何逻辑。
我有一些计数器存储在 REDIS 中,这些计数器在 customer.rb 中的状态变化时得到更新。我需要存储的东西是:
1) 与用户关联的客户数(用户 has_many 客户) 2) 状态为(使用 aasm_state)'open' 或 'claimed' 的客户数 3) 状态为(using aasm_state)'open
的客户数每当客户的状态发生变化时,我都会 increment/decrement Redis 进行相应的计数。但是,无论我尝试过什么,计数似乎总是在一段时间后消失。
我正在使用 Sidekiq,但我认为这不是并发问题,因为 REDIS 不应该受到并发问题的影响,对吗?
这是我的计数更新方法:
def reset_stats
if aasm_state_was == 'open' && aasm_state == 'claimed' # open => assigned
# update company and user
user.redis_increment_my_customers_length
company.redis_decrement_open_customers_length
elsif user_id_changed? && aasm_state_was == 'claimed' && aasm_state == 'claimed' # 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 aasm_state_was == 'claimed' && aasm_state == 'closed' # 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
elsif aasm_state_was == 'closed' && aasm_state == 'claimed' # closed => assigned
# update company and user
user.redis_increment_my_customers_length
company.redis_increment_all_customers_length
elsif 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
并在 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.to_i
end
def redis_increment_my_customers_length
RED.get(redis_length_key) ? RED.incr(redis_length_key) : my_customers_length
end
def redis_decrement_my_customers_length
RED.get(redis_length_key) ? RED.decr(redis_length_key) : my_customers_length
end
def my_customers_length
if l = RED.get(redis_length_key)
l.to_i
else
set_my_customers_length(my_customers.length)
end
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.to_i
end
def redis_increment_open_customers_length
RED.get(redis_open_length_key) ? RED.incr(redis_open_length_key) : open_customers_length
end
def redis_decrement_open_customers_length
RED.get(redis_open_length_key) ? RED.decr(redis_open_length_key) : open_customers_length
end
def open_customers_length
if l = RED.get(redis_open_length_key)
return l.to_i
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 redis_increment_all_customers_length
RED.get(redis_all_length_key) ? RED.incr(redis_all_length_key) : all_customers_length
end
def redis_decrement_all_customers_length
RED.get(redis_all_length_key) ? RED.decr(redis_all_length_key) : all_customers_length
end
def all_customers_length
if l = RED.get(redis_all_length_key)
l.to_i
else
set_all_customers_length(open_or_claimed_customers.length)
end
end
def open_or_claimed_customers
customers.open_or_claimed
end
是否有更好的模式来实现我想要完成的目标?这非常令人沮丧,因为一段时间后计数似乎总是不正确。请帮忙!
您在调用 set_my_customers_length(my_customers_length + 1)
和调用 RED.set(redis_open_length_key, l)
之间存在竞争条件。
- 两个进程启动。 当第一次调用两个进程时,
- my_customers_length 为 5。
- 第一个进程进行第二次调用并将 Redis 设置为 6。
- 第二个进程进行第二次调用并再次将 Redis 设置为 6。
- Redis 值实际上应该是 7。
考虑使用 Redis 的 INCR 和 DECR 函数自动更新值。
你这里有一个竞争条件:
RED.get(redis_all_length_key) ? RED.incr(redis_all_length_key) : all_customers_length
你不能在读取和写入 Redis 之间做任何逻辑。