/* * Copyright (c) 2014, PostgreSQL Global Development Group * See the LICENSE file in the project root for more information. */ package org.postgresql.hostchooser; import static java.lang.System.currentTimeMillis; import org.postgresql.util.HostSpec; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Keeps track of HostSpec targets in a global map. */ public class GlobalHostStatusTracker { private static final Map<HostSpec, HostSpecStatus> hostStatusMap = new HashMap<HostSpec, HostSpecStatus>(); /** * Store the actual observed host status. * * @param hostSpec The host whose status is known. * @param hostStatus Latest known status for the host. */ public static void reportHostStatus(HostSpec hostSpec, HostStatus hostStatus) { long now = currentTimeMillis(); synchronized (hostStatusMap) { HostSpecStatus oldStatus = hostStatusMap.get(hostSpec); if (oldStatus == null || updateStatusFromTo(oldStatus.status, hostStatus)) { hostStatusMap.put(hostSpec, new HostSpecStatus(hostSpec, hostStatus, now)); } } } private static boolean updateStatusFromTo(HostStatus oldStatus, HostStatus newStatus) { if (oldStatus == null) { return true; } if (newStatus == HostStatus.ConnectOK) { return oldStatus != HostStatus.Master && oldStatus != HostStatus.Slave; } return true; } /** * Returns a list of candidate hosts that have the required targetServerType. * * @param hostSpecs The potential list of hosts. * @param targetServerType The required target server type. * @param hostRecheckMillis How stale information is allowed. * @return candidate hosts to connect to. */ static List<HostSpecStatus> getCandidateHosts(HostSpec[] hostSpecs, HostRequirement targetServerType, long hostRecheckMillis) { List<HostSpecStatus> candidates = new ArrayList<HostSpecStatus>(hostSpecs.length); long latestAllowedUpdate = currentTimeMillis() - hostRecheckMillis; synchronized (hostStatusMap) { for (HostSpec hostSpec : hostSpecs) { HostSpecStatus hostInfo = hostStatusMap.get(hostSpec); // return null status wrapper if if the current value is not known or is too old if (hostInfo == null || hostInfo.lastUpdated < latestAllowedUpdate) { hostInfo = new HostSpecStatus(hostSpec, null, Long.MAX_VALUE); } // candidates are nodes we do not know about and the nodes with correct type if (hostInfo.status == null || targetServerType.allowConnectingTo(hostInfo.status)) { candidates.add(hostInfo); } } } return candidates; } /** * Immutable structure of known status of one HostSpec. */ static class HostSpecStatus { final HostSpec host; final HostStatus status; final long lastUpdated; HostSpecStatus(HostSpec host, HostStatus hostStatus, long lastUpdated) { this.host = host; this.status = hostStatus; this.lastUpdated = lastUpdated; } @Override public String toString() { return host.toString() + '=' + status; } } }