aerospike 2.10.0 → 2.15.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +1 -1
  4. data/lib/aerospike.rb +20 -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/hll_operation.rb +200 -0
  9. data/lib/aerospike/cdt/hll_policy.rb +34 -0
  10. data/lib/aerospike/cdt/hll_write_flags.rb +53 -0
  11. data/lib/aerospike/cdt/list_operation.rb +1 -1
  12. data/lib/aerospike/cdt/map_operation.rb +1 -1
  13. data/lib/aerospike/cdt/map_order.rb +1 -1
  14. data/lib/aerospike/cdt/map_policy.rb +1 -1
  15. data/lib/aerospike/cdt/map_return_type.rb +1 -1
  16. data/lib/aerospike/cdt/map_write_mode.rb +1 -1
  17. data/lib/aerospike/client.rb +34 -18
  18. data/lib/aerospike/cluster.rb +139 -17
  19. data/lib/aerospike/cluster/partition.rb +1 -1
  20. data/lib/aerospike/cluster/partition_parser.rb +169 -0
  21. data/lib/aerospike/cluster/rack_parser.rb +117 -0
  22. data/lib/aerospike/command/admin_command.rb +1 -1
  23. data/lib/aerospike/command/batch_direct_command.rb +2 -1
  24. data/lib/aerospike/command/batch_direct_exists_command.rb +1 -1
  25. data/lib/aerospike/command/batch_direct_node.rb +3 -3
  26. data/lib/aerospike/command/batch_index_command.rb +11 -2
  27. data/lib/aerospike/command/batch_index_node.rb +2 -2
  28. data/lib/aerospike/command/batch_item.rb +1 -1
  29. data/lib/aerospike/command/command.rb +168 -11
  30. data/lib/aerospike/command/delete_command.rb +21 -5
  31. data/lib/aerospike/command/execute_command.rb +1 -1
  32. data/lib/aerospike/command/exists_command.rb +21 -5
  33. data/lib/aerospike/command/field_type.rb +3 -1
  34. data/lib/aerospike/command/multi_command.rb +55 -5
  35. data/lib/aerospike/command/operate_command.rb +6 -1
  36. data/lib/aerospike/command/read_command.rb +63 -20
  37. data/lib/aerospike/command/read_header_command.rb +18 -6
  38. data/lib/aerospike/command/roles.rb +1 -1
  39. data/lib/aerospike/command/single_command.rb +9 -3
  40. data/lib/aerospike/command/touch_command.rb +48 -4
  41. data/lib/aerospike/command/unsupported_particle_type_validator.rb +1 -1
  42. data/lib/aerospike/command/write_command.rb +13 -4
  43. data/lib/aerospike/connection/create.rb +1 -1
  44. data/lib/aerospike/features.rb +3 -1
  45. data/lib/aerospike/geo_json.rb +70 -1
  46. data/lib/aerospike/host.rb +1 -1
  47. data/lib/aerospike/info.rb +1 -1
  48. data/lib/aerospike/key.rb +1 -1
  49. data/lib/aerospike/language.rb +1 -1
  50. data/lib/aerospike/node.rb +21 -7
  51. data/lib/aerospike/node/rebalance.rb +50 -0
  52. data/lib/aerospike/node/refresh/info.rb +4 -1
  53. data/lib/aerospike/node/refresh/partitions.rb +6 -15
  54. data/lib/aerospike/node/refresh/racks.rb +47 -0
  55. data/lib/aerospike/node/refresh/reset.rb +1 -0
  56. data/lib/aerospike/node/verify/rebalance_generation.rb +43 -0
  57. data/lib/aerospike/node_validator.rb +4 -19
  58. data/lib/aerospike/operation.rb +8 -1
  59. data/lib/aerospike/policy/admin_policy.rb +1 -1
  60. data/lib/aerospike/policy/batch_policy.rb +1 -1
  61. data/lib/aerospike/policy/client_policy.rb +16 -1
  62. data/lib/aerospike/policy/commit_level.rb +1 -1
  63. data/lib/aerospike/policy/consistency_level.rb +1 -1
  64. data/lib/aerospike/policy/generation_policy.rb +1 -1
  65. data/lib/aerospike/policy/operate_policy.rb +1 -1
  66. data/lib/aerospike/policy/policy.rb +64 -2
  67. data/lib/aerospike/policy/priority.rb +1 -1
  68. data/lib/aerospike/policy/query_policy.rb +8 -1
  69. data/lib/aerospike/policy/record_bin_multiplicity.rb +1 -1
  70. data/lib/aerospike/policy/record_exists_action.rb +1 -1
  71. data/lib/aerospike/policy/replica.rb +45 -0
  72. data/lib/aerospike/policy/scan_policy.rb +8 -1
  73. data/lib/aerospike/policy/write_policy.rb +1 -1
  74. data/lib/aerospike/query/filter.rb +1 -1
  75. data/lib/aerospike/query/pred_exp.rb +192 -0
  76. data/lib/aerospike/query/pred_exp/and_or.rb +32 -0
  77. data/lib/aerospike/query/pred_exp/geo_json_value.rb +41 -0
  78. data/lib/aerospike/query/pred_exp/integer_value.rb +32 -0
  79. data/lib/aerospike/query/pred_exp/op.rb +27 -0
  80. data/lib/aerospike/query/pred_exp/regex.rb +32 -0
  81. data/lib/aerospike/query/pred_exp/regex_flags.rb +23 -0
  82. data/lib/aerospike/query/pred_exp/string_value.rb +29 -0
  83. data/lib/aerospike/query/query_command.rb +27 -1
  84. data/lib/aerospike/query/recordset.rb +5 -5
  85. data/lib/aerospike/query/scan_command.rb +1 -1
  86. data/lib/aerospike/query/statement.rb +12 -3
  87. data/lib/aerospike/query/stream_command.rb +1 -1
  88. data/lib/aerospike/record.rb +1 -1
  89. data/lib/aerospike/result_code.rb +26 -7
  90. data/lib/aerospike/socket/base.rb +4 -3
  91. data/lib/aerospike/task/execute_task.rb +1 -1
  92. data/lib/aerospike/task/index_task.rb +1 -1
  93. data/lib/aerospike/task/task.rb +1 -1
  94. data/lib/aerospike/task/udf_register_task.rb +1 -1
  95. data/lib/aerospike/task/udf_remove_task.rb +1 -1
  96. data/lib/aerospike/ttl.rb +1 -1
  97. data/lib/aerospike/udf.rb +1 -1
  98. data/lib/aerospike/user_role.rb +1 -1
  99. data/lib/aerospike/utils/buffer.rb +14 -4
  100. data/lib/aerospike/utils/packer.rb +1 -1
  101. data/lib/aerospike/utils/pool.rb +1 -1
  102. data/lib/aerospike/utils/unpacker.rb +7 -2
  103. data/lib/aerospike/value/particle_type.rb +2 -2
  104. data/lib/aerospike/value/value.rb +106 -29
  105. data/lib/aerospike/version.rb +1 -1
  106. metadata +22 -8
  107. data/lib/aerospike/cluster/partition_tokenizer_new.rb +0 -130
  108. 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,56 @@ 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
264
  when Aerospike::Operation::READ
208
- read_attr |= INFO1_READ
265
+ read_attr |= INFO1_READ
209
266
 
210
267
  # Read all bins if no bin is specified.
211
268
  read_attr |= INFO1_GET_ALL unless operation.bin_name
212
269
 
213
270
  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
271
+ # The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
272
+ # The workaround is to request a non-existent bin.
273
+ # TODO: Fix this on server.
274
+ # read_attr |= _INFO1_READ | _INFO1_NOBINDATA
275
+ read_attr |= INFO1_READ
219
276
  read_header = true
220
277
 
278
+ when Aerospike::Operation::CDT_READ,Aerospike::Operation::HLL_READ
279
+ read_attr |= INFO1_READ
280
+
221
281
  else
222
282
  write_attr = INFO2_WRITE
223
283
  end
224
284
 
285
+ if [Aerospike::Operation::HLL_MODIFY, Aerospike::Operation::HLL_READ].include?(operation.op_type)
286
+ record_bin_multiplicity = true
287
+ end
288
+
225
289
  estimate_operation_size_for_operation(operation)
226
290
  end
227
291
  size_buffer
228
292
 
293
+
294
+ write_attr |= INFO2_RESPOND_ALL_OPS if write_attr != 0 && record_bin_multiplicity
295
+
229
296
  if write_attr != 0
230
297
  write_header_with_policy(policy, read_attr, write_attr, field_count, operations.length)
231
298
  else
232
299
  write_header(policy, read_attr, write_attr, field_count, operations.length)
233
300
  end
234
301
  write_key(key, policy)
302
+ write_predexp(policy.predexp, predexp_size)
235
303
 
236
304
  operations.each do |operation|
237
305
  write_operation_for_operation(operation)
@@ -240,11 +308,16 @@ module Aerospike
240
308
  write_operation_for_bin(nil, Aerospike::Operation::READ) if read_header
241
309
 
242
310
  end_cmd
311
+ mark_compressed(policy)
243
312
  end
244
313
 
245
314
  def set_udf(policy, key, package_name, function_name, args)
246
315
  begin_cmd
247
316
  field_count = estimate_key_size(key, policy)
317
+
318
+ predexp_size = estimate_predexp(policy.predexp)
319
+ field_count += 1 if predexp_size > 0
320
+
248
321
  arg_bytes = args.to_bytes
249
322
 
250
323
  field_count += estimate_udf_size(package_name, function_name, arg_bytes)
@@ -252,11 +325,13 @@ module Aerospike
252
325
 
253
326
  write_header(policy, 0, INFO2_WRITE, field_count, 0)
254
327
  write_key(key, policy)
328
+ write_predexp(policy.predexp, predexp_size)
255
329
  write_field_string(package_name, Aerospike::FieldType::UDF_PACKAGE_NAME)
256
330
  write_field_string(function_name, Aerospike::FieldType::UDF_FUNCTION)
257
331
  write_field_bytes(arg_bytes, Aerospike::FieldType::UDF_ARGLIST)
258
332
 
259
333
  end_cmd
334
+ mark_compressed(policy)
260
335
  end
261
336
 
262
337
  def set_scan(policy, namespace, set_name, bin_names)
@@ -273,6 +348,14 @@ module Aerospike
273
348
  @data_offset += set_name.bytesize + FIELD_HEADER_SIZE
274
349
  field_count += 1
275
350
  end
351
+
352
+ if policy.records_per_second > 0
353
+ @data_offset += 4 + FIELD_HEADER_SIZE
354
+ field_count += 1
355
+ end
356
+
357
+ predexp_size = estimate_predexp(policy.predexp)
358
+ field_count += 1 if predexp_size > 0
276
359
 
277
360
  # Estimate scan options size.
278
361
  @data_offset += 2 + FIELD_HEADER_SIZE
@@ -310,6 +393,12 @@ module Aerospike
310
393
  write_field_string(set_name, Aerospike::FieldType::TABLE)
311
394
  end
312
395
 
396
+ if policy.records_per_second > 0
397
+ write_field_int(policy.records_per_second, Aerospike::FieldType::RECORDS_PER_SECOND)
398
+ end
399
+
400
+ write_predexp(policy.predexp, predexp_size)
401
+
313
402
  write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
314
403
 
315
404
  priority = policy.priority & 0xFF
@@ -355,6 +444,7 @@ module Aerospike
355
444
  break if @policy.timeout > 0 && Time.now > limit
356
445
 
357
446
  begin
447
+ @node = get_node
358
448
  @conn = @node.get_connection(@policy.timeout)
359
449
  rescue => e
360
450
  # Socket connection error has occurred. Decrease health and retry.
@@ -498,9 +588,20 @@ module Aerospike
498
588
  @data_offset += OPERATION_HEADER_SIZE
499
589
  end
500
590
 
591
+ def estimate_predexp(predexp)
592
+ if predexp && predexp.size > 0
593
+ @data_offset += FIELD_HEADER_SIZE
594
+ sz = Aerospike::PredExp.estimate_size(predexp)
595
+ @data_offset += sz
596
+ return sz
597
+ end
598
+ return 0
599
+ end
600
+
501
601
  # Generic header write.
502
602
  def write_header(policy, read_attr, write_attr, field_count, operation_count)
503
603
  read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
604
+ read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
504
605
 
505
606
  # Write all header data except total size which must be written last.
506
607
  @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
@@ -550,6 +651,7 @@ module Aerospike
550
651
  info_attr |= INFO3_COMMIT_MASTER if policy.commit_level == Aerospike::CommitLevel::COMMIT_MASTER
551
652
  read_attr |= INFO1_CONSISTENCY_ALL if policy.consistency_level == Aerospike::ConsistencyLevel::CONSISTENCY_ALL
552
653
  write_attr |= INFO2_DURABLE_DELETE if policy.durable_delete
654
+ read_attr |= INFO1_COMPRESS_RESPONSE if policy.use_compression
553
655
 
554
656
  # Write all header data except total size which must be written last.
555
657
  @data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
@@ -681,21 +783,34 @@ module Aerospike
681
783
  @data_offset += len
682
784
  end
683
785
 
786
+ def write_field_int(i, ftype)
787
+ @data_buffer.write_int32(i, @data_offset+FIELD_HEADER_SIZE)
788
+ write_field_header(4, ftype)
789
+ @data_offset += 4
790
+ end
791
+
684
792
  def write_field_bytes(bytes, ftype)
685
793
  @data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
686
-
687
794
  write_field_header(bytes.bytesize, ftype)
688
795
  @data_offset += bytes.bytesize
689
796
  end
690
797
 
691
798
  def write_field_header(size, ftype)
692
- # Buffer.Int32ToBytes(size+1), @data_buffer, @data_offset
693
799
  @data_buffer.write_int32(size+1, @data_offset)
694
800
  @data_offset += 4
695
801
  @data_buffer.write_byte(ftype, @data_offset)
696
802
  @data_offset += 1
697
803
  end
698
804
 
805
+ def write_predexp(predexp, predexp_size)
806
+ if predexp && predexp.size > 0
807
+ write_field_header(predexp_size, Aerospike::FieldType::PREDEXP)
808
+ @data_offset = Aerospike::PredExp.write(
809
+ predexp, @data_buffer, @data_offset
810
+ )
811
+ end
812
+ end
813
+
699
814
  def begin_cmd
700
815
  @data_offset = MSG_TOTAL_HEADER_SIZE
701
816
  end
@@ -719,6 +834,48 @@ module Aerospike
719
834
  @data_buffer.write_int64(size, 0)
720
835
  end
721
836
 
837
+ def use_compression?
838
+ @compress
839
+ end
840
+
841
+ def compress_buffer
842
+ if @data_offset > COMPRESS_THRESHOLD
843
+ compressed = Zlib::deflate(@data_buffer.buf, Zlib::DEFAULT_COMPRESSION)
844
+
845
+ # write original size as header
846
+ proto_s = "%08d" % 0
847
+ proto_s[0, 8] = [@data_offset].pack('q>')
848
+ compressed.prepend(proto_s)
849
+
850
+ # write proto
851
+ proto = (compressed.size+8) | Integer(CL_MSG_VERSION << 56) | Integer(AS_MSG_TYPE << 48)
852
+ proto_s = "%08d" % 0
853
+ proto_s[0, 8] = [proto].pack('q>')
854
+ compressed.prepend(proto_s)
855
+
856
+ @data_buffer = Buffer.new(-1, compressed)
857
+ @data_offset = @data_buffer.size
858
+ end
859
+ end
860
+
861
+ # isCompressed returns the length of the compressed buffer.
862
+ # If the buffer is not compressed, the result will be -1
863
+ def compressed_size
864
+ # A number of these are commented out because we just don't care enough to read
865
+ # that section of the header. If we do care, uncomment and check!
866
+ proto = @data_buffer.read_int64(0)
867
+ size = proto & 0xFFFFFFFFFFFF
868
+ msg_type = (proto >> 48) & 0xFF
869
+
870
+ return nil if msg_type != AS_MSG_TYPE_COMPRESSED
871
+
872
+ size
873
+ end
874
+
875
+ def mark_compressed(policy)
876
+ @compress = policy.use_compression
877
+ end
878
+
722
879
  end # class
723
880
 
724
881
  end # module