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.
- 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
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
|
-
{
|
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,
|
97
|
-
TTL = config_value(ttl, Config),
|
98
|
-
MemoryMB = config_value(max_memory, Config),
|
99
|
-
case MemoryMB of
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
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 [
|
37
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
end
|
108
|
+
@http_pool = Pool.new(
|
109
|
+
method(:new_http_backend),
|
110
|
+
lambda { |b| b.teardown }
|
111
|
+
)
|
204
112
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
#
|
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
|
-
# @
|
258
|
-
def backend
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
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
|
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
|
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
|
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
|
-
|
310
|
-
|
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
|
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
|
-
|
332
|
-
|
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
|
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
|
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 #{
|
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
|
307
|
+
backend do |b|
|
308
|
+
b.list_keys bucket, &block
|
309
|
+
end
|
367
310
|
else
|
368
|
-
backend
|
311
|
+
backend do |b|
|
312
|
+
b.list_keys bucket
|
313
|
+
end
|
369
314
|
end
|
370
315
|
end
|
371
|
-
|
372
|
-
#
|
373
|
-
def
|
374
|
-
|
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
|
-
#
|
378
|
-
|
379
|
-
|
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
|
-
#
|
383
|
-
|
384
|
-
|
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
|
-
#
|
388
|
-
|
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
|
367
|
+
backend do |b|
|
368
|
+
b.ping
|
369
|
+
end
|
391
370
|
end
|
392
|
-
|
393
|
-
#
|
394
|
-
def
|
395
|
-
|
371
|
+
|
372
|
+
# Yields a protocol buffers backend.
|
373
|
+
def protobuffs(&block)
|
374
|
+
recover_from @protobuffs_pool, &block
|
396
375
|
end
|
397
376
|
|
398
|
-
#
|
399
|
-
def
|
400
|
-
|
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
|
-
#
|
404
|
-
|
405
|
-
|
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
|
-
#
|
409
|
-
|
410
|
-
|
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
|
-
#
|
414
|
-
def
|
415
|
-
|
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
|
-
|
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
|
-
|
444
|
-
|
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
|
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
|
-
|
466
|
-
|
467
|
-
|
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
|
-
|
476
|
-
|
524
|
+
@nodes.each do |n|
|
525
|
+
n.ssl_disable
|
526
|
+
end
|
477
527
|
end
|
478
528
|
|
479
529
|
# @private
|