aerospike 0.1.3 → 0.1.5
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 +13 -0
- data/README.md +12 -5
- data/lib/aerospike.rb +12 -1
- data/lib/aerospike/atomic/atomic.rb +51 -0
- data/lib/aerospike/client.rb +172 -9
- data/lib/aerospike/cluster/cluster.rb +6 -6
- data/lib/aerospike/cluster/node.rb +3 -1
- data/lib/aerospike/command/batch_command.rb +70 -2
- data/lib/aerospike/command/batch_command_get.rb +0 -66
- data/lib/aerospike/command/command.rb +81 -8
- data/lib/aerospike/command/read_command.rb +2 -2
- data/lib/aerospike/command/single_command.rb +1 -1
- data/lib/aerospike/command/write_command.rb +1 -1
- data/lib/aerospike/info.rb +1 -2
- data/lib/aerospike/ldt/large_list.rb +1 -0
- data/lib/aerospike/ldt/large_map.rb +1 -0
- data/lib/aerospike/policy/batch_policy.rb +38 -0
- data/lib/aerospike/policy/query_policy.rb +33 -0
- data/lib/aerospike/policy/scan_policy.rb +41 -0
- data/lib/aerospike/query/filter.rb +66 -0
- data/lib/aerospike/query/query_command.rb +200 -0
- data/lib/aerospike/query/recordset.rb +121 -0
- data/lib/aerospike/query/scan_command.rb +42 -0
- data/lib/aerospike/query/statement.rb +70 -0
- data/lib/aerospike/query/stream_command.rb +68 -0
- data/lib/aerospike/task/task.rb +2 -1
- data/lib/aerospike/utils/epoc.rb +8 -1
- data/lib/aerospike/value/value.rb +1 -1
- data/lib/aerospike/version.rb +1 -1
- metadata +28 -24
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http:#www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'aerospike/policy/batch_policy'
|
17
|
+
|
18
|
+
module Aerospike
|
19
|
+
|
20
|
+
# Container object for scan policy command.
|
21
|
+
class QueryPolicy < BatchPolicy
|
22
|
+
|
23
|
+
def initialize()
|
24
|
+
super()
|
25
|
+
|
26
|
+
@max_retries = 0
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class
|
32
|
+
|
33
|
+
end # module
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# Copyright 2014 Aerospike, Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http:#www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'aerospike/policy/batch_policy'
|
17
|
+
|
18
|
+
module Aerospike
|
19
|
+
|
20
|
+
# Container object for scan policy command.
|
21
|
+
class ScanPolicy < BatchPolicy
|
22
|
+
|
23
|
+
attr_accessor :scan_percent, :concurrent_nodes,
|
24
|
+
:include_bin_data, :fail_on_cluster_change
|
25
|
+
|
26
|
+
def initialize(scan_percent=nil, concurrent_nodes=nil, include_bin_data=nil, fail_on_cluster_change=nil)
|
27
|
+
super()
|
28
|
+
|
29
|
+
@scan_percent = scan_percent || 100
|
30
|
+
@concurrent_nodes = concurrent_nodes.nil? ? true : concurrent_nodes
|
31
|
+
@include_bin_data = include_bin_data.nil? ? true : include_bin_data
|
32
|
+
@fail_on_cluster_change = fail_on_cluster_change.nil? ? true : fail_on_cluster_change
|
33
|
+
|
34
|
+
@max_retries = 0
|
35
|
+
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
end # class
|
40
|
+
|
41
|
+
end # module
|
@@ -0,0 +1,66 @@
|
|
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
|
+
module Aerospike
|
18
|
+
|
19
|
+
class Filter
|
20
|
+
|
21
|
+
def self.Equal(bin_name, value)
|
22
|
+
Filter.new(bin_name, value, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.Range(bin_name, from, to)
|
26
|
+
Filter.new(bin_name, from, to)
|
27
|
+
end
|
28
|
+
|
29
|
+
def estimate_size
|
30
|
+
return @name.bytesize + @begin.estimate_size + @end.estimate_size + 10
|
31
|
+
end
|
32
|
+
|
33
|
+
def write(buf, offset)
|
34
|
+
# Write name.
|
35
|
+
len = buf.write_binary(@name, offset+1)
|
36
|
+
buf.write_byte(len, offset)
|
37
|
+
offset += len + 1
|
38
|
+
|
39
|
+
# Write particle type.
|
40
|
+
buf.write_byte(@begin.type, offset)
|
41
|
+
offset+=1
|
42
|
+
|
43
|
+
# Write filter begin.
|
44
|
+
len = @begin.write(buf, offset+4)
|
45
|
+
buf.write_int32(len, offset)
|
46
|
+
offset += len + 4
|
47
|
+
|
48
|
+
# Write filter end.
|
49
|
+
len = @end.write(buf, offset+4)
|
50
|
+
buf.write_int32(len, offset)
|
51
|
+
offset += len + 4
|
52
|
+
|
53
|
+
offset
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize(bin_name, begin_value, end_value)
|
59
|
+
@name = bin_name
|
60
|
+
@begin = Aerospike::Value.of(begin_value)
|
61
|
+
@end = Aerospike::Value.of(end_value)
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,200 @@
|
|
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/query/stream_command'
|
18
|
+
require 'aerospike/query/recordset'
|
19
|
+
|
20
|
+
module Aerospike
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
class QueryCommand < StreamCommand
|
25
|
+
|
26
|
+
def initialize(node, policy, statement, recordset)
|
27
|
+
super(node)
|
28
|
+
|
29
|
+
@policy = policy
|
30
|
+
@statement = statement
|
31
|
+
@recordset = recordset
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_buffer
|
35
|
+
fieldCount = 0
|
36
|
+
filterSize = 0
|
37
|
+
binNameSize = 0
|
38
|
+
|
39
|
+
begin_cmd
|
40
|
+
|
41
|
+
if @statement.namespace
|
42
|
+
@data_offset += @statement.namespace.bytesize + FIELD_HEADER_SIZE
|
43
|
+
fieldCount+=1
|
44
|
+
end
|
45
|
+
|
46
|
+
if @statement.index_name
|
47
|
+
@data_offset += @statement.index_name.bytesize + FIELD_HEADER_SIZE
|
48
|
+
fieldCount+=1
|
49
|
+
end
|
50
|
+
|
51
|
+
if @statement.set_name
|
52
|
+
@data_offset += @statement.set_name.bytesize + FIELD_HEADER_SIZE
|
53
|
+
fieldCount+=1
|
54
|
+
end
|
55
|
+
|
56
|
+
if @statement.filters && @statement.filters.length > 0
|
57
|
+
@data_offset += FIELD_HEADER_SIZE
|
58
|
+
filterSize+=1 # num filters
|
59
|
+
|
60
|
+
@statement.filters.each do |filter|
|
61
|
+
sz = filter.estimate_size
|
62
|
+
filterSize += sz
|
63
|
+
end
|
64
|
+
@data_offset += filterSize
|
65
|
+
fieldCount+=1
|
66
|
+
else
|
67
|
+
# Calling query with no filters is more efficiently handled by a primary index scan.
|
68
|
+
# Estimate scan options size.
|
69
|
+
@data_offset += (2 + FIELD_HEADER_SIZE)
|
70
|
+
fieldCount+=1
|
71
|
+
end
|
72
|
+
|
73
|
+
if @statement.bin_names && @statement.bin_names.length > 0
|
74
|
+
@data_offset += FIELD_HEADER_SIZE
|
75
|
+
binNameSize+=1 # num bin names
|
76
|
+
|
77
|
+
@statement.bin_names.each do |bin_name|
|
78
|
+
binNameSize += bin_name.bytesize + 1 #OPERATION_HEADER_SIZE
|
79
|
+
end
|
80
|
+
@data_offset += binNameSize
|
81
|
+
fieldCount+=1
|
82
|
+
end
|
83
|
+
|
84
|
+
@statement.task_id = Time.now.to_i + Time.usec if @statement.task_id == 0
|
85
|
+
|
86
|
+
@data_offset += 8 + FIELD_HEADER_SIZE
|
87
|
+
fieldCount+=1
|
88
|
+
|
89
|
+
if @statement.function_name
|
90
|
+
@data_offset += FIELD_HEADER_SIZE + 1 # udf type
|
91
|
+
@data_offset += @statement.package_name.bytesize + FIELD_HEADER_SIZE
|
92
|
+
@data_offset += @statement.function_name.bytesize + FIELD_HEADER_SIZE
|
93
|
+
|
94
|
+
if @statement.function_args && @statement.function_args.length > 0
|
95
|
+
functionArgBuffer = Value.of(@statement.function_args).bytes
|
96
|
+
else
|
97
|
+
functionArgBuffer = 0.ord
|
98
|
+
end
|
99
|
+
@data_offset += FIELD_HEADER_SIZE + functionArgBuffer.bytesize
|
100
|
+
fieldCount += 4
|
101
|
+
end
|
102
|
+
|
103
|
+
if @statement.filters.nil? || @statement.filters.empty?
|
104
|
+
if @statement.bin_names && @statement.bin_names.length > 0
|
105
|
+
@statement.bin_names.each do |bin_name|
|
106
|
+
estimate_operation_size_for_bin_name(bin_name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
size_buffer
|
112
|
+
|
113
|
+
readAttr = INFO1_READ
|
114
|
+
operation_count = (@statement.filters.to_a.length == 0 && @statement.bin_names.to_a.length == 0) ? @statement.bin_names.length : 0
|
115
|
+
|
116
|
+
write_header(readAttr, 0, fieldCount, operation_count)
|
117
|
+
|
118
|
+
if @statement.namespace
|
119
|
+
write_field_string(@statement.namespace, Aerospike::FieldType::NAMESPACE)
|
120
|
+
end
|
121
|
+
|
122
|
+
unless @statement.index_name.nil?
|
123
|
+
write_field_string(@statement.index_name, Aerospike::FieldType::INDEX_NAME)
|
124
|
+
end
|
125
|
+
|
126
|
+
if @statement.set_name
|
127
|
+
write_field_string(@statement.set_name, Aerospike::FieldType::TABLE)
|
128
|
+
end
|
129
|
+
|
130
|
+
if @statement.filters && @statement.filters.length > 0
|
131
|
+
write_field_header(filterSize, Aerospike::FieldType::INDEX_RANGE)
|
132
|
+
@data_buffer.write_byte(@statement.filters.length, @data_offset)
|
133
|
+
@data_offset+=1
|
134
|
+
|
135
|
+
@statement.filters.each do |filter|
|
136
|
+
@data_offset = filter.write(@data_buffer, @data_offset)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Query bin names are specified as a field (Scan bin names are specified later as operations)
|
140
|
+
if @statement.bin_names && @statement.bin_names.length > 0
|
141
|
+
write_field_header(binNameSize, Aerospike::FieldType::QUERY_BINLIST)
|
142
|
+
@data_buffer.write_byte(@statement.bin_names.length, @data_offset)
|
143
|
+
@data_offset += 1
|
144
|
+
|
145
|
+
@statement.bin_names.each do |bin_name|
|
146
|
+
len = @data_buffer.write_binary(bin_name, @data_offset + 1)
|
147
|
+
@data_buffer.write_byte(len, @data_offset)
|
148
|
+
@data_offset += len + 1;
|
149
|
+
end
|
150
|
+
end
|
151
|
+
else
|
152
|
+
# Calling query with no filters is more efficiently handled by a primary index scan.
|
153
|
+
write_field_header(2, Aerospike::FieldType::SCAN_OPTIONS)
|
154
|
+
priority = @policy.priority.ord
|
155
|
+
priority = priority << 4
|
156
|
+
@data_buffer.write_byte(priority, @data_offset)
|
157
|
+
@data_offset+=1
|
158
|
+
@data_buffer.write_byte(100.ord, @data_offset)
|
159
|
+
@data_offset+=1
|
160
|
+
end
|
161
|
+
|
162
|
+
if @statement.bin_names && @statement.bin_names.length > 0
|
163
|
+
write_field_header(binNameSize, Aerospike::FieldType::QUERY_BINLIST)
|
164
|
+
@data_buffer.write_byte(@statement.bin_names.length, @data_offset)
|
165
|
+
@data_offset+=1
|
166
|
+
|
167
|
+
@statement.bin_names.each do |bin_name|
|
168
|
+
len = @data_buffer.write_binary(bin_name, @data_offset + 1)
|
169
|
+
@data_buffer.write_byte(len, @data_offset)
|
170
|
+
@data_offset += len + 1
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
write_field_header(8, Aerospike::FieldType::TRAN_ID)
|
175
|
+
@data_buffer.write_int64(@statement.task_id, @data_offset)
|
176
|
+
@data_offset += 8
|
177
|
+
|
178
|
+
if @statement.function_name
|
179
|
+
write_field_header(1, Aerospike::FieldType::UDF_OP)
|
180
|
+
if @statement.return_data
|
181
|
+
@data_buffer.write_byte(1, @data_offset)
|
182
|
+
@data_offset+=1
|
183
|
+
else
|
184
|
+
@data_buffer.write_byte(2, @data_offset)
|
185
|
+
@data_offset+=1
|
186
|
+
end
|
187
|
+
|
188
|
+
write_field_string(@statement.package_name, Aerospike::FieldType::UDF_PACKAGE_NAME)
|
189
|
+
write_field_string(@statement.function_name, Aerospike::FieldType::UDF_FUNCTION)
|
190
|
+
write_field_bytes(functionArgBuffer, Aerospike::FieldType::UDF_ARGLIST)
|
191
|
+
end
|
192
|
+
|
193
|
+
end_cmd
|
194
|
+
|
195
|
+
return nil
|
196
|
+
end
|
197
|
+
|
198
|
+
end # class
|
199
|
+
|
200
|
+
end # module
|
@@ -0,0 +1,121 @@
|
|
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
|
+
module Aerospike
|
18
|
+
|
19
|
+
# Recordset implements a queue for a producer-consumer pattern
|
20
|
+
# a producer is a thread that fetches records from one node and puts them on this queue
|
21
|
+
# a consumer fetches records from this queue
|
22
|
+
# so the production and the consumptoin are decoupled
|
23
|
+
# there can be an unlimited count of producer threads and consumer threads
|
24
|
+
class Recordset
|
25
|
+
|
26
|
+
attr_reader :records
|
27
|
+
|
28
|
+
def initialize(queue_size = 5000, thread_count = 1, type)
|
29
|
+
queue_size = thread_count if queue_size < thread_count
|
30
|
+
@records = SizedQueue.new(queue_size)
|
31
|
+
|
32
|
+
# holds the count of active threads.
|
33
|
+
# when it reaches zero it means the whole operations of fetching records from server nodes is finished
|
34
|
+
@active_threads = Atomic.new(thread_count)
|
35
|
+
|
36
|
+
# operation cancelled by user or an exception occured in one of the threads
|
37
|
+
@cancelled = Atomic.new(false)
|
38
|
+
|
39
|
+
# saves the exception that occurred inside one of the threads to reraise it in the main thread
|
40
|
+
# and also is a signal to terminate other threads as the whole operation is assumed as failed
|
41
|
+
@thread_exception = Atomic.new(nil)
|
42
|
+
|
43
|
+
# type of the operation. it is either :scan or :query
|
44
|
+
@type = type
|
45
|
+
end
|
46
|
+
|
47
|
+
# fetches and return the first record from the queue
|
48
|
+
# if the operation is not finished and the queue is empty it blocks and waits for new records
|
49
|
+
# it sets the exception if it reaches the EOF mark, and returns nil
|
50
|
+
# EOF means the operation has finished and no more records are comming from server nodes
|
51
|
+
# it re-raises the exception occurred in threads, or which was set after reaching the EOF in the previous call
|
52
|
+
def next_record
|
53
|
+
raise @thread_exception.get unless @thread_exception.get.nil?
|
54
|
+
|
55
|
+
r = @records.deq
|
56
|
+
|
57
|
+
set_exception if r.nil?
|
58
|
+
|
59
|
+
r
|
60
|
+
end
|
61
|
+
|
62
|
+
# recordset is active unless it is cancelled by the user or an exception has occurred in of threads
|
63
|
+
def active?
|
64
|
+
!@cancelled.get
|
65
|
+
end
|
66
|
+
|
67
|
+
# this is called by working threads to signal their job is finished
|
68
|
+
# it decreases the count of active threads and puts an EOF on queue when all threads are finished
|
69
|
+
def thread_finished
|
70
|
+
@active_threads.update do |v|
|
71
|
+
v -= 1
|
72
|
+
@records.enq(nil) if v == 0
|
73
|
+
v
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# this is called by a thread who faced an exception to singnal to terminate the whole operation
|
78
|
+
# it also may be called by the user to terminate the command in the middle of fetching records from server nodes
|
79
|
+
# it clears the queue so that if any threads are waiting for the queue get unblocked and find out about the cancellation
|
80
|
+
def cancel(expn=nil)
|
81
|
+
set_exception(expn)
|
82
|
+
@cancelled.set(true)
|
83
|
+
@records.clear
|
84
|
+
end
|
85
|
+
|
86
|
+
# fetches and returns all the records from the queue until the whole operation is finished and it reaches an EOF mark
|
87
|
+
# calling cancel inside the each block raises an exception to signal other consumer threads
|
88
|
+
def each(&block)
|
89
|
+
r = true
|
90
|
+
while r
|
91
|
+
r = next_record
|
92
|
+
# nil means EOF
|
93
|
+
unless r.nil?
|
94
|
+
block.call(r)
|
95
|
+
else
|
96
|
+
# reached the EOF
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# the command is a scan if there are no filters applied otherwise it is a query
|
103
|
+
def is_scan?
|
104
|
+
@filters.nil? || @filters.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def set_exception(expn=nil)
|
110
|
+
expn ||= (@type == :scan ? SCAN_TERMINATED_EXCEPTION : QUERY_TERMINATED_EXCEPTION)
|
111
|
+
@thread_exception.set(expn)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
SCAN_TERMINATED_EXCEPTION = Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SCAN_TERMINATED)
|
119
|
+
QUERY_TERMINATED_EXCEPTION = Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SCAN_TERMINATED)
|
120
|
+
|
121
|
+
end
|