Search code examples
javaspringatomicintegersequence-generators

How to write a Java sequence generator for UniqueId, which will Auto-Reset to '1' (base seq. number) at midnight everyday


We have a Spring boot service where we receive files everyday, because of some issue (on producer) we are receiving multiple files with same name & date appended. The new file overwriting the old, to handle it we want to append a sequence (starting from 1) at every file name. But sequence should be auto-reset to '1' at midnight everyday.

Can anybody suggest a API or a way to reset the sequence.

To generate auto sequence, we are using AtomicSequenceGenerator , but we are unable to implement a simple auto-reset logic.

public class AtomicSequenceGenerator implements SequenceGenerator {

    private AtomicLong value = new AtomicLong(1);

    @Override
    public long getNext() {
        return value.getAndIncrement();
    }
}

Solution

  • To not receive twice a 1:

    public class AtomicSequenceGenerator implements SequenceGenerator {
    
        private AtomicLong value = new AtomicLong(1);
        private volatile LocalDate lastDate = LocalDate.now();
    
        @Override
        public long getNext() {
            LocalDate today = LocalDate.now();
            if (!today.equals(lastDate)) {
                synchronized(this) {
                    if (!today.equals(lastDate)) {
                        lastDate = today;
                        value.set(1);
                    }
                }
            }
            return value.getAndIncrement();
        }
    }
    

    That is a bit ugly, so try a single counter:

    public class AtomicSequenceGenerator implements SequenceGenerator {
    
        private static long countWithDate(long count, LocalDate date) {
            return (((long)date.getDayOfYear()) << (63L-9)) | count;
        }
    
        private static long countPart(long datedCount) {
            return datedCount & ((1L << (63L-9)) - 1);
        }
    
        private static boolean dateChanged(long datedCount, LocalDate date) {
             return (int)(datedCount >>> (63L-9)) != date.getDayOfYear();
        }
    
        private AtomicLong value = new AtomicLong(countWithDate(1, LocalDate.now()));
    
        @Override
        public long getNext() {
            long datedCount = value.getAndIncrement();
            LocalDate today = LocalDate.now();
            if (dateChanged(dateCount, today)) {
                long next = countWithDate(1L, today);
                if (value.compareAndSet(datedCount+1, next)) {
                    datedCount = next;
                } else {
                    datedCount = getNext();
                }
            }
            return datedCount;
        }
    }
    

    This uses an AtomicLong with the day-of-year packed into the counter.

    • One pulls a next counter.
    • If the date changed then:
    • when one could set the next day's 1, then give it.
    • when not, someone earlier with probably an earlier counter took the 1, and then we need to take the next again.