aerospike 2.19.0 → 2.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +354 -244
  3. data/lib/aerospike/atomic/atomic.rb +1 -1
  4. data/lib/aerospike/cdt/context.rb +137 -70
  5. data/lib/aerospike/cdt/list_return_type.rb +4 -0
  6. data/lib/aerospike/cdt/map_operation.rb +6 -6
  7. data/lib/aerospike/cdt/map_policy.rb +16 -2
  8. data/lib/aerospike/cdt/map_return_type.rb +13 -1
  9. data/lib/aerospike/client.rb +137 -115
  10. data/lib/aerospike/cluster/create_connection.rb +1 -1
  11. data/lib/aerospike/cluster.rb +41 -4
  12. data/lib/aerospike/command/admin_command.rb +368 -52
  13. data/lib/aerospike/command/batch_index_command.rb +4 -8
  14. data/lib/aerospike/command/batch_index_exists_command.rb +1 -1
  15. data/lib/aerospike/command/batch_index_node.rb +1 -1
  16. data/lib/aerospike/command/batch_item.rb +1 -1
  17. data/lib/aerospike/command/command.rb +180 -123
  18. data/lib/aerospike/command/field_type.rb +25 -24
  19. data/lib/aerospike/command/login_command.rb +164 -0
  20. data/lib/aerospike/command/multi_command.rb +25 -2
  21. data/lib/aerospike/command/operate_args.rb +99 -0
  22. data/lib/aerospike/command/operate_command.rb +6 -11
  23. data/lib/aerospike/command/read_command.rb +2 -2
  24. data/lib/aerospike/connection/authenticate.rb +36 -3
  25. data/lib/aerospike/exp/exp.rb +1329 -0
  26. data/lib/aerospike/exp/exp_bit.rb +388 -0
  27. data/lib/aerospike/exp/exp_hll.rb +169 -0
  28. data/lib/aerospike/exp/exp_list.rb +403 -0
  29. data/lib/aerospike/exp/exp_map.rb +493 -0
  30. data/lib/aerospike/exp/operation.rb +56 -0
  31. data/lib/aerospike/features.rb +22 -9
  32. data/lib/aerospike/host/parse.rb +2 -2
  33. data/lib/aerospike/key.rb +10 -1
  34. data/lib/aerospike/node/refresh/info.rb +1 -1
  35. data/lib/aerospike/node/verify/name.rb +1 -1
  36. data/lib/aerospike/node/verify/partition_generation.rb +1 -1
  37. data/lib/aerospike/node/verify/peers_generation.rb +1 -1
  38. data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
  39. data/lib/aerospike/node_validator.rb +6 -1
  40. data/lib/aerospike/operation.rb +20 -22
  41. data/lib/aerospike/policy/auth_mode.rb +36 -0
  42. data/lib/aerospike/policy/client_policy.rb +4 -1
  43. data/lib/aerospike/policy/policy.rb +29 -13
  44. data/lib/aerospike/policy/query_policy.rb +35 -2
  45. data/lib/aerospike/policy/scan_policy.rb +19 -2
  46. data/lib/aerospike/privilege.rb +133 -0
  47. data/lib/aerospike/query/filter.rb +44 -32
  48. data/lib/aerospike/query/node_partitions.rb +39 -0
  49. data/lib/aerospike/query/partition_filter.rb +66 -0
  50. data/lib/aerospike/{command/roles.rb → query/partition_status.rb} +16 -19
  51. data/lib/aerospike/query/partition_tracker.rb +347 -0
  52. data/lib/aerospike/query/query_command.rb +20 -10
  53. data/lib/aerospike/query/query_executor.rb +71 -0
  54. data/lib/aerospike/query/query_partition_command.rb +267 -0
  55. data/lib/aerospike/query/recordset.rb +9 -9
  56. data/lib/aerospike/query/scan_command.rb +3 -2
  57. data/lib/aerospike/query/scan_executor.rb +71 -0
  58. data/lib/aerospike/query/scan_partition_command.rb +49 -0
  59. data/lib/aerospike/query/statement.rb +8 -1
  60. data/lib/aerospike/query/stream_command.rb +17 -0
  61. data/lib/aerospike/result_code.rb +83 -8
  62. data/lib/aerospike/role.rb +55 -0
  63. data/lib/aerospike/task/execute_task.rb +19 -16
  64. data/lib/aerospike/task/index_task.rb +1 -1
  65. data/lib/aerospike/user_role.rb +26 -1
  66. data/lib/aerospike/utils/buffer.rb +93 -29
  67. data/lib/aerospike/utils/packer.rb +7 -6
  68. data/lib/aerospike/utils/pool.rb +1 -1
  69. data/lib/aerospike/value/particle_type.rb +1 -12
  70. data/lib/aerospike/value/value.rb +35 -60
  71. data/lib/aerospike/version.rb +1 -1
  72. data/lib/aerospike.rb +156 -136
  73. metadata +24 -6
@@ -21,30 +21,31 @@ module Aerospike
21
21
 
22
22
  module FieldType
23
23
 
24
- NAMESPACE = 0
25
- TABLE = 1
26
- KEY = 2
27
- #BIN = 3
28
- DIGEST_RIPE = 4
29
- #GU_TID = 5
30
- DIGEST_RIPE_ARRAY = 6
31
- TRAN_ID = 7 # user supplied transaction id, which is simply passed back
32
- SCAN_OPTIONS = 8
33
- SCAN_TIMEOUT = 9
34
- RECORDS_PER_SECOND = 10
35
- INDEX_NAME = 21
36
- INDEX_RANGE = 22
37
- INDEX_FILTER = 23
38
- INDEX_LIMIT = 24
39
- INDEX_ORDER_BY = 25
40
- INDEX_TYPE = 26
41
- UDF_PACKAGE_NAME = 30
42
- UDF_FUNCTION = 31
43
- UDF_ARGLIST = 32
44
- UDF_OP = 33
45
- QUERY_BINLIST = 40
46
- BATCH_INDEX = 41
47
- PREDEXP = 43
24
+ NAMESPACE = 0
25
+ TABLE = 1
26
+ KEY = 2
27
+ DIGEST_RIPE = 4
28
+ DIGEST_RIPE_ARRAY = 6
29
+ TRAN_ID = 7 # user supplied transaction id, which is simply passed back
30
+ SCAN_OPTIONS = 8
31
+ SOCKET_TIMEOUT = 9
32
+ RECORDS_PER_SECOND = 10
33
+ PID_ARRAY = 11
34
+ DIGEST_ARRAY = 12
35
+ MAX_RECORDS = 13
36
+ BVAL_ARRAY = 15
37
+ INDEX_NAME = 21
38
+ INDEX_RANGE = 22
39
+ INDEX_CONTEXT = 23
40
+ INDEX_TYPE = 26
41
+ UDF_PACKAGE_NAME = 30
42
+ UDF_FUNCTION = 31
43
+ UDF_ARGLIST = 32
44
+ UDF_OP = 33
45
+ QUERY_BINLIST = 40
46
+ BATCH_INDEX = 41
47
+ BATCH_INDEX_WITH_SET = 42
48
+ FILTER_EXP = 43
48
49
 
49
50
  end # module
50
51
 
@@ -0,0 +1,164 @@
1
+ # encoding: utf-8
2
+ # Copyright 2014-2020 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
+ require 'aerospike/command/admin_command'
18
+
19
+ module Aerospike
20
+
21
+ private
22
+
23
+ attr_reader :session_token, :session_expiration
24
+
25
+ class LoginCommand < AdminCommand #:nodoc:
26
+
27
+ def login(conn, policy)
28
+ hashed_pass = LoginCommand.hash_password(policy.password)
29
+ authenticate(conn, policy, hashed_pass)
30
+ end
31
+
32
+ def authenticate(conn, user, hashed_pass)
33
+ write_header(LOGIN, 2)
34
+ write_field_str(USER, policy.user)
35
+ write_field_bytes(CREDENTIAL, hashed_pass)
36
+
37
+ parse_tokens(conn)
38
+ end
39
+
40
+ def authenticate_new(conn, cluster)
41
+ @data_offset = 8
42
+ policy = cluster.client_policy
43
+ case policy.auth_mode
44
+ when Aerospike::AuthMode::EXTERNAL
45
+ write_header(LOGIN, 3)
46
+ write_field_str(USER, policy.user)
47
+ write_field_bytes(CREDENTIAL, cluster.password)
48
+ write_field_str(CLEAR_PASSWORD, policy.password)
49
+ when Aerospike::AuthMode::INTERNAL
50
+ write_header(LOGIN, 2)
51
+ write_field_str(USER, policy.user)
52
+ write_field_bytes(CREDENTIAL, cluster.password)
53
+ when Aerospike::AuthMode::PKI
54
+ write_header(LOGIN, 0)
55
+ else
56
+ raise Exceptions::Aerospike.new(Aerospike::ResultCode::COMMAND_REJECTED, "Invalid client_policy#auth_mode.")
57
+ end
58
+
59
+ parse_tokens(conn)
60
+ cluster.session_token = @session_token
61
+ cluster.session_expiration = @session_expiration
62
+ end
63
+
64
+ def parse_tokens(conn)
65
+ begin
66
+ write_size
67
+ conn.write(@data_buffer, @data_offset)
68
+ conn.read(@data_buffer, HEADER_SIZE)
69
+
70
+ result = @data_buffer.read(RESULT_CODE)
71
+
72
+ if result != 0
73
+ return if result == Aerospike::ResultCode::SECURITY_NOT_ENABLED
74
+ raise Exceptions::Aerospike.new(result, "Authentication failed")
75
+ end
76
+
77
+ # read the rest of the buffer
78
+ size = @data_buffer.read_int64(0)
79
+ receive_size = (size & 0xFFFFFFFFFFFF) - HEADER_REMAINING
80
+ field_count = @data_buffer.read(11) & 0xFF
81
+
82
+ if receive_size <= 0 || receive_size > @data_buffer.size || field_count <= 0
83
+ raise Exceptions::Aerospike.new(result, "Node failed to retrieve session token")
84
+ end
85
+
86
+ if @data_buffer.size < receive_size
87
+ @data_buffer.resize(receive_size)
88
+ end
89
+
90
+ conn.read(@data_buffer, receive_size)
91
+
92
+ @data_offset = 0
93
+ for i in 0...field_count
94
+ mlen = @data_buffer.read_int32(@data_offset)
95
+ @data_offset += 4
96
+ id = @data_buffer.read(@data_offset)
97
+ @data_offset += 1
98
+ mlen -= 1
99
+
100
+ case id
101
+ when SESSION_TOKEN
102
+ # copy the contents of the buffer into a new byte slice
103
+ @session_token = @data_buffer.read(@data_offset, mlen)
104
+
105
+ when SESSION_TTL
106
+ # Subtract 60 seconds from TTL so client session expires before server session.
107
+ seconds = @data_buffer.read_int32(@data_offset) - 60
108
+
109
+ if seconds > 0
110
+ @session_expiration = Time.now + (seconds/86400)
111
+ else
112
+ Aerospike.logger.warn("Invalid session TTL: #{seconds}")
113
+ raise Exceptions::Aerospike.new(result, "Node failed to retrieve session token")
114
+ end
115
+ end
116
+
117
+ @data_offset += mlen
118
+ end
119
+
120
+ if !@session_token
121
+ raise Exceptions::Aerospike.new(result, "Node failed to retrieve session token")
122
+ end
123
+ ensure
124
+ Buffer.put(@data_buffer)
125
+ end
126
+ end
127
+
128
+ def authenticate_via_token(conn, cluster)
129
+ @data_offset = 8
130
+ policy = cluster.client_policy
131
+ if policy.auth_mode == Aerospike::AuthMode::PKI
132
+ write_header(AUTHENTICATE, 1)
133
+ else
134
+ write_header(AUTHENTICATE, 2)
135
+ write_field_str(USER, policy.user)
136
+ end
137
+
138
+ write_field_bytes(SESSION_TOKEN, cluster.session_token) if cluster.session_token
139
+ write_size
140
+
141
+ conn.write(@data_buffer, @data_offset)
142
+ conn.read(@data_buffer, HEADER_SIZE)
143
+
144
+ result = @data_buffer.read(RESULT_CODE)
145
+ size = @data_buffer.read_int64(0)
146
+ receive_size = (size & 0xFFFFFFFFFFFF) - HEADER_REMAINING
147
+ conn.read(@data_buffer, receive_size)
148
+
149
+ if result != 0
150
+ return if result == Aerospike::ResultCode::SECURITY_NOT_ENABLED
151
+ raise Exceptions::Aerospike.new(result, "Authentication failed")
152
+ end
153
+
154
+ nil
155
+ end
156
+
157
+ SALT = '$2a$10$7EqJtq98hPqEX7fNZaFWoO'
158
+ def self.hash_password(password)
159
+ # Hashing the password with the cost of 10, with a static salt
160
+ return BCrypt::Engine.hash_secret(password, SALT, :cost => 10)
161
+ end
162
+ end
163
+ end
164
+
@@ -32,6 +32,9 @@ module Aerospike
32
32
  @compressed_data_buffer = nil
33
33
  @compressed_data_offset = nil
34
34
 
35
+ @node_partitions = nil
36
+ @tracker = nil
37
+
35
38
  self
36
39
  end
37
40
 
@@ -97,8 +100,9 @@ module Aerospike
97
100
 
98
101
  # The only valid server return codes are "ok", "not found" and "filtered out".
99
102
  # If other return codes are received, then abort the batch.
100
- if result_code != 0
103
+ if result_code != 0
101
104
  if result_code == Aerospike::ResultCode::KEY_NOT_FOUND_ERROR || result_code == Aerospike::ResultCode::FILTERED_OUT
105
+ # NOOP
102
106
  else
103
107
  raise Aerospike::Exceptions::Aerospike.new(result_code)
104
108
  end
@@ -142,12 +146,31 @@ module Aerospike
142
146
  set_name = @data_buffer.read(1, size).force_encoding('utf-8')
143
147
  when Aerospike::FieldType::KEY
144
148
  user_key = Aerospike::bytes_to_key_value(@data_buffer.read(1).ord, @data_buffer, 2, size-1)
149
+ when Aerospike::FieldType::BVAL_ARRAY
150
+ bval = @data_buffer.read_uint64_little_endian(1)
145
151
  end
146
152
 
147
153
  i = i.succ
148
154
  end
149
155
 
150
- Aerospike::Key.new(namespace, set_name, user_key, digest)
156
+ Aerospike::Key.new(namespace, set_name, user_key, digest, bval: bval)
157
+ end
158
+
159
+ def skip_key(field_count)
160
+ # in Stream queries, there are no keys
161
+ return unless field_count > 0
162
+
163
+ i = 0
164
+ while i < field_count
165
+ read_bytes(4)
166
+
167
+ fieldlen = @data_buffer.read_int32(0)
168
+ read_bytes(fieldlen)
169
+
170
+ i = i.succ
171
+ end
172
+
173
+ nil
151
174
  end
152
175
 
153
176
  # Parses the given byte buffer and populate the result object.
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+ # Copyright 2016-2020 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
+ require "aerospike/operation"
18
+
19
+ module Aerospike
20
+ private
21
+
22
+ class OperateArgs
23
+ attr_reader :write_policy, :operations, :partition
24
+ attr_reader :size, :read_attr, :write_attr, :has_write
25
+
26
+ RESPOND_ALL_OPS_READ_CMDS = [Operation::BIT_READ, Operation::EXP_READ, Operation::HLL_READ, Operation::CDT_READ]
27
+ READ_CMDS = [Operation::BIT_READ, Operation::EXP_READ, Operation::HLL_READ, Operation::CDT_READ, Operation::CDT_READ, Operation::READ]
28
+ MODIFY_CMDS = [Operation::BIT_MODIFY, Operation::EXP_MODIFY, Operation::HLL_MODIFY, Operation::CDT_MODIFY]
29
+
30
+ def initialize(cluster, policy, write_default, read_default, key, operations)
31
+ @operations = operations
32
+
33
+ data_offset = 0
34
+ rattr = 0
35
+ wattr = 0
36
+ write = false
37
+ read_bin = false
38
+ read_header = false
39
+ respond_all_ops = false
40
+
41
+ @operations.each do |operation|
42
+ if READ_CMDS.include?(operation.op_type)
43
+ if RESPOND_ALL_OPS_READ_CMDS.include?(operation.op_type)
44
+ # Map @operations require respond_all_ops to be true.
45
+ respond_all_ops = true
46
+ end
47
+
48
+ rattr |= Aerospike::INFO1_READ
49
+
50
+ # Read all bins if no bin is specified.
51
+ rattr |= Aerospike::INFO1_GET_ALL if operation.bin_name.nil?
52
+ read_bin = true
53
+ elsif operation.op_type == Operation::READ_HEADER
54
+ rattr |= Aerospike::INFO1_READ
55
+ read_header = true
56
+ elsif MODIFY_CMDS.include?(operation.op_type)
57
+ # Map @operations require respond_all_ops to be true.
58
+ respond_all_ops = true
59
+
60
+ wattr = Aerospike::INFO2_WRITE
61
+ write = true
62
+ else
63
+ wattr = Aerospike::INFO2_WRITE
64
+ write = true
65
+ end
66
+ data_offset += operation.bin_name.bytesize + Aerospike::OPERATION_HEADER_SIZE unless operation.bin_name.nil?
67
+ data_offset += operation.bin_value.estimate_size
68
+ end
69
+
70
+ @size = data_offset
71
+ @has_write = write
72
+
73
+ if read_header && !read_bin
74
+ rattr |= Aerospike::INFO1_NOBINDATA
75
+ end
76
+ @read_attr = rattr
77
+
78
+ if policy.nil?
79
+ @write_policy = write ? write_default : read_default
80
+ else
81
+ @write_policy = policy
82
+ end
83
+
84
+ # When GET_ALL is specified, RESPOND_ALL_OPS must be disabled.
85
+ if (respond_all_ops && policy.record_bin_multiplicity) && (rattr & Aerospike::INFO1_GET_ALL) == 0
86
+ wattr |= Aerospike::INFO2_RESPOND_ALL_OPS
87
+ end
88
+ @write_attr = wattr
89
+
90
+ if write
91
+ # @partition = Partition.write(cluster, @write_policy, key)
92
+ @partition = Partition.new_by_key(key)
93
+ else
94
+ # @partition = Partition.read(cluster, @write_policy, key)
95
+ @partition = Partition.new_by_key(key)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -14,33 +14,28 @@
14
14
  # License for the specific language governing permissions and limitations under
15
15
  # the License.
16
16
 
17
- require 'aerospike/command/read_command'
17
+ require "aerospike/command/read_command"
18
18
 
19
19
  module Aerospike
20
-
21
20
  private
22
21
 
23
22
  class OperateCommand < ReadCommand #:nodoc:
23
+ def initialize(cluster, key, args)
24
+ super(cluster, args.write_policy, key, nil)
24
25
 
25
- def initialize(cluster, policy, key, operations)
26
- super(cluster, policy, key, nil)
27
-
28
- @operations = operations
26
+ @args = args
29
27
  end
30
28
 
31
29
  def get_node
32
30
  @cluster.master_node(@partition)
33
31
  end
34
32
 
35
-
36
33
  def write_bins
37
- @operations.select{|op| op.op_type == Aerospike::Operation::WRITE}.map(&:bin).compact
34
+ @operations.select { |op| op.op_type == Aerospike::Operation::WRITE }.map(&:bin).compact
38
35
  end
39
36
 
40
37
  def write_buffer
41
- set_operate(@policy, @key, @operations)
38
+ set_operate(@args.write_policy, @key, @args)
42
39
  end
43
-
44
40
  end # class
45
-
46
41
  end # module
@@ -98,7 +98,7 @@ module Aerospike
98
98
  receive_size = (sz & 0xFFFFFFFFFFFF) - header_length
99
99
 
100
100
  # Read remaining message bytes.
101
- if compressed_sz
101
+ if compressed_sz
102
102
  @data_buffer.eat!(MSG_TOTAL_HEADER_SIZE)
103
103
  elsif receive_size > 0
104
104
  size_buffer_sz(receive_size)
@@ -117,7 +117,7 @@ module Aerospike
117
117
  @record = Record.new(@node, @key, nil, generation, expiration)
118
118
  return
119
119
  end
120
-
120
+
121
121
  @record = parse_record(op_count, field_count, generation, expiration)
122
122
  return
123
123
  end
@@ -21,9 +21,41 @@ module Aerospike
21
21
  module Connection # :nodoc:
22
22
  module Authenticate
23
23
  class << self
24
- def call(conn, user, password)
25
- command = AdminCommand.new
26
- command.authenticate(conn, user, password)
24
+ def call(conn, user, hashed_pass)
25
+ command = LoginCommand.new
26
+ command.authenticate(conn, user, hashed_pass)
27
+ true
28
+ rescue ::Aerospike::Exceptions::Aerospike
29
+ conn.close if conn
30
+ raise ::Aerospike::Exceptions::InvalidCredentials
31
+ end
32
+ end
33
+ end
34
+ module AuthenticateNew
35
+ class << self
36
+ INVALID_SESSION_ERR = [ResultCode::INVALID_CREDENTIAL,
37
+ ResultCode::EXPIRED_SESSION]
38
+
39
+ def call(conn, cluster)
40
+ command = LoginCommand.new
41
+ if cluster.session_valid?
42
+ begin
43
+ command.authenticate_via_token(conn, cluster)
44
+ rescue => ae
45
+ # always reset session info on errors to be on the safe side
46
+ cluster.reset_session_info
47
+ if ae.is_a?(Exceptions::Aerospike)
48
+ if INVALID_SESSION_ERR.include?(ae.result_code)
49
+ command.authenticate_new(conn, cluster)
50
+ return true
51
+ end
52
+ end
53
+ raise ae
54
+ end
55
+ else
56
+ command.authenticate_new(conn, cluster)
57
+ end
58
+
27
59
  true
28
60
  rescue ::Aerospike::Exceptions::Aerospike
29
61
  conn.close if conn
@@ -33,3 +65,4 @@ module Aerospike
33
65
  end
34
66
  end
35
67
  end
68
+