def incr_site_stats engine, stats # Increment the site:stats counters (in redis). We use 'created_at' # to checkpoint the time we created these stats redis.multi do redis.hsetnx 'site:stats', 'created_at', Time.now.utc.to_string stats.each_pair do |k, v| redis.hincrby 'site:stats', k, v.to_i end end # Start the transaction by doing optimistic locking with watch redis.watch 'site:stats' site_stats = redis.hgetall 'site:stats' created_at = Time.parse_utc site_stats['created_at'] elapsed = Time.now.utc.to_i - created_at.utc.to_i # Save the stats in CouchDB every 5 minutes if elapsed > 300 ok = redis.multi { redis.del 'site:stats' } # If the del operation fails, then there was a conflict and someone else # updated the site:stats hash. Otherwise, increment the counters in CouchDB if ok db.update_doc 'stats' do |_doc| site_stats.each_pair do |k, v| next if k == 'created_at' _doc[k] ||= 0 _doc[k] += v.to_i end _doc end end else redis.unwatch end end