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,128 @@
|
|
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 'base64'
|
18
|
+
|
19
|
+
module Aerospike
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
REPLICAS_NAME = 'replicas-master'
|
24
|
+
|
25
|
+
class PartitionTokenizerNew
|
26
|
+
|
27
|
+
def initialize(conn)
|
28
|
+
# Use low-level info methods and parse byte array directly for maximum performance.
|
29
|
+
# Send format: replicas-master\n
|
30
|
+
# Receive format: replicas-master\t<ns1>:<base 64 encoded bitmap>;<ns2>:<base 64 encoded bitmap>... \n
|
31
|
+
info_map = Info.request(conn, REPLICAS_NAME)
|
32
|
+
|
33
|
+
info = info_map[REPLICAS_NAME]
|
34
|
+
|
35
|
+
@length = info ? info.length : 0
|
36
|
+
|
37
|
+
if !info || @length == 0
|
38
|
+
raise Aerospike::Exceptions::Connection.new("#{replicas_name} is empty")
|
39
|
+
end
|
40
|
+
|
41
|
+
@buffer = info
|
42
|
+
@offset = 0
|
43
|
+
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_partition(nmap, node)
|
48
|
+
amap = nil
|
49
|
+
|
50
|
+
beginning = @offset
|
51
|
+
copied = false
|
52
|
+
|
53
|
+
while @offset < @length
|
54
|
+
if @buffer[@offset] == ':'
|
55
|
+
# Parse namespace.
|
56
|
+
namespace = @buffer[beginning...@offset].strip
|
57
|
+
|
58
|
+
if namespace.length <= 0 || namespace.length >= 32
|
59
|
+
response = get_truncated_response
|
60
|
+
raise Aerospike::Exceptions::Parse.new(
|
61
|
+
"Invalid partition namespace #{namespace}. Response=#{response}"
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
@offset+=1
|
66
|
+
beginning = @offset
|
67
|
+
|
68
|
+
# Parse partition id.
|
69
|
+
while @offset < @length
|
70
|
+
b = @buffer[@offset]
|
71
|
+
|
72
|
+
break if b == ';' || b == "\n"
|
73
|
+
@offset+=1
|
74
|
+
end
|
75
|
+
|
76
|
+
if @offset == beginning
|
77
|
+
response = get_truncated_response
|
78
|
+
|
79
|
+
raise Aerospike::Exceptions::Parse.new(
|
80
|
+
"Empty partition id for namespace #{namespace}. Response=#{response}"
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
node_array = nmap[namespace]
|
85
|
+
|
86
|
+
if !node_array
|
87
|
+
if !copied
|
88
|
+
# Make shallow copy of map.
|
89
|
+
amap = {}
|
90
|
+
nmap.each {|k, v| amap[k] = Atomic.new(v)}
|
91
|
+
copied = true
|
92
|
+
end
|
93
|
+
|
94
|
+
node_array = Atomic.new(Array.new(Aerospike::Node::PARTITIONS))
|
95
|
+
amap[namespace] = node_array
|
96
|
+
end
|
97
|
+
|
98
|
+
bit_map_length = @offset - beginning
|
99
|
+
restore_buffer = Base64.strict_decode64(@buffer[beginning, bit_map_length])
|
100
|
+
for i in 0...Aerospike::Node::PARTITIONS
|
101
|
+
if (restore_buffer[i>>3].ord & (0x80 >> (i & 7))) != 0
|
102
|
+
# Logger.Info("Map: `" + namespace + "`," + strconv.Itoa(i) + "," + node.String)
|
103
|
+
node_array.update{|v| v[i] = node; v}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@offset+=1
|
108
|
+
beginning = @offset
|
109
|
+
else
|
110
|
+
@offset+=1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
copied ? amap : nil
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def get_truncated_response
|
120
|
+
max = @length
|
121
|
+
@length = max if @length > 200
|
122
|
+
@buffer[0...max]
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
end # class
|
127
|
+
|
128
|
+
end # module
|
@@ -0,0 +1,135 @@
|
|
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 'base64'
|
18
|
+
|
19
|
+
module Aerospike
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
class PartitionTokenizerOld
|
24
|
+
|
25
|
+
def initialize(conn)
|
26
|
+
# Use low-level info methods and parse byte array directly for maximum performance.
|
27
|
+
# Send format: replicas-master\n
|
28
|
+
# Receive format: replicas-master\t<ns1>:<base 64 encoded bitmap>;<ns2>:<base 64 encoded bitmap>... \n
|
29
|
+
info_map = Info.request(conn, REPLICAS_NAME)
|
30
|
+
|
31
|
+
info = info_map[REPLICAS_NAME]
|
32
|
+
|
33
|
+
@length = info ? info.length : 0
|
34
|
+
|
35
|
+
if !info || @length == 0
|
36
|
+
raise Aerospike::Exceptions::Connection.new("#{replicas_name} is empty")
|
37
|
+
end
|
38
|
+
|
39
|
+
@buffer = info
|
40
|
+
@offset = 0
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def update_partition(nmap, node)
|
46
|
+
amap = nil
|
47
|
+
copied = false
|
48
|
+
|
49
|
+
while partition = get_next
|
50
|
+
node_array = nmap[partition.namespace]
|
51
|
+
|
52
|
+
if !node_array
|
53
|
+
if !copied
|
54
|
+
# Make shallow copy of map.
|
55
|
+
amap = {}
|
56
|
+
nmap.each {|k, v| amap[k] = v}
|
57
|
+
copied = true
|
58
|
+
end
|
59
|
+
|
60
|
+
node_array = Atomic.new(Array.new(Aerospike::Node::PARTITIONS))
|
61
|
+
amap[partition.namespace] = node_array
|
62
|
+
end
|
63
|
+
|
64
|
+
Aerospike.logger.debug("#{partition.to_s}, #{node.name}")
|
65
|
+
node_array.update{|v| v[partition.partition_id] = node; v }
|
66
|
+
end
|
67
|
+
|
68
|
+
copied ? amap : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def get_next
|
74
|
+
beginning = @offset
|
75
|
+
|
76
|
+
while @offset < @length
|
77
|
+
if @buffer[@offset] == ':'
|
78
|
+
# Parse namespace.
|
79
|
+
namespace = @buffer[beginning...@offset].strip
|
80
|
+
|
81
|
+
if namespace.length <= 0 || namespace.length >= 32
|
82
|
+
response = get_truncated_response
|
83
|
+
raise Aerospike::Exceptions::Parse.new(
|
84
|
+
"Invalid partition namespace #{namespace}. Response=#{response}"
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
@offset+=1
|
89
|
+
beginning = @offset
|
90
|
+
|
91
|
+
# Parse partition id.
|
92
|
+
while @offset < @length
|
93
|
+
b = @buffer[@offset]
|
94
|
+
|
95
|
+
break if b == ';' || b == "\n"
|
96
|
+
@offset+=1
|
97
|
+
end
|
98
|
+
|
99
|
+
if @offset == beginning
|
100
|
+
response = get_truncated_response
|
101
|
+
raise Aerospike::Exceptions::Parse.new(
|
102
|
+
"Empty partition id for namespace #{namespace}. Response=#{response}"
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
partition_id = @buffer[beginning...@offset].to_i
|
107
|
+
if partition_id < 0 || partition_id >= Aerospike::Node::PARTITIONS
|
108
|
+
response = get_truncated_response
|
109
|
+
partition_string = @buffer[beginning...@offset]
|
110
|
+
raise Aerospike::Exceptions::Parse.new(
|
111
|
+
"Invalid partition id #{partition_string} for namespace #{namespace}. Response=#{response}"
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
@offset+=1
|
116
|
+
beginning = @offset
|
117
|
+
|
118
|
+
return Partition.new(namespace, partition_id)
|
119
|
+
end
|
120
|
+
@offset+=1
|
121
|
+
end
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def get_truncated_response
|
127
|
+
max = @length
|
128
|
+
@length = max if @length > 200
|
129
|
+
@buffer[0...max]
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
end # class
|
134
|
+
|
135
|
+
end # module
|
@@ -0,0 +1,120 @@
|
|
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 BatchCommand < Command
|
28
|
+
|
29
|
+
def initialize(node)
|
30
|
+
super(node)
|
31
|
+
|
32
|
+
@valid = true
|
33
|
+
@mutex = Mutex.new
|
34
|
+
@records = Queue.new
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_result
|
40
|
+
# Read socket into receive buffer one record at a time. Do not read entire receive size
|
41
|
+
# because the receive buffer would be too big.
|
42
|
+
status = true
|
43
|
+
|
44
|
+
while status
|
45
|
+
# Read header.
|
46
|
+
read_bytes(8)
|
47
|
+
|
48
|
+
size = @data_buffer.read_int64(0)
|
49
|
+
receive_size = size & 0xFFFFFFFFFFFF
|
50
|
+
|
51
|
+
if receive_size > 0
|
52
|
+
status = parse_record_results(receive_size)
|
53
|
+
else
|
54
|
+
status = false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_key(field_count)
|
60
|
+
digest = nil
|
61
|
+
namespace = nil
|
62
|
+
set_name = nil
|
63
|
+
user_key = nil
|
64
|
+
|
65
|
+
for i in 0...field_count
|
66
|
+
read_bytes(4)
|
67
|
+
|
68
|
+
fieldlen = @data_buffer.read_int32(0)
|
69
|
+
read_bytes(fieldlen)
|
70
|
+
|
71
|
+
fieldtype = @data_buffer.read(0).ord
|
72
|
+
size = fieldlen - 1
|
73
|
+
|
74
|
+
case fieldtype
|
75
|
+
when Aerospike::FieldType::DIGEST_RIPE
|
76
|
+
digest = @data_buffer.read(1, size)
|
77
|
+
when Aerospike::FieldType::NAMESPACE
|
78
|
+
namespace = @data_buffer.read(1, size).force_encoding('utf-8')
|
79
|
+
when Aerospike::FieldType::TABLE
|
80
|
+
set_name = @data_buffer.read(1, size).force_encoding('utf-8')
|
81
|
+
when Aerospike::FieldType::KEY
|
82
|
+
user_key = Value.bytes_to_key_value(@data_buffer.read(1).ord, @data_buffer, 2, size-1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
Aerospike::Key.new(namespace, set_name, user_key, digest)
|
87
|
+
end
|
88
|
+
|
89
|
+
def read_bytes(length)
|
90
|
+
if length > @data_buffer.length
|
91
|
+
# Corrupted data streams can result in a huge length.
|
92
|
+
# Do a sanity check here.
|
93
|
+
if length > Aerospike::Buffer::MAX_BUFFER_SIZE
|
94
|
+
raise Aerospike::Exceptions::Parse.new("Invalid read_bytes length: #{length}")
|
95
|
+
end
|
96
|
+
@data_buffer = Buffer.new(length)
|
97
|
+
end
|
98
|
+
|
99
|
+
@conn.read(@data_buffer, length)
|
100
|
+
@data_offset += length
|
101
|
+
end
|
102
|
+
|
103
|
+
def stop
|
104
|
+
@mutex.synchronize do
|
105
|
+
@valid = false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def valid?
|
110
|
+
res = nil
|
111
|
+
@mutex.synchronize do
|
112
|
+
res = @valid
|
113
|
+
end
|
114
|
+
|
115
|
+
res
|
116
|
+
end
|
117
|
+
|
118
|
+
end # class
|
119
|
+
|
120
|
+
end # module
|
@@ -0,0 +1,93 @@
|
|
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 BatchCommandExists < BatchCommand
|
24
|
+
|
25
|
+
def initialize(node, batch_namespace, policy, key_map, exists_array)
|
26
|
+
super(node)
|
27
|
+
|
28
|
+
@batch_namespace = batch_namespace
|
29
|
+
@policy = policy
|
30
|
+
@key_map = key_map
|
31
|
+
@exists_array = exists_array
|
32
|
+
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_buffer
|
37
|
+
set_batch_exists(@batch_namespace)
|
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
|
+
if !valid?
|
48
|
+
raise Aerospike::Exceptions::QueryTerminated.new
|
49
|
+
end
|
50
|
+
|
51
|
+
read_bytes(MSG_REMAINING_HEADER_SIZE)
|
52
|
+
|
53
|
+
result_code = @data_buffer.read(5).ord & 0xFF
|
54
|
+
|
55
|
+
# The only valid server return codes are "ok" and "not found".
|
56
|
+
# If other return codes are received, then abort the batch.
|
57
|
+
if result_code != 0 && result_code != Aerospike::ResultCode::KEY_NOT_FOUND_ERROR
|
58
|
+
raise Aerospike::Exceptions::Aerospike.new(result_code)
|
59
|
+
end
|
60
|
+
|
61
|
+
info3 = @data_buffer.read(3).ord
|
62
|
+
|
63
|
+
# If cmd is the end marker of the response, do not proceed further
|
64
|
+
return false if info3 & INFO3_LAST == INFO3_LAST
|
65
|
+
|
66
|
+
field_count = @data_buffer.read_int16(18)
|
67
|
+
op_count = @data_buffer.read_int16(20)
|
68
|
+
|
69
|
+
if op_count > 0
|
70
|
+
raise Aerospike::Exceptions::Parse('Received bins that were not requested!')
|
71
|
+
end
|
72
|
+
|
73
|
+
key = parse_key(field_count)
|
74
|
+
item = @key_map[key.digest]
|
75
|
+
|
76
|
+
if item
|
77
|
+
index = item.index
|
78
|
+
|
79
|
+
# only set the results to true; as a result, no synchronization is needed
|
80
|
+
@exists_array[index] = (result_code == 0)
|
81
|
+
else
|
82
|
+
Aerospike::logger.debug("Unexpected batch key returned: #{key.namespace}, #{key.digest}")
|
83
|
+
end
|
84
|
+
|
85
|
+
end # while
|
86
|
+
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end # class
|
92
|
+
|
93
|
+
end # module
|