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.
- data/.gitignore +7 -4
- data/Gemfile +12 -17
- data/Guardfile +1 -1
- data/LICENSE +16 -0
- data/README.markdown +178 -0
- data/RELEASE_NOTES.md +99 -0
- data/Rakefile +25 -1
- data/erl_src/riak_kv_test014_backend.beam +0 -0
- data/erl_src/riak_kv_test014_backend.erl +189 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +37 -19
- data/lib/riak/client.rb +322 -272
- data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
- data/lib/riak/client/decaying.rb +28 -0
- data/lib/riak/client/excon_backend.rb +27 -11
- data/lib/riak/client/http_backend.rb +71 -2
- data/lib/riak/client/http_backend/configuration.rb +17 -3
- data/lib/riak/client/http_backend/transport_methods.rb +3 -3
- data/lib/riak/client/net_http_backend.rb +18 -14
- data/lib/riak/client/node.rb +111 -0
- data/lib/riak/client/pool.rb +180 -0
- data/lib/riak/client/protobuffs_backend.rb +15 -5
- data/lib/riak/client/search.rb +9 -3
- data/lib/riak/link.rb +5 -7
- data/lib/riak/locale/en.yml +1 -0
- data/lib/riak/node/configuration.rb +1 -0
- data/lib/riak/node/defaults.rb +19 -6
- data/lib/riak/node/generation.rb +9 -2
- data/lib/riak/node/log.rb +2 -2
- data/lib/riak/node/version.rb +22 -16
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/serializers.rb +1 -1
- data/lib/riak/test_server.rb +10 -2
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +3 -3
- data/spec/failover/failover.rb +59 -0
- data/spec/integration/riak/http_backends_spec.rb +2 -2
- data/spec/integration/riak/node_spec.rb +16 -24
- data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
- data/spec/integration/riak/test_server_spec.rb +4 -3
- data/spec/integration/riak/threading_spec.rb +193 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
- data/spec/riak/bucket_spec.rb +2 -1
- data/spec/riak/client_spec.rb +80 -181
- data/spec/riak/excon_backend_spec.rb +3 -2
- data/spec/riak/http_backend/configuration_spec.rb +37 -5
- data/spec/riak/http_backend/object_methods_spec.rb +1 -1
- data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
- data/spec/riak/http_backend_spec.rb +53 -3
- data/spec/riak/map_reduce_spec.rb +1 -1
- data/spec/riak/net_http_backend_spec.rb +1 -2
- data/spec/riak/node_spec.rb +173 -0
- data/spec/riak/pool_spec.rb +306 -0
- data/spec/riak/robject_spec.rb +8 -4
- data/spec/riak/search_spec.rb +66 -15
- data/spec/riak/serializers_spec.rb +12 -1
- data/spec/spec_helper.rb +9 -1
- data/spec/support/http_backend_implementation_examples.rb +6 -2
- data/spec/support/sometimes.rb +46 -0
- data/spec/support/test_server.rb +50 -19
- data/spec/support/unified_backend_examples.rb +11 -10
- data/spec/support/version_filter.rb +14 -0
- metadata +40 -29
- data/lib/active_support/cache/riak_store.rb +0 -2
- data/lib/riak/cache_store.rb +0 -84
- data/lib/riak/client/pump.rb +0 -30
- data/lib/riak/util/fiber1.8.rb +0 -48
- 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
|
-
@
|
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
|
-
|
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
|
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 ==
|
247
|
+
subject.send(resource).should == node.http_paths[alternate]
|
216
248
|
end
|
217
249
|
|
218
|
-
it "should fallback to
|
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 ==
|
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
|
-
@
|
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) }.
|
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
|
@@ -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.
|
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
|