aerospike 4.0.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/lib/aerospike/aerospike_exception.rb +22 -22
  4. data/lib/aerospike/batch_attr.rb +2 -2
  5. data/lib/aerospike/batch_read.rb +2 -2
  6. data/lib/aerospike/client.rb +1 -1
  7. data/lib/aerospike/cluster/partition_parser.rb +5 -3
  8. data/lib/aerospike/cluster/rack_parser.rb +6 -4
  9. data/lib/aerospike/cluster.rb +15 -15
  10. data/lib/aerospike/command/batch_index_command.rb +1 -1
  11. data/lib/aerospike/command/batch_index_exists_command.rb +1 -1
  12. data/lib/aerospike/command/batch_operate_command.rb +1 -1
  13. data/lib/aerospike/command/command.rb +33 -15
  14. data/lib/aerospike/command/delete_command.rb +2 -2
  15. data/lib/aerospike/command/exists_command.rb +2 -2
  16. data/lib/aerospike/command/multi_command.rb +11 -11
  17. data/lib/aerospike/command/read_command.rb +7 -7
  18. data/lib/aerospike/command/read_header_command.rb +3 -3
  19. data/lib/aerospike/command/touch_command.rb +3 -3
  20. data/lib/aerospike/command/write_command.rb +2 -3
  21. data/lib/aerospike/exp/exp_map.rb +5 -5
  22. data/lib/aerospike/node/verify/cluster_name.rb +1 -1
  23. data/lib/aerospike/node/verify/name.rb +4 -4
  24. data/lib/aerospike/node/verify/partition_generation.rb +1 -1
  25. data/lib/aerospike/node/verify/peers_generation.rb +1 -1
  26. data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
  27. data/lib/aerospike/policy/batch_read_policy.rb +18 -1
  28. data/lib/aerospike/policy/policy.rb +18 -1
  29. data/lib/aerospike/policy/query_duration.rb +48 -0
  30. data/lib/aerospike/policy/query_policy.rb +13 -8
  31. data/lib/aerospike/query/partition_tracker.rb +1 -1
  32. data/lib/aerospike/query/server_command.rb +1 -1
  33. data/lib/aerospike/query/stream_command.rb +10 -11
  34. data/lib/aerospike/result_code.rb +7 -1
  35. data/lib/aerospike/version.rb +1 -1
  36. data/lib/aerospike.rb +1 -0
  37. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbbb3eae452fb9c1bbe7a0a99e7505b4fa774396b9591edc80ae707abb9896e0
4
- data.tar.gz: cd2bf9f22247dbc8f49581507b17f6347edf0a6f46a0c307f87eae45c3bf775b
3
+ metadata.gz: 6807e226bf4278fb49b9b27a13ddca657120fff617e4a9e2fd0553fd9479f7f6
4
+ data.tar.gz: 07d35bca9861064b0528ef7f9f7d3fefd1930deb868ab6e45f8f1ddf57eded6c
5
5
  SHA512:
6
- metadata.gz: 88c12b118fcdf74f170a06ac3158b47f3e81076ee7c851d5ca06ea80b82d2b668c2df6588444e22c011dd43cbddce6586ecaaa30c895e54cefd47573e75b55ac
7
- data.tar.gz: ffc436ee1b5696765fdc8ab00f87ef0c6e33afea3ae3ddc2d63dcd01fed91e62f3ddb8b109acf9fae65bc5bcabd9c1f36af8c60b87e86fb25c8b1528ed5e926b
6
+ metadata.gz: f027d16fc67b814e135fc831b7d0b5effce98ea18255df72dc8d33fd63ed1a86c181566fd38211257390576af3306b027eb3213937b31b846b62b0cad69a17e6
7
+ data.tar.gz: e7125a75bef40d4d5b021408ba09fce58ade3c5b039a81e18241d72ca82936c71df826289131c9416630b2ad6022d85ba136069ea6514d1bac54ba12f461d13e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [4.2.0] 2024-12-18
6
+
7
+ - **Fixes**
8
+ - [CLIENT-3195] Fix ruby client does not return failed nodes on timeout.
9
+
10
+ ## [4.1.0] 2024-10-22
11
+
12
+ - **New Features**
13
+ - [CLIENT-2833] Support `Policy#ReadTouchTtlPercent`.
14
+ - [CLIENT-2826] Support `QueryDuration` in `QueryPolicy#ExpectedDuration`.
15
+ - [CLIENT-3103] Support `XDR_KEY_BUSY`.
16
+
17
+ - **Fixes**
18
+ - [CLIENT-3144] Various fixes. PR #132 and #133 Thanks to [Igor Pstyga](https://github.com/opti)
19
+ - Fix `BatchRead` for multiple records with operations.
20
+ - Use correct namespace for the `MapReturnType`.
21
+ - `BatchRead` with operations would throw an exception.
22
+ - Fix a test with invalid map key in Server v7.1.
23
+
5
24
  ## [4.0.0] 2024-08-14
6
25
 
7
26
  - **New Features**
@@ -21,79 +21,79 @@ require 'aerospike/result_code'
21
21
  module Aerospike
22
22
  module Exceptions
23
23
  class Aerospike < StandardError
24
- attr_reader :result_code
24
+ attr_reader :result_code, :failed_nodes
25
25
 
26
- def initialize(result_code, message = nil)
26
+ def initialize(result_code, message = nil, failed_nodes = nil)
27
27
  @result_code = result_code
28
+ @failed_nodes = failed_nodes
28
29
  message ||= ResultCode.message(result_code)
29
30
  super(message)
30
31
  end
31
32
  end
32
33
 
33
34
  class Timeout < Aerospike
34
- attr_reader :timeout, :iterations, :failed_nodes, :failed_connections
35
+ attr_reader :timeout, :iterations, :failed_connections
35
36
 
36
37
  def initialize(timeout, iterations, failed_nodes=nil, failed_connections=nil)
37
38
  @timeout = timeout
38
39
  @iterations = iterations
39
- @failed_nodes = failed_nodes
40
40
  @failed_connections = failed_connections
41
41
 
42
- super(ResultCode::TIMEOUT)
42
+ super(ResultCode::TIMEOUT, nil, failed_nodes)
43
43
  end
44
44
  end
45
45
 
46
46
  class InvalidCredentials < Aerospike
47
- def initialize(msg = nil)
48
- super(ResultCode::NOT_AUTHENTICATED, msg)
47
+ def initialize(msg = nil, node=nil)
48
+ super(ResultCode::NOT_AUTHENTICATED, msg, [node])
49
49
  end
50
50
  end
51
51
 
52
52
  class Serialize < Aerospike
53
53
  def initialize(msg=nil)
54
- super(ResultCode::SERIALIZE_ERROR, msg)
54
+ super(ResultCode::SERIALIZE_ERROR, msg, [node])
55
55
  end
56
56
  end
57
57
 
58
58
  class Parse < Aerospike
59
- def initialize(msg=nil)
60
- super(ResultCode::PARSE_ERROR, msg)
59
+ def initialize(msg=nil, node=nil)
60
+ super(ResultCode::PARSE_ERROR, msg, [node])
61
61
  end
62
62
  end
63
63
 
64
64
  class Connection < Aerospike
65
- def initialize(msg=nil)
66
- super(ResultCode::SERVER_NOT_AVAILABLE, msg)
65
+ def initialize(msg=nil, node=nil)
66
+ super(ResultCode::SERVER_NOT_AVAILABLE, msg, [node])
67
67
  end
68
68
  end
69
69
 
70
70
  class InvalidNode < Aerospike
71
- def initialize(msg=nil)
72
- super(ResultCode::INVALID_NODE_ERROR, msg)
71
+ def initialize(msg=nil, node=nil)
72
+ super(ResultCode::INVALID_NODE_ERROR, msg, [node])
73
73
  end
74
74
  end
75
75
 
76
76
  class ScanTerminated < Aerospike
77
- def initialize(msg=nil)
78
- super(ResultCode::SCAN_TERMINATED, msg)
77
+ def initialize(msg=nil, node=nil)
78
+ super(ResultCode::SCAN_TERMINATED, msg, [node])
79
79
  end
80
80
  end
81
81
 
82
82
  class QueryTerminated < Aerospike
83
- def initialize(msg=nil)
84
- super(ResultCode::QUERY_TERMINATED, msg)
83
+ def initialize(msg=nil, node=nil)
84
+ super(ResultCode::QUERY_TERMINATED, msg, [node])
85
85
  end
86
86
  end
87
87
 
88
88
  class CommandRejected < Aerospike
89
- def initialize(msg=nil)
90
- super(ResultCode::COMMAND_REJECTED, msg)
89
+ def initialize(msg=nil, node=nil)
90
+ super(ResultCode::COMMAND_REJECTED, msg, [node])
91
91
  end
92
92
  end
93
93
 
94
94
  class InvalidNamespace < Aerospike
95
- def initialize(msg=nil)
96
- super(ResultCode::INVALID_NAMESPACE, msg)
95
+ def initialize(msg=nil, node=nil)
96
+ super(ResultCode::INVALID_NAMESPACE, msg, [node])
97
97
  end
98
98
  end
99
99
  end
@@ -75,7 +75,7 @@ module Aerospike
75
75
  @write_attr = 0
76
76
  @info_attr = 0
77
77
 
78
- @expiration = 0
78
+ @expiration = rp.read_touch_ttl_percent
79
79
  @generation = 0
80
80
  @has_write = false
81
81
  @send_key = false
@@ -88,7 +88,7 @@ module Aerospike
88
88
  @write_attr = 0
89
89
  @info_attr = 0
90
90
 
91
- @expiration = 0
91
+ @expiration = rp.read_touch_ttl_percent
92
92
  @generation = 0
93
93
  @has_write = false
94
94
  @send_key = false
@@ -68,7 +68,7 @@ module Aerospike
68
68
  # For internal use only.
69
69
  def ==(other) # :nodoc:
70
70
  other && other.instance_of?(self.class) &&
71
- @bin_names.sort == other.bin_names.sort && @ops.sort == other.ops.sort &&
71
+ @bin_names&.sort == other.bin_names&.sort && @ops == other.ops &&
72
72
  @policy == other.policy && @read_all_bins == other.read_all_bins
73
73
  end
74
74
 
@@ -88,7 +88,7 @@ module Aerospike
88
88
  raise AerospikeException.new(ResultCode::PARAMETER_ERROR, "Write operations not allowed in batch read")
89
89
  end
90
90
  size += op.bin_name.bytesize + Aerospike::OPERATION_HEADER_SIZE
91
- size += op.value.estimate_size
91
+ size += op.bin_value.estimate_size
92
92
  end
93
93
 
94
94
  size
@@ -239,7 +239,7 @@ module Aerospike
239
239
 
240
240
  response = send_info_command(policy, str_cmd, node).upcase
241
241
  return if response == "OK"
242
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_ERROR, "Truncate failed: #{response}")
242
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_ERROR, "Truncate failed: #{response}", [node])
243
243
  end
244
244
 
245
245
  #-------------------------------------------------------
@@ -42,7 +42,7 @@ module Aerospike
42
42
 
43
43
  info = info_map[REPLICAS_ALL]
44
44
  if !info || info.length == 0
45
- raise Aerospike::Exceptions::Connection.new("#{REPLICAS_ALL} response for node #{@node.name} is empty")
45
+ raise Aerospike::Exceptions::Connection.new("#{REPLICAS_ALL} response for node #{@node.name} is empty", @node)
46
46
  end
47
47
 
48
48
  @buffer = info
@@ -112,7 +112,8 @@ module Aerospike
112
112
  if namespace.length <= 0 || namespace.length >= 32
113
113
  response = get_truncated_response
114
114
  raise Aerospike::Exceptions::Parse.new(
115
- "Invalid partition namespace #{namespace}. Response=#{response}"
115
+ "Invalid partition namespace #{namespace}. Response=#{response}",
116
+ @node
116
117
  )
117
118
  end
118
119
 
@@ -133,7 +134,8 @@ module Aerospike
133
134
  if count < 0 || count > 4096
134
135
  response = get_truncated_response
135
136
  raise Aerospike::Exceptions::Parse.new(
136
- "Invalid partition count #{count}. Response=#{response}"
137
+ "Invalid partition count #{count}. Response=#{response}",
138
+ @node
137
139
  )
138
140
  end
139
141
 
@@ -43,7 +43,7 @@ module Aerospike
43
43
 
44
44
  info = info_map[RACK_IDS]
45
45
  if !info || info.length == 0
46
- raise Aerospike::Exceptions::Connection.new("#{RACK_IDS} response for node #{@node.name} is empty")
46
+ raise Aerospike::Exceptions::Connection.new("#{RACK_IDS} response for node #{@node.name} is empty", @node)
47
47
  end
48
48
 
49
49
  @buffer = info
@@ -54,7 +54,7 @@ module Aerospike
54
54
  namespace = parse_name
55
55
  rack_id = parse_rack_id
56
56
 
57
- @racks = {} if !@racks
57
+ @racks ||= {}
58
58
  @racks[namespace] = rack_id
59
59
  end
60
60
 
@@ -76,7 +76,8 @@ module Aerospike
76
76
  if namespace.length <= 0 || namespace.length >= 32
77
77
  response = get_truncated_response
78
78
  raise Aerospike::Exceptions::Parse.new(
79
- "Invalid rack namespace #{namespace}. Response=#{response}"
79
+ "Invalid rack namespace #{namespace}. Response=#{response}",
80
+ @node
80
81
  )
81
82
  end
82
83
 
@@ -97,7 +98,8 @@ module Aerospike
97
98
  if rack_id < 0
98
99
  response = get_truncated_response
99
100
  raise Aerospike::Exceptions::Parse.new(
100
- "Invalid rack_id #{rack_id}. Response=#{response}"
101
+ "Invalid rack_id #{rack_id}. Response=#{response}",
102
+ @node
101
103
  )
102
104
  end
103
105
 
@@ -129,7 +129,7 @@ module Aerospike
129
129
  when Aerospike::Replica::RANDOM
130
130
  random_node
131
131
  else
132
- raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
132
+ raise Aerospike::Exceptions::InvalidNode.new("invalid policy.replica value")
133
133
  end
134
134
  end
135
135
 
@@ -147,7 +147,7 @@ module Aerospike
147
147
  when Aerospike::Replica::RANDOM
148
148
  random_node
149
149
  else
150
- raise Aerospike::Exceptions::InvalidNode("invalid policy.replica value")
150
+ raise Aerospike::Exceptions::InvalidNode.new("invalid policy.replica value")
151
151
  end
152
152
  end
153
153
 
@@ -155,13 +155,13 @@ module Aerospike
155
155
  def master_node(partition)
156
156
  partition_map = partitions
157
157
  replica_array = partition_map[partition.namespace]
158
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array
158
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless replica_array
159
159
 
160
160
  node_array = replica_array.get[0]
161
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless node_array
161
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless node_array
162
162
 
163
163
  node = node_array.get[partition.partition_id]
164
- raise Aerospike::Exceptions::InvalidNode if !node || !node.active?
164
+ raise Aerospike::Exceptions::InvalidNode.new("no active node found") if !node || !node.active?
165
165
 
166
166
  node
167
167
  end
@@ -170,7 +170,7 @@ module Aerospike
170
170
  def rack_node(partition, seq)
171
171
  partition_map = partitions
172
172
  replica_array = partition_map[partition.namespace]
173
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array
173
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless replica_array
174
174
 
175
175
  replica_array = replica_array.get
176
176
 
@@ -195,14 +195,14 @@ module Aerospike
195
195
 
196
196
  return fallback if fallback
197
197
 
198
- raise Aerospike::Exceptions::InvalidNode
198
+ raise Aerospike::Exceptions::InvalidNode.new("no active node found")
199
199
  end
200
200
 
201
201
  # Returns a node on the cluster for read operations
202
202
  def master_proles_node(partition)
203
203
  partition_map = partitions
204
204
  replica_array = partition_map[partition.namespace]
205
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array
205
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless replica_array
206
206
 
207
207
  replica_array = replica_array.get
208
208
 
@@ -214,14 +214,14 @@ module Aerospike
214
214
  return node if node && node.active?
215
215
  end
216
216
 
217
- raise Aerospike::Exceptions::InvalidNode
217
+ raise Aerospike::Exceptions::InvalidNode.new("no active node found")
218
218
  end
219
219
 
220
220
  # Returns a random node on the cluster
221
221
  def sequence_node(partition, seq)
222
222
  partition_map = partitions
223
223
  replica_array = partition_map[partition.namespace]
224
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array
224
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless replica_array
225
225
 
226
226
  replica_array = replica_array.get
227
227
 
@@ -233,7 +233,7 @@ module Aerospike
233
233
  return node if node && node.active?
234
234
  end
235
235
 
236
- raise Aerospike::Exceptions::InvalidNode
236
+ raise Aerospike::Exceptions::InvalidNode.new("node active node found")
237
237
  end
238
238
 
239
239
  def get_node_for_key(replica_policy, key, is_write: false)
@@ -251,10 +251,10 @@ module Aerospike
251
251
 
252
252
  partition_map = partitions
253
253
  replica_array = partition_map[namespace]
254
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless replica_array
254
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless replica_array
255
255
 
256
256
  node_array = replica_array.get[0]
257
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") unless node_array
257
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") unless node_array
258
258
 
259
259
 
260
260
  pid = 0
@@ -281,7 +281,7 @@ module Aerospike
281
281
 
282
282
  i = i.succ
283
283
  end
284
- raise Aerospike::Exceptions::InvalidNode
284
+ raise Aerospike::Exceptions::InvalidNode.new("no active node found")
285
285
  end
286
286
 
287
287
  # Returns a list of all nodes in the cluster
@@ -296,7 +296,7 @@ module Aerospike
296
296
  def get_node_by_name(node_name)
297
297
  node = find_node_by_name(node_name)
298
298
 
299
- raise Aerospike::Exceptions::InvalidNode unless node
299
+ raise Aerospike::Exceptions::InvalidNode.new("node `#{node_name}` not found") unless node
300
300
 
301
301
  node
302
302
  end
@@ -62,7 +62,7 @@ module Aerospike
62
62
  end
63
63
  end
64
64
  size_buffer
65
- write_header_read(policy, read_attr | INFO1_BATCH, 0, field_count, 0)
65
+ write_header_read(policy, read_attr | INFO1_BATCH, 0, 0, field_count, 0)
66
66
 
67
67
  write_filter_exp(@policy.filter_exp, exp_size)
68
68
 
@@ -33,7 +33,7 @@ module Aerospike
33
33
  op_count = @data_buffer.read_int16(20)
34
34
 
35
35
  if op_count > 0
36
- raise Aerospike::Exceptions::Parse.new('Received bins that were not requested!')
36
+ raise Aerospike::Exceptions::Parse.new('Received bins that were not requested!', @node)
37
37
  end
38
38
 
39
39
  skip_key(field_count)
@@ -92,7 +92,7 @@ module Aerospike
92
92
  if record.bin_names&.length&.> 0
93
93
  write_batch_bin_names(key, record.bin_names, attr, attr.filter_exp)
94
94
  elsif record.ops&.length&.> 0
95
- attr.adjust_read(br.ops)
95
+ attr.adjust_read(record.ops)
96
96
  write_batch_operations(key, record.ops, attr, attr.filter_exp)
97
97
  else
98
98
  attr.adjust_read_all_bins(record.read_all_bins)
@@ -58,7 +58,8 @@ module Aerospike
58
58
  INFO2_DURABLE_DELETE = Integer(1 << 4)
59
59
  # Create only. Fail if record already exists.
60
60
  INFO2_CREATE_ONLY = Integer(1 << 5)
61
-
61
+ # Treat as long query, but relax read consistency.
62
+ INFO2_RELAX_AP_LONG_QUERY = (1 << 6)
62
63
  # Return a result for every operation.
63
64
  INFO2_RESPOND_ALL_OPS = Integer(1 << 7)
64
65
 
@@ -195,7 +196,7 @@ module Aerospike
195
196
  field_count += 1 if exp_size > 0
196
197
 
197
198
  size_buffer
198
- write_header_read(policy, INFO1_READ | INFO1_GET_ALL, 0, field_count, 0)
199
+ write_header_read(policy, INFO1_READ | INFO1_GET_ALL, 0, 0, field_count, 0)
199
200
  write_key(key)
200
201
  write_filter_exp(@policy.filter_exp, exp_size)
201
202
  end_cmd
@@ -220,7 +221,7 @@ module Aerospike
220
221
  attr |= INFO1_GET_ALL
221
222
  end
222
223
 
223
- write_header_read(policy, attr, 0, field_count, bin_names.length)
224
+ write_header_read(policy, attr, 0, 0, field_count, bin_names.length)
224
225
  write_key(key)
225
226
  write_filter_exp(@policy.filter_exp, exp_size)
226
227
 
@@ -269,7 +270,7 @@ module Aerospike
269
270
 
270
271
  size_buffer
271
272
 
272
- write_header_read_write(policy, args.read_attr, args.write_attr, field_count, args.operations.length)
273
+ write_header_read_write(policy, args, field_count)
273
274
  write_key(key, policy)
274
275
  write_filter_exp(policy.filter_exp, exp_size)
275
276
 
@@ -377,7 +378,7 @@ module Aerospike
377
378
  operation_count = bin_names.length
378
379
  end
379
380
 
380
- write_header_read(policy, read_attr, info_attr, field_count, operation_count)
381
+ write_header_read(policy, read_attr, 0, info_attr, field_count, operation_count)
381
382
 
382
383
  if namespace
383
384
  write_field_string(namespace, Aerospike::FieldType::NAMESPACE)
@@ -570,7 +571,7 @@ module Aerospike
570
571
  if operations
571
572
 
572
573
  unless background
573
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::PARAMETER_ERROR)
574
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::PARAMETER_ERROR, nil, [@node])
574
575
  end
575
576
 
576
577
  operations.each do |operation|
@@ -591,10 +592,16 @@ module Aerospike
591
592
  write_header_write(policy, INFO2_WRITE, field_count, operation_count)
592
593
  else
593
594
  read_attr = INFO1_READ
595
+ write_attr = 0
596
+
594
597
  read_attr |= INFO1_NOBINDATA unless policy.include_bin_data
595
- read_attr |= INFO1_SHORT_QUERY if policy.short_query
598
+ if policy.short_query || policy.expected_duration == QueryDuration::SHORT
599
+ read_attr |= INFO1_SHORT_QUERY
600
+ elsif policy.expected_duration == QueryDuration::LONG_RELAX_AP
601
+ write_attr |= INFO2_RELAX_AP_LONG_QUERY
602
+ end
596
603
  info_attr = INFO3_PARTITION_DONE if is_new
597
- write_header_read(policy, read_attr, info_attr, field_count, operation_count)
604
+ write_header_read(policy, read_attr, write_attr, info_attr, field_count, operation_count)
598
605
  end
599
606
 
600
607
 
@@ -678,6 +685,7 @@ module Aerospike
678
685
 
679
686
  def execute
680
687
  iterations = 0
688
+ failed_nodes = []
681
689
 
682
690
  # set timeout outside the loop
683
691
  limit = Time.now + @policy.timeout
@@ -698,6 +706,7 @@ module Aerospike
698
706
  @node = get_node
699
707
  @conn = @node.get_connection(@policy.timeout)
700
708
  rescue => e
709
+ failed_nodes << @node if @node
701
710
  if @node
702
711
  # Socket connection error has occurred. Decrease health and retry.
703
712
  @node.decrease_health
@@ -717,6 +726,7 @@ module Aerospike
717
726
  begin
718
727
  write_buffer
719
728
  rescue => e
729
+ failed_nodes << @node if @node
720
730
  Aerospike.logger.error(e)
721
731
 
722
732
  # All runtime exceptions are considered fatal. Do not retry.
@@ -731,6 +741,7 @@ module Aerospike
731
741
  begin
732
742
  @conn.write(@data_buffer, @data_offset)
733
743
  rescue => e
744
+ failed_nodes << @node if @node
734
745
  # IO errors are considered temporary anomalies. Retry.
735
746
  # Close socket to flush out possible garbage. Do not put back in pool.
736
747
  @conn.close if @conn
@@ -746,6 +757,7 @@ module Aerospike
746
757
  begin
747
758
  parse_result
748
759
  rescue => e
760
+ failed_nodes << @node if @node
749
761
  case e
750
762
  # do not log the following exceptions
751
763
  when Aerospike::Exceptions::ScanTerminated
@@ -776,7 +788,7 @@ module Aerospike
776
788
  end # while
777
789
 
778
790
  # execution timeout
779
- raise Aerospike::Exceptions::Timeout.new(limit, iterations)
791
+ raise Aerospike::Exceptions::Timeout.new(limit, iterations, failed_nodes)
780
792
  end
781
793
 
782
794
  protected
@@ -903,10 +915,14 @@ module Aerospike
903
915
  end
904
916
 
905
917
  # Header write for write operations.
906
- def write_header_read_write(policy, read_attr, write_attr, field_count, operation_count)
918
+ def write_header_read_write(policy, args, field_count)
907
919
  # Set flags.
908
920
  generation = Integer(0)
921
+ ttl = args.has_write ? policy.expiration : policy.read_touch_ttl_percent
922
+ read_attr = args.read_attr
923
+ write_attr = args.write_attr
909
924
  info_attr = Integer(0)
925
+ operation_count = args.operations.length
910
926
 
911
927
  case policy.record_exists_action
912
928
  when Aerospike::RecordExistsAction::UPDATE
@@ -942,7 +958,7 @@ module Aerospike
942
958
  @data_buffer.write_byte(0, 12) # unused
943
959
  @data_buffer.write_byte(0, 13) # clear the result code
944
960
  @data_buffer.write_uint32(generation, 14)
945
- @data_buffer.write_uint32(policy.ttl, 18)
961
+ @data_buffer.write_uint32(ttl, 18)
946
962
 
947
963
  # Initialize timeout. It will be written later.
948
964
  @data_buffer.write_byte(0, 22)
@@ -956,18 +972,19 @@ module Aerospike
956
972
  @data_offset = MSG_TOTAL_HEADER_SIZE
957
973
  end
958
974
 
959
- def write_header_read(policy, read_attr, info_attr, field_count, operation_count)
975
+ def write_header_read(policy, read_attr, write_attr, info_attr, field_count, operation_count)
960
976
  read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
961
977
  #TODO: Add SC Mode
962
978
 
963
979
  @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message header.length.
964
980
  @data_buffer.write_byte(read_attr, 9)
965
- @data_buffer.write_byte(0, 10)
981
+ @data_buffer.write_byte(write_attr, 10)
966
982
  @data_buffer.write_byte(info_attr, 11)
967
983
 
968
- (12...22).each { |i| @data_buffer.write_byte(0, i) }
984
+ (12...18).each { |i| @data_buffer.write_byte(0, i) }
969
985
 
970
986
  # Initialize timeout. It will be written later.
987
+ @data_buffer.write_int32(policy.read_touch_ttl_percent, 18)
971
988
  @data_buffer.write_byte(0, 22)
972
989
  @data_buffer.write_byte(0, 23)
973
990
  @data_buffer.write_byte(0, 24)
@@ -988,9 +1005,10 @@ module Aerospike
988
1005
  @data_buffer.write_byte(0, 10)
989
1006
  @data_buffer.write_byte(info_attr, 11)
990
1007
 
991
- (12...22).each { |i| @data_buffer.write_byte(0, i) }
1008
+ (12...18).each { |i| @data_buffer.write_byte(0, i) }
992
1009
 
993
1010
  # Initialize timeout. It will be written later.
1011
+ @data_buffer.write_int32(policy.read_touch_ttl_percent, 18)
994
1012
  @data_buffer.write_byte(0, 22)
995
1013
  @data_buffer.write_byte(0, 23)
996
1014
  @data_buffer.write_byte(0, 24)
@@ -59,13 +59,13 @@ module Aerospike
59
59
 
60
60
  if result_code == Aerospike::ResultCode::FILTERED_OUT
61
61
  if @policy.fail_on_filtered_out
62
- raise Aerospike::Exceptions::Aerospike.new(result_code)
62
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
63
63
  end
64
64
  @existed = true
65
65
  return
66
66
  end
67
67
 
68
- raise Aerospike::Exceptions::Aerospike.new(result_code)
68
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
69
69
  end
70
70
 
71
71
  end # class
@@ -59,13 +59,13 @@ module Aerospike
59
59
 
60
60
  if result_code == Aerospike::ResultCode::FILTERED_OUT
61
61
  if @policy.fail_on_filtered_out
62
- raise Aerospike::Exceptions::Aerospike.new(result_code)
62
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
63
63
  end
64
64
  @exists = true
65
65
  return
66
66
  end
67
67
 
68
- raise Aerospike::Exceptions::Aerospike.new(result_code)
68
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
69
69
  end
70
70
 
71
71
  end # class
@@ -24,7 +24,7 @@ module Aerospike
24
24
  class MultiCommand < Command #:nodoc:
25
25
 
26
26
  def initialize(node)
27
- super(node)
27
+ super
28
28
 
29
29
  @valid = true
30
30
  @mutex = Mutex.new
@@ -69,7 +69,7 @@ module Aerospike
69
69
 
70
70
  # inflate the results
71
71
  # TODO: reuse the current buffer
72
- uncompressed = Zlib::inflate(@data_buffer.buf)
72
+ uncompressed = Zlib.inflate(@data_buffer.buf)
73
73
  receive_size = uncompressed.size - 8
74
74
 
75
75
  @compressed_data_buffer = Buffer.new(-1, uncompressed)
@@ -83,11 +83,11 @@ module Aerospike
83
83
  end
84
84
  end
85
85
 
86
- if receive_size > 0
87
- status = parse_group(receive_size)
88
- else
89
- status = false
90
- end
86
+ status = if receive_size > 0
87
+ parse_group(receive_size)
88
+ else
89
+ false
90
+ end
91
91
  end
92
92
  end
93
93
 
@@ -101,10 +101,10 @@ module Aerospike
101
101
  # The only valid server return codes are "ok", "not found" and "filtered out".
102
102
  # If other return codes are received, then abort the batch.
103
103
  if result_code != 0
104
- if result_code == Aerospike::ResultCode::KEY_NOT_FOUND_ERROR || result_code == Aerospike::ResultCode::FILTERED_OUT
104
+ if [Aerospike::ResultCode::KEY_NOT_FOUND_ERROR, Aerospike::ResultCode::FILTERED_OUT].include?(result_code)
105
105
  # NOOP
106
106
  else
107
- raise Aerospike::Exceptions::Aerospike.new(result_code)
107
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
108
108
  end
109
109
  end
110
110
 
@@ -145,7 +145,7 @@ module Aerospike
145
145
  when Aerospike::FieldType::TABLE
146
146
  set_name = @data_buffer.read(1, size).force_encoding('utf-8')
147
147
  when Aerospike::FieldType::KEY
148
- user_key = Aerospike::bytes_to_key_value(@data_buffer.read(1).ord, @data_buffer, 2, size-1)
148
+ user_key = Aerospike.bytes_to_key_value(@data_buffer.read(1).ord, @data_buffer, 2, size-1)
149
149
  when Aerospike::FieldType::BVAL_ARRAY
150
150
  bval = @data_buffer.read_uint64_little_endian(1)
151
151
  end
@@ -207,7 +207,7 @@ module Aerospike
207
207
  # Corrupted data streams can result in a huge length.
208
208
  # Do a sanity check here.
209
209
  if length > Aerospike::Buffer::MAX_BUFFER_SIZE
210
- raise Aerospike::Exceptions::Parse.new("Invalid read_bytes length: #{length}")
210
+ raise Aerospike::Exceptions::Parse.new("Invalid read_bytes length: #{length}", [@node])
211
211
  end
212
212
  @data_buffer = Buffer.new(length)
213
213
  end
@@ -70,7 +70,7 @@ module Aerospike
70
70
 
71
71
  # inflate the results
72
72
  # TODO: reuse the current buffer
73
- uncompressed = Zlib::inflate(@data_buffer.buf)
73
+ uncompressed = Zlib.inflate(@data_buffer.buf)
74
74
 
75
75
  @data_buffer = Buffer.new(-1, uncompressed)
76
76
  rescue => e
@@ -126,7 +126,7 @@ module Aerospike
126
126
 
127
127
  if result_code == Aerospike::ResultCode::FILTERED_OUT
128
128
  if @policy.fail_on_filtered_out
129
- raise Aerospike::Exceptions::Aerospike.new(result_code)
129
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
130
130
  end
131
131
  return
132
132
  end
@@ -141,19 +141,19 @@ module Aerospike
141
141
  end
142
142
  end
143
143
 
144
- raise Aerospike::Exceptions::Aerospike.new(result_code)
144
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
145
145
  end
146
146
 
147
147
  def handle_udf_error(result_code)
148
148
  ret = @record.bins['FAILURE']
149
- raise Aerospike::Exceptions::Aerospike.new(result_code, ret) if ret
150
- raise Aerospike::Exceptions::Aerospike.new(result_code)
149
+ raise Aerospike::Exceptions::Aerospike.new(result_code, ret, [@node]) if ret
150
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
151
151
  end
152
152
 
153
153
  def parse_record(op_count, field_count, generation, expiration)
154
154
  bins = op_count > 0 ? {} : nil
155
155
  receive_offset = 0
156
- single_bin_value = (!(OperatePolicy === policy) || policy.record_bin_multiplicity == RecordBinMultiplicity::SINGLE)
156
+ single_bin_value = !policy.is_a?(OperatePolicy) || policy.record_bin_multiplicity == RecordBinMultiplicity::SINGLE
157
157
 
158
158
  # There can be fields in the response (setname etc).
159
159
  # But for now, ignore them. Expose them to the API if needed in the future.
@@ -181,7 +181,7 @@ module Aerospike
181
181
 
182
182
  if single_bin_value || !bins.has_key?(name)
183
183
  bins[name] = value
184
- elsif (prev = bins[name]).kind_of?(OpResults)
184
+ elsif (prev = bins[name]).is_a?(OpResults)
185
185
  prev << value
186
186
  else
187
187
  bins[name] = OpResults.new << prev << value
@@ -50,7 +50,7 @@ module Aerospike
50
50
  if result_code == 0
51
51
  generation = @data_buffer.read_int32(14)
52
52
  expiration = @data_buffer.read_int32(18)
53
- @record = Record.new(@node, @key, nil, generation, expiration)
53
+ @record = Record.new(@node, @key, nil, generation, expiration)
54
54
  return
55
55
  end
56
56
 
@@ -62,12 +62,12 @@ module Aerospike
62
62
  if result_code == Aerospike::ResultCode::FILTERED_OUT
63
63
  @record = nil
64
64
  if @policy.fail_on_filtered_out
65
- raise Aerospike::Exceptions::Aerospike.new(result_code)
65
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
66
66
  end
67
67
  return
68
68
  end
69
69
 
70
- raise Aerospike::Exceptions::Aerospike.new(result_code)
70
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
71
71
  end
72
72
 
73
73
  end # class
@@ -59,7 +59,7 @@ module Aerospike
59
59
 
60
60
  # inflate the results
61
61
  # TODO: reuse the current buffer
62
- uncompressed = Zlib::inflate(@data_buffer.buf)
62
+ uncompressed = Zlib.inflate(@data_buffer.buf)
63
63
 
64
64
  @data_buffer = Buffer.new(-1, uncompressed)
65
65
  rescue => e
@@ -81,12 +81,12 @@ module Aerospike
81
81
 
82
82
  if result_code == Aerospike::ResultCode::FILTERED_OUT
83
83
  if @policy.fail_on_filtered_out
84
- raise Aerospike::Exceptions::Aerospike.new(result_code)
84
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
85
85
  end
86
86
  return
87
87
  end
88
88
 
89
- raise Aerospike::Exceptions::Aerospike.new(result_code)
89
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
90
90
  end
91
91
 
92
92
  end # class
@@ -23,7 +23,6 @@ module Aerospike
23
23
  class WriteCommand < SingleCommand #:nodoc:
24
24
 
25
25
  def initialize(cluster, policy, key, bins, operation)
26
-
27
26
  super(cluster, key)
28
27
 
29
28
  @bins = bins
@@ -60,12 +59,12 @@ module Aerospike
60
59
 
61
60
  if result_code == Aerospike::ResultCode::FILTERED_OUT
62
61
  if @policy.fail_on_filtered_out
63
- raise Aerospike::Exceptions::Aerospike.new(result_code)
62
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
64
63
  end
65
64
  return
66
65
  end
67
66
 
68
- raise Aerospike::Exceptions::Aerospike.new(result_code)
67
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
69
68
  end
70
69
 
71
70
  end # class
@@ -476,21 +476,21 @@ module Aerospike
476
476
  def self.get_value_type(return_type)
477
477
  t = return_type & ~CDT::MapReturnType::INVERTED
478
478
  case t
479
- when MapReturnType::INDEX, MapReturnType::REVERSE_INDEX, MapReturnType::RANK, MapReturnType::REVERSE_RANK
479
+ when CDT::MapReturnType::INDEX, CDT::MapReturnType::REVERSE_INDEX, CDT::MapReturnType::RANK, CDT::MapReturnType::REVERSE_RANK
480
480
  # This method only called from expressions that can return multiple integers (ie list).
481
481
  Exp::Type::LIST
482
482
 
483
- when MapReturnType::COUNT
483
+ when CDT::MapReturnType::COUNT
484
484
  Exp::Type::INT
485
485
 
486
- when MapReturnType::KEY, MapReturnType::VALUE
486
+ when CDT::MapReturnType::KEY, CDT::MapReturnType::VALUE
487
487
  # This method only called from expressions that can return multiple objects (ie list).
488
488
  Exp::Type::LIST
489
489
 
490
- when MapReturnType::KEY_VALUE, MapReturnType::ORDERED_MAP, MapReturnType::UNORDERED_MAP
490
+ when CDT::MapReturnType::KEY_VALUE, CDT::MapReturnType::ORDERED_MAP, CDT::MapReturnType::UNORDERED_MAP
491
491
  Exp::Type::MAP
492
492
 
493
- when MapReturnType::EXISTS
493
+ when CDT::MapReturnType::EXISTS
494
494
  Exp::Type::BOOL
495
495
 
496
496
  else
@@ -25,7 +25,7 @@ module Aerospike
25
25
  def call(node, info_map)
26
26
  if node.cluster_name && node.cluster_name != info_map['cluster-name']
27
27
  node.inactive!
28
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, "Cluster name does not match. expected: #{node.cluster_name}, got: #{info_map['cluster-name']}")
28
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, "Cluster name does not match. expected: #{node.cluster_name}, got: #{info_map['cluster-name']}", [node])
29
29
  end
30
30
  end
31
31
  end
@@ -25,15 +25,15 @@ module Aerospike
25
25
  def call(node, info_map)
26
26
  info_name = info_map['node']
27
27
 
28
- if !info_name
28
+ unless info_name
29
29
  node.decrease_health
30
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, 'Node name is empty')
30
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, 'Node name is empty', [node])
31
31
  end
32
32
 
33
- if !(node.name == info_name)
33
+ unless node.name == info_name
34
34
  # Set node to inactive immediately.
35
35
  node.inactive!
36
- raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, "Node name has changed. Old=#{node.name} New= #{info_name}")
36
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NODE_ERROR, "Node name has changed. Old=#{node.name} New= #{info_name}", [node])
37
37
  end
38
38
  end
39
39
  end
@@ -27,7 +27,7 @@ module Aerospike
27
27
  def call(node, info_map)
28
28
  gen_string = info_map.fetch('partition-generation', nil)
29
29
 
30
- raise Aerospike::Exceptions::Parse.new('partition-generation is empty') if gen_string.to_s.empty?
30
+ raise Aerospike::Exceptions::Parse.new('partition-generation is empty', [node]) if gen_string.to_s.empty?
31
31
 
32
32
  generation = gen_string.to_i
33
33
 
@@ -25,7 +25,7 @@ module Aerospike
25
25
  def call(node, info_map, peers)
26
26
  gen_string = info_map.fetch('peers-generation', nil)
27
27
 
28
- raise Aerospike::Exceptions::Parse.new('peers-generation is empty') if gen_string.to_s.empty?
28
+ raise Aerospike::Exceptions::Parse.new('peers-generation is empty', node) if gen_string.to_s.empty?
29
29
 
30
30
  generation = gen_string.to_i
31
31
 
@@ -27,7 +27,7 @@ module Aerospike
27
27
  def call(node, info_map)
28
28
  gen_string = info_map.fetch('rebalance-generation', nil)
29
29
 
30
- raise Aerospike::Exceptions::Parse.new('rebalance-generation is empty') if gen_string.to_s.empty?
30
+ raise Aerospike::Exceptions::Parse.new('rebalance-generation is empty', node) if gen_string.to_s.empty?
31
31
 
32
32
  generation = gen_string.to_i
33
33
 
@@ -20,7 +20,7 @@ module Aerospike
20
20
  # Policy attributes used in batch read commands.
21
21
  class BatchReadPolicy
22
22
 
23
- attr_accessor :filter_exp
23
+ attr_accessor :filter_exp, :read_touch_ttl_percent
24
24
 
25
25
  def initialize(opt={})
26
26
  # Optional expression filter. If filter_exp exists and evaluates to false, the specific batch key
@@ -33,6 +33,23 @@ module Aerospike
33
33
  #
34
34
  # Default: nil
35
35
  @filter_exp = opt[:filter_exp]
36
+
37
+ # Determines how record TTL (time to live) is affected on reads. When enabled, the server can
38
+ # efficiently operate as a read-based LRU cache where the least recently used records are expired.
39
+ # The value is expressed as a percentage of the TTL sent on the most recent write such that a read
40
+ # within this interval of the record’s end of life will generate a touch.
41
+ #
42
+ # For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
43
+ # 80, the next read within 8 hours of the record's end of life (equivalent to 2 hours after the most
44
+ # recent write) will result in a touch, resetting the TTL to another 10 hours.
45
+ #
46
+ # Values:
47
+ #
48
+ # 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
49
+ # -1 : Do not reset record TTL on reads.
50
+ # 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
51
+ # Default: 0
52
+ @read_touch_ttl_percent = opt[:read_touch_ttl_percent] || 0
36
53
  end
37
54
  end
38
55
  end
@@ -22,7 +22,7 @@ module Aerospike
22
22
  # Container object for client policy command.
23
23
  class Policy
24
24
  attr_accessor :filter_exp, :priority, :timeout, :max_retries, :sleep_between_retries, :consistency_level,
25
- :fail_on_filtered_out, :replica, :use_compression, :socket_timeout
25
+ :fail_on_filtered_out, :replica, :use_compression, :socket_timeout, :read_touch_ttl_percent
26
26
 
27
27
  alias total_timeout timeout
28
28
  alias total_timeout= timeout=
@@ -95,6 +95,23 @@ module Aerospike
95
95
  # has not yet been exceeded.
96
96
  @max_retries = opt[:max_retries] || 2
97
97
 
98
+ # Determines how record TTL (time to live) is affected on reads. When enabled, the server can
99
+ # efficiently operate as a read-based LRU cache where the least recently used records are expired.
100
+ # The value is expressed as a percentage of the TTL sent on the most recent write such that a read
101
+ # within this interval of the record’s end of life will generate a touch.
102
+ #
103
+ # For example, if the most recent write had a TTL of 10 hours and read_touch_ttl_percent is set to
104
+ # 80, the next read within 8 hours of the record's end of life (equivalent to 2 hours after the most
105
+ # recent write) will result in a touch, resetting the TTL to another 10 hours.
106
+ #
107
+ # Values:
108
+ #
109
+ # 0 : Use server config default-read-touch-ttl-pct for the record's namespace/set.
110
+ # -1 : Do not reset record TTL on reads.
111
+ # 1 - 100 : Reset record TTL on reads when within this percentage of the most recent write TTL.
112
+ # Default: 0
113
+ @read_touch_ttl_percent = opt[:read_touch_ttl_percent] || 0
114
+
98
115
  # Duration to sleep between retries if a transaction fails and the
99
116
  # timeout was not exceeded. Enter zero to skip sleep.
100
117
  @sleep_between_retries = opt[:sleep_between_retries] || 0.5
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+ # Copyright 2014-2024 Aerospike, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:#www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Aerospike
17
+
18
+ # Defines the expected query duration. The server treats the query in different ways depending on the expected duration.
19
+ # This enum is ignored for aggregation queries, background queries and server versions < 6.0.
20
+ module QueryDuration
21
+
22
+ # The query is expected to return more than 100 records per node. The server optimizes for a large record set in
23
+ # the following ways:
24
+ #
25
+ # Allow query to be run in multiple threads using the server's query threading configuration.
26
+ # Do not relax read consistency for AP namespaces.
27
+ # Add the query to the server's query monitor.
28
+ # Do not add the overall latency to the server's latency histogram.
29
+ # Do not allow server timeouts.
30
+ LONG = 0
31
+
32
+ # The query is expected to return less than 100 records per node. The server optimizes for a small record set in
33
+ # the following ways:
34
+ # Always run the query in one thread and ignore the server's query threading configuration.
35
+ # Allow query to be inlined directly on the server's service thread.
36
+ # Relax read consistency for AP namespaces.
37
+ # Do not add the query to the server's query monitor.
38
+ # Add the overall latency to the server's latency histogram.
39
+ # Allow server timeouts. The default server timeout for a short query is 1 second.
40
+ SHORT = 1
41
+
42
+ # Treat query as a LONG query, but relax read consistency for AP namespaces.
43
+ # This value is treated exactly like LONG for server versions < 7.1.
44
+ LONG_RELAX_AP = 2
45
+
46
+ end # module
47
+
48
+ end # module
@@ -15,6 +15,7 @@
15
15
  # License for the specific language governing permissions and limitations under
16
16
  # the License.
17
17
 
18
+ require 'aerospike/policy/query_duration'
18
19
  require 'aerospike/policy/policy'
19
20
 
20
21
  module Aerospike
@@ -22,16 +23,10 @@ module Aerospike
22
23
  # Container object for query policy command.
23
24
  class QueryPolicy < Policy
24
25
 
25
- attr_accessor :concurrent_nodes
26
- attr_accessor :max_records
27
- attr_accessor :include_bin_data
28
- attr_accessor :record_queue_size
29
- attr_accessor :records_per_second
30
- attr_accessor :socket_timeout
31
- attr_accessor :short_query
26
+ attr_accessor :concurrent_nodes, :max_records, :include_bin_data, :record_queue_size, :records_per_second, :socket_timeout, :short_query, :expected_duration
32
27
 
33
28
  def initialize(opt={})
34
- super(opt)
29
+ super
35
30
 
36
31
  # Indicates if bin data is retrieved. If false, only record digests (and
37
32
  # user keys if stored on the server) are retrieved.
@@ -74,11 +69,21 @@ module Aerospike
74
69
  # Default is 0
75
70
  @records_per_second = opt[:records_per_second] || 0
76
71
 
72
+ # Expected query duration. The server treats the query in different ways depending on the expected duration.
73
+ # This field is ignored for aggregation queries, background queries and server versions < 6.0.
74
+ #
75
+ # Default: QueryDuration::LONG
76
+ @expected_duration = opt[:expected_duration] || QueryDuration::LONG
77
+
78
+ # DEPRECATED
77
79
  # Detemine wether query expected to return less than 100 records.
78
80
  # If true, the server will optimize the query for a small record set.
79
81
  # This field is ignored for aggregation queries, background queries
80
82
  # and server versions 6.0+.
81
83
  #
84
+ # This field is deprecated and will eventually be removed. Use {expected_duration} instead.
85
+ # For backwards compatibility: If ShortQuery is true, the query is treated as a short query and
86
+ # {expected_duration} is ignored. If {short_query} is false, {expected_duration} is used as defaults to {Policy#QueryDuration#LONG}.
82
87
  # Default: false
83
88
  @short_query = opt[:short_query] ||false
84
89
 
@@ -77,7 +77,7 @@ module Aerospike
77
77
 
78
78
  pmap = cluster.partitions
79
79
  replica_array = pmap[namespace]
80
- raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
80
+ raise Aerospike::Exceptions::InvalidNamespace.new("namespace not found in the partition map") if !replica_array
81
81
 
82
82
  master = (replica_array.get)[0]
83
83
  master = master.get
@@ -45,7 +45,7 @@ module Aerospike
45
45
  if result_code == Aerospike::ResultCode::KEY_NOT_FOUND_ERROR
46
46
  return false
47
47
  end
48
- raise Aerospike::Exceptions::Aerospike.new(result_code)
48
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
49
49
  end
50
50
  op_count = @data_buffer.read_int16(20)
51
51
  if op_count <= 0
@@ -46,7 +46,7 @@ module Aerospike
46
46
  read_bytes(receive_size - @data_offset) if @data_offset < receive_size
47
47
  return nil
48
48
  else
49
- raise Aerospike::Exceptions::Aerospike.new(result_code)
49
+ raise Aerospike::Exceptions::Aerospike.new(result_code, nil, [@node])
50
50
  end
51
51
 
52
52
  info3 = @data_buffer.read(3).ord
@@ -72,17 +72,16 @@ module Aerospike
72
72
  next
73
73
  end
74
74
 
75
- if result_code == 0
76
- if @recordset.active?
77
- @recordset.records.enq(parse_record(key, op_count, generation, expiration))
78
- else
79
- expn = @recordset.is_scan? ? SCAN_TERMINATED_EXCEPTION : QUERY_TERMINATED_EXCEPTION
80
- raise expn
81
- end
82
-
83
- # UDF results do not return a key
84
- @tracker&.set_last(@node_partitions, key, key.bval) if key
75
+ next unless result_code == 0
76
+ if @recordset.active?
77
+ @recordset.records.enq(parse_record(key, op_count, generation, expiration))
78
+ else
79
+ expn = @recordset.is_scan? ? SCAN_TERMINATED_EXCEPTION : QUERY_TERMINATED_EXCEPTION
80
+ raise expn
85
81
  end
82
+
83
+ # UDF results do not return a key
84
+ @tracker&.set_last(@node_partitions, key, key.bval) if key
86
85
  end # while
87
86
 
88
87
  true
@@ -168,6 +168,9 @@ module Aerospike
168
168
  # Write command loses conflict to XDR.
169
169
  LOST_CONFLICT = 28
170
170
 
171
+ # Write can't complete until XDR finishes shipping.
172
+ XDR_KEY_BUSY = 32
173
+
171
174
  # There are no more records left for query.
172
175
  QUERY_END = 50
173
176
 
@@ -445,6 +448,10 @@ module Aerospike
445
448
  when LOST_CONFLICT
446
449
  "Write command loses conflict to XDR."
447
450
 
451
+ # Write can't complete until XDR finishes shipping.
452
+ when XDR_KEY_BUSY
453
+ "XDR key busy"
454
+
448
455
  when QUERY_END
449
456
  "Query end"
450
457
 
@@ -580,7 +587,6 @@ module Aerospike
580
587
  else
581
588
  "ResultCode #{code} unknown in the client. Please file a github issue."
582
589
  end # case
583
-
584
590
  end
585
591
 
586
592
  end # class
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Aerospike
3
- VERSION = "4.0.0"
3
+ VERSION = "4.2.0"
4
4
  end
data/lib/aerospike.rb CHANGED
@@ -90,6 +90,7 @@ require "aerospike/cdt/bit_policy"
90
90
  require "aerospike/geo_json"
91
91
  require "aerospike/ttl"
92
92
 
93
+ require "aerospike/policy/query_duration"
93
94
  require "aerospike/policy/client_policy"
94
95
  require "aerospike/policy/priority"
95
96
  require "aerospike/policy/record_exists_action"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aerospike
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Khosrow Afroozeh
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-08-14 00:00:00.000000000 Z
13
+ date: 2024-12-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: msgpack
@@ -166,6 +166,7 @@ files:
166
166
  - lib/aerospike/policy/operate_policy.rb
167
167
  - lib/aerospike/policy/policy.rb
168
168
  - lib/aerospike/policy/priority.rb
169
+ - lib/aerospike/policy/query_duration.rb
169
170
  - lib/aerospike/policy/query_policy.rb
170
171
  - lib/aerospike/policy/record_bin_multiplicity.rb
171
172
  - lib/aerospike/policy/record_exists_action.rb