aerospike 2.24.0 → 2.26.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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