riak-client 1.4.5 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Gemfile +0 -1
  4. data/{LICENSE → LICENSE.md} +0 -0
  5. data/README.markdown +211 -66
  6. data/RELEASE_NOTES.md +22 -47
  7. data/Rakefile +45 -0
  8. data/lib/riak.rb +1 -1
  9. data/lib/riak/bucket.rb +2 -2
  10. data/lib/riak/client.rb +22 -195
  11. data/lib/riak/client/beefcake/crdt_loader.rb +127 -0
  12. data/lib/riak/client/beefcake/crdt_operator.rb +222 -0
  13. data/lib/riak/client/beefcake/footer +4 -0
  14. data/lib/riak/client/beefcake/header +6 -0
  15. data/lib/riak/client/beefcake/message_codes.rb +29 -0
  16. data/lib/riak/client/beefcake/message_overlay.rb +61 -0
  17. data/lib/riak/client/beefcake/messages.rb +733 -371
  18. data/lib/riak/client/beefcake/object_methods.rb +1 -1
  19. data/lib/riak/client/beefcake/protocol.rb +105 -0
  20. data/lib/riak/client/beefcake/socket.rb +243 -0
  21. data/lib/riak/client/beefcake_protobuffs_backend.rb +262 -122
  22. data/lib/riak/client/node.rb +4 -75
  23. data/lib/riak/client/protobuffs_backend.rb +6 -14
  24. data/lib/riak/client/search.rb +0 -64
  25. data/lib/riak/client/yokozuna.rb +52 -0
  26. data/lib/riak/counter.rb +1 -1
  27. data/lib/riak/crdt.rb +21 -0
  28. data/lib/riak/crdt/base.rb +97 -0
  29. data/lib/riak/crdt/batch_counter.rb +19 -0
  30. data/lib/riak/crdt/batch_map.rb +41 -0
  31. data/lib/riak/crdt/counter.rb +71 -0
  32. data/lib/riak/crdt/inner_counter.rb +74 -0
  33. data/lib/riak/crdt/inner_flag.rb +42 -0
  34. data/lib/riak/crdt/inner_map.rb +53 -0
  35. data/lib/riak/crdt/inner_register.rb +26 -0
  36. data/lib/riak/crdt/inner_set.rb +95 -0
  37. data/lib/riak/crdt/map.rb +88 -0
  38. data/lib/riak/crdt/operation.rb +19 -0
  39. data/lib/riak/crdt/set.rb +156 -0
  40. data/lib/riak/crdt/typed_collection.rb +131 -0
  41. data/lib/riak/errors/base.rb +9 -0
  42. data/lib/riak/errors/connection_error.rb +44 -0
  43. data/lib/riak/errors/crdt_error.rb +18 -0
  44. data/lib/riak/errors/failed_request.rb +56 -0
  45. data/lib/riak/errors/protobuffs_error.rb +11 -0
  46. data/lib/riak/i18n.rb +2 -0
  47. data/lib/riak/json.rb +1 -1
  48. data/lib/riak/locale/en.yml +26 -1
  49. data/lib/riak/locale/fr.yml +0 -1
  50. data/lib/riak/map_reduce.rb +1 -1
  51. data/lib/riak/map_reduce/results.rb +1 -1
  52. data/lib/riak/multiget.rb +1 -2
  53. data/lib/riak/rcontent.rb +8 -3
  54. data/lib/riak/robject.rb +2 -8
  55. data/lib/riak/secondary_index.rb +4 -4
  56. data/lib/riak/serializers.rb +1 -1
  57. data/lib/riak/util/escape.rb +3 -5
  58. data/lib/riak/version.rb +1 -1
  59. data/lib/riak/walk_spec.rb +7 -3
  60. data/riak-client.gemspec +10 -8
  61. data/spec/fixtures/bitcask.txt +25 -0
  62. data/spec/integration/riak/bucket_types_spec.rb +61 -0
  63. data/spec/integration/riak/counters_spec.rb +17 -32
  64. data/spec/integration/riak/crdt_spec.rb +181 -0
  65. data/spec/integration/riak/crdt_validation/map_spec.rb +63 -0
  66. data/spec/integration/riak/crdt_validation/set_spec.rb +122 -0
  67. data/spec/integration/riak/protobuffs_backends_spec.rb +9 -26
  68. data/spec/integration/riak/security_spec.rb +94 -0
  69. data/spec/integration/riak/threading_spec.rb +24 -67
  70. data/spec/integration/yokozuna/index_spec.rb +61 -0
  71. data/spec/integration/yokozuna/queries_spec.rb +116 -0
  72. data/spec/integration/yokozuna/schema_spec.rb +49 -0
  73. data/spec/riak/beefcake_protobuffs_backend/crdt_operator_spec.rb +222 -0
  74. data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +4 -4
  75. data/spec/riak/beefcake_protobuffs_backend/protocol_spec.rb +189 -0
  76. data/spec/riak/beefcake_protobuffs_backend/socket_spec.rb +151 -0
  77. data/spec/riak/beefcake_protobuffs_backend_spec.rb +68 -106
  78. data/spec/riak/bucket_spec.rb +81 -77
  79. data/spec/riak/client_spec.rb +43 -340
  80. data/spec/riak/core_ext/to_param_spec.rb +2 -2
  81. data/spec/riak/counter_spec.rb +20 -20
  82. data/spec/riak/crdt/counter_spec.rb +52 -0
  83. data/spec/riak/crdt/inner_counter_spec.rb +21 -0
  84. data/spec/riak/crdt/inner_flag_spec.rb +39 -0
  85. data/spec/riak/crdt/inner_map_spec.rb +47 -0
  86. data/spec/riak/crdt/inner_register_spec.rb +40 -0
  87. data/spec/riak/crdt/inner_set_spec.rb +33 -0
  88. data/spec/riak/crdt/map_spec.rb +77 -0
  89. data/spec/riak/crdt/set_spec.rb +58 -0
  90. data/spec/riak/crdt/shared_examples.rb +74 -0
  91. data/spec/riak/crdt/typed_collection_spec.rb +231 -0
  92. data/spec/riak/escape_spec.rb +33 -37
  93. data/spec/riak/feature_detection_spec.rb +45 -45
  94. data/spec/riak/index_collection_spec.rb +12 -12
  95. data/spec/riak/link_spec.rb +34 -34
  96. data/spec/riak/list_buckets_spec.rb +7 -7
  97. data/spec/riak/map_reduce/filter_builder_spec.rb +6 -6
  98. data/spec/riak/map_reduce/phase_spec.rb +35 -35
  99. data/spec/riak/map_reduce_spec.rb +89 -87
  100. data/spec/riak/multiget_spec.rb +20 -15
  101. data/spec/riak/node_spec.rb +5 -152
  102. data/spec/riak/robject_spec.rb +95 -108
  103. data/spec/riak/search_spec.rb +17 -139
  104. data/spec/riak/secondary_index_spec.rb +49 -49
  105. data/spec/riak/serializers_spec.rb +9 -9
  106. data/spec/riak/stamp_spec.rb +9 -9
  107. data/spec/riak/walk_spec_spec.rb +46 -46
  108. data/spec/spec_helper.rb +14 -22
  109. data/spec/support/certs/README.md +13 -0
  110. data/spec/support/certs/ca.crt +22 -0
  111. data/spec/support/certs/client.crt +95 -0
  112. data/spec/support/certs/client.key +27 -0
  113. data/spec/support/certs/empty_ca.crt +21 -0
  114. data/spec/support/certs/server.crl +13 -0
  115. data/spec/support/certs/server.crt +95 -0
  116. data/spec/support/certs/server.key +27 -0
  117. data/spec/support/integration_setup.rb +1 -1
  118. data/spec/support/search_corpus_setup.rb +29 -8
  119. data/spec/support/test_client.rb +46 -0
  120. data/spec/support/test_client.yml.example +10 -0
  121. data/spec/support/unified_backend_examples.rb +104 -83
  122. data/spec/support/version_filter.rb +2 -2
  123. data/spec/support/wait_until.rb +14 -0
  124. metadata +134 -132
  125. data/erl_src/riak_kv_test014_backend.beam +0 -0
  126. data/erl_src/riak_kv_test014_backend.erl +0 -189
  127. data/erl_src/riak_kv_test_backend.beam +0 -0
  128. data/erl_src/riak_kv_test_backend.erl +0 -731
  129. data/erl_src/riak_search_test_backend.beam +0 -0
  130. data/erl_src/riak_search_test_backend.erl +0 -175
  131. data/lib/riak/client/excon_backend.rb +0 -172
  132. data/lib/riak/client/http_backend.rb +0 -413
  133. data/lib/riak/client/http_backend/bucket_streamer.rb +0 -15
  134. data/lib/riak/client/http_backend/chunked_json_streamer.rb +0 -42
  135. data/lib/riak/client/http_backend/configuration.rb +0 -227
  136. data/lib/riak/client/http_backend/key_streamer.rb +0 -15
  137. data/lib/riak/client/http_backend/object_methods.rb +0 -114
  138. data/lib/riak/client/http_backend/request_headers.rb +0 -34
  139. data/lib/riak/client/http_backend/transport_methods.rb +0 -201
  140. data/lib/riak/client/instrumentation.rb +0 -25
  141. data/lib/riak/client/net_http_backend.rb +0 -82
  142. data/lib/riak/cluster.rb +0 -151
  143. data/lib/riak/failed_request.rb +0 -81
  144. data/lib/riak/instrumentation.rb +0 -6
  145. data/lib/riak/node.rb +0 -40
  146. data/lib/riak/node/configuration.rb +0 -304
  147. data/lib/riak/node/console.rb +0 -133
  148. data/lib/riak/node/control.rb +0 -207
  149. data/lib/riak/node/defaults.rb +0 -85
  150. data/lib/riak/node/generation.rb +0 -127
  151. data/lib/riak/node/log.rb +0 -34
  152. data/lib/riak/node/version.rb +0 -29
  153. data/lib/riak/search.rb +0 -3
  154. data/lib/riak/test_server.rb +0 -89
  155. data/lib/riak/util/headers.rb +0 -32
  156. data/lib/riak/util/multipart.rb +0 -52
  157. data/lib/riak/util/multipart/stream_parser.rb +0 -62
  158. data/spec/fixtures/munchausen.txt +0 -1033
  159. data/spec/integration/riak/cluster_spec.rb +0 -88
  160. data/spec/integration/riak/http_backends_spec.rb +0 -180
  161. data/spec/integration/riak/node_spec.rb +0 -170
  162. data/spec/integration/riak/test_server_spec.rb +0 -57
  163. data/spec/riak/excon_backend_spec.rb +0 -102
  164. data/spec/riak/headers_spec.rb +0 -21
  165. data/spec/riak/http_backend/configuration_spec.rb +0 -273
  166. data/spec/riak/http_backend/object_methods_spec.rb +0 -243
  167. data/spec/riak/http_backend/transport_methods_spec.rb +0 -97
  168. data/spec/riak/http_backend_spec.rb +0 -367
  169. data/spec/riak/instrumentation_spec.rb +0 -167
  170. data/spec/riak/multipart_spec.rb +0 -23
  171. data/spec/riak/net_http_backend_spec.rb +0 -15
  172. data/spec/riak/stream_parser_spec.rb +0 -53
  173. data/spec/support/drb_mock_server.rb +0 -39
  174. data/spec/support/http_backend_implementation_examples.rb +0 -253
  175. data/spec/support/mock_server.rb +0 -81
  176. data/spec/support/mocks.rb +0 -4
  177. data/spec/support/riak_test.rb +0 -77
  178. data/spec/support/sometimes.rb +0 -46
  179. data/spec/support/test_server.rb +0 -61
  180. data/spec/support/test_server.yml.example +0 -14
@@ -31,7 +31,7 @@ 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
- robject.siblings = pbuf.content.map do |c|
34
+ robject.siblings = (pbuf.content || []).map do |c|
35
35
  RContent.new(robject) do |sibling|
36
36
  load_content(c, sibling)
37
37
  end
@@ -0,0 +1,105 @@
1
+ require 'riak/client/beefcake/messages'
2
+ require 'riak/client/beefcake/message_codes'
3
+ require 'riak/errors/failed_request'
4
+ require 'riak/errors/protobuffs_error'
5
+
6
+ module Riak
7
+ class Client
8
+ class BeefcakeProtobuffsBackend < ProtobuffsBackend
9
+ class Protocol
10
+ include Riak::Util::Translation
11
+ attr_reader :socket
12
+
13
+ def initialize(socket)
14
+ @socket = socket
15
+ end
16
+
17
+ # Encodes and writes a Riak-formatted message, including protocol buffer
18
+ # payload if given.
19
+ #
20
+ # @param [Symbol, Integer] code the symbolic or numeric code for the
21
+ # message
22
+ # @param [Beefcake::Message, nil] message the protocol buffer message
23
+ # payload, or nil if the message carries no payload
24
+ def write(code, message=nil)
25
+ if code.is_a? Symbol
26
+ code = BeefcakeMessageCodes.index code
27
+ end
28
+
29
+ serialized = serialize message
30
+
31
+ header = [serialized.length + 1, code].pack 'NC'
32
+
33
+ payload = header + serialized
34
+
35
+ socket.write payload
36
+ socket.flush
37
+ end
38
+
39
+ # Receives a Riak-formatted message, and returns the symbolic name of
40
+ # the message along with the string payload from the network.
41
+ #
42
+ # @return [Array<Symbol, String>]
43
+ def receive
44
+ header = socket.read 5
45
+
46
+ raise ProtobuffsFailedHeader.new if header.nil?
47
+ message_length, code = header.unpack 'NC'
48
+ body_length = message_length - 1
49
+ body = nil
50
+ body = socket.read body_length if body_length > 0
51
+
52
+ name = BeefcakeMessageCodes[code]
53
+
54
+ return name, body
55
+ end
56
+
57
+ # Receives a Riak-formatted message, checks the symbolic name against
58
+ # the given code, decodes it if it matches, and can optionally return
59
+ # success if the payload is empty.
60
+ #
61
+ # @param [Symbol] code the code for the message
62
+ # @param [Class, nil] decoder_class the class to attempt to decode
63
+ # the payload with
64
+ # @param [Hash] options
65
+ # @option options [Boolean] :empty_body_acceptable Whether to accept
66
+ # an empty body and not attempt decoding. In this case, this method
67
+ # will return the symbol `:empty` instead of a `Beefcake::Message`
68
+ # instance
69
+ # @return [Beefcake::Message, :empty]
70
+ # @raise {ProtobuffsErrorResponse} if the message from Riak was a
71
+ # 255-ErrorResp
72
+ # @raise {ProtobuffsUnexpectedResponse} if the message from riak did
73
+ # not match `code`
74
+ def expect(code, decoder_class=nil, options={ })
75
+ code = BeefcakeMessageCodes[code] unless code.is_a? Symbol
76
+ name, body = receive
77
+
78
+ if name == :ErrorResp
79
+ raise ProtobuffsErrorResponse.new RpbErrorResp.decode(body)
80
+ end
81
+
82
+ if name != code
83
+ raise ProtobuffsUnexpectedResponse.new name, code
84
+ end
85
+
86
+ return true if decoder_class.nil?
87
+
88
+ return :empty if body.nil? && options[:empty_body_acceptable]
89
+
90
+ return decoder_class.decode body
91
+ end
92
+
93
+ private
94
+
95
+ def serialize(message)
96
+ return '' if message.nil?
97
+ return message if message.is_a? String
98
+ return message.encode.to_s if message.is_a? Beefcake::Message
99
+
100
+ raise ArgumentError.new t('pbc.unknown_serialize', message: message)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,243 @@
1
+ require 'openssl'
2
+ require 'r509/cert/validator'
3
+ require 'riak/client/beefcake/messages'
4
+ require 'riak/errors/connection_error'
5
+
6
+ module Riak
7
+ class Client
8
+ class BeefcakeProtobuffsBackend
9
+ # A factory class for making sockets, whether secure or not
10
+ # @api private
11
+ class BeefcakeSocket
12
+ include Client::BeefcakeMessageCodes
13
+ # Only create class methods, don't initialize
14
+ class << self
15
+ def new(host, port, options={})
16
+ return start_tcp_socket(host, port) if options[:authentication].blank?
17
+ return start_tls_socket(host, port, options[:authentication])
18
+ end
19
+
20
+ private
21
+ def start_tcp_socket(host, port)
22
+ TCPSocket.new(host, port).tap do |sock|
23
+ sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
24
+ end
25
+ end
26
+
27
+ def start_tls_socket(host, port, authentication)
28
+ raise Riak::UserConfigurationError.new if authentication[:username]
29
+
30
+ tcp = start_tcp_socket(host, port)
31
+ TlsInitiator.new(tcp, host, authentication).tls_socket
32
+ end
33
+
34
+ # Wrap up the logic to turn a TCP socket into a TLS socket.
35
+ # Depends on Beefcake, which should be relatively safe.
36
+ class TlsInitiator
37
+ BC = ::Riak::Client::BeefcakeProtobuffsBackend
38
+ include Util::Translation
39
+
40
+ # Create a TLS Initiator
41
+ #
42
+ # @param tcp_socket [TCPSocket] the {TCPSocket} to start TLS on
43
+ # @param authentication [Hash] a hash of authentication details
44
+ def initialize(tcp_socket, host, authentication)
45
+ @sock = @tcp = tcp_socket
46
+ @host = host
47
+ @auth = authentication
48
+ end
49
+
50
+ # Return the SSLSocket that has a TLS session running. (TLS is a
51
+ # better and safer SSL).
52
+ #
53
+ # @return [OpenSSL::SSL::SSLSocket]
54
+ def tls_socket
55
+ configure_context
56
+ start_tls
57
+ validate_session
58
+ send_authentication
59
+ validate_connection
60
+ return @tls
61
+ end
62
+
63
+ private
64
+ def riak_cert
65
+ @riak_cert ||= R509::Cert.new cert: @tls.peer_cert
66
+ end
67
+
68
+ # Set up an SSL context with appropriate defaults for Riak TLS
69
+ def configure_context
70
+ @context = OpenSSL::SSL::SSLContext.new
71
+
72
+ # Replace insecure defaults
73
+ @context.ssl_version = @auth[:ssl_version] || :TLSv1_2_client
74
+ @context.verify_mode = @auth[:verify_mode] || OpenSSL::SSL::VERIFY_PEER
75
+
76
+ cert_ify
77
+ key_ify
78
+
79
+ # Defer to defaults
80
+ %w{ cert key client_ca ca_file ca_path timeout }.each do |k|
81
+ @context.send(:"#{k}=", @auth[k.to_sym]) if @auth[k.to_sym]
82
+ end
83
+ end
84
+
85
+ # Convert cert and client_ca fields to X509 Certs
86
+ def cert_ify
87
+ %w{ cert client_ca }.each do |k|
88
+ candidate = @auth[k.to_sym]
89
+ next if candidate.nil?
90
+ next if candidate.is_a? OpenSSL::X509::Certificate
91
+
92
+ @auth[k.to_sym] = OpenSSL::X509::Certificate.new try_load candidate
93
+ end
94
+ end
95
+
96
+ def key_ify
97
+ candidate = @auth[:key]
98
+ return if candidate.nil?
99
+ return if candidate.is_a? OpenSSL::PKey::PKey
100
+
101
+ candidate = try_load candidate
102
+
103
+ pkey_class_names = OpenSSL::PKey.
104
+ constants.
105
+ reject{|s| s.to_s =~ /Error$/}
106
+
107
+ pkey_classes = pkey_class_names.map{ |n| OpenSSL::PKey.const_get n }
108
+
109
+ pkey_classes.each do |klass|
110
+ begin
111
+ successfully_initialized = klass.new candidate
112
+ @auth[:key] = successfully_initialized
113
+ return
114
+ rescue
115
+ next
116
+ end
117
+ end
118
+
119
+ # Don't try and guess what the key is
120
+ raise TlsError::UnknownKeyTypeError.new
121
+ end
122
+
123
+ # Figure out if the given string is the data itself or a path to the data
124
+ def try_load(data_or_path)
125
+ begin
126
+ data_or_path = File.read data_or_path
127
+ rescue Errno::ENOENT
128
+ # couldn't read the file, it might be a string containing
129
+ # a key
130
+ rescue Errno::ENAMETOOLONG
131
+ # the filename is too long, it's almost certainly a string
132
+ # containing a key
133
+ rescue => e
134
+ raise TlsError::ReadDataError.new e, data_or_path
135
+ end
136
+
137
+ return data_or_path
138
+ end
139
+
140
+ # Attempt to exchange the TCP socket for a TLS socket.
141
+ def start_tls
142
+ write_message :StartTls
143
+ expect_message :StartTls
144
+ # Swap the tls socket in for the tcp socket, so write_message and
145
+ # read_message continue working
146
+ @sock = @tls = OpenSSL::SSL::SSLSocket.new @tcp, @context
147
+ @tls.connect
148
+ end
149
+
150
+ # Validate the TLS session
151
+ def validate_session
152
+ if @auth[:verify_hostname] &&
153
+ !OpenSSL::SSL::verify_certificate_identity(riak_cert.cert, @host)
154
+ raise TlsError::CertHostMismatchError.new
155
+ end
156
+
157
+ unless riak_cert.valid?
158
+ raise TlsError::CertNotValidError.new
159
+ end
160
+
161
+ validator = R509::Cert::Validator.new riak_cert
162
+
163
+ validator_options = {}
164
+ validator_options[:ocsp] = !!@auth[:ocsp]
165
+ validator_options[:crl] = !!@auth[:crl]
166
+ validator_options[:crl_file] = @auth[:crl_file]
167
+
168
+ unless validator.validate(validator_options)
169
+ raise TlsError::CertRevokedError.new
170
+ end
171
+ end
172
+
173
+ def validator_options
174
+ o = {
175
+ ocsp: !!@auth[:ocsp],
176
+ crl: !!@auth[:crl]
177
+ }
178
+
179
+ if @auth[:crl_file]
180
+ o[:crl_file] = @auth[:crl_file]
181
+ o[:crl] = true
182
+ end
183
+
184
+ return o
185
+ end
186
+
187
+ # Send an AuthReq with the authentication data. Rely on beefcake
188
+ # discarding message parts it doesn't understand.
189
+ def send_authentication
190
+ req = BC::RpbAuthReq.new @auth
191
+ write_message :AuthReq, req.encode
192
+ expect_message :AuthResp
193
+ end
194
+
195
+ # Ping the Riak node and make sure it actually works.
196
+ def validate_connection
197
+ write_message :PingReq
198
+ expect_message :PingResp
199
+ end
200
+
201
+ # Write a protocol buffers message to whatever the current
202
+ # socket is.
203
+ def write_message(code, message='')
204
+ if code.is_a? Symbol
205
+ code = BeefcakeMessageCodes.index code
206
+ end
207
+
208
+ header = [message.length+1, code].pack 'NC'
209
+ @sock.write header + message
210
+ end
211
+
212
+ def read_message
213
+ header = @sock.read 5
214
+ raise TlsError.new(t('ssl.eof_during_init')) if header.nil?
215
+ len, code = header.unpack 'NC'
216
+ decode = BeefcakeMessageCodes[code]
217
+ return decode, '' if len == 1
218
+
219
+ message = @sock.read(len - 1)
220
+ return decode, message
221
+ end
222
+
223
+ def expect_message(expected_code)
224
+ if expected_code.is_a? Numeric
225
+ expected_code = BeefcakeMessageCodes[code]
226
+ end
227
+
228
+ candidate_code, message = read_message
229
+ return message if expected_code == candidate_code
230
+
231
+ raise TlsError.new(t('ssl.unexpected_during_init',
232
+ expected: expected_code.inspect,
233
+ actual: candidate_code.inspect,
234
+ body: message.inspect
235
+ ))
236
+
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -1,7 +1,7 @@
1
1
  require 'base64'
2
2
  require 'riak/json'
3
3
  require 'riak/client'
4
- require 'riak/failed_request'
4
+ require 'riak/errors/failed_request'
5
5
  require 'riak/client/protobuffs_backend'
6
6
 
7
7
  module Riak
@@ -11,13 +11,50 @@ module Riak
11
11
  begin
12
12
  require 'beefcake'
13
13
  require 'riak/client/beefcake/messages'
14
+ require 'riak/client/beefcake/message_overlay'
14
15
  require 'riak/client/beefcake/object_methods'
16
+ require 'riak/client/beefcake/crdt_operator'
17
+ require 'riak/client/beefcake/crdt_loader'
18
+ require 'riak/client/beefcake/protocol'
19
+ require 'riak/client/beefcake/socket'
15
20
  true
16
21
  rescue LoadError, NameError
17
22
  false
18
23
  end
19
24
  end
20
25
 
26
+ def protocol
27
+ p = Protocol.new socket
28
+ yield p
29
+ end
30
+
31
+ def new_socket
32
+ BeefcakeSocket.new @node.host, @node.pb_port, authentication: client.authentication
33
+ end
34
+
35
+ def ping
36
+ protocol do |p|
37
+ p.write :PingReq
38
+ p.expect :PingResp
39
+ end
40
+ end
41
+
42
+ def get_client_id
43
+ protocol do |p|
44
+ p.write :GetClientIdReq
45
+ p.expect(:GetClientIdResp, RpbGetClientIdResp).client_id
46
+ end
47
+ end
48
+
49
+ def server_info
50
+ resp = protocol do |p|
51
+ p.write :GetServerInfoReq
52
+ p.expect(:GetServerInfoResp, RpbGetServerInfoResp)
53
+ end
54
+
55
+ { node: resp.node, server_version: resp.server_version }
56
+ end
57
+
21
58
  def set_client_id(id)
22
59
  value = case id
23
60
  when Integer
@@ -26,16 +63,29 @@ module Riak
26
63
  id.to_s
27
64
  end
28
65
  req = RpbSetClientIdReq.new(:client_id => value)
29
- write_protobuff(:SetClientIdReq, req)
30
- decode_response
66
+ protocol do |p|
67
+ p.write :SetClientIdReq, req
68
+ p.expect :SetClientIdResp
69
+ end
70
+ return true
31
71
  end
32
72
 
33
73
  def fetch_object(bucket, key, options={})
34
74
  options = prune_unsupported_options(:GetReq, normalize_quorums(options))
35
75
  bucket = Bucket === bucket ? bucket.name : bucket
36
76
  req = RpbGetReq.new(options.merge(:bucket => maybe_encode(bucket), :key => maybe_encode(key)))
37
- write_protobuff(:GetReq, req)
38
- decode_response(RObject.new(client.bucket(bucket), key))
77
+
78
+ resp = protocol do |p|
79
+ p.write :GetReq, req
80
+ p.expect :GetResp, RpbGetResp, empty_body_acceptable: true
81
+ end
82
+
83
+ if :empty == resp
84
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
85
+ end
86
+
87
+ template = RObject.new(client.bucket(bucket), key)
88
+ load_object(resp, template)
39
89
  end
40
90
 
41
91
  def reload_object(robject, options={})
@@ -44,11 +94,21 @@ module Riak
44
94
  options[:key] = maybe_encode(robject.key)
45
95
  options[:if_modified] = maybe_encode Base64.decode64(robject.vclock) if robject.vclock
46
96
  req = RpbGetReq.new(prune_unsupported_options(:GetReq, options))
47
- write_protobuff(:GetReq, req)
48
- decode_response(robject)
97
+
98
+ resp = protocol do |p|
99
+ p.write :GetReq, req
100
+ p.expect :GetResp, RpbGetResp, empty_body_acceptable: true
101
+ end
102
+
103
+ if :empty == resp
104
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
105
+ end
106
+
107
+ load_object(resp, robject)
49
108
  end
50
109
 
51
110
  def store_object(robject, options={})
111
+ options[:return_body] ||= options[:returnbody]
52
112
  options = normalize_quorums(options)
53
113
  if robject.prevent_stale_writes
54
114
  unless pb_conditionals?
@@ -62,8 +122,15 @@ module Riak
62
122
  end
63
123
  end
64
124
  req = dump_object(robject, prune_unsupported_options(:PutReq, options))
65
- write_protobuff(:PutReq, req)
66
- decode_response(robject)
125
+
126
+ resp = protocol do |p|
127
+ p.write(:PutReq, req)
128
+ p.expect :PutResp, RpbPutResp, empty_body_acceptable: true
129
+ end
130
+
131
+ return true if :empty == resp
132
+
133
+ load_object resp, robject
67
134
  end
68
135
 
69
136
  def delete_object(bucket, key, options={})
@@ -73,8 +140,13 @@ module Riak
73
140
  options[:key] = maybe_encode(key)
74
141
  options[:vclock] = Base64.decode64(options[:vclock]) if options[:vclock]
75
142
  req = RpbDelReq.new(prune_unsupported_options(:DelReq, options))
76
- write_protobuff(:DelReq, req)
77
- decode_response
143
+
144
+ protocol do |p|
145
+ p.write :DelReq, req
146
+ p.expect :DelResp
147
+ end
148
+
149
+ return true
78
150
  end
79
151
 
80
152
  def get_counter(bucket, key, options={})
@@ -85,9 +157,17 @@ module Riak
85
157
  options[:key] = key
86
158
 
87
159
  request = RpbCounterGetReq.new options
88
- write_protobuff :CounterGetReq, request
89
160
 
90
- decode_response
161
+ resp = protocol do |p|
162
+ p.write :CounterGetReq, request
163
+ p.expect :CounterGetResp, RpbCounterGetResp, empty_body_acceptable: true
164
+ end
165
+
166
+ if :empty == resp
167
+ return 0
168
+ end
169
+
170
+ return resp.value || 0
91
171
  end
92
172
 
93
173
  def post_counter(bucket, key, amount, options={})
@@ -96,53 +176,83 @@ module Riak
96
176
  options = normalize_quorums(options)
97
177
  options[:bucket] = bucket
98
178
  options[:key] = key
99
- # TODO: raise if ammount doesn't fit in sint64
179
+ # TODO: raise if amount doesn't fit in sint64
100
180
  options[:amount] = amount
101
-
181
+ options[:returnvalue] = options[:returnvalue] || options[:return_value]
182
+
102
183
  request = RpbCounterUpdateReq.new options
103
- write_protobuff :CounterUpdateReq, request
104
184
 
105
- decode_response
185
+ resp = protocol do |p|
186
+ p.write :CounterUpdateReq, request
187
+ p.expect :CounterUpdateResp, RpbCounterUpdateResp, empty_body_acceptable: true
188
+ end
189
+
190
+ return nil if :empty == resp
191
+
192
+ return resp.value
106
193
  end
107
194
 
108
- def get_bucket_props(bucket)
195
+ def get_bucket_props(bucket, options = { })
109
196
  bucket = bucket.name if Bucket === bucket
197
+
110
198
  req = RpbGetBucketReq.new(:bucket => maybe_encode(bucket))
111
- write_protobuff(:GetBucketReq, req)
112
- resp = normalize_quorums decode_response
199
+ req.type = options[:type] if options[:type]
200
+
201
+ resp_message = protocol do |p|
202
+ p.write :GetBucketReq, req
203
+ p.expect :GetBucketResp, RpbGetBucketResp
204
+ end
205
+
206
+ resp = normalize_quorums resp_message.props.to_hash.stringify_keys
113
207
  normalized = normalize_hooks resp
114
208
  normalized.stringify_keys
115
209
  end
116
210
 
117
- def set_bucket_props(bucket, props)
211
+ def set_bucket_props(bucket, props, type=nil)
118
212
  bucket = bucket.name if Bucket === bucket
119
- props = props.slice('n_val', 'allow_mult')
120
- req = RpbSetBucketReq.new(:bucket => maybe_encode(bucket), :props => RpbBucketProps.new(props))
121
- write_protobuff(:SetBucketReq, req)
122
- decode_response
213
+ req = RpbSetBucketReq.new(
214
+ bucket: maybe_encode(bucket),
215
+ props: RpbBucketProps.new(props.symbolize_keys),
216
+ type: type)
217
+
218
+ protocol do |p|
219
+ p.write :SetBucketReq, req
220
+ p.expect :SetBucketResp
221
+ end
123
222
  end
124
223
 
125
224
  def reset_bucket_props(bucket)
126
225
  bucket = bucket.name if Bucket === bucket
127
226
  req = RpbResetBucketReq.new(:bucket => maybe_encode(bucket))
128
- write_protobuff(:ResetBucketReq)
129
- decode_response
227
+
228
+ protocol do |p|
229
+ p.write :ResetBucketReq, req
230
+ p.expect :ResetBucketResp
231
+ end
130
232
  end
131
233
 
132
234
  def list_keys(bucket, options={}, &block)
133
235
  bucket = bucket.name if Bucket === bucket
134
236
  req = RpbListKeysReq.new(options.merge(:bucket => maybe_encode(bucket)))
135
- write_protobuff(:ListKeysReq, req)
237
+
136
238
  keys = []
137
- while msg = decode_response
138
- break if msg.done
139
- if block_given?
140
- yield msg.keys
141
- else
142
- keys += msg.keys
239
+
240
+ protocol do |p|
241
+ p.write :ListKeysReq, req
242
+
243
+ while msg = p.expect(:ListKeysResp, RpbListKeysResp)
244
+ break if msg.done
245
+ if block_given?
246
+ yield msg.keys
247
+ else
248
+ keys += msg.keys
249
+ end
143
250
  end
144
251
  end
145
- block_given? || keys
252
+
253
+ return keys unless block_given?
254
+
255
+ return true
146
256
  end
147
257
 
148
258
  # override the simple list_buckets
@@ -155,24 +265,35 @@ module Riak
155
265
 
156
266
  request = RpbListBucketsReq.new options
157
267
 
158
- write_protobuff :ListBucketsReq, request
268
+ resp = protocol do |p|
269
+ p.write :ListBucketsReq, request
270
+
271
+ p.expect :ListBucketsResp, RpbListBucketsResp, empty_body_acceptable: true
272
+ end
273
+
274
+ return [] if :empty == resp
159
275
 
160
- decode_response
276
+ resp.buckets
161
277
  end
162
278
 
163
279
  def mapred(mr, &block)
164
280
  raise MapReduceError.new(t("empty_map_reduce_query")) if mr.query.empty? && !mapred_phaseless?
165
281
  req = RpbMapRedReq.new(:request => mr.to_json, :content_type => "application/json")
166
- write_protobuff(:MapRedReq, req)
282
+
167
283
  results = MapReduce::Results.new(mr)
168
- while msg = decode_response
169
- break if msg.done
170
- if block_given?
171
- yield msg.phase, JSON.parse(msg.response)
172
- else
173
- results.add msg.phase, JSON.parse(msg.response)
284
+
285
+ protocol do |p|
286
+ p.write :MapRedReq, req
287
+ while msg = p.expect(:MapRedResp, RpbMapRedResp)
288
+ break if msg.done
289
+ if block_given?
290
+ yield msg.phase, JSON.parse(msg.response)
291
+ else
292
+ results.add msg.phase, JSON.parse(msg.response)
293
+ end
174
294
  end
175
295
  end
296
+
176
297
  block_given? || results.report
177
298
  end
178
299
 
@@ -197,8 +318,11 @@ module Riak
197
318
  options[:stream] = block_given?
198
319
 
199
320
  req = RpbIndexReq.new(options)
200
- write_protobuff(:IndexReq, req)
201
- decode_index_response(&block)
321
+
322
+ protocol do |p|
323
+ p.write :IndexReq, req
324
+ decode_index_response(p, &block)
325
+ end
202
326
  end
203
327
 
204
328
  def search(index, query, options={})
@@ -206,35 +330,97 @@ module Riak
206
330
  options = options.symbolize_keys
207
331
  options[:op] = options.delete(:'q.op') if options[:'q.op']
208
332
  req = RpbSearchQueryReq.new(options.merge(:index => index || 'search', :q => query))
209
- write_protobuff(:SearchQueryReq, req)
210
- decode_response
333
+
334
+ resp = protocol do |p|
335
+ p.write :SearchQueryReq, req
336
+ p.expect :SearchQueryResp, RpbSearchQueryResp
337
+ end
338
+
339
+ resp.docs = [] if resp.docs.nil?
340
+
341
+ ret = { 'max_score' => resp.max_score, 'num_found' => resp.num_found }
342
+ ret['docs'] = resp.docs.map { |d| decode_doc d }
343
+
344
+ return ret
345
+ end
346
+
347
+ def create_search_index(name, schema=nil, n_val=nil)
348
+ index = RpbYokozunaIndex.new(:name => name, :schema => schema, :n_val => n_val)
349
+ req = RpbYokozunaIndexPutReq.new(:index => index)
350
+
351
+ protocol do |p|
352
+ p.write :YokozunaIndexPutReq, req
353
+ p.expect :PutResp
354
+ end
355
+ end
356
+
357
+ def get_search_index(name)
358
+ req = RpbYokozunaIndexGetReq.new(:name => name)
359
+ resp = protocol do |p|
360
+ p.write :YokozunaIndexGetReq, req
361
+ p.expect :YokozunaIndexGetResp, RpbYokozunaIndexGetResp, empty_body_acceptable: true
362
+ end
363
+
364
+ if :empty == resp
365
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
366
+ end
367
+
368
+ if resp.index && Array === resp
369
+ resp.index.map{|index| {:name => index.name, :schema => index.schema, :n_val => index.n_val} }
370
+ else
371
+ resp
372
+ end
373
+ end
374
+
375
+ def delete_search_index(name)
376
+ req = RpbYokozunaIndexDeleteReq.new(:name => name)
377
+ protocol do |p|
378
+ p.write :YokozunaIndexDeleteReq, req
379
+ p.expect :DelResp
380
+ end
381
+ true
382
+ end
383
+
384
+ def create_search_schema(name, content)
385
+ schema = RpbYokozunaSchema.new(:name => name, :content => content)
386
+ req = RpbYokozunaSchemaPutReq.new(:schema => schema)
387
+
388
+ protocol do |p|
389
+ p.write :YokozunaSchemaPutReq, req
390
+ p.expect :PutResp
391
+ end
392
+ true
393
+ end
394
+
395
+ def get_search_schema(name)
396
+ req = RpbYokozunaSchemaGetReq.new(:name => name)
397
+
398
+ resp = protocol do |p|
399
+ p.write :YokozunaSchemaGetReq, req
400
+ p.expect :YokozunaSchemaGetResp, RpbYokozunaSchemaGetResp
401
+ end
402
+
403
+ resp.schema ? resp.schema : resp
211
404
  end
212
405
 
213
- private
214
406
  def write_protobuff(code, message)
215
407
  encoded = message.encode
216
408
  header = [encoded.length+1, MESSAGE_CODES.index(code)].pack("NC")
217
409
  socket.write(header + encoded)
218
410
  end
219
411
 
412
+ private
220
413
  def decode_response(*args)
221
414
  header = socket.read(5)
222
- raise SocketError, "Unexpected EOF on PBC socket" if header.nil?
415
+ raise ProtobuffsFailedHeader.new if header.nil?
223
416
  msglen, msgcode = header.unpack("NC")
224
417
  if msglen == 1
225
418
  case MESSAGE_CODES[msgcode]
226
- when :PingResp,
227
- :SetClientIdResp,
228
- :PutResp,
229
- :DelResp,
230
- :SetBucketResp,
231
- :ResetBucketResp
232
- true
233
- when :ListBucketsResp,
234
- :ListKeysResp,
419
+ when :ListBucketsResp,
235
420
  :IndexResp
236
421
  []
237
- when :GetResp
422
+ when :GetResp,
423
+ :YokozunaSchemaGetResp
238
424
  raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
239
425
  when :CounterGetResp,
240
426
  :CounterUpdateResp
@@ -248,44 +434,6 @@ module Riak
248
434
  when :ErrorResp
249
435
  res = RpbErrorResp.decode(message)
250
436
  raise Riak::ProtobuffsFailedRequest.new(res.errcode, res.errmsg)
251
- when :GetClientIdResp
252
- res = RpbGetClientIdResp.decode(message)
253
- res.client_id
254
- when :GetServerInfoResp
255
- res = RpbGetServerInfoResp.decode(message)
256
- {:node => res.node, :server_version => res.server_version}
257
- when :GetResp
258
- res = RpbGetResp.decode(message)
259
- load_object(res, args.first)
260
- when :PutResp
261
- res = RpbPutResp.decode(message)
262
- load_object(res, args.first)
263
- when :ListBucketsResp
264
- res = RpbListBucketsResp.decode(message)
265
- res.buckets
266
- when :ListKeysResp
267
- RpbListKeysResp.decode(message)
268
- when :GetBucketResp
269
- res = RpbGetBucketResp.decode(message)
270
- res.props.to_hash.stringify_keys
271
- when :MapRedResp
272
- RpbMapRedResp.decode(message)
273
- when :IndexResp
274
- res = RpbIndexResp.decode(message)
275
- IndexCollection.new_from_protobuf res
276
- when :SearchQueryResp
277
- res = RpbSearchQueryResp.decode(message)
278
- { 'docs' => res.docs.map {|d| decode_doc(d) },
279
- 'max_score' => res.max_score,
280
- 'num_found' => res.num_found }
281
- when :CSBucketResp
282
- res = RpbCSBucketResp.decode message
283
- when :CounterUpdateResp
284
- res = RpbCounterUpdateResp.decode message
285
- res.value || nil
286
- when :CounterGetResp
287
- res = RpbCounterGetResp.decode message
288
- res.value || 0
289
437
  end
290
438
  end
291
439
  rescue SystemCallError, SocketError => e
@@ -312,45 +460,37 @@ module Riak
312
460
  end
313
461
  end
314
462
 
315
- def decode_index_response
463
+ def decode_index_response(p)
316
464
  loop do
317
- header = socket.read(5)
318
- raise SocketError, "Unexpected EOF on PBC socket" if header.nil?
319
- msglen, msgcode = header.unpack("NC")
320
- code = MESSAGE_CODES[msgcode]
321
- if code == :ErrorResp
322
- resp = RpbErrorResp.decode socket.read msglen - 1
323
- message = resp.errmsg
324
- if match = message.match(/indexes_not_supported,(\w+)/)
325
- message = t('index.wrong_backend', backend: match[1])
326
- end
327
- raise ProtobuffsFailedRequest.new resp.errcode, message
328
- elsif code != :IndexResp
329
- teardown # close socket, we don't know what's going on anymore
330
- inner = ProtobuffsFailedRequest.new code, t('protobuffs.unexpected_message')
331
- raise Innertube::Pool::BadResource, inner
332
- end
465
+ resp = p.expect :IndexResp, RpbIndexResp, empty_body_acceptable: true
333
466
 
334
- if msglen == 1
467
+ if :empty == resp
335
468
  return if block_given?
336
469
  return IndexCollection.new_from_protobuf(RpbIndexResp.decode(''))
337
470
  end
338
471
 
339
- message = RpbIndexResp.decode socket.read msglen - 1
340
-
341
472
  if !block_given?
342
- return IndexCollection.new_from_protobuf(message)
473
+ return IndexCollection.new_from_protobuf(resp)
343
474
  end
344
475
 
345
- content = message.keys || message.results || []
476
+ content = resp.keys || resp.results || []
346
477
  yield content
347
478
 
348
- return if message.done
479
+ return if resp.done
480
+ end
481
+ rescue ProtobuffsErrorResponse => err
482
+ if match = err.message.match(/indexes_not_supported,(\w+)/)
483
+ old_err = err
484
+ err = ProtobuffsFailedRequest.new(:indexes_not_supported,
485
+ t('index.wrong_backend', backend: match[1])
486
+ )
349
487
  end
488
+
489
+ raise err
350
490
  end
351
491
 
352
492
  def decode_doc(doc)
353
- Hash[doc.properties.map {|p| [ force_utf8(p.key), force_utf8(p.value) ] }]
493
+ Hash[doc.fields.map {|p| [ force_utf8(p.key), force_utf8(p.value) ] }]
354
494
  end
355
495
 
356
496
  def force_utf8(str)