aerospike 2.11.0 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  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/bit_operation.rb +376 -0
  9. data/lib/aerospike/cdt/bit_overflow_action.rb +46 -0
  10. data/lib/aerospike/cdt/bit_policy.rb +36 -0
  11. data/lib/aerospike/cdt/bit_resize_flags.rb +44 -0
  12. data/lib/aerospike/cdt/bit_write_flags.rb +51 -0
  13. data/lib/aerospike/cdt/context.rb +101 -0
  14. data/lib/aerospike/cdt/hll_operation.rb +200 -0
  15. data/lib/aerospike/cdt/hll_policy.rb +34 -0
  16. data/lib/aerospike/cdt/hll_write_flags.rb +53 -0
  17. data/lib/aerospike/cdt/list_operation.rb +127 -97
  18. data/lib/aerospike/cdt/list_sort_flags.rb +10 -2
  19. data/lib/aerospike/cdt/map_operation.rb +154 -93
  20. data/lib/aerospike/cdt/map_order.rb +1 -1
  21. data/lib/aerospike/cdt/map_policy.rb +1 -1
  22. data/lib/aerospike/cdt/map_return_type.rb +1 -1
  23. data/lib/aerospike/cdt/map_write_mode.rb +1 -1
  24. data/lib/aerospike/client.rb +33 -17
  25. data/lib/aerospike/cluster.rb +139 -17
  26. data/lib/aerospike/cluster/partition.rb +1 -1
  27. data/lib/aerospike/cluster/partition_parser.rb +169 -0
  28. data/lib/aerospike/cluster/rack_parser.rb +117 -0
  29. data/lib/aerospike/command/admin_command.rb +1 -1
  30. data/lib/aerospike/command/batch_direct_command.rb +2 -1
  31. data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
  32. data/lib/aerospike/command/batch_direct_node.rb +3 -3
  33. data/lib/aerospike/command/batch_index_command.rb +11 -2
  34. data/lib/aerospike/command/batch_index_node.rb +2 -2
  35. data/lib/aerospike/command/batch_item.rb +1 -1
  36. data/lib/aerospike/command/command.rb +168 -12
  37. data/lib/aerospike/command/delete_command.rb +21 -5
  38. data/lib/aerospike/command/execute_command.rb +1 -1
  39. data/lib/aerospike/command/exists_command.rb +21 -5
  40. data/lib/aerospike/command/field_type.rb +2 -1
  41. data/lib/aerospike/command/multi_command.rb +55 -5
  42. data/lib/aerospike/command/operate_command.rb +6 -1
  43. data/lib/aerospike/command/read_command.rb +63 -20
  44. data/lib/aerospike/command/read_header_command.rb +18 -6
  45. data/lib/aerospike/command/roles.rb +1 -1
  46. data/lib/aerospike/command/single_command.rb +9 -3
  47. data/lib/aerospike/command/touch_command.rb +48 -4
  48. data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
  49. data/lib/aerospike/command/write_command.rb +13 -4
  50. data/lib/aerospike/connection/create.rb +1 -1
  51. data/lib/aerospike/features.rb +6 -1
  52. data/lib/aerospike/geo_json.rb +1 -1
  53. data/lib/aerospike/host.rb +1 -1
  54. data/lib/aerospike/info.rb +1 -1
  55. data/lib/aerospike/key.rb +1 -1
  56. data/lib/aerospike/language.rb +1 -1
  57. data/lib/aerospike/node.rb +21 -7
  58. data/lib/aerospike/node/rebalance.rb +50 -0
  59. data/lib/aerospike/node/refresh/info.rb +4 -1
  60. data/lib/aerospike/node/refresh/partitions.rb +6 -15
  61. data/lib/aerospike/node/refresh/racks.rb +47 -0
  62. data/lib/aerospike/node/refresh/reset.rb +1 -0
  63. data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
  64. data/lib/aerospike/node_validator.rb +4 -19
  65. data/lib/aerospike/operation.rb +13 -3
  66. data/lib/aerospike/policy/admin_policy.rb +1 -1
  67. data/lib/aerospike/policy/batch_policy.rb +1 -1
  68. data/lib/aerospike/policy/client_policy.rb +16 -1
  69. data/lib/aerospike/policy/commit_level.rb +1 -1
  70. data/lib/aerospike/policy/consistency_level.rb +1 -1
  71. data/lib/aerospike/policy/generation_policy.rb +1 -1
  72. data/lib/aerospike/policy/operate_policy.rb +1 -1
  73. data/lib/aerospike/policy/policy.rb +64 -2
  74. data/lib/aerospike/policy/priority.rb +1 -1
  75. data/lib/aerospike/policy/query_policy.rb +8 -1
  76. data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
  77. data/lib/aerospike/policy/record_exists_action.rb +1 -1
  78. data/lib/aerospike/policy/replica.rb +45 -0
  79. data/lib/aerospike/policy/scan_policy.rb +8 -1
  80. data/lib/aerospike/policy/write_policy.rb +1 -1
  81. data/lib/aerospike/query/filter.rb +1 -1
  82. data/lib/aerospike/query/query_command.rb +16 -5
  83. data/lib/aerospike/query/recordset.rb +1 -1
  84. data/lib/aerospike/query/scan_command.rb +1 -1
  85. data/lib/aerospike/query/statement.rb +9 -2
  86. data/lib/aerospike/query/stream_command.rb +1 -1
  87. data/lib/aerospike/record.rb +1 -1
  88. data/lib/aerospike/result_code.rb +26 -7
  89. data/lib/aerospike/socket/base.rb +4 -3
  90. data/lib/aerospike/task/execute_task.rb +1 -1
  91. data/lib/aerospike/task/index_task.rb +1 -1
  92. data/lib/aerospike/task/task.rb +1 -1
  93. data/lib/aerospike/task/udf_register_task.rb +1 -1
  94. data/lib/aerospike/task/udf_remove_task.rb +1 -1
  95. data/lib/aerospike/ttl.rb +1 -1
  96. data/lib/aerospike/udf.rb +1 -1
  97. data/lib/aerospike/user_role.rb +1 -1
  98. data/lib/aerospike/utils/buffer.rb +14 -4
  99. data/lib/aerospike/utils/packer.rb +1 -1
  100. data/lib/aerospike/utils/pool.rb +1 -1
  101. data/lib/aerospike/utils/unpacker.rb +1 -1
  102. data/lib/aerospike/value/particle_type.rb +2 -2
  103. data/lib/aerospike/value/value.rb +165 -33
  104. data/lib/aerospike/version.rb +1 -1
  105. metadata +20 -8
  106. data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
  107. data/lib/aerospike/cluster/partition_tokenizer_old.rb +0 -135
@@ -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.
@@ -55,6 +59,9 @@ module Aerospike
55
59
  # Create only. Fail if record already exists.
56
60
  INFO2_CREATE_ONLY = Integer(1 << 5)
57
61
 
62
+ # Return a result for every operation.
63
+ INFO2_RESPOND_ALL_OPS = Integer(1 << 7)
64
+
58
65
  # This is the last of a multi-part message.
59
66
  INFO3_LAST = Integer(1 << 0)
60
67
  # Commit to master only before declaring success.
@@ -72,19 +79,29 @@ module Aerospike
72
79
  OPERATION_HEADER_SIZE = 8
73
80
  MSG_REMAINING_HEADER_SIZE = 22
74
81
  DIGEST_SIZE = 20
82
+ COMPRESS_THRESHOLD = 128
75
83
  CL_MSG_VERSION = 2
76
84
  AS_MSG_TYPE = 3
85
+ AS_MSG_TYPE_COMPRESSED = 4
77
86
 
78
87
  class Command #:nodoc:
79
88
 
80
- def initialize(node)
89
+ def initialize(node=nil)
90
+ @data_offset = 0
91
+ @data_buffer = nil
92
+
81
93
  @node = node
82
94
 
95
+ @compress = false
96
+
97
+ # will add before use
98
+ @sequence = Atomic.new(-1)
99
+
83
100
  self
84
101
  end
85
102
 
86
103
  # List of all bins that this command will write to - sub-classes should
87
- # overrite this as appropriate.
104
+ # override this as appropriate.
88
105
  def write_bins
89
106
  []
90
107
  end
@@ -93,6 +110,9 @@ module Aerospike
93
110
  def set_write(policy, operation, key, bins)
94
111
  begin_cmd
95
112
  field_count = estimate_key_size(key, policy)
113
+
114
+ predexp_size = estimate_predexp(policy.predexp)
115
+ field_count += 1 if predexp_size > 0
96
116
 
97
117
  bins.each do |bin|
98
118
  estimate_operation_size_for_bin(bin)
@@ -102,21 +122,28 @@ module Aerospike
102
122
 
103
123
  write_header_with_policy(policy, 0, INFO2_WRITE, field_count, bins.length)
104
124
  write_key(key, policy)
125
+ write_predexp(policy.predexp, predexp_size)
105
126
 
106
127
  bins.each do |bin|
107
128
  write_operation_for_bin(bin, operation)
108
129
  end
109
130
 
110
131
  end_cmd
132
+ mark_compressed(policy)
111
133
  end
112
134
 
113
135
  # Writes the command for delete operations
114
136
  def set_delete(policy, key)
115
137
  begin_cmd
116
138
  field_count = estimate_key_size(key)
139
+
140
+ predexp_size = estimate_predexp(policy.predexp)
141
+ field_count += 1 if predexp_size > 0
142
+
117
143
  size_buffer
118
144
  write_header_with_policy(policy, 0, INFO2_WRITE|INFO2_DELETE, field_count, 0)
119
145
  write_key(key)
146
+ write_predexp(policy.predexp, predexp_size)
120
147
  end_cmd
121
148
  end
122
149
 
@@ -124,10 +151,15 @@ module Aerospike
124
151
  def set_touch(policy, key)
125
152
  begin_cmd
126
153
  field_count = estimate_key_size(key)
154
+
155
+ predexp_size = estimate_predexp(policy.predexp)
156
+ field_count += 1 if predexp_size > 0
157
+
127
158
  estimate_operation_size
128
159
  size_buffer
129
160
  write_header_with_policy(policy, 0, INFO2_WRITE, field_count, 1)
130
161
  write_key(key)
162
+ write_predexp(policy.predexp, predexp_size)
131
163
  write_operation_for_operation_type(Aerospike::Operation::TOUCH)
132
164
  end_cmd
133
165
  end
@@ -136,9 +168,14 @@ module Aerospike
136
168
  def set_exists(policy, key)
137
169
  begin_cmd
138
170
  field_count = estimate_key_size(key)
171
+
172
+ predexp_size = estimate_predexp(policy.predexp)
173
+ field_count += 1 if predexp_size > 0
174
+
139
175
  size_buffer
140
176
  write_header(policy, INFO1_READ|INFO1_NOBINDATA, 0, field_count, 0)
141
177
  write_key(key)
178
+ write_predexp(policy.predexp, predexp_size)
142
179
  end_cmd
143
180
  end
144
181
 
@@ -146,9 +183,14 @@ module Aerospike
146
183
  def set_read_for_key_only(policy, key)
147
184
  begin_cmd
148
185
  field_count = estimate_key_size(key)
186
+
187
+ predexp_size = estimate_predexp(policy.predexp)
188
+ field_count += 1 if predexp_size > 0
189
+
149
190
  size_buffer
150
191
  write_header(policy, INFO1_READ|INFO1_GET_ALL, 0, field_count, 0)
151
192
  write_key(key)
193
+ write_predexp(policy.predexp, predexp_size)
152
194
  end_cmd
153
195
  end
154
196
 
@@ -157,6 +199,10 @@ module Aerospike
157
199
  if bin_names && bin_names.length > 0
158
200
  begin_cmd
159
201
  field_count = estimate_key_size(key)
202
+
203
+ predexp_size = estimate_predexp(policy.predexp)
204
+ field_count += 1 if predexp_size > 0
205
+
160
206
 
161
207
  bin_names.each do |bin_name|
162
208
  estimate_operation_size_for_bin_name(bin_name)
@@ -165,6 +211,7 @@ module Aerospike
165
211
  size_buffer
166
212
  write_header(policy, INFO1_READ, 0, field_count, bin_names.length)
167
213
  write_key(key)
214
+ write_predexp(policy.predexp, predexp_size)
168
215
 
169
216
  bin_names.each do |bin_name|
170
217
  write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
@@ -180,6 +227,10 @@ module Aerospike
180
227
  def set_read_header(policy, key)
181
228
  begin_cmd
182
229
  field_count = estimate_key_size(key)
230
+
231
+ predexp_size = estimate_predexp(policy.predexp)
232
+ field_count += 1 if predexp_size > 0
233
+
183
234
  estimate_operation_size_for_bin_name('')
184
235
  size_buffer
185
236
 
@@ -190,6 +241,7 @@ module Aerospike
190
241
  write_header(policy, INFO1_READ, 0, field_count, 1)
191
242
 
192
243
  write_key(key)
244
+ write_predexp(policy.predexp, predexp_size)
193
245
  write_operation_for_bin_name('', Aerospike::Operation::READ)
194
246
  end_cmd
195
247
  end
@@ -198,40 +250,55 @@ module Aerospike
198
250
  def set_operate(policy, key, operations)
199
251
  begin_cmd
200
252
  field_count = estimate_key_size(key, policy)
253
+
254
+ predexp_size = estimate_predexp(policy.predexp)
255
+ field_count += 1 if predexp_size > 0
256
+
201
257
  read_attr = 0
202
258
  write_attr = 0
203
259
  read_header = false
260
+ record_bin_multiplicity = policy.record_bin_multiplicity == RecordBinMultiplicity::ARRAY
204
261
 
205
262
  operations.each do |operation|
206
263
  case operation.op_type
207
- when Aerospike::Operation::READ
208
- read_attr |= INFO1_READ
264
+ when Aerospike::Operation::READ, Aerospike::Operation::CDT_READ,
265
+ Aerospike::Operation::HLL_READ, Aerospike::Operation::BIT_READ
266
+ read_attr |= INFO1_READ
209
267
 
210
268
  # Read all bins if no bin is specified.
211
269
  read_attr |= INFO1_GET_ALL unless operation.bin_name
212
270
 
213
271
  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
272
+ # The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
273
+ # The workaround is to request a non-existent bin.
274
+ # TODO: Fix this on server.
275
+ # read_attr |= _INFO1_READ | _INFO1_NOBINDATA
276
+ read_attr |= INFO1_READ
219
277
  read_header = true
220
278
 
221
279
  else
222
280
  write_attr = INFO2_WRITE
223
281
  end
224
282
 
283
+ if [Aerospike::Operation::HLL_MODIFY, Aerospike::Operation::HLL_READ,
284
+ Aerospike::Operation::BIT_MODIFY, Aerospike::Operation::BIT_READ].include?(operation.op_type)
285
+ record_bin_multiplicity = true
286
+ end
287
+
225
288
  estimate_operation_size_for_operation(operation)
226
289
  end
227
290
  size_buffer
228
291
 
292
+
293
+ write_attr |= INFO2_RESPOND_ALL_OPS if write_attr != 0 && record_bin_multiplicity
294
+
229
295
  if write_attr != 0
230
296
  write_header_with_policy(policy, read_attr, write_attr, field_count, operations.length)
231
297
  else
232
298
  write_header(policy, read_attr, write_attr, field_count, operations.length)
233
299
  end
234
300
  write_key(key, policy)
301
+ write_predexp(policy.predexp, predexp_size)
235
302
 
236
303
  operations.each do |operation|
237
304
  write_operation_for_operation(operation)
@@ -240,11 +307,16 @@ module Aerospike
240
307
  write_operation_for_bin(nil, Aerospike::Operation::READ) if read_header
241
308
 
242
309
  end_cmd
310
+ mark_compressed(policy)
243
311
  end
244
312
 
245
313
  def set_udf(policy, key, package_name, function_name, args)
246
314
  begin_cmd
247
315
  field_count = estimate_key_size(key, policy)
316
+
317
+ predexp_size = estimate_predexp(policy.predexp)
318
+ field_count += 1 if predexp_size > 0
319
+
248
320
  arg_bytes = args.to_bytes
249
321
 
250
322
  field_count += estimate_udf_size(package_name, function_name, arg_bytes)
@@ -252,11 +324,13 @@ module Aerospike
252
324
 
253
325
  write_header(policy, 0, INFO2_WRITE, field_count, 0)
254
326
  write_key(key, policy)
327
+ write_predexp(policy.predexp, predexp_size)
255
328
  write_field_string(package_name, Aerospike::FieldType::UDF_PACKAGE_NAME)
256
329
  write_field_string(function_name, Aerospike::FieldType::UDF_FUNCTION)
257
330
  write_field_bytes(arg_bytes, Aerospike::FieldType::UDF_ARGLIST)
258
331
 
259
332
  end_cmd
333
+ mark_compressed(policy)
260
334
  end
261
335
 
262
336
  def set_scan(policy, namespace, set_name, bin_names)
@@ -273,6 +347,14 @@ module Aerospike
273
347
  @data_offset += set_name.bytesize + FIELD_HEADER_SIZE
274
348
  field_count += 1
275
349
  end
350
+
351
+ if policy.records_per_second > 0
352
+ @data_offset += 4 + FIELD_HEADER_SIZE
353
+ field_count += 1
354
+ end
355
+
356
+ predexp_size = estimate_predexp(policy.predexp)
357
+ field_count += 1 if predexp_size > 0
276
358
 
277
359
  # Estimate scan options size.
278
360
  @data_offset += 2 + FIELD_HEADER_SIZE
@@ -310,6 +392,12 @@ module Aerospike
310
392
  write_field_string(set_name, Aerospike::FieldType::TABLE)
311
393
  end
312
394
 
395
+ if policy.records_per_second > 0
396
+ write_field_int(policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND)
397
+ end
398
+
399
+ write_predexp(policy.predexp, predexp_size)
400
+
313
401
  write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
314
402
 
315
403
  priority = policy.priority & 0xFF
@@ -355,6 +443,7 @@ module Aerospike
355
443
  break if @policy.timeout > 0 && Time.now > limit
356
444
 
357
445
  begin
446
+ @node = get_node
358
447
  @conn = @node.get_connection(@policy.timeout)
359
448
  rescue => e
360
449
  # Socket connection error has occurred. Decrease health and retry.
@@ -498,9 +587,20 @@ module Aerospike
498
587
  @data_offset += OPERATION_HEADER_SIZE
499
588
  end
500
589
 
590
+ def estimate_predexp(predexp)
591
+ if predexp && predexp.size > 0
592
+ @data_offset += FIELD_HEADER_SIZE
593
+ sz = Aerospike::PredExp.estimate_size(predexp)
594
+ @data_offset += sz
595
+ return sz
596
+ end
597
+ return 0
598
+ end
599
+
501
600
  # Generic header write.
502
601
  def write_header(policy, read_attr, write_attr, field_count, operation_count)
503
602
  read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
603
+ read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
504
604
 
505
605
  # Write all header data except total size which must be written last.
506
606
  @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
@@ -550,6 +650,7 @@ module Aerospike
550
650
  info_attr |= INFO3_COMMIT_MASTER if policy.commit_level == Aerospike::CommitLevel::COMMIT_MASTER
551
651
  read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
552
652
  write_attr |= INFO2_DURABLE_DELETE if policy.durable_delete
653
+ read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
553
654
 
554
655
  # Write all header data except total size which must be written last.
555
656
  @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
@@ -681,21 +782,34 @@ module Aerospike
681
782
  @data_offset += len
682
783
  end
683
784
 
785
+ def write_field_int(i, ftype)
786
+ @data_buffer.write_int32(i, @data_offset+FIELD_HEADER_SIZE)
787
+ write_field_header(4, ftype)
788
+ @data_offset += 4
789
+ end
790
+
684
791
  def write_field_bytes(bytes, ftype)
685
792
  @data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
686
-
687
793
  write_field_header(bytes.bytesize, ftype)
688
794
  @data_offset += bytes.bytesize
689
795
  end
690
796
 
691
797
  def write_field_header(size, ftype)
692
- # Buffer.Int32ToBytes(size+1), @data_buffer, @data_offset
693
798
  @data_buffer.write_int32(size+1, @data_offset)
694
799
  @data_offset += 4
695
800
  @data_buffer.write_byte(ftype, @data_offset)
696
801
  @data_offset += 1
697
802
  end
698
803
 
804
+ def write_predexp(predexp, predexp_size)
805
+ if predexp && predexp.size > 0
806
+ write_field_header(predexp_size, Aerospike::FieldType::PREDEXP)
807
+ @data_offset = Aerospike::PredExp.write(
808
+ predexp, @data_buffer, @data_offset
809
+ )
810
+ end
811
+ end
812
+
699
813
  def begin_cmd
700
814
  @data_offset = MSG_TOTAL_HEADER_SIZE
701
815
  end
@@ -719,6 +833,48 @@ module Aerospike
719
833
  @data_buffer.write_int64(size, 0)
720
834
  end
721
835
 
836
+ def use_compression?
837
+ @compress
838
+ end
839
+
840
+ def compress_buffer
841
+ if @data_offset > COMPRESS_THRESHOLD
842
+ compressed = Zlib::deflate(@data_buffer.buf, Zlib::DEFAULT_COMPRESSION)
843
+
844
+ # write original size as header
845
+ proto_s = "%08d" % 0
846
+ proto_s[0, 8] = [@data_offset].pack('q>')
847
+ compressed.prepend(proto_s)
848
+
849
+ # write proto
850
+ proto = (compressed.size+8) | Integer(CL_MSG_VERSION << 56) | Integer(AS_MSG_TYPE << 48)
851
+ proto_s = "%08d" % 0
852
+ proto_s[0, 8] = [proto].pack('q>')
853
+ compressed.prepend(proto_s)
854
+
855
+ @data_buffer = Buffer.new(-1, compressed)
856
+ @data_offset = @data_buffer.size
857
+ end
858
+ end
859
+
860
+ # isCompressed returns the length of the compressed buffer.
861
+ # If the buffer is not compressed, the result will be -1
862
+ def compressed_size
863
+ # A number of these are commented out because we just don't care enough to read
864
+ # that section of the header. If we do care, uncomment and check!
865
+ proto = @data_buffer.read_int64(0)
866
+ size = proto & 0xFFFFFFFFFFFF
867
+ msg_type = (proto >> 48) & 0xFF
868
+
869
+ return nil if msg_type != AS_MSG_TYPE_COMPRESSED
870
+
871
+ size
872
+ end
873
+
874
+ def mark_compressed(policy)
875
+ @compress = policy.use_compression
876
+ end
877
+
722
878
  end # class
723
879
 
724
880
  end # module