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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +354 -244
  3. data/lib/aerospike/atomic/atomic.rb +1 -1
  4. data/lib/aerospike/cdt/context.rb +137 -70
  5. data/lib/aerospike/cdt/list_return_type.rb +4 -0
  6. data/lib/aerospike/cdt/map_operation.rb +6 -6
  7. data/lib/aerospike/cdt/map_policy.rb +16 -2
  8. data/lib/aerospike/cdt/map_return_type.rb +13 -1
  9. data/lib/aerospike/client.rb +137 -115
  10. data/lib/aerospike/cluster/create_connection.rb +1 -1
  11. data/lib/aerospike/cluster.rb +41 -4
  12. data/lib/aerospike/command/admin_command.rb +368 -52
  13. data/lib/aerospike/command/batch_index_command.rb +4 -8
  14. data/lib/aerospike/command/batch_index_exists_command.rb +1 -1
  15. data/lib/aerospike/command/batch_index_node.rb +1 -1
  16. data/lib/aerospike/command/batch_item.rb +1 -1
  17. data/lib/aerospike/command/command.rb +180 -123
  18. data/lib/aerospike/command/field_type.rb +25 -24
  19. data/lib/aerospike/command/login_command.rb +164 -0
  20. data/lib/aerospike/command/multi_command.rb +25 -2
  21. data/lib/aerospike/command/operate_args.rb +99 -0
  22. data/lib/aerospike/command/operate_command.rb +6 -11
  23. data/lib/aerospike/command/read_command.rb +2 -2
  24. data/lib/aerospike/connection/authenticate.rb +36 -3
  25. data/lib/aerospike/exp/exp.rb +1329 -0
  26. data/lib/aerospike/exp/exp_bit.rb +388 -0
  27. data/lib/aerospike/exp/exp_hll.rb +169 -0
  28. data/lib/aerospike/exp/exp_list.rb +403 -0
  29. data/lib/aerospike/exp/exp_map.rb +493 -0
  30. data/lib/aerospike/exp/operation.rb +56 -0
  31. data/lib/aerospike/features.rb +22 -9
  32. data/lib/aerospike/host/parse.rb +2 -2
  33. data/lib/aerospike/key.rb +10 -1
  34. data/lib/aerospike/node/refresh/info.rb +1 -1
  35. data/lib/aerospike/node/verify/name.rb +1 -1
  36. data/lib/aerospike/node/verify/partition_generation.rb +1 -1
  37. data/lib/aerospike/node/verify/peers_generation.rb +1 -1
  38. data/lib/aerospike/node/verify/rebalance_generation.rb +1 -1
  39. data/lib/aerospike/node_validator.rb +6 -1
  40. data/lib/aerospike/operation.rb +20 -22
  41. data/lib/aerospike/policy/auth_mode.rb +36 -0
  42. data/lib/aerospike/policy/client_policy.rb +4 -1
  43. data/lib/aerospike/policy/policy.rb +29 -13
  44. data/lib/aerospike/policy/query_policy.rb +35 -2
  45. data/lib/aerospike/policy/scan_policy.rb +19 -2
  46. data/lib/aerospike/privilege.rb +133 -0
  47. data/lib/aerospike/query/filter.rb +44 -32
  48. data/lib/aerospike/query/node_partitions.rb +39 -0
  49. data/lib/aerospike/query/partition_filter.rb +66 -0
  50. data/lib/aerospike/{command/roles.rb → query/partition_status.rb} +16 -19
  51. data/lib/aerospike/query/partition_tracker.rb +347 -0
  52. data/lib/aerospike/query/query_command.rb +20 -10
  53. data/lib/aerospike/query/query_executor.rb +71 -0
  54. data/lib/aerospike/query/query_partition_command.rb +267 -0
  55. data/lib/aerospike/query/recordset.rb +9 -9
  56. data/lib/aerospike/query/scan_command.rb +3 -2
  57. data/lib/aerospike/query/scan_executor.rb +71 -0
  58. data/lib/aerospike/query/scan_partition_command.rb +49 -0
  59. data/lib/aerospike/query/statement.rb +8 -1
  60. data/lib/aerospike/query/stream_command.rb +17 -0
  61. data/lib/aerospike/result_code.rb +83 -8
  62. data/lib/aerospike/role.rb +55 -0
  63. data/lib/aerospike/task/execute_task.rb +19 -16
  64. data/lib/aerospike/task/index_task.rb +1 -1
  65. data/lib/aerospike/user_role.rb +26 -1
  66. data/lib/aerospike/utils/buffer.rb +93 -29
  67. data/lib/aerospike/utils/packer.rb +7 -6
  68. data/lib/aerospike/utils/pool.rb +1 -1
  69. data/lib/aerospike/value/particle_type.rb +1 -12
  70. data/lib/aerospike/value/value.rb +35 -60
  71. data/lib/aerospike/version.rb +1 -1
  72. data/lib/aerospike.rb +156 -136
  73. metadata +24 -6
@@ -15,8 +15,8 @@
15
15
  # License for the specific language governing permissions and limitations under
16
16
  # the License.
17
17
 
18
- require 'digest'
19
- require 'base64'
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 == 'OK'
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
- command = OperateCommand.new(@cluster, policy, key, operations)
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('binary')
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['error']
435
- raise Aerospike::Exceptions::CommandRejected.new("Registration failed: #{res['error']}\nFile: #{res['file']}\nLine: #{res['line']}\nMessage: #{res['message']}")
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 == 'ok'
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 = 'udf-list'
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('=', 2)
482
+ k, v = values.split("=", 2)
484
483
  case k
485
- when 'filename'
484
+ when "filename"
486
485
  udf.filename = v
487
- when 'hash'
486
+ when "hash"
488
487
  udf.hash = v
489
- when 'type'
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?('SUCCESS') }
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?('FAILURE') }
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
- def create_index(namespace, set_name, index_name, bin_name, index_type, collection_type = nil, options = nil)
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 == 'OK'
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?('FAIL:200')
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 == 'OK'
610
+ return if response == "OK"
610
611
 
611
612
  # Index did not previously exist. Return without error.
612
- return if response.start_with?('FAIL:201')
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
- def scan_all(namespace, set_name, bin_names = nil, options = nil)
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
- recordset = Recordset.new(policy.record_queue_size, nodes.length, :scan)
643
-
644
- if policy.concurrent_nodes
645
- # Use a thread per node
646
- nodes.each do |node|
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
- # ScanNode reads all records in specified namespace and set, from one node only.
681
- # The policy can be used to specify timeouts.
682
- def scan_node(node, namespace, set_name, bin_names = nil, options = nil)
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
- node = @cluster.get_node_by_name(node) unless node.is_a?(Aerospike::Node)
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
- command = ScanCommand.new(node, new_policy, namespace, set_name, bin_names, recordset)
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
- # Query executes a query and returns a recordset.
718
- # The query executor puts records on a channel from separate goroutines.
719
- # The caller can concurrently pops records off the channel through the
720
- # record channel.
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 3 servers.
723
- # If the policy is nil, a default policy will be generated.
724
- def query(statement, options = nil)
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, "Scan failed because cluster is empty.")
709
+ raise Aerospike::Exceptions::Aerospike.new(Aerospike::ResultCode::SERVER_NOT_AVAILABLE, "Query failed because cluster is empty.")
731
710
  end
732
711
 
733
- recordset = Recordset.new(policy.record_queue_size, nodes.length, :query)
734
-
735
- # Use a thread per node
736
- nodes.each do |node|
737
- Thread.new do
738
- Thread.current.abort_on_exception = true
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 = AdminCommand.hash_password(password)
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 = AdminCommand.hash_password(password)
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::Authenticate.(conn, cluster.user, cluster.password)
35
+ Connection::AuthenticateNew.(conn, cluster)
36
36
  end
37
37
  end
38
38
  end
@@ -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 = AdminCommand.hash_password(policy.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 stablized
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 milisecond
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
- thr.join(@connection_timeout)
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?