riak-client 1.0.5 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +4 -3
- data/.rspec +1 -0
- data/Gemfile +1 -0
- data/RELEASE_NOTES.md +47 -0
- data/Rakefile +0 -1
- data/erl_src/riak_kv_test_backend.erl +34 -0
- data/lib/riak/client.rb +3 -1
- data/lib/riak/client/beefcake/messages.rb +49 -1
- data/lib/riak/client/beefcake/object_methods.rb +14 -21
- data/lib/riak/client/beefcake_protobuffs_backend.rb +58 -12
- data/lib/riak/client/decaying.rb +31 -23
- data/lib/riak/client/feature_detection.rb +88 -0
- data/lib/riak/client/http_backend.rb +27 -6
- data/lib/riak/client/http_backend/configuration.rb +13 -0
- data/lib/riak/client/http_backend/object_methods.rb +33 -25
- data/lib/riak/client/node.rb +7 -2
- data/lib/riak/client/protobuffs_backend.rb +54 -3
- data/lib/riak/client/search.rb +2 -2
- data/lib/riak/conflict.rb +13 -0
- data/lib/riak/locale/en.yml +2 -0
- data/lib/riak/map_reduce.rb +1 -1
- data/lib/riak/map_reduce/filter_builder.rb +2 -2
- data/lib/riak/map_reduce/results.rb +49 -0
- data/lib/riak/node/console.rb +17 -16
- data/lib/riak/node/generation.rb +9 -0
- data/lib/riak/rcontent.rb +168 -0
- data/lib/riak/robject.rb +37 -157
- data/lib/riak/util/escape.rb +5 -1
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +37 -5
- data/spec/fixtures/multipart-basic-conflict.txt +15 -0
- data/spec/fixtures/munchausen.txt +1033 -0
- data/spec/integration/riak/cluster_spec.rb +1 -1
- data/spec/integration/riak/http_backends_spec.rb +23 -2
- data/spec/integration/riak/node_spec.rb +2 -2
- data/spec/integration/riak/protobuffs_backends_spec.rb +17 -2
- data/spec/integration/riak/test_server_spec.rb +1 -1
- data/spec/integration/riak/threading_spec.rb +3 -3
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +58 -25
- data/spec/riak/escape_spec.rb +3 -0
- data/spec/riak/feature_detection_spec.rb +61 -0
- data/spec/riak/http_backend/object_methods_spec.rb +4 -13
- data/spec/riak/http_backend_spec.rb +6 -5
- data/spec/riak/map_reduce_spec.rb +0 -5
- data/spec/riak/robject_spec.rb +12 -11
- data/spec/spec_helper.rb +3 -1
- data/spec/support/riak_test.rb +77 -0
- data/spec/support/search_corpus_setup.rb +18 -0
- data/spec/support/sometimes.rb +1 -1
- data/spec/support/test_server.rb +1 -1
- data/spec/support/unified_backend_examples.rb +53 -7
- data/spec/support/version_filter.rb +4 -11
- metadata +56 -22
- data/lib/riak/client/pool.rb +0 -180
- data/spec/riak/pool_spec.rb +0 -306
data/.document
ADDED
data/.gitignore
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## NOTE: This file must be manually kept in sync with the gemspec, but
|
2
|
+
## should not normally need to be modified unless new top-level files
|
3
|
+
## or directory trees are being added.
|
4
|
+
|
1
5
|
## MAC OS
|
2
6
|
.DS_Store
|
3
7
|
|
@@ -18,9 +22,6 @@ coverage
|
|
18
22
|
rdoc
|
19
23
|
pkg
|
20
24
|
|
21
|
-
## This line is for the dirglob only
|
22
|
-
pkg/*
|
23
|
-
|
24
25
|
## PROJECT::SPECIFIC
|
25
26
|
_notes
|
26
27
|
doc
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
CHANGED
data/RELEASE_NOTES.md
CHANGED
@@ -1,5 +1,52 @@
|
|
1
1
|
# Riak Ruby Client Release Notes
|
2
2
|
|
3
|
+
## 1.1.0 Feature Release - 2012-11-07
|
4
|
+
|
5
|
+
Release 1.1.0 includes full Riak 1.2 compatibility, and includes
|
6
|
+
improvements to the handling of siblings, the node generation
|
7
|
+
tools, and resolves a number of important bugs.
|
8
|
+
|
9
|
+
Features:
|
10
|
+
|
11
|
+
* Client features are enabled or disabled based on the detected Riak
|
12
|
+
version.
|
13
|
+
* Riak 1.2 compatibility, including search and 2I over Protocol
|
14
|
+
Buffers.
|
15
|
+
* Phaseless MapReduce (which was available in 1.1) is allowed, using
|
16
|
+
feature detection to determine whether an exception is raised.
|
17
|
+
* Conditional store_object operations on Protocol Buffers use the
|
18
|
+
message features available since Riak 1.0.
|
19
|
+
* The integration test-suite can be run without generating a test
|
20
|
+
node, which lets us support riak_test.
|
21
|
+
|
22
|
+
Bugfixes:
|
23
|
+
|
24
|
+
* URL-escaping now allows some normally URI-safe characters to be
|
25
|
+
escaped.
|
26
|
+
* JRuby should be more reliable when attaching to a generated node's
|
27
|
+
console.
|
28
|
+
* The client backend pool has been extracted to the Innertube gem,
|
29
|
+
which is now a dependency.
|
30
|
+
* Fix a documentation issue around key-filters.
|
31
|
+
* Fix RSpec formatter and deprecation errors.
|
32
|
+
* Object siblings are now a separate class (RContent) rather than
|
33
|
+
being unclean copies of the parent RObject. If only one sibling
|
34
|
+
exists, the original accessors (e.g. `content_type`, `data`) will
|
35
|
+
behave as expected. When more than one sibling exists, they will
|
36
|
+
raise `Riak::Conflict`. This should prevent unintentional storing of
|
37
|
+
unresolved objects back into Riak as `multipart/mixed` values.
|
38
|
+
* `Riak::Client#ssl=` won't blow away existing `ssl_options` if set to
|
39
|
+
`true`.
|
40
|
+
* Generated nodes will ensure that the source's
|
41
|
+
`ssl_distribution.args_file` exists by invoking `riak chkconfig`.
|
42
|
+
* Copy fixes for the `$key` index on the memory/test backend from
|
43
|
+
riak_kv.
|
44
|
+
* The shape of MapReduce results will no longer be changed by the
|
45
|
+
Protocol Buffers backend, which manifested as kept phases without
|
46
|
+
results being removed from the return value. Implementing this
|
47
|
+
required all HTTP requests to use streaming, even if invoked without
|
48
|
+
a block.
|
49
|
+
|
3
50
|
## 1.0.5 Packaging Fix Release - 2012-10-12
|
4
51
|
|
5
52
|
Release 1.0.5 fixes a bug with the RubyGems packaging that
|
data/Rakefile
CHANGED
@@ -61,6 +61,7 @@
|
|
61
61
|
|
62
62
|
-ifdef(TEST).
|
63
63
|
-include_lib("eunit/include/eunit.hrl").
|
64
|
+
-compile([export_all]).
|
64
65
|
-endif.
|
65
66
|
|
66
67
|
-define(API_VERSION, 1).
|
@@ -427,6 +428,9 @@ fold_keys_fun(FoldKeysFun, {index, FilterBucket, {eq, <<"$bucket">>, _}}) ->
|
|
427
428
|
fold_keys_fun(FoldKeysFun, {index, FilterBucket, {range, <<"$key">>, _, _}}) ->
|
428
429
|
%% 2I range query on special $key field...
|
429
430
|
fold_keys_fun(FoldKeysFun, {bucket, FilterBucket});
|
431
|
+
fold_keys_fun(FoldKeysFun, {index, FilterBucket, {eq, <<"$key">>, _}}) ->
|
432
|
+
%% 2I eq query on special $key field...
|
433
|
+
fold_keys_fun(FoldKeysFun, {bucket, FilterBucket});
|
430
434
|
fold_keys_fun(FoldKeysFun, {index, _FilterBucket, _Query}) ->
|
431
435
|
fun({{Bucket, _FilterField, _FilterTerm, Key}, _}, Acc) ->
|
432
436
|
FoldKeysFun(Bucket, Key, Acc);
|
@@ -469,6 +473,8 @@ get_index_folder(Folder, Acc0, {index, Bucket, {range, <<"$key">>, Min, Max}}, D
|
|
469
473
|
fun() ->
|
470
474
|
key_range_folder(Folder, Acc0, DataRef, {Bucket, Min}, {Bucket, Min, Max})
|
471
475
|
end;
|
476
|
+
get_index_folder(Folder, Acc0, {index, Bucket, {eq, <<"$key">>, Val}}, DataRef, IndexRef) ->
|
477
|
+
get_index_folder(Folder, Acc0, {index, Bucket, {range, <<"$key">>, Val, Val}}, DataRef, IndexRef);
|
472
478
|
get_index_folder(Folder, Acc0, {index, Bucket, {eq, Field, Term}}, _, IndexRef) ->
|
473
479
|
fun() ->
|
474
480
|
index_range_folder(Folder, Acc0, IndexRef, {Bucket, Field, Term, undefined}, {Bucket, Field, Term, Term})
|
@@ -668,6 +674,34 @@ max_memory_test_() ->
|
|
668
674
|
?_assertEqual({ok, Value2, State2}, get(Bucket, Key2, State2))
|
669
675
|
].
|
670
676
|
|
677
|
+
regression_367_key_range_test_() ->
|
678
|
+
{ok, State} = start(142, []),
|
679
|
+
Keys = [begin
|
680
|
+
Bin = list_to_binary(integer_to_list(I)),
|
681
|
+
if I < 10 ->
|
682
|
+
<<"obj0", Bin/binary>>;
|
683
|
+
true -> <<"obj", Bin/binary>>
|
684
|
+
end
|
685
|
+
end || I <- lists:seq(1,30) ],
|
686
|
+
Bucket = <<"keyrange">>,
|
687
|
+
Value = <<"foobarbaz">>,
|
688
|
+
State1 = lists:foldl(fun(Key, IState) ->
|
689
|
+
{ok, NewState} = put(Bucket, Key, [], Value, IState),
|
690
|
+
NewState
|
691
|
+
end, State, Keys),
|
692
|
+
Folder = fun(_B, K, Acc) ->
|
693
|
+
Acc ++ [K]
|
694
|
+
end,
|
695
|
+
[
|
696
|
+
?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj01">>, <<"obj01">>}}], State1)),
|
697
|
+
?_assertEqual({ok, [<<"obj10">>,<<"obj11">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj10">>, <<"obj11">>}}], State1)),
|
698
|
+
?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj00">>, <<"obj01">>}}], State1)),
|
699
|
+
?_assertEqual({ok, lists:sort(Keys)}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj0">>, <<"obj31">>}}], State1)),
|
700
|
+
?_assertEqual({ok, []}, fold_keys(Folder, [], [{index, Bucket, {range, <<"$key">>, <<"obj31">>, <<"obj32">>}}], State1)),
|
701
|
+
?_assertEqual({ok, [<<"obj01">>]}, fold_keys(Folder, [], [{index, Bucket, {eq, <<"$key">>, <<"obj01">>}}], State1)),
|
702
|
+
?_assertEqual(ok, stop(State1))
|
703
|
+
].
|
704
|
+
|
671
705
|
-ifdef(EQC).
|
672
706
|
|
673
707
|
eqc_test_() ->
|
data/lib/riak/client.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
require 'delegate'
|
3
|
+
require 'innertube'
|
3
4
|
require 'riak'
|
4
5
|
require 'riak/util/translation'
|
5
6
|
require 'riak/util/escape'
|
6
7
|
require 'riak/failed_request'
|
7
|
-
require 'riak/client/pool'
|
8
8
|
require 'riak/client/decaying'
|
9
9
|
require 'riak/client/node'
|
10
10
|
require 'riak/client/search'
|
@@ -47,6 +47,8 @@ module Riak
|
|
47
47
|
SystemCallError,
|
48
48
|
]
|
49
49
|
|
50
|
+
Pool = ::Innertube::Pool
|
51
|
+
|
50
52
|
# @return [String] The protocol to use for the Riak endpoint
|
51
53
|
attr_reader :protocol
|
52
54
|
|
@@ -89,7 +89,7 @@ module Riak
|
|
89
89
|
required :content, RpbContent, 4
|
90
90
|
optional :w, :uint32, 5
|
91
91
|
optional :dw, :uint32, 6
|
92
|
-
optional :returnbody,
|
92
|
+
optional :returnbody, :bool, 7
|
93
93
|
optional :pw, :uint32, 8
|
94
94
|
optional :if_not_modified, :bool, 9
|
95
95
|
optional :if_none_match, :bool, 10
|
@@ -160,6 +160,54 @@ module Riak
|
|
160
160
|
optional :response, :bytes, 2
|
161
161
|
optional :done, :bool, 3
|
162
162
|
end
|
163
|
+
|
164
|
+
class RpbIndexReq
|
165
|
+
include Beefcake::Message
|
166
|
+
module IndexQueryType
|
167
|
+
EQ = 0
|
168
|
+
RANGE = 1
|
169
|
+
end
|
170
|
+
|
171
|
+
required :bucket, :bytes, 1
|
172
|
+
required :index, :bytes, 2
|
173
|
+
required :qtype, IndexQueryType, 3
|
174
|
+
optional :key, :bytes, 4
|
175
|
+
optional :range_min, :bytes, 5
|
176
|
+
optional :range_max, :bytes, 6
|
177
|
+
end
|
178
|
+
|
179
|
+
class RpbIndexResp
|
180
|
+
include Beefcake::Message
|
181
|
+
repeated :keys, :bytes, 1
|
182
|
+
end
|
183
|
+
|
184
|
+
class RpbSearchDoc
|
185
|
+
include Beefcake::Message
|
186
|
+
# We have to name this differently than the .proto file does
|
187
|
+
# because Beefcake uses 'fields' as an instance method.
|
188
|
+
repeated :properties, RpbPair, 1
|
189
|
+
end
|
190
|
+
|
191
|
+
class RpbSearchQueryReq
|
192
|
+
include Beefcake::Message
|
193
|
+
required :q, :bytes, 1
|
194
|
+
required :index, :bytes, 2
|
195
|
+
optional :rows, :uint32, 3
|
196
|
+
optional :start, :uint32, 4
|
197
|
+
optional :sort, :bytes, 5
|
198
|
+
optional :filter, :bytes, 6
|
199
|
+
optional :df, :bytes, 7
|
200
|
+
optional :op, :bytes, 8
|
201
|
+
repeated :fl, :bytes, 9
|
202
|
+
optional :presort, :bytes, 10
|
203
|
+
end
|
204
|
+
|
205
|
+
class RpbSearchQueryResp
|
206
|
+
include Beefcake::Message
|
207
|
+
repeated :docs, RpbSearchDoc, 1, :default => []
|
208
|
+
optional :max_score, :float, 2
|
209
|
+
optional :num_found, :uint32, 3
|
210
|
+
end
|
163
211
|
end
|
164
212
|
end
|
165
213
|
end
|
@@ -31,40 +31,33 @@ module Riak
|
|
31
31
|
return robject if pbuf.respond_to?(:unchanged) && pbuf.unchanged # Reloading
|
32
32
|
robject.vclock = Base64.encode64(pbuf.vclock).chomp if pbuf.vclock
|
33
33
|
robject.key = maybe_unescape(pbuf.key) if pbuf.respond_to?(:key) && pbuf.key # Put w/o key
|
34
|
-
|
35
|
-
robject
|
36
|
-
robject.siblings = pbuf.content.map do |c|
|
37
|
-
sibling = RObject.new(robject.bucket, robject.key)
|
38
|
-
sibling.vclock = robject.vclock
|
34
|
+
robject.siblings = pbuf.content.map do |c|
|
35
|
+
RContent.new(robject) do |sibling|
|
39
36
|
load_content(c, sibling)
|
40
37
|
end
|
41
|
-
|
42
|
-
return robject.attempt_conflict_resolution
|
43
|
-
else
|
44
|
-
load_content(pbuf.content.first, robject)
|
45
38
|
end
|
46
|
-
robject
|
39
|
+
robject.conflict? ? robject.attempt_conflict_resolution : robject
|
47
40
|
end
|
48
41
|
|
49
42
|
private
|
50
|
-
def load_content(pbuf,
|
43
|
+
def load_content(pbuf, rcontent)
|
51
44
|
if ENCODING && pbuf.charset.present?
|
52
45
|
pbuf.value.force_encoding(pbuf.charset) if Encoding.find(pbuf.charset)
|
53
46
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
pbuf.usermeta.each {|pair| decode_meta(pair,
|
47
|
+
rcontent.raw_data = pbuf.value
|
48
|
+
rcontent.etag = pbuf.vtag if pbuf.vtag.present?
|
49
|
+
rcontent.content_type = pbuf.content_type if pbuf.content_type.present?
|
50
|
+
rcontent.links = pbuf.links.map(&method(:decode_link)) if pbuf.links.present?
|
51
|
+
pbuf.usermeta.each {|pair| decode_meta(pair, rcontent.meta) } if pbuf.usermeta.present?
|
59
52
|
if pbuf.indexes.present?
|
60
|
-
|
61
|
-
pbuf.indexes.each {|pair| decode_index(pair,
|
53
|
+
rcontent.indexes.clear
|
54
|
+
pbuf.indexes.each {|pair| decode_index(pair, rcontent.indexes) }
|
62
55
|
end
|
63
56
|
if pbuf.last_mod.present?
|
64
|
-
|
65
|
-
|
57
|
+
rcontent.last_modified = Time.at(pbuf.last_mod)
|
58
|
+
rcontent.last_modified += pbuf.last_mod_usecs / 1000000 if pbuf.last_mod_usecs.present?
|
66
59
|
end
|
67
|
-
|
60
|
+
rcontent
|
68
61
|
end
|
69
62
|
|
70
63
|
def decode_link(pbuf)
|
@@ -31,7 +31,7 @@ module Riak
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def fetch_object(bucket, key, options={})
|
34
|
-
options = normalize_quorums(options)
|
34
|
+
options = prune_unsupported_options(:GetReq, normalize_quorums(options))
|
35
35
|
bucket = Bucket === bucket ? bucket.name : bucket
|
36
36
|
req = RpbGetReq.new(options.merge(:bucket => maybe_encode(bucket), :key => maybe_encode(key)))
|
37
37
|
write_protobuff(:GetReq, req)
|
@@ -43,18 +43,25 @@ module Riak
|
|
43
43
|
options[:bucket] = maybe_encode(robject.bucket.name)
|
44
44
|
options[:key] = maybe_encode(robject.key)
|
45
45
|
options[:if_modified] = maybe_encode Base64.decode64(robject.vclock) if robject.vclock
|
46
|
-
req = RpbGetReq.new(options)
|
46
|
+
req = RpbGetReq.new(prune_unsupported_options(:GetReq, options))
|
47
47
|
write_protobuff(:GetReq, req)
|
48
48
|
decode_response(robject)
|
49
49
|
end
|
50
50
|
|
51
51
|
def store_object(robject, options={})
|
52
|
+
options = normalize_quorums(options)
|
52
53
|
if robject.prevent_stale_writes
|
53
|
-
|
54
|
-
|
54
|
+
unless pb_conditionals?
|
55
|
+
other = fetch_object(robject.bucket, robject.key)
|
56
|
+
raise Riak::ProtobuffsFailedRequest.new(:stale_object, t("stale_write_prevented")) unless other.vclock == robject.vclock
|
57
|
+
end
|
58
|
+
if robject.vclock
|
59
|
+
options[:if_not_modified] = true
|
60
|
+
else
|
61
|
+
options[:if_none_match] = true
|
62
|
+
end
|
55
63
|
end
|
56
|
-
|
57
|
-
req = dump_object(robject, options)
|
64
|
+
req = dump_object(robject, prune_unsupported_options(:PutReq, options))
|
58
65
|
write_protobuff(:PutReq, req)
|
59
66
|
decode_response(robject)
|
60
67
|
end
|
@@ -65,7 +72,7 @@ module Riak
|
|
65
72
|
options[:bucket] = maybe_encode(bucket)
|
66
73
|
options[:key] = maybe_encode(key)
|
67
74
|
options[:vclock] = Base64.decode64(options[:vclock]) if options[:vclock]
|
68
|
-
req = RpbDelReq.new(options)
|
75
|
+
req = RpbDelReq.new(prune_unsupported_options(:DelReq, options))
|
69
76
|
write_protobuff(:DelReq, req)
|
70
77
|
decode_response
|
71
78
|
end
|
@@ -102,19 +109,47 @@ module Riak
|
|
102
109
|
end
|
103
110
|
|
104
111
|
def mapred(mr, &block)
|
112
|
+
raise MapReduceError.new(t("empty_map_reduce_query")) if mr.query.empty? && !mapred_phaseless?
|
105
113
|
req = RpbMapRedReq.new(:request => mr.to_json, :content_type => "application/json")
|
106
114
|
write_protobuff(:MapRedReq, req)
|
107
|
-
results =
|
115
|
+
results = MapReduce::Results.new(mr)
|
108
116
|
while msg = decode_response
|
109
117
|
break if msg.done
|
110
118
|
if block_given?
|
111
119
|
yield msg.phase, JSON.parse(msg.response)
|
112
120
|
else
|
113
|
-
results
|
114
|
-
results[msg.phase] += JSON.parse(msg.response)
|
121
|
+
results.add msg.phase, JSON.parse(msg.response)
|
115
122
|
end
|
116
123
|
end
|
117
|
-
block_given? || results.
|
124
|
+
block_given? || results.report
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_index(bucket, index, query)
|
128
|
+
return super unless pb_indexes?
|
129
|
+
if Range === query
|
130
|
+
options = {
|
131
|
+
:qtype => RpbIndexReq::IndexQueryType::RANGE,
|
132
|
+
:range_min => query.begin.to_s,
|
133
|
+
:range_max => query.end.to_s
|
134
|
+
}
|
135
|
+
else
|
136
|
+
options = {
|
137
|
+
:qtype => RpbIndexReq::IndexQueryType::EQ,
|
138
|
+
:key => query.to_s
|
139
|
+
}
|
140
|
+
end
|
141
|
+
req = RpbIndexReq.new(options.merge(:bucket => bucket, :index => index))
|
142
|
+
write_protobuff(:IndexReq, req)
|
143
|
+
decode_response
|
144
|
+
end
|
145
|
+
|
146
|
+
def search(index, query, options={})
|
147
|
+
return super unless pb_search?
|
148
|
+
options = options.symbolize_keys
|
149
|
+
options[:op] = options.delete(:'q.op') if options[:'q.op']
|
150
|
+
req = RpbSearchQueryReq.new(options.merge(:index => index || 'search', :q => query))
|
151
|
+
write_protobuff(:SearchQueryReq, req)
|
152
|
+
decode_response
|
118
153
|
end
|
119
154
|
|
120
155
|
private
|
@@ -167,12 +202,23 @@ module Riak
|
|
167
202
|
{'n_val' => res.props.n_val, 'allow_mult' => res.props.allow_mult}
|
168
203
|
when :MapRedResp
|
169
204
|
RpbMapRedResp.decode(message)
|
205
|
+
when :IndexResp
|
206
|
+
res = RpbIndexResp.decode(message)
|
207
|
+
res.keys
|
208
|
+
when :SearchQueryResp
|
209
|
+
res = RpbSearchQueryResp.decode(message)
|
210
|
+
{ 'docs' => res.docs.map {|d| decode_doc(d) },
|
211
|
+
'max_score' => res.max_score,
|
212
|
+
'num_found' => res.num_found }
|
170
213
|
end
|
171
214
|
end
|
172
215
|
rescue SystemCallError, SocketError => e
|
173
216
|
reset_socket
|
174
217
|
raise
|
175
|
-
|
218
|
+
end
|
219
|
+
|
220
|
+
def decode_doc(doc)
|
221
|
+
Hash[doc.properties.map {|p| [ p.key, p.value ] }]
|
176
222
|
end
|
177
223
|
end
|
178
224
|
end
|
data/lib/riak/client/decaying.rb
CHANGED
@@ -1,28 +1,36 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Riak
|
2
|
+
class Client
|
3
|
+
# A float value which decays exponentially toward 0 over time.
|
4
|
+
# @private
|
5
|
+
class Decaying
|
6
|
+
attr_accessor :e
|
7
|
+
attr_accessor :p
|
3
8
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
# @param [Hash] opts options
|
10
|
+
# @option options [Float] :p (0.0) The initial value
|
11
|
+
# @option options [Float] :e (Math::E) Exponent base
|
12
|
+
# @option options [Float] :r (Math.log(0.5) / 10) Timescale
|
13
|
+
# factor - defaulting to decay 50% every 10 seconds
|
14
|
+
def initialize(opts = {})
|
15
|
+
@p = opts[:p] || 0.0
|
16
|
+
@e = opts[:e] || Math::E
|
17
|
+
@r = opts[:r] || Math.log(0.5) / 10
|
18
|
+
@t0 = Time.now
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
# Add to current value.
|
22
|
+
# @param [Float] d the value to add
|
23
|
+
def <<(d)
|
24
|
+
@p = value + d
|
25
|
+
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
# @return [Float] the current value (adjusted for the time decay)
|
28
|
+
def value
|
29
|
+
now = Time.now
|
30
|
+
dt = now - @t0
|
31
|
+
@t0 = now
|
32
|
+
@p = @p * (@e ** (@r * dt))
|
33
|
+
end
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|