aerospike 2.24.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.
@@ -13,26 +13,44 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- require 'aerospike/policy/priority'
17
- require 'aerospike/policy/consistency_level'
18
- require 'aerospike/policy/replica'
19
-
16
+ require "aerospike/policy/priority"
17
+ require "aerospike/policy/consistency_level"
18
+ require "aerospike/policy/replica"
20
19
 
21
20
  module Aerospike
22
21
 
23
22
  # Container object for client policy command.
24
23
  class Policy
25
-
26
- attr_accessor :priority, :timeout, :max_retries, :sleep_between_retries, :consistency_level,
24
+ attr_accessor :filter_exp, :priority, :timeout, :max_retries, :sleep_between_retries, :consistency_level,
27
25
  :predexp, :fail_on_filtered_out, :replica, :use_compression
28
26
 
29
27
  alias total_timeout timeout
30
28
  alias total_timeout= timeout=
31
29
 
32
- def initialize(opt={})
30
+ def initialize(opt = {})
33
31
  # Container object for transaction policy attributes used in all database
34
32
  # operation calls.
35
33
 
34
+ # Optional expression filter. If filterExp exists and evaluates to false, the
35
+ # transaction is ignored.
36
+ #
37
+ # Default: nil
38
+ #
39
+ # ==== Examples:
40
+ #
41
+ # p = Policy.new
42
+ # p.filter_exp = Exp.build(Exp.eq(Exp.int_bin("a"), Exp.int_val(11)));
43
+ @filter_exp = opt[:filter_exp]
44
+
45
+ # Throw exception if {#filter_exp} is defined and that filter evaluates
46
+ # to false (transaction ignored). The {AerospikeException}
47
+ # will contain result code {ResultCode::FILTERED_OUT}.
48
+ #
49
+ # This field is not applicable to batch, scan or query commands.
50
+ #
51
+ # Default: false
52
+ @fail_on_filtered_out = opt[:fail_on_filtered_out] || false
53
+
36
54
  # Priority of request relative to other transactions.
37
55
  # Currently, only used for scans.
38
56
  @priority = opt[:priority] || Priority::DEFAULT
@@ -74,7 +92,6 @@ module Aerospike
74
92
  # ]
75
93
  @predexp = opt[:predexp] || nil
76
94
 
77
-
78
95
  # Throw exception if @predexp is defined and that filter evaluates
79
96
  # to false (transaction ignored). The Aerospike::Exceptions::Aerospike
80
97
  # will contain result code Aerospike::ResultCode::FILTERED_OUT.
@@ -86,7 +103,6 @@ module Aerospike
86
103
  # read operation.
87
104
  @consistency_level = opt[:consistency_level] || Aerospike::ConsistencyLevel::CONSISTENCY_ONE
88
105
 
89
-
90
106
  # Send read commands to the node containing the key's partition replica type.
91
107
  # Write commands are not affected by this setting, because all writes are directed
92
108
  # to the node containing the key's master partition.
@@ -118,8 +134,5 @@ module Aerospike
118
134
  # timeout was not exceeded. Enter zero to skip sleep.
119
135
  @sleep_between_retries = opt[:sleep_between_retries] || 0.5
120
136
  end
121
-
122
-
123
137
  end # class
124
-
125
138
  end # module
@@ -15,39 +15,51 @@
15
15
  # the License.
16
16
 
17
17
  module Aerospike
18
-
19
18
  class Filter
19
+ attr_reader :packed_ctx
20
20
 
21
- def self.Equal(bin_name, value)
22
- Filter.new(bin_name, value, value)
23
- end
21
+ # open up the class to alias the class methods for naming consistency
22
+ class << self
23
+ def equal(bin_name, value, ctx: nil)
24
+ Filter.new(bin_name, value, value, nil, nil, ctx)
25
+ end
24
26
 
25
- def self.Contains(bin_name, value, col_type)
26
- Filter.new(bin_name, value, value, nil, col_type)
27
- end
27
+ def contains(bin_name, value, col_type, ctx: nil)
28
+ Filter.new(bin_name, value, value, nil, col_type, ctx)
29
+ end
28
30
 
29
- def self.Range(bin_name, from, to, col_type = nil)
30
- Filter.new(bin_name, from, to, nil, col_type)
31
- end
31
+ def range(bin_name, from, to, col_type = nil, ctx: nil)
32
+ Filter.new(bin_name, from, to, nil, col_type, ctx)
33
+ end
32
34
 
33
- def self.geoWithinGeoJSONRegion(bin_name, region, col_type = nil)
34
- region = region.to_json
35
- Filter.new(bin_name, region, region, ParticleType::GEOJSON, col_type)
36
- end
35
+ def geo_within_geo_region(bin_name, region, col_type = nil, ctx: nil)
36
+ region = region.to_json
37
+ Filter.new(bin_name, region, region, ParticleType::GEOJSON, col_type, ctx)
38
+ end
37
39
 
38
- def self.geoWithinRadius(bin_name, lon, lat, radius_meter, col_type = nil)
39
- region = GeoJSON.new({type: "AeroCircle", coordinates: [[lon, lat], radius_meter]})
40
- geoWithinGeoJSONRegion(bin_name, region, col_type)
41
- end
40
+ def geo_within_radius(bin_name, lon, lat, radius_meter, col_type = nil, ctx: nil)
41
+ region = GeoJSON.new({ type: "AeroCircle", coordinates: [[lon, lat], radius_meter] })
42
+ geo_within_geo_region(bin_name, region, col_type, ctx: ctx)
43
+ end
42
44
 
43
- def self.geoContainsGeoJSONPoint(bin_name, point, col_type = nil)
44
- point = point.to_json
45
- Filter.new(bin_name, point, point, ParticleType::GEOJSON, col_type)
46
- end
45
+ def geo_contains_geo_point(bin_name, point, col_type = nil, ctx: nil)
46
+ point = point.to_json
47
+ Filter.new(bin_name, point, point, ParticleType::GEOJSON, col_type, ctx)
48
+ end
47
49
 
48
- def self.geoContainsPoint(bin_name, lon, lat, col_type = nil)
49
- point = GeoJSON.new({type: "Point", coordinates: [lon, lat]})
50
- geoContainsGeoJSONPoint(bin_name, point, col_type)
50
+ def geo_contains_point(bin_name, lon, lat, col_type = nil, ctx: nil)
51
+ point = GeoJSON.new({ type: "Point", coordinates: [lon, lat] })
52
+ geo_contains_geo_point(bin_name, point, col_type, ctx: ctx)
53
+ end
54
+
55
+ # alias the old names for compatibility
56
+ alias :Equal :equal
57
+ alias :Contains :contains
58
+ alias :Range :range
59
+ alias :geoWithinGeoJSONRegion :geo_within_geo_region
60
+ alias :geoWithinRadius :geo_within_radius
61
+ alias :geoContainsGeoJSONPoint :geo_contains_geo_point
62
+ alias :geoContainsPoint :geo_contains_point
51
63
  end
52
64
 
53
65
  def estimate_size
@@ -56,21 +68,21 @@ module Aerospike
56
68
 
57
69
  def write(buf, offset)
58
70
  # Write name.
59
- len = buf.write_binary(@name, offset+1)
71
+ len = buf.write_binary(@name, offset + 1)
60
72
  buf.write_byte(len, offset)
61
73
  offset += len + 1
62
74
 
63
75
  # Write particle type.
64
76
  buf.write_byte(@val_type, offset)
65
- offset+=1
77
+ offset += 1
66
78
 
67
79
  # Write filter begin.
68
- len = @begin.write(buf, offset+4)
80
+ len = @begin.write(buf, offset + 4)
69
81
  buf.write_int32(len, offset)
70
82
  offset += len + 4
71
83
 
72
84
  # Write filter end.
73
- len = @end.write(buf, offset+4)
85
+ len = @end.write(buf, offset + 4)
74
86
  buf.write_int32(len, offset)
75
87
  offset += len + 4
76
88
 
@@ -98,7 +110,7 @@ module Aerospike
98
110
 
99
111
  private
100
112
 
101
- def initialize(bin_name, begin_value, end_value, val_type = nil, col_type = nil)
113
+ def initialize(bin_name, begin_value, end_value, val_type = nil, col_type = nil, ctx = nil)
102
114
  @name = bin_name
103
115
  @begin = Aerospike::Value.of(begin_value)
104
116
  @end = Aerospike::Value.of(end_value)
@@ -107,8 +119,8 @@ module Aerospike
107
119
  # but in certain cases caller can override the type.
108
120
  @val_type = val_type || @begin.type
109
121
  @col_type = col_type
110
- end
111
122
 
123
+ @packed_ctx = CDT::Context.bytes(ctx)
124
+ end
112
125
  end # class
113
-
114
126
  end
@@ -17,28 +17,29 @@
17
17
 
18
18
  module Aerospike
19
19
  class QueryExecutor # :nodoc:
20
-
21
20
  def self.query_partitions(cluster, policy, tracker, statement, recordset)
22
21
  interval = policy.sleep_between_retries
23
22
 
24
23
  should_retry = false
25
24
 
26
25
  loop do
26
+ # reset last_expn
27
+ @last_expn = nil
28
+
27
29
  list = tracker.assign_partitions_to_nodes(cluster, statement.namespace)
28
30
 
29
31
  if policy.concurrent_nodes
30
32
  threads = []
31
33
  # Use a thread per node
32
34
  list.each do |node_partition|
33
-
34
35
  threads << Thread.new do
35
36
  Thread.current.abort_on_exception = true
36
37
  command = QueryPartitionCommand.new(node_partition.node, tracker, policy, statement, recordset, node_partition)
37
38
  begin
38
39
  command.execute
39
40
  rescue => e
41
+ @last_expn = e unless e == QUERY_TERMINATED_EXCEPTION
40
42
  should_retry ||= command.should_retry(e)
41
- # puts "should retry: #{should_retry}"
42
43
  Aerospike.logger.error(e.backtrace.join("\n")) unless e == QUERY_TERMINATED_EXCEPTION
43
44
  end
44
45
  end
@@ -51,23 +52,20 @@ module Aerospike
51
52
  begin
52
53
  command.execute
53
54
  rescue => e
55
+ @last_expn = e unless e == QUERY_TERMINATED_EXCEPTION
54
56
  should_retry ||= command.should_retry(e)
55
57
  Aerospike.logger.error(e.backtrace.join("\n")) unless e == QUERY_TERMINATED_EXCEPTION
56
58
  end
57
59
  end
58
60
  end
59
61
 
60
- complete = tracker.complete?(@cluster, policy)
61
-
62
- if complete || !should_retry
63
- recordset.thread_finished
62
+ if tracker.complete?(@cluster, policy) || !should_retry
63
+ recordset.thread_finished(@last_expn)
64
64
  return
65
65
  end
66
66
  sleep(interval) if policy.sleep_between_retries > 0
67
67
  statement.reset_task_id
68
68
  end
69
69
  end
70
-
71
70
  end
72
-
73
71
  end
@@ -14,15 +14,13 @@
14
14
  # License for the specific language governing permissions and limitations under
15
15
  # the License.
16
16
 
17
- require 'aerospike/query/stream_command'
18
- require 'aerospike/query/recordset'
17
+ require "aerospike/query/stream_command"
18
+ require "aerospike/query/recordset"
19
19
 
20
20
  module Aerospike
21
-
22
21
  private
23
22
 
24
23
  class QueryPartitionCommand < QueryCommand #:nodoc:
25
-
26
24
  def initialize(node, tracker, policy, statement, recordset, node_partitions)
27
25
  super(node, policy, statement, recordset, @node_partitions)
28
26
  @node_partitions = node_partitions
@@ -39,29 +37,29 @@ module Aerospike
39
37
 
40
38
  if @statement.namespace
41
39
  @data_offset += @statement.namespace.bytesize + FIELD_HEADER_SIZE
42
- field_count+=1
40
+ field_count += 1
43
41
  end
44
42
 
45
43
  if @statement.set_name
46
44
  @data_offset += @statement.set_name.bytesize + FIELD_HEADER_SIZE
47
- field_count+=1
45
+ field_count += 1
48
46
  end
49
47
 
50
48
  # Estimate recordsPerSecond field size. This field is used in new servers and not used
51
49
  # (but harmless to add) in old servers.
52
50
  if @policy.records_per_second > 0
53
51
  @data_offset += 4 + FIELD_HEADER_SIZE
54
- field_count+=1
52
+ field_count += 1
55
53
  end
56
54
 
57
55
  # Estimate socket timeout field size. This field is used in new servers and not used
58
56
  # (but harmless to add) in old servers.
59
57
  @data_offset += 4 + FIELD_HEADER_SIZE
60
- field_count+=1
58
+ field_count += 1
61
59
 
62
60
  # Estimate task_id field.
63
61
  @data_offset += 8 + FIELD_HEADER_SIZE
64
- field_count+=1
62
+ field_count += 1
65
63
 
66
64
  filter = @statement.filters[0]
67
65
  bin_names = @statement.bin_names
@@ -73,23 +71,22 @@ module Aerospike
73
71
  # Estimate INDEX_TYPE field.
74
72
  if col_type > 0
75
73
  @data_offset += FIELD_HEADER_SIZE + 1
76
- field_count+=1
74
+ field_count += 1
77
75
  end
78
76
 
79
77
  # Estimate INDEX_RANGE field.
80
78
  @data_offset += FIELD_HEADER_SIZE
81
- filter_size+=1 # num filters
79
+ filter_size += 1 # num filters
82
80
  filter_size += filter.estimate_size
83
81
 
84
82
  @data_offset += filter_size
85
- field_count+=1
86
-
87
- # TODO: Implement
88
- # packed_ctx = filter.packed_ctx
89
- # if packed_ctx
90
- # @data_offset += FIELD_HEADER_SIZE + packed_ctx.length
91
- # field_count+=1
92
- # end
83
+ field_count += 1
84
+
85
+ packed_ctx = filter.packed_ctx
86
+ if packed_ctx
87
+ @data_offset += FIELD_HEADER_SIZE + packed_ctx.length
88
+ field_count += 1
89
+ end
93
90
  end
94
91
 
95
92
  @statement.set_task_id
@@ -102,13 +99,18 @@ module Aerospike
102
99
  field_count += 1
103
100
  end
104
101
 
102
+ unless @policy.filter_exp.nil?
103
+ exp_size = estimate_expression_size(@policy.filter_exp)
104
+ field_count += 1 if exp_size > 0
105
+ end
106
+
105
107
  # Estimate aggregation/background function size.
106
108
  if @statement.function_name
107
109
  @data_offset += FIELD_HEADER_SIZE + 1 # udf type
108
110
  @data_offset += @statement.package_name.bytesize + FIELD_HEADER_SIZE
109
111
  @data_offset += @statement.function_name.bytesize + FIELD_HEADER_SIZE
110
112
 
111
- function_arg_buffer=''
113
+ function_arg_buffer = ""
112
114
  if @statement.function_args && @statement.function_args.length > 0
113
115
  function_arg_buffer = Value.of(@statement.function_args).to_bytes
114
116
  end
@@ -133,24 +135,24 @@ module Aerospike
133
135
 
134
136
  if parts_full_size > 0
135
137
  @data_offset += parts_full_size + FIELD_HEADER_SIZE
136
- field_count+=1
138
+ field_count += 1
137
139
  end
138
140
 
139
141
  if parts_partial_digest_size > 0
140
142
  @data_offset += parts_partial_digest_size + FIELD_HEADER_SIZE
141
- field_count+=1
143
+ field_count += 1
142
144
  end
143
145
 
144
146
  if parts_partial_bval_size > 0
145
147
  @data_offset += parts_partial_bval_size + FIELD_HEADER_SIZE
146
- field_count+=1
148
+ field_count += 1
147
149
  end
148
150
 
149
151
  # Estimate max records field size. This field is used in new servers and not used
150
152
  # (but harmless to add) in old servers.
151
153
  if max_records > 0
152
154
  @data_offset += 8 + FIELD_HEADER_SIZE
153
- field_count+=1
155
+ field_count += 1
154
156
  end
155
157
 
156
158
  operation_count = 0
@@ -180,6 +182,8 @@ module Aerospike
180
182
  # Write records per second.
181
183
  write_field_int(@policy.records_per_second, FieldType::RECORDS_PER_SECOND) if @policy.records_per_second > 0
182
184
 
185
+ write_filter_exp(@policy.filter_exp, exp_size)
186
+
183
187
  # Write socket idle timeout.
184
188
  write_field_int(@policy.socket_timeout, FieldType::SOCKET_TIMEOUT)
185
189
 
@@ -205,11 +209,10 @@ module Aerospike
205
209
  @data_offset += @data_buffer.write_byte(1, @data_offset)
206
210
  @data_offset = filter.write(@data_buffer, @data_offset)
207
211
 
208
- # TODO: Implement
209
- # if packed_ctx
210
- # write_field_header(packed_ctx.length, FieldType::INDEX_CONTEXT)
211
- # @data_buffer.write_binary(packed_ctx, @data_offset)
212
- # end
212
+ if packed_ctx
213
+ write_field_header(packed_ctx.length, FieldType::INDEX_CONTEXT)
214
+ @data_offset += @data_buffer.write_binary(packed_ctx, @data_offset)
215
+ end
213
216
  end
214
217
 
215
218
  if @statement.function_name
@@ -260,7 +263,5 @@ module Aerospike
260
263
  # !! converts nil to false
261
264
  !!@tracker&.should_retry(@node_partitions, e)
262
265
  end
263
-
264
266
  end # class
265
-
266
267
  end # module
@@ -22,7 +22,6 @@ module Aerospike
22
22
  # so the production and the consumptoin are decoupled
23
23
  # there can be an unlimited count of producer threads and consumer threads
24
24
  class Recordset
25
-
26
25
  attr_reader :records
27
26
 
28
27
  def initialize(queue_size = 5000, thread_count = 1, type)
@@ -66,18 +65,21 @@ module Aerospike
66
65
 
67
66
  # this is called by working threads to signal their job is finished
68
67
  # it decreases the count of active threads and puts an EOF on queue when all threads are finished
69
- def thread_finished
68
+ # e is an exception that has happened in the exceutor, and outside of the threads themselves
69
+ def thread_finished(expn = nil)
70
70
  @active_threads.update do |v|
71
71
  v -= 1
72
72
  @records.enq(nil) if v == 0
73
73
  v
74
74
  end
75
+
76
+ raise expn unless expn.nil?
75
77
  end
76
78
 
77
79
  # this is called by a thread who faced an exception to singnal to terminate the whole operation
78
80
  # it also may be called by the user to terminate the command in the middle of fetching records from server nodes
79
81
  # it clears the queue so that if any threads are waiting for the queue get unblocked and find out about the cancellation
80
- def cancel(expn=nil)
82
+ def cancel(expn = nil)
81
83
  set_exception(expn)
82
84
  @cancelled.set(true)
83
85
  @records.clear
@@ -104,18 +106,16 @@ module Aerospike
104
106
  @filters.nil? || @filters.empty?
105
107
  end
106
108
 
107
- private
109
+ private
108
110
 
109
- def set_exception(expn=nil)
111
+ def set_exception(expn = nil)
110
112
  expn ||= (@type == :scan ? SCAN_TERMINATED_EXCEPTION : QUERY_TERMINATED_EXCEPTION)
111
113
  @thread_exception.set(expn)
112
114
  end
113
-
114
115
  end
115
116
 
116
117
  private
117
118
 
118
- SCAN_TERMINATED_EXCEPTION = Aerospike::Exceptions::ScanTerminated.new()
119
- QUERY_TERMINATED_EXCEPTION = Aerospike::Exceptions::QueryTerminated.new()
120
-
119
+ SCAN_TERMINATED_EXCEPTION = Aerospike::Exceptions::ScanTerminated.new()
120
+ QUERY_TERMINATED_EXCEPTION = Aerospike::Exceptions::QueryTerminated.new()
121
121
  end
@@ -17,26 +17,28 @@
17
17
 
18
18
  module Aerospike
19
19
  class ScanExecutor # :nodoc:
20
-
21
20
  def self.scan_partitions(policy, cluster, tracker, namespace, set_name, recordset, bin_names = nil)
22
21
  interval = policy.sleep_between_retries
23
22
 
24
23
  should_retry = false
25
24
 
26
25
  loop do
26
+ # reset last_expn
27
+ @last_expn = nil
28
+
27
29
  list = tracker.assign_partitions_to_nodes(cluster, namespace)
28
30
 
29
31
  if policy.concurrent_nodes
30
32
  threads = []
31
33
  # Use a thread per node
32
34
  list.each do |node_partition|
33
-
34
35
  threads << Thread.new do
35
36
  Thread.current.abort_on_exception = true
36
37
  command = ScanPartitionCommand.new(policy, tracker, node_partition, namespace, set_name, bin_names, recordset)
37
38
  begin
38
39
  command.execute
39
40
  rescue => e
41
+ @last_expn = e unless e == SCAN_TERMINATED_EXCEPTION
40
42
  should_retry ||= command.should_retry(e)
41
43
  Aerospike.logger.error(e.backtrace.join("\n")) unless e == SCAN_TERMINATED_EXCEPTION
42
44
  end
@@ -50,6 +52,7 @@ module Aerospike
50
52
  begin
51
53
  command.execute
52
54
  rescue => e
55
+ @last_expn = e unless e == SCAN_TERMINATED_EXCEPTION
53
56
  should_retry ||= command.should_retry(e)
54
57
  Aerospike.logger.error(e.backtrace.join("\n")) unless e == SCAN_TERMINATED_EXCEPTION
55
58
  end
@@ -57,13 +60,12 @@ module Aerospike
57
60
  end
58
61
 
59
62
  if tracker.complete?(@cluster, policy) || !should_retry
60
- recordset.thread_finished
63
+ recordset.thread_finished(@last_expn)
61
64
  return
62
65
  end
63
66
  sleep(interval) if policy.sleep_between_retries > 0
67
+ statement.reset_task_id
64
68
  end
65
69
  end
66
-
67
70
  end
68
-
69
71
  end
@@ -13,7 +13,6 @@
13
13
  # limitations under the License.
14
14
 
15
15
  module Aerospike
16
-
17
16
  private
18
17
 
19
18
  # ExecuteTask is used to poll for long running server execute job completion.
@@ -29,19 +28,24 @@ module Aerospike
29
28
  self
30
29
  end
31
30
 
32
- # IsDone queries all nodes for task completion status.
31
+ # queries all nodes for task completion status.
33
32
  def all_nodes_done?
34
-
35
- if @scan
36
- command = 'scan-list'
37
- else
38
- command = 'query-list'
39
- end
33
+ modul = @scan ? "scan" : "query"
34
+ cmd1 = "query-show:trid=#{@task_id}"
35
+ cmd2 = modul + "-show:trid=#{@task_id}"
36
+ cmd3 = "jobs:module=" + modul + ";cmd=get-job;trid=#{@task_id}"
40
37
 
41
38
  nodes = @cluster.nodes
42
39
  done = false
43
40
 
44
41
  nodes.each do |node|
42
+ command = cmd3
43
+ if node.supports_feature?(Aerospike::Features::PARTITION_QUERY)
44
+ command = cmd1
45
+ elsif node.supports_feature?(Aerospike::Features::QUERY_SHOW)
46
+ command = cmd2
47
+ end
48
+
45
49
  conn = node.get_connection(0)
46
50
  responseMap, _ = Info.request(conn, command)
47
51
  node.put_connection(conn)
@@ -58,28 +62,27 @@ module Aerospike
58
62
 
59
63
  b = index + find.length
60
64
  response = response[b, response.length]
61
- find = 'job_status='
65
+ find = "job_status="
62
66
  index = response.index(find)
63
67
 
64
68
  next unless index
65
69
 
66
70
  b = index + find.length
67
71
  response = response[b, response.length]
68
- e = response.index(':')
72
+ e = response.index(":")
69
73
  status = response[0, e]
70
74
 
71
75
  case status
72
- when 'ABORTED'
76
+ when "ABORTED"
73
77
  raise Aerospike::Exceptions::QueryTerminated
74
- when 'IN PROGRESS'
78
+ when "IN PROGRESS"
75
79
  return false
76
- when 'DONE'
80
+ when "DONE"
77
81
  done = true
78
82
  end
79
83
  end
80
84
 
81
85
  done
82
86
  end
83
-
84
87
  end
85
88
  end