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
Binary file
@@ -52,7 +52,9 @@
52
52
  is_empty/1,
53
53
  status/1,
54
54
  callback/3,
55
- reset/0]).
55
+ reset/0,
56
+ capabilities/1,
57
+ capabilities/2]).
56
58
 
57
59
  -ifdef(TEST).
58
60
  -include_lib("eunit/include/eunit.hrl").
@@ -79,7 +81,7 @@
79
81
  -spec reset() -> ok | {error, timeout}.
80
82
  reset() ->
81
83
  {ok, Ring} = riak_core_ring_manager:get_my_ring(),
82
- [ ets:delete_all_objects(list_to_atom("kv" ++ integer_to_list(P))) ||
84
+ [ catch ets:delete_all_objects(list_to_atom("kv" ++ integer_to_list(P))) ||
83
85
  P <- riak_core_ring:my_indices(Ring) ],
84
86
  ok.
85
87
 
@@ -87,28 +89,44 @@ reset() ->
87
89
 
88
90
  %% @doc Return the major version of the
89
91
  %% current API and a capabilities list.
90
- -spec api_version() -> {integer(), [atom()]}.
92
+ -spec api_version() -> {ok, integer()} | {integer(), [atom()]}.
91
93
  api_version() ->
92
- {?API_VERSION, ?CAPABILITIES}.
94
+ case lists:member({capabilities, 1}, riak_kv_backend:behaviour_info(callbacks)) of
95
+ true -> % Using 1.1 API or later
96
+ {ok, ?API_VERSION};
97
+ _ -> % Using 1.0 API
98
+ {?API_VERSION, ?CAPABILITIES}
99
+ end.
100
+
101
+ %% @doc Return the capabilities of the backend.
102
+ -spec capabilities(state()) -> {ok, [atom()]}.
103
+ capabilities(_) ->
104
+ {ok, ?CAPABILITIES}.
105
+
106
+ %% @doc Return the capabilities of the backend.
107
+ -spec capabilities(riak_object:bucket(), state()) -> {ok, [atom()]}.
108
+ capabilities(_, _) ->
109
+ {ok, ?CAPABILITIES}.
93
110
 
94
111
  %% @doc Start the memory backend
95
112
  -spec start(integer(), config()) -> {ok, state()}.
96
- start(Partition, Config) ->
97
- TTL = config_value(ttl, Config),
98
- MemoryMB = config_value(max_memory, Config),
99
- case MemoryMB of
100
- undefined ->
101
- MaxMemory = undefined,
102
- TimeRef = undefined;
103
- _ ->
104
- MaxMemory = MemoryMB * 1024 * 1024,
105
- TimeRef = ets:new(list_to_atom(integer_to_list(Partition)), [ordered_set])
106
- end,
113
+ start(Partition, _Config) ->
114
+ %% TTL = config_value(ttl, Config),
115
+ %% MemoryMB = config_value(max_memory, Config),
116
+ %% case MemoryMB of
117
+ %% undefined ->
118
+ %% MaxMemory = undefined,
119
+ %% TimeRef = undefined;
120
+ %% _ ->
121
+ %% MaxMemory = MemoryMB * 1024 * 1024,
122
+ %% TimeRef = ets:new(list_to_atom(integer_to_list(Partition)), [ordered_set])
123
+ %% end,
107
124
  DataRef = ets:new(list_to_atom("kv" ++ integer_to_list(Partition)), [named_table, public]),
108
- {ok, #state{data_ref=DataRef,
109
- max_memory=MaxMemory,
110
- time_ref=TimeRef,
111
- ttl=TTL}}.
125
+ {ok, #state{data_ref=DataRef
126
+ %% max_memory=MaxMemory,
127
+ %% time_ref=TimeRef,
128
+ %% ttl=TTL
129
+ }}.
112
130
 
113
131
  %% @doc Stop the memory backend
114
132
  -spec stop(state()) -> ok.
data/lib/riak/client.rb CHANGED
@@ -4,6 +4,9 @@ require 'riak'
4
4
  require 'riak/util/translation'
5
5
  require 'riak/util/escape'
6
6
  require 'riak/failed_request'
7
+ require 'riak/client/pool'
8
+ require 'riak/client/decaying'
9
+ require 'riak/client/node'
7
10
  require 'riak/client/search'
8
11
  require 'riak/client/http_backend'
9
12
  require 'riak/client/net_http_backend'
@@ -28,44 +31,50 @@ module Riak
28
31
  # Regexp for validating hostnames, lifted from uri.rb in Ruby 1.8.6
29
32
  HOST_REGEX = /^(?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\])$/n
30
33
 
31
- VALID_OPTIONS = [:protocol, :host, :port, :http_port, :pb_port, :prefix, :client_id, :mapred, :luwak, :solr, :http_paths, :http_backend, :protobuffs_backend, :ssl, :basic_auth]
34
+ # Valid constructor options.
35
+ VALID_OPTIONS = [:protocol, :nodes, :client_id, :http_backend, :protobuffs_backend] | Node::VALID_OPTIONS
36
+
37
+ # Network errors.
38
+ NETWORK_ERRORS = [
39
+ EOFError,
40
+ Errno::ECONNABORTED,
41
+ Errno::ECONNREFUSED,
42
+ Errno::ECONNRESET,
43
+ Errno::ENETDOWN,
44
+ Errno::ENETRESET,
45
+ Errno::ENETUNREACH,
46
+ SocketError,
47
+ SystemCallError,
48
+ ]
32
49
 
33
50
  # @return [String] The protocol to use for the Riak endpoint
34
51
  attr_reader :protocol
35
52
 
36
- # @return [String] The host or IP address for the Riak endpoint
37
- attr_reader :host
38
-
39
- # @return [Fixnum] The HTTP(S) port for the Riak endpoint
40
- attr_reader :http_port
41
-
42
- # @return [Fixnum] The Protocol Buffers port for the Riak endpoint
43
- attr_reader :pb_port
44
-
45
- # @return [String] The user:pass for http basic authentication
46
- attr_reader :basic_auth
47
-
48
- # @return [Hash|nil] The SSL options that get built when using SSL
49
- attr_reader :ssl_options
53
+ # @return [Array] The set of Nodes this client can communicate with.
54
+ attr_accessor :nodes
50
55
 
51
56
  # @return [String] The internal client ID used by Riak to route responses
52
57
  attr_reader :client_id
53
58
 
54
- # @attr_writer [Hash|nil] The writer that will build valid SSL options
55
- # from the provided config
56
- attr_writer :ssl
57
-
58
- # @return [Hash] HTTP path configuration.
59
- attr_accessor :http_paths
60
-
61
59
  # @return [Symbol] The HTTP backend/client to use
62
60
  attr_accessor :http_backend
63
61
 
62
+ # @return [Client::Pool] A pool of HTTP connections
63
+ attr_reader :http_pool
64
+
64
65
  # @return [Symbol] The Protocol Buffers backend/client to use
65
66
  attr_accessor :protobuffs_backend
66
67
 
68
+ # @return [Client::Pool] A pool of protobuffs connections
69
+ attr_reader :protobuffs_pool
70
+
67
71
  # Creates a client connection to Riak
68
72
  # @param [Hash] options configuration options for the client
73
+ # @option options [Array] :nodes A list of nodes this client connects to.
74
+ # Each element of the list is a hash which is passed to Node.new, e.g.
75
+ # {host: '127.0.0.1', pb_port: 1234, ...}.
76
+ # If no nodes are given, a single node is constructed from the remaining
77
+ # options given to Client.new.
69
78
  # @option options [String] :host ('127.0.0.1') The host or IP address for the Riak endpoint
70
79
  # @option options [Fixnum] :http_port (8098) The port of the Riak HTTP endpoint
71
80
  # @option options [Fixnum] :pb_port (8087) The port of the Riak Protocol Buffers endpoint
@@ -76,192 +85,56 @@ module Riak
76
85
  # @option options [String, Symbol] :protobuffs_backend (:Beefcake) which Protocol Buffers backend to use
77
86
  # @raise [ArgumentError] raised if any invalid options are given
78
87
  def initialize(options={})
79
- unless (options.keys - VALID_OPTIONS).empty?
80
- raise ArgumentError, t("invalid_options")
88
+ if options.include? :port
89
+ warn(t('deprecated.port', :backtrace => caller[0..2].join("\n ")))
81
90
  end
82
- self.protocol = options[:protocol] || "http"
83
- self.ssl = options[:ssl] if options[:ssl]
84
- self.host = options[:host] || "127.0.0.1"
85
- self.http_port = options[:http_port] || 8098
86
- self.pb_port = options[:pb_port] || 8087
87
- self.port = options[:port] if options[:port]
88
- self.http_paths = {
89
- prefix: options[:prefix] || "/riak/",
90
- mapred: options[:mapred] || "/mapred",
91
- luwak: options[:luwak] || "/luwak",
92
- solr: options[:solr] || "/solr" # Unused?
93
- }.merge(options[:http_paths] || {})
94
- self.http_backend = options[:http_backend] || :NetHTTP
95
- self.protobuffs_backend = options[:protobuffs_backend] || :Beefcake
96
- self.basic_auth = options[:basic_auth] if options[:basic_auth]
97
- self.client_id = options[:client_id] if options[:client_id]
98
- end
99
91
 
100
- # Set the client ID for this client. Must be a string or Fixnum value 0 =< value < MAX_CLIENT_ID.
101
- # @param [String, Fixnum] value The internal client ID used by Riak to route responses
102
- # @raise [ArgumentError] when an invalid client ID is given
103
- # @return [String] the assigned client ID
104
- def client_id=(value)
105
- value = case value
106
- when 0...MAX_CLIENT_ID, String
107
- value
108
- else
109
- raise ArgumentError, t("invalid_client_id", :max_id => MAX_CLIENT_ID)
110
- end
111
- backend.set_client_id value if backend.respond_to?(:set_client_id)
112
- @client_id = value
113
- end
114
-
115
- def client_id
116
- @client_id ||= backend.respond_to?(:get_client_id) ? backend.get_client_id : make_client_id
117
- end
118
-
119
- # Set the protocol of the Riak endpoint. Value must be in the
120
- # Riak::Client::PROTOCOLS array.
121
- # @raise [ArgumentError] if the protocol is not in PROTOCOLS
122
- # @return [String] the protocol being assigned
123
- def protocol=(value)
124
- unless PROTOCOLS.include?(value.to_s)
125
- raise ArgumentError, t("protocol_invalid", :invalid => value, :valid => PROTOCOLS.join(', '))
92
+ unless (evil = options.keys - VALID_OPTIONS).empty?
93
+ raise ArgumentError, "#{evil.inspect} are not valid options for Client.new"
126
94
  end
127
- @ssl_options ||= {} if value == 'https'
128
- @ssl_options = nil if value == 'http'
129
- @backend = nil
130
- @protocol = value
131
- end
132
-
133
- # Set the hostname of the Riak endpoint. Must be an IPv4, IPv6, or valid hostname
134
- # @param [String] value The host or IP address for the Riak endpoint
135
- # @raise [ArgumentError] if an invalid hostname is given
136
- # @return [String] the assigned hostname
137
- def host=(value)
138
- raise ArgumentError, t("hostname_invalid") unless String === value && value.present? && value =~ HOST_REGEX
139
- @host = value
140
- end
141
95
 
142
- # @return [Fixnum] The port of the Riak endpoint
143
- # @deprecated Ports for HTTP(S) and Protocol Buffers are
144
- # segregated. Use {#http_port} or {#pb_port}.
145
- def port
146
- warn(t('deprecated.port', :backtrace => caller.join("\n")))
147
- case protocol
148
- when /http/i
149
- http_port
150
- when /pbc/i
151
- pb_port
152
- end
153
- end
154
-
155
- # Set the port number of the Riak endpoint. This must be an
156
- # integer between 0 and 65535.
157
- # @deprecated Ports for HTTP(S) and Protocol Buffers are
158
- # segregated. Use {#http_port=} or {#pb_port=}.
159
- # @param [Fixnum] value The port number of the Riak endpoint
160
- # @raise [ArgumentError] if an invalid port number is given
161
- # @return [Fixnum] the assigned port number
162
- def port=(value)
163
- warn(t('deprecated.port', :backtrace => caller[0..2].join("\n ")))
164
- raise ArgumentError, t("port_invalid") unless (0..65535).include?(value)
165
- case protocol
166
- when /http/i
167
- self.http_port = value
168
- when /pbc/i
169
- self.pb_port = value
96
+ @nodes = (options[:nodes] || []).map do |n|
97
+ Client::Node.new self, n
98
+ end
99
+ if @nodes.empty? or options[:host] or options[:http_port] or options[:pb_port]
100
+ @nodes |= [Client::Node.new(self, options)]
170
101
  end
171
- end
172
-
173
- # Set the HTTP(S) port for the Riak endpoint
174
- # @param [Fixnum] value The HTTP port number of the Riak endpoint
175
- # @raise [ArgumentError] if an invalid port number is given
176
- # @return [Fixnum] the assigned port number
177
- def http_port=(value)
178
- raise ArgumentError, t("port_invalid") unless (0..65535).include?(value)
179
- @http_port = value
180
- end
181
-
182
- # Set the Protocol Buffers port for the Riak endpoint
183
- # @param [Fixnum] value The Protocol Buffers port number of the Riak endpoint
184
- # @raise [ArgumentError] if an invalid port number is given
185
- # @return [Fixnum] the assigned port number
186
- def pb_port=(value)
187
- raise ArgumentError, t("port_invalid") unless (0..65535).include?(value)
188
- @pb_port = value
189
- end
190
-
191
102
 
192
- # Sets the HTTP Basic Authentication credentials.
193
- # @param [String] value an auth string in the form "user:password"
194
- def basic_auth=(value)
195
- raise ArgumentError, t("invalid_basic_auth") unless value.to_s.split(':').length === 2
196
- @basic_auth = value
197
- end
103
+ @protobuffs_pool = Pool.new(
104
+ method(:new_protobuffs_backend),
105
+ lambda { |b| b.teardown }
106
+ )
198
107
 
199
- # Sets the desired HTTP backend
200
- def http_backend=(value)
201
- @http, @backend = nil, nil
202
- @http_backend = value
203
- end
108
+ @http_pool = Pool.new(
109
+ method(:new_http_backend),
110
+ lambda { |b| b.teardown }
111
+ )
204
112
 
205
- # Sets the desired Protocol Buffers backend
206
- def protobuffs_backend=(value)
207
- @protobuffs, @backend = nil, nil
208
- @protobuffs_backend = value
113
+ self.protocol = options[:protocol] || "http"
114
+ self.http_backend = options[:http_backend] || :NetHTTP
115
+ self.protobuffs_backend = options[:protobuffs_backend] || :Beefcake
116
+ self.client_id = options[:client_id] if options[:client_id]
209
117
  end
210
118
 
211
- # Enables or disables SSL on the client to be utilized by the HTTP Backends
212
- def ssl=(value)
213
- @ssl_options = Hash === value ? value : {}
214
- value ? ssl_enable : ssl_disable
215
- end
216
-
217
- # Checks if SSL is enabled for HTTP
218
- def ssl_enabled?
219
- protocol == 'https' || @ssl_options.present?
220
- end
221
-
222
- # Automatically detects and returns an appropriate HTTP backend.
223
- # The HTTP backend is used internally by the Riak client, but can also
224
- # be used to access the server directly.
225
- # @return [HTTPBackend] the HTTP backend for this client
226
- def http
227
- @http ||= begin
228
- klass = self.class.const_get("#{@http_backend}Backend")
229
- if klass.configured?
230
- klass.new(self)
231
- else
232
- raise t('http_configuration', :backend => @http_backend)
233
- end
234
- end
235
- end
236
-
237
- # Automatically detects and returns an appropriate Protocol
238
- # Buffers backend. The Protocol Buffers backend is used
239
- # internally by the Riak client but can also be used to access the
240
- # server directly.
241
- # @return [ProtobuffsBackend] the Protocol Buffers backend for
242
- # this client
243
- def protobuffs
244
- @protobuffs ||= begin
245
- klass = self.class.const_get("#{@protobuffs_backend}ProtobuffsBackend")
246
- if klass.configured?
247
- klass.new(self)
248
- else
249
- raise t('protobuffs_configuration', :backend => @protobuffs_backend)
250
- end
251
- end
252
- end
253
-
254
- # Returns a backend for operations that are protocol-independent.
119
+ # Yields a backend for operations that are protocol-independent.
255
120
  # You can change which type of backend is used by setting the
256
121
  # {#protocol}.
257
- # @return [HTTPBackend,ProtobuffsBackend] an appropriate client backend
258
- def backend
259
- @backend ||= case @protocol.to_s
260
- when /https?/i
261
- http
262
- when /pbc/i
263
- protobuffs
264
- end
122
+ # @yield [HTTPBackend,ProtobuffsBackend] an appropriate client backend
123
+ def backend(&block)
124
+ case @protocol.to_s
125
+ when /https?/i
126
+ http &block
127
+ when /pbc/i
128
+ protobuffs &block
129
+ end
130
+ end
131
+
132
+ # Sets basic HTTP auth on all nodes.
133
+ def basic_auth=(auth)
134
+ @nodes.each do |node|
135
+ node.basic_auth = auth
136
+ end
137
+ auth
265
138
  end
266
139
 
267
140
  # Retrieves a bucket from Riak.
@@ -286,34 +159,90 @@ module Riak
286
159
  # @return [Array<Bucket>] a list of buckets
287
160
  def buckets
288
161
  warn(t('list_buckets', :backtrace => caller.join("\n "))) unless Riak.disable_list_keys_warnings
289
- backend.list_buckets.map {|name| Bucket.new(self, name) }
162
+ backend do |b|
163
+ b.list_buckets.map {|name| Bucket.new(self, name) }
164
+ end
290
165
  end
291
166
  alias :list_buckets :buckets
292
167
 
168
+ # Choose a node from a set.
169
+ def choose_node(nodes = self.nodes)
170
+ # Prefer nodes which have gone a reasonable time without errors.
171
+ s = nodes.select do |node|
172
+ node.error_rate.value < 0.1
173
+ end
174
+
175
+ if s.empty?
176
+ # Fall back to minimally broken node.
177
+ nodes.min_by do |node|
178
+ node.error_rate.value
179
+ end
180
+ else
181
+ s[rand(s.size)]
182
+ end
183
+ end
184
+
185
+ # Set the client ID for this client. Must be a string or Fixnum value 0 =<
186
+ # value < MAX_CLIENT_ID.
187
+ # @param [String, Fixnum] value The internal client ID used by Riak to route responses
188
+ # @raise [ArgumentError] when an invalid client ID is given
189
+ # @return [String] the assigned client ID
190
+ def client_id=(value)
191
+ value = case value
192
+ when 0...MAX_CLIENT_ID, String
193
+ value
194
+ else
195
+ raise ArgumentError, t("invalid_client_id", :max_id => MAX_CLIENT_ID)
196
+ end
197
+
198
+ # Change all existing backend client IDs.
199
+ @protobuffs_pool.each do |pb|
200
+ pb.set_client_id value if pb.respond_to?(:set_client_id)
201
+ end
202
+ @client_id = value
203
+ end
204
+
205
+ def client_id
206
+ @client_id ||= backend do |b|
207
+ if b.respond_to?(:get_client_id)
208
+ b.get_client_id
209
+ else
210
+ make_client_id
211
+ end
212
+ end
213
+ end
214
+
293
215
  # Deletes a file stored via the "Luwak" interface
294
216
  # @param [String] filename the key/filename to delete
295
217
  def delete_file(filename)
296
- http.delete([204,404], http_paths[:luwak], escape(filename))
218
+ http do |h|
219
+ h.delete_file(filename)
220
+ end
297
221
  true
298
222
  end
299
-
223
+
300
224
  # Delete an object. See Bucket#delete
301
225
  def delete_object(bucket, key, options = {})
302
- backend.delete_object(bucket, key, options)
226
+ backend do |b|
227
+ b.delete_object(bucket, key, options)
228
+ end
303
229
  end
304
230
 
305
231
  # Checks whether a file exists in "Luwak".
306
232
  # @param [String] key the key to check
307
233
  # @return [true, false] whether the key exists in "Luwak"
308
234
  def file_exists?(key)
309
- result = http.head([200,404], http_paths[:luwak], escape(key))
310
- result[:code] == 200
235
+ http do |h|
236
+ h.file_exists?(key)
237
+ end
311
238
  end
312
239
  alias :file_exist? :file_exists?
313
240
 
314
241
  # Bucket properties. See Bucket#props
315
242
  def get_bucket_props(bucket)
316
- backend.get_bucket_props bucket
243
+ backend do |b|
244
+ b.get_bucket_props bucket
245
+ end
317
246
  end
318
247
 
319
248
  # Retrieves a large file/IO object from Riak via the "Luwak"
@@ -328,98 +257,225 @@ module Riak
328
257
  # from the method.
329
258
  # @yieldparam [String] chunk a single chunk of the object's data
330
259
  def get_file(filename, &block)
331
- if block_given?
332
- http.get(200, http_paths[:luwak], escape(filename), &block)
333
- nil
334
- else
335
- tmpfile = LuwakFile.new(escape(filename))
336
- begin
337
- response = http.get(200, http_paths[:luwak], escape(filename)) do |chunk|
338
- tmpfile.write chunk
339
- end
340
- tmpfile.content_type = response[:headers]['content-type'].first
341
- tmpfile
342
- ensure
343
- tmpfile.close
344
- end
260
+ http do |h|
261
+ h.get_file(filename, &block)
345
262
  end
346
263
  end
347
264
 
348
265
  # Queries a secondary index on a bucket. See Bucket#get_index
349
266
  def get_index(bucket, index, query)
350
- backend.get_index bucket, index, query
267
+ backend do |b|
268
+ b.get_index bucket, index, query
269
+ end
351
270
  end
352
-
271
+
353
272
  # Get an object. See Bucket#get
354
273
  def get_object(bucket, key, options = {})
355
- backend.fetch_object(bucket, key, options)
274
+ backend do |b|
275
+ b.fetch_object(bucket, key, options)
276
+ end
277
+ end
278
+
279
+ # Yields an HTTPBackend.
280
+ def http(&block)
281
+ recover_from @http_pool, &block
282
+ end
283
+
284
+ # Sets the desired HTTP backend
285
+ def http_backend=(value)
286
+ @http_backend = value
287
+ # Shut down existing connections using the old backend
288
+ @http_pool.clear
289
+ @http_backend
356
290
  end
357
291
 
358
292
  # @return [String] A representation suitable for IRB and debugging output.
359
293
  def inspect
360
- "#<Riak::Client #{protocol}://#{host}:#{protocol == 'pbc' ? pb_port : http_port}>"
294
+ "#<Riak::Client #{nodes.inspect}>"
295
+ end
296
+
297
+ # Link-walk.
298
+ def link_walk(object, specs)
299
+ http do |h|
300
+ h.link_walk object, specs
301
+ end
361
302
  end
362
303
 
363
304
  # Retrieves a list of keys in the given bucket. See Bucket#keys
364
305
  def list_keys(bucket, &block)
365
306
  if block_given?
366
- backend.list_keys bucket, &block
307
+ backend do |b|
308
+ b.list_keys bucket, &block
309
+ end
367
310
  else
368
- backend.list_keys bucket
311
+ backend do |b|
312
+ b.list_keys bucket
313
+ end
369
314
  end
370
315
  end
371
-
372
- # Deprecated accessor for http_paths[:luwak]
373
- def luwak
374
- http_paths[:luwak]
316
+
317
+ # Executes a mapreduce request. See MapReduce#run
318
+ def mapred(mr, &block)
319
+ backend do |b|
320
+ b.mapred(mr, &block)
321
+ end
375
322
  end
376
323
 
377
- # Deprecated accessor for http_paths[:luwak]
378
- def luwak=(luwak)
379
- http_paths[:luwak] = luwak
324
+ # Creates a new HTTP backend.
325
+ # @return [HTTPBackend] An HTTP backend for a given node.
326
+ def new_http_backend
327
+ klass = self.class.const_get("#{@http_backend}Backend")
328
+ if klass.configured?
329
+ node = choose_node(
330
+ @nodes.select do |n|
331
+ n.http?
332
+ end
333
+ )
334
+
335
+ klass.new(self, node)
336
+ else
337
+ raise t('http_configuration', :backend => @http_backend)
338
+ end
380
339
  end
381
340
 
382
- # Executes a mapreduce request. See MapReduce#run
383
- def mapred(mr, &block)
384
- backend.mapred(mr, &block)
341
+ # Creates a new protocol buffers backend.
342
+ # @return [ProtobuffsBackend] the Protocol Buffers backend for
343
+ # a given node.
344
+ def new_protobuffs_backend
345
+ klass = self.class.const_get("#{@protobuffs_backend}ProtobuffsBackend")
346
+ if klass.configured?
347
+ node = choose_node(
348
+ @nodes.select do |n|
349
+ n.protobuffs?
350
+ end
351
+ )
352
+
353
+ klass.new(self, node)
354
+ else
355
+ raise t('protobuffs_configuration', :backend => @protobuffs_backend)
356
+ end
385
357
  end
386
358
 
387
- # Pings the Riak server to check for liveness.
388
- # @return [true,false] whether the Riak server is alive and reachable
359
+ # @return [Node] An arbitrary Node.
360
+ def node
361
+ nodes[rand nodes.size]
362
+ end
363
+
364
+ # Pings the Riak cluster to check for liveness.
365
+ # @return [true,false] whether the Riak cluster is alive and reachable
389
366
  def ping
390
- backend.ping
367
+ backend do |b|
368
+ b.ping
369
+ end
391
370
  end
392
-
393
- # Deprecated accessor for http_paths[:prefix]
394
- def prefix
395
- http_paths[:prefix]
371
+
372
+ # Yields a protocol buffers backend.
373
+ def protobuffs(&block)
374
+ recover_from @protobuffs_pool, &block
396
375
  end
397
376
 
398
- # Deprecated accessor http_paths[:prefix]
399
- def prefix=(prefix)
400
- http_paths[:prefix] = prefix
377
+ # Sets the desired Protocol Buffers backend
378
+ def protobuffs_backend=(value)
379
+ # Shutdown any connections using the old backend
380
+ @protobuffs_backend = value
381
+ @protobuffs_pool.clear
382
+ @protobuffs_backend
401
383
  end
402
384
 
403
- # Reloads the object from Riak.
404
- def reload_object(object, options = {})
405
- backend.reload_object(object, options)
385
+ # Set the protocol of the Riak endpoint. Value must be in the
386
+ # Riak::Client::PROTOCOLS array.
387
+ # @raise [ArgumentError] if the protocol is not in PROTOCOLS
388
+ # @return [String] the protocol being assigned
389
+ def protocol=(value)
390
+ unless PROTOCOLS.include?(value.to_s)
391
+ raise ArgumentError, t("protocol_invalid", :invalid => value, :valid => PROTOCOLS.join(', '))
392
+ end
393
+
394
+ case value
395
+ when 'https'
396
+ nodes.each do |node|
397
+ node.ssl_options ||= {}
398
+ end
399
+ when 'http'
400
+ nodes.each do |node|
401
+ node.ssl_options = nil
402
+ end
403
+ end
404
+
405
+ #TODO
406
+ @backend = nil
407
+ @protocol = value
406
408
  end
407
-
408
- # Deprecated accessor for http_paths[:solr]
409
- def solr
410
- http_paths[:solr]
409
+
410
+ # Takes a pool. Acquires a backend from the pool and yields it with
411
+ # node-specific error recovery.
412
+ def recover_from(pool)
413
+ skip_nodes = []
414
+ take_opts = {}
415
+ tries = 3
416
+
417
+ begin
418
+ # Only select nodes which we haven't used before.
419
+ unless skip_nodes.empty?
420
+ take_opts[:filter] = lambda do |backend|
421
+ not skip_nodes.include? backend.node
422
+ end
423
+ end
424
+
425
+ # Acquire a backend
426
+ pool.take(take_opts) do |backend|
427
+ begin
428
+ yield backend
429
+ rescue *NETWORK_ERRORS => e
430
+ # Network error.
431
+ tries -= 1
432
+
433
+ # Notify the node that a request against it failed.
434
+ backend.node.error_rate << 1
435
+
436
+ # Skip this node next time.
437
+ skip_nodes << backend.node
438
+
439
+ # And delete this connection.
440
+ raise Pool::BadResource, e
441
+ end
442
+ end
443
+ rescue Pool::BadResource => e
444
+ retry if tries > 0
445
+ raise e.message
446
+ end
411
447
  end
412
448
 
413
- # Deprecated accessor for http_paths[:solr]
414
- def solr=(solr)
415
- http_paths[:solr] = solr
449
+ # Reloads the object from Riak.
450
+ def reload_object(object, options = {})
451
+ backend do |b|
452
+ b.reload_object(object, options)
453
+ end
416
454
  end
417
455
 
418
456
  # Sets the properties on a bucket. See Bucket#props=
419
457
  def set_bucket_props(bucket, properties)
420
- backend.set_bucket_props(bucket, properties)
458
+ # A bug in Beefcake is still giving us trouble with default booleans.
459
+ # Until it is resolved, we'll use the HTTP backend.
460
+ http do |b|
461
+ b.set_bucket_props(bucket, properties)
462
+ end
421
463
  end
422
-
464
+
465
+ # Enables or disables SSL on all nodes, for HTTP backends.
466
+ def ssl=(value)
467
+ @nodes.each do |node|
468
+ node.ssl = value
469
+ end
470
+
471
+ if value
472
+ @protocol = 'https'
473
+ else
474
+ @protocol = 'http'
475
+ end
476
+ value
477
+ end
478
+
423
479
  # Exposes a {Stamp} object for use in generating unique
424
480
  # identifiers.
425
481
  # @return [Stamp] an ID generator
@@ -440,20 +496,17 @@ module Riak
440
496
  # @param [String, #read] data the contents of the file
441
497
  # @return [String] the key/filename where the object was stored
442
498
  def store_file(*args)
443
- data, content_type, filename = args.reverse
444
- if filename
445
- http.put(204, http_paths[:luwak], escape(filename), data, {"Content-Type" => content_type})
446
- filename
447
- else
448
- response = http.post(201, http_paths[:luwak], data, {"Content-Type" => content_type})
449
- response[:headers]["location"].first.split("/").last
499
+ http do |h|
500
+ h.store_file(*args)
450
501
  end
451
502
  end
452
503
 
453
504
  # Stores an object in Riak.
454
505
  def store_object(object, options = {})
455
506
  params = {:returnbody => true}.merge(options)
456
- backend.store_object(object, params)
507
+ backend do |b|
508
+ b.store_object(object, params)
509
+ end
457
510
  end
458
511
 
459
512
  private
@@ -462,18 +515,15 @@ module Riak
462
515
  end
463
516
 
464
517
  def ssl_enable
465
- self.protocol = 'https'
466
- @ssl_options[:pem] = File.read(@ssl_options[:pem_file]) if @ssl_options[:pem_file]
467
- @ssl_options[:verify_mode] ||= "peer" if @ssl_options.stringify_keys.any? {|k,v| %w[pem ca_file ca_path].include?(k)}
468
- @ssl_options[:verify_mode] ||= "none"
469
- raise ArgumentError.new(t('invalid_ssl_verify_mode', :invalid => @ssl_options[:verify_mode])) unless %w[none peer].include?(@ssl_options[:verify_mode])
470
-
471
- @ssl_options
518
+ @nodes.each do |n|
519
+ n.ssl_enable
520
+ end
472
521
  end
473
522
 
474
523
  def ssl_disable
475
- self.protocol = 'http'
476
- @ssl_options = nil
524
+ @nodes.each do |n|
525
+ n.ssl_disable
526
+ end
477
527
  end
478
528
 
479
529
  # @private