aerospike 2.19.0 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +354 -244
  3. data/lib/aerospike/atomic/atomic.rb +1 -1
  4. data/lib/aerospike/cdt/context.rb +137 -70
  5. data/lib/aerospike/cdt/list_return_type.rb +4 -0
  6. data/lib/aerospike/cdt/map_operation.rb +6 -6
  7. data/lib/aerospike/cdt/map_policy.rb +16 -2
  8. data/lib/aerospike/cdt/map_return_type.rb +13 -1
  9. data/lib/aerospike/client.rb +137 -115
  10. data/lib/aerospike/cluster/create_connection.rb +1 -1
  11. data/lib/aerospike/cluster.rb +41 -4
  12. data/lib/aerospike/command/admin_command.rb +368 -52
  13. data/lib/aerospike/command/batch_index_command.rb +4 -8
  14. data/lib/aerospike/command/batch_index_exists_command.rb +1 -1
  15. data/lib/aerospike/command/batch_index_node.rb +1 -1
  16. data/lib/aerospike/command/batch_item.rb +1 -1
  17. data/lib/aerospike/command/command.rb +180 -123
  18. data/lib/aerospike/command/field_type.rb +25 -24
  19. data/lib/aerospike/command/login_command.rb +164 -0
  20. data/lib/aerospike/command/multi_command.rb +25 -2
  21. data/lib/aerospike/command/operate_args.rb +99 -0
  22. data/lib/aerospike/command/operate_command.rb +6 -11
  23. data/lib/aerospike/command/read_command.rb +2 -2
  24. data/lib/aerospike/connection/authenticate.rb +36 -3
  25. data/lib/aerospike/exp/exp.rb +1329 -0
  26. data/lib/aerospike/exp/exp_bit.rb +388 -0
  27. data/lib/aerospike/exp/exp_hll.rb +169 -0
  28. data/lib/aerospike/exp/exp_list.rb +403 -0
  29. data/lib/aerospike/exp/exp_map.rb +493 -0
  30. data/lib/aerospike/exp/operation.rb +56 -0
  31. data/lib/aerospike/features.rb +22 -9
  32. data/lib/aerospike/host/parse.rb +2 -2
  33. data/lib/aerospike/key.rb +10 -1
  34. data/lib/aerospike/node/refresh/info.rb +1 -1
  35. data/lib/aerospike/node/verify/name.rb +1 -1
  36. data/lib/aerospike/node/verify/partition_generation.rb +1 -1
  37. data/lib/aerospike/node/verify/peers_generation.rb +1 -1
  38. data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
  39. data/lib/aerospike/node_validator.rb +6 -1
  40. data/lib/aerospike/operation.rb +20 -22
  41. data/lib/aerospike/policy/auth_mode.rb +36 -0
  42. data/lib/aerospike/policy/client_policy.rb +4 -1
  43. data/lib/aerospike/policy/policy.rb +29 -13
  44. data/lib/aerospike/policy/query_policy.rb +35 -2
  45. data/lib/aerospike/policy/scan_policy.rb +19 -2
  46. data/lib/aerospike/privilege.rb +133 -0
  47. data/lib/aerospike/query/filter.rb +44 -32
  48. data/lib/aerospike/query/node_partitions.rb +39 -0
  49. data/lib/aerospike/query/partition_filter.rb +66 -0
  50. data/lib/aerospike/{command/roles.rb → query/partition_status.rb} +16 -19
  51. data/lib/aerospike/query/partition_tracker.rb +347 -0
  52. data/lib/aerospike/query/query_command.rb +20 -10
  53. data/lib/aerospike/query/query_executor.rb +71 -0
  54. data/lib/aerospike/query/query_partition_command.rb +267 -0
  55. data/lib/aerospike/query/recordset.rb +9 -9
  56. data/lib/aerospike/query/scan_command.rb +3 -2
  57. data/lib/aerospike/query/scan_executor.rb +71 -0
  58. data/lib/aerospike/query/scan_partition_command.rb +49 -0
  59. data/lib/aerospike/query/statement.rb +8 -1
  60. data/lib/aerospike/query/stream_command.rb +17 -0
  61. data/lib/aerospike/result_code.rb +83 -8
  62. data/lib/aerospike/role.rb +55 -0
  63. data/lib/aerospike/task/execute_task.rb +19 -16
  64. data/lib/aerospike/task/index_task.rb +1 -1
  65. data/lib/aerospike/user_role.rb +26 -1
  66. data/lib/aerospike/utils/buffer.rb +93 -29
  67. data/lib/aerospike/utils/packer.rb +7 -6
  68. data/lib/aerospike/utils/pool.rb +1 -1
  69. data/lib/aerospike/value/particle_type.rb +1 -12
  70. data/lib/aerospike/value/value.rb +35 -60
  71. data/lib/aerospike/version.rb +1 -1
  72. data/lib/aerospike.rb +156 -136
  73. metadata +24 -6
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2014-2020 Aerospike, Inc.
4
+ #
5
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
6
+ # license agreements.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
+ # use this file except in compliance with the License. You may obtain a copy of
10
+ # the License at http:#www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+
18
+ module Aerospike
19
+ class PartitionFilter
20
+ attr_reader :partition_begin, :count, :digest
21
+ attr_accessor :partitions, :done
22
+
23
+ alias done? done
24
+
25
+ # Creates a partition filter that
26
+ # reads all the partitions.
27
+ def self.all
28
+ PartitionFilter.new(0, Aerospike::Node::PARTITIONS)
29
+ end
30
+
31
+ # Creates a partition filter by partition id.
32
+ # Partition id is between 0 - 4095
33
+ def self.by_id(partition_id)
34
+ PartitionFilter.new(partition_id, 1)
35
+ end
36
+
37
+ # Creates a partition filter by partition range.
38
+ # begin partition id is between 0 - 4095
39
+ # count is the number of partitions, in the range of 1 - 4096 inclusive.
40
+ def self.by_range(partition_begin, count)
41
+ PartitionFilter.new(partition_begin, count)
42
+ end
43
+
44
+ # Creates a partition filter that will return
45
+ # records after key's digest in the partition containing the digest.
46
+ # Note that digest order is not the same as userKey order. This method
47
+ # only works for scan or query with nil filter.
48
+ def self.by_key(key)
49
+ PartitionFilter.new(key.partition_id, 1, key.digest)
50
+ end
51
+
52
+ def to_s
53
+ "PartitionFilter<begin: #{@partition_begin}, count: #{@count}, digest: #{@digest}, done: #{@done}>"
54
+ end
55
+
56
+ private
57
+
58
+ def initialize(partition_begin, count, digest = nil, partitions = nil, done = false)
59
+ @partition_begin = partition_begin
60
+ @count = count
61
+ @digest = digest
62
+ @partitions = partitions
63
+ @done = done
64
+ end
65
+ end
66
+ end
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  # Copyright 2014-2020 Aerospike, Inc.
3
4
  #
4
5
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
@@ -16,24 +17,20 @@
16
17
 
17
18
  module Aerospike
18
19
 
19
- # Pre-defined user roles.
20
- module Role
21
-
22
- # Manage users and their roles.
23
- USER_ADMIN = 'user-admin'
24
-
25
- # Manage indicies, user-defined functions and server configuration.
26
- SYS_ADMIN = 'sys-admin'
27
-
28
- # Allow read, write and UDF transactions with the database.
29
- READ_WRITE_UDF = "read-write-udf"
30
-
31
- # Allow read and write transactions with the database.
32
- READ_WRITE = 'read-write'
20
+ class PartitionStatus #:nodoc:
21
+ attr_accessor :bval, :id, :digest, :retry
33
22
 
34
- # Allow read transactions with the database.
35
- READ = 'read'
23
+ def initialize(id)
24
+ @id = id
25
+ @retry = true
26
+ end
36
27
 
37
- end # module
28
+ #
29
+ # Show the PartitionStatus as String.
30
+ #
31
+ def to_s
32
+ "PartitionStatus<bval: #{@bval}, id: #{@id}, retry: #{@retry}, digest: #{@digest}>"
33
+ end
38
34
 
39
- end # module
35
+ end
36
+ end
@@ -0,0 +1,347 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2014-2020 Aerospike, Inc.
4
+ #
5
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
6
+ # license agreements.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
+ # use this file except in compliance with the License. You may obtain a copy of
10
+ # the License at http:#www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+
18
+ module Aerospike
19
+ class PartitionTracker
20
+ attr_reader :partitions, :partitions_capacity, :partition_begin, :node_capacity,
21
+ :node_filter, :partition_filter, :node_partitions_list, :max_records,
22
+ :sleep_between_retries, :socket_timeout, :total_timeout, :iteration, :deadline
23
+
24
+ def initialize(policy, nodes, partition_filter = nil)
25
+ if partition_filter.nil?
26
+ return init_for_node(policy, nodes[0]) if nodes.length == 1
27
+ return init_for_nodes(policy, nodes)
28
+ end
29
+
30
+ # Validate here instead of initial PartitionFilter constructor because total number of
31
+ # cluster partitions may change on the server and PartitionFilter will never have access
32
+ # to Cluster instance. Use fixed number of partitions for now.
33
+ unless partition_filter.partition_begin.between?(0, Node::PARTITIONS - 1)
34
+ raise Aerospike::Exceptions::Aerospike.new(
35
+ Aerospike::ResultCode::PARAMETER_ERROR,
36
+ "Invalid partition begin #{partition_filter.partition_begin}. Valid range: 0-#{Aerospike::Node::PARTITIONS - 1}"
37
+ )
38
+ end
39
+
40
+ if partition_filter.count <= 0
41
+ raise Aerospike::Exceptions::Aerospike.new(
42
+ Aerospike::ResultCode::PARAMETER_ERROR,
43
+ "Invalid partition count #{partition_filter.count}"
44
+ )
45
+ end
46
+
47
+ if partition_filter.partition_begin + partition_filter.count > Node::PARTITIONS
48
+ raise Aerospike::Exceptions::Aerospike.new(
49
+ Aerospike::ResultCode::PARAMETER_ERROR,
50
+ "Invalid partition range (#{partition_filter.partition_begin}, #{partition_filter.partition_begin + partition_filter.count}"
51
+ )
52
+ end
53
+
54
+ @partition_begin = partition_filter.partition_begin
55
+ @node_capacity = nodes.length
56
+ @node_filter = nil
57
+ @partitions_capacity = partition_filter.count
58
+ @max_records = policy.max_records
59
+ @iteration = 1
60
+
61
+ if partition_filter.partitions.nil? then
62
+ partition_filter.partitions = init_partitions(policy, partition_filter.count, partition_filter.digest)
63
+ elsif policy.max_records <= 0
64
+ # Retry all partitions when max_records not specified.
65
+ partition_filter.partitions.each do |ps|
66
+ ps.retry = true
67
+ end
68
+ end
69
+
70
+ @partitions = partition_filter.partitions
71
+ @partition_filter = partition_filter
72
+ init_timeout(policy)
73
+ end
74
+
75
+ def assign_partitions_to_nodes(cluster, namespace)
76
+ list = []
77
+
78
+ pmap = cluster.partitions
79
+ replica_array = pmap[namespace]
80
+ raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
81
+
82
+ master = (replica_array.get)[0]
83
+ master = master.get
84
+
85
+ @partitions.each do |part|
86
+ if part&.retry
87
+ node = master[part.id]
88
+
89
+ unless node
90
+ raise Exceptions::Aerospike.new(Aerospike::ResultCode::INVALID_NAMESPACE, "Invalid Partition Id #{part.id} for namespace `#{namespace}` in Partition Scan")
91
+ end
92
+
93
+ part.retry = false
94
+
95
+
96
+ # Use node name to check for single node equality because
97
+ # partition map may be in transitional state between
98
+ # the old and new node with the same name.
99
+ next if @node_filter && @node_filter.name != node.name
100
+
101
+ np = find_node(list, node)
102
+
103
+ unless np
104
+ # If the partition map is in a transitional state, multiple
105
+ # node_partitions instances (each with different partitions)
106
+ # may be created for a single node.
107
+ np = NodePartitions.new(node)
108
+ list << np
109
+ end
110
+ np.add_partition(part)
111
+ end
112
+ end
113
+
114
+ if @max_records.positive?
115
+ # Distribute max_records across nodes.
116
+ node_size = list.length
117
+
118
+ if @max_records < node_size
119
+ # Only include nodes that have at least 1 record requested.
120
+ node_size = @max_records
121
+ list = list[0...node_size]
122
+ end
123
+
124
+ max = 0
125
+ max = @max_records / node_size if node_size.positive?
126
+ rem = @max_records - (max * node_size)
127
+
128
+ list[0...node_size].each_with_index do |np, i|
129
+ np.record_max = (i < rem ? max + 1 : max)
130
+ end
131
+ end
132
+
133
+ @node_partitions_list = list
134
+ list
135
+ end
136
+
137
+ def init_timeout(policy)
138
+ @sleep_between_retries = policy.sleep_between_retries
139
+ @socket_timeout = policy.socket_timeout
140
+ @total_timeout = policy.timeout
141
+ if @total_timeout.positive?
142
+ @deadline = Time.now + @total_timeout
143
+ if !@socket_timeout || @socket_timeout > @total_timeout
144
+ @socket_timeout = @total_timeout
145
+ end
146
+ end
147
+ end
148
+
149
+ def init_partitions(policy, partition_count, digest)
150
+ parts_all = Array.new(partition_count)
151
+
152
+ (0...partition_count).each do |i|
153
+ parts_all[i] = Aerospike::PartitionStatus.new(@partition_begin + i)
154
+ end
155
+
156
+ parts_all[0].digest = digest if digest
157
+
158
+ @sleep_between_retries = policy.sleep_between_retries
159
+ @socket_timeout = policy.socket_timeout
160
+ @total_timeout = policy.timeout
161
+
162
+ if @total_timeout.positive?
163
+ @deadline = Time.now + @total_timeout
164
+
165
+ if @socket_timeout == 0 || @socket_timeout > @total_timeout
166
+ @socket_timeout = @total_timeout
167
+ end
168
+ end
169
+
170
+ parts_all
171
+ end
172
+
173
+ attr_writer :sleep_between_retries
174
+
175
+
176
+ def find_node(list, node)
177
+ list.each do |node_partition|
178
+ # Use pointer equality for performance.
179
+ return node_partition if node_partition.node == node
180
+ end
181
+ nil
182
+ end
183
+
184
+ def partition_unavailable(node_partitions, partition_id)
185
+ @partitions[partition_id-@partition_begin].retry = true
186
+ node_partitions.parts_unavailable+=1
187
+ end
188
+
189
+ def set_digest(node_partitions, key)
190
+ partition_id = key.partition_id
191
+ @partitions[partition_id-@partition_begin].digest = key.digest
192
+ node_partitions.record_count+=1
193
+ end
194
+
195
+ def set_last(node_partitions, key, bval)
196
+ partition_id = key.partition_id()
197
+ if partition_id-@partition_begin < 0
198
+ raise "key.partition_id: #{@partition_id}, partition_begin: #{@partition_begin}"
199
+ end
200
+ ps = @partitions[partition_id-@partition_begin]
201
+ ps.digest = key.digest
202
+ ps.bval = bval
203
+ node_partitions.record_count+=1
204
+ end
205
+
206
+ def complete?(cluster, policy)
207
+ record_count = 0
208
+ parts_unavailable = 0
209
+
210
+ @node_partitions_list.each do |np|
211
+ record_count += np.record_count
212
+ parts_unavailable += np.parts_unavailable
213
+ end
214
+
215
+ if parts_unavailable == 0
216
+ if @max_records <= 0
217
+ @partition_filter&.done = true
218
+ else
219
+ if cluster.supports_partition_query.get()
220
+ done = true
221
+
222
+ @node_partitions_list.each do |np|
223
+ if np.record_count >= np.record_max
224
+ mark_retry(np)
225
+ done = false
226
+ end
227
+ end
228
+
229
+ @partition_filter&.done = done
230
+ else
231
+ # Server version >= 6.0 will return all records for each node up to
232
+ # that node's max. If node's record count reached max, there stilthen
233
+ # may be records available for that node.
234
+ @node_partitions_list.each do |np|
235
+ mark_retry(np) if np.record_count > 0
236
+ end
237
+ # Servers version < 6.0 can return less records than max and still
238
+ # have more records for each node, so the node is only done if nthen
239
+ # records were retrieved for that node.
240
+
241
+ @partition_filter&.done = (record_count == 0)
242
+ end
243
+ end
244
+ return true
245
+ end
246
+
247
+ return true if @max_records&.positive? && record_count >= @max_records
248
+
249
+ # Check if limits have been reached
250
+ if policy.max_retries.positive? && @iteration > policy.max_retries
251
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::MAX_RETRIES_EXCEEDED, "Max retries exceeded: #{policy.max_retries}")
252
+ end
253
+
254
+ if policy.total_timeout > 0
255
+ # Check for total timeout.
256
+ remaining = @deadline - Time.now - @sleep_between_retries
257
+
258
+ raise Aerospike::Exceptions::Timeout.new(policy.totle_timeout, @iteration) if remaining <= 0
259
+
260
+ if remaining < @total_timeout
261
+ @total_timeout = remaining
262
+
263
+ if @socket_timeout > @total_timeout
264
+ @socket_timeout = @total_timeout
265
+ end
266
+ end
267
+ end
268
+
269
+ # Prepare for next iteration.
270
+ if @max_records > 0
271
+ @max_records -= record_count
272
+ end
273
+ @iteration+=1
274
+ false
275
+ end
276
+
277
+ def should_retry(node_partitions, err)
278
+ case err
279
+ when Aerospike::Exceptions::Aerospike
280
+ case err.result_code
281
+ when Aerospike::ResultCode::TIMEOUT,
282
+ Aerospike::ResultCode::NETWORK_ERROR,
283
+ Aerospike::ResultCode::SERVER_NOT_AVAILABLE,
284
+ Aerospike::ResultCode::INDEX_NOTFOUND
285
+ mark_retry(node_partitions)
286
+ node_partitions.parts_unavailable = node_partitions.parts_full.length + node_partitions.parts_partial.length
287
+ true
288
+ end
289
+ else
290
+ false
291
+ end
292
+ end
293
+
294
+ def mark_retry(node_partitions)
295
+ node_partitions.parts_full.each do |ps|
296
+ ps.retry = true
297
+ end
298
+
299
+ node_partitions.parts_partial.each do |ps|
300
+ ps.retry = true
301
+ end
302
+ end
303
+
304
+ def to_s
305
+ sb = StringIO.new
306
+ @partitions.each_with_index do |ps, i|
307
+ sb << ps.to_s
308
+ sb << if (i+1)%16 == 0
309
+ "\n"
310
+ else
311
+ "\t"
312
+ end
313
+ end
314
+ sb.string
315
+ end
316
+
317
+ private
318
+
319
+ def init_for_nodes(policy, nodes)
320
+ ppn = Aerospike::Node::PARTITIONS / nodes.length
321
+ ppn += ppn / 4
322
+
323
+ @partition_begin = 0
324
+ @node_capacity = nodes.length
325
+ @node_filter = nil
326
+ @partitions_capacity = ppn
327
+ @max_records = policy.max_records
328
+ @iteration = 1
329
+
330
+ @partitions = init_partitions(policy, Aerospike::Node::PARTITIONS, nil)
331
+ init_timeout(policy)
332
+ end
333
+
334
+ def init_for_node(policy, node)
335
+ @partition_begin = 0
336
+ @node_capacity = 1
337
+ @node_filter = node
338
+ @partitions_capacity = Aerospike::Node::PARTITIONS
339
+ @max_records = policy.max_records
340
+ @iteration = 1
341
+
342
+ @partitions = init_partitions(policy, Aerospike::Node::PARTITIONS, nil)
343
+ init_timeout(policy)
344
+ end
345
+
346
+ end
347
+ end
@@ -23,12 +23,13 @@ module Aerospike
23
23
 
24
24
  class QueryCommand < StreamCommand #:nodoc:
25
25
 
26
- def initialize(node, policy, statement, recordset)
26
+ def initialize(node, policy, statement, recordset, partitions)
27
27
  super(node)
28
28
 
29
29
  @policy = policy
30
30
  @statement = statement
31
31
  @recordset = recordset
32
+ @partitions = partitions
32
33
  end
33
34
 
34
35
  def write_buffer
@@ -82,6 +83,9 @@ module Aerospike
82
83
  fieldCount+=1
83
84
  end
84
85
  else
86
+ @data_offset += @partitions.length * 2 + FIELD_HEADER_SIZE
87
+ fieldCount += 1
88
+
85
89
  if @policy.records_per_second > 0
86
90
  @data_offset += 4 + FIELD_HEADER_SIZE
87
91
  fieldCount += 1
@@ -89,8 +93,8 @@ module Aerospike
89
93
 
90
94
  # Calling query with no filters is more efficiently handled by a primary index scan.
91
95
  # Estimate scan options size.
92
- @data_offset += (2 + FIELD_HEADER_SIZE)
93
- fieldCount+=1
96
+ # @data_offset += (2 + FIELD_HEADER_SIZE)
97
+ # fieldCount+=1
94
98
  end
95
99
 
96
100
  @statement.set_task_id
@@ -177,18 +181,24 @@ module Aerospike
177
181
  end
178
182
  end
179
183
  else
184
+ write_field_header(@partitions.length * 2, Aerospike::FieldType::PID_ARRAY)
185
+ for pid in @partitions
186
+ @data_buffer.write_uint16_little_endian(pid, @data_offset)
187
+ @data_offset += 2
188
+ end
189
+
180
190
  if @policy.records_per_second > 0
181
191
  write_field_int(@policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND)
182
192
  end
183
193
 
184
194
  # Calling query with no filters is more efficiently handled by a primary index scan.
185
- write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
186
- priority = @policy.priority.ord
187
- priority = priority << 4
188
- @data_buffer.write_byte(priority, @data_offset)
189
- @data_offset+=1
190
- @data_buffer.write_byte(100.ord, @data_offset)
191
- @data_offset+=1
195
+ # write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
196
+ # priority = @policy.priority.ord
197
+ # priority = priority << 4
198
+ # @data_buffer.write_byte(priority, @data_offset)
199
+ # @data_offset+=1
200
+ # @data_buffer.write_byte(100.ord, @data_offset)
201
+ # @data_offset+=1
192
202
  end
193
203
 
194
204
  write_field_header(8, Aerospike::FieldType::TRAN_ID)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2014-2020 Aerospike, Inc.
4
+ #
5
+ # Portions may be licensed to Aerospike, Inc. under one or more contributor
6
+ # license agreements.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
+ # use this file except in compliance with the License. You may obtain a copy of
10
+ # the License at http:#www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations under
16
+ # the License.
17
+
18
+ module Aerospike
19
+ class QueryExecutor # :nodoc:
20
+ def self.query_partitions(cluster, policy, tracker, statement, recordset)
21
+ interval = policy.sleep_between_retries
22
+
23
+ should_retry = false
24
+
25
+ loop do
26
+ # reset last_expn
27
+ @last_expn = nil
28
+
29
+ list = tracker.assign_partitions_to_nodes(cluster, statement.namespace)
30
+
31
+ if policy.concurrent_nodes
32
+ threads = []
33
+ # Use a thread per node
34
+ list.each do |node_partition|
35
+ threads << Thread.new do
36
+ Thread.current.abort_on_exception = true
37
+ command = QueryPartitionCommand.new(node_partition.node, tracker, policy, statement, recordset, node_partition)
38
+ begin
39
+ command.execute
40
+ rescue => e
41
+ @last_expn = e unless e == QUERY_TERMINATED_EXCEPTION
42
+ should_retry ||= command.should_retry(e)
43
+ Aerospike.logger.error(e.backtrace.join("\n")) unless e == QUERY_TERMINATED_EXCEPTION
44
+ end
45
+ end
46
+ end
47
+ threads.each(&:join)
48
+ else
49
+ # Use a single thread for all nodes for all node
50
+ list.each do |node_partition|
51
+ command = QueryPartitionCommand.new(node_partition.node, tracker, policy, statement, recordset, node_partition)
52
+ begin
53
+ command.execute
54
+ rescue => e
55
+ @last_expn = e unless e == QUERY_TERMINATED_EXCEPTION
56
+ should_retry ||= command.should_retry(e)
57
+ Aerospike.logger.error(e.backtrace.join("\n")) unless e == QUERY_TERMINATED_EXCEPTION
58
+ end
59
+ end
60
+ end
61
+
62
+ if tracker.complete?(@cluster, policy) || !should_retry
63
+ recordset.thread_finished(@last_expn)
64
+ return
65
+ end
66
+ sleep(interval) if policy.sleep_between_retries > 0
67
+ statement.reset_task_id
68
+ end
69
+ end
70
+ end
71
+ end