riak-client 1.0.0.beta → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -4
- data/Gemfile +12 -17
- data/Guardfile +1 -1
- data/LICENSE +16 -0
- data/README.markdown +178 -0
- data/RELEASE_NOTES.md +99 -0
- data/Rakefile +25 -1
- data/erl_src/riak_kv_test014_backend.beam +0 -0
- data/erl_src/riak_kv_test014_backend.erl +189 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +37 -19
- data/lib/riak/client.rb +322 -272
- data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
- data/lib/riak/client/decaying.rb +28 -0
- data/lib/riak/client/excon_backend.rb +27 -11
- data/lib/riak/client/http_backend.rb +71 -2
- data/lib/riak/client/http_backend/configuration.rb +17 -3
- data/lib/riak/client/http_backend/transport_methods.rb +3 -3
- data/lib/riak/client/net_http_backend.rb +18 -14
- data/lib/riak/client/node.rb +111 -0
- data/lib/riak/client/pool.rb +180 -0
- data/lib/riak/client/protobuffs_backend.rb +15 -5
- data/lib/riak/client/search.rb +9 -3
- data/lib/riak/link.rb +5 -7
- data/lib/riak/locale/en.yml +1 -0
- data/lib/riak/node/configuration.rb +1 -0
- data/lib/riak/node/defaults.rb +19 -6
- data/lib/riak/node/generation.rb +9 -2
- data/lib/riak/node/log.rb +2 -2
- data/lib/riak/node/version.rb +22 -16
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/serializers.rb +1 -1
- data/lib/riak/test_server.rb +10 -2
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +3 -3
- data/spec/failover/failover.rb +59 -0
- data/spec/integration/riak/http_backends_spec.rb +2 -2
- data/spec/integration/riak/node_spec.rb +16 -24
- data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
- data/spec/integration/riak/test_server_spec.rb +4 -3
- data/spec/integration/riak/threading_spec.rb +193 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
- data/spec/riak/bucket_spec.rb +2 -1
- data/spec/riak/client_spec.rb +80 -181
- data/spec/riak/excon_backend_spec.rb +3 -2
- data/spec/riak/http_backend/configuration_spec.rb +37 -5
- data/spec/riak/http_backend/object_methods_spec.rb +1 -1
- data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
- data/spec/riak/http_backend_spec.rb +53 -3
- data/spec/riak/map_reduce_spec.rb +1 -1
- data/spec/riak/net_http_backend_spec.rb +1 -2
- data/spec/riak/node_spec.rb +173 -0
- data/spec/riak/pool_spec.rb +306 -0
- data/spec/riak/robject_spec.rb +8 -4
- data/spec/riak/search_spec.rb +66 -15
- data/spec/riak/serializers_spec.rb +12 -1
- data/spec/spec_helper.rb +9 -1
- data/spec/support/http_backend_implementation_examples.rb +6 -2
- data/spec/support/sometimes.rb +46 -0
- data/spec/support/test_server.rb +50 -19
- data/spec/support/unified_backend_examples.rb +11 -10
- data/spec/support/version_filter.rb +14 -0
- metadata +40 -29
- data/lib/active_support/cache/riak_store.rb +0 -2
- data/lib/riak/cache_store.rb +0 -84
- data/lib/riak/client/pump.rb +0 -30
- data/lib/riak/util/fiber1.8.rb +0 -48
- data/spec/integration/riak/cache_store_spec.rb +0 -129
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
|