aerospike 2.19.0 → 2.26.0
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 +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?
|