All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.geewit.snowflake.impl.CachedUidGenerator Maven / Gradle / Ivy

package io.geewit.snowflake.impl;

import io.geewit.snowflake.buffer.BufferPaddingExecutor;
import io.geewit.snowflake.buffer.RejectedPutBufferHandler;
import io.geewit.snowflake.buffer.RejectedTakeBufferHandler;
import io.geewit.snowflake.buffer.RingBuffer;
import io.geewit.snowflake.exception.UidGenerateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * Represents a cached implementation of {@link io.geewit.snowflake.UidGenerator} extends
 * from {@link DefaultUidGenerator}, based on a lock free {@link RingBuffer}

*

* The spring properties you can specified as below:
*

  • boostPower: RingBuffer size boost for a power of 2, Sample: boostPower is 3, it means the buffer size * will be ({@link io.geewit.snowflake.BitsAllocator#getMaxSequence()} + 1) << * {@link #boostPower}, Default as {@value #DEFAULT_BOOST_POWER} *
  • paddingFactor: Represents a percent value of (0 - 100). When the count of rest available UIDs reach the * threshold, it will trigger padding buffer. Default as{@link RingBuffer#DEFAULT_PADDING_PERCENT} * Sample: paddingFactor=20, bufferSize=1000 -> threshold=1000 * 20 /100, padding buffer will be triggered when tail-cursorscheduleInterval: Padding buffer in a schedule, specify padding buffer interval, Unit as second *
  • rejectedPutBufferHandler: Policy for rejected put buffer. Default as discard put request, just do logging *
  • rejectedTakeBufferHandler: Policy for rejected take buffer. Default as throwing up an exception * * @author geewit */ public class CachedUidGenerator extends DefaultUidGenerator { private static final Logger logger = LoggerFactory.getLogger(CachedUidGenerator.class); private static final int DEFAULT_BOOST_POWER = 3; /** * Spring properties */ private int boostPower = DEFAULT_BOOST_POWER; private final int paddingFactor = RingBuffer.DEFAULT_PADDING_PERCENT; private Long scheduleInterval; private RejectedPutBufferHandler rejectedPutBufferHandler; private RejectedTakeBufferHandler rejectedTakeBufferHandler; /** * RingBuffer */ private RingBuffer ringBuffer; private BufferPaddingExecutor bufferPaddingExecutor; public CachedUidGenerator(long workerId) { // initialize workerId & bitsAllocator super(workerId); // initialize RingBuffer & RingBufferPaddingExecutor this.initRingBuffer(); logger.info("Initialized RingBuffer successfully."); } @Override public long getUID() { try { return ringBuffer.take(); } catch (Exception e) { logger.error("Generate unique id exception. ", e); throw new UidGenerateException(e); } } @Override public String parseUID(long uid) { return super.parseUID(uid); } public void destroy() { bufferPaddingExecutor.shutdown(); } /** * Get the UIDs in the same specified second under the max sequence * * @param currentSecond currentSecond * @return UID list, size of {@link io.geewit.snowflake.BitsAllocator#getMaxSequence()} + 1 */ protected List nextIdsForOneSecond(long currentSecond) { // Initialize result list size of (max sequence + 1) int listSize = (int) bitsAllocator.getMaxSequence() + 1; // Allocate the first sequence of the second, the others can be calculated with the offset long firstSeqUid = bitsAllocator.allocate(currentSecond - epochSeconds, workerId, 0L); return IntStream.range(0, listSize).mapToObj(offset -> firstSeqUid + offset).collect(Collectors.toCollection(() -> new ArrayList<>(listSize))); } /** * Initialize RingBuffer & RingBufferPaddingExecutor */ private void initRingBuffer() { // initialize RingBuffer int bufferSize = ((int) bitsAllocator.getMaxSequence() + 1) << boostPower; this.ringBuffer = new RingBuffer(bufferSize, paddingFactor); logger.info("Initialized ring buffer size:{}, paddingFactor:{}", bufferSize, paddingFactor); // initialize RingBufferPaddingExecutor boolean usingSchedule = (scheduleInterval != null); this.bufferPaddingExecutor = new BufferPaddingExecutor(ringBuffer, this::nextIdsForOneSecond, usingSchedule); if (usingSchedule) { bufferPaddingExecutor.setScheduleInterval(scheduleInterval); } logger.info("Initialized BufferPaddingExecutor. Using schdule:{}, interval:{}", usingSchedule, scheduleInterval); // set rejected put/take handle policy this.ringBuffer.setBufferPaddingExecutor(bufferPaddingExecutor); if (rejectedPutBufferHandler != null) { this.ringBuffer.setRejectedPutHandler(rejectedPutBufferHandler); } if (rejectedTakeBufferHandler != null) { this.ringBuffer.setRejectedTakeHandler(rejectedTakeBufferHandler); } // fill in all slots of the RingBuffer bufferPaddingExecutor.paddingBuffer(); // start buffer padding threads bufferPaddingExecutor.start(); } /** * Setters for spring property */ public void setBoostPower(int boostPower) { assert boostPower > 0 : "Boost power must be positive!"; this.boostPower = boostPower; } public void setRejectedPutBufferHandler(RejectedPutBufferHandler rejectedPutBufferHandler) { assert rejectedPutBufferHandler != null : "RejectedPutBufferHandler can't be null!"; this.rejectedPutBufferHandler = rejectedPutBufferHandler; } public void setRejectedTakeBufferHandler(RejectedTakeBufferHandler rejectedTakeBufferHandler) { assert rejectedTakeBufferHandler != null : "RejectedTakeBufferHandler can't be null!"; this.rejectedTakeBufferHandler = rejectedTakeBufferHandler; } public void setScheduleInterval(long scheduleInterval) { assert scheduleInterval > 0 : "Schedule interval must positive!"; this.scheduleInterval = scheduleInterval; } }




  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy