aerospike 2.9.1 → 2.14.0

Sign up to get free protection for your applications and to get access to all the features.
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