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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -4
  3. data/README.md +1 -1
  4. data/lib/aerospike.rb +17 -4
  5. data/lib/aerospike/aerospike_exception.rb +7 -1
  6. data/lib/aerospike/atomic/atomic.rb +1 -1
  7. data/lib/aerospike/bin.rb +1 -1
  8. data/lib/aerospike/cdt/list_operation.rb +1 -1
  9. data/lib/aerospike/cdt/map_operation.rb +1 -1
  10. data/lib/aerospike/cdt/map_order.rb +1 -1
  11. data/lib/aerospike/cdt/map_policy.rb +1 -1
  12. data/lib/aerospike/cdt/map_return_type.rb +1 -1
  13. data/lib/aerospike/cdt/map_write_mode.rb +1 -1
  14. data/lib/aerospike/client.rb +31 -17
  15. data/lib/aerospike/cluster.rb +139 -17
  16. data/lib/aerospike/cluster/partition.rb +1 -1
  17. data/lib/aerospike/cluster/partition_parser.rb +169 -0
  18. data/lib/aerospike/cluster/rack_parser.rb +117 -0
  19. data/lib/aerospike/command/admin_command.rb +1 -1
  20. data/lib/aerospike/command/batch_direct_command.rb +2 -1
  21. data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
  22. data/lib/aerospike/command/batch_direct_node.rb +3 -3
  23. data/lib/aerospike/command/batch_index_command.rb +11 -2
  24. data/lib/aerospike/command/batch_index_node.rb +2 -2
  25. data/lib/aerospike/command/batch_item.rb +1 -1
  26. data/lib/aerospike/command/command.rb +157 -11
  27. data/lib/aerospike/command/delete_command.rb +21 -5
  28. data/lib/aerospike/command/execute_command.rb +1 -1
  29. data/lib/aerospike/command/exists_command.rb +21 -5
  30. data/lib/aerospike/command/field_type.rb +3 -1
  31. data/lib/aerospike/command/multi_command.rb +55 -5
  32. data/lib/aerospike/command/operate_command.rb +6 -1
  33. data/lib/aerospike/command/read_command.rb +63 -20
  34. data/lib/aerospike/command/read_header_command.rb +18 -6
  35. data/lib/aerospike/command/roles.rb +1 -1
  36. data/lib/aerospike/command/single_command.rb +9 -3
  37. data/lib/aerospike/command/touch_command.rb +48 -4
  38. data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
  39. data/lib/aerospike/command/write_command.rb +13 -4
  40. data/lib/aerospike/connection/create.rb +1 -1
  41. data/lib/aerospike/features.rb +3 -1
  42. data/lib/aerospike/geo_json.rb +70 -1
  43. data/lib/aerospike/host.rb +1 -1
  44. data/lib/aerospike/info.rb +1 -1
  45. data/lib/aerospike/key.rb +1 -1
  46. data/lib/aerospike/language.rb +1 -1
  47. data/lib/aerospike/node.rb +21 -7
  48. data/lib/aerospike/node/rebalance.rb +50 -0
  49. data/lib/aerospike/node/refresh/info.rb +4 -1
  50. data/lib/aerospike/node/refresh/partitions.rb +6 -15
  51. data/lib/aerospike/node/refresh/racks.rb +47 -0
  52. data/lib/aerospike/node/refresh/reset.rb +1 -0
  53. data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
  54. data/lib/aerospike/node_validator.rb +45 -40
  55. data/lib/aerospike/operation.rb +6 -1
  56. data/lib/aerospike/policy/admin_policy.rb +1 -1
  57. data/lib/aerospike/policy/batch_policy.rb +1 -1
  58. data/lib/aerospike/policy/client_policy.rb +16 -1
  59. data/lib/aerospike/policy/commit_level.rb +1 -1
  60. data/lib/aerospike/policy/consistency_level.rb +1 -1
  61. data/lib/aerospike/policy/generation_policy.rb +1 -1
  62. data/lib/aerospike/policy/operate_policy.rb +1 -1
  63. data/lib/aerospike/policy/policy.rb +64 -2
  64. data/lib/aerospike/policy/priority.rb +1 -1
  65. data/lib/aerospike/policy/query_policy.rb +8 -1
  66. data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
  67. data/lib/aerospike/policy/record_exists_action.rb +1 -1
  68. data/lib/aerospike/policy/replica.rb +45 -0
  69. data/lib/aerospike/policy/scan_policy.rb +8 -1
  70. data/lib/aerospike/policy/write_policy.rb +1 -1
  71. data/lib/aerospike/query/filter.rb +1 -1
  72. data/lib/aerospike/query/pred_exp.rb +192 -0
  73. data/lib/aerospike/query/pred_exp/and_or.rb +32 -0
  74. data/lib/aerospike/query/pred_exp/geo_json_value.rb +41 -0
  75. data/lib/aerospike/query/pred_exp/integer_value.rb +32 -0
  76. data/lib/aerospike/query/pred_exp/op.rb +27 -0
  77. data/lib/aerospike/query/pred_exp/regex.rb +32 -0
  78. data/lib/aerospike/query/pred_exp/regex_flags.rb +23 -0
  79. data/lib/aerospike/query/pred_exp/string_value.rb +29 -0
  80. data/lib/aerospike/query/query_command.rb +27 -1
  81. data/lib/aerospike/query/recordset.rb +5 -5
  82. data/lib/aerospike/query/scan_command.rb +1 -1
  83. data/lib/aerospike/query/statement.rb +12 -3
  84. data/lib/aerospike/query/stream_command.rb +1 -1
  85. data/lib/aerospike/record.rb +1 -1
  86. data/lib/aerospike/result_code.rb +13 -7
  87. data/lib/aerospike/socket/base.rb +4 -3
  88. data/lib/aerospike/task/execute_task.rb +1 -1
  89. data/lib/aerospike/task/index_task.rb +1 -1
  90. data/lib/aerospike/task/task.rb +1 -1
  91. data/lib/aerospike/task/udf_register_task.rb +1 -1
  92. data/lib/aerospike/task/udf_remove_task.rb +1 -1
  93. data/lib/aerospike/ttl.rb +1 -1
  94. data/lib/aerospike/udf.rb +1 -1
  95. data/lib/aerospike/user_role.rb +1 -1
  96. data/lib/aerospike/utils/buffer.rb +14 -4
  97. data/lib/aerospike/utils/packer.rb +1 -1
  98. data/lib/aerospike/utils/pool.rb +1 -1
  99. data/lib/aerospike/utils/unpacker.rb +7 -2
  100. data/lib/aerospike/value/particle_type.rb +1 -1
  101. data/lib/aerospike/value/value.rb +59 -29
  102. data/lib/aerospike/version.rb +1 -1
  103. metadata +19 -8
  104. data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
  105. data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright 2014-2017 Aerospike, Inc.
3
+ # Copyright 2014-2020 Aerospike, Inc.
4
4
  #
5
5
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
6
6
  # license agreements.
@@ -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,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # Copyright 2014-2017 Aerospike, Inc.
2
+ # Copyright 2014-2020 Aerospike, Inc.
3
3
  #
4
4
  # Portions may be licensed to Aerospike, Inc. under one or more contributor
5
5
  # license agreements.
@@ -1,4 +1,4 @@
1
- # Copyright 2014-2018 Aerospike, Inc.
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-2018 Aerospike, Inc.
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.
@@ -1,4 +1,4 @@
1
- # Copyright 2014-2018 Aerospike, Inc.
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, 1, 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(field_count, @data_offset)
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-2018 Aerospike, Inc.
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.
@@ -1,4 +1,4 @@
1
- # Copyright 2014-2018 Aerospike, Inc.
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
- # overrite this as appropriate.
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
- read_attr |= INFO1_READ
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
- # The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
215
- # The workaround is to request a non-existent bin.
216
- # TODO: Fix this on server.
217
- # read_attr |= _INFO1_READ | _INFO1_NOBINDATA
218
- read_attr |= INFO1_READ
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