riak-client 1.0.0.beta → 1.0.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.
Files changed (69) hide show
  1. data/.gitignore +7 -4
  2. data/Gemfile +12 -17
  3. data/Guardfile +1 -1
  4. data/LICENSE +16 -0
  5. data/README.markdown +178 -0
  6. data/RELEASE_NOTES.md +99 -0
  7. data/Rakefile +25 -1
  8. data/erl_src/riak_kv_test014_backend.beam +0 -0
  9. data/erl_src/riak_kv_test014_backend.erl +189 -0
  10. data/erl_src/riak_kv_test_backend.beam +0 -0
  11. data/erl_src/riak_kv_test_backend.erl +37 -19
  12. data/lib/riak/client.rb +322 -272
  13. data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
  14. data/lib/riak/client/decaying.rb +28 -0
  15. data/lib/riak/client/excon_backend.rb +27 -11
  16. data/lib/riak/client/http_backend.rb +71 -2
  17. data/lib/riak/client/http_backend/configuration.rb +17 -3
  18. data/lib/riak/client/http_backend/transport_methods.rb +3 -3
  19. data/lib/riak/client/net_http_backend.rb +18 -14
  20. data/lib/riak/client/node.rb +111 -0
  21. data/lib/riak/client/pool.rb +180 -0
  22. data/lib/riak/client/protobuffs_backend.rb +15 -5
  23. data/lib/riak/client/search.rb +9 -3
  24. data/lib/riak/link.rb +5 -7
  25. data/lib/riak/locale/en.yml +1 -0
  26. data/lib/riak/node/configuration.rb +1 -0
  27. data/lib/riak/node/defaults.rb +19 -6
  28. data/lib/riak/node/generation.rb +9 -2
  29. data/lib/riak/node/log.rb +2 -2
  30. data/lib/riak/node/version.rb +22 -16
  31. data/lib/riak/robject.rb +19 -3
  32. data/lib/riak/serializers.rb +1 -1
  33. data/lib/riak/test_server.rb +10 -2
  34. data/lib/riak/version.rb +1 -1
  35. data/riak-client.gemspec +3 -3
  36. data/spec/failover/failover.rb +59 -0
  37. data/spec/integration/riak/http_backends_spec.rb +2 -2
  38. data/spec/integration/riak/node_spec.rb +16 -24
  39. data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
  40. data/spec/integration/riak/test_server_spec.rb +4 -3
  41. data/spec/integration/riak/threading_spec.rb +193 -0
  42. data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
  43. data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
  44. data/spec/riak/bucket_spec.rb +2 -1
  45. data/spec/riak/client_spec.rb +80 -181
  46. data/spec/riak/excon_backend_spec.rb +3 -2
  47. data/spec/riak/http_backend/configuration_spec.rb +37 -5
  48. data/spec/riak/http_backend/object_methods_spec.rb +1 -1
  49. data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
  50. data/spec/riak/http_backend_spec.rb +53 -3
  51. data/spec/riak/map_reduce_spec.rb +1 -1
  52. data/spec/riak/net_http_backend_spec.rb +1 -2
  53. data/spec/riak/node_spec.rb +173 -0
  54. data/spec/riak/pool_spec.rb +306 -0
  55. data/spec/riak/robject_spec.rb +8 -4
  56. data/spec/riak/search_spec.rb +66 -15
  57. data/spec/riak/serializers_spec.rb +12 -1
  58. data/spec/spec_helper.rb +9 -1
  59. data/spec/support/http_backend_implementation_examples.rb +6 -2
  60. data/spec/support/sometimes.rb +46 -0
  61. data/spec/support/test_server.rb +50 -19
  62. data/spec/support/unified_backend_examples.rb +11 -10
  63. data/spec/support/version_filter.rb +14 -0
  64. metadata +40 -29
  65. data/lib/active_support/cache/riak_store.rb +0 -2
  66. data/lib/riak/cache_store.rb +0 -84
  67. data/lib/riak/client/pump.rb +0 -30
  68. data/lib/riak/util/fiber1.8.rb +0 -48
  69. data/spec/integration/riak/cache_store_spec.rb +0 -129
@@ -24,13 +24,14 @@ else
24
24
 
25
25
  before :each do
26
26
  @client = Riak::Client.new(:http_port => $mock_server.port, :http_backend => :Excon) # Point to our mock
27
- @backend = @client.http
27
+ @node = @client.node
28
+ @backend = @client.new_http_backend
28
29
  @_mock_set = false
29
30
  end
30
31
 
31
32
  after :each do
32
33
  if @_mock_set
33
- $mock_server.satisfied.should be_true("Expected #{@_mock_set.inspect}, failed")
34
+ $mock_server.satisfied.should be_true #("Expected #{@_mock_set.inspect}, failed")
34
35
  end
35
36
  end
36
37
 
@@ -2,7 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  describe Riak::Client::HTTPBackend::Configuration do
4
4
  let(:client){ Riak::Client.new }
5
- subject { Riak::Client::HTTPBackend.new(client) }
5
+ let(:node){ client.node }
6
+ subject { Riak::Client::HTTPBackend.new(client, node) }
6
7
  let(:uri){ URI.parse("http://127.0.0.1:8098/") }
7
8
 
8
9
  context "generating resource URIs" do
@@ -200,6 +201,37 @@ describe Riak::Client::HTTPBackend::Configuration do
200
201
  end
201
202
  end
202
203
 
204
+ context "when Luwak is enabled" do
205
+ before { subject.should_receive(:get).with(200, uri).and_return(:headers => {'link' => ['</riak>; rel="riak_kv_wm_raw", </ping>; rel="riak_kv_wm_ping", </stats>; rel="riak_kv_wm_stats", </mapred>; rel="riak_kv_wm_mapred", </luwak>; rel="luwak_wm_file"']}) }
206
+
207
+ it "should generate a path for a file" do
208
+ url = subject.luwak_path('foo')
209
+ url.should be_kind_of(URI)
210
+ url.path.should == '/luwak/foo'
211
+ end
212
+
213
+ it "should generate a path for the root" do
214
+ url = subject.luwak_path(nil)
215
+ url.should be_kind_of(URI)
216
+ url.path.should == '/luwak'
217
+ end
218
+
219
+ it "should escape a nested path" do
220
+ url = subject.luwak_path("foo/bar/baz")
221
+ url.should be_kind_of(URI)
222
+ url.path.should == '/luwak/foo%2Fbar%2Fbaz'
223
+ end
224
+ end
225
+
226
+ context "when Luwak is disabled" do
227
+ before { subject.should_receive(:get).with(200, uri).and_return(:headers => {'link' => ['</riak>; rel="riak_kv_wm_raw", </ping>; rel="riak_kv_wm_ping", </stats>; rel="riak_kv_wm_stats", </mapred>; rel="riak_kv_wm_mapred"']}) }
228
+
229
+ it "should raise an error when generating the path" do
230
+ expect { subject.luwak_path(nil) }.to raise_error
231
+ expect { subject.luwak_path('foo') }.to raise_error
232
+ end
233
+ end
234
+
203
235
  {
204
236
  :riak_kv_wm_raw => :prefix,
205
237
  :riak_kv_wm_link_walker => :prefix,
@@ -210,14 +242,14 @@ describe Riak::Client::HTTPBackend::Configuration do
210
242
  subject.send(resource).should == "/path"
211
243
  end
212
244
 
213
- it "should fallback to client.http_paths[:#{alternate}] if the #{resource} resource is not found" do
245
+ it "should fallback to node.http_paths[:#{alternate}] if the #{resource} resource is not found" do
214
246
  subject.should_receive(:get).with(200, uri).and_return(:headers => {'link' => ['</>; rel="top"']})
215
- subject.send(resource).should == client.http_paths[alternate]
247
+ subject.send(resource).should == node.http_paths[alternate]
216
248
  end
217
249
 
218
- it "should fallback to client.http_paths[:#{alternate}] if request fails" do
250
+ it "should fallback to node.http_paths[:#{alternate}] if request fails" do
219
251
  subject.should_receive(:get).with(200, uri).and_raise(Riak::HTTPFailedRequest.new(:get, 200, 404, {}, ""))
220
- subject.send(resource).should == client.http_paths[alternate]
252
+ subject.send(resource).should == node.http_paths[alternate]
221
253
  end
222
254
  end
223
255
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Riak::Client::HTTPBackend::ObjectMethods do
4
4
  before :each do
5
5
  @client = Riak::Client.new
6
- @backend = Riak::Client::HTTPBackend.new(@client)
6
+ @backend = Riak::Client::HTTPBackend.new(@client, @client.node)
7
7
  @bucket = Riak::Bucket.new(@client, "bucket")
8
8
  @object = Riak::RObject.new(@bucket, "bar")
9
9
  @backend.stub!(:new_scheme?).and_return(false)
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Riak::Client::HTTPBackend::TransportMethods do
4
4
  before :each do
5
5
  @client = Riak::Client.new
6
- @backend = Riak::Client::HTTPBackend.new(@client)
6
+ @backend = Riak::Client::HTTPBackend.new(@client, @client.node)
7
7
  @backend.instance_variable_set(:@server_config, {})
8
8
  end
9
9
 
@@ -91,7 +91,7 @@ describe Riak::Client::HTTPBackend::TransportMethods do
91
91
 
92
92
  it "should allow using the https protocol" do
93
93
  @client = Riak::Client.new(:protocol => 'https')
94
- @backend = Riak::Client::HTTPBackend.new(@client)
94
+ @backend = Riak::Client::HTTPBackend.new(@client, @client.node)
95
95
  @backend.root_uri.to_s.should eq("https://127.0.0.1:8098")
96
96
  end
97
97
  end
@@ -3,19 +3,25 @@ require 'spec_helper'
3
3
  describe Riak::Client::HTTPBackend do
4
4
  before :each do
5
5
  @client = Riak::Client.new
6
- @backend = Riak::Client::HTTPBackend.new(@client)
6
+ @node = @client.nodes.first
7
+ @backend = Riak::Client::HTTPBackend.new(@client, @node)
7
8
  @backend.instance_variable_set(:@server_config, {})
8
9
  end
9
10
 
10
- it "should take the Riak::Client when creating" do
11
+ it "should take the Riak::Client and Riak::Node when creating" do
11
12
  lambda { Riak::Client::HTTPBackend.new(nil) }.should raise_error(ArgumentError)
12
- lambda { Riak::Client::HTTPBackend.new(@client) }.should_not raise_error
13
+ lambda { Riak::Client::HTTPBackend.new(@client) }.should raise_error(ArgumentError)
14
+ lambda { Riak::Client::HTTPBackend.new(@client, @node) }.should_not raise_error
13
15
  end
14
16
 
15
17
  it "should make the client accessible" do
16
18
  @backend.client.should == @client
17
19
  end
18
20
 
21
+ it 'should make the node accessible' do
22
+ @backend.node.should == @node
23
+ end
24
+
19
25
  context "pinging the server" do
20
26
  it "should succeed on 200" do
21
27
  @backend.should_receive(:get).with(200, @backend.ping_path).and_return({:code => 200, :body => "OK"})
@@ -285,4 +291,48 @@ describe Riak::Client::HTTPBackend do
285
291
  @backend.update_search_index(nil, 'postbody')
286
292
  end
287
293
  end
294
+ context "Luwak" do
295
+ before { @backend.send(:server_config)[:luwak_wm_file] = '/luwak' }
296
+ context "fetching a file" do
297
+ before do
298
+
299
+ @backend.should_receive(:get).with(200, @backend.luwak_path("greeting.txt")).and_yield("Hello,").and_yield(" world!").and_return({:code => 200, :headers => {"content-type" => ["text/plain"]}})
300
+ end
301
+
302
+ it "should return a tempfile when no block is given" do
303
+ file = @backend.get_file("greeting.txt")
304
+ file.open {|f| f.read.should == "Hello, world!" }
305
+ end
306
+
307
+ it "should expose the original key and content-type on the temporary file" do
308
+ file = @backend.get_file("greeting.txt")
309
+ file.original_filename.should == 'greeting.txt'
310
+ file.content_type.should == 'text/plain'
311
+ end
312
+
313
+ it "should yield chunks of the file to the block and return nil" do
314
+ string = ""
315
+ result = @backend.get_file("greeting.txt"){|chunk| string << chunk }
316
+ result.should be_nil
317
+ string.should == "Hello, world!"
318
+ end
319
+ end
320
+
321
+ context "storing a file" do
322
+ it "should store a file with the given filename" do
323
+ @backend.should_receive(:put).with(204, @backend.luwak_path("greeting.txt"), anything, {"Content-Type" => "text/plain"}).and_return({:code => 204, :headers => {}})
324
+ @backend.store_file("greeting.txt", "text/plain", "Hello, world").should == 'greeting.txt'
325
+ end
326
+
327
+ it "should store a file and return the key/filename when none is given" do
328
+ @backend.should_receive(:post).with(201, @backend.luwak_path(nil), anything, {"Content-Type" => "text/plain"}).and_return({:code => 201, :headers => {'location' => ["/luwak/123456789"]}})
329
+ @backend.store_file("text/plain", "Hello, world").should == '123456789'
330
+ end
331
+ end
332
+
333
+ it "should detect whether a file exists" do
334
+ @backend.should_receive(:head).with([200,404], @backend.luwak_path("foo")).and_return({:code => 200})
335
+ @backend.file_exists?("foo").should be_true
336
+ end
337
+ end
288
338
  end
@@ -4,7 +4,7 @@ describe Riak::MapReduce do
4
4
  before :each do
5
5
  @client = Riak::Client.new
6
6
  @backend = mock("Backend")
7
- @client.stub!(:backend).and_return(@backend)
7
+ @client.stub!(:backend).and_yield(@backend)
8
8
  @mr = Riak::MapReduce.new(@client)
9
9
  end
10
10
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Riak::Client::NetHTTPBackend do
4
4
  before :each do
5
5
  @client = Riak::Client.new(:http_backend => :NetHTTP)
6
- @backend = @client.http
6
+ @backend = @client.new_http_backend
7
7
  FakeWeb.allow_net_connect = false
8
8
  end
9
9
 
@@ -12,5 +12,4 @@ describe Riak::Client::NetHTTPBackend do
12
12
  end
13
13
 
14
14
  it_should_behave_like "HTTP backend"
15
-
16
15
  end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ describe Riak::Node do
4
+ before :each do
5
+ @client = Riak::Client.new
6
+ @node = Riak::Client::Node.new @client
7
+ end
8
+
9
+ describe 'when initializing' do
10
+ it 'should default to the local interface on port 8098/8087' do
11
+ node = Riak::Client::Node.new @client
12
+ node.host.should == '127.0.0.1'
13
+ node.http_port.should == 8098
14
+ node.pb_port.should == 8087
15
+ end
16
+
17
+ it 'should accept a host' do
18
+ node = Riak::Client::Node.new(@client, :host => 'riak.basho.com')
19
+ node.host.should == "riak.basho.com"
20
+ end
21
+
22
+ it 'should accept an HTTP port' do
23
+ node = Riak::Client::Node.new(@client, :http_port => 9000)
24
+ node.http_port.should == 9000
25
+ end
26
+
27
+ it 'should accept a Protobuffs port' do
28
+ node = Riak::Client::Node.new @client, :pb_port => 9000
29
+ node.pb_port.should == 9000
30
+ end
31
+
32
+ it 'should accept basic_auth' do
33
+ node = Riak::Client::Node.new @client, :basic_auth => 'user:pass'
34
+ node.basic_auth.should == "user:pass"
35
+ end
36
+
37
+ it 'should accept a path prefix' do
38
+ node = Riak::Client::Node.new @client, :prefix => "/jiak/"
39
+ node.http_paths[:prefix].should == "/jiak/"
40
+ end
41
+
42
+ it 'default prefix should be /riak/' do
43
+ @node.http_paths[:prefix].should == "/riak/"
44
+ end
45
+
46
+ it 'should accept a mapreduce path' do
47
+ node = Riak::Client::Node.new @client, :mapred => "/mr"
48
+ node.http_paths[:mapred].should == "/mr"
49
+ end
50
+
51
+ it "default mapreduce path should be /mapred" do
52
+ @node.http_paths[:mapred].should == "/mapred"
53
+ end
54
+
55
+ it 'should accept a solr path' do
56
+ node = Riak::Client::Node.new @client, :solr => "/mr"
57
+ node.http_paths[:solr].should == "/mr"
58
+ end
59
+
60
+ it "default solr path should be /solr" do
61
+ @node.http_paths[:solr].should == "/solr"
62
+ end
63
+
64
+ it 'should accept a luwak path' do
65
+ node = Riak::Client::Node.new @client, :luwak => "/mr"
66
+ node.http_paths[:luwak].should == "/mr"
67
+ end
68
+
69
+ it "default luwak path should be /luwak" do
70
+ @node.http_paths[:luwak].should == "/luwak"
71
+ end
72
+ end
73
+
74
+ describe "setting http auth" do
75
+ it "should allow setting basic_auth" do
76
+ @node.should respond_to(:basic_auth=)
77
+ @node.basic_auth = "user:pass"
78
+ @node.basic_auth.should == "user:pass"
79
+ @node.basic_auth = nil
80
+ @node.basic_auth.should == nil
81
+ end
82
+
83
+ it "should require that basic auth splits into two even parts" do
84
+ lambda { @node.basic_auth ="user" }.should raise_error(ArgumentError, "basic auth must be set using 'user:pass'")
85
+ end
86
+ end
87
+
88
+ describe 'ssl' do
89
+ before :each do
90
+ @client = Riak::Client.new
91
+ @node = Riak::Client::Node.new @client
92
+ end
93
+
94
+ it 'should not allow reading ssl options via ssl' do
95
+ @node.should_not respond_to(:ssl)
96
+ end
97
+
98
+ it 'should allow setting ssl to true' do
99
+ @node.ssl = true
100
+ @node.ssl_options[:verify_mode].should eq('none')
101
+ end
102
+
103
+ it "should should clear ssl options when setting ssl to false" do
104
+ @node.ssl = true
105
+ @node.ssl_options.should_not be_nil
106
+ @node.ssl = false
107
+ @node.ssl_options.should be_nil
108
+ end
109
+
110
+ it "should allow setting the verify_mode to none" do
111
+ @node.ssl = {:verify_mode => "none"}
112
+ @node.ssl_options[:verify_mode].should eq("none")
113
+ end
114
+
115
+ it "should allow setting the verify_mode to peer" do
116
+ @node.ssl = {:verify_mode => "peer"}
117
+ @node.ssl_options[:verify_mode].should eq("peer")
118
+ end
119
+
120
+ it "should not allow setting the verify_mode to anything else" do
121
+ lambda {@node.ssl = {:verify_mode => :your_mom}}.should raise_error(ArgumentError)
122
+ end
123
+
124
+ it "should default verify_mode to none" do
125
+ @node.ssl = true
126
+ @node.ssl_options[:verify_mode].should eq("none")
127
+ end
128
+
129
+ it "should let the backend know if ssl is enabled" do
130
+ @node.should_not be_ssl_enabled
131
+ @node.ssl = true
132
+ @node.should be_ssl_enabled
133
+ end
134
+
135
+ it "should allow setting the pem" do
136
+ @node.ssl = {:pem => 'i-am-a-pem'}
137
+ @node.ssl_options[:pem].should eq('i-am-a-pem')
138
+ end
139
+
140
+ it "should set them pem from the contents of pem_file" do
141
+ filepath = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test.pem'))
142
+ @node.ssl = {:pem_file => filepath}
143
+ @node.ssl_options[:pem].should eq("i-am-a-pem\n")
144
+ end
145
+
146
+ it "should allow setting the pem_password" do
147
+ @node.ssl = {:pem_password => 'pem-pass'}
148
+ @node.ssl_options[:pem_password].should eq('pem-pass')
149
+ end
150
+
151
+ it "should allow setting the ca_file" do
152
+ @node.ssl = {:ca_file => '/path/to/ca.crt'}
153
+ @node.ssl_options[:ca_file].should eq('/path/to/ca.crt')
154
+ end
155
+
156
+ it "should allow setting the ca_path" do
157
+ @node.ssl = {:ca_path => '/path/to/certs/'}
158
+ @node.ssl_options[:ca_path].should eq('/path/to/certs/')
159
+ end
160
+
161
+ %w[pem ca_file ca_path].each do |option|
162
+ it "should default the verify_mode to peer when setting the #{option}" do
163
+ @node.ssl = {option.to_sym => 'test-data'}
164
+ @node.ssl_options[:verify_mode].should eq("peer")
165
+ end
166
+
167
+ it "should allow setting the verify mode when setting the #{option}" do
168
+ @node.ssl = {option.to_sym => 'test-data', :verify_mode => "none"}
169
+ @node.ssl_options[:verify_mode].should eq("none")
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,306 @@
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