riak-client 1.2.0 → 1.4.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +1 -7
- data/README.markdown +66 -0
- data/RELEASE_NOTES.md +27 -0
- data/lib/riak/bucket.rb +24 -5
- data/lib/riak/client.rb +42 -7
- data/lib/riak/client/beefcake/message_codes.rb +56 -0
- data/lib/riak/client/beefcake/messages.rb +190 -18
- data/lib/riak/client/beefcake_protobuffs_backend.rb +143 -10
- data/lib/riak/client/feature_detection.rb +26 -1
- data/lib/riak/client/http_backend.rb +58 -9
- data/lib/riak/client/http_backend/bucket_streamer.rb +15 -0
- data/lib/riak/client/http_backend/chunked_json_streamer.rb +42 -0
- data/lib/riak/client/http_backend/configuration.rb +17 -1
- data/lib/riak/client/http_backend/key_streamer.rb +4 -32
- data/lib/riak/client/protobuffs_backend.rb +12 -34
- data/lib/riak/counter.rb +101 -0
- data/lib/riak/index_collection.rb +71 -0
- data/lib/riak/list_buckets.rb +28 -0
- data/lib/riak/locale/en.yml +14 -0
- data/lib/riak/multiget.rb +123 -0
- data/lib/riak/node.rb +2 -0
- data/lib/riak/node/configuration.rb +32 -21
- data/lib/riak/node/defaults.rb +2 -0
- data/lib/riak/node/generation.rb +19 -7
- data/lib/riak/node/version.rb +2 -16
- data/lib/riak/robject.rb +1 -0
- data/lib/riak/secondary_index.rb +67 -0
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +3 -2
- data/spec/integration/riak/counters_spec.rb +51 -0
- data/spec/integration/riak/http_backends_spec.rb +24 -14
- data/spec/integration/riak/node_spec.rb +6 -28
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +84 -0
- data/spec/riak/bucket_spec.rb +55 -5
- data/spec/riak/client_spec.rb +34 -0
- data/spec/riak/counter_spec.rb +122 -0
- data/spec/riak/index_collection_spec.rb +50 -0
- data/spec/riak/list_buckets_spec.rb +41 -0
- data/spec/riak/multiget_spec.rb +76 -0
- data/spec/riak/robject_spec.rb +4 -1
- data/spec/riak/secondary_index_spec.rb +225 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/sometimes.rb +2 -2
- data/spec/support/unified_backend_examples.rb +4 -0
- metadata +41 -47
@@ -1,40 +1,12 @@
|
|
1
|
-
require 'riak/
|
2
|
-
require 'riak/json'
|
1
|
+
require 'riak/client/http_backend/chunked_json_streamer'
|
3
2
|
|
4
3
|
module Riak
|
5
4
|
class Client
|
6
5
|
class HTTPBackend
|
7
6
|
# @private
|
8
|
-
class KeyStreamer
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(block)
|
12
|
-
@buffer = ""
|
13
|
-
@block = block
|
14
|
-
end
|
15
|
-
|
16
|
-
def accept(chunk)
|
17
|
-
@buffer << chunk
|
18
|
-
consume
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_proc
|
22
|
-
method(:accept).to_proc
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def consume
|
27
|
-
while @buffer =~ /\}\{/
|
28
|
-
stream($~.pre_match + '}')
|
29
|
-
@buffer = '{' + $~.post_match
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def stream(str)
|
34
|
-
obj = JSON.parse(str) rescue nil
|
35
|
-
if obj && obj['keys']
|
36
|
-
@block.call obj['keys'].map(&method(:maybe_unescape))
|
37
|
-
end
|
7
|
+
class KeyStreamer < ChunkedJsonStreamer
|
8
|
+
def get_values(obj)
|
9
|
+
obj['keys']
|
38
10
|
end
|
39
11
|
end
|
40
12
|
end
|
@@ -4,6 +4,7 @@ require 'base64'
|
|
4
4
|
require 'digest/sha1'
|
5
5
|
require 'riak/util/translation'
|
6
6
|
require 'riak/client/feature_detection'
|
7
|
+
require 'riak/client/beefcake/message_codes'
|
7
8
|
|
8
9
|
module Riak
|
9
10
|
class Client
|
@@ -12,38 +13,7 @@ module Riak
|
|
12
13
|
include Util::Escape
|
13
14
|
include FeatureDetection
|
14
15
|
|
15
|
-
|
16
|
-
MESSAGE_CODES = %W[
|
17
|
-
ErrorResp
|
18
|
-
PingReq
|
19
|
-
PingResp
|
20
|
-
GetClientIdReq
|
21
|
-
GetClientIdResp
|
22
|
-
SetClientIdReq
|
23
|
-
SetClientIdResp
|
24
|
-
GetServerInfoReq
|
25
|
-
GetServerInfoResp
|
26
|
-
GetReq
|
27
|
-
GetResp
|
28
|
-
PutReq
|
29
|
-
PutResp
|
30
|
-
DelReq
|
31
|
-
DelResp
|
32
|
-
ListBucketsReq
|
33
|
-
ListBucketsResp
|
34
|
-
ListKeysReq
|
35
|
-
ListKeysResp
|
36
|
-
GetBucketReq
|
37
|
-
GetBucketResp
|
38
|
-
SetBucketReq
|
39
|
-
SetBucketResp
|
40
|
-
MapRedReq
|
41
|
-
MapRedResp
|
42
|
-
IndexReq
|
43
|
-
IndexResp
|
44
|
-
SearchQueryReq
|
45
|
-
SearchQueryResp
|
46
|
-
].map {|s| s.intern }.freeze
|
16
|
+
MESSAGE_CODES = BeefcakeMessageCodes
|
47
17
|
|
48
18
|
def self.simple(method, code)
|
49
19
|
define_method method do
|
@@ -62,7 +32,6 @@ module Riak
|
|
62
32
|
simple :ping, :PingReq
|
63
33
|
simple :get_client_id, :GetClientIdReq
|
64
34
|
simple :server_info, :GetServerInfoReq
|
65
|
-
simple :list_buckets, :ListBucketsReq
|
66
35
|
|
67
36
|
# Performs a secondary-index query via emulation through MapReduce.
|
68
37
|
# @param [String, Bucket] bucket the bucket to query
|
@@ -144,6 +113,9 @@ module Riak
|
|
144
113
|
unless quorum_controls?
|
145
114
|
[:notfound_ok, :basic_quorum, :pr, :pw].each {|k| options.delete k }
|
146
115
|
end
|
116
|
+
unless key_object_bucket_timeouts?
|
117
|
+
options.delete :timeout
|
118
|
+
end
|
147
119
|
unless pb_head?
|
148
120
|
[:head, :return_head].each {|k| options.delete k }
|
149
121
|
end
|
@@ -160,7 +132,9 @@ module Riak
|
|
160
132
|
def normalize_quorums(options={})
|
161
133
|
options.dup.tap do |o|
|
162
134
|
[:r, :pr, :w, :pw, :dw, :rw].each do |k|
|
163
|
-
o[k] = normalize_quorum_value(o[k]) if o[k]
|
135
|
+
next o[k] = normalize_quorum_value(o[k]) if o[k]
|
136
|
+
s = k.to_s
|
137
|
+
o[k] = o[s] = denormalize_quorum_value(o[s]) if o[s]
|
164
138
|
end
|
165
139
|
end
|
166
140
|
end
|
@@ -168,6 +142,10 @@ module Riak
|
|
168
142
|
def normalize_quorum_value(q)
|
169
143
|
QUORUMS[q.to_s] || q.to_i
|
170
144
|
end
|
145
|
+
|
146
|
+
def denormalize_quorum_value(q)
|
147
|
+
QUORUMS.invert[q] || q.to_i
|
148
|
+
end
|
171
149
|
end
|
172
150
|
end
|
173
151
|
end
|
data/lib/riak/counter.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'riak/failed_request'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
|
5
|
+
# A distributed counter that supports incrementing by positive and negative
|
6
|
+
# integers.
|
7
|
+
class Counter
|
8
|
+
include Util::Translation
|
9
|
+
attr_accessor :bucket
|
10
|
+
attr_accessor :key
|
11
|
+
attr_accessor :client
|
12
|
+
|
13
|
+
# Create a Riak counter.
|
14
|
+
# @param [Bucket] bucket the {Riak::Bucket} for this counter
|
15
|
+
# @param [String] key the name of the counter
|
16
|
+
def initialize(bucket, key)
|
17
|
+
raise ArgumentError, t("bucket_type", bucket: bucket.inspect) unless bucket.is_a? Bucket
|
18
|
+
raise ArgumentError, t("string_type", string: key.inspect) unless key.is_a? String
|
19
|
+
@bucket, @key = bucket, key
|
20
|
+
@client = bucket.client
|
21
|
+
|
22
|
+
validate_bucket
|
23
|
+
end
|
24
|
+
|
25
|
+
# Retrieve the current value of the counter.
|
26
|
+
# @param [Hash] options
|
27
|
+
# @option options [Fixnum,String] :r ("quorum") read quorum (numeric or
|
28
|
+
# symbolic)
|
29
|
+
def value(options={})
|
30
|
+
backend do |backend|
|
31
|
+
backend.get_counter bucket, key, options
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias :to_i :value
|
35
|
+
|
36
|
+
# Increment the counter and return its new value.
|
37
|
+
# @param amount [Integer] the amount to increment the counter by.
|
38
|
+
def increment_and_return(amount=1)
|
39
|
+
increment amount, return_value: true
|
40
|
+
end
|
41
|
+
|
42
|
+
# Decrement the counter and return its new value.
|
43
|
+
# @param amount [Integer] the amount to decrement the counter by. Negative
|
44
|
+
# values increment the counter.
|
45
|
+
def decrement_and_return(amount=1)
|
46
|
+
increment_and_return -amount
|
47
|
+
end
|
48
|
+
|
49
|
+
# Increment the counter.
|
50
|
+
# @param amount [Integer] the amount to increment the counter by
|
51
|
+
# @param [Hash] options
|
52
|
+
# @option options [Boolean] :return_value whether to return the new counter
|
53
|
+
# value. Default false.
|
54
|
+
# @option options [Fixnum,String] :r ("quorum") read quorum (numeric or
|
55
|
+
# symbolic)
|
56
|
+
# @option options [Fixnum] :w the "w" parameter (Write quorum)
|
57
|
+
# @option options [Fixnum] :dw the "dw" parameter (Durable-write quorum)
|
58
|
+
def increment(amount=1, options={})
|
59
|
+
validate_amount amount
|
60
|
+
|
61
|
+
backend do |backend|
|
62
|
+
backend.post_counter bucket, key, amount, options
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Decrement the counter.
|
67
|
+
# @param amount [Integer] the amount to decrement the counter by. Negative
|
68
|
+
# values increment the counter.
|
69
|
+
# @param [Hash] options
|
70
|
+
# @option options [Boolean] :return_value whether to return the new counter
|
71
|
+
# value. Default false.
|
72
|
+
# @option options [Fixnum,String] :r ("quorum") read quorum (numeric or
|
73
|
+
# symbolic)
|
74
|
+
# @option options [Fixnum] :w the "w" parameter (Write quorum)
|
75
|
+
# @option options [Fixnum] :dw the "dw" parameter (Durable-write quorum)
|
76
|
+
def decrement(amount=1, options={})
|
77
|
+
increment(-amount, options)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def validate_bucket
|
82
|
+
raise ArgumentError, t("counter.bucket_needs_allow_mult") unless bucket.allow_mult
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_amount(amount)
|
86
|
+
raise ArgumentError, t("counter.increment_by_integer") unless amount.is_a? Integer
|
87
|
+
end
|
88
|
+
|
89
|
+
def backend(&blk)
|
90
|
+
begin
|
91
|
+
return client.backend &blk
|
92
|
+
rescue Riak::FailedRequest => e
|
93
|
+
raise QuorumError.new e if e.message =~ /unsatisfied/
|
94
|
+
raise e
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class QuorumError < Riak::FailedRequest
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Riak
|
2
|
+
|
3
|
+
# IndexCollection provides extra tools for managing index matches returned by
|
4
|
+
# a Secondary Index query. In Riak 1.4, these queries can be paginaged, and
|
5
|
+
# match keys up with the index values they matched against.
|
6
|
+
class IndexCollection < Array
|
7
|
+
|
8
|
+
# @return [String] The continuation used to retrieve the next page of a
|
9
|
+
# paginated query.
|
10
|
+
attr_accessor :continuation
|
11
|
+
|
12
|
+
# @return [Hash<Integer/String, String>] A hash of index keys (String or
|
13
|
+
# Integer, depending on whether the query was a binary or integer) to
|
14
|
+
# arrays of keys.
|
15
|
+
attr_accessor :with_terms
|
16
|
+
|
17
|
+
def self.new_from_json(json)
|
18
|
+
parsed = JSON.parse json
|
19
|
+
fresh = nil
|
20
|
+
if parsed['keys']
|
21
|
+
fresh = new parsed['keys']
|
22
|
+
elsif parsed['results']
|
23
|
+
fresh_terms = load_json_terms(parsed)
|
24
|
+
fresh = new fresh_terms.values.flatten
|
25
|
+
fresh.with_terms = fresh_terms
|
26
|
+
else
|
27
|
+
fresh = new []
|
28
|
+
end
|
29
|
+
fresh.continuation = parsed['continuation']
|
30
|
+
|
31
|
+
fresh
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.new_from_protobuf(message)
|
35
|
+
fresh = nil
|
36
|
+
if message.keys
|
37
|
+
fresh = new message.keys
|
38
|
+
elsif message.results
|
39
|
+
fresh_terms = load_pb_terms(message)
|
40
|
+
fresh = new fresh_terms.values.flatten
|
41
|
+
fresh.with_terms = fresh_terms
|
42
|
+
else
|
43
|
+
fresh = new
|
44
|
+
end
|
45
|
+
fresh.continuation = message.continuation
|
46
|
+
|
47
|
+
fresh
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def self.load_json_terms(parsed)
|
52
|
+
fresh_terms = Hash.new{Array.new}
|
53
|
+
parsed['results'].each do |r|
|
54
|
+
k = r.keys.first
|
55
|
+
v = r[k]
|
56
|
+
fresh_terms[k] += [v]
|
57
|
+
end
|
58
|
+
|
59
|
+
fresh_terms
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.load_pb_terms(message)
|
63
|
+
fresh_terms = Hash.new{Array.new}
|
64
|
+
message.results.each do |r|
|
65
|
+
fresh_terms[r.key] += [r.value]
|
66
|
+
end
|
67
|
+
|
68
|
+
fresh_terms
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Riak
|
2
|
+
class ListBuckets
|
3
|
+
def initialize(client, options, block)
|
4
|
+
@client = client
|
5
|
+
@block = block
|
6
|
+
@options = options
|
7
|
+
perform_request
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform_request
|
11
|
+
@client.backend do |be|
|
12
|
+
be.list_buckets @options, &wrapped_block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def wrapped_block
|
19
|
+
proc do |bucket_names|
|
20
|
+
next if bucket_names.nil?
|
21
|
+
bucket_names.each do |bucket_name|
|
22
|
+
bucket = @client.bucket bucket_name
|
23
|
+
@block.call bucket
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/riak/locale/en.yml
CHANGED
@@ -2,9 +2,13 @@ en:
|
|
2
2
|
riak:
|
3
3
|
backwards_clock: "System clock moved backwards, ID generation will fail for %{delay} more milliseconds."
|
4
4
|
bucket_link_conversion: "Can't convert a bucket link to a walk spec"
|
5
|
+
bucket_type: "invalid argument %{bucket} is not a Riak::Bucket"
|
5
6
|
client_type: "invalid argument %{client} is not a Riak::Client"
|
6
7
|
conflict_resolver_invalid: "The given resolver (%{resolver}) did not respond to :call"
|
7
8
|
content_type_undefined: "content_type is not defined!"
|
9
|
+
counter:
|
10
|
+
bucket_needs_allow_mult: "Counters require allow_mult to be enabled on their bucket."
|
11
|
+
increment_by_integer: "Counters can only be incremented or decremented by integers."
|
8
12
|
deprecated:
|
9
13
|
port: "DEPRECATION: Riak::Client#port has been deprecated, use #http_port or #pb_port for the appropriate protocol.\n%{backtrace}"
|
10
14
|
search: "DEPRECATION: Riak Search features are included in the main client, you no longer need to require 'riak/search'.\n%{backtrace}"
|
@@ -18,10 +22,17 @@ en:
|
|
18
22
|
http_failed_request: "Expected %{expected} from Riak but received %{code}. %{body}"
|
19
23
|
hostname_invalid: "host must be a valid hostname"
|
20
24
|
protocol_invalid: "'%{invalid}' is not a valid protocol, valid values are %{valid}"
|
25
|
+
index:
|
26
|
+
no_next_page: "The returned search did not have a continuation available."
|
27
|
+
pagination_not_available: "The Riak server does not support secondary index pagination."
|
28
|
+
return_terms_not_available: "The Riak server does not support return_terms."
|
29
|
+
streaming_not_available: "The Riak server does not support streaming."
|
30
|
+
include_terms_is_wrong: "include_terms isn't a valid option; return_terms is."
|
21
31
|
invalid_basic_auth: "basic auth must be set using 'user:pass'"
|
22
32
|
invalid_client_id: "Invalid client ID, must be a string or between 0 and %{max_id}"
|
23
33
|
invalid_io_object: "Invalid IO-like object assigned to RObject#data. It should be assigned to raw_data instead."
|
24
34
|
invalid_function_value: "invalid value for function: %{value}"
|
35
|
+
invalid_multiget_thread_count: "Invalid multiget thread count, must be nil or a positive integer."
|
25
36
|
invalid_options: "Invalid configuration options given."
|
26
37
|
invalid_phase_type: "type must be :map, :reduce, or :link"
|
27
38
|
invalid_ssl_verify_mode: "%{invalid} is not a valid :verify_mode option for SSL. Valid options are 'peer' and 'none'."
|
@@ -48,7 +59,10 @@ en:
|
|
48
59
|
source_and_root_required: "Riak::Node configuration must include :source and :root keys."
|
49
60
|
stale_write_prevented: "Stale write prevented by client."
|
50
61
|
stored_function_invalid: "function must have :bucket and :key when a hash"
|
62
|
+
streaming_bucket_list_without_block: "Streaming bucket list was requested but no block was given."
|
51
63
|
string_type: "invalid_argument %{string} is not a String"
|
52
64
|
too_few_arguments: "too few arguments: %{params}"
|
53
65
|
walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
|
54
66
|
wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
|
67
|
+
zero_length_bucket: "bucket name cannot be a String of zero length"
|
68
|
+
zero_length_key: "key cannot be a String of zero length"
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'riak/client'
|
2
|
+
require 'riak/bucket'
|
3
|
+
require 'riak/cluster'
|
4
|
+
|
5
|
+
module Riak
|
6
|
+
# Coordinates a parallel fetch operation for multiple values.
|
7
|
+
class Multiget
|
8
|
+
include Util::Translation
|
9
|
+
|
10
|
+
# @return [Riak::Client] the associated client
|
11
|
+
attr_reader :client
|
12
|
+
|
13
|
+
# @return [Array<Bucket, String>] fetch_list an {Array} of {Bucket} and {String} keys to fetch
|
14
|
+
attr_reader :fetch_list
|
15
|
+
|
16
|
+
# @return [Hash<fetch_list_entry, RObject] result_hash a {Hash} of {Bucket} and {String} key pairs to {RObject} instances
|
17
|
+
attr_accessor :result_hash
|
18
|
+
|
19
|
+
# @return [Boolean] finished if the fetch operation has completed
|
20
|
+
attr_reader :finished
|
21
|
+
|
22
|
+
# @return [Integer] The number of threads to use
|
23
|
+
attr_accessor :thread_count
|
24
|
+
|
25
|
+
# Perform a Riak Multiget operation.
|
26
|
+
# @param [Client] client the {Riak::Client} that will perform the multiget
|
27
|
+
# @param [Array<Bucket, String>] fetch_list an {Array} of {Bucket} and {String} keys to fetch
|
28
|
+
# @return [Hash<fetch_list_entry, RObject] result_hash a {Hash} of {Bucket} and {String} key pairs to {Robject} instances
|
29
|
+
def self.get_all(client, fetch_list)
|
30
|
+
multi = new client, fetch_list
|
31
|
+
multi.fetch
|
32
|
+
multi.results
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a Riak Multiget operation.
|
36
|
+
# @param [Client] client the {Riak::Client} that will perform the multiget
|
37
|
+
# @param [Array<Bucket, String>] fetch_list an {Array} of {Bucket} and {String} keys to fetch
|
38
|
+
def initialize(client, fetch_list)
|
39
|
+
raise ArgumentError, t('client_type', :client => client.inspect) unless client.is_a? Riak::Client
|
40
|
+
raise ArgumentError, t('array_type', :array => fetch_list.inspect) unless fetch_list.is_a? Array
|
41
|
+
|
42
|
+
validate_fetch_list fetch_list
|
43
|
+
@client, @fetch_list = client, fetch_list.uniq
|
44
|
+
self.result_hash = Hash.new
|
45
|
+
@finished = false
|
46
|
+
self.thread_count = client.multiget_threads
|
47
|
+
end
|
48
|
+
|
49
|
+
# Starts the parallelized fetch operation
|
50
|
+
# @raise [ArgumentError] when a non-positive-Integer count is given
|
51
|
+
def fetch
|
52
|
+
queue = fetch_list.dup
|
53
|
+
queue_mutex = Mutex.new
|
54
|
+
result_mutex = Mutex.new
|
55
|
+
|
56
|
+
unless thread_count.is_a?(Integer) && thread_count > 0
|
57
|
+
raise ArgumentError, t("invalid_multiget_thread_count")
|
58
|
+
end
|
59
|
+
|
60
|
+
@threads = 1.upto(thread_count).map do |_node|
|
61
|
+
Thread.new do
|
62
|
+
loop do
|
63
|
+
pair = queue_mutex.synchronize do
|
64
|
+
queue.shift
|
65
|
+
end
|
66
|
+
|
67
|
+
break if pair.nil?
|
68
|
+
|
69
|
+
found = attempt_fetch(*pair)
|
70
|
+
result_mutex.synchronize do
|
71
|
+
result_hash[pair] = found
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def results
|
79
|
+
wait_for_finish
|
80
|
+
result_hash
|
81
|
+
end
|
82
|
+
|
83
|
+
def finished?
|
84
|
+
set_finished_for_thread_liveness
|
85
|
+
finished
|
86
|
+
end
|
87
|
+
|
88
|
+
def wait_for_finish
|
89
|
+
return if finished?
|
90
|
+
@threads.each {|t| t.join }
|
91
|
+
@finished = true
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def attempt_fetch(bucket, key)
|
97
|
+
bucket[key]
|
98
|
+
rescue Riak::FailedRequest => e
|
99
|
+
raise e unless e.not_found?
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_finished_for_thread_liveness
|
104
|
+
return if @finished # already done
|
105
|
+
|
106
|
+
all_dead = @threads.none? {|t| t.alive? }
|
107
|
+
return unless all_dead # still working
|
108
|
+
|
109
|
+
@finished = true
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
def validate_fetch_list(fetch_list)
|
114
|
+
return unless erroneous = fetch_list.detect do |e|
|
115
|
+
bucket, key = e
|
116
|
+
next true unless bucket.is_a? Bucket
|
117
|
+
next true unless key.is_a? String
|
118
|
+
end
|
119
|
+
|
120
|
+
raise ArgumentError, t('fetch_list_type', :problem => erroneous)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|