Saturday, February 16, 2013

Generating Unique Ids across multiple JVM and over restart using long data type only

/*
* Long.MAX_VALUE : 9 2 2 3 3 7 2 0 3 6 8 5 4 7 7 5 8 0 7  Total 19 digits
*
* lets use fist 2 digit for peer id
* now we are left with 17 digits
* if we restrict peer id between 10 - 91
*
* then rest all 17 could be all 9's to hold in long
*
* lets create 10 UIDGenerator Objects each with unique id [0-9]
*
* so now we are left with 16 digits
*
* so if we start at some time
* System.currentTimeMillis() : 1360443174566 (13 digit)
*
* ID : XXY1360443174566TTT
*                         XX : peer id
*                          Y : object instance ID
*     1360443174566 : System.currentMillis(); //seed will change on every restart
*                       TTT : 000     Allowing to generate 999 message max per millisecond

* so we can generate max 999 unique ids per millisecond
* means safe to generate approx: 1000 per millisecond, to overcome uniqueness over jvm restart
*
* we can generate 9999999999999999-1360443174566000 = 863955682543999 * 10 unique ids
* (Aprox:8000 Trillion )before getting duplicate if we start on 1360443174566
*
* Possible Failure
* 1. system time is reset
* 2. generating more than 10000 per millisecond then does not gives guarantee over jvm restart.
*/

public class UIDUtil {
    private static UIDUtil instance;
    private UIDGenerator[] uidGenerators;
    private static short MAX_ALLOWED_PEER_ID = 91;
    private static short MIN_ALLOWED_PEER_ID = 10;
    private long totalIdsGenerated;
    private long startTime;
    private UIDUtil() {
        short peerId = loadPeerId();
        uidGenerators = new UIDGenerator[10];
        for(byte i=0; i<10; i++) {
            uidGenerators[i] = new UIDGenerator(peerId, i);
        }
        startTime = System.currentTimeMillis();
    }
    public static UIDUtil getInstance() {
        if(instance == null)
            instance = new UIDUtil();
        return instance;
    }
    public long getNextId() {
        return uidGenerators[(int) ((totalIdsGenerated++)%10)].getNextID();
    }
    private short loadPeerId() {
        //TODO:    load peerId from properties, db, remote etc
        short peerId = 90;
        if(peerId > MAX_ALLOWED_PEER_ID || peerId < MIN_ALLOWED_PEER_ID) {
            peerId = (short) (MIN_ALLOWED_PEER_ID + (short)(Math.random() * ((MAX_ALLOWED_PEER_ID - MIN_ALLOWED_PEER_ID) + 1)));
            try {
                throw new Exception("Peer ID must be in the range : "+MIN_ALLOWED_PEER_ID+"-"+MAX_ALLOWED_PEER_ID+" inclusive");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return peerId;
    }
    public long getTotalIdsGenerated() {
        return totalIdsGenerated;
    }
    public double getIdProducingRate() {
        long elapsedTime = System.currentTimeMillis()-startTime;
        if(elapsedTime == 0)
            elapsedTime = 1;
        return (double) (totalIdsGenerated/elapsedTime)*1000;
    }
}

 

public class UIDGenerator {
    private long variableId ;
    private long fixedId;

    private static long MAX_ALLOWED_VARIABLE_ID = 9999999999999999l;
    public UIDGenerator(short peerId, byte index) {
        //place peer on proper decimal location
        fixedId = (long) (peerId*Math.pow(10, 17));//exp : 9100000000000000000
        fixedId += (long) (index*Math.pow(10, 16));//exp : 9130000000000000000
        //initialise with 16 digit seed
        variableId = loadVariableId();
    }

    public synchronized long getNextID() {
        long newId = variableId++;
        if(newId == MAX_ALLOWED_VARIABLE_ID) {
            variableId = loadVariableId();
        }
        return (fixedId + newId);
    }

    private long loadVariableId() {
        long currentTime = System.currentTimeMillis();
        long temp = currentTime;
        byte digits = 0;
        while(temp > 0) {
            temp /= 10;
            digits++;
        }
        return (long) (currentTime*Math.pow(10, 16-digits));
    }
}

Search Ranjeet's Blog