/** * Copyright 2013 Oak Ridge National Laboratory * Author: James Horey <horeyjl@ornl.gov> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package gov.ornl.keva.core; /** * Java libs. **/ import java.util.Arrays; import java.nio.ByteBuffer; /** * A vector clock is a logical time mechanism that consists of a set of * two-tuples <ID, Time>. Vector clocks differ from regular "wall" time * in two ways: * 1) Vector clocks can be independent from each other, thus effectively * forming branches of values * 2) Vector clocks are not automatically synchronized across clients. That * means multiple clients could create independent branches unless care is * taken to share a common clock reference. * * The benefit of using vector clocks is that it is easier to reason about * independent branches of values, and that vector clocks can be used without * explicit time synchronization. * * @author James Horey */ public class VectorClock { /** * Completely precedes another clock. */ public static final short PRECEDES = 0; /** * Competely succeeds another clock. */ public static final short SUCCEEDS = 1; /** * Contains independent elements. */ public static final short INDEPENDENT = 2; /** * Exact same value. */ public static final short SAME = 3; protected byte[] id; // The identifier. protected int value; // Logical timestamp. protected long milli; // Local timestamp for pruning. /** * Construct an empty vector clock. */ public VectorClock() { this(null, 0); } /** * Construct a complete vector clock. * * @param id Client ID * @param value Tuple value */ public VectorClock(byte[] id, int value) { this.id = id; this.value = value; milli = System.currentTimeMillis(); } /** * Increase the value of the clock. */ public VectorClock inc() { return new VectorClock(id, value + 1); } /** * Get the client ID. * * @return Client ID */ public byte[] getID() { return id; } /** * Get the tuple value. * * @return Tuple value */ public int getValue() { return value; } /** * Get the local time * * @return Local wall time */ public long getLocalTime() { return milli; } /** * Set the local time. Necessary since we occasionally need * to adjust the wall time. * * @param time The wall time. */ public void setLocalTime(long time) { milli = time; } /** * Compare our current clock with the supplied clock. * * @param clock The vector clock to compare * @return One of the comparison values (same, preceeding, succeeding, or independent) */ public short compare(VectorClock clock) { if(Arrays.equals(id, clock.getID())) { if(value == clock.getValue()) { return SAME; } else if(value > clock.getValue()) { return SUCCEEDS; } else { return PRECEDES; } } // These clocks are not causually related. return INDEPENDENT; } /** * Estimate the size of the vector clock. * * @return Vector clock size in bytes. */ public int memory() { return id.length + (Integer.SIZE / 8) + (Long.SIZE / 8); } /** * Serialize the clock tuple. * * @return byte array of the clock tuple. */ public void serialize(ByteBuffer buffer) { buffer.putInt(value); // Write the tuple value. buffer.putLong(milli); // Write the local timestamp. buffer.put(id); } /** * Initialize the tuple from the byte array. * * @param data Serialized tuple */ public void unSerialize(byte[] data) { ByteBuffer buffer = ByteBuffer.wrap(data); value = buffer.getInt(); // Read the logical timestamp. milli = buffer.getLong(); // Read the local time. id = new byte[buffer.remaining()]; buffer.get(id); } /** * Equality operation for vector clocks. Necessary for hash maps, etc. * * @param obj The "equals" operation requires the parameter to be a * generic object. However, this method expects the object to be Vector clock. * @return True if the clocks are equal. False otherwise. */ @Override public boolean equals(Object obj) { if(this == obj) { return true; } VectorClock oc = (VectorClock)obj; short p = compare(oc); return p == SAME; } /** * Generate a hashcode of this vector clock. * * @return Hash value */ @Override public int hashCode() { return Arrays.hashCode(id) * value; } /** * Get a string representation of the vector clock. Mainly * used for display/debugging. * * @return String representation of the clock */ @Override public String toString() { return "<" + new String(getID()) + "-" + getValue() + ">"; } /** * Create a new vector clock using a client ID as the first value. * * @param id Client ID * @return A new vector clock */ public static VectorClock fromString(byte[] id) { return new VectorClock(id, 0); } }