relix 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NWRmYWJjZWYxNDM0MmU3OTEzOGE4MzFhODI4Y2RhOWU5ZWY3ZThjMA==
4
+ OGM4Mzc2ZGI4YjZmNTBlYjczMjM4MjA0ZDE3YzM4ZGQ2ZmU1NTg0ZA==
5
5
  data.tar.gz: !binary |-
6
- NzJjMDM1MDdmMzM0ZDgyODU1MmMwN2YxZTA0ODgyYzEzOWRiZDYxZg==
6
+ MWRkMDg2ZTdlOWVmMmY2NWFkNDc1OGRiN2QwNThjZDc5NzQ2MzA2Zg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YTNmNGVjN2U2ZDc5OTQ1Mzk5ZmZkNjlhYjI3MTI3N2RlZjQwZDMyMTJjOTZk
10
- NmEyNWExZmEyNWQ4Y2IyMWIzNWFjMThiMmY1MTgwOWQxMDEzYTk5MTE3MDlj
11
- Mjg3Y2E4M2EwYmUwMDM4YTNkMmQ1N2JlNzYyNGY4ODE4M2I1YzU=
9
+ MTU3YjZmNjYxOTEzODAxN2EyM2M3Nzk5ZWVhYTg5M2IyMGFkZDE0ZGIzMmYz
10
+ NjQyYmQ2MTc3NGU4Zjc1ZmU5ZTE0Yjc5NTNkOGFmN2I0NmYzZGZiZDkxYjYy
11
+ ZjBjYTY4MDlhYmQ5OTVlM2FiZWQzZWJhMTkyYTBmZTE3NjJiNzM=
12
12
  data.tar.gz: !binary |-
13
- Y2YwODAwZTRkNDI5MTZkMTlhZGVhYmRiOWNkMWUxMjQ1M2JmMzUwYjg5YzFh
14
- ZWI5N2YyMjJjZTQ4MmM3ZGRjMjlhMWQzNjQ2Zjc0NWYyZmQyNzliZjVkOTFi
15
- ZTk5MWJiZjAyNjE1ZjJiMTNkYzEyZjNjOWQ4YTQ4NTdmNzFjZDI=
13
+ NTdkZjBlMTE2YThjNTkyOTRkMWI4YWQ0ZGI4NTM2ZGY5ZDFlOTVhYTc3OWYw
14
+ MmQ0MmNlMjExZTYyOGU4YjY0ZmJjNGQyNWI1YjMwYmI5N2RlNTI1MDcxN2Fl
15
+ NTEzY2I4NjljZmU1ZTA3ZDhkYzQ2YjZlODcyNTgzYWZlNjFjNjE=
data/HISTORY.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 2.2.0
2
+
3
+ * Add index removal. (ntalbott)
4
+
1
5
  ### 2.1.0
2
6
 
3
7
  * Make IndexSet lazy-retrieve the Redis client. (ntalbott)
data/README.md CHANGED
@@ -296,3 +296,28 @@ Keys take up space, and especially since Redis holds the keyset in memory it can
296
296
  ### Legacy
297
297
 
298
298
  This (eventually to be deprecated and removed) strategy exactly mirrors the keying supported by Relix when first released.
299
+
300
+
301
+ ## Maintenance
302
+
303
+ ### Index removal
304
+
305
+ Sometimes an index is no longer needed, or is being moved or renamed, and you'll want to clean up the old index data including both the index itself as well as the current values that are tracked for that index.
306
+
307
+ To remove an index, first change its declaration like so:
308
+
309
+ class Transaction
310
+ relix do
311
+ obsolete{:multi, :account_key, order: :created_at}
312
+ end
313
+ end
314
+
315
+ This just wraps the original index declaration in an `obsolete` block. Doing this makes it so that the index is no longer updated (though it will still deindex), and also marks the index for removal within Relix.
316
+
317
+ Second, run `destroy_index` on the index:
318
+
319
+ Transaction.relix.destroy_index(:account_key)
320
+
321
+ This will remove all related data from Redis. Note that `destroy_index` is idempotent, so while it's not recommended you run it multiple times (it iterates through the whole primary key space), it won't hurt anything if you do.
322
+
323
+ And finally: just delete the whole `obsolete` line from your relix declaration. The index is now dead - long live the index!
@@ -6,6 +6,7 @@ module Relix
6
6
  @klass = klass
7
7
  @redis_source = redis_source
8
8
  @indexes = Hash.new
9
+ @obsolete_indexes = Hash.new
9
10
  @keyer = Keyer.default_for(@klass) unless parent
10
11
  end
11
12
 
@@ -46,8 +47,63 @@ module Relix
46
47
  end
47
48
 
48
49
  def add_index(index_type, name, options={})
49
- accessor = (options.delete(:on) || name)
50
- @indexes[name.to_s] = Relix.index_types[index_type].new(self, name, accessor, options)
50
+ raise Relix::InvalidIndexError.new("Index #{name} is already declared as obsolete.") if @obsolete_indexes[name.to_s]
51
+
52
+ @indexes[name.to_s] = create_index(self, index_type, name, options)
53
+ end
54
+
55
+ class Obsolater
56
+ def initialize(index_set)
57
+ @index_set = index_set
58
+ end
59
+
60
+ def method_missing(m, *args)
61
+ if Relix.index_types.keys.include?(m.to_sym)
62
+ @index_set.add_obsolete_index(m, *args)
63
+ else
64
+ raise ArgumentError.new("Unknown index type #{m}.")
65
+ end
66
+ end
67
+ end
68
+
69
+ def obsolete(&block)
70
+ raise ArgumentError.new("No block passed.") unless block_given?
71
+
72
+ Obsolater.new(self).instance_eval(&block)
73
+ end
74
+
75
+ def add_obsolete_index(index_type, name, options={})
76
+ raise Relix::InvalidIndexError.new("Primary key indexes cannot be obsoleted.") if(index_type == :primary_key)
77
+ raise Relix::InvalidIndexError.new("Index #{name} is already declared as non-obsolete.") if @indexes[name.to_s]
78
+
79
+ @obsolete_indexes[name.to_s] = create_index(self, index_type, name, options)
80
+ end
81
+
82
+ def destroy_index(name)
83
+ name = name.to_s
84
+ index = @obsolete_indexes[name]
85
+ raise MissingIndexError.new("No obsolete index found for #{name}.") unless index
86
+ raise InvalidIndexError.new("Indexes built on immutable attributes cannot be destroyed.") if index.attribute_immutable?
87
+
88
+ lookup.each do |pk|
89
+ handle_concurrent_modifications(pk) do
90
+ current_values_name = current_values_name(pk)
91
+ redis.watch current_values_name
92
+ current_values = redis.hgetall(current_values_name)
93
+
94
+ old_value = current_values[name]
95
+
96
+ ((watch = index.watch(old_value)) && !watch.empty? && redis.watch(*watch))
97
+ ops = []
98
+ ops << proc{ index.destroy(redis, pk, old_value) } if index.respond_to?(:destroy)
99
+ ops << proc{ redis.hdel current_values_name, name }
100
+ ops
101
+ end
102
+ end
103
+
104
+ if index.respond_to?(:destroy_all)
105
+ index.destroy_all(redis)
106
+ end
51
107
  end
52
108
 
53
109
  def indexes
@@ -114,7 +170,7 @@ module Relix
114
170
  redis.watch current_values_name
115
171
  current_values = redis.hgetall(current_values_name)
116
172
 
117
- full_index_list.map do |name, index|
173
+ full_index_list(:including_obsolete).map do |name, index|
118
174
  old_value = if index.attribute_immutable?
119
175
  index.read_normalized(object)
120
176
  else
@@ -133,7 +189,7 @@ module Relix
133
189
  redis.watch current_values_name
134
190
  current_values = redis.hgetall(current_values_name)
135
191
 
136
- full_index_list.map do |name, index|
192
+ full_index_list(:including_obsolete).map do |name, index|
137
193
  old_value = current_values[name]
138
194
 
139
195
  ((watch = index.watch(old_value)) && !watch.empty? && redis.watch(*watch))
@@ -164,12 +220,21 @@ module Relix
164
220
 
165
221
  protected
166
222
 
167
- def full_index_list
168
- (parent ? parent.full_index_list.merge(@indexes) : @indexes)
223
+ def full_index_list(including_obsolete=false)
224
+ list = (parent ? parent.full_index_list.merge(@indexes) : @indexes)
225
+ if including_obsolete
226
+ list = @obsolete_indexes.merge(list)
227
+ end
228
+ list
169
229
  end
170
230
 
171
231
  private
172
232
 
233
+ def create_index(index_set, index_type, name, options)
234
+ accessor = (options.delete(:on) || name)
235
+ Relix.index_types[index_type].new(index_set, name, accessor, options)
236
+ end
237
+
173
238
  def handle_concurrent_modifications(primary_key)
174
239
  retries = 5
175
240
  loop do
@@ -203,4 +268,5 @@ module Relix
203
268
  class MissingPrimaryKeyError < Relix::Error; end
204
269
  class RedisIndexingError < Relix::Error; end
205
270
  class ExceededRetriesForConcurrentWritesError < Relix::Error; end
271
+ class InvalidIndexError < Relix::Error; end
206
272
  end
@@ -20,6 +20,11 @@ module Relix
20
20
  deindex_value(r, old_value) if index_values?
21
21
  end
22
22
 
23
+ def destroy(r, pk, old_value)
24
+ r.del(key_for(old_value))
25
+ r.destroy_values(r) if index_values?
26
+ end
27
+
23
28
  def eq(r, value, options={})
24
29
  r.zrange(key_for(value), *range_from_options(r, options, value))
25
30
  end
@@ -59,6 +64,10 @@ module Relix
59
64
  return "OK"
60
65
  ), [values_key, key_for(old_value)], [old_value]
61
66
  end
67
+
68
+ def destroy_value(r)
69
+ r.del(values_key)
70
+ end
62
71
  end
63
72
  register_index MultiIndex
64
73
  end
@@ -23,6 +23,10 @@ module Relix
23
23
  r.zrem(sorted_set_name, pk)
24
24
  end
25
25
 
26
+ def destroy_all(r)
27
+ r.del(sorted_set_name)
28
+ end
29
+
26
30
  def create_query_clause(redis)
27
31
  QueryClause.new(redis, self)
28
32
  end
@@ -38,6 +38,11 @@ module Relix
38
38
  r.zrem(sorted_set_name, pk)
39
39
  end
40
40
 
41
+ def destroy_all(r)
42
+ r.del(hash_name)
43
+ r.del(sorted_set_name)
44
+ end
45
+
41
46
  def all(r, options={})
42
47
  r.zrange(sorted_set_name, *range_from_options(r, options))
43
48
  end
data/lib/relix/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Relix
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  REDIS_VERSION = "2.6"
4
4
 
5
5
  class Version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relix
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathaniel Talbott
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-03-20 00:00:00.000000000 Z
11
+ date: 2013-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  prerelease: false