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
@@ -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