aerospike 2.11.0 → 2.16.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 (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