InitializationPolicy.java

package redis.clients.jedis.mcf;

import redis.clients.jedis.annots.Experimental;

/**
 * Interface for initialization policies.
 * <p>
 * An initialization policy determines when a multi-database connection is ready to be returned
 * based on the availability of individual database connections.
 * </p>
 * <p>
 * The policy is evaluated based on the completion status of database connection health checks, and
 * the decision to continue waiting, succeed, or fail is based on the number of available, pending,
 * and failed connections.
 * </p>
 * @author Ali Takavci
 * @since 7.3
 */
@Experimental
public interface InitializationPolicy {

  enum Decision {
    CONTINUE, SUCCESS, FAIL
  }

  Decision evaluate(InitializationContext context);

  interface InitializationContext {

    int getAvailableConnections();

    int getFailedConnections();

    int getPendingConnections();

  }

  /**
   * Built-in initialization policies.
   * <p>
   * The policy is evaluated based on the completion status of database health checks, and the
   * decision to continue waiting, succeed, or fail is based on the number of available, pending,
   * and failed connections.
   * </p>
   * Built-in policies are:
   * <ul>
   * <li>{@link BuiltIn#ALL_AVAILABLE} - All databases need to be available</li>
   * <li>{@link BuiltIn#MAJORITY_AVAILABLE} - Majority of databases need to be available</li>
   * <li>{@link BuiltIn#ONE_AVAILABLE} - At least one database needs to be available</li>
   * </ul>
   */
  class BuiltIn {

    /**
     * Policy that requires all databases to be available before the connection is ready.
     */
    public static final InitializationPolicy ALL_AVAILABLE = new AllAvailablePolicy();

    /**
     * Policy that requires a majority of databases to be available before the connection is ready.
     */
    public static final InitializationPolicy MAJORITY_AVAILABLE = new MajorityAvailablePolicy();

    /**
     * Policy that requires at least one database to be available before the connection is ready.
     */
    public static final InitializationPolicy ONE_AVAILABLE = new OneAvailablePolicy();

    /*
     * All databases need to be available. The connection is ready only when all database health
     * checks have completed successfully. If any connection fails, the initialization fails. If all
     * connections are available, initialization succeeds. Otherwise, continue waiting.
     */
    private static class AllAvailablePolicy implements InitializationPolicy {

      @Override
      public Decision evaluate(InitializationContext ctx) {
        // Any failure means overall failure
        if (ctx.getFailedConnections() > 0) {
          return Decision.FAIL;
        }

        // All connections completed successfully
        if (ctx.getPendingConnections() == 0) {
          // No connections configured at all - fail
          if (ctx.getAvailableConnections() == 0) {
            return Decision.FAIL;
          }
          return Decision.SUCCESS;
        }

        return Decision.CONTINUE;
      }

    }

    /*
     * A majority of databases need to be available. The connection is ready when more than half of
     * the database connections are available. This means initialization can succeed early once
     * majority is reached, or fail early if majority becomes impossible.
     */
    private static class MajorityAvailablePolicy implements InitializationPolicy {

      @Override
      public Decision evaluate(InitializationContext ctx) {
        int total = ctx.getPendingConnections() + ctx.getAvailableConnections()
            + ctx.getFailedConnections();
        int required = (total / 2) + 1;

        // Early success - majority reached
        if (ctx.getAvailableConnections() >= required) {
          return Decision.SUCCESS;
        }

        // Early failure - impossible to reach majority
        int maxPossibleAvailable = ctx.getAvailableConnections() + ctx.getPendingConnections();
        if (maxPossibleAvailable < required) {
          return Decision.FAIL;
        }

        // Final evaluation - no more pending
        if (ctx.getPendingConnections() == 0) {
          return ctx.getAvailableConnections() >= required ? Decision.SUCCESS : Decision.FAIL;
        }

        return Decision.CONTINUE;
      }

    }

    /*
     * At least one database needs to be available. The connection is ready as soon as any database
     * connection is available. Initialization fails only if all connections fail.
     */
    private static class OneAvailablePolicy implements InitializationPolicy {

      @Override
      public Decision evaluate(InitializationContext ctx) {
        // Any success means overall success
        if (ctx.getAvailableConnections() > 0) {
          return Decision.SUCCESS;
        }

        // All connections completed with failures
        if (ctx.getPendingConnections() == 0) {
          return Decision.FAIL;
        }

        return Decision.CONTINUE;
      }

    }

  }

}