aerospike 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +0 -0
- data/LICENSE +203 -0
- data/README.md +123 -0
- data/lib/aerospike.rb +69 -0
- data/lib/aerospike/aerospike_exception.rb +111 -0
- data/lib/aerospike/bin.rb +46 -0
- data/lib/aerospike/client.rb +649 -0
- data/lib/aerospike/cluster/cluster.rb +537 -0
- data/lib/aerospike/cluster/connection.rb +113 -0
- data/lib/aerospike/cluster/node.rb +248 -0
- data/lib/aerospike/cluster/node_validator.rb +85 -0
- data/lib/aerospike/cluster/partition.rb +54 -0
- data/lib/aerospike/cluster/partition_tokenizer_new.rb +128 -0
- data/lib/aerospike/cluster/partition_tokenizer_old.rb +135 -0
- data/lib/aerospike/command/batch_command.rb +120 -0
- data/lib/aerospike/command/batch_command_exists.rb +93 -0
- data/lib/aerospike/command/batch_command_get.rb +150 -0
- data/lib/aerospike/command/batch_item.rb +69 -0
- data/lib/aerospike/command/batch_node.rb +82 -0
- data/lib/aerospike/command/command.rb +680 -0
- data/lib/aerospike/command/delete_command.rb +57 -0
- data/lib/aerospike/command/execute_command.rb +42 -0
- data/lib/aerospike/command/exists_command.rb +57 -0
- data/lib/aerospike/command/field_type.rb +44 -0
- data/lib/aerospike/command/operate_command.rb +37 -0
- data/lib/aerospike/command/read_command.rb +174 -0
- data/lib/aerospike/command/read_header_command.rb +63 -0
- data/lib/aerospike/command/single_command.rb +60 -0
- data/lib/aerospike/command/touch_command.rb +50 -0
- data/lib/aerospike/command/write_command.rb +60 -0
- data/lib/aerospike/host.rb +43 -0
- data/lib/aerospike/info.rb +96 -0
- data/lib/aerospike/key.rb +99 -0
- data/lib/aerospike/language.rb +25 -0
- data/lib/aerospike/ldt/large.rb +69 -0
- data/lib/aerospike/ldt/large_list.rb +100 -0
- data/lib/aerospike/ldt/large_map.rb +82 -0
- data/lib/aerospike/ldt/large_set.rb +78 -0
- data/lib/aerospike/ldt/large_stack.rb +72 -0
- data/lib/aerospike/loggable.rb +55 -0
- data/lib/aerospike/operation.rb +70 -0
- data/lib/aerospike/policy/client_policy.rb +37 -0
- data/lib/aerospike/policy/generation_policy.rb +37 -0
- data/lib/aerospike/policy/policy.rb +54 -0
- data/lib/aerospike/policy/priority.rb +34 -0
- data/lib/aerospike/policy/record_exists_action.rb +45 -0
- data/lib/aerospike/policy/write_policy.rb +61 -0
- data/lib/aerospike/record.rb +42 -0
- data/lib/aerospike/result_code.rb +353 -0
- data/lib/aerospike/task/index_task.rb +59 -0
- data/lib/aerospike/task/task.rb +71 -0
- data/lib/aerospike/task/udf_register_task.rb +55 -0
- data/lib/aerospike/task/udf_remove_task.rb +55 -0
- data/lib/aerospike/udf.rb +24 -0
- data/lib/aerospike/utils/buffer.rb +139 -0
- data/lib/aerospike/utils/epoc.rb +28 -0
- data/lib/aerospike/utils/pool.rb +65 -0
- data/lib/aerospike/value/particle_type.rb +45 -0
- data/lib/aerospike/value/value.rb +380 -0
- data/lib/aerospike/version.rb +4 -0
- metadata +132 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
5
|
+
# license agreements.
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
9
|
+
# the License at http:#www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
13
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
14
|
+
# License for the specific language governing permissions and limitations under
|
15
|
+
# the License.
|
16
|
+
|
17
|
+
require 'aerospike/command/batch_command'
|
18
|
+
|
19
|
+
module Aerospike
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
class BatchCommandGet < BatchCommand
|
24
|
+
|
25
|
+
def initialize(node, batch_namespace, policy, key_map, bin_names, records, read_attr)
|
26
|
+
super(node)
|
27
|
+
|
28
|
+
@batch_namespace = batch_namespace
|
29
|
+
@policy = policy
|
30
|
+
@key_map = key_map
|
31
|
+
@bin_names = bin_names
|
32
|
+
@records = records
|
33
|
+
@read_attr = read_attr
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_buffer
|
37
|
+
set_batch_get(@batch_namespace, @bin_names, @read_attr)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Parse all results in the batch. Add records to shared list.
|
41
|
+
# If the record was not found, the bins will be nil.
|
42
|
+
def parse_record_results(receive_size)
|
43
|
+
#Parse each message response and add it to the result array
|
44
|
+
@data_offset = 0
|
45
|
+
|
46
|
+
while @data_offset < receive_size
|
47
|
+
read_bytes(MSG_REMAINING_HEADER_SIZE)
|
48
|
+
result_code = @data_buffer.read(5).ord & 0xFF
|
49
|
+
|
50
|
+
# The only valid server return codes are "ok" and "not found".
|
51
|
+
# If other return codes are received, then abort the batch.
|
52
|
+
if result_code != 0 && result_code != Aerospike::ResultCode::KEY_NOT_FOUND_ERROR
|
53
|
+
raise Aerospike::Exceptions::Aerospike.new(result_code)
|
54
|
+
end
|
55
|
+
|
56
|
+
info3 = @data_buffer.read(3).ord
|
57
|
+
|
58
|
+
# If cmd is the end marker of the response, do not proceed further
|
59
|
+
return false if (info3 & INFO3_LAST) == INFO3_LAST
|
60
|
+
|
61
|
+
generation = @data_buffer.read_int32(6).ord
|
62
|
+
expiration = @data_buffer.read_int32(10).ord
|
63
|
+
field_count = @data_buffer.read_int16(18).ord
|
64
|
+
op_count = @data_buffer.read_int16(20).ord
|
65
|
+
key = parse_key(field_count)
|
66
|
+
item = @key_map[key.digest]
|
67
|
+
|
68
|
+
if item
|
69
|
+
if result_code == 0
|
70
|
+
index = item.index
|
71
|
+
@records[index] = parse_record(key, op_count, generation, expiration)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
Aerospike.logger.debug("Unexpected batch key returned: #{key.namespace}, #{key.digest}")
|
75
|
+
end
|
76
|
+
|
77
|
+
end # while
|
78
|
+
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
# Parses the given byte buffer and populate the result object.
|
83
|
+
# Returns the number of bytes that were parsed from the given buffer.
|
84
|
+
def parse_record(key, op_count, generation, expiration)
|
85
|
+
bins = nil
|
86
|
+
duplicates = nil
|
87
|
+
|
88
|
+
for i in 0...op_count
|
89
|
+
raise Aerospike::Exceptions::QueryTerminated.new unless valid?
|
90
|
+
|
91
|
+
read_bytes(8)
|
92
|
+
|
93
|
+
op_size = @data_buffer.read_int32(0).ord
|
94
|
+
particle_type = @data_buffer.read(5).ord
|
95
|
+
version = @data_buffer.read(6).ord
|
96
|
+
name_size = @data_buffer.read(7).ord
|
97
|
+
|
98
|
+
read_bytes(name_size)
|
99
|
+
name = @data_buffer.read(0, name_size).force_encoding('utf-8')
|
100
|
+
|
101
|
+
particle_bytes_size = op_size - (4 + name_size)
|
102
|
+
read_bytes(particle_bytes_size)
|
103
|
+
value = Aerospike.bytes_to_particle(particle_type, @data_buffer, 0, particle_bytes_size)
|
104
|
+
|
105
|
+
# Currently, the batch command returns all the bins even if a subset of
|
106
|
+
# the bins are requested. We have to filter it on the client side.
|
107
|
+
# TODO: Filter batch bins on server!
|
108
|
+
if !@bin_names || @bin_names.any?{|bn| bn == name}
|
109
|
+
vmap = nil
|
110
|
+
|
111
|
+
if version > 0 || duplicates
|
112
|
+
unless duplicates
|
113
|
+
duplicates = []
|
114
|
+
duplicates << bins
|
115
|
+
bins = nil
|
116
|
+
|
117
|
+
for j in 0...version
|
118
|
+
duplicates << nil
|
119
|
+
end
|
120
|
+
else
|
121
|
+
for j in duplicates.length..version
|
122
|
+
duplicates << nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
vmap = duplicates[version]
|
127
|
+
unless vmap
|
128
|
+
vmap = {}
|
129
|
+
duplicates[version] = vmap
|
130
|
+
end
|
131
|
+
else
|
132
|
+
unless bins
|
133
|
+
bins = {}
|
134
|
+
end
|
135
|
+
vmap = bins
|
136
|
+
end
|
137
|
+
vmap[name] = value
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Remove nil duplicates just in case there were holes in the version number space.
|
142
|
+
# TODO: this seems to be a bad idea; O(n) algorithm after another O(n) algorithm
|
143
|
+
duplicates.compact! if duplicates
|
144
|
+
|
145
|
+
Record.new(@node, key, bins, duplicates, generation, expiration)
|
146
|
+
end
|
147
|
+
|
148
|
+
end # class
|
149
|
+
|
150
|
+
end # module
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
5
|
+
# license agreements.
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
9
|
+
# the License at http:#www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
13
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
14
|
+
# License for the specific language governing permissions and limitations under
|
15
|
+
# the License.
|
16
|
+
|
17
|
+
require 'thread'
|
18
|
+
|
19
|
+
require 'aerospike/record'
|
20
|
+
|
21
|
+
require 'aerospike/command/command'
|
22
|
+
|
23
|
+
module Aerospike
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
class BatchItem
|
28
|
+
|
29
|
+
def self.generate_map(keys)
|
30
|
+
key_map = {}
|
31
|
+
keys.each_with_index do |key, i|
|
32
|
+
item = key_map[key.digest]
|
33
|
+
unless item
|
34
|
+
item = BatchItem.new(i)
|
35
|
+
key_map[key.digest] = item
|
36
|
+
else
|
37
|
+
item.add_duplicate(i)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
key_map
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def initialize(index)
|
46
|
+
@index = index
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_duplicate(idx)
|
50
|
+
unless @duplicates
|
51
|
+
@duplicates = []
|
52
|
+
@duplicates << @index
|
53
|
+
@index = 0
|
54
|
+
end
|
55
|
+
|
56
|
+
@duplicates << idx
|
57
|
+
end
|
58
|
+
|
59
|
+
def index
|
60
|
+
return @index unless @duplicates
|
61
|
+
|
62
|
+
r = @duplicates[@index]
|
63
|
+
@index+=1
|
64
|
+
return r
|
65
|
+
end
|
66
|
+
|
67
|
+
end # class
|
68
|
+
|
69
|
+
end # module
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
5
|
+
# license agreements.
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
9
|
+
# the License at http:#www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
13
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
14
|
+
# License for the specific language governing permissions and limitations under
|
15
|
+
# the License.
|
16
|
+
|
17
|
+
require 'thread'
|
18
|
+
|
19
|
+
require 'aerospike/record'
|
20
|
+
|
21
|
+
require 'aerospike/command/command'
|
22
|
+
|
23
|
+
module Aerospike
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
BatchNamespace = Struct.new :namespace, :keys
|
28
|
+
|
29
|
+
class BatchNode
|
30
|
+
|
31
|
+
attr_accessor :node, :batch_namespaces, :key_capacity
|
32
|
+
|
33
|
+
def self.generate_list(cluster, keys)
|
34
|
+
nodes = cluster.nodes
|
35
|
+
|
36
|
+
if nodes.length == 0
|
37
|
+
raise Aerospike::Exceptions::Connection.new("command failed because cluster is empty.")
|
38
|
+
end
|
39
|
+
|
40
|
+
node_count = nodes.length
|
41
|
+
keys_per_node = (keys.length/node_count).to_i + 10
|
42
|
+
|
43
|
+
# Split keys by server node.
|
44
|
+
batch_nodes = []
|
45
|
+
|
46
|
+
keys.each do |key|
|
47
|
+
partition = Partition.new_by_key(key)
|
48
|
+
|
49
|
+
# error not required
|
50
|
+
node = cluster.get_node(partition)
|
51
|
+
batch_node = batch_nodes.detect{|bn| bn.node == node}
|
52
|
+
|
53
|
+
unless batch_node
|
54
|
+
batch_nodes << BatchNode.new(node, keys_per_node, key)
|
55
|
+
else
|
56
|
+
batch_node.add_key(key)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
batch_nodes
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def initialize(node, key_capacity, key)
|
65
|
+
@node = node
|
66
|
+
@key_capacity = key_capacity
|
67
|
+
@batch_namespaces = [BatchNamespace.new(key.namespace, [key])]
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_key(key)
|
71
|
+
batch_namespace = @batch_namespaces.detect{|bn| bn.namespace == key.namespace }
|
72
|
+
|
73
|
+
unless batch_namespace
|
74
|
+
@batch_namespaces << BatchNamespace.new(key.namespace, [key])
|
75
|
+
else
|
76
|
+
batch_namespace.keys << key
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end # class
|
81
|
+
|
82
|
+
end # module
|
@@ -0,0 +1,680 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Portions may be licensed to Aerospike, Inc. under one or more contributor
|
5
|
+
# license agreements.
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
8
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
9
|
+
# the License at http:#www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
13
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
14
|
+
# License for the specific language governing permissions and limitations under
|
15
|
+
# the License.
|
16
|
+
|
17
|
+
require 'time'
|
18
|
+
|
19
|
+
|
20
|
+
require 'msgpack'
|
21
|
+
require 'aerospike/result_code'
|
22
|
+
require 'aerospike/command/field_type'
|
23
|
+
|
24
|
+
module Aerospike
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Flags commented out are not supported by cmd client.
|
29
|
+
# Contains a read operation.
|
30
|
+
INFO1_READ = Integer(1 << 0)
|
31
|
+
# Get all bins.
|
32
|
+
INFO1_GET_ALL = Integer(1 << 1)
|
33
|
+
|
34
|
+
# Do not read the bins
|
35
|
+
INFO1_NOBINDATA = Integer(1 << 5)
|
36
|
+
|
37
|
+
# Create or update record
|
38
|
+
INFO2_WRITE = Integer(1 << 0)
|
39
|
+
# Fling a record into the belly of Moloch.
|
40
|
+
INFO2_DELETE = Integer(1 << 1)
|
41
|
+
# Update if expected generation == old.
|
42
|
+
INFO2_GENERATION = Integer(1 << 2)
|
43
|
+
# Update if new generation >= old, good for restore.
|
44
|
+
INFO2_GENERATION_GT = Integer(1 << 3)
|
45
|
+
# Create a duplicate on a generation collision.
|
46
|
+
INFO2_GENERATION_DUP = Integer(1 << 4)
|
47
|
+
# Create only. Fail if record already exists.
|
48
|
+
INFO2_CREATE_ONLY = Integer(1 << 5)
|
49
|
+
|
50
|
+
# This is the last of a multi-part message.
|
51
|
+
INFO3_LAST = Integer(1 << 0)
|
52
|
+
# Update only. Merge bins.
|
53
|
+
INFO3_UPDATE_ONLY = Integer(1 << 3)
|
54
|
+
|
55
|
+
# Create or completely replace record.
|
56
|
+
INFO3_CREATE_OR_REPLACE = Integer(1 << 4)
|
57
|
+
# Completely replace existing record only.
|
58
|
+
INFO3_REPLACE_ONLY = Integer(1 << 5)
|
59
|
+
|
60
|
+
MSG_TOTAL_HEADER_SIZE = 30
|
61
|
+
FIELD_HEADER_SIZE = 5
|
62
|
+
OPERATION_HEADER_SIZE = 8
|
63
|
+
MSG_REMAINING_HEADER_SIZE = 22
|
64
|
+
DIGEST_SIZE = 20
|
65
|
+
CL_MSG_VERSION = 2
|
66
|
+
AS_MSG_TYPE = 3
|
67
|
+
|
68
|
+
class Command
|
69
|
+
|
70
|
+
def initialize(node)
|
71
|
+
@node = node
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Writes the command for write operations
|
77
|
+
def set_write(policy, operation, key, bins)
|
78
|
+
begin_cmd
|
79
|
+
field_count = estimate_key_size(key)
|
80
|
+
|
81
|
+
if policy.send_key
|
82
|
+
# field header size + key size
|
83
|
+
@data_offset += key.user_key_as_value.estimate_size + FIELD_HEADER_SIZE
|
84
|
+
field_count += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
bins.each do |bin|
|
88
|
+
estimate_operation_size_for_bin(bin)
|
89
|
+
end
|
90
|
+
|
91
|
+
size_buffer
|
92
|
+
|
93
|
+
write_header_with_policy(policy, 0, INFO2_WRITE, field_count, bins.length)
|
94
|
+
write_key(key)
|
95
|
+
|
96
|
+
if policy.send_key
|
97
|
+
write_field_value(key.user_key_as_value, Aerospike::FieldType::KEY)
|
98
|
+
end
|
99
|
+
|
100
|
+
bins.each do |bin|
|
101
|
+
write_operation_for_bin(bin, operation)
|
102
|
+
end
|
103
|
+
|
104
|
+
end_cmd
|
105
|
+
end
|
106
|
+
|
107
|
+
# Writes the command for delete operations
|
108
|
+
def set_delete(policy, key)
|
109
|
+
begin_cmd
|
110
|
+
field_count = estimate_key_size(key)
|
111
|
+
size_buffer
|
112
|
+
write_header_with_policy(policy, 0, INFO2_WRITE|INFO2_DELETE, field_count, 0)
|
113
|
+
write_key(key)
|
114
|
+
end_cmd
|
115
|
+
end
|
116
|
+
|
117
|
+
# Writes the command for touch operations
|
118
|
+
def set_touch(policy, key)
|
119
|
+
begin_cmd
|
120
|
+
field_count = estimate_key_size(key)
|
121
|
+
estimate_operation_size
|
122
|
+
size_buffer
|
123
|
+
write_header_with_policy(policy, 0, INFO2_WRITE, field_count, 1)
|
124
|
+
write_key(key)
|
125
|
+
write_operation_for_operation_type(Aerospike::Operation::TOUCH)
|
126
|
+
end_cmd
|
127
|
+
end
|
128
|
+
|
129
|
+
# Writes the command for exist operations
|
130
|
+
def set_exists(key)
|
131
|
+
begin_cmd
|
132
|
+
field_count = estimate_key_size(key)
|
133
|
+
size_buffer
|
134
|
+
write_header(INFO1_READ|INFO1_NOBINDATA, 0, field_count, 0)
|
135
|
+
write_key(key)
|
136
|
+
end_cmd
|
137
|
+
end
|
138
|
+
|
139
|
+
# Writes the command for get operations (all bins)
|
140
|
+
def set_read_for_key_only(key)
|
141
|
+
begin_cmd
|
142
|
+
field_count = estimate_key_size(key)
|
143
|
+
size_buffer
|
144
|
+
write_header(INFO1_READ|INFO1_GET_ALL, 0, field_count, 0)
|
145
|
+
write_key(key)
|
146
|
+
end_cmd
|
147
|
+
end
|
148
|
+
|
149
|
+
# Writes the command for get operations (specified bins)
|
150
|
+
def set_read(key, bin_names)
|
151
|
+
if bin_names && bin_names.length > 0
|
152
|
+
begin_cmd
|
153
|
+
field_count = estimate_key_size(key)
|
154
|
+
|
155
|
+
bin_names.each do |bin_name|
|
156
|
+
estimate_operation_size_for_bin_name(bin_name)
|
157
|
+
end
|
158
|
+
|
159
|
+
size_buffer
|
160
|
+
write_header(INFO1_READ, 0, field_count, bin_names.length)
|
161
|
+
write_key(key)
|
162
|
+
|
163
|
+
bin_names.each do |bin_name|
|
164
|
+
write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
|
165
|
+
end
|
166
|
+
|
167
|
+
end_cmd
|
168
|
+
else
|
169
|
+
set_read_for_key_only(key)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Writes the command for getting metadata operations
|
174
|
+
def set_read_header(key)
|
175
|
+
begin_cmd
|
176
|
+
field_count = estimate_key_size(key)
|
177
|
+
estimate_operation_size_for_bin_name('')
|
178
|
+
size_buffer
|
179
|
+
|
180
|
+
# The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
|
181
|
+
# The workaround is to request a non-existent bin.
|
182
|
+
# TODO: Fix this on server.
|
183
|
+
#command.set_read(INFO1_READ | _INFO1_NOBINDATA);
|
184
|
+
write_header(INFO1_READ, 0, field_count, 1)
|
185
|
+
|
186
|
+
write_key(key)
|
187
|
+
write_operation_for_bin_name('', Aerospike::Operation::READ)
|
188
|
+
end_cmd
|
189
|
+
end
|
190
|
+
|
191
|
+
# Implements different command operations
|
192
|
+
def set_operate(policy, key, operations)
|
193
|
+
begin_cmd
|
194
|
+
field_count = estimate_key_size(key)
|
195
|
+
read_attr = 0
|
196
|
+
write_attr = 0
|
197
|
+
read_header = false
|
198
|
+
|
199
|
+
operations.each do |operation|
|
200
|
+
case operation.op_type
|
201
|
+
when Aerospike::Operation::READ
|
202
|
+
read_attr |= INFO1_READ
|
203
|
+
|
204
|
+
# Read all bins if no bin is specified.
|
205
|
+
read_attr |= INFO1_GET_ALL unless operation.bin_name
|
206
|
+
|
207
|
+
when Aerospike::Operation::READ_HEADER
|
208
|
+
# The server does not currently return record header data with _INFO1_NOBINDATA attribute set.
|
209
|
+
# The workaround is to request a non-existent bin.
|
210
|
+
# TODO: Fix this on server.
|
211
|
+
# read_attr |= _INFO1_READ | _INFO1_NOBINDATA
|
212
|
+
read_attr |= INFO1_READ
|
213
|
+
read_header = true
|
214
|
+
|
215
|
+
else
|
216
|
+
write_attr = INFO2_WRITE
|
217
|
+
end
|
218
|
+
|
219
|
+
estimate_operation_size_for_operation(operation)
|
220
|
+
end
|
221
|
+
size_buffer
|
222
|
+
|
223
|
+
if write_attr != 0
|
224
|
+
write_header_with_policy(policy, read_attr, write_attr, field_count, operations.length)
|
225
|
+
else
|
226
|
+
write_header(read_attr, write_attr, field_count, operations.length)
|
227
|
+
end
|
228
|
+
write_key(key)
|
229
|
+
|
230
|
+
operations.each do |operation|
|
231
|
+
write_operation_for_operation(operation)
|
232
|
+
end
|
233
|
+
|
234
|
+
write_operation_for_bin(nil, Aerospike::Operation::READ) if read_header
|
235
|
+
|
236
|
+
end_cmd
|
237
|
+
end
|
238
|
+
|
239
|
+
def set_udf(key, package_name, function_name, args)
|
240
|
+
begin_cmd
|
241
|
+
field_count = estimate_key_size(key)
|
242
|
+
arg_bytes = args.to_bytes
|
243
|
+
|
244
|
+
field_count += estimate_udf_size(package_name, function_name, arg_bytes)
|
245
|
+
size_buffer
|
246
|
+
|
247
|
+
write_header(0, INFO2_WRITE, field_count, 0)
|
248
|
+
write_key(key)
|
249
|
+
write_field_string(package_name, Aerospike::FieldType::UDF_PACKAGE_NAME)
|
250
|
+
write_field_string(function_name, Aerospike::FieldType::UDF_FUNCTION)
|
251
|
+
write_field_bytes(arg_bytes, Aerospike::FieldType::UDF_ARGLIST)
|
252
|
+
|
253
|
+
end_cmd
|
254
|
+
end
|
255
|
+
|
256
|
+
def set_batch_exists(batch_namespace)
|
257
|
+
# Estimate buffer size
|
258
|
+
begin_cmd
|
259
|
+
keys = batch_namespace.keys
|
260
|
+
byte_size = keys.length * DIGEST_SIZE
|
261
|
+
|
262
|
+
@data_offset += (batch_namespace ? batch_namespace.namespace.bytesize : 0) +
|
263
|
+
FIELD_HEADER_SIZE + byte_size + FIELD_HEADER_SIZE
|
264
|
+
|
265
|
+
size_buffer
|
266
|
+
|
267
|
+
write_header(INFO1_READ|INFO1_NOBINDATA, 0, 2, 0)
|
268
|
+
write_field_string(batch_namespace.namespace, Aerospike::FieldType::NAMESPACE)
|
269
|
+
write_field_header(byte_size, Aerospike::FieldType::DIGEST_RIPE_ARRAY)
|
270
|
+
|
271
|
+
keys.each do |key|
|
272
|
+
@data_buffer.write_binary(key.digest, @data_offset)
|
273
|
+
@data_offset += key.digest.bytesize
|
274
|
+
end
|
275
|
+
end_cmd
|
276
|
+
end
|
277
|
+
|
278
|
+
def set_batch_get(batch_namespace, bin_names, read_attr)
|
279
|
+
# Estimate buffer size
|
280
|
+
begin_cmd
|
281
|
+
byte_size = batch_namespace.keys.length * DIGEST_SIZE
|
282
|
+
|
283
|
+
@data_offset += batch_namespace.namespace.bytesize +
|
284
|
+
FIELD_HEADER_SIZE + byte_size + FIELD_HEADER_SIZE
|
285
|
+
|
286
|
+
if bin_names
|
287
|
+
bin_names.each do |bin_name|
|
288
|
+
estimate_operation_size_for_bin_name(bin_name)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
size_buffer
|
293
|
+
|
294
|
+
operation_count = 0
|
295
|
+
if bin_names
|
296
|
+
operation_count = bin_names.length
|
297
|
+
end
|
298
|
+
|
299
|
+
write_header(read_attr, 0, 2, operation_count)
|
300
|
+
write_field_string(batch_namespace.namespace, Aerospike::FieldType::NAMESPACE)
|
301
|
+
write_field_header(byte_size, Aerospike::FieldType::DIGEST_RIPE_ARRAY)
|
302
|
+
|
303
|
+
batch_namespace.keys.each do |key|
|
304
|
+
@data_buffer.write_binary(key.digest, @data_offset)
|
305
|
+
@data_offset += key.digest.bytesize
|
306
|
+
end
|
307
|
+
|
308
|
+
if bin_names
|
309
|
+
bin_names.each do |bin_name|
|
310
|
+
write_operation_for_bin_name(bin_name, Aerospike::Operation::READ)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
end_cmd
|
315
|
+
end
|
316
|
+
|
317
|
+
def execute
|
318
|
+
iterations = 0
|
319
|
+
|
320
|
+
# set timeout outside the loop
|
321
|
+
limit = Time.now + @policy.timeout
|
322
|
+
|
323
|
+
# Execute command until successful, timed out or maximum iterations have been reached.
|
324
|
+
while true
|
325
|
+
# too many retries
|
326
|
+
iterations += 1
|
327
|
+
break if (@policy.max_retries > 0) && (iterations > @policy.max_retries+1)
|
328
|
+
|
329
|
+
# Sleep before trying again, after the first iteration
|
330
|
+
sleep(@policy.sleep_between_retries) if iterations > 1 && @policy.sleep_between_retries > 0
|
331
|
+
|
332
|
+
# check for command timeout
|
333
|
+
break if @policy.timeout > 0 && Time.now > limit
|
334
|
+
|
335
|
+
begin
|
336
|
+
@conn = @node.get_connection(@policy.timeout)
|
337
|
+
rescue => e
|
338
|
+
# Socket connection error has occurred. Decrease health and retry.
|
339
|
+
@node.decrease_health
|
340
|
+
|
341
|
+
Aerospike.logger.warn("Node #{@node.to_s}: #{e}")
|
342
|
+
next
|
343
|
+
end
|
344
|
+
|
345
|
+
# Draw a buffer from buffer pool, and make sure it will be put back
|
346
|
+
begin
|
347
|
+
@data_buffer = Buffer.get
|
348
|
+
|
349
|
+
# Set command buffer.
|
350
|
+
begin
|
351
|
+
write_buffer
|
352
|
+
rescue => e
|
353
|
+
# All runtime exceptions are considered fatal. Do not retry.
|
354
|
+
# Close socket to flush out possible garbage. Do not put back in pool.
|
355
|
+
@conn.close
|
356
|
+
raise e
|
357
|
+
end
|
358
|
+
|
359
|
+
# Reset timeout in send buffer (destined for server) and socket.
|
360
|
+
@data_buffer.write_int32((@policy.timeout * 1000).to_i, 22)
|
361
|
+
|
362
|
+
# Send command.
|
363
|
+
begin
|
364
|
+
@conn.write(@data_buffer, @data_offset)
|
365
|
+
rescue => e
|
366
|
+
# IO errors are considered temporary anomalies. Retry.
|
367
|
+
# Close socket to flush out possible garbage. Do not put back in pool.
|
368
|
+
@conn.close
|
369
|
+
|
370
|
+
Aerospike.logger.warn("Node #{@node.to_s}: #{e}")
|
371
|
+
# IO error means connection to server @node is unhealthy.
|
372
|
+
# Reflect cmd status.
|
373
|
+
@node.decrease_health
|
374
|
+
next
|
375
|
+
end
|
376
|
+
|
377
|
+
# Parse results.
|
378
|
+
begin
|
379
|
+
parse_result
|
380
|
+
rescue => e
|
381
|
+
# close the connection
|
382
|
+
# cancelling/closing the batch/multi commands will return an error, which will
|
383
|
+
# close the connection to throw away its data and signal the server about the
|
384
|
+
# situation. We will not put back the connection in the buffer.
|
385
|
+
@conn.close
|
386
|
+
raise e
|
387
|
+
end
|
388
|
+
|
389
|
+
# Reflect healthy status.
|
390
|
+
@node.restore_health
|
391
|
+
|
392
|
+
# Put connection back in pool.
|
393
|
+
@node.put_connection(@conn)
|
394
|
+
|
395
|
+
# command has completed successfully. Exit method.
|
396
|
+
return
|
397
|
+
ensure
|
398
|
+
Buffer.put(@data_buffer)
|
399
|
+
end
|
400
|
+
|
401
|
+
end # while
|
402
|
+
|
403
|
+
# execution timeout
|
404
|
+
raise Aerospike::Exceptions::Timeout.new(limit, iterations)
|
405
|
+
end
|
406
|
+
|
407
|
+
protected
|
408
|
+
|
409
|
+
|
410
|
+
def estimate_key_size(key)
|
411
|
+
field_count = 0
|
412
|
+
|
413
|
+
if key.namespace
|
414
|
+
@data_offset += key.namespace.length + FIELD_HEADER_SIZE
|
415
|
+
field_count += 1
|
416
|
+
end
|
417
|
+
|
418
|
+
if key.set_name
|
419
|
+
@data_offset += key.set_name.length + FIELD_HEADER_SIZE
|
420
|
+
field_count += 1
|
421
|
+
end
|
422
|
+
|
423
|
+
@data_offset += key.digest.length + FIELD_HEADER_SIZE
|
424
|
+
field_count += 1
|
425
|
+
|
426
|
+
return field_count
|
427
|
+
end
|
428
|
+
|
429
|
+
def estimate_udf_size(package_name, function_name, bytes)
|
430
|
+
@data_offset += package_name.bytesize + FIELD_HEADER_SIZE
|
431
|
+
@data_offset += function_name.bytesize + FIELD_HEADER_SIZE
|
432
|
+
@data_offset += bytes.bytesize + FIELD_HEADER_SIZE
|
433
|
+
return 3
|
434
|
+
end
|
435
|
+
|
436
|
+
def estimate_operation_size_for_bin(bin)
|
437
|
+
@data_offset += bin.name.length + OPERATION_HEADER_SIZE
|
438
|
+
@data_offset += bin.value_object.estimate_size
|
439
|
+
end
|
440
|
+
|
441
|
+
def estimate_operation_size_for_operation(operation)
|
442
|
+
bin_len = 0
|
443
|
+
|
444
|
+
if operation.bin_name
|
445
|
+
bin_len = operation.bin_name.length
|
446
|
+
end
|
447
|
+
|
448
|
+
@data_offset += bin_len + OPERATION_HEADER_SIZE
|
449
|
+
|
450
|
+
if operation.bin_value
|
451
|
+
@data_offset += operation.bin_value.estimate_size
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def estimate_operation_size_for_bin_name(bin_name)
|
456
|
+
@data_offset += bin_name.length + OPERATION_HEADER_SIZE
|
457
|
+
end
|
458
|
+
|
459
|
+
def estimate_operation_size
|
460
|
+
@data_offset += OPERATION_HEADER_SIZE
|
461
|
+
end
|
462
|
+
|
463
|
+
# Generic header write.
|
464
|
+
def write_header(read_attr, write_attr, field_count, operation_count)
|
465
|
+
# Write all header data except total size which must be written last.
|
466
|
+
@data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
|
467
|
+
@data_buffer.write_byte(read_attr, 9)
|
468
|
+
@data_buffer.write_byte(write_attr, 10)
|
469
|
+
|
470
|
+
for i in 11..25
|
471
|
+
@data_buffer.write_byte(0, i)
|
472
|
+
end
|
473
|
+
|
474
|
+
@data_buffer.write_int16(field_count, 26)
|
475
|
+
@data_buffer.write_int16(operation_count, 28)
|
476
|
+
|
477
|
+
@data_offset = MSG_TOTAL_HEADER_SIZE
|
478
|
+
end
|
479
|
+
|
480
|
+
# Header write for write operations.
|
481
|
+
def write_header_with_policy(policy, read_attr, write_attr, field_count, operation_count)
|
482
|
+
# Set flags.
|
483
|
+
generation = Integer(0)
|
484
|
+
info_attr = Integer(0)
|
485
|
+
|
486
|
+
case policy.record_exists_action
|
487
|
+
when Aerospike::RecordExistsAction::UPDATE
|
488
|
+
when Aerospike::RecordExistsAction::UPDATE_ONLY
|
489
|
+
info_attr |= INFO3_UPDATE_ONLY
|
490
|
+
when Aerospike::RecordExistsAction::REPLACE
|
491
|
+
info_attr |= INFO3_CREATE_OR_REPLACE
|
492
|
+
when Aerospike::RecordExistsAction::REPLACE_ONLY
|
493
|
+
info_attr |= INFO3_REPLACE_ONLY
|
494
|
+
when Aerospike::RecordExistsAction::CREATE_ONLY
|
495
|
+
write_attr |= INFO2_CREATE_ONLY
|
496
|
+
end
|
497
|
+
|
498
|
+
case policy.generation_policy
|
499
|
+
when Aerospike::GenerationPolicy::NONE
|
500
|
+
when Aerospike::GenerationPolicy::EXPECT_GEN_EQUAL
|
501
|
+
generation = policy.generation
|
502
|
+
write_attr |= INFO2_GENERATION
|
503
|
+
when Aerospike::GenerationPolicy::EXPECT_GEN_GT
|
504
|
+
generation = policy.generation
|
505
|
+
write_attr |= INFO2_GENERATION_GT
|
506
|
+
when Aerospike::GenerationPolicy::DUPLICATE
|
507
|
+
generation = policy.generation
|
508
|
+
write_attr |= INFO2_GENERATION_DUP
|
509
|
+
end
|
510
|
+
|
511
|
+
# Write all header data except total size which must be written last.
|
512
|
+
@data_buffer.write_byte(MSG_REMAINING_HEADER_SIZE, 8) # Message heade.length.
|
513
|
+
@data_buffer.write_byte(read_attr, 9)
|
514
|
+
@data_buffer.write_byte(write_attr, 10)
|
515
|
+
@data_buffer.write_byte(info_attr, 11)
|
516
|
+
@data_buffer.write_byte(0, 12) # unused
|
517
|
+
@data_buffer.write_byte(0, 13) # clear the result code
|
518
|
+
# Buffer.Int32ToBytes(generation, @data_buffer, 14)
|
519
|
+
@data_buffer.write_int32(generation, 14)
|
520
|
+
# Buffer.Int32ToBytes(policy.expiration, @data_buffer, 18)
|
521
|
+
@data_buffer.write_int32(policy.expiration, 18)
|
522
|
+
|
523
|
+
# Initialize timeout. It will be written later.
|
524
|
+
@data_buffer.write_byte(0, 22)
|
525
|
+
@data_buffer.write_byte(0, 23)
|
526
|
+
@data_buffer.write_byte(0, 24)
|
527
|
+
@data_buffer.write_byte(0, 25)
|
528
|
+
|
529
|
+
|
530
|
+
# Buffer.Int16ToBytes(field_count, @data_buffer, 26)
|
531
|
+
@data_buffer.write_int16(field_count, 26)
|
532
|
+
# Buffer.Int16ToBytes(operation_count, @data_buffer, 28)
|
533
|
+
@data_buffer.write_int16(operation_count, 28)
|
534
|
+
|
535
|
+
@data_offset = MSG_TOTAL_HEADER_SIZE
|
536
|
+
end
|
537
|
+
|
538
|
+
def write_key(key)
|
539
|
+
# Write key into buffer.
|
540
|
+
if key.namespace
|
541
|
+
write_field_string(key.namespace, Aerospike::FieldType::NAMESPACE)
|
542
|
+
end
|
543
|
+
|
544
|
+
if key.set_name
|
545
|
+
write_field_string(key.set_name, Aerospike::FieldType::TABLE)
|
546
|
+
end
|
547
|
+
|
548
|
+
write_field_bytes(key.digest, Aerospike::FieldType::DIGEST_RIPE)
|
549
|
+
end
|
550
|
+
|
551
|
+
def write_operation_for_bin(bin, operation)
|
552
|
+
name_length = @data_buffer.write_binary(bin.name, @data_offset+OPERATION_HEADER_SIZE)
|
553
|
+
value_length = bin.value_object.write(@data_buffer, @data_offset+OPERATION_HEADER_SIZE+name_length)
|
554
|
+
|
555
|
+
# Buffer.Int32ToBytes(name_length+value_length+4, @data_buffer, @data_offset)
|
556
|
+
@data_buffer.write_int32(name_length+value_length+4, @data_offset)
|
557
|
+
|
558
|
+
@data_offset += 4
|
559
|
+
@data_buffer.write_byte(operation, @data_offset)
|
560
|
+
@data_offset += 1
|
561
|
+
@data_buffer.write_byte(bin.value_object.type, @data_offset)
|
562
|
+
@data_offset += 1
|
563
|
+
@data_buffer.write_byte(0, @data_offset)
|
564
|
+
@data_offset += 1
|
565
|
+
@data_buffer.write_byte(name_length, @data_offset)
|
566
|
+
@data_offset += 1
|
567
|
+
@data_offset += name_length + value_length
|
568
|
+
end
|
569
|
+
|
570
|
+
def write_operation_for_operation(operation)
|
571
|
+
name_length = 0
|
572
|
+
if operation.bin_name
|
573
|
+
name_length = @data_buffer.write_binary(operation.bin_name, @data_offset+OPERATION_HEADER_SIZE)
|
574
|
+
end
|
575
|
+
|
576
|
+
value_length = operation.bin_value.write(@data_buffer, @data_offset+OPERATION_HEADER_SIZE+name_length)
|
577
|
+
|
578
|
+
# Buffer.Int32ToBytes(name_length+value_length+4, @data_buffer, @data_offset)
|
579
|
+
@data_buffer.write_int32(name_length+value_length+4, @data_offset)
|
580
|
+
|
581
|
+
@data_offset += 4
|
582
|
+
@data_buffer.write_byte(operation.op_type, @data_offset)
|
583
|
+
@data_offset += 1
|
584
|
+
@data_buffer.write_byte(operation.bin_value.type, @data_offset)
|
585
|
+
@data_offset += 1
|
586
|
+
@data_buffer.write_byte(0, @data_offset)
|
587
|
+
@data_offset += 1
|
588
|
+
@data_buffer.write_byte(name_length, @data_offset)
|
589
|
+
@data_offset += 1
|
590
|
+
@data_offset += name_length + value_length
|
591
|
+
end
|
592
|
+
|
593
|
+
def write_operation_for_bin_name(name, operation)
|
594
|
+
name_length = @data_buffer.write_binary(name, @data_offset+OPERATION_HEADER_SIZE)
|
595
|
+
# Buffer.Int32ToBytes(name_length+4, @data_buffer, @data_offset)
|
596
|
+
@data_buffer.write_int32(name_length+4, @data_offset)
|
597
|
+
|
598
|
+
@data_offset += 4
|
599
|
+
@data_buffer.write_byte(operation, @data_offset)
|
600
|
+
@data_offset += 1
|
601
|
+
@data_buffer.write_byte(0, @data_offset)
|
602
|
+
@data_offset += 1
|
603
|
+
@data_buffer.write_byte(0, @data_offset)
|
604
|
+
@data_offset += 1
|
605
|
+
@data_buffer.write_byte(name_length, @data_offset)
|
606
|
+
@data_offset += 1
|
607
|
+
@data_offset += name_length
|
608
|
+
end
|
609
|
+
|
610
|
+
def write_operation_for_operation_type(operation)
|
611
|
+
# Buffer.Int32ToBytes(4), @data_buffer, @data_offset
|
612
|
+
@data_buffer.write_int32(4, @data_offset)
|
613
|
+
@data_offset += 4
|
614
|
+
@data_buffer.write_byte(operation, @data_offset)
|
615
|
+
@data_offset += 1
|
616
|
+
@data_buffer.write_byte(0, @data_offset)
|
617
|
+
@data_offset += 1
|
618
|
+
@data_buffer.write_byte(0, @data_offset)
|
619
|
+
@data_offset += 1
|
620
|
+
@data_buffer.write_byte(0, @data_offset)
|
621
|
+
@data_offset += 1
|
622
|
+
end
|
623
|
+
|
624
|
+
def write_field_value(value, ftype)
|
625
|
+
offset = @data_offset + FIELD_HEADER_SIZE
|
626
|
+
@data_buffer.write_byte(value.type, offset)
|
627
|
+
offset += 1
|
628
|
+
len = value.write(@data_buffer, offset)
|
629
|
+
len += 1
|
630
|
+
write_field_header(len, ftype)
|
631
|
+
@data_offset += len
|
632
|
+
end
|
633
|
+
|
634
|
+
def write_field_string(str, ftype)
|
635
|
+
len = @data_buffer.write_binary(str, @data_offset+FIELD_HEADER_SIZE)
|
636
|
+
write_field_header(len, ftype)
|
637
|
+
@data_offset += len
|
638
|
+
end
|
639
|
+
|
640
|
+
def write_field_bytes(bytes, ftype)
|
641
|
+
@data_buffer.write_binary(bytes, @data_offset+FIELD_HEADER_SIZE)
|
642
|
+
|
643
|
+
write_field_header(bytes.bytesize, ftype)
|
644
|
+
@data_offset += bytes.bytesize
|
645
|
+
end
|
646
|
+
|
647
|
+
def write_field_header(size, ftype)
|
648
|
+
# Buffer.Int32ToBytes(size+1), @data_buffer, @data_offset
|
649
|
+
@data_buffer.write_int32(size+1, @data_offset)
|
650
|
+
@data_offset += 4
|
651
|
+
@data_buffer.write_byte(ftype, @data_offset)
|
652
|
+
@data_offset += 1
|
653
|
+
end
|
654
|
+
|
655
|
+
def begin_cmd
|
656
|
+
@data_offset = MSG_TOTAL_HEADER_SIZE
|
657
|
+
end
|
658
|
+
|
659
|
+
def size_buffer
|
660
|
+
size_buffer_sz(@data_offset)
|
661
|
+
end
|
662
|
+
|
663
|
+
def size_buffer_sz(size)
|
664
|
+
# Corrupted data streams can result in a hug.length.
|
665
|
+
# Do a sanity check here.
|
666
|
+
if size > Buffer::MAX_BUFFER_SIZE
|
667
|
+
raise Aerospike::Exceptions::Parse.new("Invalid size for buffer: #{size}")
|
668
|
+
end
|
669
|
+
|
670
|
+
@data_buffer.resize(size)
|
671
|
+
end
|
672
|
+
|
673
|
+
def end_cmd
|
674
|
+
size = (@data_offset-8) | Integer(CL_MSG_VERSION << 56) | Integer(AS_MSG_TYPE << 48)
|
675
|
+
@data_buffer.write_int64(size, 0)
|
676
|
+
end
|
677
|
+
|
678
|
+
end # class
|
679
|
+
|
680
|
+
end # module
|