Redis计数器一直关闭

时间:2015-05-05 20:14:26

标签: ruby-on-rails redis sidekiq

这是一个很长的问题,所以请提前道歉。我们使用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问题?

1 个答案:

答案 0 :(得分:3)

你的Redis逻辑不是原子的,它在redis_increment_open_customers_length中有一个巨大的竞争条件。使用INCR命令代替GET + SET

http://redis.io/commands/incr