aerospike 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/lib/aerospike.rb +5 -0
  4. data/lib/aerospike/atomic/atomic.rb +3 -1
  5. data/lib/aerospike/client.rb +98 -32
  6. data/lib/aerospike/cluster/cluster.rb +25 -8
  7. data/lib/aerospike/cluster/connection.rb +1 -1
  8. data/lib/aerospike/cluster/node.rb +16 -3
  9. data/lib/aerospike/cluster/node_validator.rb +16 -4
  10. data/lib/aerospike/cluster/partition.rb +1 -1
  11. data/lib/aerospike/cluster/partition_tokenizer_new.rb +4 -2
  12. data/lib/aerospike/cluster/partition_tokenizer_old.rb +1 -1
  13. data/lib/aerospike/command/admin_command.rb +353 -0
  14. data/lib/aerospike/command/batch_command.rb +12 -42
  15. data/lib/aerospike/command/batch_command_exists.rb +1 -1
  16. data/lib/aerospike/command/batch_command_get.rb +1 -1
  17. data/lib/aerospike/command/batch_item.rb +1 -1
  18. data/lib/aerospike/command/batch_node.rb +1 -1
  19. data/lib/aerospike/command/command.rb +9 -14
  20. data/lib/aerospike/command/delete_command.rb +1 -1
  21. data/lib/aerospike/command/execute_command.rb +1 -1
  22. data/lib/aerospike/command/exists_command.rb +1 -1
  23. data/lib/aerospike/command/operate_command.rb +1 -1
  24. data/lib/aerospike/command/read_command.rb +12 -38
  25. data/lib/aerospike/command/read_header_command.rb +2 -2
  26. data/lib/aerospike/command/roles.rb +36 -0
  27. data/lib/aerospike/command/single_command.rb +1 -1
  28. data/lib/aerospike/command/touch_command.rb +1 -1
  29. data/lib/aerospike/command/write_command.rb +1 -1
  30. data/lib/aerospike/info.rb +1 -1
  31. data/lib/aerospike/loggable.rb +1 -1
  32. data/lib/aerospike/policy/admin_policy.rb +33 -0
  33. data/lib/aerospike/policy/batch_policy.rb +5 -5
  34. data/lib/aerospike/policy/client_policy.rb +15 -4
  35. data/lib/aerospike/policy/generation_policy.rb +0 -5
  36. data/lib/aerospike/policy/policy.rb +6 -6
  37. data/lib/aerospike/policy/query_policy.rb +2 -2
  38. data/lib/aerospike/policy/scan_policy.rb +6 -6
  39. data/lib/aerospike/policy/write_policy.rb +8 -8
  40. data/lib/aerospike/query/query_command.rb +1 -1
  41. data/lib/aerospike/query/scan_command.rb +1 -1
  42. data/lib/aerospike/query/stream_command.rb +1 -1
  43. data/lib/aerospike/record.rb +2 -3
  44. data/lib/aerospike/result_code.rb +11 -1
  45. data/lib/aerospike/user_role.rb +30 -0
  46. data/lib/aerospike/utils/buffer.rb +22 -2
  47. data/lib/aerospike/utils/epoc.rb +3 -1
  48. data/lib/aerospike/utils/pool.rb +1 -1
  49. data/lib/aerospike/value/value.rb +12 -13
  50. data/lib/aerospike/version.rb +1 -1
  51. metadata +6 -2
@@ -18,11 +18,12 @@ module Aerospike
18
18
 
19
19
  private
20
20
 
21
- class NodeValidator
21
+ class NodeValidator # :nodoc:
22
22
 
23
23
  attr_reader :host, :aliases, :name, :use_new_info
24
24
 
25
- def initialize(host, timeout)
25
+ def initialize(cluster, host, timeout)
26
+ @cluster = cluster
26
27
  @use_new_info = true
27
28
  @host = host
28
29
 
@@ -47,8 +48,19 @@ module Aerospike
47
48
  def set_address(timeout)
48
49
  @aliases.each do |aliass|
49
50
  begin
50
- conn = Connection.new(aliass.name, aliass.port, 1)
51
- conn.timeout = timeout
51
+ conn = Connection.new(aliass.name, aliass.port, timeout)
52
+
53
+ # need to authenticate
54
+ if @cluster.user && @cluster.user != ''
55
+ begin
56
+ command = AdminCommand.new
57
+ command.authenticate(conn, @cluster.user, @cluster.password)
58
+ rescue => e
59
+ # Socket not authenticated. Do not put back into pool.
60
+ conn.close if conn
61
+ raise e
62
+ end
63
+ end
52
64
 
53
65
  info_map= Info.request(conn, 'node', 'build')
54
66
  if node_name = info_map['node']
@@ -18,7 +18,7 @@ module Aerospike
18
18
 
19
19
  private
20
20
 
21
- class Partition
21
+ class Partition # :nodoc:
22
22
  attr_reader :namespace, :partition_id
23
23
 
24
24
  def initialize(namespace, partition_id)
@@ -22,7 +22,7 @@ module Aerospike
22
22
 
23
23
  REPLICAS_NAME = 'replicas-master'
24
24
 
25
- class PartitionTokenizerNew
25
+ class PartitionTokenizerNew #:nodoc:
26
26
 
27
27
  def initialize(conn)
28
28
  # Use low-level info methods and parse byte array directly for maximum performance.
@@ -97,11 +97,13 @@ module Aerospike
97
97
 
98
98
  bit_map_length = @offset - beginning
99
99
  restore_buffer = Base64.strict_decode64(@buffer[beginning, bit_map_length])
100
- for i in 0...Aerospike::Node::PARTITIONS
100
+ i = 0
101
+ while i < Aerospike::Node::PARTITIONS
101
102
  if (restore_buffer[i>>3].ord & (0x80 >> (i & 7))) != 0
102
103
  # Logger.Info("Map: `" + namespace + "`," + strconv.Itoa(i) + "," + node.String)
103
104
  node_array.update{|v| v[i] = node; v}
104
105
  end
106
+ i = i.succ
105
107
  end
106
108
 
107
109
  @offset+=1
@@ -20,7 +20,7 @@ module Aerospike
20
20
 
21
21
  private
22
22
 
23
- class PartitionTokenizerOld
23
+ class PartitionTokenizerOld #:nodoc:
24
24
 
25
25
  def initialize(conn)
26
26
  # Use low-level info methods and parse byte array directly for maximum performance.
@@ -0,0 +1,353 @@
1
+ # encoding: utf-8
2
+ # Copyright 2014 Aerospike, Inc.
3
+ #
4
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
+ # license agreements.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
8
+ # use this file except in compliance with the License. You may obtain a copy of
9
+ # the License at http:#www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ module Aerospike
18
+
19
+ private
20
+ # Commands
21
+ AUTHENTICATE = 0
22
+ CREATE_USER = 1
23
+ DROP_USER = 2
24
+ SET_PASSWORD = 3
25
+ CHANGE_PASSWORD = 4
26
+ GRANT_ROLES = 5
27
+ REVOKE_ROLES = 6
28
+ REPLACE_ROLES = 7
29
+ #CREATE_ROLE = 8
30
+ QUERY_USERS = 9
31
+ #QUERY_ROLES = 10
32
+
33
+ # Field IDs
34
+ USER = 0
35
+ PASSWORD = 1
36
+ OLD_PASSWORD = 2
37
+ CREDENTIAL = 3
38
+ ROLES = 10
39
+ #PRIVILEGES = 11
40
+
41
+ # Misc
42
+ MSG_VERSION = 0
43
+ MSG_TYPE = 2
44
+
45
+ HEADER_SIZE = 24
46
+ HEADER_REMAINING = 16
47
+ RESULT_CODE = 9
48
+ QUERY_END = 50
49
+
50
+ class AdminCommand #:nodoc:
51
+
52
+ def initialize
53
+ @data_buffer = Buffer.get
54
+ @data_offset = 8
55
+ end
56
+
57
+ def authenticate(conn, user, password)
58
+ begin
59
+ set_authenticate(user, password)
60
+ conn.write(@data_buffer, @data_offset)
61
+ conn.read(@data_buffer, HEADER_SIZE)
62
+
63
+ result = @data_buffer.read(RESULT_CODE)
64
+ raise Exceptions::Aerospike.new(result, "Authentication failed") if result != 0
65
+ ensure
66
+ Buffer.put(@data_buffer)
67
+ end
68
+ end
69
+
70
+ def set_authenticate(user, password)
71
+ write_header(AUTHENTICATE, 2)
72
+ write_field_str(USER, user)
73
+ write_field_bytes(CREDENTIAL, password)
74
+ write_size
75
+
76
+ return @data_offset
77
+ end
78
+
79
+ def create_user(cluster, policy, user, password, roles)
80
+ write_header(CREATE_USER, 3)
81
+ write_field_str(USER, user)
82
+ write_field_bytes(PASSWORD, password)
83
+ write_roles(roles)
84
+ execute_command(cluster, policy)
85
+ end
86
+
87
+ def drop_user(cluster, policy, user)
88
+ write_header(DROP_USER, 1)
89
+ write_field_str(USER, user)
90
+ execute_command(cluster, policy)
91
+ end
92
+
93
+ def set_password(cluster, policy, user, password)
94
+ write_header(SET_PASSWORD, 2)
95
+ write_field_str(USER, user)
96
+ write_field_bytes(PASSWORD, password)
97
+ execute_command(cluster, policy)
98
+ end
99
+
100
+ def change_password(cluster, policy, user, password)
101
+ write_header(CHANGE_PASSWORD, 3)
102
+ write_field_str(USER, user)
103
+ write_field_bytes(OLD_PASSWORD, cluster.password)
104
+ write_field_bytes(PASSWORD, password)
105
+ execute_command(cluster, policy)
106
+ end
107
+
108
+ def grant_roles(cluster, policy, user, roles)
109
+ write_header(GRANT_ROLES, 2)
110
+ write_field_str(USER, user)
111
+ write_roles(roles)
112
+ execute_command(cluster, policy)
113
+ end
114
+
115
+ def revoke_roles(cluster, policy, user, roles)
116
+ write_header(REVOKE_ROLES, 2)
117
+ write_field_str(USER, user)
118
+ write_roles(roles)
119
+ execute_command(cluster, policy)
120
+ end
121
+
122
+ def replace_roles(cluster, policy, user, roles)
123
+ write_header(REPLACE_ROLES, 2)
124
+ write_field_str(USER, user)
125
+ write_roles(roles)
126
+ execute_command(cluster, policy)
127
+ end
128
+
129
+ def query_user(cluster, policy, user)
130
+ # TODO: Remove the workaround in the future
131
+ sleep(0.010)
132
+
133
+ list = []
134
+ begin
135
+ write_header(QUERY_USERS, 1)
136
+ write_field_str(USER, user)
137
+ list = read_users(cluster, policy)
138
+ return (list.is_a?(Array) && list.length > 0 ? list.first : nil)
139
+ ensure
140
+ Buffer.put(@data_buffer)
141
+ end
142
+ end
143
+
144
+ def query_users(cluster, policy)
145
+ # TODO: Remove the workaround in the future
146
+ sleep(0.010)
147
+ begin
148
+ write_header(QUERY_USERS, 0)
149
+ return read_users(cluster, policy)
150
+ ensure
151
+ Buffer.put(@data_buffer)
152
+ end
153
+ end
154
+
155
+ def write_roles(roles)
156
+ offset = @data_offset + FIELD_HEADER_SIZE
157
+ @data_buffer.write_byte(roles.length.ord, offset)
158
+ offset += 1
159
+
160
+ roles.each do |role|
161
+ len = @data_buffer.write_binary(role, offset+1)
162
+ @data_buffer.write_byte(len, offset)
163
+ offset += len + 1
164
+ end
165
+
166
+ size = offset - @data_offset - FIELD_HEADER_SIZE
167
+ write_field_header(ROLES, size)
168
+ @data_offset = offset
169
+ end
170
+
171
+ def write_size
172
+ # Write total size of message which is the current offset.
173
+ size = Integer(@data_offset-8) | Integer(MSG_VERSION << 56) | Integer(MSG_TYPE << 48)
174
+ @data_buffer.write_int64(size, 0)
175
+ end
176
+
177
+ def write_header(command, field_count)
178
+ # Authenticate header is almost all zeros
179
+ i = @data_offset
180
+ while i < @data_offset+16
181
+ @data_buffer.write_byte(0, i)
182
+ i = i.succ
183
+ end
184
+ @data_buffer.write_byte(command, @data_offset+2)
185
+ @data_buffer.write_byte(field_count, @data_offset+3)
186
+ @data_offset += 16
187
+ end
188
+
189
+ def write_field_str(id, str)
190
+ len = @data_buffer.write_binary(str, @data_offset+FIELD_HEADER_SIZE)
191
+ write_field_header(id, len)
192
+ @data_offset += len
193
+ end
194
+
195
+ def write_field_bytes(id, bytes)
196
+ @data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
197
+ write_field_header(id, bytes.bytesize)
198
+ @data_offset += bytes.bytesize
199
+ end
200
+
201
+ def write_field_header(id, size)
202
+ @data_buffer.write_int32(size+1, @data_offset)
203
+ @data_offset += 4
204
+ @data_buffer.write_byte(id, @data_offset)
205
+ @data_offset += 1
206
+ end
207
+
208
+ def execute_command(cluster, policy)
209
+ # TODO: Remove the workaround in the future
210
+ sleep(0.010)
211
+
212
+ write_size
213
+ node = cluster.random_node
214
+
215
+ timeout = 1
216
+ timeout = policy.timeout if policy && policy.timeout > 0
217
+
218
+ conn = node.get_connection(timeout)
219
+
220
+ begin
221
+ conn.write(@data_buffer, @data_offset)
222
+ conn.read(@data_buffer, HEADER_SIZE)
223
+ node.put_connection(conn)
224
+ rescue => e
225
+ conn.close if conn
226
+ raise e
227
+ end
228
+
229
+ result = @data_buffer.read(RESULT_CODE)
230
+ raise Exceptions::Aerospike.new(result) if result != 0
231
+
232
+ Buffer.put(@data_buffer)
233
+ end
234
+
235
+ def read_users(cluster, policy)
236
+ write_size
237
+ node = cluster.random_node
238
+
239
+ timeout = 1
240
+ timeout = policy.timeout if policy != nil && policy.timeout > 0
241
+
242
+ status = -1
243
+ list = []
244
+ begin
245
+ conn = node.get_connection(timeout)
246
+ conn.write(@data_buffer, @data_offset)
247
+ status, list = read_user_blocks(conn)
248
+ node.put_connection(conn)
249
+ rescue => e
250
+ conn.close if conn
251
+ raise e
252
+ end
253
+
254
+ raise Exceptions::Aerospike.new(result) if status > 0
255
+
256
+ return list
257
+ end
258
+
259
+ def read_user_blocks(conn)
260
+ rlist = []
261
+ status = 0
262
+ begin
263
+ while status == 0
264
+ conn.read(@data_buffer, 8)
265
+ size = @data_buffer.read_int64(0)
266
+ receive_size = (size & 0xFFFFFFFFFFFF)
267
+
268
+ if receive_size > 0
269
+ @data_buffer.resize(receive_size) if receive_size > @data_buffer.size
270
+
271
+ conn.read(@data_buffer, receive_size)
272
+ status, list = parse_users(receive_size)
273
+ rlist.concat(list.to_a)
274
+ else
275
+ break
276
+ end
277
+ end
278
+ return status, rlist
279
+ rescue => e
280
+ return -1, []
281
+ end
282
+ end
283
+
284
+ def parse_users(receive_size)
285
+ @data_offset = 0
286
+ list = []
287
+
288
+ while @data_offset < receive_size
289
+ result_code = @data_buffer.read(@data_offset+1)
290
+
291
+ if result_code != 0
292
+ return (result_code == QUERY_END ? -1 : result_code)
293
+ end
294
+
295
+ userRoles = UserRoles.new
296
+ field_count = @data_buffer.read(@data_offset+3)
297
+ @data_offset += HEADER_REMAINING
298
+
299
+ i = 0
300
+ while i < field_count
301
+ len = @data_buffer.read_int32(@data_offset)
302
+ @data_offset += 4
303
+ id = @data_buffer.read(@data_offset)
304
+ @data_offset += 1
305
+ len -= 1
306
+
307
+ case id
308
+ when USER
309
+ userRoles.user = @data_buffer.read(@data_offset, len)
310
+ @data_offset += len
311
+ when ROLES
312
+ parse_roles(userRoles)
313
+ else
314
+ @data_offset += len
315
+ end
316
+
317
+ i = i.succ
318
+ end
319
+
320
+ next if userRoles.user == "" && userRoles.roles == nil
321
+
322
+ userRoles.roles = [] if userRoles.roles == nil
323
+ list << userRoles
324
+ end
325
+
326
+ return 0, list
327
+ end
328
+
329
+ def parse_roles(userRoles)
330
+ size = @data_buffer.read(@data_offset)
331
+ @data_offset += 1
332
+ userRoles.roles = []
333
+
334
+ i = 0
335
+ while i < size
336
+ len = @data_buffer.read(@data_offset)
337
+ @data_offset += 1
338
+ role = @data_buffer.read(@data_offset, len)
339
+ @data_offset += len
340
+ userRoles.roles << role
341
+
342
+ i = i.succ
343
+ end
344
+ end
345
+
346
+ SALT = '$2a$10$7EqJtq98hPqEX7fNZaFWoO'
347
+ def self.hash_password(password)
348
+ # Hashing the password with the cost of 10, with a static salt
349
+ return BCrypt::Engine.hash_secret(password, SALT, :cost => 10)
350
+ end
351
+ end
352
+ end
353
+
@@ -24,7 +24,7 @@ module Aerospike
24
24
 
25
25
  private
26
26
 
27
- class BatchCommand < Command
27
+ class BatchCommand < Command #:nodoc:
28
28
 
29
29
  def initialize(node)
30
30
  super(node)
@@ -61,7 +61,8 @@ module Aerospike
61
61
  set_name = nil
62
62
  user_key = nil
63
63
 
64
- for i in 0...field_count
64
+ i = 0
65
+ while i < field_count
65
66
  read_bytes(4)
66
67
 
67
68
  fieldlen = @data_buffer.read_int32(0)
@@ -80,6 +81,8 @@ module Aerospike
80
81
  when Aerospike::FieldType::KEY
81
82
  user_key = Aerospike::bytes_to_key_value(@data_buffer.read(1).ord, @data_buffer, 2, size-1)
82
83
  end
84
+
85
+ i = i.succ
83
86
  end
84
87
 
85
88
  Aerospike::Key.new(namespace, set_name, user_key, digest)
@@ -88,17 +91,15 @@ module Aerospike
88
91
  # Parses the given byte buffer and populate the result object.
89
92
  # Returns the number of bytes that were parsed from the given buffer.
90
93
  def parse_record(key, op_count, generation, expiration)
91
- bins = nil
92
- duplicates = nil
93
-
94
- for i in 0...op_count
94
+ bins = op_count > 0 ? {} : nil
95
+ i = 0
96
+ while i < op_count
95
97
  raise Aerospike::Exceptions::QueryTerminated.new unless valid?
96
98
 
97
99
  read_bytes(8)
98
100
 
99
101
  op_size = @data_buffer.read_int32(0).ord
100
102
  particle_type = @data_buffer.read(5).ord
101
- version = @data_buffer.read(6).ord
102
103
  name_size = @data_buffer.read(7).ord
103
104
 
104
105
  read_bytes(name_size)
@@ -114,44 +115,13 @@ module Aerospike
114
115
  # if !@bin_names || @bin_names.any?{|bn| bn == name}
115
116
  # if !@bin_names || (@bin_names == []) || @bin_names.any?{|bn| bn == name}
116
117
  if !@bin_names || (@bin_names.empty?) || @bin_names.any?{|bn| bn == name}
117
-
118
- vmap = nil
119
-
120
- if version > 0 || duplicates
121
- unless duplicates
122
- duplicates = []
123
- duplicates << bins
124
- bins = nil
125
-
126
- for j in 0...version
127
- duplicates << nil
128
- end
129
- else
130
- for j in duplicates.length..version
131
- duplicates << nil
132
- end
133
- end
134
-
135
- vmap = duplicates[version]
136
- unless vmap
137
- vmap = {}
138
- duplicates[version] = vmap
139
- end
140
- else
141
- unless bins
142
- bins = {}
143
- end
144
- vmap = bins
145
- end
146
- vmap[name] = value
118
+ bins[name] = value
147
119
  end
148
- end
149
120
 
150
- # Remove nil duplicates just in case there were holes in the version number space.
151
- # TODO: this seems to be a bad idea; O(n) algorithm after another O(n) algorithm
152
- duplicates.compact! if duplicates
121
+ i = i.succ
122
+ end
153
123
 
154
- Record.new(@node, key, bins, duplicates, generation, expiration)
124
+ Record.new(@node, key, bins, generation, expiration)
155
125
  end
156
126
 
157
127
  def read_bytes(length)