似乎无法保持redis计数器的正确性

时间:2015-06-29 23:37:51

标签: ruby-on-rails redis sidekiq

我有一些计数器存储在REDIS中,可以根据customer.rb中的状态更改进行更新。我需要存储的东西是:

1)与用户关联的客户数量(用户has_many客户) 2)状态为(使用aasm_state)'open'或'声明'的客户数 3)状态为(使用aasm_state)'open

的客户数

每当客户的状态发生变化时,我都会相应地增加/减少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

我正在努力实现的目标是否有更好的模式?这非常令人沮丧,因为一段时间后,计数似乎总是变得不正确。请帮忙!

2 个答案:

答案 0 :(得分:3)

您在致电RED.set(redis_open_length_key, l)和致电时间val strings = List("hi","I","am","here") //this list is a stream of words from Twitter val mystrings = strings.filter(word => !word.contains("I" || "sam") // I need filter out certain stop words 之间存在竞争条件。

  1. 两个进程开始。
  2. 当第一次调用两个进程时,
  3. my_customers_length为5。
  4. 第一个进程进行第二次调用并将Redis设置为6.
  5. 第二个进程进行第二次调用并再次将Redis设置为6。
  6. Redis值实际上应为7。
  7. 考虑使用Redis的INCR和DECR函数以原子方式更新值。

答案 1 :(得分:0)

你的竞争条件如下:

RED.get(redis_all_length_key) ? RED.incr(redis_all_length_key) : all_customers_length

在读取和写入Redis之间不能做任何逻辑。