redis-cluster-client 0.4.17 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc35dc957459313cb97b78fae42fadb612788e8e65eb49449db24b66294057fa
4
- data.tar.gz: 03cf3604892eb1a881123bdfeabaeac30816ed940a827c5390b2320b3e9e26f0
3
+ metadata.gz: ac7d6ca1474a42ad2d4027de72dbd087f6d248b3678ad2e360ed55b56dc60790
4
+ data.tar.gz: ce4de83f3f601772df8c95381a65172245967bd9568ff7959aec5858e55fb7af
5
5
  SHA512:
6
- metadata.gz: 7f8de53c53938381fb88c53ac2f13e8b7a78a3ee5653493fe0496acebaf0da57b568fb8c4436f8d19a7ba84d55018d9e4786c3726af2586581d0e7daee7b383f
7
- data.tar.gz: 7b65b9e12fd251ca8a6c278c4fb245fc08b114c9cbfac58f88e5aca81d8b9ac9aa4939ef9dbf2ee042f313764b3205a9490453b7a2310915338e50c2871e4c47
6
+ metadata.gz: 3cb2167fb97c6ab7ccba66e888521a1a119e2f17a0c373829bce0abb41518ead4005c0a7c176373c45942e9cd9fca71355b9898e7cc2b67db22d6291a76b324e
7
+ data.tar.gz: 1da6192fcb6f33359b508ff4e3445ec029373ff9606f78170051a682a29371524d6f08864f4a0887b3cbc298e462d13fe8cc9080eb29bd9cf8d965c7a766409e
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis_client/cluster/node/replica_mixin'
4
+
5
+ class RedisClient
6
+ class Cluster
7
+ class Node
8
+ class RandomReplicaOrPrimary
9
+ include ::RedisClient::Cluster::Node::ReplicaMixin
10
+
11
+ def replica_clients
12
+ keys = @replications.values.filter_map(&:sample)
13
+ @clients.select { |k, _| keys.include?(k) }
14
+ end
15
+
16
+ def clients_for_scanning(seed: nil)
17
+ random = seed.nil? ? Random : Random.new(seed)
18
+ keys = @replications.map do |primary_node_key, replica_node_keys|
19
+ decide_use_primary?(random, replica_node_keys.size) ? primary_node_key : replica_node_keys.sample(random: random)
20
+ end
21
+
22
+ clients.select { |k, _| keys.include?(k) }
23
+ end
24
+
25
+ def find_node_key_of_replica(primary_node_key, seed: nil)
26
+ random = seed.nil? ? Random : Random.new(seed)
27
+
28
+ replica_node_keys = @replications.fetch(primary_node_key, EMPTY_ARRAY)
29
+ if decide_use_primary?(random, replica_node_keys.size)
30
+ primary_node_key
31
+ else
32
+ replica_node_keys.sample(random: random) || primary_node_key
33
+ end
34
+ end
35
+
36
+ def any_replica_node_key(seed: nil)
37
+ random = seed.nil? ? Random : Random.new(seed)
38
+ @replica_node_keys.sample(random: random) || any_primary_node_key(seed: seed)
39
+ end
40
+
41
+ private
42
+
43
+ # Randomly equally likely choose node to read between primary and all replicas
44
+ # e.g. 1 primary + 1 replica = 50% probability to read from primary
45
+ # e.g. 1 primary + 2 replica = 33% probability to read from primary
46
+ # e.g. 1 primary + 0 replica = 100% probability to read from primary
47
+ def decide_use_primary?(random, replica_nodes)
48
+ primary_nodes = 1.0
49
+ total = primary_nodes + replica_nodes
50
+ random.rand < primary_nodes / total
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -5,6 +5,7 @@ require 'redis_client/config'
5
5
  require 'redis_client/cluster/errors'
6
6
  require 'redis_client/cluster/node/primary_only'
7
7
  require 'redis_client/cluster/node/random_replica'
8
+ require 'redis_client/cluster/node/random_replica_or_primary'
8
9
  require 'redis_client/cluster/node/latency_replica'
9
10
 
10
11
  class RedisClient
@@ -284,6 +285,8 @@ class RedisClient
284
285
  def make_topology_class(with_replica, replica_affinity)
285
286
  if with_replica && replica_affinity == :random
286
287
  ::RedisClient::Cluster::Node::RandomReplica
288
+ elsif with_replica && replica_affinity == :random_with_primary
289
+ ::RedisClient::Cluster::Node::RandomReplicaOrPrimary
287
290
  elsif with_replica && replica_affinity == :latency
288
291
  ::RedisClient::Cluster::Node::LatencyReplica
289
292
  else
@@ -149,7 +149,7 @@ class RedisClient
149
149
  all_replies = errors = nil
150
150
  @pipelines&.each_slice(MAX_THREADS) do |chuncked_pipelines|
151
151
  chuncked_pipelines
152
- .map { |node_key, pipeline| [node_key, build_thread_for_pipeline(@router, node_key, pipeline)] }
152
+ .map { |node_key, pipeline| [node_key, build_thread_for_pipeline(@router.find_node(node_key), pipeline)] }
153
153
  .each do |node_key, thread|
154
154
  case v = thread.value
155
155
  when ::RedisClient::Cluster::Pipeline::RedirectionNeeded
@@ -182,9 +182,9 @@ class RedisClient
182
182
  @pipelines[node_key]
183
183
  end
184
184
 
185
- def build_thread_for_pipeline(router, node_key, pipeline)
186
- Thread.new(router, node_key, pipeline) do |rt, nk, pl|
187
- replies = do_pipelining(rt.find_node(nk), pl)
185
+ def build_thread_for_pipeline(client, pipeline)
186
+ Thread.new(client, pipeline) do |cli, pl|
187
+ replies = do_pipelining(cli, pl)
188
188
  raise ReplySizeError, "commands: #{pl._size}, replies: #{replies.size}" if pl._size != replies.size
189
189
 
190
190
  replies
@@ -6,44 +6,44 @@ class RedisClient
6
6
  class Cluster
7
7
  class PubSub
8
8
  class State
9
- def initialize(client)
9
+ def initialize(client, queue)
10
10
  @client = client
11
11
  @worker = nil
12
+ @queue = queue
12
13
  end
13
14
 
14
15
  def call(command)
15
16
  @client.call_v(command)
16
17
  end
17
18
 
19
+ def ensure_worker
20
+ @worker = spawn_worker(@client, @queue) unless @worker&.alive?
21
+ end
22
+
18
23
  def close
19
24
  @worker.exit if @worker&.alive?
20
25
  @client.close
21
26
  end
22
27
 
23
- def take_message(timeout)
24
- @worker = subscribe(@client, timeout) if @worker.nil?
25
- return if @worker.alive?
26
-
27
- message = @worker.value
28
- @worker = nil
29
- message
30
- end
31
-
32
28
  private
33
29
 
34
- def subscribe(client, timeout)
35
- Thread.new(client, timeout) do |pubsub, to|
36
- pubsub.next_event(to)
37
- rescue StandardError => e
38
- e
30
+ def spawn_worker(client, queue)
31
+ Thread.new(client, queue) do |pubsub, q|
32
+ loop do
33
+ q << pubsub.next_event
34
+ rescue StandardError => e
35
+ q << e
36
+ end
39
37
  end
40
38
  end
41
39
  end
42
40
 
41
+ BUF_SIZE = Integer(ENV.fetch('REDIS_CLIENT_PUBSUB_BUF_SIZE', 1024))
42
+
43
43
  def initialize(router, command_builder)
44
44
  @router = router
45
45
  @command_builder = command_builder
46
- @state_list = []
46
+ @queue = SizedQueue.new(BUF_SIZE)
47
47
  @state_dict = {}
48
48
  end
49
49
 
@@ -56,26 +56,25 @@ class RedisClient
56
56
  end
57
57
 
58
58
  def close
59
- @state_list.each(&:close)
60
- @state_list.clear
59
+ @state_dict.each_value(&:close)
61
60
  @state_dict.clear
61
+ @queue.clear
62
62
  end
63
63
 
64
64
  def next_event(timeout = nil)
65
- return if @state_list.empty?
66
-
67
- @state_list.shuffle!
65
+ @state_dict.each_value(&:ensure_worker)
68
66
  max_duration = calc_max_duration(timeout)
69
67
  starting = obtain_current_time
68
+
70
69
  loop do
71
70
  break if max_duration > 0 && obtain_current_time - starting > max_duration
72
71
 
73
- @state_list.each do |pubsub|
74
- message = pubsub.take_message(timeout)
75
- return message if message
72
+ case event = @queue.pop(true)
73
+ when StandardError then raise event
74
+ when Array then break event
76
75
  end
77
-
78
- sleep 0.001
76
+ rescue ThreadError
77
+ sleep 0.005
79
78
  end
80
79
  end
81
80
 
@@ -100,8 +99,7 @@ class RedisClient
100
99
  def add_state(node_key)
101
100
  return @state_dict[node_key] if @state_dict.key?(node_key)
102
101
 
103
- state = State.new(@router.find_node(node_key).pubsub)
104
- @state_list << state
102
+ state = State.new(@router.find_node(node_key).pubsub, @queue)
105
103
  @state_dict[node_key] = state
106
104
  end
107
105
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-cluster-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.17
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Taishi Kasuga
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-02 00:00:00.000000000 Z
11
+ date: 2023-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client
@@ -40,6 +40,7 @@ files:
40
40
  - lib/redis_client/cluster/node/latency_replica.rb
41
41
  - lib/redis_client/cluster/node/primary_only.rb
42
42
  - lib/redis_client/cluster/node/random_replica.rb
43
+ - lib/redis_client/cluster/node/random_replica_or_primary.rb
43
44
  - lib/redis_client/cluster/node/replica_mixin.rb
44
45
  - lib/redis_client/cluster/node_key.rb
45
46
  - lib/redis_client/cluster/normalized_cmd_name.rb