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.
@@ -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