aerospike 4.0.0 → 4.2.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 (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