riak-client 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/RELEASE_NOTES.md +16 -0
- data/Rakefile +3 -2
- data/lib/riak/client/beefcake/message_codes.rb +12 -0
- data/lib/riak/client/beefcake/messages.rb +205 -5
- data/lib/riak/client/beefcake/object_methods.rb +1 -2
- data/lib/riak/client/beefcake/operator.rb +9 -0
- data/lib/riak/client/beefcake/time_series_delete_operator.rb +24 -0
- data/lib/riak/client/beefcake/time_series_get_operator.rb +35 -0
- data/lib/riak/client/beefcake/time_series_list_operator.rb +42 -0
- data/lib/riak/client/beefcake/time_series_put_operator.rb +32 -0
- data/lib/riak/client/beefcake/time_series_query_operator.rb +42 -0
- data/lib/riak/client/beefcake/ts_cell_codec.rb +63 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +5 -0
- data/lib/riak/client.rb +3 -0
- data/lib/riak/errors/time_series.rb +23 -0
- data/lib/riak/locale/en.yml +5 -0
- data/lib/riak/time_series/collection.rb +5 -0
- data/lib/riak/time_series/deletion.rb +26 -0
- data/lib/riak/time_series/list.rb +66 -0
- data/lib/riak/time_series/query.rb +43 -0
- data/lib/riak/time_series/read.rb +18 -0
- data/lib/riak/time_series/row.rb +4 -0
- data/lib/riak/time_series/submission.rb +32 -0
- data/lib/riak/time_series.rb +17 -0
- data/lib/riak/version.rb +1 -1
- data/lib/riak.rb +16 -0
- data/spec/integration/riak/bucket_types_spec.rb +6 -6
- data/spec/integration/riak/time_series_spec.rb +168 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +31 -2
- data/spec/riak/beefcake_protobuffs_backend/ts_cell_codec_spec.rb +116 -0
- data/spec/riak/client_spec.rb +11 -0
- data/spec/riak/crdt/inner_flag_spec.rb +1 -1
- data/spec/riak/time_series/deletion_spec.rb +33 -0
- data/spec/riak/time_series/listing_spec.rb +50 -0
- data/spec/riak/time_series/submission_spec.rb +35 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_client.yml +0 -1
- metadata +29 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8eb1ae6b34bdb0bc9e7ace398e001f570dfe0782
|
4
|
+
data.tar.gz: f4b53df09ae0c37710f74bc6fa4bb2bc2e47e82e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bb8412b0efd79d81d486a87302c0985e59420d7d1a2d1cd9119f60afef9dbffbed2fc19a003c4e31917080c14816830545d3bcebab200d23b0f6ab0e8c3374f
|
7
|
+
data.tar.gz: 3ffebec87cfddc2ddd8422785f49e9814452ed94720c27d8666ff815e224f072c046302741c34d2b77aa4026f872a1e928cd25f7a4b93a901367518ad83a3b13
|
data/.rspec
CHANGED
data/RELEASE_NOTES.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Riak Ruby Client Release Notes
|
2
2
|
|
3
|
+
## 2.3.0 Release - 2015-12-15
|
4
|
+
|
5
|
+
Version 2.3.0 is a feature release, introducing support for the Riak TS time
|
6
|
+
series database.
|
7
|
+
|
8
|
+
New features:
|
9
|
+
|
10
|
+
* Riak TS support, for queries, reads, writes, and deletes. Key list support
|
11
|
+
is also provided.
|
12
|
+
* Network error logging, thanks to Sebastian Röbke.
|
13
|
+
|
14
|
+
Bug fixes:
|
15
|
+
|
16
|
+
* The `last_modified` field on `RContent` and `RObject` objects now has
|
17
|
+
microsecond precision, thanks to Sebastian Röbke.
|
18
|
+
|
3
19
|
## 2.2.2 Release - 2015-11-24
|
4
20
|
|
5
21
|
Version 2.2.2 is a bugfix release.
|
data/Rakefile
CHANGED
@@ -90,7 +90,7 @@ task :pb_defs => 'beefcake:pb_defs'
|
|
90
90
|
namespace :beefcake do
|
91
91
|
task :pb_defs => 'lib/riak/client/beefcake/messages.rb'
|
92
92
|
|
93
|
-
PROTO_FILES = %w{riak_kv riak_search riak_yokozuna riak_dt}
|
93
|
+
PROTO_FILES = %w{riak_kv riak_search riak_yokozuna riak_dt riak_ts}
|
94
94
|
PROTO_TMP = PROTO_FILES.map{|f| "tmp/#{f}.pb.rb"}
|
95
95
|
|
96
96
|
task :clean do
|
@@ -113,7 +113,8 @@ namespace :beefcake do
|
|
113
113
|
|
114
114
|
directory 'tmp/riak_pb' => 'tmp' do
|
115
115
|
cd 'tmp' do
|
116
|
-
|
116
|
+
# NB: change this once TS is published
|
117
|
+
sh "git clone -b end-to-end/timeseries https://github.com/basho/riak_pb.git"
|
117
118
|
end
|
118
119
|
end
|
119
120
|
end
|
@@ -68,6 +68,18 @@ module Riak
|
|
68
68
|
:DtUpdateReq => 82,
|
69
69
|
:DtUpdateResp => 83,
|
70
70
|
|
71
|
+
# riak time series
|
72
|
+
:TsQueryReq => 90,
|
73
|
+
:TsQueryResp => 91,
|
74
|
+
:TsPutReq => 92,
|
75
|
+
:TsPutResp => 93,
|
76
|
+
:TsDelReq => 94,
|
77
|
+
:TsDelResp => 95,
|
78
|
+
:TsGetReq => 96,
|
79
|
+
:TsGetResp => 97,
|
80
|
+
:TsListKeysReq => 98,
|
81
|
+
:TsListKeysResp => 99,
|
82
|
+
|
71
83
|
# internal
|
72
84
|
:AuthReq => 253,
|
73
85
|
:AuthResp => 254,
|
@@ -4,7 +4,7 @@ module Riak
|
|
4
4
|
class Client
|
5
5
|
# @private
|
6
6
|
class BeefcakeProtobuffsBackend
|
7
|
-
## Generated from riak.proto
|
7
|
+
## Generated from riak.proto
|
8
8
|
require "beefcake"
|
9
9
|
|
10
10
|
|
@@ -67,6 +67,14 @@ class RpbAuthReq
|
|
67
67
|
include Beefcake::Message
|
68
68
|
end
|
69
69
|
|
70
|
+
class RpbToggleEncodingReq
|
71
|
+
include Beefcake::Message
|
72
|
+
end
|
73
|
+
|
74
|
+
class RpbToggleEncodingResp
|
75
|
+
include Beefcake::Message
|
76
|
+
end
|
77
|
+
|
70
78
|
class RpbErrorResp
|
71
79
|
required :errmsg, :bytes, 1
|
72
80
|
required :errcode, :uint32, 2
|
@@ -156,7 +164,15 @@ class RpbAuthReq
|
|
156
164
|
required :user, :bytes, 1
|
157
165
|
required :password, :bytes, 2
|
158
166
|
end
|
159
|
-
|
167
|
+
|
168
|
+
class RpbToggleEncodingReq
|
169
|
+
required :use_native, :bool, 1
|
170
|
+
end
|
171
|
+
|
172
|
+
class RpbToggleEncodingResp
|
173
|
+
required :use_native, :bool, 1
|
174
|
+
end
|
175
|
+
## Generated from riak_kv.proto
|
160
176
|
require "beefcake"
|
161
177
|
|
162
178
|
|
@@ -225,6 +241,10 @@ class RpbIndexResp
|
|
225
241
|
include Beefcake::Message
|
226
242
|
end
|
227
243
|
|
244
|
+
class RpbIndexBodyResp
|
245
|
+
include Beefcake::Message
|
246
|
+
end
|
247
|
+
|
228
248
|
class RpbCSBucketReq
|
229
249
|
include Beefcake::Message
|
230
250
|
end
|
@@ -273,6 +293,18 @@ class RpbBucketKeyPreflistItem
|
|
273
293
|
include Beefcake::Message
|
274
294
|
end
|
275
295
|
|
296
|
+
class RpbCoverageReq
|
297
|
+
include Beefcake::Message
|
298
|
+
end
|
299
|
+
|
300
|
+
class RpbCoverageResp
|
301
|
+
include Beefcake::Message
|
302
|
+
end
|
303
|
+
|
304
|
+
class RpbCoverageEntry
|
305
|
+
include Beefcake::Message
|
306
|
+
end
|
307
|
+
|
276
308
|
class RpbGetClientIdResp
|
277
309
|
required :client_id, :bytes, 1
|
278
310
|
end
|
@@ -392,6 +424,8 @@ class RpbIndexReq
|
|
392
424
|
optional :type, :bytes, 12
|
393
425
|
optional :term_regex, :bytes, 13
|
394
426
|
optional :pagination_sort, :bool, 14
|
427
|
+
optional :cover_context, :bytes, 15
|
428
|
+
optional :return_body, :bool, 16
|
395
429
|
end
|
396
430
|
|
397
431
|
class RpbIndexResp
|
@@ -401,6 +435,12 @@ class RpbIndexResp
|
|
401
435
|
optional :done, :bool, 4
|
402
436
|
end
|
403
437
|
|
438
|
+
class RpbIndexBodyResp
|
439
|
+
repeated :objects, RpbIndexObject, 1
|
440
|
+
optional :continuation, :bytes, 2
|
441
|
+
optional :done, :bool, 3
|
442
|
+
end
|
443
|
+
|
404
444
|
class RpbCSBucketReq
|
405
445
|
required :bucket, :bytes, 1
|
406
446
|
required :start_key, :bytes, 2
|
@@ -411,6 +451,7 @@ class RpbCSBucketReq
|
|
411
451
|
optional :max_results, :uint32, 7
|
412
452
|
optional :timeout, :uint32, 8
|
413
453
|
optional :type, :bytes, 9
|
454
|
+
optional :cover_context, :bytes, 10
|
414
455
|
end
|
415
456
|
|
416
457
|
class RpbCSBucketResp
|
@@ -486,7 +527,26 @@ class RpbBucketKeyPreflistItem
|
|
486
527
|
required :node, :bytes, 2
|
487
528
|
required :primary, :bool, 3
|
488
529
|
end
|
489
|
-
|
530
|
+
|
531
|
+
class RpbCoverageReq
|
532
|
+
optional :type, :bytes, 1
|
533
|
+
required :bucket, :bytes, 2
|
534
|
+
optional :min_partitions, :uint32, 3
|
535
|
+
optional :replace_cover, :bytes, 4
|
536
|
+
repeated :unavailable_cover, :bytes, 5
|
537
|
+
end
|
538
|
+
|
539
|
+
class RpbCoverageResp
|
540
|
+
repeated :entries, RpbCoverageEntry, 1
|
541
|
+
end
|
542
|
+
|
543
|
+
class RpbCoverageEntry
|
544
|
+
required :ip, :bytes, 1
|
545
|
+
required :port, :uint32, 2
|
546
|
+
optional :keyspace_desc, :bytes, 3
|
547
|
+
required :cover_context, :bytes, 4
|
548
|
+
end
|
549
|
+
## Generated from riak_search.proto
|
490
550
|
require "beefcake"
|
491
551
|
|
492
552
|
|
@@ -524,7 +584,7 @@ class RpbSearchQueryResp
|
|
524
584
|
optional :max_score, :float, 2
|
525
585
|
optional :num_found, :uint32, 3
|
526
586
|
end
|
527
|
-
## Generated from riak_yokozuna.proto
|
587
|
+
## Generated from riak_yokozuna.proto
|
528
588
|
require "beefcake"
|
529
589
|
|
530
590
|
|
@@ -603,7 +663,7 @@ end
|
|
603
663
|
class RpbYokozunaSchemaGetResp
|
604
664
|
required :schema, RpbYokozunaSchema, 1
|
605
665
|
end
|
606
|
-
## Generated from riak_dt.proto
|
666
|
+
## Generated from riak_dt.proto
|
607
667
|
require "beefcake"
|
608
668
|
|
609
669
|
|
@@ -766,6 +826,146 @@ class DtUpdateResp
|
|
766
826
|
repeated :set_value, :bytes, 4
|
767
827
|
repeated :map_value, MapEntry, 5
|
768
828
|
end
|
829
|
+
## Generated from riak_ts.proto
|
830
|
+
require "beefcake"
|
831
|
+
|
832
|
+
|
833
|
+
module TsColumnType
|
834
|
+
VARCHAR = 0
|
835
|
+
SINT64 = 1
|
836
|
+
DOUBLE = 2
|
837
|
+
TIMESTAMP = 3
|
838
|
+
BOOLEAN = 4
|
839
|
+
end
|
840
|
+
|
841
|
+
class TsQueryReq
|
842
|
+
include Beefcake::Message
|
843
|
+
end
|
844
|
+
|
845
|
+
class TsQueryResp
|
846
|
+
include Beefcake::Message
|
847
|
+
end
|
848
|
+
|
849
|
+
class TsGetReq
|
850
|
+
include Beefcake::Message
|
851
|
+
end
|
852
|
+
|
853
|
+
class TsGetResp
|
854
|
+
include Beefcake::Message
|
855
|
+
end
|
856
|
+
|
857
|
+
class TsPutReq
|
858
|
+
include Beefcake::Message
|
859
|
+
end
|
860
|
+
|
861
|
+
class TsPutResp
|
862
|
+
include Beefcake::Message
|
863
|
+
end
|
864
|
+
|
865
|
+
class TsDelReq
|
866
|
+
include Beefcake::Message
|
867
|
+
end
|
868
|
+
|
869
|
+
class TsDelResp
|
870
|
+
include Beefcake::Message
|
871
|
+
end
|
872
|
+
|
873
|
+
class TsInterpolation
|
874
|
+
include Beefcake::Message
|
875
|
+
end
|
876
|
+
|
877
|
+
class TsColumnDescription
|
878
|
+
include Beefcake::Message
|
879
|
+
end
|
880
|
+
|
881
|
+
class TsRow
|
882
|
+
include Beefcake::Message
|
883
|
+
end
|
884
|
+
|
885
|
+
class TsCell
|
886
|
+
include Beefcake::Message
|
887
|
+
end
|
888
|
+
|
889
|
+
class TsListKeysReq
|
890
|
+
include Beefcake::Message
|
891
|
+
end
|
892
|
+
|
893
|
+
class TsListKeysResp
|
894
|
+
include Beefcake::Message
|
895
|
+
end
|
896
|
+
|
897
|
+
class TsQueryReq
|
898
|
+
optional :query, TsInterpolation, 1
|
899
|
+
optional :stream, :bool, 2, :default => false
|
900
|
+
end
|
901
|
+
|
902
|
+
class TsQueryResp
|
903
|
+
repeated :columns, TsColumnDescription, 1
|
904
|
+
repeated :rows, TsRow, 2
|
905
|
+
optional :done, :bool, 3, :default => true
|
906
|
+
end
|
907
|
+
|
908
|
+
class TsGetReq
|
909
|
+
required :table, :bytes, 1
|
910
|
+
repeated :key, TsCell, 2
|
911
|
+
optional :timeout, :uint32, 3
|
912
|
+
end
|
913
|
+
|
914
|
+
class TsGetResp
|
915
|
+
repeated :columns, TsColumnDescription, 1
|
916
|
+
repeated :rows, TsRow, 2
|
917
|
+
end
|
918
|
+
|
919
|
+
class TsPutReq
|
920
|
+
required :table, :bytes, 1
|
921
|
+
repeated :columns, TsColumnDescription, 2
|
922
|
+
repeated :rows, TsRow, 3
|
923
|
+
end
|
924
|
+
|
925
|
+
class TsPutResp
|
926
|
+
end
|
927
|
+
|
928
|
+
class TsDelReq
|
929
|
+
required :table, :bytes, 1
|
930
|
+
repeated :key, TsCell, 2
|
931
|
+
optional :vclock, :bytes, 3
|
932
|
+
optional :timeout, :uint32, 4
|
933
|
+
end
|
934
|
+
|
935
|
+
class TsDelResp
|
936
|
+
end
|
937
|
+
|
938
|
+
class TsInterpolation
|
939
|
+
required :base, :bytes, 1
|
940
|
+
repeated :interpolations, RpbPair, 2
|
941
|
+
end
|
942
|
+
|
943
|
+
class TsColumnDescription
|
944
|
+
required :name, :bytes, 1
|
945
|
+
required :type, TsColumnType, 2
|
946
|
+
end
|
947
|
+
|
948
|
+
class TsRow
|
949
|
+
repeated :cells, TsCell, 1
|
950
|
+
end
|
951
|
+
|
952
|
+
class TsCell
|
953
|
+
optional :varchar_value, :bytes, 1
|
954
|
+
optional :sint64_value, :sint64, 2
|
955
|
+
optional :timestamp_value, :sint64, 3
|
956
|
+
optional :boolean_value, :bool, 4
|
957
|
+
optional :double_value, :double, 5
|
958
|
+
end
|
959
|
+
|
960
|
+
class TsListKeysReq
|
961
|
+
required :table, :bytes, 1
|
962
|
+
optional :timeout, :uint32, 2
|
963
|
+
end
|
964
|
+
|
965
|
+
class TsListKeysResp
|
966
|
+
repeated :keys, TsRow, 1
|
967
|
+
optional :done, :bool, 2
|
968
|
+
end
|
769
969
|
|
770
970
|
end
|
771
971
|
end
|
@@ -49,8 +49,7 @@ module Riak
|
|
49
49
|
pbuf.indexes.each {|pair| decode_index(pair, rcontent.indexes) }
|
50
50
|
end
|
51
51
|
if pbuf.last_mod.present?
|
52
|
-
rcontent.last_modified = Time.at(pbuf.last_mod)
|
53
|
-
rcontent.last_modified += pbuf.last_mod_usecs / 1000000 if pbuf.last_mod_usecs.present?
|
52
|
+
rcontent.last_modified = Time.at(pbuf.last_mod, pbuf.last_mod_usecs || 0)
|
54
53
|
end
|
55
54
|
rcontent
|
56
55
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative './ts_cell_codec'
|
2
|
+
require_relative './operator'
|
3
|
+
|
4
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
5
|
+
def time_series_delete_operator
|
6
|
+
TimeSeriesDeleteOperator.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TimeSeriesDeleteOperator < Operator
|
10
|
+
def delete(table_name, key_components, options = {})
|
11
|
+
codec = TsCellCodec.new
|
12
|
+
|
13
|
+
request_options = options.merge(table: table_name,
|
14
|
+
key: codec.cells_for(key_components))
|
15
|
+
|
16
|
+
request = TsDelReq.new request_options
|
17
|
+
|
18
|
+
backend.protocol do |p|
|
19
|
+
p.write :TsDelReq, request
|
20
|
+
p.expect :TsDelResp, TsDelResp, empty_body_acceptable: true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative './ts_cell_codec'
|
2
|
+
require_relative './operator'
|
3
|
+
|
4
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
5
|
+
def time_series_get_operator
|
6
|
+
TimeSeriesGetOperator.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TimeSeriesGetOperator < Operator
|
10
|
+
def get(table_name, key_components, options = {})
|
11
|
+
codec = TsCellCodec.new
|
12
|
+
|
13
|
+
request_options = options.merge(table: table_name,
|
14
|
+
key: codec.cells_for(key_components))
|
15
|
+
|
16
|
+
request = TsGetReq.new request_options
|
17
|
+
|
18
|
+
result = begin
|
19
|
+
backend.protocol do |p|
|
20
|
+
p.write :TsGetReq, request
|
21
|
+
result = p.expect :TsGetResp, TsGetResp, empty_body_acceptable: true
|
22
|
+
end
|
23
|
+
rescue Riak::ProtobuffsErrorResponse => e
|
24
|
+
raise unless e.code == 10
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
return nil if result == :empty
|
29
|
+
|
30
|
+
Riak::TimeSeries::Collection.new(result.rows.map do |row|
|
31
|
+
Riak::TimeSeries::Row.new codec.scalars_for row.cells
|
32
|
+
end.to_a)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative './ts_cell_codec'
|
2
|
+
require_relative './operator'
|
3
|
+
|
4
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
5
|
+
def time_series_list_operator
|
6
|
+
TimeSeriesListOperator.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TimeSeriesListOperator < Operator
|
10
|
+
def list(table_name, block, options = { })
|
11
|
+
request = TsListKeysReq.new options.merge(table: table_name)
|
12
|
+
|
13
|
+
return streaming_list_keys(request, &block) unless block.nil?
|
14
|
+
|
15
|
+
Riak::TimeSeries::Collection.new.tap do |key_buffer|
|
16
|
+
streaming_list_keys(request) do |key_row|
|
17
|
+
key_buffer << key_row
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def streaming_list_keys(request)
|
25
|
+
backend.protocol do |p|
|
26
|
+
p.write :TsListKeysReq, request
|
27
|
+
|
28
|
+
codec = TsCellCodec.new
|
29
|
+
|
30
|
+
while resp = p.expect(:TsListKeysResp, TsListKeysResp)
|
31
|
+
break if resp.done
|
32
|
+
resp.keys.each do |row|
|
33
|
+
key_fields = codec.scalars_for row.cells
|
34
|
+
yield key_fields
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative './ts_cell_codec'
|
2
|
+
require_relative './operator'
|
3
|
+
|
4
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
5
|
+
def time_series_put_operator
|
6
|
+
TimeSeriesPutOperator.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TimeSeriesPutOperator < Operator
|
10
|
+
def put(table_name, measurements)
|
11
|
+
rows = rows_for measurements
|
12
|
+
|
13
|
+
request = TsPutReq.new table: table_name, rows: rows
|
14
|
+
|
15
|
+
backend.protocol do |p|
|
16
|
+
p.write :TsPutReq, request
|
17
|
+
p.expect :TsPutResp, TsPutResp, empty_body_acceptable: true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def rows_for(measurements)
|
23
|
+
codec = TsCellCodec.new
|
24
|
+
measurements.map do |measurement|
|
25
|
+
# expect a measurement to be mappable
|
26
|
+
TsRow.new(cells: measurement.map do |measure|
|
27
|
+
codec.cell_for measure
|
28
|
+
end)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative './ts_cell_codec'
|
2
|
+
require_relative './operator'
|
3
|
+
|
4
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
5
|
+
def time_series_query_operator
|
6
|
+
TimeSeriesQueryOperator.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
class TimeSeriesQueryOperator < Operator
|
10
|
+
def query(base, interpolations = { })
|
11
|
+
interpolator = TsInterpolation.new base: base
|
12
|
+
interpolator.interpolations = pairs_for interpolations
|
13
|
+
|
14
|
+
request = TsQueryReq.new query: interpolator
|
15
|
+
|
16
|
+
result = backend.protocol do |p|
|
17
|
+
p.write :TsQueryReq, request
|
18
|
+
p.expect :TsQueryResp, TsQueryResp, empty_body_acceptable: true
|
19
|
+
end
|
20
|
+
|
21
|
+
return nil if :empty == result
|
22
|
+
|
23
|
+
codec = TsCellCodec.new
|
24
|
+
|
25
|
+
collection = Riak::TimeSeries::Collection.
|
26
|
+
new(result.rows.map do |row|
|
27
|
+
Riak::TimeSeries::Row.new codec.scalars_for row.cells
|
28
|
+
end)
|
29
|
+
|
30
|
+
collection.columns = result.columns
|
31
|
+
|
32
|
+
collection
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def pairs_for(interpolations)
|
37
|
+
interpolations.map do |key, value|
|
38
|
+
RpbPair.new key: key.to_s, value: value.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
class Riak::Client::BeefcakeProtobuffsBackend
|
4
|
+
class TsCellCodec
|
5
|
+
def cells_for(measures)
|
6
|
+
measures.map{ |m| cell_for m }
|
7
|
+
end
|
8
|
+
|
9
|
+
def scalars_for(cells)
|
10
|
+
cells.map{ |c| scalar_for c }
|
11
|
+
end
|
12
|
+
|
13
|
+
def cell_for(measure)
|
14
|
+
TsCell.new case measure
|
15
|
+
when String
|
16
|
+
{ varchar_value: measure }
|
17
|
+
when Fixnum
|
18
|
+
{ sint64_value: measure }
|
19
|
+
when Bignum
|
20
|
+
{ sint64_value: check_bignum_range(measure) }
|
21
|
+
when Float
|
22
|
+
{ double_value: measure }
|
23
|
+
when BigDecimal
|
24
|
+
{ double_value: measure.to_f }
|
25
|
+
when Rational
|
26
|
+
fail Riak::TimeSeriesError::SerializeRationalNumberError
|
27
|
+
when Complex
|
28
|
+
fail Riak::TimeSeriesError::SerializeComplexNumberError
|
29
|
+
when Time
|
30
|
+
seconds = measure.to_f
|
31
|
+
milliseconds = seconds * 1000
|
32
|
+
truncated_ms = milliseconds.to_i
|
33
|
+
{ timestamp_value: truncated_ms }
|
34
|
+
when TrueClass, FalseClass
|
35
|
+
{ boolean_value: measure }
|
36
|
+
when nil
|
37
|
+
{ }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def scalar_for(cell)
|
42
|
+
cell.varchar_value ||
|
43
|
+
cell.sint64_value ||
|
44
|
+
cell.double_value ||
|
45
|
+
timestamp(cell) ||
|
46
|
+
cell.boolean_value # boolean_value is last, so we can get either false, nil, or true
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def check_bignum_range(bignum)
|
51
|
+
if (bignum > -0x8000000000000000) && (bignum < 0x7FFFFFFFFFFFFFFF)
|
52
|
+
return bignum
|
53
|
+
end
|
54
|
+
|
55
|
+
fail Riak::TimeSeriesError::SerializeBigIntegerError, bignum
|
56
|
+
end
|
57
|
+
|
58
|
+
def timestamp(cell)
|
59
|
+
return false unless cell.timestamp_value.is_a? Integer
|
60
|
+
Time.at(cell.timestamp_value.to_f / 1000)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -16,6 +16,11 @@ module Riak
|
|
16
16
|
require 'riak/client/beefcake/bucket_properties_operator'
|
17
17
|
require 'riak/client/beefcake/crdt_operator'
|
18
18
|
require 'riak/client/beefcake/crdt_loader'
|
19
|
+
require 'riak/client/beefcake/time_series_delete_operator'
|
20
|
+
require 'riak/client/beefcake/time_series_get_operator'
|
21
|
+
require 'riak/client/beefcake/time_series_list_operator'
|
22
|
+
require 'riak/client/beefcake/time_series_put_operator'
|
23
|
+
require 'riak/client/beefcake/time_series_query_operator'
|
19
24
|
require 'riak/client/beefcake/protocol'
|
20
25
|
require 'riak/client/beefcake/socket'
|
21
26
|
true
|
data/lib/riak/client.rb
CHANGED
@@ -21,6 +21,7 @@ require 'riak/multiget'
|
|
21
21
|
require 'riak/secondary_index'
|
22
22
|
require 'riak/search'
|
23
23
|
require 'riak/stamp'
|
24
|
+
require 'riak/time_series'
|
24
25
|
require 'riak/list_buckets'
|
25
26
|
|
26
27
|
module Riak
|
@@ -356,6 +357,8 @@ module Riak
|
|
356
357
|
begin
|
357
358
|
yield backend
|
358
359
|
rescue *NETWORK_ERRORS => e
|
360
|
+
Riak.logger.warn("Riak client error: #{e.inspect} for #{backend.inspect}")
|
361
|
+
|
359
362
|
# Network error.
|
360
363
|
tries -= 1
|
361
364
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'riak/errors/base'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
class TimeSeriesError < Error
|
5
|
+
class SerializeComplexNumberError < TimeSeriesError
|
6
|
+
def initialize
|
7
|
+
super t('time_series.serialize_complex_number')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class SerializeRationalNumberError < TimeSeriesError
|
12
|
+
def initialize
|
13
|
+
super t('time_series.serialize_rational_number')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class SerializeBigIntegerError < TimeSeriesError
|
18
|
+
def initialize(bignum)
|
19
|
+
super t('time_series.serialize_big_integer', bignum: bignum)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/riak/locale/en.yml
CHANGED
@@ -97,6 +97,11 @@ en:
|
|
97
97
|
stored_function_invalid: "function must have :bucket and :key when a hash"
|
98
98
|
streaming_bucket_list_without_block: "Streaming bucket list was requested but no block was given."
|
99
99
|
string_type: "invalid_argument %{string} is not a String"
|
100
|
+
time_series:
|
101
|
+
list_keys: "Riak::TimeSeries::List is an expensive operation that should not be used in production.\n %{backtrace}"
|
102
|
+
serialize_big_integer: "%{bignum} is out of range for sint64 field"
|
103
|
+
serialize_complex_number: "Cannot serialize Complex numbers"
|
104
|
+
serialize_rational_number: "Cannot serialize Rational numbers without losing precision"
|
100
105
|
too_few_arguments: "too few arguments: %{params}"
|
101
106
|
walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
|
102
107
|
wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
|