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
@@ -214,6 +214,10 @@ describe Riak::RObject do
214
214
  @object.conflict?.should be_false
215
215
  end
216
216
 
217
+ it 'should return [self] for siblings' do
218
+ @object.siblings.should == [@object]
219
+ end
220
+
217
221
  describe "when there are multiple values in an object" do
218
222
  before :each do
219
223
  response = @sample_response.dup
@@ -257,7 +261,7 @@ describe Riak::RObject do
257
261
  describe "when storing the object normally" do
258
262
  before :each do
259
263
  @backend = mock("Backend")
260
- @client.stub!(:backend).and_return(@backend)
264
+ @client.stub!(:backend).and_yield(@backend)
261
265
  @object = Riak::RObject.new(@bucket)
262
266
  @object.content_type = "text/plain"
263
267
  @object.data = "This is some text."
@@ -278,7 +282,7 @@ describe Riak::RObject do
278
282
  describe "when reloading the object" do
279
283
  before :each do
280
284
  @backend = mock("Backend")
281
- @client.stub!(:backend).and_return(@backend)
285
+ @client.stub!(:backend).and_yield(@backend)
282
286
  @object = Riak::RObject.new(@bucket, "bar")
283
287
  @object.vclock = "somereallylongstring"
284
288
  end
@@ -318,7 +322,7 @@ describe Riak::RObject do
318
322
  describe "walking from the object to linked objects" do
319
323
  before :each do
320
324
  @http = mock("HTTPBackend")
321
- @client.stub!(:http).and_return(@http)
325
+ @client.stub!(:http).and_yield(@http)
322
326
  @client.stub!(:bucket).and_return(@bucket)
323
327
  @object = Riak::RObject.new(@bucket, "bar")
324
328
  end
@@ -332,7 +336,7 @@ describe Riak::RObject do
332
336
  describe "when deleting" do
333
337
  before :each do
334
338
  @backend = mock("Backend")
335
- @client.stub!(:backend).and_return(@backend)
339
+ @client.stub!(:backend).and_yield(@backend)
336
340
  @object = Riak::RObject.new(@bucket, "bar")
337
341
  end
338
342
 
@@ -1,11 +1,12 @@
1
1
  require 'spec_helper'
2
+ require 'rexml/document'
2
3
 
3
4
  describe "Search features" do
4
5
  describe Riak::Client do
5
6
  before :each do
6
7
  @client = Riak::Client.new
7
8
  @http = mock(Riak::Client::HTTPBackend)
8
- @client.stub!(:http).and_return(@http)
9
+ @client.stub!(:http).and_yield(@http)
9
10
  end
10
11
 
11
12
  describe "searching" do
@@ -28,20 +29,40 @@ describe "Search features" do
28
29
  describe "indexing documents" do
29
30
  it "should update the default index" do
30
31
  doc = {'field' => "value", 'id' => 1}
31
- if doc.to_a.first.first == 'field'
32
- expect_update_body('<add><doc><field name="field">value</field><field name="id">1</field></doc></add>')
33
- else # 1.8.7, I hate you.
34
- expect_update_body('<add><doc><field name="id">1</field><field name="field">value</field></doc></add>')
32
+ expect_update_body do |body|
33
+ root = REXML::Document.new(body).root
34
+ root.name.should == 'add'
35
+ root.elements.should have(1).item
36
+ root.elements[1].each_element do |el|
37
+ case el.attributes['name']
38
+ when 'field'
39
+ el.text.should == 'value'
40
+ when 'id'
41
+ el.text.should == '1'
42
+ else
43
+ fail "Spurious element in #{root.elements[1]}! #{el}"
44
+ end
45
+ end
35
46
  end
36
47
  @client.index(doc)
37
48
  end
38
49
 
39
50
  it "should update the specified index" do
40
51
  doc = {'field' => "value", 'id' => 1}
41
- if doc.to_a.first.first == 'field'
42
- expect_update_body('<add><doc><field name="field">value</field><field name="id">1</field></doc></add>', 'foo')
43
- else # 1.8.7, I hate you.
44
- expect_update_body('<add><doc><field name="id">1</field><field name="field">value</field></doc></add>', 'foo')
52
+ expect_update_body(nil, "foo") do |body|
53
+ root = REXML::Document.new(body).root
54
+ root.name.should == 'add'
55
+ root.elements.should have(1).item
56
+ root.elements[1].each_element do |el|
57
+ case el.attributes['name']
58
+ when 'field'
59
+ el.text.should == 'value'
60
+ when 'id'
61
+ el.text.should == '1'
62
+ else
63
+ fail "Spurious element in #{root.elements[1]}! #{el}"
64
+ end
65
+ end
45
66
  end
46
67
  @client.index("foo", doc)
47
68
  end
@@ -54,10 +75,34 @@ describe "Search features" do
54
75
 
55
76
  it "should include multiple documents in the <add> request" do
56
77
  docs = {'field' => "value", 'id' => 1}, {'foo' => "bar", 'id' => 2}
57
- if docs.first.to_a.first.first == 'field'
58
- expect_update_body('<add><doc><field name="field">value</field><field name="id">1</field></doc><doc><field name="foo">bar</field><field name="id">2</field></doc></add>')
59
- else # 1.8.7, I hate you
60
- expect_update_body('<add><doc><field name="id">1</field><field name="field">value</field></doc><doc><field name="id">2</field><field name="foo">bar</field></doc></add>')
78
+ expect_update_body do |body|
79
+ root = REXML::Document.new(body).root
80
+ root.name.should == 'add'
81
+ docs = root.elements
82
+ docs.each do |d|
83
+ d.name.should == 'doc'
84
+ d.elements.size.should == 2
85
+ end
86
+ docs[1].each_element do |el|
87
+ case el.attributes['name']
88
+ when 'field'
89
+ el.text.should == 'value'
90
+ when 'id'
91
+ el.text.should == '1'
92
+ else
93
+ fail "Spurious element in #{docs[0]}! #{el}"
94
+ end
95
+ end
96
+ docs[2].each_element do |el|
97
+ case el.attributes['name']
98
+ when 'foo'
99
+ el.text.should == 'bar'
100
+ when 'id'
101
+ el.text.should == '2'
102
+ else
103
+ fail "Spurious element in #{docs[1]}! #{el}"
104
+ end
105
+ end
61
106
  end
62
107
  @client.index(*docs)
63
108
  end
@@ -93,8 +138,14 @@ describe "Search features" do
93
138
  end
94
139
  end
95
140
 
96
- def expect_update_body(body, index=nil)
97
- @http.should_receive(:update_search_index).with(index, body)
141
+ def expect_update_body(body=nil, index=nil)
142
+ if block_given?
143
+ @http.should_receive(:update_search_index).with(index, kind_of(String)) do |i, b|
144
+ yield b
145
+ end
146
+ else
147
+ @http.should_receive(:update_search_index).with(index, body)
148
+ end
98
149
  end
99
150
  end
100
151
 
@@ -73,7 +73,7 @@ describe Riak::Serializers do
73
73
  end
74
74
 
75
75
  def o.load(string)
76
- string.sub!(/^The string is: /, '')
76
+ string.sub(/^The string is: /, '')
77
77
  end
78
78
  end
79
79
  end
@@ -81,6 +81,7 @@ describe Riak::Serializers do
81
81
  it 'can be registered' do
82
82
  described_class['application/custom-type-1'] = custom_serializer
83
83
  described_class['application/custom-type-1'].should be(custom_serializer)
84
+ # fail
84
85
  end
85
86
 
86
87
  it_behaves_like "a serializer", "application/custom-type-a", "foo", "The string is: foo" do
@@ -88,6 +89,16 @@ describe Riak::Serializers do
88
89
  described_class['application/custom-type-a'] = custom_serializer
89
90
  end
90
91
  end
92
+
93
+ after(:each) do
94
+ # Make sure to clean up the registered serializer
95
+ %w{application/custom-type-1 application/custom-type-a}.each do |ctype|
96
+ described_class.send(:serializers).delete(ctype)
97
+ end
98
+ end
91
99
  end
100
+
101
+ it_behaves_like "a serializer", "application/json; charset=UTF-8", { "a" => 7 }, %q|{"a":7}|
102
+ it_behaves_like "a serializer", "application/json ;charset=UTF-8", { "a" => 7 }, %q|{"a":7}|
92
103
  end
93
104
 
data/spec/spec_helper.rb CHANGED
@@ -10,6 +10,8 @@ require 'fakeweb'
10
10
  Riak.disable_list_keys_warnings = true
11
11
 
12
12
  %w[integration_setup
13
+ version_filter
14
+ sometimes
13
15
  http_backend_implementation_examples
14
16
  unified_backend_examples
15
17
  mocks
@@ -20,7 +22,7 @@ Riak.disable_list_keys_warnings = true
20
22
  end
21
23
 
22
24
  RSpec.configure do |config|
23
- config.debug = true
25
+ #config.debug = true
24
26
  config.mock_with :rspec
25
27
 
26
28
  config.before(:all, :integration => true) do
@@ -38,4 +40,10 @@ RSpec.configure do |config|
38
40
 
39
41
  config.filter_run :focus => true
40
42
  config.run_all_when_everything_filtered = true
43
+
44
+ if defined?(::Java)
45
+ config.seed = Time.now.utc
46
+ else
47
+ config.order = :random
48
+ end
41
49
  end
@@ -185,7 +185,11 @@ shared_examples_for "HTTP backend" do
185
185
 
186
186
  describe "SSL" do
187
187
  it "should be supported" do
188
- @client.http_port = $mock_server.port + 1 unless @client.http_backend == :NetHTTP
188
+ unless @client.http_backend == :NetHTTP
189
+ @client.nodes.each do |node|
190
+ node.http_port = $mock_server.port + 1
191
+ end
192
+ end
189
193
  @client.ssl = true
190
194
  setup_http_mock(:get, @backend.path("/riak/","ssl").to_s, :body => "Success!")
191
195
  response = @backend.get(200, @backend.path("/riak/","ssl"))
@@ -194,7 +198,7 @@ shared_examples_for "HTTP backend" do
194
198
  end
195
199
 
196
200
  describe "HTTP Basic Authentication", :basic_auth => true do
197
- it "should add the http basic auth header" do
201
+ sometimes "should add the http basic auth header" do
198
202
  @client.basic_auth = "ripple:rocks"
199
203
  if @client.http_backend == :NetHTTP
200
204
  setup_http_mock(:get, "http://ripple:rocks@127.0.0.1:8098/riak/auth", :body => 'Success!')
@@ -0,0 +1,46 @@
1
+ # Emulates the QuickCheck ?SOMETIMES macro.
2
+
3
+ module Sometimes
4
+ def run_with_retries(example_to_run, retries)
5
+ self.example.metadata[:retries] ||= retries
6
+ retries.times do |t|
7
+ self.example.metadata[:retried] = t + 1
8
+ self.example.instance_variable_set(:@exception, nil)
9
+ example_to_run.run
10
+ break unless self.example.exception
11
+ end
12
+ if e = self.example.exception
13
+ new_exception = e.exception(e.message + "[Retried #{retries} times]")
14
+ new_exception.set_backtrace e.backtrace
15
+ self.example.instance_variable_set(:@exception, new_exception)
16
+ end
17
+ end
18
+ end
19
+
20
+ RSpec.configure do |config|
21
+ config.include Sometimes
22
+ config.alias_example_to :sometimes, :sometimes => true
23
+ config.add_setting :sometimes_retry_count, :default => 3
24
+
25
+ config.around(:each, :sometimes => true) do |example|
26
+ retries = example.metadata[:retries] || RSpec.configuration.sometimes_retry_count
27
+ run_with_retries(example, retries)
28
+ end
29
+
30
+ config.after(:suite) do
31
+ formatter = RSpec.configuration.formatters.first
32
+ color = lambda {|tint, msg| formatter.send(tint, msg) }
33
+ retried_examples = RSpec.world.example_groups.map do |g|
34
+ g.descendants.map do |d|
35
+ d.filtered_examples.select {|e| e.metadata[:sometimes] && e.metadata[:retried] > 1 }
36
+ end
37
+ end.flatten
38
+ formatter.message color[retried_examples.empty? ? :green : :yellow, "\n\nRetried examples: #{retried_examples.count}"]
39
+ unless retried_examples.empty?
40
+ retried_examples.each do |e|
41
+ formatter.message " #{e.full_description}"
42
+ formatter.message(color[:yellow, " [#{e.metadata[:retried]}/#{e.metadata[:retries]}] "] + formatter.class.relative_path(e.location))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,30 +1,61 @@
1
1
  require 'riak/test_server'
2
2
 
3
- RSpec.configure do |config|
4
- config.before(:each, :integration => true) do
5
- begin
6
- unless $test_server
7
- config = YAML.load_file("spec/support/test_server.yml")
3
+ module TestServerSupport
4
+ def test_server
5
+ unless $test_server
6
+ begin
7
+ require 'yaml'
8
+ config = YAML.load_file(File.expand_path("../test_server.yml", __FILE__))
8
9
  $test_server = Riak::TestServer.create(:root => config['root'],
9
- :source => config['source'],
10
- :min_port => config['min_port'] || 15000)
11
- at_exit { $test_server.stop }
12
- end
13
- if example.metadata[:test_server] == false
14
- $test_server.stop
15
- else
16
- $test_server.create unless $test_server.exist?
17
- $test_server.start
10
+ :source => config['source'],
11
+ :min_port => config['min_port'] || 15000)
12
+ rescue SocketError => e
13
+ warn "Couldn't connect to Riak TestServer! #{$test_server.inspect}"
14
+ warn "Skipping remaining integration tests."
15
+ warn_crash_log
16
+ $test_server_fatal = e
17
+ rescue => e
18
+ warn "Can't run integration specs without the test server. Please create/verify spec/support/test_server.yml."
19
+ warn "Skipping remaining integration tests."
20
+ warn e.inspect
21
+ warn_crash_log
22
+ $test_server_fatal = e
18
23
  end
19
- rescue => e
20
- warn "Can't run integration specs without the test server. Please create spec/support/test_server.yml."
21
- warn e.inspect
24
+ end
25
+ $test_server
26
+ end
27
+
28
+ def test_server_fatal
29
+ $test_server_fatal
30
+ end
31
+
32
+ def warn_crash_log
33
+ if $test_server && crash_log = $test_server.log + 'crash.log'
34
+ warn crash_log.read if crash_log.exist?
35
+ end
36
+ end
37
+ end
38
+
39
+ RSpec.configure do |config|
40
+ config.include TestServerSupport, :integration => true
41
+
42
+ config.before(:each, :integration => true) do
43
+ fail "Test server not working: #{test_server_fatal}" if test_server_fatal
44
+ if example.metadata[:test_server] == false
45
+ test_server.stop
46
+ else
47
+ test_server.create unless test_server.exist?
48
+ test_server.start
22
49
  end
23
50
  end
24
51
 
25
52
  config.after(:each, :integration => true) do
26
- if $test_server && example.metadata[:test_server] != false
27
- $test_server.drop
53
+ if test_server && !test_server_fatal && example.metadata[:test_server] != false
54
+ test_server.drop
28
55
  end
29
56
  end
57
+
58
+ config.after(:suite) do
59
+ $test_server.stop if $test_server
60
+ end
30
61
  end
@@ -9,9 +9,9 @@ shared_examples_for "Unified backend API" do
9
9
  context "fetching an object" do
10
10
  before do
11
11
  @robject = Riak::RObject.new(@client.bucket("test"), "fetch")
12
- @robject.indexes['test_bin'] << 'pass'
13
12
  @robject.content_type = "application/json"
14
13
  @robject.data = { "test" => "pass" }
14
+ @robject.indexes['test_bin'] << 'pass' if test_server.version >= "1.0.0"
15
15
  @backend.store_object(@robject)
16
16
  end
17
17
 
@@ -38,15 +38,14 @@ shared_examples_for "Unified backend API" do
38
38
  robj.data.should == { "test" => "pass" }
39
39
  end
40
40
 
41
- it "should accept a PR value of #{q.inspect} for the request" do
41
+ it "should accept a PR value of #{q.inspect} for the request", :version => "1.0.0" do
42
42
  robj = @backend.fetch_object("test", "fetch", :pr => q)
43
43
  robj.should be_kind_of(Riak::RObject)
44
44
  robj.data.should == { "test" => "pass" }
45
45
  end
46
46
  end
47
47
 
48
- it "should marshal indexes properly" do
49
- # This really tests both storing and fetching indexes, given the setup
48
+ sometimes "should marshal indexes properly", :version => "1.0.0", :retries => 5 do
50
49
  robj = @backend.fetch_object('test', 'fetch')
51
50
  robj.indexes['test_bin'].should be
52
51
  robj.indexes['test_bin'].should include('pass')
@@ -74,14 +73,16 @@ shared_examples_for "Unified backend API" do
74
73
  @backend.reload_object(@robject, :r => q)
75
74
  end
76
75
 
77
- it "should accept a valid PR value of #{q.inspect} for the request" do
76
+ it "should accept a valid PR value of #{q.inspect} for the request", :version => "1.0.0" do
78
77
  @backend.reload_object(@robject, :pr => q)
79
78
  end
80
79
  end
81
80
 
82
81
  after do
83
- @robject.vclock.should == @robject2.vclock
84
- @robject.data['test'].should == "second"
82
+ unless example.pending?
83
+ @robject.vclock.should == @robject2.vclock
84
+ @robject.data['test'].should == "second"
85
+ end
85
86
  end
86
87
  end
87
88
 
@@ -112,7 +113,7 @@ shared_examples_for "Unified backend API" do
112
113
  @backend.store_object(@robject, :returnbody => false, :w => :all, :dw => q)
113
114
  end
114
115
 
115
- it "should accept a PW value of #{q.inspect} for the request" do
116
+ it "should accept a PW value of #{q.inspect} for the request", :version => "1.0.0" do
116
117
  @backend.store_object(@robject, :returnbody => false, :pw => q)
117
118
  end
118
119
  end
@@ -206,7 +207,7 @@ shared_examples_for "Unified backend API" do
206
207
  @backend.list_keys("test") do |keys|
207
208
  keys.each do |key|
208
209
  begin
209
- @backend.fetch_object("test", key)
210
+ @client.get_object("test", key)
210
211
  rescue => e
211
212
  errors << e
212
213
  end
@@ -263,7 +264,7 @@ shared_examples_for "Unified backend API" do
263
264
  unless result.empty?
264
265
  result.each do |v|
265
266
  begin
266
- @backend.fetch_object("test", v['value'])
267
+ @client.get_object("test", v['value'])
267
268
  rescue => e
268
269
  errors << e
269
270
  end