aerospike 2.19.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+