package org.skywalking.apm.agent.core.context.ids;

import org.skywalking.apm.agent.core.conf.RemoteDownstreamConfig;
import org.skywalking.apm.agent.core.dictionary.DictionaryUtil;

public final class GlobalIdGenerator {
    private static final ThreadLocal<Integer> THREAD_ID_SEQUENCE = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    private GlobalIdGenerator() {
    }

    /**
     * Generate a new id, combined by three long numbers.
     *
     * The first one represents application instance id. (most likely just an integer value, would be helpful in
     * protobuf)
     *
     * The second one represents thread id. (most likely just an integer value, would be helpful in protobuf)
     *
     * The third one also has two parts,<br/>
     * 1) a timestamp, measured in milliseconds<br/>
     * 2) a seq, in current thread, between 0(included) and 9999(included)
     *
     * Notice, a long costs 8 bytes, three longs cost 24 bytes. And at the same time, a char costs 2 bytes. So
     * sky-walking's old global and segment id like this: "S.1490097253214.-866187727.57515.1.1" which costs at least 72
     * bytes.
     *
     * @return an array contains three long numbers, which represents a unique id.
     */
    public static ID generate() {
        if (RemoteDownstreamConfig.Agent.APPLICATION_INSTANCE_ID == DictionaryUtil.nullValue()) {
            throw new IllegalStateException();
        }
        Integer seq = THREAD_ID_SEQUENCE.get();
        seq++;
        if (seq > 9999) {
            seq = 0;
        }
        THREAD_ID_SEQUENCE.set(seq);

        return new ID(
            RemoteDownstreamConfig.Agent.APPLICATION_INSTANCE_ID,
            Thread.currentThread().getId(),
            System.currentTimeMillis() * 10000 + seq
        );
    }
}

@@ -45,16 +45,41 @@
 t.ids;%0A%0A
+import java.util.Random;%0A
 import o
@@ -278,22 +278,24 @@
 dLocal%3CI
-nteger
+DContext
 %3E THREAD
@@ -326,22 +326,24 @@
 dLocal%3CI
-nteger
+DContext
 %3E() %7B%0A  
@@ -377,22 +377,24 @@
 tected I
-nteger
+DContext
  initial
@@ -422,17 +422,67 @@
  return 
-0
+new IDContext(System.currentTimeMillis(), (short)0)
 ;%0A      
@@ -1588,18 +1588,24 @@
    I
-nteger seq
+DContext context
  = T
@@ -1629,24 +1629,25 @@
 .get();%0A
+%0A
         
 seq++;%0A 
@@ -1642,242 +1642,1185 @@
     
-seq++
+return new ID(%0A            RemoteDownstreamConfig.Agent.APPLICATION_INSTANCE_ID,%0A            Thread.currentThread().getId(),%0A            context.nextSeq()%0A        )
 ;%0A    
+%7D%0A%0A
     
-if (seq %3E 9999) %7B%0A            seq = 0;%0A        %7D%0A        THREAD_ID_SEQUENCE.set(seq);%0A%0A        return new ID(%0A            RemoteDownstreamConfig.Agent.APPLICATION_INSTANCE_ID,%0A            Thread.currentThread().getId(),
+private static class IDContext %7B%0A        private long lastTimestamp;%0A        private short threadSeq;%0A%0A        // Just for considering time-shift-back only.%0A        private long runRandomTimestamp;%0A        private int lastRandomValue;%0A        private Random random;%0A%0A        private IDContext(long lastTimestamp, short threadSeq) %7B%0A            this.lastTimestamp = lastTimestamp;%0A            this.threadSeq = threadSeq;%0A        %7D%0A%0A        private long nextSeq() %7B%0A            return timestamp() * 10000 + nextThreadSeq();%0A        %7D%0A%0A        private long timestamp() %7B%0A            long currentTimeMillis = System.currentTimeMillis();%0A%0A            if (currentTimeMillis %3C lastTimestamp) %7B%0A                // Just for considering time-shift-back by Ops or OS. @hanahmily 's suggestion.%0A                if (random == null) %7B%0A                    random = new Random();%0A                %7D%0A                if (runRandomTimestamp != currentTimeMillis) %7B%0A                    lastRandomValue = random.nextInt();
 %0A   
@@ -2832,59 +2832,330 @@
     
-System.currentTimeMillis() * 10000 + seq%0A        );
+        runRandomTimestamp = currentTimeMillis;%0A                %7D%0A                return lastRandomValue;%0A            %7D else %7B%0A                lastTimestamp = currentTimeMillis;%0A                return lastTimestamp;%0A            %7D%0A        %7D%0A%0A        private short nextThreadSeq() %7B%0A            return threadSeq++;%0A        %7D
 %0A   

Support ime-shift-back by Ops or OS, try to keep the id unique.