protected ReaderFailoverResult getReaderFailoverConnection()

in wrapper/src/main/java/software/amazon/jdbc/plugin/failover2/FailoverConnectionPlugin.java [394:494]


  protected ReaderFailoverResult getReaderFailoverConnection(long failoverEndTimeNano) throws TimeoutException {

    // The roles in this list might not be accurate, depending on whether the new topology has become available yet.
    final List<HostSpec> hosts = this.pluginService.getHosts();
    final Set<HostSpec> readerCandidates = hosts.stream()
        .filter(hostSpec -> HostRole.READER.equals(hostSpec.getRole()))
        .collect(Collectors.toSet());
    final HostSpec originalWriter = hosts.stream()
        .filter(hostSpec -> HostRole.WRITER.equals(hostSpec.getRole()))
        .findFirst()
        .orElse(null);
    boolean isOriginalWriterStillWriter = false;

    do {
      // First, try all original readers
      final Set<HostSpec> remainingReaders = new HashSet<>(readerCandidates);
      while (!remainingReaders.isEmpty() && System.nanoTime() < failoverEndTimeNano) {
        HostSpec readerCandidate;
        try {
          readerCandidate =
              this.pluginService.getHostSpecByStrategy(
                  new ArrayList<>(remainingReaders),
                  HostRole.READER,
                  this.failoverReaderHostSelectorStrategySetting);
        } catch (UnsupportedOperationException | SQLException ex) {
          LOGGER.finest(
              Utils.logTopology(
                  new ArrayList<>(remainingReaders),
                  Messages.get("Failover.errorSelectingReaderHost", new Object[]{ex.getMessage()})));
          break;
        }

        if (readerCandidate == null) {
          LOGGER.finest(
              Utils.logTopology(new ArrayList<>(remainingReaders), Messages.get("Failover.readerCandidateNull")));
          break;
        }

        try {
          Connection candidateConn = this.pluginService.connect(readerCandidate, this.properties, this);
          // Since the roles in the host list might not be accurate, we execute a query to check the instance's role.
          HostRole role = this.pluginService.getHostRole(candidateConn);
          if (role == HostRole.READER || this.failoverMode != STRICT_READER) {
            HostSpec updatedHostSpec = new HostSpec(readerCandidate, role);
            return new ReaderFailoverResult(candidateConn, updatedHostSpec);
          }

          // The role is WRITER or UNKNOWN, and we are in STRICT_READER mode, so the connection is not valid.
          remainingReaders.remove(readerCandidate);
          candidateConn.close();

          if (role == HostRole.WRITER) {
            // The reader candidate is actually a writer, which is not valid when failoverMode is STRICT_READER.
            // We will remove it from the list of reader candidates to avoid retrying it in future iterations.
            readerCandidates.remove(readerCandidate);
          } else {
            LOGGER.fine(
                Messages.get("Failover.strictReaderUnknownHostRole", new Object[]{readerCandidate.getUrl()}));
          }
        } catch (SQLException ex) {
          remainingReaders.remove(readerCandidate);
        }
      }

      // We were not able to connect to any of the original readers. We will try connecting to the original writer,
      // which may have been demoted to a reader.

      if (originalWriter == null || System.nanoTime() > failoverEndTimeNano) {
        // No writer was found in the original topology, or we have timed out.
        continue;
      }

      if (this.failoverMode == STRICT_READER && isOriginalWriterStillWriter) {
        // The original writer has been verified, so it is not valid when in STRICT_READER mode.
        continue;
      }

      // Try the original writer, which may have been demoted to a reader.
      try {
        Connection candidateConn = this.pluginService.connect(originalWriter, this.properties, this);
        HostRole role = this.pluginService.getHostRole(candidateConn);
        if (role == HostRole.READER || this.failoverMode != STRICT_READER) {
          HostSpec updatedHostSpec = new HostSpec(originalWriter, role);
          return new ReaderFailoverResult(candidateConn, updatedHostSpec);
        }

        // The role is WRITER or UNKNOWN, and we are in STRICT_READER mode, so the connection is not valid.
        candidateConn.close();

        if (role == HostRole.WRITER) {
          isOriginalWriterStillWriter = true;
        } else {
          LOGGER.fine(Messages.get("Failover.strictReaderUnknownHostRole", new Object[]{originalWriter.getUrl()}));
        }
      } catch (SQLException ex) {
        LOGGER.fine(Messages.get("Failover.failedReaderConnection", new Object[]{originalWriter.getUrl()}));
      }
    } while (System.nanoTime() < failoverEndTimeNano); // All hosts failed. Keep trying until we hit the timeout.

    throw new TimeoutException(Messages.get("Failover.failoverReaderTimeout"));
  }