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