2015-06-29 26 views
0

我有幾個計數器存儲在REDIS中,這些計數器在customer.rb中的狀態更改時得到更新。我需要存儲的事情是:似乎無法保持redis計數器正確

1)與用戶相關聯的客戶的計數(用戶的has_many客戶) 2)具有(使用aasm_state)「開」或「權利」 3的狀態的客戶計)具有(使用aasm_state)狀態的客戶數「打開

每當客戶狀態發生變化時,我相應地增加/減少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 

有沒有更好的模式什麼我正在努力完成?這一直非常令人沮喪,因爲這些計數在一段時間後似乎總是不正確。請幫忙!

回答

3

在您撥打電話號碼set_my_customers_length(my_customers_length + 1)和撥打電話RED.set(redis_open_length_key, l)之間的時間段內,您會遇到競爭狀況。

  1. 兩個過程開始。
  2. my_customers_length在第一次調用這兩個進程時爲5。
  3. 第一個進程進行第二次調用,並將Redis設置爲6.
  4. 第二個進程進行第二次調用,並將Redis再次設置爲6。
  5. Redis的價值其實應該是7

考慮使用Redis的INCR與DECR功能原子更新值。

+0

這是一個很好的建議,謝謝。我修復了我的代碼並部署了一個小時後......計數再次減1。我正在用EDIT中提供的方法隨機檢查計數。還有什麼可以繼續嗎?我部署時計數可能會關閉,所以我重置它們並很快再次檢查。(更新我的原始問題與您的更新) –

+0

也,如果有幫助,最新的重置顯示,redis計數是+1大於實際計數 –

+0

沒有運氣...計數仍然關閉 –

0

你有一個競爭條件在這裏:

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

你不能做任何邏輯類型B在閱讀和寫信給Redis之前。