aerospike 2.22.0 → 2.23.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/lib/aerospike/atomic/atomic.rb +1 -1
- data/lib/aerospike/cdt/context.rb +7 -7
- data/lib/aerospike/cdt/list_return_type.rb +4 -0
- data/lib/aerospike/cdt/map_operation.rb +6 -6
- data/lib/aerospike/cdt/map_return_type.rb +4 -0
- data/lib/aerospike/client.rb +34 -59
- data/lib/aerospike/command/admin_command.rb +1 -1
- data/lib/aerospike/command/batch_index_node.rb +1 -1
- data/lib/aerospike/command/batch_item.rb +1 -1
- data/lib/aerospike/command/command.rb +62 -24
- data/lib/aerospike/command/field_type.rb +3 -0
- data/lib/aerospike/command/login_command.rb +4 -4
- data/lib/aerospike/command/multi_command.rb +8 -2
- data/lib/aerospike/command/read_command.rb +2 -2
- data/lib/aerospike/connection/authenticate.rb +3 -3
- data/lib/aerospike/features.rb +9 -9
- data/lib/aerospike/host/parse.rb +2 -2
- data/lib/aerospike/key.rb +10 -1
- data/lib/aerospike/node/refresh/info.rb +1 -1
- data/lib/aerospike/node/verify/name.rb +1 -1
- data/lib/aerospike/node/verify/partition_generation.rb +1 -1
- data/lib/aerospike/node/verify/peers_generation.rb +1 -1
- data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
- data/lib/aerospike/policy/policy.rb +4 -1
- data/lib/aerospike/policy/scan_policy.rb +20 -1
- data/lib/aerospike/privilege.rb +1 -1
- data/lib/aerospike/query/node_partitions.rb +39 -0
- data/lib/aerospike/query/partition_filter.rb +66 -0
- data/lib/aerospike/query/partition_status.rb +36 -0
- data/lib/aerospike/query/partition_tracker.rb +347 -0
- data/lib/aerospike/query/scan_command.rb +3 -3
- data/lib/aerospike/query/scan_executor.rb +69 -0
- data/lib/aerospike/query/scan_partition_command.rb +49 -0
- data/lib/aerospike/query/statement.rb +1 -1
- data/lib/aerospike/query/stream_command.rb +14 -1
- data/lib/aerospike/result_code.rb +79 -4
- data/lib/aerospike/role.rb +2 -2
- data/lib/aerospike/task/execute_task.rb +2 -2
- data/lib/aerospike/task/index_task.rb +1 -1
- data/lib/aerospike/user_role.rb +1 -1
- data/lib/aerospike/utils/buffer.rb +6 -0
- data/lib/aerospike/utils/pool.rb +1 -1
- data/lib/aerospike/value/value.rb +6 -6
- data/lib/aerospike/version.rb +1 -1
- data/lib/aerospike.rb +6 -0
- metadata +11 -5
data/lib/aerospike/features.rb
CHANGED
@@ -23,10 +23,10 @@ module Aerospike
|
|
23
23
|
module Features
|
24
24
|
|
25
25
|
# Server supports List Complex Data Type (CDT)
|
26
|
-
CDT_LIST = :
|
26
|
+
CDT_LIST = :'cdt-list'
|
27
27
|
|
28
28
|
# Server supports Map Complex Data Type (CDT)
|
29
|
-
CDT_MAP = :
|
29
|
+
CDT_MAP = :'cdt-map'
|
30
30
|
|
31
31
|
# Server supports Float data type
|
32
32
|
FLOAT = :float
|
@@ -34,16 +34,16 @@ module Aerospike
|
|
34
34
|
# Server supports geo-spatial data type and indexing
|
35
35
|
GEO = :geo
|
36
36
|
|
37
|
-
# Server requires
|
38
|
-
LUT_NOW = :
|
37
|
+
# Server requires 'lut=now' in truncate command (AER-5955)
|
38
|
+
LUT_NOW = :'lut-now'
|
39
39
|
|
40
|
-
# Server supports the new
|
40
|
+
# Server supports the new 'peers' protocol for automatic node discovery
|
41
41
|
PEERS = :peers
|
42
42
|
|
43
|
-
# Server supports the
|
44
|
-
TRUNCATE_NAMESPACE = :
|
43
|
+
# Server supports the 'truncate-namespace' command
|
44
|
+
TRUNCATE_NAMESPACE = :'truncate-namespace'
|
45
45
|
|
46
|
-
# Server supports the
|
47
|
-
BLOB_BITS = :
|
46
|
+
# Server supports the 'blob-bits' command
|
47
|
+
BLOB_BITS = :'blob-bits'
|
48
48
|
end
|
49
49
|
end
|
data/lib/aerospike/host/parse.rb
CHANGED
data/lib/aerospike/key.rb
CHANGED
@@ -53,15 +53,20 @@ module Aerospike
|
|
53
53
|
|
54
54
|
attr_reader :namespace, :set_name, :digest
|
55
55
|
|
56
|
-
def initialize(ns, set, val, digest=nil, v1_compatible: self.class.v1_compatible?)
|
56
|
+
def initialize(ns, set, val, digest=nil, bval: nil, v1_compatible: self.class.v1_compatible?)
|
57
57
|
@namespace = ns
|
58
58
|
@set_name = set
|
59
59
|
@user_key = Value.of(val)
|
60
60
|
check_key!(@namespace, @set_name, @user_key, !digest.nil?)
|
61
61
|
@digest = digest || compute_digest(v1_compatible)
|
62
|
+
@bval = bval
|
62
63
|
end
|
63
64
|
|
64
65
|
|
66
|
+
def bval
|
67
|
+
@bval
|
68
|
+
end
|
69
|
+
|
65
70
|
def to_s
|
66
71
|
"#{@namespace}:#{@set_name}:#{@user_key}:#{@digest.nil? ? '' : @digest.bytes}"
|
67
72
|
end
|
@@ -85,6 +90,10 @@ module Aerospike
|
|
85
90
|
@digest.hash
|
86
91
|
end
|
87
92
|
|
93
|
+
def partition_id
|
94
|
+
(@digest[0..3].unpack(Partition::UNPACK_FORMAT)[0] & 0xFFFF) % Node::PARTITIONS
|
95
|
+
end
|
96
|
+
|
88
97
|
private
|
89
98
|
|
90
99
|
def valid_key?(value, has_digest)
|
@@ -30,7 +30,7 @@ module Aerospike
|
|
30
30
|
def call(node, peers)
|
31
31
|
conn = node.tend_connection
|
32
32
|
if peers.use_peers?
|
33
|
-
cmds = node.cluster.rack_aware ? CMDS_REBALANCE : CMDS_PEERS
|
33
|
+
cmds = node.cluster.rack_aware ? CMDS_REBALANCE : CMDS_PEERS
|
34
34
|
info_map = ::Aerospike::Info.request(conn, *cmds)
|
35
35
|
Verify::PeersGeneration.(node, info_map, peers)
|
36
36
|
Verify::PartitionGeneration.(node, info_map)
|
@@ -26,6 +26,9 @@ module Aerospike
|
|
26
26
|
attr_accessor :priority, :timeout, :max_retries, :sleep_between_retries, :consistency_level,
|
27
27
|
:predexp, :fail_on_filtered_out, :replica, :use_compression
|
28
28
|
|
29
|
+
alias total_timeout timeout
|
30
|
+
alias total_timeout= timeout=
|
31
|
+
|
29
32
|
def initialize(opt={})
|
30
33
|
# Container object for transaction policy attributes used in all database
|
31
34
|
# operation calls.
|
@@ -85,7 +88,7 @@ module Aerospike
|
|
85
88
|
|
86
89
|
|
87
90
|
# Send read commands to the node containing the key's partition replica type.
|
88
|
-
# Write commands are not affected by this setting, because all writes are directed
|
91
|
+
# Write commands are not affected by this setting, because all writes are directed
|
89
92
|
# to the node containing the key's master partition.
|
90
93
|
#
|
91
94
|
# Default to sending read commands to the node containing the key's master partition.
|
@@ -22,6 +22,7 @@ module Aerospike
|
|
22
22
|
# Container object for scan policy command.
|
23
23
|
class ScanPolicy < Policy
|
24
24
|
|
25
|
+
attr_accessor :max_records
|
25
26
|
attr_accessor :scan_percent
|
26
27
|
attr_accessor :concurrent_nodes
|
27
28
|
attr_accessor :include_bin_data
|
@@ -35,6 +36,16 @@ module Aerospike
|
|
35
36
|
|
36
37
|
@max_retries = 0
|
37
38
|
|
39
|
+
# Approximates the number of records to return to the client. This number is divided by the
|
40
|
+
# number of nodes involved in the query. The actual number of records returned
|
41
|
+
# may be less than MaxRecords if node record counts are small and unbalanced across
|
42
|
+
# nodes.
|
43
|
+
#
|
44
|
+
# This field is supported on server versions >= 4.9.
|
45
|
+
#
|
46
|
+
# Default: 0 (do not limit record count)
|
47
|
+
@max_records = opt.fetch(:max_records) { 0 }
|
48
|
+
|
38
49
|
# Percent of data to scan. Valid integer range is 1 to 100.
|
39
50
|
# Default is 100.
|
40
51
|
@scan_percent = opt[:scan_percent] || 100
|
@@ -51,7 +62,15 @@ module Aerospike
|
|
51
62
|
# Default is true.
|
52
63
|
@fail_on_cluster_change = opt.fetch(:fail_on_cluster_change) { true }
|
53
64
|
|
54
|
-
|
65
|
+
# Determines network timeout for each attempt.
|
66
|
+
#
|
67
|
+
# If socket_timeout is not zero and socket_timeout is reached before an attempt completes,
|
68
|
+
# the Timeout above is checked. If Timeout is not exceeded, the transaction
|
69
|
+
# is retried. If both socket_timeout and Timeout are non-zero, socket_timeout must be less
|
70
|
+
# than or equal to Timeout, otherwise Timeout will also be used for socket_timeout.
|
71
|
+
#
|
72
|
+
# Default: 30s
|
73
|
+
@socket_timeout = opt[:socket_timeout] || 30000
|
55
74
|
|
56
75
|
# Number of records to place in queue before blocking. Records received
|
57
76
|
# from multiple server nodes will be placed in a queue. A separate thread
|
data/lib/aerospike/privilege.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
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 NodePartitions
|
20
|
+
attr_accessor :node, :parts_full, :parts_partial, :record_count, :record_max, :parts_unavailable
|
21
|
+
|
22
|
+
def initialize(node)
|
23
|
+
@node= node
|
24
|
+
@parts_full= []
|
25
|
+
@parts_partial= []
|
26
|
+
@record_count= 0
|
27
|
+
@parts_unavailable= 0
|
28
|
+
@record_max= 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"Node #{@node.inspect}: full: #{@parts_full.length}, partial: #{@parts_partial.length}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_partition(partition_status)
|
36
|
+
partition_status.digest.nil? ? @parts_full << partition_status : @parts_partial << partition_status
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -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
|
@@ -0,0 +1,36 @@
|
|
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
|
+
|
20
|
+
class PartitionStatus #:nodoc:
|
21
|
+
attr_accessor :bval, :id, :digest, :retry
|
22
|
+
|
23
|
+
def initialize(id)
|
24
|
+
@id = id
|
25
|
+
@retry = true
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Show the PartitionStatus as String.
|
30
|
+
#
|
31
|
+
def to_s
|
32
|
+
"PartitionStatus<bval: #{@bval}, id: #{@id}, retry: #{@retry}, digest: #{@digest}>"
|
33
|
+
end
|
34
|
+
|
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
|