riak-client 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +4 -3
  3. data/.rspec +1 -0
  4. data/Gemfile +1 -0
  5. data/RELEASE_NOTES.md +47 -0
  6. data/Rakefile +0 -1
  7. data/erl_src/riak_kv_test_backend.erl +34 -0
  8. data/lib/riak/client.rb +3 -1
  9. data/lib/riak/client/beefcake/messages.rb +49 -1
  10. data/lib/riak/client/beefcake/object_methods.rb +14 -21
  11. data/lib/riak/client/beefcake_protobuffs_backend.rb +58 -12
  12. data/lib/riak/client/decaying.rb +31 -23
  13. data/lib/riak/client/feature_detection.rb +88 -0
  14. data/lib/riak/client/http_backend.rb +27 -6
  15. data/lib/riak/client/http_backend/configuration.rb +13 -0
  16. data/lib/riak/client/http_backend/object_methods.rb +33 -25
  17. data/lib/riak/client/node.rb +7 -2
  18. data/lib/riak/client/protobuffs_backend.rb +54 -3
  19. data/lib/riak/client/search.rb +2 -2
  20. data/lib/riak/conflict.rb +13 -0
  21. data/lib/riak/locale/en.yml +2 -0
  22. data/lib/riak/map_reduce.rb +1 -1
  23. data/lib/riak/map_reduce/filter_builder.rb +2 -2
  24. data/lib/riak/map_reduce/results.rb +49 -0
  25. data/lib/riak/node/console.rb +17 -16
  26. data/lib/riak/node/generation.rb +9 -0
  27. data/lib/riak/rcontent.rb +168 -0
  28. data/lib/riak/robject.rb +37 -157
  29. data/lib/riak/util/escape.rb +5 -1
  30. data/lib/riak/version.rb +1 -1
  31. data/riak-client.gemspec +37 -5
  32. data/spec/fixtures/multipart-basic-conflict.txt +15 -0
  33. data/spec/fixtures/munchausen.txt +1033 -0
  34. data/spec/integration/riak/cluster_spec.rb +1 -1
  35. data/spec/integration/riak/http_backends_spec.rb +23 -2
  36. data/spec/integration/riak/node_spec.rb +2 -2
  37. data/spec/integration/riak/protobuffs_backends_spec.rb +17 -2
  38. data/spec/integration/riak/test_server_spec.rb +1 -1
  39. data/spec/integration/riak/threading_spec.rb +3 -3
  40. data/spec/riak/beefcake_protobuffs_backend_spec.rb +58 -25
  41. data/spec/riak/escape_spec.rb +3 -0
  42. data/spec/riak/feature_detection_spec.rb +61 -0
  43. data/spec/riak/http_backend/object_methods_spec.rb +4 -13
  44. data/spec/riak/http_backend_spec.rb +6 -5
  45. data/spec/riak/map_reduce_spec.rb +0 -5
  46. data/spec/riak/robject_spec.rb +12 -11
  47. data/spec/spec_helper.rb +3 -1
  48. data/spec/support/riak_test.rb +77 -0
  49. data/spec/support/search_corpus_setup.rb +18 -0
  50. data/spec/support/sometimes.rb +1 -1
  51. data/spec/support/test_server.rb +1 -1
  52. data/spec/support/unified_backend_examples.rb +53 -7
  53. data/spec/support/version_filter.rb +4 -11
  54. metadata +56 -22
  55. data/lib/riak/client/pool.rb +0 -180
  56. data/spec/riak/pool_spec.rb +0 -306
@@ -1,180 +0,0 @@
1
- require 'thread'
2
-
3
- module Riak
4
- class Client
5
- # A re-entrant thread-safe resource pool. Generates new resources on
6
- # demand.
7
- # @private
8
- class Pool
9
- # Raised when a taken element should be deleted from the pool.
10
- class BadResource < RuntimeError
11
- end
12
-
13
- # An element of the pool. Comprises an object with an owning
14
- # thread.
15
- # @private
16
- class Element
17
- attr_accessor :object
18
- attr_accessor :owner
19
- def initialize(object)
20
- @object = object
21
- @owner = owner
22
- end
23
-
24
- # Claims this element of the pool for the current Thread.
25
- def lock
26
- self.owner = Thread.current
27
- end
28
-
29
- # Is this element locked/claimed?
30
- def locked?
31
- not owner.nil?
32
- end
33
-
34
- # Releases this element of the pool from the current Thread.
35
- def unlock
36
- self.owner = nil
37
- end
38
-
39
- # Is this element available for use?
40
- def unlocked?
41
- owner.nil?
42
- end
43
- end
44
-
45
- attr_accessor :pool
46
- attr_accessor :open
47
- attr_accessor :close
48
-
49
- # Open is a callable which returns a new object for the pool. Close is
50
- # called with an object before it is freed.
51
- def initialize(open, close)
52
- @open = open
53
- @close = close
54
- @lock = Mutex.new
55
- @iterator = Mutex.new
56
- @element_released = ConditionVariable.new
57
- @pool = Set.new
58
- end
59
-
60
- # On each element of the pool, calls close(element) and removes it.
61
- # @private
62
- def clear
63
- each_element do |e|
64
- delete_element e
65
- end
66
- end
67
- alias :close :clear
68
-
69
- # Deletes an element of the pool. Calls close on its object.
70
- # Not intendend for external use.
71
- def delete_element(e)
72
- @close.call(e.object)
73
- @lock.synchronize do
74
- @pool.delete e
75
- end
76
- end
77
-
78
- # Locks each element in turn and closes/deletes elements for which the
79
- # object passes the block.
80
- def delete_if
81
- raise ArgumentError, "block required" unless block_given?
82
-
83
- each_element do |e|
84
- if yield e.object
85
- delete_element e
86
- end
87
- end
88
- end
89
-
90
- # Acquire an element of the pool. Yields the object. If all
91
- # elements are claimed, it will create another one.
92
- # @yield [obj] a block that will perform some action with the
93
- # element of the pool
94
- # @yieldparam [Object] resource a resource managed by the pool.
95
- # Locked for the duration of the block
96
- # @param [callable] :filter a callable which receives objects and has
97
- # the opportunity to reject each in turn.
98
- # @param [Object] :default if no resources are available, use this object
99
- # instead of calling #open.
100
- # @private
101
- def take(opts = {})
102
- unless block_given?
103
- raise ArgumentError, "block required"
104
- end
105
-
106
- r = nil
107
- begin
108
- e = nil
109
- @lock.synchronize do
110
- # Find an existing element.
111
- if f = opts[:filter]
112
- e = pool.find { |e| e.unlocked? and f.call(e.object) }
113
- else
114
- e = pool.find { |e| e.unlocked? }
115
- end
116
-
117
- unless e
118
- # No objects were acceptable
119
- resource = opts[:default] || @open.call
120
- e = Element.new(resource)
121
- pool << e
122
- end
123
- e.lock
124
- end
125
-
126
- r = yield e.object
127
- rescue BadResource
128
- delete_element e
129
- raise
130
- ensure
131
- # Unlock
132
- if e
133
- e.unlock
134
- @element_released.signal
135
- end
136
- end
137
- r
138
- end
139
- alias >> take
140
-
141
- # Iterate over a snapshot of the pool. Yielded objects are locked for the
142
- # duration of the block. This may block the current thread until elements
143
- # are released by other threads.
144
- # @private
145
- def each_element
146
- targets = @pool.to_a
147
- unlocked = []
148
-
149
- @iterator.synchronize do
150
- until targets.empty?
151
- @lock.synchronize do
152
- unlocked, targets = targets.partition {|e| e.unlocked? }
153
- unlocked.each {|e| e.lock }
154
- end
155
-
156
- unlocked.each do |e|
157
- begin
158
- yield e
159
- ensure
160
- e.unlock
161
- end
162
- end
163
- @element_released.wait(@iterator) unless targets.empty?
164
- end
165
- end
166
- end
167
-
168
- # As each_element, but yields objects, not wrapper elements.
169
- def each
170
- each_element do |e|
171
- yield e.object
172
- end
173
- end
174
-
175
- def size
176
- @lock.synchronize { @pool.size }
177
- end
178
- end
179
- end
180
- end
@@ -1,306 +0,0 @@
1
- require 'spec_helper'
2
- require 'thread'
3
-
4
- describe Riak::Client::Pool do
5
- describe 'basics' do
6
- subject {
7
- described_class.new(
8
- lambda { [0] },
9
- lambda { |x| }
10
- )
11
- }
12
-
13
- it 'yields a new object' do
14
- subject.take do |x|
15
- x.should == [0]
16
- end
17
- end
18
-
19
- it 'retains a single object for serial access' do
20
- n = 100
21
- n.times do |i|
22
- subject.take do |x|
23
- x.should == [i]
24
- x[0] += 1
25
- end
26
- end
27
- subject.size.should == 1
28
- end
29
-
30
- it 'should be re-entrant' do
31
- n = 10
32
- n.times do |i|
33
- subject.take do |x|
34
- x.replace [1]
35
- subject.take do |y|
36
- y.replace [2]
37
- subject.take do |z|
38
- z.replace [3]
39
- subject.take do |t|
40
- t.replace [4]
41
- end
42
- end
43
- end
44
- end
45
- end
46
- subject.pool.map { |e| e.object.first }.sort.should == [1,2,3,4]
47
- end
48
-
49
-
50
- it 'should unlock when exceptions are raised' do
51
- begin
52
- subject.take do |x|
53
- x << 1
54
- subject.take do |y|
55
- x << 2
56
- y << 3
57
- raise
58
- end
59
- end
60
- rescue
61
- end
62
- subject.pool.all? { |e| not e.owner }.should == true
63
- subject.pool.map { |e| e.object }.to_set.should == [
64
- [0,1,2],
65
- [0,3]
66
- ].to_set
67
- end
68
-
69
- it 'should delete when BadResource is raised' do
70
- subject.open = lambda do
71
- m = mock('resource')
72
- m.should_receive(:close)
73
- m
74
- end
75
- subject.close = lambda do |res|
76
- res.close
77
- end
78
-
79
- lambda do
80
- subject.take do |x|
81
- raise Riak::Client::Pool::BadResource
82
- end
83
- end.should raise_error(Riak::Client::Pool::BadResource)
84
- subject.size.should == 0
85
- end
86
- end
87
-
88
- describe 'threads' do
89
- subject {
90
- described_class.new(
91
- lambda { [] },
92
- lambda { |x| }
93
- )
94
- }
95
-
96
- it 'should allocate n objects for n concurrent operations' do
97
- # n threads concurrently allocate and sign objects from the pool
98
- n = 10
99
- readyq = Queue.new
100
- finishq = Queue.new
101
- threads = (0...n).map do
102
- Thread.new do
103
- subject.take do |x|
104
- readyq << 1
105
- x << Thread.current
106
- finishq.pop
107
- end
108
- end
109
- end
110
-
111
- n.times { readyq.pop }
112
- n.times { finishq << 1 }
113
-
114
- # Wait for completion
115
- threads.each do |t|
116
- t.join
117
- end
118
-
119
- # Should have taken exactly n objects to do this
120
- subject.size.should == n
121
- # And each one should be signed exactly once
122
- subject.pool.map do |e|
123
- e.object.size.should == 1
124
- e.object.first
125
- end.to_set.should == threads.to_set
126
- end
127
-
128
- it 'take with filter and default' do
129
- n = 10
130
- subject = described_class.new(
131
- lambda { [] },
132
- lambda { |x| }
133
- )
134
-
135
- # Allocate several elements of the pool
136
- q = Queue.new
137
- threads = (0...n).map do |i|
138
- Thread.new do
139
- subject.take do |a|
140
- a << i
141
- q << 1
142
- sleep 0.02
143
- end
144
- end
145
- end
146
-
147
- # Wait for all threads to have acquired an element
148
- n.times { q.pop }
149
-
150
- threads.each do |t|
151
- t.join
152
- end
153
-
154
- # Get and delete existing even elements
155
- got = Set.new
156
- (n / 2).times do
157
- begin
158
- subject.take(
159
- :filter => lambda { |x| x.first.even? },
160
- :default => [:default]
161
- ) do |x|
162
- got << x.first
163
- raise Riak::Client::Pool::BadResource
164
- end
165
- rescue Riak::Client::Pool::BadResource
166
- end
167
- end
168
- got.should == (0...n).select(&:even?).to_set
169
-
170
- # This time, no even elements exist, so we should get the default.
171
- subject.take(
172
- :filter => lambda { |x| x.first.even? },
173
- :default => :default
174
- ) do |x|
175
- x.should == :default
176
- end
177
- end
178
-
179
- it 'iterates over a snapshot of all connections, even ones in use' do
180
- started = Queue.new
181
- n = 30
182
- threads = (0..n).map do
183
- Thread.new do
184
- psleep = 0.75 * rand # up to 50ms sleep
185
- subject.take do |a|
186
- started << 1
187
- a << rand
188
- sleep psleep
189
- end
190
- end
191
- end
192
-
193
- n.times { started.pop }
194
- touched = []
195
-
196
- subject.each do |e|
197
- touched << e
198
- end
199
-
200
- threads.each do |t|
201
- t.join
202
- end
203
-
204
- touched.should be_all {|item| subject.pool.find {|e| e.object == item } }
205
- end
206
-
207
- it 'should clear' do
208
- n = 10
209
- subject = described_class.new(
210
- lambda { mock('connection').tap {|m| m.should_receive(:teardown) }},
211
- lambda { |b| b.teardown }
212
- )
213
-
214
- # Allocate several elements of the pool
215
- q = Queue.new
216
- threads = (0...n).map do |i|
217
- Thread.new do
218
- subject.take do |a|
219
- q << 1
220
- sleep 0.1
221
- end
222
- end
223
- end
224
-
225
- # Wait for all threads to have acquired an element
226
- n.times { q.pop }
227
-
228
- # Clear the pool while threads still have elements checked out
229
- subject.clear
230
- subject.pool.should be_empty
231
-
232
- # Wait for threads to complete
233
- threads.each do |t|
234
- t.join
235
- end
236
- end
237
-
238
- it 'should delete_if' do
239
- n = 10
240
- subject = described_class.new(
241
- lambda { [] },
242
- lambda { |x| }
243
- )
244
-
245
- # Allocate several elements of the pool
246
- q = Queue.new
247
- threads = (0...n).map do |i|
248
- Thread.new do
249
- subject.take do |a|
250
- a << i
251
- q << 1
252
- sleep 0.02
253
- end
254
- end
255
- end
256
-
257
- # Wait for all threads to have acquired an element
258
- n.times { q.pop }
259
-
260
- # Delete odd elements
261
- subject.delete_if do |x|
262
- x.first.odd?
263
- end
264
-
265
- # Verify odds are gone.
266
- subject.pool.all? do |x|
267
- x.object.first.even?
268
- end.should == true
269
-
270
- # Wait for threads
271
- threads.each do |t|
272
- t.join
273
- end
274
- end
275
-
276
- it 'stress test', :slow => true do
277
- n = 100
278
- psleep = 0.8
279
- tsleep = 0.01
280
- rounds = 100
281
-
282
- threads = (0...n).map do
283
- Thread.new do
284
- rounds.times do |i|
285
- subject.take do |a|
286
- a.should == []
287
- a << Thread.current
288
- a.should == [Thread.current]
289
-
290
- # Sleep and check
291
- while rand < psleep
292
- sleep tsleep
293
- a.should == [Thread.current]
294
- end
295
-
296
- a.delete Thread.current
297
- end
298
- end
299
- end
300
- end
301
- threads.each do |t|
302
- t.join
303
- end
304
- end
305
- end
306
- end