riak-client 1.0.0.beta → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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