aerospike 2.9.1 → 2.14.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 +50 -4
- data/README.md +1 -1
- data/lib/aerospike.rb +17 -4
- data/lib/aerospike/aerospike_exception.rb +7 -1
- data/lib/aerospike/atomic/atomic.rb +1 -1
- data/lib/aerospike/bin.rb +1 -1
- data/lib/aerospike/cdt/list_operation.rb +1 -1
- data/lib/aerospike/cdt/map_operation.rb +1 -1
- data/lib/aerospike/cdt/map_order.rb +1 -1
- data/lib/aerospike/cdt/map_policy.rb +1 -1
- data/lib/aerospike/cdt/map_return_type.rb +1 -1
- data/lib/aerospike/cdt/map_write_mode.rb +1 -1
- data/lib/aerospike/client.rb +31 -17
- data/lib/aerospike/cluster.rb +139 -17
- data/lib/aerospike/cluster/partition.rb +1 -1
- data/lib/aerospike/cluster/partition_parser.rb +169 -0
- data/lib/aerospike/cluster/rack_parser.rb +117 -0
- data/lib/aerospike/command/admin_command.rb +1 -1
- data/lib/aerospike/command/batch_direct_command.rb +2 -1
- data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
- data/lib/aerospike/command/batch_direct_node.rb +3 -3
- data/lib/aerospike/command/batch_index_command.rb +11 -2
- data/lib/aerospike/command/batch_index_node.rb +2 -2
- data/lib/aerospike/command/batch_item.rb +1 -1
- data/lib/aerospike/command/command.rb +157 -11
- data/lib/aerospike/command/delete_command.rb +21 -5
- data/lib/aerospike/command/execute_command.rb +1 -1
- data/lib/aerospike/command/exists_command.rb +21 -5
- data/lib/aerospike/command/field_type.rb +3 -1
- data/lib/aerospike/command/multi_command.rb +55 -5
- data/lib/aerospike/command/operate_command.rb +6 -1
- data/lib/aerospike/command/read_command.rb +63 -20
- data/lib/aerospike/command/read_header_command.rb +18 -6
- data/lib/aerospike/command/roles.rb +1 -1
- data/lib/aerospike/command/single_command.rb +9 -3
- data/lib/aerospike/command/touch_command.rb +48 -4
- data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
- data/lib/aerospike/command/write_command.rb +13 -4
- data/lib/aerospike/connection/create.rb +1 -1
- data/lib/aerospike/features.rb +3 -1
- data/lib/aerospike/geo_json.rb +70 -1
- data/lib/aerospike/host.rb +1 -1
- data/lib/aerospike/info.rb +1 -1
- data/lib/aerospike/key.rb +1 -1
- data/lib/aerospike/language.rb +1 -1
- data/lib/aerospike/node.rb +21 -7
- data/lib/aerospike/node/rebalance.rb +50 -0
- data/lib/aerospike/node/refresh/info.rb +4 -1
- data/lib/aerospike/node/refresh/partitions.rb +6 -15
- data/lib/aerospike/node/refresh/racks.rb +47 -0
- data/lib/aerospike/node/refresh/reset.rb +1 -0
- data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
- data/lib/aerospike/node_validator.rb +45 -40
- data/lib/aerospike/operation.rb +6 -1
- data/lib/aerospike/policy/admin_policy.rb +1 -1
- data/lib/aerospike/policy/batch_policy.rb +1 -1
- data/lib/aerospike/policy/client_policy.rb +16 -1
- data/lib/aerospike/policy/commit_level.rb +1 -1
- data/lib/aerospike/policy/consistency_level.rb +1 -1
- data/lib/aerospike/policy/generation_policy.rb +1 -1
- data/lib/aerospike/policy/operate_policy.rb +1 -1
- data/lib/aerospike/policy/policy.rb +64 -2
- data/lib/aerospike/policy/priority.rb +1 -1
- data/lib/aerospike/policy/query_policy.rb +8 -1
- data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
- data/lib/aerospike/policy/record_exists_action.rb +1 -1
- data/lib/aerospike/policy/replica.rb +45 -0
- data/lib/aerospike/policy/scan_policy.rb +8 -1
- data/lib/aerospike/policy/write_policy.rb +1 -1
- data/lib/aerospike/query/filter.rb +1 -1
- data/lib/aerospike/query/pred_exp.rb +192 -0
- data/lib/aerospike/query/pred_exp/and_or.rb +32 -0
- data/lib/aerospike/query/pred_exp/geo_json_value.rb +41 -0
- data/lib/aerospike/query/pred_exp/integer_value.rb +32 -0
- data/lib/aerospike/query/pred_exp/op.rb +27 -0
- data/lib/aerospike/query/pred_exp/regex.rb +32 -0
- data/lib/aerospike/query/pred_exp/regex_flags.rb +23 -0
- data/lib/aerospike/query/pred_exp/string_value.rb +29 -0
- data/lib/aerospike/query/query_command.rb +27 -1
- data/lib/aerospike/query/recordset.rb +5 -5
- data/lib/aerospike/query/scan_command.rb +1 -1
- data/lib/aerospike/query/statement.rb +12 -3
- data/lib/aerospike/query/stream_command.rb +1 -1
- data/lib/aerospike/record.rb +1 -1
- data/lib/aerospike/result_code.rb +13 -7
- data/lib/aerospike/socket/base.rb +4 -3
- data/lib/aerospike/task/execute_task.rb +1 -1
- data/lib/aerospike/task/index_task.rb +1 -1
- data/lib/aerospike/task/task.rb +1 -1
- data/lib/aerospike/task/udf_register_task.rb +1 -1
- data/lib/aerospike/task/udf_remove_task.rb +1 -1
- data/lib/aerospike/ttl.rb +1 -1
- data/lib/aerospike/udf.rb +1 -1
- data/lib/aerospike/user_role.rb +1 -1
- data/lib/aerospike/utils/buffer.rb +14 -4
- data/lib/aerospike/utils/packer.rb +1 -1
- data/lib/aerospike/utils/pool.rb +1 -1
- data/lib/aerospike/utils/unpacker.rb +7 -2
- data/lib/aerospike/value/particle_type.rb +1 -1
- data/lib/aerospike/value/value.rb +59 -29
- data/lib/aerospike/version.rb +1 -1
- metadata +19 -8
- data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
- data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
@@ -0,0 +1,169 @@
|
|
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
|
+
require 'base64'
|
19
|
+
|
20
|
+
module Aerospike
|
21
|
+
|
22
|
+
class PartitionParser #:nodoc:
|
23
|
+
|
24
|
+
attr_accessor :copied, :partition_generation
|
25
|
+
|
26
|
+
PARTITION_GENERATION = "partition-generation";
|
27
|
+
REPLICAS_ALL = "replicas-all";
|
28
|
+
|
29
|
+
def initialize(node, conn)
|
30
|
+
@node = node
|
31
|
+
@conn = conn
|
32
|
+
end
|
33
|
+
|
34
|
+
def update_partitions(current_map)
|
35
|
+
# Use low-level info methods and parse byte array directly for maximum performance.
|
36
|
+
# Receive format: replicas-all\t
|
37
|
+
# <ns1>:<count>,<base 64 encoded bitmap1>,<base 64 encoded bitmap2>...;
|
38
|
+
# <ns2>:<count>,<base 64 encoded bitmap1>,<base 64 encoded bitmap2>...;\n
|
39
|
+
info_map = Info.request(@conn, PARTITION_GENERATION, REPLICAS_ALL)
|
40
|
+
|
41
|
+
@partition_generation = info_map[PARTITION_GENERATION].to_i
|
42
|
+
|
43
|
+
info = info_map[REPLICAS_ALL]
|
44
|
+
if !info || info.length == 0
|
45
|
+
raise Aerospike::Exceptions::Connection.new("#{REPLICAS_ALL} response for node #{@node.name} is empty")
|
46
|
+
end
|
47
|
+
|
48
|
+
@buffer = info
|
49
|
+
@length = info.length
|
50
|
+
@offset = 0
|
51
|
+
|
52
|
+
new_map = nil
|
53
|
+
copied = false
|
54
|
+
beginning = @offset
|
55
|
+
|
56
|
+
while @offset < @length && @buffer[@offset] != '\n'
|
57
|
+
namespace = parse_name
|
58
|
+
replica_count = parse_replica_count
|
59
|
+
|
60
|
+
replica_array = current_map[namespace]
|
61
|
+
if !replica_array
|
62
|
+
if !copied
|
63
|
+
# Make shallow copy of map.
|
64
|
+
new_map = current_map.clone
|
65
|
+
copied = true
|
66
|
+
end
|
67
|
+
|
68
|
+
replica_array = Atomic.new(Array.new(replica_count))
|
69
|
+
new_map[namespace] = replica_array
|
70
|
+
end
|
71
|
+
|
72
|
+
for replica in 0...replica_count do
|
73
|
+
node_array = (replica_array.get)[replica]
|
74
|
+
|
75
|
+
if !node_array
|
76
|
+
if !copied
|
77
|
+
# Make shallow copy of map.
|
78
|
+
new_map = current_map.clone
|
79
|
+
copied = true
|
80
|
+
end
|
81
|
+
|
82
|
+
node_array = Atomic.new(Array.new(Aerospike::Node::PARTITIONS))
|
83
|
+
new_map[namespace].update{|v| v[replica] = node_array; v}
|
84
|
+
end
|
85
|
+
|
86
|
+
restore_buffer = parse_bitmap
|
87
|
+
i = 0
|
88
|
+
while i < Aerospike::Node::PARTITIONS
|
89
|
+
if (restore_buffer[i>>3].ord & (0x80 >> (i & 7))) != 0
|
90
|
+
node_array.update{|v| v[i] = @node; v}
|
91
|
+
end
|
92
|
+
i = i.succ
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
copied ? new_map : nil
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def parse_name
|
103
|
+
beginning = @offset
|
104
|
+
while @offset < @length
|
105
|
+
break if @buffer[@offset] == ':'
|
106
|
+
@offset+=1
|
107
|
+
end
|
108
|
+
|
109
|
+
# Parse namespace.
|
110
|
+
namespace = @buffer[beginning...@offset].strip
|
111
|
+
|
112
|
+
if namespace.length <= 0 || namespace.length >= 32
|
113
|
+
response = get_truncated_response
|
114
|
+
raise Aerospike::Exceptions::Parse.new(
|
115
|
+
"Invalid partition namespace #{namespace}. Response=#{response}"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
@offset+=1
|
120
|
+
namespace
|
121
|
+
end
|
122
|
+
|
123
|
+
def parse_replica_count
|
124
|
+
beginning = @offset
|
125
|
+
while @offset < @length
|
126
|
+
break if @buffer[@offset] == ','
|
127
|
+
@offset+=1
|
128
|
+
end
|
129
|
+
|
130
|
+
# Parse count
|
131
|
+
count = @buffer[beginning...@offset].strip.to_i
|
132
|
+
|
133
|
+
if count < 0 || count > 4096
|
134
|
+
response = get_truncated_response
|
135
|
+
raise Aerospike::Exceptions::Parse.new(
|
136
|
+
"Invalid partition count #{count}. Response=#{response}"
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
@offset+=1
|
141
|
+
count
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_bitmap
|
145
|
+
beginning = @offset
|
146
|
+
while @offset < @length
|
147
|
+
break if @buffer[@offset] == ','
|
148
|
+
break if @buffer[@offset] == ';'
|
149
|
+
@offset+=1
|
150
|
+
end
|
151
|
+
|
152
|
+
bit_map_length = @offset - beginning
|
153
|
+
restore_buffer = Base64.strict_decode64(@buffer[beginning, bit_map_length])
|
154
|
+
|
155
|
+
@offset+=1
|
156
|
+
restore_buffer
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def get_truncated_response
|
161
|
+
max = @length
|
162
|
+
@length = max if @length > 200
|
163
|
+
@buffer[0...max]
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
end # class
|
168
|
+
|
169
|
+
end # module
|
@@ -0,0 +1,117 @@
|
|
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
|
+
require 'base64'
|
19
|
+
|
20
|
+
module Aerospike
|
21
|
+
|
22
|
+
class RackParser #:nodoc:
|
23
|
+
|
24
|
+
attr_accessor :rebalance_generation, :racks
|
25
|
+
|
26
|
+
REBALANCE_GENERATION = "rebalance-generation"
|
27
|
+
RACK_IDS = "rack-ids"
|
28
|
+
|
29
|
+
def initialize(node, conn)
|
30
|
+
@node = node
|
31
|
+
@conn = conn
|
32
|
+
@racks = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def update_racks
|
36
|
+
# Use low-level info methods and parse byte array directly for maximum performance.
|
37
|
+
# Receive format: rack-ids\t
|
38
|
+
# <ns1>:<rack-id>...;
|
39
|
+
# <ns2>:<rack-id>...; ...
|
40
|
+
info_map = Info.request(@conn, REBALANCE_GENERATION, RACK_IDS)
|
41
|
+
|
42
|
+
@rebalance_generation = info_map[REBALANCE_GENERATION].to_i
|
43
|
+
|
44
|
+
info = info_map[RACK_IDS]
|
45
|
+
if !info || info.length == 0
|
46
|
+
raise Aerospike::Exceptions::Connection.new("#{RACK_IDS} response for node #{@node.name} is empty")
|
47
|
+
end
|
48
|
+
|
49
|
+
@buffer = info
|
50
|
+
@length = info.length
|
51
|
+
@offset = 0
|
52
|
+
|
53
|
+
while @offset < @length && @buffer[@offset] != '\n'
|
54
|
+
namespace = parse_name
|
55
|
+
rack_id = parse_rack_id
|
56
|
+
|
57
|
+
@racks = {} if !@racks
|
58
|
+
@racks[namespace] = rack_id
|
59
|
+
end
|
60
|
+
|
61
|
+
@racks
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def parse_name
|
67
|
+
beginning = @offset
|
68
|
+
while @offset < @length
|
69
|
+
break if @buffer[@offset] == ':'
|
70
|
+
@offset+=1
|
71
|
+
end
|
72
|
+
|
73
|
+
# Parse namespace.
|
74
|
+
namespace = @buffer[beginning...@offset].strip
|
75
|
+
|
76
|
+
if namespace.length <= 0 || namespace.length >= 32
|
77
|
+
response = get_truncated_response
|
78
|
+
raise Aerospike::Exceptions::Parse.new(
|
79
|
+
"Invalid rack namespace #{namespace}. Response=#{response}"
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
@offset+=1
|
84
|
+
namespace
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_rack_id
|
88
|
+
beginning = @offset
|
89
|
+
while @offset < @length
|
90
|
+
break if @buffer[@offset] == ';'
|
91
|
+
@offset+=1
|
92
|
+
end
|
93
|
+
|
94
|
+
# Parse rack_id
|
95
|
+
rack_id = @buffer[beginning...@offset].strip.to_i
|
96
|
+
|
97
|
+
if rack_id < 0
|
98
|
+
response = get_truncated_response
|
99
|
+
raise Aerospike::Exceptions::Parse.new(
|
100
|
+
"Invalid rack_id #{rack_id}. Response=#{response}"
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
@offset+=1
|
105
|
+
rack_id
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_truncated_response
|
109
|
+
max = @length
|
110
|
+
@length = max if @length > 200
|
111
|
+
@buffer[0...max]
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
end # class
|
116
|
+
|
117
|
+
end # module
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2014-
|
1
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
2
2
|
#
|
3
3
|
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
4
4
|
# license agreements.
|
@@ -75,6 +75,7 @@ module Aerospike
|
|
75
75
|
end
|
76
76
|
|
77
77
|
end_cmd
|
78
|
+
mark_compressed(@policy)
|
78
79
|
end
|
79
80
|
|
80
81
|
# Parse all results in the batch. Add records to shared list.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2014-
|
1
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
2
2
|
#
|
3
3
|
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
4
4
|
# license agreements.
|
@@ -24,8 +24,8 @@ module Aerospike
|
|
24
24
|
attr_accessor :node
|
25
25
|
attr_accessor :batch_namespaces
|
26
26
|
|
27
|
-
def self.generate_list(cluster, keys)
|
28
|
-
keys.group_by { |key| cluster.get_node_for_key(key) }
|
27
|
+
def self.generate_list(cluster, replica_policy, keys)
|
28
|
+
keys.group_by { |key| cluster.get_node_for_key(replica_policy, key) }
|
29
29
|
.map { |node, keys_for_node| BatchDirectNode.new(node, keys_for_node) }
|
30
30
|
end
|
31
31
|
|
@@ -39,7 +39,12 @@ module Aerospike
|
|
39
39
|
def write_buffer
|
40
40
|
bin_name_size = 0
|
41
41
|
operation_count = 0
|
42
|
+
field_count_row = 1
|
42
43
|
field_count = 1
|
44
|
+
|
45
|
+
predexp_size = estimate_predexp(@policy.predexp)
|
46
|
+
field_count += 1 if predexp_size > 0
|
47
|
+
|
43
48
|
if bin_names
|
44
49
|
bin_names.each do |bin_name|
|
45
50
|
bin_name_size += bin_name.bytesize + OPERATION_HEADER_SIZE
|
@@ -61,7 +66,10 @@ module Aerospike
|
|
61
66
|
end
|
62
67
|
end
|
63
68
|
size_buffer
|
64
|
-
write_header(policy,read_attr | INFO1_BATCH, 0,
|
69
|
+
write_header(policy,read_attr | INFO1_BATCH, 0, field_count, 0)
|
70
|
+
|
71
|
+
write_predexp(@policy.predexp, predexp_size)
|
72
|
+
|
65
73
|
write_field_header(0, Aerospike::FieldType::BATCH_INDEX)
|
66
74
|
@data_offset += @data_buffer.write_int32(batch.keys.length, @data_offset)
|
67
75
|
@data_offset += @data_buffer.write_byte(1, @data_offset)
|
@@ -77,7 +85,7 @@ module Aerospike
|
|
77
85
|
else
|
78
86
|
@data_offset += @data_buffer.write_byte(0, @data_offset)
|
79
87
|
@data_offset += @data_buffer.write_byte(read_attr, @data_offset)
|
80
|
-
@data_offset += @data_buffer.write_int16(
|
88
|
+
@data_offset += @data_buffer.write_int16(field_count_row, @data_offset)
|
81
89
|
@data_offset += @data_buffer.write_int16(operation_count, @data_offset)
|
82
90
|
write_field_string(key.namespace, Aerospike::FieldType::NAMESPACE)
|
83
91
|
|
@@ -90,6 +98,7 @@ module Aerospike
|
|
90
98
|
end
|
91
99
|
end
|
92
100
|
end_cmd
|
101
|
+
mark_compressed(@policy)
|
93
102
|
end
|
94
103
|
|
95
104
|
# Parse all results in the batch. Add records to shared list.
|
@@ -22,9 +22,9 @@ module Aerospike
|
|
22
22
|
attr_accessor :node
|
23
23
|
attr_accessor :keys_by_idx
|
24
24
|
|
25
|
-
def self.generate_list(cluster, keys)
|
25
|
+
def self.generate_list(cluster, replica_policy, keys)
|
26
26
|
keys.each_with_index
|
27
|
-
.group_by { |key, _| cluster.get_node_for_key(key) }
|
27
|
+
.group_by { |key, _| cluster.get_node_for_key(replica_policy, key) }
|
28
28
|
.map { |node, keys_with_idx| BatchIndexNode.new(node, keys_with_idx) }
|
29
29
|
end
|
30
30
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright 2014-
|
1
|
+
# Copyright 2014-2020 Aerospike, Inc.
|
2
2
|
#
|
3
3
|
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
4
4
|
# license agreements.
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# the License.
|
17
17
|
|
18
18
|
require 'time'
|
19
|
+
require 'zlib'
|
19
20
|
|
20
21
|
require 'msgpack'
|
21
22
|
require 'aerospike/result_code'
|
@@ -42,6 +43,9 @@ module Aerospike
|
|
42
43
|
# Involve all replicas in read operation.
|
43
44
|
INFO1_CONSISTENCY_ALL = Integer(1 << 6)
|
44
45
|
|
46
|
+
# Tell server to compress it's response.
|
47
|
+
INFO1_COMPRESS_RESPONSE = (1 << 7)
|
48
|
+
|
45
49
|
# Create or update record
|
46
50
|
INFO2_WRITE = Integer(1 << 0)
|
47
51
|
# Fling a record into the belly of Moloch.
|
@@ -72,19 +76,29 @@ module Aerospike
|
|
72
76
|
OPERATION_HEADER_SIZE = 8
|
73
77
|
MSG_REMAINING_HEADER_SIZE = 22
|
74
78
|
DIGEST_SIZE = 20
|
79
|
+
COMPRESS_THRESHOLD = 128
|
75
80
|
CL_MSG_VERSION = 2
|
76
81
|
AS_MSG_TYPE = 3
|
82
|
+
AS_MSG_TYPE_COMPRESSED = 4
|
77
83
|
|
78
84
|
class Command #:nodoc:
|
79
85
|
|
80
|
-
def initialize(node)
|
86
|
+
def initialize(node=nil)
|
87
|
+
@data_offset = 0
|
88
|
+
@data_buffer = nil
|
89
|
+
|
81
90
|
@node = node
|
82
91
|
|
92
|
+
@compress = false
|
93
|
+
|
94
|
+
# will add before use
|
95
|
+
@sequence = Atomic.new(-1)
|
96
|
+
|
83
97
|
self
|
84
98
|
end
|
85
99
|
|
86
100
|
# List of all bins that this command will write to - sub-classes should
|
87
|
-
#
|
101
|
+
# override this as appropriate.
|
88
102
|
def write_bins
|
89
103
|
[]
|
90
104
|
end
|
@@ -93,6 +107,9 @@ module Aerospike
|
|
93
107
|
def set_write(policy, operation, key, bins)
|
94
108
|
begin_cmd
|
95
109
|
field_count = estimate_key_size(key, policy)
|
110
|
+
|
111
|
+
predexp_size = estimate_predexp(policy.predexp)
|
112
|
+
field_count += 1 if predexp_size > 0
|
96
113
|
|
97
114
|
bins.each do |bin|
|
98
115
|
estimate_operation_size_for_bin(bin)
|
@@ -102,21 +119,28 @@ module Aerospike
|
|
102
119
|
|
103
120
|
write_header_with_policy(policy, 0, INFO2_WRITE, field_count, bins.length)
|
104
121
|
write_key(key, policy)
|
122
|
+
write_predexp(policy.predexp, predexp_size)
|
105
123
|
|
106
124
|
bins.each do |bin|
|
107
125
|
write_operation_for_bin(bin, operation)
|
108
126
|
end
|
109
127
|
|
110
128
|
end_cmd
|
129
|
+
mark_compressed(policy)
|
111
130
|
end
|
112
131
|
|
113
132
|
# Writes the command for delete operations
|
114
133
|
def set_delete(policy, key)
|
115
134
|
begin_cmd
|
116
135
|
field_count = estimate_key_size(key)
|
136
|
+
|
137
|
+
predexp_size = estimate_predexp(policy.predexp)
|
138
|
+
field_count += 1 if predexp_size > 0
|
139
|
+
|
117
140
|
size_buffer
|
118
141
|
write_header_with_policy(policy, 0, INFO2_WRITE|INFO2_DELETE, field_count, 0)
|
119
142
|
write_key(key)
|
143
|
+
write_predexp(policy.predexp, predexp_size)
|
120
144
|
end_cmd
|
121
145
|
end
|
122
146
|
|
@@ -124,10 +148,15 @@ module Aerospike
|
|
124
148
|
def set_touch(policy, key)
|
125
149
|
begin_cmd
|
126
150
|
field_count = estimate_key_size(key)
|
151
|
+
|
152
|
+
predexp_size = estimate_predexp(policy.predexp)
|
153
|
+
field_count += 1 if predexp_size > 0
|
154
|
+
|
127
155
|
estimate_operation_size
|
128
156
|
size_buffer
|
129
157
|
write_header_with_policy(policy, 0, INFO2_WRITE, field_count, 1)
|
130
158
|
write_key(key)
|
159
|
+
write_predexp(policy.predexp, predexp_size)
|
131
160
|
write_operation_for_operation_type(Aerospike::Operation::TOUCH)
|
132
161
|
end_cmd
|
133
162
|
end
|
@@ -136,9 +165,14 @@ module Aerospike
|
|
136
165
|
def set_exists(policy, key)
|
137
166
|
begin_cmd
|
138
167
|
field_count = estimate_key_size(key)
|
168
|
+
|
169
|
+
predexp_size = estimate_predexp(policy.predexp)
|
170
|
+
field_count += 1 if predexp_size > 0
|
171
|
+
|
139
172
|
size_buffer
|
140
173
|
write_header(policy, INFO1_READ|INFO1_NOBINDATA, 0, field_count, 0)
|
141
174
|
write_key(key)
|
175
|
+
write_predexp(policy.predexp, predexp_size)
|
142
176
|
end_cmd
|
143
177
|
end
|
144
178
|
|
@@ -146,9 +180,14 @@ module Aerospike
|
|
146
180
|
def set_read_for_key_only(policy, key)
|
147
181
|
begin_cmd
|
148
182
|
field_count = estimate_key_size(key)
|
183
|
+
|
184
|
+
predexp_size = estimate_predexp(policy.predexp)
|
185
|
+
field_count += 1 if predexp_size > 0
|
186
|
+
|
149
187
|
size_buffer
|
150
188
|
write_header(policy, INFO1_READ|INFO1_GET_ALL, 0, field_count, 0)
|
151
189
|
write_key(key)
|
190
|
+
write_predexp(policy.predexp, predexp_size)
|
152
191
|
end_cmd
|
153
192
|
end
|
154
193
|
|
@@ -157,6 +196,10 @@ module Aerospike
|
|
157
196
|
if bin_names && bin_names.length > 0
|
158
197
|
begin_cmd
|
159
198
|
field_count = estimate_key_size(key)
|
199
|
+
|
200
|
+
predexp_size = estimate_predexp(policy.predexp)
|
201
|
+
field_count += 1 if predexp_size > 0
|
202
|
+
|
160
203
|
|
161
204
|
bin_names.each do |bin_name|
|
162
205
|
estimate_operation_size_for_bin_name(bin_name)
|
@@ -165,6 +208,7 @@ module Aerospike
|
|
165
208
|
size_buffer
|
166
209
|
write_header(policy, INFO1_READ, 0, field_count, bin_names.length)
|
167
210
|
write_key(key)
|
211
|
+
write_predexp(policy.predexp, predexp_size)
|
168
212
|
|
169
213
|
bin_names.each do |bin_name|
|
170
214
|
write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
|
@@ -180,6 +224,10 @@ module Aerospike
|
|
180
224
|
def set_read_header(policy, key)
|
181
225
|
begin_cmd
|
182
226
|
field_count = estimate_key_size(key)
|
227
|
+
|
228
|
+
predexp_size = estimate_predexp(policy.predexp)
|
229
|
+
field_count += 1 if predexp_size > 0
|
230
|
+
|
183
231
|
estimate_operation_size_for_bin_name('')
|
184
232
|
size_buffer
|
185
233
|
|
@@ -190,6 +238,7 @@ module Aerospike
|
|
190
238
|
write_header(policy, INFO1_READ, 0, field_count, 1)
|
191
239
|
|
192
240
|
write_key(key)
|
241
|
+
write_predexp(policy.predexp, predexp_size)
|
193
242
|
write_operation_for_bin_name('', Aerospike::Operation::READ)
|
194
243
|
end_cmd
|
195
244
|
end
|
@@ -198,6 +247,10 @@ module Aerospike
|
|
198
247
|
def set_operate(policy, key, operations)
|
199
248
|
begin_cmd
|
200
249
|
field_count = estimate_key_size(key, policy)
|
250
|
+
|
251
|
+
predexp_size = estimate_predexp(policy.predexp)
|
252
|
+
field_count += 1 if predexp_size > 0
|
253
|
+
|
201
254
|
read_attr = 0
|
202
255
|
write_attr = 0
|
203
256
|
read_header = false
|
@@ -205,19 +258,22 @@ module Aerospike
|
|
205
258
|
operations.each do |operation|
|
206
259
|
case operation.op_type
|
207
260
|
when Aerospike::Operation::READ
|
208
|
-
|
261
|
+
read_attr |= INFO1_READ
|
209
262
|
|
210
263
|
# Read all bins if no bin is specified.
|
211
264
|
read_attr |= INFO1_GET_ALL unless operation.bin_name
|
212
265
|
|
213
266
|
when Aerospike::Operation::READ_HEADER
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
267
|
+
# The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
|
268
|
+
# The workaround is to request a non-existent bin.
|
269
|
+
# TODO: Fix this on server.
|
270
|
+
# read_attr |= _INFO1_READ | _INFO1_NOBINDATA
|
271
|
+
read_attr |= INFO1_READ
|
219
272
|
read_header = true
|
220
273
|
|
274
|
+
when Aerospike::Operation::CDT_READ
|
275
|
+
read_attr |= INFO1_READ
|
276
|
+
|
221
277
|
else
|
222
278
|
write_attr = INFO2_WRITE
|
223
279
|
end
|
@@ -232,6 +288,7 @@ module Aerospike
|
|
232
288
|
write_header(policy, read_attr, write_attr, field_count, operations.length)
|
233
289
|
end
|
234
290
|
write_key(key, policy)
|
291
|
+
write_predexp(policy.predexp, predexp_size)
|
235
292
|
|
236
293
|
operations.each do |operation|
|
237
294
|
write_operation_for_operation(operation)
|
@@ -240,11 +297,16 @@ module Aerospike
|
|
240
297
|
write_operation_for_bin(nil, Aerospike::Operation::READ) if read_header
|
241
298
|
|
242
299
|
end_cmd
|
300
|
+
mark_compressed(policy)
|
243
301
|
end
|
244
302
|
|
245
303
|
def set_udf(policy, key, package_name, function_name, args)
|
246
304
|
begin_cmd
|
247
305
|
field_count = estimate_key_size(key, policy)
|
306
|
+
|
307
|
+
predexp_size = estimate_predexp(policy.predexp)
|
308
|
+
field_count += 1 if predexp_size > 0
|
309
|
+
|
248
310
|
arg_bytes = args.to_bytes
|
249
311
|
|
250
312
|
field_count += estimate_udf_size(package_name, function_name, arg_bytes)
|
@@ -252,11 +314,13 @@ module Aerospike
|
|
252
314
|
|
253
315
|
write_header(policy, 0, INFO2_WRITE, field_count, 0)
|
254
316
|
write_key(key, policy)
|
317
|
+
write_predexp(policy.predexp, predexp_size)
|
255
318
|
write_field_string(package_name, Aerospike::FieldType::UDF_PACKAGE_NAME)
|
256
319
|
write_field_string(function_name, Aerospike::FieldType::UDF_FUNCTION)
|
257
320
|
write_field_bytes(arg_bytes, Aerospike::FieldType::UDF_ARGLIST)
|
258
321
|
|
259
322
|
end_cmd
|
323
|
+
mark_compressed(policy)
|
260
324
|
end
|
261
325
|
|
262
326
|
def set_scan(policy, namespace, set_name, bin_names)
|
@@ -273,6 +337,14 @@ module Aerospike
|
|
273
337
|
@data_offset += set_name.bytesize + FIELD_HEADER_SIZE
|
274
338
|
field_count += 1
|
275
339
|
end
|
340
|
+
|
341
|
+
if policy.records_per_second > 0
|
342
|
+
@data_offset += 4 + FIELD_HEADER_SIZE
|
343
|
+
field_count += 1
|
344
|
+
end
|
345
|
+
|
346
|
+
predexp_size = estimate_predexp(policy.predexp)
|
347
|
+
field_count += 1 if predexp_size > 0
|
276
348
|
|
277
349
|
# Estimate scan options size.
|
278
350
|
@data_offset += 2 + FIELD_HEADER_SIZE
|
@@ -310,6 +382,12 @@ module Aerospike
|
|
310
382
|
write_field_string(set_name, Aerospike::FieldType::TABLE)
|
311
383
|
end
|
312
384
|
|
385
|
+
if policy.records_per_second > 0
|
386
|
+
write_field_int(policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND)
|
387
|
+
end
|
388
|
+
|
389
|
+
write_predexp(policy.predexp, predexp_size)
|
390
|
+
|
313
391
|
write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
|
314
392
|
|
315
393
|
priority = policy.priority & 0xFF
|
@@ -355,6 +433,7 @@ module Aerospike
|
|
355
433
|
break if @policy.timeout > 0 && Time.now > limit
|
356
434
|
|
357
435
|
begin
|
436
|
+
@node = get_node
|
358
437
|
@conn = @node.get_connection(@policy.timeout)
|
359
438
|
rescue => e
|
360
439
|
# Socket connection error has occurred. Decrease health and retry.
|
@@ -498,9 +577,20 @@ module Aerospike
|
|
498
577
|
@data_offset += OPERATION_HEADER_SIZE
|
499
578
|
end
|
500
579
|
|
580
|
+
def estimate_predexp(predexp)
|
581
|
+
if predexp && predexp.size > 0
|
582
|
+
@data_offset += FIELD_HEADER_SIZE
|
583
|
+
sz = Aerospike::PredExp.estimate_size(predexp)
|
584
|
+
@data_offset += sz
|
585
|
+
return sz
|
586
|
+
end
|
587
|
+
return 0
|
588
|
+
end
|
589
|
+
|
501
590
|
# Generic header write.
|
502
591
|
def write_header(policy, read_attr, write_attr, field_count, operation_count)
|
503
592
|
read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
|
593
|
+
read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
|
504
594
|
|
505
595
|
# Write all header data except total size which must be written last.
|
506
596
|
@data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
|
@@ -550,6 +640,7 @@ module Aerospike
|
|
550
640
|
info_attr |= INFO3_COMMIT_MASTER if policy.commit_level == Aerospike::CommitLevel::COMMIT_MASTER
|
551
641
|
read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
|
552
642
|
write_attr |= INFO2_DURABLE_DELETE if policy.durable_delete
|
643
|
+
read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
|
553
644
|
|
554
645
|
# Write all header data except total size which must be written last.
|
555
646
|
@data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
|
@@ -681,21 +772,34 @@ module Aerospike
|
|
681
772
|
@data_offset += len
|
682
773
|
end
|
683
774
|
|
775
|
+
def write_field_int(i, ftype)
|
776
|
+
@data_buffer.write_int32(i, @data_offset+FIELD_HEADER_SIZE)
|
777
|
+
write_field_header(4, ftype)
|
778
|
+
@data_offset += 4
|
779
|
+
end
|
780
|
+
|
684
781
|
def write_field_bytes(bytes, ftype)
|
685
782
|
@data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
|
686
|
-
|
687
783
|
write_field_header(bytes.bytesize, ftype)
|
688
784
|
@data_offset += bytes.bytesize
|
689
785
|
end
|
690
786
|
|
691
787
|
def write_field_header(size, ftype)
|
692
|
-
# Buffer.Int32ToBytes(size+1), @data_buffer, @data_offset
|
693
788
|
@data_buffer.write_int32(size+1, @data_offset)
|
694
789
|
@data_offset += 4
|
695
790
|
@data_buffer.write_byte(ftype, @data_offset)
|
696
791
|
@data_offset += 1
|
697
792
|
end
|
698
793
|
|
794
|
+
def write_predexp(predexp, predexp_size)
|
795
|
+
if predexp && predexp.size > 0
|
796
|
+
write_field_header(predexp_size, Aerospike::FieldType::PREDEXP)
|
797
|
+
@data_offset = Aerospike::PredExp.write(
|
798
|
+
predexp, @data_buffer, @data_offset
|
799
|
+
)
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
699
803
|
def begin_cmd
|
700
804
|
@data_offset = MSG_TOTAL_HEADER_SIZE
|
701
805
|
end
|
@@ -719,6 +823,48 @@ module Aerospike
|
|
719
823
|
@data_buffer.write_int64(size, 0)
|
720
824
|
end
|
721
825
|
|
826
|
+
def use_compression?
|
827
|
+
@compress
|
828
|
+
end
|
829
|
+
|
830
|
+
def compress_buffer
|
831
|
+
if @data_offset > COMPRESS_THRESHOLD
|
832
|
+
compressed = Zlib::deflate(@data_buffer.buf, Zlib::DEFAULT_COMPRESSION)
|
833
|
+
|
834
|
+
# write original size as header
|
835
|
+
proto_s = "%08d" % 0
|
836
|
+
proto_s[0, 8] = [@data_offset].pack('q>')
|
837
|
+
compressed.prepend(proto_s)
|
838
|
+
|
839
|
+
# write proto
|
840
|
+
proto = (compressed.size+8) | Integer(CL_MSG_VERSION << 56) | Integer(AS_MSG_TYPE << 48)
|
841
|
+
proto_s = "%08d" % 0
|
842
|
+
proto_s[0, 8] = [proto].pack('q>')
|
843
|
+
compressed.prepend(proto_s)
|
844
|
+
|
845
|
+
@data_buffer = Buffer.new(-1, compressed)
|
846
|
+
@data_offset = @data_buffer.size
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
# isCompressed returns the length of the compressed buffer.
|
851
|
+
# If the buffer is not compressed, the result will be -1
|
852
|
+
def compressed_size
|
853
|
+
# A number of these are commented out because we just don't care enough to read
|
854
|
+
# that section of the header. If we do care, uncomment and check!
|
855
|
+
proto = @data_buffer.read_int64(0)
|
856
|
+
size = proto & 0xFFFFFFFFFFFF
|
857
|
+
msg_type = (proto >> 48) & 0xFF
|
858
|
+
|
859
|
+
return nil if msg_type != AS_MSG_TYPE_COMPRESSED
|
860
|
+
|
861
|
+
size
|
862
|
+
end
|
863
|
+
|
864
|
+
def mark_compressed(policy)
|
865
|
+
@compress = policy.use_compression
|
866
|
+
end
|
867
|
+
|
722
868
|
end # class
|
723
869
|
|
724
870
|
end # module
|