redis_counters 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +3 -0
- data/Makefile +14 -0
- data/README.md +320 -0
- data/Rakefile +15 -0
- data/lib/redis_counters.rb +21 -0
- data/lib/redis_counters/base_counter.rb +84 -0
- data/lib/redis_counters/bucket.rb +59 -0
- data/lib/redis_counters/cluster.rb +22 -0
- data/lib/redis_counters/clusterize_and_partitionize.rb +194 -0
- data/lib/redis_counters/hash_counter.rb +70 -0
- data/lib/redis_counters/partition.rb +16 -0
- data/lib/redis_counters/unique_hash_counter.rb +51 -0
- data/lib/redis_counters/unique_values_lists/base.rb +57 -0
- data/lib/redis_counters/unique_values_lists/blocking.rb +167 -0
- data/lib/redis_counters/unique_values_lists/expirable.rb +155 -0
- data/lib/redis_counters/unique_values_lists/non_blocking.rb +91 -0
- data/lib/redis_counters/version.rb +3 -0
- data/redis_counters.gemspec +31 -0
- data/spec/redis_counters/base_spec.rb +29 -0
- data/spec/redis_counters/hash_counter_spec.rb +462 -0
- data/spec/redis_counters/unique_hash_counter_spec.rb +83 -0
- data/spec/redis_counters/unique_values_lists/blocking_spec.rb +94 -0
- data/spec/redis_counters/unique_values_lists/expirable_spec.rb +6 -0
- data/spec/redis_counters/unique_values_lists/non_blicking_spec.rb +6 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/unique_values_lists/common.rb +563 -0
- data/spec/support/unique_values_lists/expirable.rb +162 -0
- data/spec/support/unique_values_lists/set.rb +119 -0
- data/tasks/audit.rake +6 -0
- data/tasks/cane.rake +12 -0
- data/tasks/changelog.rake +7 -0
- data/tasks/coverage.rake +21 -0
- data/tasks/support.rb +24 -0
- metadata +242 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'redis_counters/hash_counter'
|
3
|
+
|
4
|
+
module RedisCounters
|
5
|
+
|
6
|
+
# HashCounter, с возможностью подсчета только уникальных значений.
|
7
|
+
|
8
|
+
class UniqueHashCounter < HashCounter
|
9
|
+
UNIQUE_LIST_POSTFIX = 'uq'.freeze
|
10
|
+
|
11
|
+
UNIQUE_LIST_POSTFIX_DELIMITER = '_'.freeze
|
12
|
+
|
13
|
+
attr_reader :unique_values_list
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def process_value
|
18
|
+
unique_values_list.add(params) { super }
|
19
|
+
end
|
20
|
+
|
21
|
+
def init
|
22
|
+
super
|
23
|
+
@unique_values_list = unique_values_list_class.new(
|
24
|
+
redis,
|
25
|
+
unique_values_list_options
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def unique_values_list_options
|
30
|
+
options.fetch(:unique_list).merge!(:counter_name => unique_values_list_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def unique_values_list_name
|
34
|
+
[counter_name, UNIQUE_LIST_POSTFIX].join(unique_list_postfix_delimiter)
|
35
|
+
end
|
36
|
+
|
37
|
+
def unique_values_list_class
|
38
|
+
unique_values_list_options.fetch(:list_class).to_s.constantize
|
39
|
+
end
|
40
|
+
|
41
|
+
def unique_list_postfix_delimiter
|
42
|
+
@unique_list_postfix_delimiter ||= options.fetch(:unique_list_postfix_delimiter, UNIQUE_LIST_POSTFIX_DELIMITER)
|
43
|
+
end
|
44
|
+
|
45
|
+
def partitions_keys(params = {})
|
46
|
+
# удаляем из списка партиций, ключи в которых хранятся списки уникальных значений
|
47
|
+
super.delete_if { |partition| partition.start_with?(unique_values_list_name) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'redis_counters/base_counter'
|
3
|
+
require 'redis_counters/clusterize_and_partitionize'
|
4
|
+
|
5
|
+
module RedisCounters
|
6
|
+
module UniqueValuesLists
|
7
|
+
|
8
|
+
# Базовый класс списка уникальных значений,
|
9
|
+
# с возможностью кластеризации и партиционирования.
|
10
|
+
|
11
|
+
class Base < RedisCounters::BaseCounter
|
12
|
+
include RedisCounters::ClusterizeAndPartitionize
|
13
|
+
|
14
|
+
alias_method :add, :process
|
15
|
+
alias_method :<<, :process
|
16
|
+
|
17
|
+
# Public: Проверяет существует ли заданное значение.
|
18
|
+
#
|
19
|
+
# params - Hash - параметры кластера и значения.
|
20
|
+
#
|
21
|
+
# Returns Boolean.
|
22
|
+
#
|
23
|
+
def has_value?(params)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def value
|
30
|
+
value_params = value_keys.map { |key| params.fetch(key) }
|
31
|
+
value_params.join(value_delimiter)
|
32
|
+
end
|
33
|
+
|
34
|
+
def value_keys
|
35
|
+
@value_keys ||= Array.wrap(options.fetch(:value_keys))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Protected: Возвращает данные партиции в виде массива хешей.
|
39
|
+
#
|
40
|
+
# Каждый элемент массива, представлен в виде хеша, содержащего все параметры уникального значения.
|
41
|
+
#
|
42
|
+
# cluster - Array - листовой кластер - массив параметров однозначно идентифицирующий кластер.
|
43
|
+
# partition - Array - листовая партиция - массив параметров однозначно идентифицирующий партицию.
|
44
|
+
#
|
45
|
+
# Returns Array of WithIndifferentAccess.
|
46
|
+
#
|
47
|
+
def partition_data(cluster, partition)
|
48
|
+
keys = value_keys
|
49
|
+
redis.smembers(key(partition, cluster)).inject(Array.new) do |result, (key, value)|
|
50
|
+
values = key.split(value_delimiter, -1) << value.to_i
|
51
|
+
result << Hash[keys.zip(values)].with_indifferent_access
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'redis_counters/unique_values_lists/base'
|
3
|
+
|
4
|
+
module RedisCounters
|
5
|
+
module UniqueValuesLists
|
6
|
+
|
7
|
+
# Список уникальных значений, на основе механизма оптимистических блокировок.
|
8
|
+
#
|
9
|
+
# смотри Optimistic locking using check-and-set:
|
10
|
+
# http://redis.io/topics/transactions
|
11
|
+
#
|
12
|
+
# Особенности:
|
13
|
+
# * Значения сохраняет в партициях;
|
14
|
+
# * Ведет список партиций;
|
15
|
+
# * Полностью транзакционен.
|
16
|
+
|
17
|
+
class Blocking < Base
|
18
|
+
PARTITIONS_LIST_POSTFIX = :partitions
|
19
|
+
|
20
|
+
# Public: Проверяет существует ли заданное значение.
|
21
|
+
#
|
22
|
+
# params - Hash - параметры кластера и значения.
|
23
|
+
#
|
24
|
+
# Returns Boolean.
|
25
|
+
#
|
26
|
+
def has_value?(params)
|
27
|
+
set_params(params)
|
28
|
+
reset_partitions_cache
|
29
|
+
value_already_exists?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Нетранзакционно удаляет данные конкретной конечной партиции.
|
33
|
+
#
|
34
|
+
# params - Hash - хеш параметров, определяющий кластер и партицию.
|
35
|
+
# write_session - Redis - соединение с Redis, в рамках которого
|
36
|
+
# будет производится удаление (опционально).
|
37
|
+
# По умолчанию - основное соединение счетчика.
|
38
|
+
#
|
39
|
+
# Если передан блок, то вызывает блок, после удаления всех данных, в транзакции.
|
40
|
+
#
|
41
|
+
# Returns Nothing.
|
42
|
+
#
|
43
|
+
def delete_partition_direct!(params = {}, write_session = redis)
|
44
|
+
super(params, write_session)
|
45
|
+
|
46
|
+
# удаляем партицию из списка
|
47
|
+
return unless use_partitions?
|
48
|
+
|
49
|
+
cluster = ::RedisCounters::Cluster.new(self, params).params
|
50
|
+
partition = ::RedisCounters::Partition.new(self, params).params(:only_leaf => true)
|
51
|
+
|
52
|
+
partition = partition.flatten.join(key_delimiter)
|
53
|
+
write_session.lrem(partitions_list_key(cluster), 0, partition)
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def key(partition = partition_params, cluster = cluster_params)
|
59
|
+
return super if use_partitions?
|
60
|
+
|
61
|
+
raise 'Array required' if partition && !partition.is_a?(Array)
|
62
|
+
raise 'Array required' if cluster && !cluster.is_a?(Array)
|
63
|
+
|
64
|
+
[counter_name, cluster, partition].flatten.compact.join(key_delimiter)
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_value
|
68
|
+
loop do
|
69
|
+
reset_partitions_cache
|
70
|
+
|
71
|
+
watch_partitions_list
|
72
|
+
watch_all_partitions
|
73
|
+
|
74
|
+
if value_already_exists?
|
75
|
+
redis.unwatch
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
result = transaction do
|
80
|
+
add_value
|
81
|
+
add_partition
|
82
|
+
yield redis if block_given?
|
83
|
+
end
|
84
|
+
|
85
|
+
return true if result.present?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def reset_partitions_cache
|
90
|
+
@partitions = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def watch_partitions_list
|
94
|
+
return unless use_partitions?
|
95
|
+
redis.watch(partitions_list_key)
|
96
|
+
end
|
97
|
+
|
98
|
+
def watch_all_partitions
|
99
|
+
all_partitions.each do |partition|
|
100
|
+
redis.watch(key(partition))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def value_already_exists?
|
105
|
+
all_partitions.reverse.any? do |partition|
|
106
|
+
redis.sismember(key(partition), value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_value
|
111
|
+
redis.sadd(key, value)
|
112
|
+
end
|
113
|
+
|
114
|
+
def all_partitions(cluster = cluster_params)
|
115
|
+
return @partitions unless @partitions.nil?
|
116
|
+
return (@partitions = [nil]) unless use_partitions?
|
117
|
+
|
118
|
+
@partitions = redis.lrange(partitions_list_key(cluster), 0, -1)
|
119
|
+
@partitions = @partitions.map do |partition|
|
120
|
+
partition.split(key_delimiter, -1)
|
121
|
+
end.delete_if(&:empty?)
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_partition
|
125
|
+
return unless use_partitions?
|
126
|
+
return unless new_partition?
|
127
|
+
redis.rpush(partitions_list_key, current_partition)
|
128
|
+
end
|
129
|
+
|
130
|
+
def partitions_list_key(cluster = cluster_params)
|
131
|
+
raise 'Array required' if cluster && !cluster.is_a?(Array)
|
132
|
+
|
133
|
+
[counter_name, cluster, PARTITIONS_LIST_POSTFIX].flatten.join(key_delimiter)
|
134
|
+
end
|
135
|
+
|
136
|
+
def current_partition
|
137
|
+
partition_params.flatten.join(key_delimiter)
|
138
|
+
end
|
139
|
+
|
140
|
+
def new_partition?
|
141
|
+
!all_partitions.include?(current_partition.split(key_delimiter))
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# Protected: Возвращает массив листовых партиций в виде ключей.
|
146
|
+
#
|
147
|
+
# Если кластер не указан и нет кластеризации в счетчике, то возвращает все партиции.
|
148
|
+
# Если партиция не указана, возвращает все партиции кластера (все партиции, если нет кластеризации).
|
149
|
+
#
|
150
|
+
# params - Hash - хеш параметров, определяющий кластер и партицию.
|
151
|
+
#
|
152
|
+
# Returns Array of Hash.
|
153
|
+
#
|
154
|
+
def partitions_keys(params = {})
|
155
|
+
reset_partitions_cache
|
156
|
+
|
157
|
+
cluster = ::RedisCounters::Cluster.new(self, params).params
|
158
|
+
partition = ::RedisCounters::Partition.new(self, params).params
|
159
|
+
|
160
|
+
partitions_keys = all_partitions(cluster).map { |part| key(part, cluster) }
|
161
|
+
|
162
|
+
fuzzy_pattern = key(partition, cluster)
|
163
|
+
partitions_keys.select { |part| part.start_with?(fuzzy_pattern) }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'redis_counters/unique_values_lists/blocking'
|
4
|
+
require 'active_support/core_ext/module/aliasing'
|
5
|
+
|
6
|
+
module RedisCounters
|
7
|
+
module UniqueValuesLists
|
8
|
+
|
9
|
+
# Список уникальных значений, с возможностью expire отдельных элементов.
|
10
|
+
#
|
11
|
+
# На основе сортированного множества.
|
12
|
+
# http://redis4you.com/code.php?id=010
|
13
|
+
#
|
14
|
+
# На основе механизма оптимистических блокировок.
|
15
|
+
# смотри Optimistic locking using check-and-set:
|
16
|
+
# http://redis.io/topics/transactions
|
17
|
+
#
|
18
|
+
# Особенности:
|
19
|
+
# * Expire - таймаут, можно установить как на уровне счетчика,
|
20
|
+
# так и на уровне отдельного занчения;
|
21
|
+
# * Очистка возможна как в автоматическогом режиме так в и ручном;
|
22
|
+
# * Значения сохраняет в партициях;
|
23
|
+
# * Ведет список партиций;
|
24
|
+
# * Полностью транзакционен.
|
25
|
+
#
|
26
|
+
# Пример:
|
27
|
+
#
|
28
|
+
# counter = RedisCounters::UniqueValuesLists::Expirable.new(redis,
|
29
|
+
# :counter_name => :sessions,
|
30
|
+
# :value_keys => [:session_id],
|
31
|
+
# :expire => 10.minutes
|
32
|
+
# )
|
33
|
+
#
|
34
|
+
# counter << session_id: 1
|
35
|
+
# counter << session_id: 2
|
36
|
+
# counter << session_id: 3, expire: :never
|
37
|
+
#
|
38
|
+
# counter.data
|
39
|
+
# > [{session_id: 1}, {session_id: 2}, {session_id: 3}]
|
40
|
+
#
|
41
|
+
# # after 10 minutes
|
42
|
+
#
|
43
|
+
# counter.data
|
44
|
+
# > [{session_id: 3}]
|
45
|
+
#
|
46
|
+
# counter.has_value?(session_id: 1)
|
47
|
+
# false
|
48
|
+
|
49
|
+
class Expirable < Blocking
|
50
|
+
DEFAULT_AUTO_CLEAN_EXPIRED = true
|
51
|
+
DEFAULT_VALUE_TIMEOUT = :never
|
52
|
+
|
53
|
+
NEVER_EXPIRE_TIMESTAMP = 0
|
54
|
+
|
55
|
+
# Public: Производит принудительную очистку expired - значений.
|
56
|
+
#
|
57
|
+
# cluster - Hash - параметры кластера, если используется кластеризация.
|
58
|
+
#
|
59
|
+
# Returns nothing.
|
60
|
+
#
|
61
|
+
def clean_expired(cluster = {})
|
62
|
+
set_params(cluster)
|
63
|
+
internal_clean_expired
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
def add_value
|
69
|
+
redis.zadd(key, value_expire_timestamp, value)
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset_partitions_cache
|
73
|
+
super
|
74
|
+
internal_clean_expired if auto_clean_expired?
|
75
|
+
end
|
76
|
+
|
77
|
+
alias_method :clean, :reset_partitions_cache
|
78
|
+
|
79
|
+
def current_timestamp
|
80
|
+
Time.now.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
def value_already_exists?
|
84
|
+
all_partitions.reverse.any? do |partition|
|
85
|
+
redis.zrank(key(partition), value).present?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def internal_clean_expired
|
90
|
+
all_partitions.each do |partition|
|
91
|
+
redis.zremrangebyscore(key(partition), "(#{NEVER_EXPIRE_TIMESTAMP}", current_timestamp)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def value_expire_timestamp
|
96
|
+
timeout = params[:expire] || default_value_expire
|
97
|
+
|
98
|
+
case timeout
|
99
|
+
when Symbol
|
100
|
+
NEVER_EXPIRE_TIMESTAMP
|
101
|
+
else
|
102
|
+
current_timestamp + timeout.to_i
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def default_value_expire
|
107
|
+
@default_value_expire ||= options[:expire].try(:seconds) || DEFAULT_VALUE_TIMEOUT
|
108
|
+
end
|
109
|
+
|
110
|
+
def auto_clean_expired?
|
111
|
+
@auto_clean_expired ||= options.fetch(:clean_expired, DEFAULT_AUTO_CLEAN_EXPIRED)
|
112
|
+
end
|
113
|
+
|
114
|
+
def partitions_with_clean(params = {})
|
115
|
+
clean_empty_partitions(params)
|
116
|
+
partitions_without_clean(params)
|
117
|
+
end
|
118
|
+
|
119
|
+
alias_method_chain :partitions, :clean
|
120
|
+
|
121
|
+
# Protected: Производит очистку expired - значений и пустых партиций.
|
122
|
+
#
|
123
|
+
# params - Hash - параметры кластера, если используется кластеризация.
|
124
|
+
#
|
125
|
+
# Returns nothing.
|
126
|
+
#
|
127
|
+
def clean_empty_partitions(params)
|
128
|
+
set_params(params)
|
129
|
+
clean
|
130
|
+
|
131
|
+
partitions_without_clean(params).each do |partition|
|
132
|
+
next if redis.zcard(key(partition.values)).nonzero?
|
133
|
+
delete_partition_direct!(params.merge(partition))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Protected: Возвращает данные партиции в виде массива хешей.
|
138
|
+
#
|
139
|
+
# Каждый элемент массива, представлен в виде хеша, содержащего все параметры уникального значения.
|
140
|
+
#
|
141
|
+
# cluster - Array - листовой кластер - массив параметров однозначно идентифицирующий кластер.
|
142
|
+
# partition - Array - листовая партиция - массив параметров однозначно идентифицирующий партицию.
|
143
|
+
#
|
144
|
+
# Returns Array of WithIndifferentAccess.
|
145
|
+
#
|
146
|
+
def partition_data(cluster, partition)
|
147
|
+
keys = value_keys
|
148
|
+
redis.zrangebyscore(key(partition, cluster), '-inf', '+inf').inject(Array.new) do |result, (key, value)|
|
149
|
+
values = key.split(value_delimiter, -1) << value.to_i
|
150
|
+
result << Hash[keys.zip(values)].with_indifferent_access
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'redis_counters/unique_values_lists/base'
|
3
|
+
|
4
|
+
module RedisCounters
|
5
|
+
module UniqueValuesLists
|
6
|
+
|
7
|
+
# Список уникальных значений, на основе не блокирующего алгоритма.
|
8
|
+
#
|
9
|
+
# Особенности:
|
10
|
+
# * 2-х кратный расход памяти в случае использования партиций;
|
11
|
+
# * Не ведет список партиций;
|
12
|
+
# * Не транзакционен;
|
13
|
+
# * Методы delete_partitions! и delete_partition_direct!, удаляют только дублирующие партиции,
|
14
|
+
# но не удаляют данные из основной партиции.
|
15
|
+
# Для удаления основной партиции необходимо вызвать delete_main_partition!
|
16
|
+
# или воспользоваться методами delete_all! или delete_all_direct!,
|
17
|
+
# для удаления всех партиций кластера включая основную.
|
18
|
+
|
19
|
+
class NonBlocking < Base
|
20
|
+
|
21
|
+
# Public: Проверяет существует ли заданное значение.
|
22
|
+
#
|
23
|
+
# params - Hash - параметры кластера и значения.
|
24
|
+
#
|
25
|
+
# Returns Boolean.
|
26
|
+
#
|
27
|
+
def has_value?(params)
|
28
|
+
set_params(params)
|
29
|
+
redis.sismember(main_partition_key, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Нетранзакционно удаляет все данные счетчика в кластере, включая основную партицию.
|
33
|
+
# Если кластеризация не используется, то удаляет все данные.
|
34
|
+
#
|
35
|
+
# cluster - Hash - хеш параметров, определяющих кластер.
|
36
|
+
# write_session - Redis - соединение с Redis, в рамках которого
|
37
|
+
# будет производится удаление (опционально).
|
38
|
+
# По умолчанию - основное соединение счетчика.
|
39
|
+
#
|
40
|
+
# Returns Nothing.
|
41
|
+
#
|
42
|
+
def delete_all_direct!(cluster, write_session = redis, parts = partitions(cluster))
|
43
|
+
super(cluster, write_session, parts)
|
44
|
+
delete_main_partition!(cluster, write_session)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Удаляет основную партицию.
|
48
|
+
#
|
49
|
+
# cluster - Hash - хеш параметров, определяющих кластер.
|
50
|
+
# Опционально, если кластеризация не используется.
|
51
|
+
# write_session - Redis - соединение с Redis, в рамках которого
|
52
|
+
# будет производится удаление (опционально).
|
53
|
+
# По умолчанию - основное соединение счетчика.
|
54
|
+
#
|
55
|
+
# Returns Nothing.
|
56
|
+
#
|
57
|
+
def delete_main_partition!(cluster = {}, write_session = redis)
|
58
|
+
cluster = ::RedisCounters::Cluster.new(self, cluster).params
|
59
|
+
key = key(main_partition, cluster)
|
60
|
+
write_session.del(key)
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def process_value
|
66
|
+
return unless add_value
|
67
|
+
yield redis if block_given?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_value
|
72
|
+
return unless redis.sadd(main_partition_key, value)
|
73
|
+
redis.sadd(current_partition_key, value) if use_partitions?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def main_partition_key
|
78
|
+
key(main_partition)
|
79
|
+
end
|
80
|
+
|
81
|
+
def current_partition_key
|
82
|
+
key
|
83
|
+
end
|
84
|
+
|
85
|
+
def main_partition
|
86
|
+
[]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|