aerospike 0.1.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ee7d4805db6b56945a1f5112c62272f3371c79f8
|
4
|
+
data.tar.gz: ab922b1acac8a4aa5b09d5605886127c12af60cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6879a1edd54bcdb9ad82e5cc3f484cbe5f56034eed1058c9aeb556b2d7b1ca93f663f2260a7d1818b311d5ab65f11a5ab2b8fbae1b85bbaaf277fc5c01234df0
|
7
|
+
data.tar.gz: 911f4c56dfe6e1d6eb801e560f87732db0309dd5e80bec9969bd5f1af17f0ec3ecfdb249ea865178b619afa6eccbaaa956bc199eba2b9b2bce7d3bdafffe22a9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## Dec 8 2014 (0.1.5)
|
2
|
+
|
3
|
+
Major features added, minor fixes and improvements.
|
4
|
+
|
5
|
+
* **New Features**:
|
6
|
+
|
7
|
+
* Added `Client.scan_node`, `Client.scan_all`
|
8
|
+
* Added `Client.query`
|
9
|
+
|
10
|
+
* **Fixes**
|
11
|
+
|
12
|
+
* Fixed getting back results only for specified bin names.
|
13
|
+
|
1
14
|
## Oct 27 2014 (0.1.3)
|
2
15
|
|
3
16
|
Minor fix.
|
data/README.md
CHANGED
@@ -28,15 +28,15 @@ include Aerospike
|
|
28
28
|
|
29
29
|
client = Client.new("127.0.0.1", 3000)
|
30
30
|
|
31
|
-
key = Key.new('test', '
|
32
|
-
|
31
|
+
key = Key.new('test', 'test', 'key value')
|
32
|
+
bin_map = {
|
33
33
|
'bin1' => 'value1',
|
34
34
|
'bin2' => 2,
|
35
35
|
'bin4' => ['value4', {'map1' => 'map val'}],
|
36
36
|
'bin5' => {'value5' => [124, "string value"]},
|
37
37
|
}
|
38
38
|
|
39
|
-
client.put(key,
|
39
|
+
client.put(key, bin_map)
|
40
40
|
record = client.get(key)
|
41
41
|
record.bins['bin1'] = 'other value'
|
42
42
|
|
@@ -83,6 +83,13 @@ Supported operating systems:
|
|
83
83
|
5. Build and Install the gem locally: ```rake build && rake install```
|
84
84
|
6. Run the benchmark: ```./tools/benchmark/benchmark.rb -u```
|
85
85
|
|
86
|
+
<a name="Performance"></a>
|
87
|
+
## Performance Tweaking
|
88
|
+
|
89
|
+
We are bending all efforts to improve the client's performance. In out reference benchmarks, Go client performs almost as good as the C client.
|
90
|
+
|
91
|
+
To read about performance variables, please refer to [`docs/performance.md`](docs/performance.md)
|
92
|
+
|
86
93
|
<a name="Tests"></a>
|
87
94
|
## Tests
|
88
95
|
|
@@ -100,13 +107,13 @@ A variety of example applications are provided in the [`examples`](examples) dir
|
|
100
107
|
See the [`examples/README.md`](examples/README.md) for details.
|
101
108
|
|
102
109
|
<a name="Tools"></a>
|
103
|
-
|
110
|
+
### Tools
|
104
111
|
|
105
112
|
A variety of clones of original tools are provided in the [`tools`](tools) directory.
|
106
113
|
They show how to use more advanced features of the library to reimplement the same functionality in a more concise way.
|
107
114
|
|
108
115
|
<a name="Benchmarks"></a>
|
109
|
-
|
116
|
+
## Benchmarks
|
110
117
|
|
111
118
|
Benchmark utility is provided in the [`tools/benchmark`](tools/benchmark) directory.
|
112
119
|
See the [`tools/benchmark/README.md`](tools/benchmark/README.md) for details.
|
data/lib/aerospike.rb
CHANGED
@@ -5,7 +5,8 @@ require "monitor"
|
|
5
5
|
require "timeout"
|
6
6
|
require 'resolv'
|
7
7
|
require 'msgpack'
|
8
|
-
|
8
|
+
|
9
|
+
require 'aerospike/atomic/atomic'
|
9
10
|
|
10
11
|
require 'aerospike/client'
|
11
12
|
require 'aerospike/utils/pool'
|
@@ -36,12 +37,16 @@ require 'aerospike/command/read_command'
|
|
36
37
|
require 'aerospike/command/delete_command'
|
37
38
|
require 'aerospike/key'
|
38
39
|
require 'aerospike/operation'
|
40
|
+
|
39
41
|
require 'aerospike/policy/client_policy'
|
40
42
|
require 'aerospike/policy/priority'
|
41
43
|
require 'aerospike/policy/record_exists_action'
|
42
44
|
require 'aerospike/policy/generation_policy'
|
43
45
|
require 'aerospike/policy/policy'
|
44
46
|
require 'aerospike/policy/write_policy'
|
47
|
+
require 'aerospike/policy/scan_policy'
|
48
|
+
require 'aerospike/policy/query_policy'
|
49
|
+
|
45
50
|
require 'aerospike/cluster/connection'
|
46
51
|
require 'aerospike/cluster/cluster'
|
47
52
|
require 'aerospike/cluster/node_validator'
|
@@ -64,6 +69,12 @@ require 'aerospike/task/udf_register_task'
|
|
64
69
|
require 'aerospike/task/task'
|
65
70
|
require 'aerospike/language'
|
66
71
|
|
72
|
+
require 'aerospike/query/recordset'
|
73
|
+
require 'aerospike/query/filter'
|
74
|
+
require 'aerospike/query/stream_command'
|
75
|
+
require 'aerospike/query/query_command'
|
76
|
+
require 'aerospike/query/scan_command'
|
77
|
+
|
67
78
|
module Aerospike
|
68
79
|
extend Loggable
|
69
80
|
end
|
@@ -0,0 +1,51 @@
|
|
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
|
+
module Aerospike
|
17
|
+
|
18
|
+
# Container object for client policy command.
|
19
|
+
class Atomic
|
20
|
+
|
21
|
+
def initialize(value)
|
22
|
+
@value = value
|
23
|
+
|
24
|
+
@mutex = Mutex.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(&block)
|
28
|
+
@mutex.synchronize do
|
29
|
+
@value = block.call(@value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get
|
34
|
+
ret = nil
|
35
|
+
@mutex.synchronize do
|
36
|
+
ret = @value
|
37
|
+
end
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
alias_method :value, :get
|
41
|
+
|
42
|
+
def set(value)
|
43
|
+
@mutex.synchronize do
|
44
|
+
@value = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
alias_method 'value='.to_sym, :set
|
48
|
+
|
49
|
+
end # class
|
50
|
+
|
51
|
+
end # module
|
data/lib/aerospike/client.rb
CHANGED
@@ -41,6 +41,8 @@ module Aerospike
|
|
41
41
|
def initialize(host, port, options={})
|
42
42
|
@default_policy = Policy.new
|
43
43
|
@default_write_policy = WritePolicy.new
|
44
|
+
@default_scan_policy = ScanPolicy.new
|
45
|
+
@default_query_policy = QueryPolicy.new
|
44
46
|
|
45
47
|
policy = opt_to_client_policy(options)
|
46
48
|
|
@@ -402,13 +404,14 @@ module Aerospike
|
|
402
404
|
str_cmd << "content-len=#{content.length};udf-type=#{language};"
|
403
405
|
# Send UDF to one node. That node will distribute the UDF to other nodes.
|
404
406
|
response_map = @cluster.request_info(@default_policy, str_cmd)
|
405
|
-
response, _ = response_map.first
|
406
407
|
|
407
408
|
res = {}
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
409
|
+
response_map.each do |k, response|
|
410
|
+
vals = response.to_s.split(';')
|
411
|
+
vals.each do |pair|
|
412
|
+
k, v = pair.split("=", 2)
|
413
|
+
res[k] = v
|
414
|
+
end
|
412
415
|
end
|
413
416
|
|
414
417
|
if res['error']
|
@@ -500,7 +503,7 @@ module Aerospike
|
|
500
503
|
raise Aerospike::Exceptions::Aerospike.new(UDF_BAD_RESPONSE, "#{obj}")
|
501
504
|
end
|
502
505
|
|
503
|
-
raise Aerospike::
|
506
|
+
raise Aerospike::Exceptions::Aerospike.new(UDF_BAD_RESPONSE, "Invalid UDF return value")
|
504
507
|
end
|
505
508
|
|
506
509
|
# Create secondary index.
|
@@ -560,6 +563,141 @@ module Aerospike
|
|
560
563
|
@cluster.request_info(@default_policy, *commands)
|
561
564
|
end
|
562
565
|
|
566
|
+
#-------------------------------------------------------
|
567
|
+
# Scan Operations
|
568
|
+
#-------------------------------------------------------
|
569
|
+
|
570
|
+
def scan_all(namespace, set_name, bin_names=[], options={})
|
571
|
+
policy = opt_to_scan_policy(options)
|
572
|
+
|
573
|
+
# wait until all migrations are finished
|
574
|
+
# TODO: implement
|
575
|
+
# @cluster.WaitUntillMigrationIsFinished(policy.timeout)
|
576
|
+
|
577
|
+
# Retry policy must be one-shot for scans.
|
578
|
+
# copy on write for policy
|
579
|
+
new_policy = policy.clone
|
580
|
+
|
581
|
+
nodes = @cluster.nodes
|
582
|
+
if nodes.length == 0
|
583
|
+
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Scan failed because cluster is empty.")
|
584
|
+
end
|
585
|
+
|
586
|
+
recordset = Recordset.new(policy.record_queue_size, nodes.length, :scan)
|
587
|
+
|
588
|
+
if policy.concurrent_nodes
|
589
|
+
# Use a thread per node
|
590
|
+
nodes.each do |node|
|
591
|
+
Thread.new do
|
592
|
+
abort_on_exception = true
|
593
|
+
command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
|
594
|
+
begin
|
595
|
+
command.execute
|
596
|
+
rescue => e
|
597
|
+
Aerospike.logger.error(e) unless e == Rescordset::SCAN_TERMINATED_EXCEPTION
|
598
|
+
recordset.cancel(e)
|
599
|
+
ensure
|
600
|
+
recordset.thread_finished
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
else
|
605
|
+
Thread.new do
|
606
|
+
abort_on_exception = true
|
607
|
+
nodes.each do |node|
|
608
|
+
command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
|
609
|
+
begin
|
610
|
+
command.execute
|
611
|
+
rescue => e
|
612
|
+
Aerospike.logger.error(e) unless e == Rescordset::SCAN_TERMINATED_EXCEPTION
|
613
|
+
recordset.cancel(e)
|
614
|
+
ensure
|
615
|
+
recordset.thread_finished
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
recordset
|
622
|
+
end
|
623
|
+
|
624
|
+
# ScanNode reads all records in specified namespace and set, from one node only.
|
625
|
+
# The policy can be used to specify timeouts.
|
626
|
+
def scan_node(node, namespace, set_name, bin_names=[], options={})
|
627
|
+
policy = opt_to_scan_policy(options)
|
628
|
+
# wait until all migrations are finished
|
629
|
+
# TODO: implement
|
630
|
+
# @cluster.WaitUntillMigrationIsFinished(policy.timeout)
|
631
|
+
|
632
|
+
# Retry policy must be one-shot for scans.
|
633
|
+
# copy on write for policy
|
634
|
+
new_policy = policy.clone
|
635
|
+
new_policy.max_retries = 0
|
636
|
+
|
637
|
+
node = @cluster.get_node_by_name(node) if !node.is_a?(Aerospike::Node)
|
638
|
+
|
639
|
+
recordset = Recordset.new(policy.record_queue_size, 1, :scan)
|
640
|
+
|
641
|
+
Thread.new do
|
642
|
+
abort_on_exception = true
|
643
|
+
command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
|
644
|
+
begin
|
645
|
+
command.execute
|
646
|
+
rescue => e
|
647
|
+
Aerospike.logger.error(e) unless e == Rescordset::SCAN_TERMINATED_EXCEPTION
|
648
|
+
recordset.cancel(e)
|
649
|
+
ensure
|
650
|
+
recordset.thread_finished
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
recordset
|
655
|
+
end
|
656
|
+
|
657
|
+
#--------------------------------------------------------
|
658
|
+
# Query functions (Supported by Aerospike 3 servers only)
|
659
|
+
#--------------------------------------------------------
|
660
|
+
|
661
|
+
# Query executes a query and returns a recordset.
|
662
|
+
# The query executor puts records on a channel from separate goroutines.
|
663
|
+
# The caller can concurrently pops records off the channel through the
|
664
|
+
# record channel.
|
665
|
+
#
|
666
|
+
# This method is only supported by Aerospike 3 servers.
|
667
|
+
# If the policy is nil, a default policy will be generated.
|
668
|
+
def query(statement, options={})
|
669
|
+
policy = opt_to_query_policy(options)
|
670
|
+
new_policy = policy.clone
|
671
|
+
|
672
|
+
# Always set a taskId
|
673
|
+
statement.task_id = Time.now.to_i if statement.task_id == 0
|
674
|
+
|
675
|
+
nodes = @cluster.nodes
|
676
|
+
if nodes.length == 0
|
677
|
+
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Scan failed because cluster is empty.")
|
678
|
+
end
|
679
|
+
|
680
|
+
recordset = Recordset.new(policy.record_queue_size, nodes.length, :query)
|
681
|
+
|
682
|
+
# Use a thread per node
|
683
|
+
nodes.each do |node|
|
684
|
+
Thread.new do
|
685
|
+
abort_on_exception = true
|
686
|
+
command = QueryCommand.new(node, new_policy, statement, recordset)
|
687
|
+
begin
|
688
|
+
command.execute
|
689
|
+
rescue => e
|
690
|
+
Aerospike.logger.error(e) unless e == Rescordset::QUERY_TERMINATED_EXCEPTION
|
691
|
+
recordset.cancel(e)
|
692
|
+
ensure
|
693
|
+
recordset.thread_finished
|
694
|
+
end
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
recordset
|
699
|
+
end
|
700
|
+
|
563
701
|
private
|
564
702
|
|
565
703
|
def send_info_command(policy, command)
|
@@ -580,7 +718,7 @@ module Aerospike
|
|
580
718
|
end
|
581
719
|
|
582
720
|
def opt_to_client_policy(options)
|
583
|
-
if options == {}
|
721
|
+
if options.nil? || options == {}
|
584
722
|
ClientPolicy.new
|
585
723
|
elsif options.is_a?(ClientPolicy)
|
586
724
|
options
|
@@ -594,7 +732,7 @@ module Aerospike
|
|
594
732
|
end
|
595
733
|
|
596
734
|
def opt_to_policy(options)
|
597
|
-
if options == {}
|
735
|
+
if options.nil? || options == {}
|
598
736
|
@default_policy
|
599
737
|
elsif options.is_a?(Policy)
|
600
738
|
options
|
@@ -609,7 +747,7 @@ module Aerospike
|
|
609
747
|
end
|
610
748
|
|
611
749
|
def opt_to_write_policy(options)
|
612
|
-
if options == {}
|
750
|
+
if options.nil? || options == {}
|
613
751
|
@default_write_policy
|
614
752
|
elsif options.is_a?(WritePolicy)
|
615
753
|
options
|
@@ -624,6 +762,31 @@ module Aerospike
|
|
624
762
|
end
|
625
763
|
end
|
626
764
|
|
765
|
+
def opt_to_scan_policy(options)
|
766
|
+
if options.nil? || options == {}
|
767
|
+
@default_scan_policy
|
768
|
+
elsif options.is_a?(ScanPolicy)
|
769
|
+
options
|
770
|
+
elsif options.is_a?(Hash)
|
771
|
+
ScanPolicy.new(
|
772
|
+
options[:scan_percent],
|
773
|
+
options[:concurrent_nodes],
|
774
|
+
options[:include_bin_data],
|
775
|
+
options[:fail_on_cluster_change]
|
776
|
+
)
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def opt_to_query_policy(options)
|
781
|
+
if options.nil? || options == {}
|
782
|
+
@default_query_policy
|
783
|
+
elsif options.is_a?(QueryPolicy)
|
784
|
+
options
|
785
|
+
elsif options.is_a?(Hash)
|
786
|
+
QueryPolicy.new()
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
627
790
|
def batch_execute(keys, &cmd_gen)
|
628
791
|
batch_nodes = BatchNode.generate_list(@cluster, keys)
|
629
792
|
threads = []
|
@@ -17,7 +17,7 @@
|
|
17
17
|
require 'thread'
|
18
18
|
require 'timeout'
|
19
19
|
|
20
|
-
require 'atomic'
|
20
|
+
require 'aerospike/atomic/atomic'
|
21
21
|
|
22
22
|
module Aerospike
|
23
23
|
|
@@ -210,7 +210,7 @@ module Aerospike
|
|
210
210
|
refresh_count += 1
|
211
211
|
friend_list.concat(friends) if friends
|
212
212
|
rescue => e
|
213
|
-
Aerospike.logger.
|
213
|
+
Aerospike.logger.error("Node `#{node}` refresh failed: #{e.to_s}")
|
214
214
|
end
|
215
215
|
end
|
216
216
|
end
|
@@ -285,7 +285,7 @@ module Aerospike
|
|
285
285
|
begin
|
286
286
|
seed_node_validator = NodeValidator.new(seed, @connection_timeout)
|
287
287
|
rescue => e
|
288
|
-
Aerospike.logger.
|
288
|
+
Aerospike.logger.error("Seed #{seed.to_s} failed: #{e}")
|
289
289
|
next
|
290
290
|
end
|
291
291
|
|
@@ -299,7 +299,7 @@ module Aerospike
|
|
299
299
|
begin
|
300
300
|
nv = NodeValidator.new(aliass, @connection_timeout)
|
301
301
|
rescue Exection => e
|
302
|
-
Aerospike.logger.
|
302
|
+
Aerospike.logger.error("Seed #{seed.to_s} failed: #{e}")
|
303
303
|
next
|
304
304
|
end
|
305
305
|
end
|
@@ -368,7 +368,7 @@ module Aerospike
|
|
368
368
|
list << node
|
369
369
|
|
370
370
|
rescue => e
|
371
|
-
Aerospike.logger.
|
371
|
+
Aerospike.logger.error("Add node #{node.to_s} failed: #{e}")
|
372
372
|
end
|
373
373
|
end
|
374
374
|
|
@@ -398,7 +398,7 @@ module Aerospike
|
|
398
398
|
|
399
399
|
when 2
|
400
400
|
# Two node clusters require at least one successful refresh before removing.
|
401
|
-
if refresh_count ==
|
401
|
+
if refresh_count == 2 && node.reference_count.value == 0 && !node.responded.value
|
402
402
|
# Node is not referenced nor did it respond.
|
403
403
|
remove_list << node
|
404
404
|
end
|