/* * JBoss, Home of Professional Open Source. * Copyright 2009, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ha.timestamp; import java.io.Serializable; /** * Provides information on possible system timestamp discrepancies between * a remote node and the local node. * <p> * <strong>Usage:</strong> The local node should record the current system * time and then request the current system time from the remote node. The * local node should then record the current system time when the response * is received from the remote node. The three values are then passed to * this class' constructor. * * @author Brian Stansberry * * @version $Revision: $ */ public class TimestampDiscrepancy implements Serializable { /** The serialVersionUID */ private static final long serialVersionUID = -6193847623651196577L; /** Fake discrepancy that indicates no system clock difference */ public static final TimestampDiscrepancy NO_DISCREPANCY; static { long now = System.currentTimeMillis(); NO_DISCREPANCY = new TimestampDiscrepancy(now, now, now); } private final long fastRequestLimit; private final long fastResponseLimit; private final long minDiscrepancy; private final long maxDiscrepancy; private final long remoteTimestamp; private final long requestRoundtripTime; /** * Create a new TimestampDiscrepancy using the value returned by a remote * request plus the local timestamps for when the request started and * completed. * * @param remoteTimestamp the timestamp returned by the remote node * @param requestSent local timestamp immediately before the timestamp request was made * @param responseReceived local timestamp immediately after receipt of response * to the timestamp request */ public TimestampDiscrepancy(long remoteTimestamp, long requestSent, long responseReceived) { if (responseReceived < requestSent) { throw new IllegalArgumentException("Apparent time travel: " + responseReceived + " is less than " + requestSent); } // Limit 1: assume the remote node responded immediately when the // request was sent, i.e. 0 time to transmit request fastRequestLimit = remoteTimestamp - requestSent; // Limit 2: assume the remote node responded immediately before the // response was received, i.e. 0 time to transmit response fastResponseLimit = responseReceived - remoteTimestamp; this.minDiscrepancy = Math.min(fastRequestLimit, fastResponseLimit); this.maxDiscrepancy = Math.max(fastRequestLimit, fastResponseLimit); this.remoteTimestamp = remoteTimestamp; this.requestRoundtripTime = responseReceived - requestSent; } /** * Generates a synthetic TimestampDiscrepancy based on a value provided * by another node adjusted for the discrepancy between this node and * the private TimestampDiscrepancy(long now) { this(now, now, now); } node that provided the base value. Used to create an estimated * discrepancy between this node and a node that can no longer be contacted * directly (e.g. because it has shut down). Necessarily less accurate * than a TimestampDiscrepancy constructed via the normal method. * * @param base * @param intermediary */ public TimestampDiscrepancy(TimestampDiscrepancy base, TimestampDiscrepancy intermediary) { if (base == null) { throw new IllegalArgumentException("Null base"); } if (intermediary == null) { throw new IllegalArgumentException("Null intermediary"); } fastRequestLimit = base.fastRequestLimit + intermediary.fastRequestLimit; fastResponseLimit = base.fastResponseLimit + intermediary.fastResponseLimit; this.minDiscrepancy = Math.min(fastRequestLimit, fastResponseLimit); this.maxDiscrepancy = Math.max(fastRequestLimit, fastResponseLimit); this.remoteTimestamp = base.remoteTimestamp; this.requestRoundtripTime = base.requestRoundtripTime + intermediary.requestRoundtripTime; } /** * Gets the timestamp that the remote node returned. * * @return the remote timestamp. */ public long getRemoteTimestamp() { return remoteTimestamp; } /** * Minimum offset that would be applied to a local timestamp to obtain * the timestamp on the remote system of a simultaneously occurring event. * * @return the minimum discrepancy */ public long getMinDiscrepancy() { return minDiscrepancy; } /** * Maximum offset that would be applied to a remote timestamp to obtain * the timestamp on the local system of a simultaneously occurring event. * * @return the maximum discrepancy */ public long getMaxDiscrepancy() { return maxDiscrepancy; } /** * Gets the higher of the absolute value of {@link #getMinDiscrepancy()} * or the absolute value of {@link #getMaxDiscrepancy()}. */ public long getAbsoluteMaxDiscrepancy() { return Math.max(Math.abs(minDiscrepancy), Math.abs(maxDiscrepancy)); } /** * Gets the difference between {@link #getMinDiscrepancy()} and * {@link #getMaxDiscrepancy()} * * @return */ public long getDiscrepancyRange() { return maxDiscrepancy - minDiscrepancy; } /** * Gets a rough estimate of the timestamp discrepancy between the systems. * * @return the average between {@link #getMinDiscrepancy()} and * {@link #getMaxDiscrepancy()} */ public long getEstimatedDiscrepancy() { return (maxDiscrepancy + minDiscrepancy) / 2; } /** * Gets the number of ms it took for the remote request that returned * {@link #getRemoteTimestamp() the remote timestamp}. The longer the * request took to execute, the less accurate the timestamp discrepancy. * * @return number of ms it took to execute the timestamp request */ public long getRequestRoundtripTime() { return requestRoundtripTime; } /** * Gets the minimum value for a local timestamp that would correspond * to the remote timestamp. * * @param remoteTimestamp the remote timestamp * @return the equivalent local timestamp */ public long getMinLocalTimestamp(long remoteTimestamp) { return remoteTimestamp - minDiscrepancy; } /** * Gets the maximum value for a local timestamp that would correspond * to the remote timestamp. * * @param remoteTimestamp the remote timestamp * @return the equivalent local timestamp */ public long getMaxLocalTimestamp(long remoteTimestamp) { return remoteTimestamp + maxDiscrepancy; } /** * Gets the minimum value for a remote timestamp that would correspond * to the local timestamp. * * @param localTimestamp the local timestamp * @return the equivalent remote timestamp */ public long getMinRemoteTimestamp(long localTimestamp) { return localTimestamp + minDiscrepancy; } /** * Gets the maximum value for a remote timestamp that would correspond * to the local timestamp. * * @param localTimestamp the local timestamp * @return the equivalent remote timestamp */ public long getMaxRemoteTimestamp(long localTimestamp) { return localTimestamp - maxDiscrepancy; } }