# frozen_string_literal: true require_relative '../errors' class Redis class Cluster # Keep client list of node for Redis Cluster Client class Node include Enumerable ReloadNeeded = Class.new(StandardError) ROLE_SLAVE = 'slave' def initialize(options, node_flags = {}, with_replica = false) @with_replica = with_replica @node_flags = node_flags @clients = build_clients(options) end def each(&block) @clients.values.each(&block) end def sample @clients.values.sample end def find_by(node_key) @clients.fetch(node_key) rescue KeyError raise ReloadNeeded end def call_all(command, &block) try_map { |_, client| client.call(command, &block) }.values end def call_master(command, &block) try_map do |node_key, client| next if slave?(node_key) client.call(command, &block) end.values end def call_slave(command, &block) return call_master(command, &block) if replica_disabled? try_map do |node_key, client| next if master?(node_key) client.call(command, &block) end.values end def process_all(commands, &block) try_map { |_, client| client.process(commands, &block) }.values end private def replica_disabled? !@with_replica end def master?(node_key) !slave?(node_key) end def slave?(node_key) @node_flags[node_key] == ROLE_SLAVE end def build_clients(options) clients = options.map do |node_key, option| next if replica_disabled? && slave?(node_key) client = Client.new(option) client.call(%i[readonly]) if slave?(node_key) [node_key, client] end clients.compact.to_h end def try_map errors = {} results = {} @clients.each do |node_key, client| begin reply = yield(node_key, client) results[node_key] = reply unless reply.nil? rescue CommandError => err errors[node_key] = err next end end return results if errors.empty? raise CommandErrorCollection, errors end end end end