package com.atlassian.bitbucket.mesh.coordination;

/**
 * A Lamport <em>logical</em> clock that allows us to determine whether one event happened before another event.
 * <p>
 * The clock is updated using the following rules:
 * <ul>
 *     <li>When node A receives a message from node B that contains a timestamp, {@link #onReceived(long)} MUST be
 *     called with the remote node's reported timestamp. Internally, this updates the clock to max(localTimestamp,
 *     remoteTimestamp) + 1.</li>
 *     <li>When node A sends a message that includes a timestamp to one or more other nodes, it MUST call
 *     {@link #tick()} to progress and obtain timestamp for the outgoing message. If the outgoing message is
 *     conceptually a multicast (i.e. the same event being sent to multiple nodes), {@link #tick()} should be called
 *     once and the resulting timestamp used for each outgoing message. Sending a PREPARE or COMMIT instruction
 *     to each replica is a good example of this.</li>
 *     <li>The application <em>SHOULD</em> also call {@link #tick()} to increment the counter when executing an
 *     internal event if the timestamp of that event is somehow relevant (e.g. it should be compared to ).</li>
 * </ul>
 * Care must be taken when comparing Lamport timestamps from different nodes because they can only be used to create
 * <em>partial causal ordering</em>; if event A caused or influenced event B, (happened-before) timestamp A MUST be
 * before timestamp B.
 * The reverse cannot be concluded though; if timestamp A &lt; timestamp B, A <em>MAY</em> have happened before B,
 * but may also incomparable to B. We can however conclude that B cannot have influenced A.
 * <p>
 * Note: The logical clock is internal to Mesh and only used in Mesh-Mesh communication.
 * <p>
 * See <a href="https://en.wikipedia.org/wiki/Lamport_timestamp">Lamport timestamp</a> for more background.
 */
public interface LogicalClock {

    /**
     * @return the current timestamp
     */
    long get();

    /**
     * Synchronizes the local clock with the remote timestamp if the local timestamp is lower than the remote
     * timestamp, and then increases the timestamp by 1 to reflect that a message has been received.
     *
     * @param remoteTimestamp the logical timestamp reported by a remote node
     * @return the (possibly) updated timestamp.
     */
    long onReceived(long remoteTimestamp);

    /**
     * Increases the internal event counter by 1 and returns the resulting timestamp
     * @return the updated timestamp
     */
    long tick();
}
