aerospike 2.19.0 → 2.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +354 -244
- data/lib/aerospike/atomic/atomic.rb +1 -1
- data/lib/aerospike/cdt/context.rb +137 -70
- data/lib/aerospike/cdt/list_return_type.rb +4 -0
- data/lib/aerospike/cdt/map_operation.rb +6 -6
- data/lib/aerospike/cdt/map_policy.rb +16 -2
- data/lib/aerospike/cdt/map_return_type.rb +13 -1
- data/lib/aerospike/client.rb +137 -115
- data/lib/aerospike/cluster/create_connection.rb +1 -1
- data/lib/aerospike/cluster.rb +41 -4
- data/lib/aerospike/command/admin_command.rb +368 -52
- data/lib/aerospike/command/batch_index_command.rb +4 -8
- data/lib/aerospike/command/batch_index_exists_command.rb +1 -1
- data/lib/aerospike/command/batch_index_node.rb +1 -1
- data/lib/aerospike/command/batch_item.rb +1 -1
- data/lib/aerospike/command/command.rb +180 -123
- data/lib/aerospike/command/field_type.rb +25 -24
- data/lib/aerospike/command/login_command.rb +164 -0
- data/lib/aerospike/command/multi_command.rb +25 -2
- data/lib/aerospike/command/operate_args.rb +99 -0
- data/lib/aerospike/command/operate_command.rb +6 -11
- data/lib/aerospike/command/read_command.rb +2 -2
- data/lib/aerospike/connection/authenticate.rb +36 -3
- data/lib/aerospike/exp/exp.rb +1329 -0
- data/lib/aerospike/exp/exp_bit.rb +388 -0
- data/lib/aerospike/exp/exp_hll.rb +169 -0
- data/lib/aerospike/exp/exp_list.rb +403 -0
- data/lib/aerospike/exp/exp_map.rb +493 -0
- data/lib/aerospike/exp/operation.rb +56 -0
- data/lib/aerospike/features.rb +22 -9
- data/lib/aerospike/host/parse.rb +2 -2
- data/lib/aerospike/key.rb +10 -1
- data/lib/aerospike/node/refresh/info.rb +1 -1
- data/lib/aerospike/node/verify/name.rb +1 -1
- data/lib/aerospike/node/verify/partition_generation.rb +1 -1
- data/lib/aerospike/node/verify/peers_generation.rb +1 -1
- data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
- data/lib/aerospike/node_validator.rb +6 -1
- data/lib/aerospike/operation.rb +20 -22
- data/lib/aerospike/policy/auth_mode.rb +36 -0
- data/lib/aerospike/policy/client_policy.rb +4 -1
- data/lib/aerospike/policy/policy.rb +29 -13
- data/lib/aerospike/policy/query_policy.rb +35 -2
- data/lib/aerospike/policy/scan_policy.rb +19 -2
- data/lib/aerospike/privilege.rb +133 -0
- data/lib/aerospike/query/filter.rb +44 -32
- data/lib/aerospike/query/node_partitions.rb +39 -0
- data/lib/aerospike/query/partition_filter.rb +66 -0
- data/lib/aerospike/{command/roles.rb → query/partition_status.rb} +16 -19
- data/lib/aerospike/query/partition_tracker.rb +347 -0
- data/lib/aerospike/query/query_command.rb +20 -10
- data/lib/aerospike/query/query_executor.rb +71 -0
- data/lib/aerospike/query/query_partition_command.rb +267 -0
- data/lib/aerospike/query/recordset.rb +9 -9
- data/lib/aerospike/query/scan_command.rb +3 -2
- data/lib/aerospike/query/scan_executor.rb +71 -0
- data/lib/aerospike/query/scan_partition_command.rb +49 -0
- data/lib/aerospike/query/statement.rb +8 -1
- data/lib/aerospike/query/stream_command.rb +17 -0
- data/lib/aerospike/result_code.rb +83 -8
- data/lib/aerospike/role.rb +55 -0
- data/lib/aerospike/task/execute_task.rb +19 -16
- data/lib/aerospike/task/index_task.rb +1 -1
- data/lib/aerospike/user_role.rb +26 -1
- data/lib/aerospike/utils/buffer.rb +93 -29
- data/lib/aerospike/utils/packer.rb +7 -6
- data/lib/aerospike/utils/pool.rb +1 -1
- data/lib/aerospike/value/particle_type.rb +1 -12
- data/lib/aerospike/value/value.rb +35 -60
- data/lib/aerospike/version.rb +1 -1
- data/lib/aerospike.rb +156 -136
- metadata +24 -6
data/lib/aerospike/client.rb
CHANGED
@@ -15,8 +15,8 @@
|
|
15
15
|
# License for the specific language governing permissions and limitations under
|
16
16
|
# the License.
|
17
17
|
|
18
|
-
require
|
19
|
-
require
|
18
|
+
require "digest"
|
19
|
+
require "base64"
|
20
20
|
|
21
21
|
module Aerospike
|
22
22
|
|
@@ -36,7 +36,6 @@ module Aerospike
|
|
36
36
|
# +:fail_if_not_connected+ set to true
|
37
37
|
|
38
38
|
class Client
|
39
|
-
|
40
39
|
attr_accessor :default_admin_policy
|
41
40
|
attr_accessor :default_batch_policy
|
42
41
|
attr_accessor :default_info_policy
|
@@ -48,8 +47,7 @@ module Aerospike
|
|
48
47
|
attr_accessor :cluster
|
49
48
|
|
50
49
|
def initialize(hosts = nil, policy: ClientPolicy.new, connect: true)
|
51
|
-
|
52
|
-
hosts = ::Aerospike::Host::Parse.(hosts || ENV['AEROSPIKE_HOSTS'] || 'localhost')
|
50
|
+
hosts = ::Aerospike::Host::Parse.(hosts || ENV["AEROSPIKE_HOSTS"] || "localhost")
|
53
51
|
policy = create_policy(policy, ClientPolicy)
|
54
52
|
set_default_policies(policy.policies)
|
55
53
|
@cluster = Cluster.new(policy, hosts)
|
@@ -249,7 +247,7 @@ module Aerospike
|
|
249
247
|
end
|
250
248
|
|
251
249
|
response = send_info_command(policy, str_cmd, node).upcase
|
252
|
-
return if response ==
|
250
|
+
return if response == "OK"
|
253
251
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_ERROR, "Truncate failed: #{response}")
|
254
252
|
end
|
255
253
|
|
@@ -386,7 +384,8 @@ module Aerospike
|
|
386
384
|
def operate(key, operations, options = nil)
|
387
385
|
policy = create_policy(options, OperatePolicy, default_operate_policy)
|
388
386
|
|
389
|
-
|
387
|
+
args = OperateArgs.new(cluster, policy, default_write_policy, default_operate_policy, key, operations)
|
388
|
+
command = OperateCommand.new(@cluster, key, args)
|
390
389
|
execute_command(command)
|
391
390
|
command.record
|
392
391
|
end
|
@@ -415,7 +414,7 @@ module Aerospike
|
|
415
414
|
def register_udf(udf_body, server_path, language, options = nil)
|
416
415
|
policy = create_policy(options, Policy, default_info_policy)
|
417
416
|
|
418
|
-
content = Base64.strict_encode64(udf_body).force_encoding(
|
417
|
+
content = Base64.strict_encode64(udf_body).force_encoding("binary")
|
419
418
|
str_cmd = "udf-put:filename=#{server_path};content=#{content};"
|
420
419
|
str_cmd << "content-len=#{content.length};udf-type=#{language};"
|
421
420
|
|
@@ -424,15 +423,15 @@ module Aerospike
|
|
424
423
|
|
425
424
|
res = {}
|
426
425
|
response_map.each do |k, response|
|
427
|
-
vals = response.to_s.split(
|
426
|
+
vals = response.to_s.split(";")
|
428
427
|
vals.each do |pair|
|
429
428
|
k, v = pair.split("=", 2)
|
430
429
|
res[k] = v
|
431
430
|
end
|
432
431
|
end
|
433
432
|
|
434
|
-
if res[
|
435
|
-
raise Aerospike::Exceptions::CommandRejected.new("Registration failed: #{res[
|
433
|
+
if res["error"]
|
434
|
+
raise Aerospike::Exceptions::CommandRejected.new("Registration failed: #{res["error"]}\nFile: #{res["file"]}\nLine: #{res["line"]}\nMessage: #{res["message"]}")
|
436
435
|
end
|
437
436
|
|
438
437
|
UdfRegisterTask.new(@cluster, server_path)
|
@@ -454,7 +453,7 @@ module Aerospike
|
|
454
453
|
response_map = @cluster.request_info(policy, str_cmd)
|
455
454
|
_, response = response_map.first
|
456
455
|
|
457
|
-
if response ==
|
456
|
+
if response == "ok"
|
458
457
|
UdfRemoveTask.new(@cluster, udf_name)
|
459
458
|
else
|
460
459
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_ERROR, response)
|
@@ -466,27 +465,27 @@ module Aerospike
|
|
466
465
|
def list_udf(options = nil)
|
467
466
|
policy = create_policy(options, Policy, default_info_policy)
|
468
467
|
|
469
|
-
str_cmd =
|
468
|
+
str_cmd = "udf-list"
|
470
469
|
|
471
470
|
# Send command to one node. That node will distribute it to other nodes.
|
472
471
|
response_map = @cluster.request_info(policy, str_cmd)
|
473
472
|
_, response = response_map.first
|
474
473
|
|
475
|
-
vals = response.split(
|
474
|
+
vals = response.split(";")
|
476
475
|
|
477
476
|
vals.map do |udf_info|
|
478
|
-
next if udf_info.strip! ==
|
477
|
+
next if udf_info.strip! == ""
|
479
478
|
|
480
|
-
udf_parts = udf_info.split(
|
479
|
+
udf_parts = udf_info.split(",")
|
481
480
|
udf = UDF.new
|
482
481
|
udf_parts.each do |values|
|
483
|
-
k, v = values.split(
|
482
|
+
k, v = values.split("=", 2)
|
484
483
|
case k
|
485
|
-
when
|
484
|
+
when "filename"
|
486
485
|
udf.filename = v
|
487
|
-
when
|
486
|
+
when "hash"
|
488
487
|
udf.hash = v
|
489
|
-
when
|
488
|
+
when "type"
|
490
489
|
udf.language = v
|
491
490
|
end
|
492
491
|
end
|
@@ -501,7 +500,7 @@ module Aerospike
|
|
501
500
|
# udf file = <server udf dir>/<package name>.lua
|
502
501
|
#
|
503
502
|
# This method is only supported by Aerospike 3 servers.
|
504
|
-
def execute_udf(key, package_name, function_name, args=[], options = nil)
|
503
|
+
def execute_udf(key, package_name, function_name, args = [], options = nil)
|
505
504
|
policy = create_policy(options, WritePolicy, default_write_policy)
|
506
505
|
|
507
506
|
command = ExecuteCommand.new(@cluster, policy, key, package_name, function_name, args)
|
@@ -514,10 +513,10 @@ module Aerospike
|
|
514
513
|
result_map = record.bins
|
515
514
|
|
516
515
|
# User defined functions don't have to return a value.
|
517
|
-
key, obj = result_map.detect{ |k, _| k.include?(
|
516
|
+
key, obj = result_map.detect { |k, _| k.include?("SUCCESS") }
|
518
517
|
return obj if key
|
519
518
|
|
520
|
-
key, obj = result_map.detect{ |k, _| k.include?(
|
519
|
+
key, obj = result_map.detect { |k, _| k.include?("FAILURE") }
|
521
520
|
message = key ? obj.to_s : "Invalid UDF return value"
|
522
521
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::UDF_BAD_RESPONSE, message)
|
523
522
|
end
|
@@ -530,7 +529,7 @@ module Aerospike
|
|
530
529
|
#
|
531
530
|
# This method is only supported by Aerospike 3 servers.
|
532
531
|
# If the policy is nil, the default relevant policy will be used.
|
533
|
-
def execute_udf_on_query(statement, package_name, function_name, function_args=[], options = nil)
|
532
|
+
def execute_udf_on_query(statement, package_name, function_name, function_args = [], options = nil)
|
534
533
|
policy = create_policy(options, QueryPolicy, default_query_policy)
|
535
534
|
|
536
535
|
nodes = @cluster.nodes
|
@@ -543,10 +542,11 @@ module Aerospike
|
|
543
542
|
|
544
543
|
# Use a thread per node
|
545
544
|
nodes.each do |node|
|
545
|
+
partitions = node.cluster.node_partitions(node, statement.namespace)
|
546
546
|
Thread.new do
|
547
547
|
Thread.current.abort_on_exception = true
|
548
548
|
begin
|
549
|
-
command = QueryCommand.new(node, policy, statement, nil)
|
549
|
+
command = QueryCommand.new(node, policy, statement, nil, partitions)
|
550
550
|
execute_command(command)
|
551
551
|
rescue => e
|
552
552
|
Aerospike.logger.error(e)
|
@@ -558,7 +558,6 @@ module Aerospike
|
|
558
558
|
ExecuteTask.new(@cluster, statement)
|
559
559
|
end
|
560
560
|
|
561
|
-
|
562
561
|
# Create secondary index.
|
563
562
|
# This asynchronous server call will return before command is complete.
|
564
563
|
# The user can optionally wait for command completion by using the returned
|
@@ -567,7 +566,8 @@ module Aerospike
|
|
567
566
|
# This method is only supported by Aerospike 3 servers.
|
568
567
|
# index_type should be :string, :numeric or :geo2dsphere (requires server version 3.7 or later)
|
569
568
|
# collection_type should be :list, :mapkeys or :mapvalues
|
570
|
-
|
569
|
+
# ctx is an optional list of context. Supported on server v6.1+.
|
570
|
+
def create_index(namespace, set_name, index_name, bin_name, index_type, collection_type = nil, options = nil, ctx: nil)
|
571
571
|
if options.nil? && collection_type.is_a?(Hash)
|
572
572
|
options, collection_type = collection_type, nil
|
573
573
|
end
|
@@ -576,18 +576,19 @@ module Aerospike
|
|
576
576
|
str_cmd = "sindex-create:ns=#{namespace}"
|
577
577
|
str_cmd << ";set=#{set_name}" unless set_name.to_s.strip.empty?
|
578
578
|
str_cmd << ";indexname=#{index_name};numbins=1"
|
579
|
+
str_cmd << ";context=#{CDT::Context.base64(ctx)}" unless ctx.to_a.empty?
|
579
580
|
str_cmd << ";indextype=#{collection_type.to_s.upcase}" if collection_type
|
580
581
|
str_cmd << ";indexdata=#{bin_name},#{index_type.to_s.upcase}"
|
581
582
|
str_cmd << ";priority=normal"
|
582
583
|
|
583
584
|
# Send index command to one node. That node will distribute the command to other nodes.
|
584
585
|
response = send_info_command(policy, str_cmd).upcase
|
585
|
-
if response ==
|
586
|
+
if response == "OK"
|
586
587
|
# Return task that could optionally be polled for completion.
|
587
588
|
return IndexTask.new(@cluster, namespace, index_name)
|
588
589
|
end
|
589
590
|
|
590
|
-
if response.start_with?(
|
591
|
+
if response.start_with?("FAIL:200")
|
591
592
|
# Index has already been created. Do not need to poll for completion.
|
592
593
|
return IndexTask.new(@cluster, namespace, index_name, true)
|
593
594
|
end
|
@@ -606,10 +607,10 @@ module Aerospike
|
|
606
607
|
|
607
608
|
# Send index command to one node. That node will distribute the command to other nodes.
|
608
609
|
response = send_info_command(policy, str_cmd).upcase
|
609
|
-
return if response ==
|
610
|
+
return if response == "OK"
|
610
611
|
|
611
612
|
# Index did not previously exist. Return without error.
|
612
|
-
return if response.start_with?(
|
613
|
+
return if response.start_with?("FAIL:201")
|
613
614
|
|
614
615
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::INDEX_GENERIC, "Drop index failed: #{response}")
|
615
616
|
end
|
@@ -623,13 +624,15 @@ module Aerospike
|
|
623
624
|
# Scan Operations
|
624
625
|
#-------------------------------------------------------
|
625
626
|
|
626
|
-
|
627
|
+
# Reads records in specified namespace and set using partition filter.
|
628
|
+
# If the policy's concurrent_nodes is specified, each server node will be read in
|
629
|
+
# parallel. Otherwise, server nodes are read sequentially.
|
630
|
+
# If partition_filter is nil, all partitions will be scanned.
|
631
|
+
# If the policy is nil, the default relevant policy will be used.
|
632
|
+
# This method is only supported by Aerospike 4.9+ servers.
|
633
|
+
def scan_partitions(partition_filter, namespace, set_name, bin_names = nil, options = nil)
|
627
634
|
policy = create_policy(options, ScanPolicy, default_scan_policy)
|
628
635
|
|
629
|
-
# wait until all migrations are finished
|
630
|
-
# TODO: implement
|
631
|
-
# @cluster.WaitUntillMigrationIsFinished(policy.timeout)
|
632
|
-
|
633
636
|
# Retry policy must be one-shot for scans.
|
634
637
|
# copy on write for policy
|
635
638
|
new_policy = policy.clone
|
@@ -639,118 +642,95 @@ module Aerospike
|
|
639
642
|
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Scan failed because cluster is empty.")
|
640
643
|
end
|
641
644
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
Thread.new do
|
648
|
-
Thread.current.abort_on_exception = true
|
649
|
-
command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
|
650
|
-
begin
|
651
|
-
execute_command(command)
|
652
|
-
rescue => e
|
653
|
-
Aerospike.logger.error(e.backtrace.join("\n")) unless e == SCAN_TERMINATED_EXCEPTION
|
654
|
-
recordset.cancel(e)
|
655
|
-
ensure
|
656
|
-
recordset.thread_finished
|
657
|
-
end
|
658
|
-
end
|
659
|
-
end
|
660
|
-
else
|
661
|
-
Thread.new do
|
662
|
-
Thread.current.abort_on_exception = true
|
663
|
-
nodes.each do |node|
|
664
|
-
command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
|
665
|
-
begin
|
666
|
-
execute_command(command)
|
667
|
-
rescue => e
|
668
|
-
Aerospike.logger.error(e.backtrace.join("\n")) unless e == SCAN_TERMINATED_EXCEPTION
|
669
|
-
recordset.cancel(e)
|
670
|
-
ensure
|
671
|
-
recordset.thread_finished
|
672
|
-
end
|
673
|
-
end
|
674
|
-
end
|
645
|
+
tracker = Aerospike::PartitionTracker.new(policy, nodes, partition_filter)
|
646
|
+
recordset = Recordset.new(policy.record_queue_size, 1, :scan)
|
647
|
+
Thread.new do
|
648
|
+
Thread.current.abort_on_exception = true
|
649
|
+
ScanExecutor.scan_partitions(policy, @cluster, tracker, namespace, set_name, recordset, bin_names)
|
675
650
|
end
|
676
651
|
|
677
652
|
recordset
|
678
653
|
end
|
679
654
|
|
680
|
-
#
|
681
|
-
#
|
682
|
-
def
|
655
|
+
# Reads all records in specified namespace and set for one node only.
|
656
|
+
# If the policy is nil, the default relevant policy will be used.
|
657
|
+
def scan_node_partitions(node, namespace, set_name, bin_names = nil, options = nil)
|
683
658
|
policy = create_policy(options, ScanPolicy, default_scan_policy)
|
684
|
-
# wait until all migrations are finished
|
685
|
-
# TODO: implement
|
686
|
-
# @cluster.WaitUntillMigrationIsFinished(policy.timeout)
|
687
659
|
|
688
660
|
# Retry policy must be one-shot for scans.
|
689
661
|
# copy on write for policy
|
690
662
|
new_policy = policy.clone
|
691
|
-
new_policy.max_retries = 0
|
692
663
|
|
693
|
-
|
664
|
+
unless node.active?
|
665
|
+
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Scan failed because cluster is empty.")
|
666
|
+
end
|
694
667
|
|
668
|
+
tracker = Aerospike::PartitionTracker.new(policy, [node])
|
695
669
|
recordset = Recordset.new(policy.record_queue_size, 1, :scan)
|
696
|
-
|
697
670
|
Thread.new do
|
698
671
|
Thread.current.abort_on_exception = true
|
699
|
-
|
700
|
-
begin
|
701
|
-
execute_command(command)
|
702
|
-
rescue => e
|
703
|
-
Aerospike.logger.error(e.backtrace.join("\n")) unless e == SCAN_TERMINATED_EXCEPTION
|
704
|
-
recordset.cancel(e)
|
705
|
-
ensure
|
706
|
-
recordset.thread_finished
|
707
|
-
end
|
672
|
+
ScanExecutor.scan_partitions(policy, @cluster, tracker, namespace, set_name, recordset, bin_names)
|
708
673
|
end
|
709
674
|
|
710
675
|
recordset
|
711
676
|
end
|
712
677
|
|
678
|
+
# Reads all records in specified namespace and set from all nodes.
|
679
|
+
# If the policy's concurrent_nodes is specified, each server node will be read in
|
680
|
+
# parallel. Otherwise, server nodes are read sequentially.
|
681
|
+
# If the policy is nil, the default relevant policy will be used.
|
682
|
+
def scan_all(namespace, set_name, bin_names = nil, options = nil)
|
683
|
+
scan_partitions(Aerospike::PartitionFilter.all, namespace, set_name, bin_names, options)
|
684
|
+
end
|
685
|
+
|
686
|
+
# ScanNode reads all records in specified namespace and set, from one node only.
|
687
|
+
# The policy can be used to specify timeouts.
|
688
|
+
def scan_node(node, namespace, set_name, bin_names = nil, options = nil)
|
689
|
+
scan_node_partitions(node, namespace, set_name, bin_names, options)
|
690
|
+
end
|
691
|
+
|
713
692
|
#--------------------------------------------------------
|
714
693
|
# Query functions (Supported by Aerospike 3 servers only)
|
715
694
|
#--------------------------------------------------------
|
716
695
|
|
717
|
-
#
|
718
|
-
# The query executor puts records on
|
719
|
-
# The caller can concurrently
|
720
|
-
#
|
696
|
+
# Executes a query for specified partitions and returns a recordset.
|
697
|
+
# The query executor puts records on the queue from separate threads.
|
698
|
+
# The caller can concurrently pop records off the queue through the
|
699
|
+
# recordset.records API.
|
721
700
|
#
|
722
|
-
# This method is only supported by Aerospike
|
723
|
-
# If the policy is nil,
|
724
|
-
def
|
701
|
+
# This method is only supported by Aerospike 4.9+ servers.
|
702
|
+
# If the policy is nil, the default relevant policy will be used.
|
703
|
+
def query_partitions(partition_filter, statement, options = nil)
|
725
704
|
policy = create_policy(options, QueryPolicy, default_query_policy)
|
726
705
|
new_policy = policy.clone
|
727
706
|
|
728
707
|
nodes = @cluster.nodes
|
729
708
|
if nodes.empty?
|
730
|
-
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "
|
709
|
+
raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Query failed because cluster is empty.")
|
731
710
|
end
|
732
711
|
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
Thread.
|
738
|
-
|
739
|
-
command = QueryCommand.new(node, new_policy, statement, recordset)
|
740
|
-
begin
|
741
|
-
execute_command(command)
|
742
|
-
rescue => e
|
743
|
-
Aerospike.logger.error(e.backtrace.join("\n")) unless e == QUERY_TERMINATED_EXCEPTION
|
744
|
-
recordset.cancel(e)
|
745
|
-
ensure
|
746
|
-
recordset.thread_finished
|
747
|
-
end
|
748
|
-
end
|
712
|
+
# result recordset
|
713
|
+
recordset = Recordset.new(policy.record_queue_size, 1, :query)
|
714
|
+
tracker = PartitionTracker.new(policy, nodes, partition_filter)
|
715
|
+
Thread.new do
|
716
|
+
Thread.current.abort_on_exception = true
|
717
|
+
QueryExecutor.query_partitions(@cluster, policy, tracker, statement, recordset)
|
749
718
|
end
|
750
719
|
|
751
720
|
recordset
|
752
721
|
end
|
753
722
|
|
723
|
+
# Query executes a query and returns a recordset.
|
724
|
+
# The query executor puts records on a channel from separate threads.
|
725
|
+
# The caller can concurrently pops records off the channel through the
|
726
|
+
# record channel.
|
727
|
+
#
|
728
|
+
# This method is only supported by Aerospike 3 servers.
|
729
|
+
# If the policy is nil, a default policy will be generated.
|
730
|
+
def query(statement, options = nil)
|
731
|
+
query_partitions(Aerospike::PartitionFilter.all, statement, options)
|
732
|
+
end
|
733
|
+
|
754
734
|
#-------------------------------------------------------
|
755
735
|
# User administration
|
756
736
|
#-------------------------------------------------------
|
@@ -759,7 +739,7 @@ module Aerospike
|
|
759
739
|
# before sending to server.
|
760
740
|
def create_user(user, password, roles, options = nil)
|
761
741
|
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
762
|
-
hash =
|
742
|
+
hash = LoginCommand.hash_password(password)
|
763
743
|
command = AdminCommand.new
|
764
744
|
command.create_user(@cluster, policy, user, hash, roles)
|
765
745
|
end
|
@@ -776,7 +756,7 @@ module Aerospike
|
|
776
756
|
raise Aerospike::Exceptions::Aerospike.new(INVALID_USER) unless @cluster.user && @cluster.user != ""
|
777
757
|
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
778
758
|
|
779
|
-
hash =
|
759
|
+
hash = LoginCommand.hash_password(password)
|
780
760
|
command = AdminCommand.new
|
781
761
|
|
782
762
|
if user == @cluster.user
|
@@ -818,6 +798,50 @@ module Aerospike
|
|
818
798
|
command.query_users(@cluster, policy)
|
819
799
|
end
|
820
800
|
|
801
|
+
# Retrieve privileges for a given role.
|
802
|
+
def query_role(role, options = nil)
|
803
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
804
|
+
command = AdminCommand.new
|
805
|
+
command.query_role(@cluster, policy, role)
|
806
|
+
end
|
807
|
+
|
808
|
+
# Retrieve all roles and their privileges.
|
809
|
+
def query_roles(options = nil)
|
810
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
811
|
+
command = AdminCommand.new
|
812
|
+
command.query_roles(@cluster, policy)
|
813
|
+
end
|
814
|
+
|
815
|
+
# Create a user-defined role.
|
816
|
+
# Quotas require server security configuration "enable-quotas" to be set to true.
|
817
|
+
# Pass 0 for quota values for no limit.
|
818
|
+
def create_role(role_name, privileges = [], allowlist = [], read_quota = 0, write_quota = 0, options = nil)
|
819
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
820
|
+
command = AdminCommand.new
|
821
|
+
command.create_role(@cluster, policy, role_name, privileges, allowlist, read_quota, write_quota)
|
822
|
+
end
|
823
|
+
|
824
|
+
# Remove a user-defined role.
|
825
|
+
def drop_role(role_name, options = nil)
|
826
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
827
|
+
command = AdminCommand.new
|
828
|
+
command.drop_role(@cluster, policy, role_name)
|
829
|
+
end
|
830
|
+
|
831
|
+
# Grant privileges to a user-defined role.
|
832
|
+
def grant_privileges(role_name, privileges, options = nil)
|
833
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
834
|
+
command = AdminCommand.new
|
835
|
+
command.grant_privileges(@cluster, policy, role_name, privileges)
|
836
|
+
end
|
837
|
+
|
838
|
+
# Revoke privileges from a user-defined role.
|
839
|
+
def revoke_privileges(role_name, privileges, options = nil)
|
840
|
+
policy = create_policy(options, AdminPolicy, default_admin_policy)
|
841
|
+
command = AdminCommand.new
|
842
|
+
command.revoke_privileges(@cluster, policy, role_name, privileges)
|
843
|
+
end
|
844
|
+
|
821
845
|
private
|
822
846
|
|
823
847
|
def set_default_policies(policies)
|
@@ -942,7 +966,5 @@ module Aerospike
|
|
942
966
|
|
943
967
|
threads.each(&:join)
|
944
968
|
end
|
945
|
-
|
946
969
|
end # class
|
947
|
-
|
948
970
|
end # module
|
@@ -32,7 +32,7 @@ module Aerospike
|
|
32
32
|
).tap do |conn|
|
33
33
|
if cluster.credentials_given?
|
34
34
|
# Authenticate will raise and close connection if invalid credentials
|
35
|
-
Connection::
|
35
|
+
Connection::AuthenticateNew.(conn, cluster)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/aerospike/cluster.rb
CHANGED
@@ -27,9 +27,12 @@ module Aerospike
|
|
27
27
|
attr_reader :features, :tls_options
|
28
28
|
attr_reader :cluster_id, :aliases
|
29
29
|
attr_reader :cluster_name
|
30
|
+
attr_reader :client_policy
|
30
31
|
attr_accessor :rack_aware, :rack_id
|
32
|
+
attr_accessor :session_token, :session_expiration
|
31
33
|
|
32
34
|
def initialize(policy, hosts)
|
35
|
+
@client_policy = policy
|
33
36
|
@cluster_seeds = hosts
|
34
37
|
@fail_if_not_connected = policy.fail_if_not_connected
|
35
38
|
@connection_queue_size = policy.connection_queue_size
|
@@ -56,7 +59,7 @@ module Aerospike
|
|
56
59
|
# setup auth info for cluster
|
57
60
|
if policy.requires_authentication
|
58
61
|
@user = policy.user
|
59
|
-
@password =
|
62
|
+
@password = LoginCommand.hash_password(policy.password)
|
60
63
|
end
|
61
64
|
|
62
65
|
initialize_tls_host_names(hosts) if tls_enabled?
|
@@ -78,6 +81,15 @@ module Aerospike
|
|
78
81
|
!(@user.nil? || @user.empty?)
|
79
82
|
end
|
80
83
|
|
84
|
+
def session_valid?
|
85
|
+
@session_token && @session_expiration && @session_expiration.to_i < Time.now.to_i
|
86
|
+
end
|
87
|
+
|
88
|
+
def reset_session_info
|
89
|
+
@session_token = nil
|
90
|
+
@session_expiration = nil
|
91
|
+
end
|
92
|
+
|
81
93
|
def tls_enabled?
|
82
94
|
!tls_options.nil? && tls_options[:enable] != false
|
83
95
|
end
|
@@ -230,6 +242,27 @@ module Aerospike
|
|
230
242
|
batch_read_node(partition, replica_policy)
|
231
243
|
end
|
232
244
|
|
245
|
+
# Returns partitions pertaining to a node
|
246
|
+
def node_partitions(node, namespace)
|
247
|
+
res = []
|
248
|
+
|
249
|
+
partition_map = partitions
|
250
|
+
replica_array = partition_map[namespace]
|
251
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !replica_array
|
252
|
+
|
253
|
+
node_array = (replica_array.get)[0]
|
254
|
+
raise Aerospike::Exceptions::InvalidNamespace("namespace not found in the partition map") if !node_array
|
255
|
+
|
256
|
+
|
257
|
+
pid = 0
|
258
|
+
for tnode in node_array.get
|
259
|
+
res << pid if node == tnode
|
260
|
+
pid+=1
|
261
|
+
end
|
262
|
+
|
263
|
+
res
|
264
|
+
end
|
265
|
+
|
233
266
|
# Returns a random node on the cluster
|
234
267
|
def random_node
|
235
268
|
# Must copy array reference for copy on write semantics to work.
|
@@ -415,6 +448,7 @@ module Aerospike
|
|
415
448
|
cluster_config_changed = true
|
416
449
|
end
|
417
450
|
|
451
|
+
|
418
452
|
cluster_config_changed
|
419
453
|
end
|
420
454
|
|
@@ -429,7 +463,7 @@ module Aerospike
|
|
429
463
|
count = -1
|
430
464
|
done = false
|
431
465
|
|
432
|
-
# will run until the cluster is
|
466
|
+
# will run until the cluster is stabilized
|
433
467
|
thr = Thread.new do
|
434
468
|
loop do
|
435
469
|
tend
|
@@ -441,14 +475,17 @@ module Aerospike
|
|
441
475
|
# Break if timed out
|
442
476
|
break if done
|
443
477
|
|
444
|
-
sleep(0.001) # sleep for a
|
478
|
+
sleep(0.001) # sleep for a millisecond
|
445
479
|
|
446
480
|
count = nodes.length
|
447
481
|
end
|
448
482
|
end
|
449
483
|
|
450
484
|
# wait for the thread to finish or timeout
|
451
|
-
|
485
|
+
# This will give the client up to 10 times the timeout duration to find
|
486
|
+
# a host and connect successfully eventually, in case the DNS
|
487
|
+
# returns multiple IPs and some of them are not reachable.
|
488
|
+
thr.join(@connection_timeout * 10)
|
452
489
|
done = true
|
453
490
|
sleep(0.001)
|
454
491
|
thr.kill if thr.alive?
|