All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redisson.connection.MasterSlaveEntry Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.40.2
Show newest version
/**
 * Copyright (c) 2013-2024 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson.connection;

import org.redisson.api.NodeType;
import org.redisson.api.RFuture;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisConnectionException;
import org.redisson.client.RedisPubSubConnection;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.config.MasterSlaveServersConfig;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.redisson.connection.ClientConnectionsEntry.FreezeReason;
import org.redisson.connection.pool.MasterConnectionPool;
import org.redisson.connection.pool.MasterPubSubConnectionPool;
import org.redisson.connection.pool.PubSubConnectionPool;
import org.redisson.connection.pool.SlaveConnectionPool;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

/**
 *
 * @author Nikita Koksharov
 *
 */
public class MasterSlaveEntry {

    final Logger log = LoggerFactory.getLogger(getClass());

    volatile ClientConnectionsEntry masterEntry;

    int references;
    
    final MasterSlaveServersConfig config;
    final ConnectionManager connectionManager;

    final MasterConnectionPool masterConnectionPool;
    
    final MasterPubSubConnectionPool masterPubSubConnectionPool;

    final PubSubConnectionPool slavePubSubConnectionPool;

    final SlaveConnectionPool slaveConnectionPool;

    final Map client2Entry = new ConcurrentHashMap<>();

    final AtomicBoolean active = new AtomicBoolean(true);

    final AtomicBoolean noPubSubSlaves = new AtomicBoolean();

    volatile int availableSlaves = -1;

    public MasterSlaveEntry(ConnectionManager connectionManager, MasterSlaveServersConfig config) {
        this.connectionManager = connectionManager;
        this.config = config;

        slaveConnectionPool = new SlaveConnectionPool(config, connectionManager, this);
        slavePubSubConnectionPool = new PubSubConnectionPool(config, connectionManager, this);
        masterConnectionPool = new MasterConnectionPool(config, connectionManager, this);
        masterPubSubConnectionPool = new MasterPubSubConnectionPool(config, connectionManager, this);
    }

    public MasterSlaveServersConfig getConfig() {
        return config;
    }

    public CompletableFuture initSlaveBalancer(Function hostnameMapper) {
        List> result = new ArrayList<>(config.getSlaveAddresses().size());
        for (String address : config.getSlaveAddresses()) {
            RedisURI uri = new RedisURI(address);
            String hostname = hostnameMapper.apply(uri);
            CompletableFuture f = addSlave(uri, hostname);
            result.add(f);
        }

        CompletableFuture future = CompletableFuture.allOf(result.toArray(new CompletableFuture[0]));
        return future.thenAccept(v -> {
            useMasterAsSlave();
        });
    }

    private void useMasterAsSlave() {
        if (hasNoSlaves()
                || config.getReadMode() == ReadMode.MASTER_SLAVE) {
            addSlaveEntry(masterEntry);
        } else {
            removeSlaveEntry(masterEntry);
        }
    }

    private void removeSlaveEntry(ClientConnectionsEntry entry) {
        slaveConnectionPool.removeEntry(entry);
        slavePubSubConnectionPool.removeEntry(entry);
        client2Entry.remove(entry.getClient());

        if (config.getSubscriptionMode() == SubscriptionMode.SLAVE) {
            entry.reattachPubSub();
        }
    }

    private void addSlaveEntry(ClientConnectionsEntry entry) {
        if (client2Entry.get(entry.getClient()) != null) {
            return;
        }
        slaveConnectionPool.addEntry(entry);
        slavePubSubConnectionPool.addEntry(entry);
        client2Entry.put(entry.getClient(), entry);
    }

    private boolean hasNoSlaves() {
        int count = (int) client2Entry.values().stream()
                .filter(e -> !e.isFreezed() && e.getNodeType() == NodeType.SLAVE)
                .count();
        return count == 0;
    }

    public CompletableFuture setupMasterEntry(InetSocketAddress address, RedisURI uri) {
        RedisClient client = connectionManager.createClient(NodeType.MASTER, address, uri, null);
        return setupMasterEntry(client);
    }

    public CompletableFuture setupMasterEntry(RedisURI address) {
        return setupMasterEntry(address, null);
    }

    public CompletableFuture setupMasterEntry(RedisURI address, String sslHostname) {
        RedisClient client = connectionManager.createClient(NodeType.MASTER, address, sslHostname);
        return setupMasterEntry(client);
    }

    private CompletableFuture setupMasterEntry(RedisClient client) {
        CompletableFuture addrFuture = client.resolveAddr();
        return addrFuture.thenCompose(res -> {
            ClientConnectionsEntry entry = new ClientConnectionsEntry(
                    client,
                    config.getMasterConnectionMinimumIdleSize(),
                    config.getMasterConnectionPoolSize(),
                    connectionManager,
                    NodeType.MASTER,
                    config);

            List> futures = new ArrayList<>();
            CompletableFuture writeFuture = entry.initConnections(config.getMasterConnectionMinimumIdleSize());
            futures.add(writeFuture);

            if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
                CompletableFuture pubSubFuture = entry.initPubSubConnections(config.getSubscriptionConnectionMinimumIdleSize());
                futures.add(pubSubFuture);
            }
            return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .thenApply(r -> {
                        masterEntry = entry;

                        if (!config.isSlaveNotUsed()) {
                            addSlaveEntry(masterEntry);
                        }

                        masterConnectionPool.addEntry(masterEntry);
                        masterPubSubConnectionPool.addEntry(masterEntry);
                        return client;
                    });
        }).whenComplete((r, e) -> {
            if (e != null) {
                client.shutdownAsync();
            }
        });
    }

    public boolean slaveDown(InetSocketAddress address) {
        ClientConnectionsEntry connectionEntry = getEntry(address);
        ClientConnectionsEntry entry = freeze(connectionEntry, FreezeReason.MANAGER);
        if (entry == null) {
            return false;
        }
        
        return slaveDown(entry);
    }

    public boolean slaveDown(RedisURI address) {
        ClientConnectionsEntry connectionEntry = getEntry(address);
        ClientConnectionsEntry entry = freeze(connectionEntry, FreezeReason.MANAGER);
        if (entry == null) {
            return false;
        }

        return slaveDown(entry);
    }

    private boolean slaveDown(ClientConnectionsEntry entry) {
        // add master as slave if no more slaves available
        if (!config.isSlaveNotUsed()
                && !masterEntry.getClient().getAddr().equals(entry.getClient().getAddr())
                    && hasNoSlaves()) {
            addSlaveEntry(masterEntry);
            log.info("master {} is used as slave", masterEntry.getClient().getAddr());
        }

        entry.nodeDown();
        return true;
    }

    public void shutdownAndReconnectAsync(RedisClient client, Throwable cause) {
        ClientConnectionsEntry entry = getEntry(client);
        if (slaveDown(entry, FreezeReason.RECONNECT)) {
            log.error("Redis node {} has been disconnected", entry.getClient().getAddr(), cause);
            scheduleCheck(entry);
        }
    }

    private void scheduleCheck(ClientConnectionsEntry entry) {
        connectionManager.getServiceManager().newTimeout(timeout -> {
            boolean res = entry.getLock().execute(() -> {
                if (entry.getFreezeReason() != FreezeReason.RECONNECT
                        || connectionManager.getServiceManager().isShuttingDown()) {
                    return false;
                }
                return true;
            });
            if (!res) {
                return;
            }

            CompletionStage connectionFuture = entry.getClient().connectAsync();
            connectionFuture.whenComplete((c, e) -> {
                boolean res2 = entry.getLock().execute(() -> {
                    if (entry.getFreezeReason() != FreezeReason.RECONNECT) {
                        return false;
                    }
                    return true;
                });
                if (!res2) {
                    return;
                }

                if (e != null) {
                    scheduleCheck(entry);
                    return;
                }
                if (!c.isActive()) {
                    c.closeAsync();
                    scheduleCheck(entry);
                    return;
                }

                RFuture f = c.async(RedisCommands.PING);
                f.whenComplete((t, ex) -> {
                    try {
                        boolean res3 = entry.getLock().execute(() -> {
                            if (entry.getFreezeReason() != FreezeReason.RECONNECT) {
                                return false;
                            }
                            return true;
                        });
                        if (!res3) {
                            return;
                        }

                        if ("PONG".equals(t)) {
                            CompletableFuture ff = slaveUpAsync(entry);
                            ff.thenAccept(r -> {
                                if (r) {
                                    log.info("slave {} has been successfully reconnected", entry.getClient().getAddr());
                                }
                            });
                        } else {
                            scheduleCheck(entry);
                        }
                    } finally {
                        c.closeAsync();
                    }
                });
            });
        }, config.getFailedSlaveReconnectionInterval(), TimeUnit.MILLISECONDS);
    }
    
    private boolean slaveDown(ClientConnectionsEntry entry, FreezeReason freezeReason) {
        ClientConnectionsEntry e = freeze(entry, freezeReason);
        if (e == null) {
            return false;
        }

        return slaveDown(entry);
    }

    public void masterDown() {
        masterEntry.nodeDown();
    }

    public boolean hasSlave(RedisClient redisClient) {
        return getEntry(redisClient) != null;
    }

    public boolean hasSlave(InetSocketAddress addr) {
        return getEntry(addr) != null;
    }
    
    public boolean hasSlave(RedisURI addr) {
        return getEntry(addr) != null;
    }

    public CompletableFuture addSlave(RedisURI address) {
        return addSlave(address, null);
    }
    
    public CompletableFuture addSlave(InetSocketAddress address, RedisURI uri) {
        return addSlave(address, uri, null);
    }

    public CompletableFuture addSlave(RedisClient client) {
        noPubSubSlaves.set(false);
        CompletableFuture addrFuture = client.resolveAddr();
        return addrFuture.thenCompose(res -> {
            ClientConnectionsEntry entry = new ClientConnectionsEntry(client,
                    config.getSlaveConnectionMinimumIdleSize(),
                    config.getSlaveConnectionPoolSize(),
                    connectionManager,
                    NodeType.SLAVE,
                    config);

            CompletableFuture slaveFuture = entry.initConnections(config.getSlaveConnectionMinimumIdleSize());
            CompletableFuture pubSubFuture = entry.initPubSubConnections(config.getSubscriptionConnectionMinimumIdleSize());
            return CompletableFuture.allOf(slaveFuture, pubSubFuture).thenAccept(r -> {
                addSlaveEntry(entry);
            });
        }).whenComplete((r, ex) -> {
            if (ex != null) {
                client.shutdownAsync();
            }
        });
    }

    public CompletableFuture addSlave(InetSocketAddress address, RedisURI uri, String sslHostname) {
        RedisClient client = connectionManager.createClient(NodeType.SLAVE, address, uri, sslHostname);
        return addSlave(client);
    }
    
    public CompletableFuture addSlave(RedisURI address, String sslHostname) {
        RedisClient client = connectionManager.createClient(NodeType.SLAVE, address, sslHostname);
        return addSlave(client);
    }

    public Collection getAllEntries() {
        return Collections.unmodifiableCollection(client2Entry.values());
    }

    public ClientConnectionsEntry getEntry(InetSocketAddress address) {
        InetSocketAddress masterAddr = masterEntry.getClient().getAddr();
        if (masterAddr.getAddress().equals(address.getAddress()) && masterAddr.getPort() == address.getPort()) {
            return masterEntry;
        }
        for (ClientConnectionsEntry entry : client2Entry.values()) {
            InetSocketAddress addr = entry.getClient().getAddr();
            if (addr.getAddress().equals(address.getAddress()) && addr.getPort() == address.getPort()) {
                return entry;
            }
        }
        return null;
    }

    public ClientConnectionsEntry getEntry(RedisClient redisClient) {
        if (masterEntry.getClient().equals(redisClient)) {
            return masterEntry;
        }
        return client2Entry.get(redisClient);
    }

    public ClientConnectionsEntry getEntry(RedisURI addr) {
        if (addr.equals(masterEntry.getClient().getAddr())) {
            return masterEntry;
        }
        for (ClientConnectionsEntry entry : client2Entry.values()) {
            InetSocketAddress entryAddr = entry.getClient().getAddr();
            if (addr.equals(entryAddr)) {
                return entry;
            }
        }
        return null;
    }

    public boolean isInit() {
        return masterEntry != null;
    }

    public RedisClient getClient() {
        return masterEntry.getClient();
    }

    private CompletableFuture slaveUpAsync(ClientConnectionsEntry entry) {
        noPubSubSlaves.set(false);
        CompletableFuture f = unfreezeAsync(entry, FreezeReason.RECONNECT);
        return f.thenApply(r -> {
            if (r) {
                excludeMasterFromSlaves(entry.getClient().getAddr());
                return r;
            }
            return r;
        });
    }
    
    public CompletableFuture slaveUpAsync(RedisURI address) {
        noPubSubSlaves.set(false);
        CompletableFuture f = unfreezeAsync(address);
        return f.thenApply(r -> {
            if (r) {
                excludeMasterFromSlaves(address);
                return r;
            }
            return r;
        });
    }

    public boolean excludeMasterFromSlaves(RedisURI address) {
        InetSocketAddress addr = masterEntry.getClient().getAddr();
        if (address.equals(addr)
                || config.getReadMode() == ReadMode.MASTER_SLAVE) {
            return false;
        }

        removeSlaveEntry(masterEntry);
        log.info("master {} excluded from slaves", addr);
        return true;
    }

    public boolean excludeMasterFromSlaves(InetSocketAddress address) {
        InetSocketAddress addr = masterEntry.getClient().getAddr();
        if (config.isSlaveNotUsed() || addr.equals(address)
                || config.getReadMode() == ReadMode.MASTER_SLAVE) {
            return false;
        }

        removeSlaveEntry(masterEntry);
        log.info("master {} excluded from slaves", addr);
        return true;
    }

    public CompletableFuture slaveUpNoMasterExclusionAsync(RedisURI address) {
        noPubSubSlaves.set(false);
        return unfreezeAsync(address);
    }

    public CompletableFuture slaveUpNoMasterExclusionAsync(InetSocketAddress address) {
        noPubSubSlaves.set(false);
        return unfreezeAsync(address);
    }

    public CompletableFuture slaveUpAsync(InetSocketAddress address) {
        noPubSubSlaves.set(false);
        CompletableFuture f = unfreezeAsync(address);
        return f.thenApply(r -> {
            if (r) {
                excludeMasterFromSlaves(address);
                return r;
            }
            return r;
        });
    }

    /**
     * Freeze slave with redis(s)://host:port from slaves list.
     * Re-attach pub/sub listeners from it to other slave.
     * Shutdown old master client.
     * 
     * @param address of Redis
     * @return client 
     */
    public CompletableFuture changeMaster(RedisURI address) {
        ClientConnectionsEntry oldMaster = masterEntry;
        CompletableFuture future = setupMasterEntry(address);
        return changeMaster(address, oldMaster, future);
    }
    
    public CompletableFuture changeMaster(InetSocketAddress address, RedisURI uri) {
        ClientConnectionsEntry oldMaster = masterEntry;
        CompletableFuture future = setupMasterEntry(address, uri);
        return changeMaster(uri, oldMaster, future);
    }


    private CompletableFuture changeMaster(RedisURI address, ClientConnectionsEntry oldMaster,
                              CompletableFuture future) {
        return future.whenComplete((newMasterClient, e) -> {
            if (e != null) {
                if (oldMaster != masterEntry) {
                    removeMaster(masterEntry);

                    masterEntry = oldMaster;
                }
                log.error("Unable to change master from: {} to: {}", oldMaster.getClient().getAddr(), address, e);
                return;
            }

            ClientConnectionsEntry entry = getAllEntries().stream().filter(node -> node.getNodeType() == NodeType.SLAVE
                                                                                        && node.getClient().getAddr().equals(masterEntry.getClient().getAddr()))
                                                                    .findAny().orElse(null);
            // remove new master entry if it is already present as slave
            if (entry != null) {
                removeSlaveEntry(entry);
                entry.nodeDown();
                entry.shutdownAsync();
                log.info("new master {} excluded from slaves", masterEntry.getClient().getAddr());
            }

            removeMaster(oldMaster);

            // check if at least one slave is available, use master as slave if false
            if (!config.isSlaveNotUsed()) {
                useMasterAsSlave();
            }
            log.info("master {} has been changed to {}", oldMaster.getClient().getAddr(), masterEntry.getClient().getAddr());
        });
    }

    private void removeMaster(ClientConnectionsEntry masterEntry) {
        masterConnectionPool.removeEntry(masterEntry);
        masterPubSubConnectionPool.removeEntry(masterEntry);
        removeSlaveEntry(masterEntry);
        masterEntry.nodeDown();
        masterEntry.shutdownAsync();
    }

    public CompletableFuture shutdownAsync() {
        if (!active.compareAndSet(true, false)) {
            return CompletableFuture.completedFuture(null);
        }

        List> futures = new ArrayList<>(client2Entry.size() + 1);
        if (masterEntry != null) {
            futures.add(masterEntry.shutdownAsync());
        }
        for (ClientConnectionsEntry entry : client2Entry.values()) {
            futures.add(entry.shutdownAsync());
        }

        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
    }

    public CompletableFuture connectionWriteOp(RedisCommand command) {
        return masterConnectionPool.get(command, false);
    }

    public CompletableFuture trackedConnectionWriteOp(RedisCommand command) {
        return masterConnectionPool.get(command, true);
    }

    public CompletableFuture redirectedConnectionWriteOp(RedisCommand command, RedisURI addr) {
        return connectionReadOp(command, addr);
    }

    public CompletableFuture connectionReadOp(RedisCommand command, boolean trackChanges) {
        if (config.getReadMode() == ReadMode.MASTER) {
            if (trackChanges) {
                return trackedConnectionWriteOp(command);
            }
            return connectionWriteOp(command);
        }
        return slaveConnectionPool.get(command, trackChanges);
    }

    public CompletableFuture connectionReadOp(RedisCommand command, RedisURI addr) {
        ClientConnectionsEntry entry = getEntry(addr);
        if (entry != null) {
            return slaveConnectionPool.get(command, entry, false);
        }
        RedisConnectionException exception = new RedisConnectionException("Can't find entry for " + addr + " command " + command);
        CompletableFuture f = new CompletableFuture<>();
        f.completeExceptionally(exception);
        return f;
    }
    
    public CompletableFuture connectionReadOp(RedisCommand command, RedisClient client, boolean trackChanges) {
        if (config.getReadMode() == ReadMode.MASTER) {
            if (trackChanges) {
                return trackedConnectionWriteOp(command);
            }
            return connectionWriteOp(command);
        }

        ClientConnectionsEntry entry = getEntry(client);
        if (entry != null) {
            return slaveConnectionPool.get(command, entry, trackChanges);
        }
        RedisConnectionException exception = new RedisConnectionException("Can't find entry for " + client + " command " + command);
        CompletableFuture f = new CompletableFuture<>();
        f.completeExceptionally(exception);
        return f;
    }

    public CompletableFuture nextPubSubConnection(ClientConnectionsEntry entry) {
        if (entry != null) {
            return slavePubSubConnectionPool.get(entry);
        }

        if (config.getSubscriptionMode() == SubscriptionMode.MASTER) {
            return masterPubSubConnectionPool.get();
        }

        if (noPubSubSlaves.get()) {
            return masterPubSubConnectionPool.get();
        }

        CompletableFuture future = slavePubSubConnectionPool.get();
        return future.handle((r, e) -> {
            if (e != null) {
                if (noPubSubSlaves.compareAndSet(false, true)) {
                    log.warn("No slaves for master: {} PubSub connections established with the master node.",
                            masterEntry.getClient().getAddr(), e);
                }
                return masterPubSubConnectionPool.get();
            }

            return CompletableFuture.completedFuture(r);
        }).thenCompose(f -> f);
    }

    public void returnPubSubConnection(RedisPubSubConnection connection) {
        ClientConnectionsEntry entry = getEntry(connection.getRedisClient());
        if (entry == null) {
            connection.closeAsync();
            return;
        }
        entry.returnConnection(connection);
    }

    public void releaseWrite(RedisConnection connection) {
        masterEntry.returnConnection(connection);
    }

    public void releaseRead(RedisConnection connection) {
        if (config.getReadMode() == ReadMode.MASTER) {
            releaseWrite(connection);
            return;
        }

        ClientConnectionsEntry entry = getEntry(connection.getRedisClient());
        if (entry == null) {
            connection.closeAsync();
            return;
        }
        entry.returnConnection(connection);
    }

    public void incReference() {
        references++;
    }

    public int decReference() {
        return --references;
    }

    public int getReferences() {
        return references;
    }

    @Override
    public String toString() {
        return "MasterSlaveEntry [masterEntry=" + masterEntry + "]";
    }

    @SuppressWarnings("BooleanExpressionComplexity")
    private ClientConnectionsEntry freeze(ClientConnectionsEntry connectionEntry, FreezeReason freezeReason) {
        if (connectionEntry == null || (connectionEntry.getClient().getConfig().getFailedNodeDetector().isNodeFailed()
                                            && connectionEntry.getFreezeReason() == FreezeReason.RECONNECT
                                            && freezeReason == FreezeReason.RECONNECT)) {
            return null;
        }

        return connectionEntry.getLock().execute(() -> {
            if (connectionEntry.isFreezed()) {
                return null;
            }

            // only RECONNECT freeze reason could be replaced
            if (connectionEntry.getFreezeReason() == null
                    || connectionEntry.getFreezeReason() == FreezeReason.RECONNECT
                    || (freezeReason == FreezeReason.MANAGER
                    && connectionEntry.getFreezeReason() != FreezeReason.MANAGER
                    && connectionEntry.getNodeType() == NodeType.SLAVE)) {
                connectionEntry.setFreezeReason(freezeReason);
                return connectionEntry;
            }
            return connectionEntry;
        });
    }

    private CompletableFuture unfreezeAsync(RedisURI address) {
        ClientConnectionsEntry entry = getEntry(address);
        if (entry == null) {
            log.error("Can't find {} in slaves! Available slaves: {}", address, client2Entry.keySet());
            return CompletableFuture.completedFuture(false);
        }

        return unfreezeAsync(entry, FreezeReason.MANAGER);
    }

    private CompletableFuture unfreezeAsync(InetSocketAddress address) {
        ClientConnectionsEntry entry = getEntry(address);
        if (entry == null) {
            log.error("Can't find {} in slaves! Available slaves: {}", address, client2Entry.keySet());
            return CompletableFuture.completedFuture(false);
        }

        return unfreezeAsync(entry, FreezeReason.MANAGER);
    }

    private CompletableFuture unfreezeAsync(ClientConnectionsEntry entry, FreezeReason freezeReason) {
        return unfreezeAsync(entry, freezeReason, 0);
    }

    private CompletableFuture unfreezeAsync(ClientConnectionsEntry entry, FreezeReason freezeReason, int retry) {
        return entry.getLock().execute(() -> {
            if (!entry.isFreezed()) {
                return CompletableFuture.completedFuture(false);
            }

            if (freezeReason != FreezeReason.RECONNECT
                    || entry.getFreezeReason() == FreezeReason.RECONNECT) {
                if (!entry.isInitialized()) {
                    entry.setInitialized(true);

                    List> futures = new ArrayList<>(2);
                    futures.add(entry.initConnections(config.getSlaveConnectionMinimumIdleSize()));
                    futures.add(entry.initPubSubConnections(config.getSubscriptionConnectionMinimumIdleSize()));

                    CompletableFuture future = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
                    CompletableFuture f = new CompletableFuture<>();
                    future.whenComplete((r, e) -> {
                        if (e != null) {
                            int maxAttempts = connectionManager.getServiceManager().getConfig().getRetryAttempts();
                            int retryInterval = connectionManager.getServiceManager().getConfig().getRetryInterval();
                            log.error("Unable to unfreeze entry: {} attempt: {} of {}", entry, retry, maxAttempts, e);
                            entry.setInitialized(false);
                            if (retry < maxAttempts) {
                                connectionManager.getServiceManager().newTimeout(t -> {
                                    CompletableFuture ff = unfreezeAsync(entry, freezeReason, retry + 1);
                                    connectionManager.getServiceManager().transfer(ff, f);
                                }, retryInterval, TimeUnit.MILLISECONDS);
                            } else {
                                f.complete(false);
                            }
                            return;
                        }

                        entry.getClient().getConfig().getFailedNodeDetector().onConnectSuccessful();
                        entry.setFreezeReason(null);
                        log.debug("Unfreezed entry: {} after {} attempts", entry, retry);
                        f.complete(true);
                    });
                    return f;
                }
            }
            return CompletableFuture.completedFuture(false);
        });
    }

    public ClientConnectionsEntry getEntry() {
        return masterEntry;
    }

    public int getAvailableSlaves() {
        return availableSlaves;
    }

    public void setAvailableSlaves(int slaves) {
        availableSlaves = slaves;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy