diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/AbstractIdMetadata.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/AbstractIdMetadata.java index c4b731b0cf..f085843085 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/AbstractIdMetadata.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/AbstractIdMetadata.java @@ -18,7 +18,10 @@ /** - * Abstract {@link IdMetadata}. + * Abstract base implementation of {@link IdMetadata}. + * + *

Provides common functionality for ID metadata implementations, + * including lazy generator initialization. * * @author ahoo wang */ @@ -27,6 +30,11 @@ public abstract class AbstractIdMetadata implements IdMetadata { private final IdDefinition idDefinition; private final LazyIdGenerator idGenerator; + /** + * Creates an instance with the given ID definition. + * + * @param idDefinition the ID definition + */ public AbstractIdMetadata(IdDefinition idDefinition) { this.idDefinition = idDefinition; this.idGenerator = new LazyIdGenerator(idDefinition.getGeneratorName()); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdGetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdGetter.java index 43418b8770..adca19ce3a 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdGetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdGetter.java @@ -14,12 +14,20 @@ package me.ahoo.cosid.accessor; /** - * CosId Getter. + * Interface for getting the ID value from an object. + * + *

Used by frameworks (MyBatis, etc.) to extract the ID from entities. * * @author ahoo wang */ public interface CosIdGetter { + /** + * Gets the ID value from the target object. + * + * @param target the object to get ID from + * @return the ID value + */ Object getId(Object target); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdSetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdSetter.java index 4bda2759b7..cf30286dd4 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdSetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/CosIdSetter.java @@ -14,12 +14,20 @@ package me.ahoo.cosid.accessor; /** - * CosId Setter. + * Interface for setting the ID value on an object. + * + *

Used by frameworks (MyBatis, etc.) to set the ID on entities. * * @author ahoo wang */ public interface CosIdSetter { + /** + * Sets the ID value on the target object. + * + * @param target the object to set ID on + * @param id the ID value to set + */ void setId(Object target, Object id); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/DefaultCosIdAccessor.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/DefaultCosIdAccessor.java index a2f5294b56..ae46cfdf59 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/DefaultCosIdAccessor.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/DefaultCosIdAccessor.java @@ -21,6 +21,9 @@ /** * Default {@link CosIdAccessor} implementation. * + *

Provides ID get/set operations on entities and ensures IDs are generated + * when missing. Supports Long, Integer, and String ID types. + * * @author ahoo wang */ public class DefaultCosIdAccessor extends AbstractIdMetadata implements CosIdAccessor { @@ -29,6 +32,13 @@ public class DefaultCosIdAccessor extends AbstractIdMetadata implements CosIdAcc private final CosIdSetter setter; private final EnsureId ensureId; + /** + * Creates a new accessor. + * + * @param idDefinition the ID definition + * @param getter the getter for extracting ID from entities + * @param setter the setter for setting ID on entities + */ public DefaultCosIdAccessor(IdDefinition idDefinition, CosIdGetter getter, CosIdSetter setter) { super(idDefinition); this.getter = getter; @@ -57,10 +67,20 @@ public void setId(Object target, Object id) { setter.setId(target, id); } + /** + * Gets the getter. + * + * @return the getter + */ public CosIdGetter getGetter() { return getter; } + /** + * Gets the setter. + * + * @return the setter + */ public CosIdSetter getSetter() { return setter; } @@ -71,6 +91,9 @@ public boolean ensureId(Object target) { return ensureId.ensureId(target); } + /** + * Ensures ID is generated for String ID types. + */ public class EnsureStringId implements EnsureId { @Override @@ -85,6 +108,9 @@ public boolean ensureId(Object target) { } } + /** + * Ensures ID is generated for Long ID types. + */ public class EnsureLongId implements EnsureId { private static final long MIN_ID = 0; @@ -99,6 +125,9 @@ public boolean ensureId(Object target) { } } + /** + * Ensures ID is generated for Integer ID types. + */ public class EnsureIntegerId implements EnsureId { private static final int MIN_ID = 0; private final IntegerIdGenerator integerIdGenerator; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdMetadata.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdMetadata.java index 8a5819c8f0..1dc450c2ae 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdMetadata.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdMetadata.java @@ -20,29 +20,62 @@ import java.lang.reflect.Field; /** - * Id Metadata. + * Metadata container for ID field information. + * + *

Provides access to ID definition, generator, and field metadata + * for objects annotated with {@code @CosId}. * * @author ahoo wang */ @Immutable public interface IdMetadata { + /** + * Gets the ID definition. + * + * @return the ID definition + */ IdDefinition getIdDefinition(); + /** + * Gets the generator name from the ID definition. + * + * @return the generator name + */ default String getGeneratorName() { return getIdDefinition().getGeneratorName(); } + /** + * Gets the ID generator. + * + * @return the ID generator + */ IdGenerator getIdGenerator(); + /** + * Gets the ID field from the definition. + * + * @return the ID field + */ default Field getIdField() { return getIdDefinition().getIdField(); } + /** + * Gets the declaring class of the ID field. + * + * @return the declaring class + */ default Class getIdDeclaringClass() { return getIdField().getDeclaringClass(); } + /** + * Gets the type of the ID field. + * + * @return the ID type + */ default Class getIdType() { return getIdDefinition().getIdType(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdTypeNotSupportException.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdTypeNotSupportException.java index bbf4b8c34b..44a7031bca 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdTypeNotSupportException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/IdTypeNotSupportException.java @@ -20,7 +20,9 @@ import java.lang.reflect.Field; /** - * ID Type Not Support Exception. + * Exception thrown when an ID field type is not supported. + * + *

CosId only supports Long, Integer, and String ID types. * * @author ahoo wang */ @@ -28,11 +30,21 @@ public class IdTypeNotSupportException extends CosIdException { private final Field idField; + /** + * Creates a new exception. + * + * @param idField the unsupported ID field + */ public IdTypeNotSupportException(Field idField) { super(Strings.lenientFormat("ID type only supports Long/long/Integer/int/String, idField:[%s]!", idField)); this.idField = idField; } + /** + * Gets the unsupported ID field. + * + * @return the field + */ public Field getIdField() { return idField; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/MultipleIdNotSupportException.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/MultipleIdNotSupportException.java index 18d8204153..2f0793019c 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/MultipleIdNotSupportException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/MultipleIdNotSupportException.java @@ -18,7 +18,9 @@ import com.google.common.base.Strings; /** - * Multiple Id Not Support Exception. + * Exception thrown when an entity has multiple @CosId fields. + * + *

CosId only supports a single ID field per entity. * * @author ahoo wang */ @@ -26,11 +28,21 @@ public class MultipleIdNotSupportException extends CosIdException { private final Class declaringClass; + /** + * Creates a new exception. + * + * @param declaringClass the class with multiple ID fields + */ public MultipleIdNotSupportException(Class declaringClass) { super(Strings.lenientFormat("Not support defining multiple CosIds, declaringClass:[%s]!", declaringClass)); this.declaringClass = declaringClass; } + /** + * Gets the declaring class. + * + * @return the class + */ public Class getDeclaringClass() { return declaringClass; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldGetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldGetter.java index 5046db0211..8dc58e6b45 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldGetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldGetter.java @@ -20,13 +20,18 @@ import java.lang.reflect.Field; /** - * Field Getter. + * ID getter that accesses field directly. * * @author ahoo wang */ public class FieldGetter implements CosIdGetter { private final Field idField; + /** + * Creates a getter for the specified field. + * + * @param idField the field to access + */ public FieldGetter(Field idField) { CosIdAccessor.ensureAccessible(idField); this.idField = idField; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldSetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldSetter.java index 071d365028..26e03167b7 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldSetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/field/FieldSetter.java @@ -20,13 +20,18 @@ import java.lang.reflect.Field; /** - * Field Setter. + * ID setter that accesses field directly. * * @author ahoo wang */ public class FieldSetter implements CosIdSetter { private final Field idField; + /** + * Creates a setter for the specified field. + * + * @param idField the field to access + */ public FieldSetter(Field idField) { CosIdAccessor.ensureAccessible(idField); this.idField = idField; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodGetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodGetter.java index bcd7468449..133602f472 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodGetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodGetter.java @@ -21,18 +21,28 @@ import java.lang.reflect.Method; /** - * Method Getter. + * ID getter that accesses via getter method. * * @author ahoo wang */ public class MethodGetter implements CosIdGetter { private final Method getter; + /** + * Creates a getter for the specified method. + * + * @param getter the getter method + */ public MethodGetter(Method getter) { CosIdAccessor.ensureAccessible(getter); this.getter = getter; } + /** + * Gets the getter method. + * + * @return the getter method + */ public Method getGetter() { return getter; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodSetter.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodSetter.java index 7c4c0c0fca..62b7ca270b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodSetter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/method/MethodSetter.java @@ -21,18 +21,28 @@ import java.lang.reflect.Method; /** - * Method Setter. + * ID setter that accesses via setter method. * * @author ahoo wang */ public class MethodSetter implements CosIdSetter { private final Method setter; + /** + * Creates a setter for the specified method. + * + * @param setter the setter method + */ public MethodSetter(Method setter) { CosIdAccessor.ensureAccessible(setter); this.setter = setter; } + /** + * Gets the setter method. + * + * @return the setter method + */ public Method getSetter() { return setter; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CompositeFieldDefinitionParser.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CompositeFieldDefinitionParser.java index 9e617c8217..e109579c92 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CompositeFieldDefinitionParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CompositeFieldDefinitionParser.java @@ -19,18 +19,25 @@ import java.util.List; /** - * Composite {@link FieldDefinitionParser}. + * Composite parser that tries multiple parsers in sequence. + * + *

Tries each parser in order until one returns a non-NOT_FOUND result. * * @see FieldDefinitionParser * @see NamedDefinitionParser */ public class CompositeFieldDefinitionParser implements FieldDefinitionParser { private final List fieldDefinitionParsers; - + + /** + * Creates a composite parser with the specified parsers. + * + * @param fieldDefinitionParsers the list of parsers to try + */ public CompositeFieldDefinitionParser(List fieldDefinitionParsers) { this.fieldDefinitionParsers = fieldDefinitionParsers; } - + @Override public IdDefinition parse(List> lookupClassList, Field field) { for (FieldDefinitionParser fieldDefinitionParser : fieldDefinitionParsers) { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CosIdAccessorParser.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CosIdAccessorParser.java index 7f8f509462..68d5c81ced 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CosIdAccessorParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/CosIdAccessorParser.java @@ -16,12 +16,18 @@ import me.ahoo.cosid.accessor.CosIdAccessor; /** - * CosIdAccessor Parser. + * Parser for creating {@link CosIdAccessor} from classes. * * @author ahoo wang */ public interface CosIdAccessorParser { + /** + * Parses a class to create its accessor. + * + * @param clazz the class to parse + * @return the accessor + */ CosIdAccessor parse(Class clazz); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/DefaultAccessorParser.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/DefaultAccessorParser.java index 25eebe6740..1b70883036 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/DefaultAccessorParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/DefaultAccessorParser.java @@ -41,7 +41,10 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Default {@link CosIdAccessorParser} implementation. + * Default implementation of {@link CosIdAccessorParser}. + * + *

Parses classes to find @CosId annotated fields + * and creates accessors using getters/setters or direct field access. * * @author ahoo wang */ @@ -54,6 +57,11 @@ public class DefaultAccessorParser implements CosIdAccessorParser { private final ConcurrentHashMap, CosIdAccessor> classMapAccessor = new ConcurrentHashMap<>(); private final FieldDefinitionParser definitionParser; + /** + * Creates a parser with the specified definition parser. + * + * @param definitionParser the field definition parser + */ public DefaultAccessorParser(FieldDefinitionParser definitionParser) { this.definitionParser = definitionParser; } @@ -63,6 +71,12 @@ public CosIdAccessor parse(Class clazz) { return classMapAccessor.computeIfAbsent(clazz, (key) -> parseClass(clazz)); } + /** + * Capitalizes a string (first character upper case). + * + * @param name the name to capitalize + * @return the capitalized name + */ public static String capitalize(String name) { if (name == null || name.length() == 0) { return name; @@ -70,6 +84,12 @@ public static String capitalize(String name) { return name.substring(0, 1).toUpperCase(ENGLISH) + name.substring(1); } + /** + * Parses a getter method for a field. + * + * @param field the field + * @return the getter method or null + */ public static Method parseGetter(Field field) { String getterName = GET_PREFIX + capitalize(field.getName()); try { @@ -86,6 +106,12 @@ public static Method parseGetter(Field field) { } } + /** + * Parses a setter method for a field. + * + * @param field the field + * @return the setter method or null + */ public static Method parseSetter(Field field) { String setterName = SET_PREFIX + capitalize(field.getName()); try { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/FieldDefinitionParser.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/FieldDefinitionParser.java index 87f251bdad..3d0646ed2a 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/FieldDefinitionParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/FieldDefinitionParser.java @@ -19,11 +19,18 @@ import java.util.List; /** - * Field IdDefinition Parser. + * Parser for creating {@link IdDefinition} from fields. * * @author ahoo wang */ @FunctionalInterface public interface FieldDefinitionParser { + /** + * Parses a field to create its ID definition. + * + * @param lookupClassList list of classes in the hierarchy + * @param field the field to parse + * @return the ID definition or null + */ IdDefinition parse(List> lookupClassList, Field field); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/NamedDefinitionParser.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/NamedDefinitionParser.java index f1a96649ec..3fb914340e 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/NamedDefinitionParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/parser/NamedDefinitionParser.java @@ -19,7 +19,9 @@ import java.util.List; /** - * Named {@link FieldDefinitionParser} implementation. + * Parses fields by name to create {@link IdDefinition}. + * + *

Creates an ID definition for fields matching the specified name. * * @author ahoo wang */ @@ -27,6 +29,11 @@ public class NamedDefinitionParser implements FieldDefinitionParser { private final String idFieldName; + /** + * Creates a parser for the specified field name. + * + * @param idFieldName the field name to match + */ public NamedDefinitionParser(String idFieldName) { this.idFieldName = idFieldName; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/CosIdAccessorRegistry.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/CosIdAccessorRegistry.java index 82a2ba413f..81d5581ada 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/CosIdAccessorRegistry.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/CosIdAccessorRegistry.java @@ -18,19 +18,45 @@ import com.google.errorprone.annotations.ThreadSafe; /** - * CosIdAccessor Registry. + * Registry for managing {@link CosIdAccessor} instances. + * + *

Provides registration and lookup of ID accessors for classes, + * enabling automatic ID injection for entities. * * @author ahoo wang */ @ThreadSafe public interface CosIdAccessorRegistry { + /** + * Registers a class, parsing its accessor from annotations. + * + * @param clazz the class to register + */ void register(Class clazz); + /** + * Registers a class with a specific accessor. + * + * @param clazz the class to register + * @param cosIdAccessor the accessor to use + */ void register(Class clazz, CosIdAccessor cosIdAccessor); + /** + * Gets the accessor for a class. + * + * @param clazz the class + * @return the accessor + */ CosIdAccessor get(Class clazz); + /** + * Ensures the target object has an ID, registering if needed. + * + * @param target the target object + * @return true if ID was ensured + */ default boolean ensureId(Object target) { CosIdAccessor cosIdAccessor = get(target.getClass()); if (CosIdAccessor.NOT_FOUND.equals(cosIdAccessor)) { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/DefaultAccessorRegistry.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/DefaultAccessorRegistry.java index 4261b51ef1..ab60db3184 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/DefaultAccessorRegistry.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/registry/DefaultAccessorRegistry.java @@ -19,7 +19,10 @@ import java.util.concurrent.ConcurrentHashMap; /** - * Default CosIdAccessorRegistry implementation. + * Default implementation of {@link CosIdAccessorRegistry}. + * + *

Uses a concurrent hash map for thread-safe registration + * and lazy parsing of accessors. * * @author ahoo wang */ @@ -28,6 +31,11 @@ public class DefaultAccessorRegistry implements CosIdAccessorRegistry { private final ConcurrentHashMap, CosIdAccessor> classMapAccessor = new ConcurrentHashMap<>(); private final CosIdAccessorParser accessorParser; + /** + * Creates a registry with the specified parser. + * + * @param accessorParser the parser for creating accessors + */ public DefaultAccessorRegistry(CosIdAccessorParser accessorParser) { this.accessorParser = accessorParser; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/CosIdScanner.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/CosIdScanner.java index b414716cff..3d067c6458 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/CosIdScanner.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/CosIdScanner.java @@ -14,10 +14,13 @@ package me.ahoo.cosid.accessor.scanner; /** - * Scan the packages and register the qualified classes to {@link me.ahoo.cosid.accessor.registry.CosIdAccessorRegistry}. + * Scans packages and registers qualified classes to {@link me.ahoo.cosid.accessor.registry.CosIdAccessorRegistry}. * * @author ahoo wang */ public interface CosIdScanner { + /** + * Scans and registers qualified classes. + */ void scan(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/DefaultCosIdScanner.java b/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/DefaultCosIdScanner.java index 7c4820ab9c..70baedd63b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/DefaultCosIdScanner.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/accessor/scanner/DefaultCosIdScanner.java @@ -28,7 +28,10 @@ import java.util.Arrays; /** - * Default {@link CosIdScanner} implementation. + * Default implementation of {@link CosIdScanner}. + * + *

Scans classpath for classes with @CosId annotations + * and registers them with the accessor registry. * * @author ahoo wang */ @@ -39,10 +42,24 @@ public class DefaultCosIdScanner implements CosIdScanner { private final CosIdAccessorParser cosIdAccessorParser; private final CosIdAccessorRegistry cosIdAccessorRegistry; + /** + * Creates a scanner with field definition parser. + * + * @param basePackages packages to scan + * @param fieldDefinitionParser the field parser + * @param cosIdAccessorRegistry the registry + */ public DefaultCosIdScanner(String[] basePackages, FieldDefinitionParser fieldDefinitionParser, CosIdAccessorRegistry cosIdAccessorRegistry) { this(basePackages, new DefaultAccessorParser(fieldDefinitionParser), cosIdAccessorRegistry); } + /** + * Creates a scanner with accessor parser. + * + * @param basePackages packages to scan + * @param cosIdAccessorParser the accessor parser + * @param cosIdAccessorRegistry the registry + */ public DefaultCosIdScanner(String[] basePackages, CosIdAccessorParser cosIdAccessorParser, CosIdAccessorRegistry cosIdAccessorRegistry) { this.basePackages = basePackages; this.cosIdAccessorRegistry = cosIdAccessorRegistry; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/annotation/AnnotationDefinitionParser.java b/cosid-core/src/main/java/me/ahoo/cosid/annotation/AnnotationDefinitionParser.java index b3f0c9316a..d9f7f3b733 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/annotation/AnnotationDefinitionParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/annotation/AnnotationDefinitionParser.java @@ -23,7 +23,10 @@ import java.util.Optional; /** - * Annotation FieldDefinitionParser. + * Parses {@link CosId} annotations to create {@link IdDefinition}. + * + *

Checks for field-level @CosId annotation first, + * then looks for type-level annotation with matching field name. * * @author ahoo wang * @see CosId @@ -31,6 +34,9 @@ @Slf4j public class AnnotationDefinitionParser implements FieldDefinitionParser { + /** + * Shared singleton instance. + */ public static final AnnotationDefinitionParser INSTANCE = new AnnotationDefinitionParser(); @Override diff --git a/cosid-core/src/main/java/me/ahoo/cosid/annotation/CosId.java b/cosid-core/src/main/java/me/ahoo/cosid/annotation/CosId.java index d5e75eae0c..977ff4bcea 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/annotation/CosId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/annotation/CosId.java @@ -22,7 +22,13 @@ import java.lang.annotation.Target; /** - * Define CosId. + * Annotation to mark fields or classes for CosId. + * + *

Can be applied to: + *

* * @author ahoo wang */ @@ -30,20 +36,22 @@ @Documented @Retention(RetentionPolicy.RUNTIME) public @interface CosId { + /** + * Default field name. + */ String DEFAULT_FIELD = "id"; /** - * id generator name. - * {@link IdGeneratorProvider#get(String)} + * Gets the ID generator name. * - * @return id generator name + * @return the generator name */ String value() default IdGeneratorProvider.SHARE; /** - * cosid field. + * Gets the ID field name (for type-level annotation). * - * @return field name of id. + * @return the field name */ String field() default DEFAULT_FIELD; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/DatePrefixIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/DatePrefixIdConverter.java index a39fa21cb8..d4e915d190 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/DatePrefixIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/DatePrefixIdConverter.java @@ -23,12 +23,27 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +/** + * ID converter that prepends a date prefix to the ID string. + * + *

This converter wraps another IdConverter and adds a date-based prefix, + * useful for time-based partitioning or bucketing of IDs. + * + * @author ahoo wang + */ public class DatePrefixIdConverter implements IdConverter, Decorator { private final String pattern; private final DateTimeFormatter formatter; private final String delimiter; private final IdConverter actual; + /** + * Creates a new DatePrefixIdConverter. + * + * @param pattern date format pattern (see {@link DateTimeFormatter}) + * @param delimiter delimiter between prefix and ID + * @param actual the underlying converter to wrap + */ public DatePrefixIdConverter(String pattern, String delimiter, IdConverter actual) { this.pattern = pattern; this.formatter = DateTimeFormatter.ofPattern(pattern); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/GroupedPrefixIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/GroupedPrefixIdConverter.java index c2f4616dfb..46cbfce902 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/GroupedPrefixIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/GroupedPrefixIdConverter.java @@ -22,11 +22,27 @@ import com.google.common.base.Preconditions; import org.jspecify.annotations.NonNull; +/** + * Converter that prepends a dynamic group key as prefix to string IDs. + * + *

The group key is obtained from the current GroupedAccessor context. + * + * @author ahoo wang + */ public class GroupedPrefixIdConverter implements IdConverter, Decorator { + /** + * Default delimiter between group key and ID. + */ public static final String DEFAULT_DELIMITER = "-"; private final String delimiter; private final IdConverter actual; + /** + * Creates a grouped prefix converter. + * + * @param delimiter the delimiter between group key and ID + * @param actual the underlying converter + */ public GroupedPrefixIdConverter(String delimiter, IdConverter actual) { Preconditions.checkNotNull(delimiter, "prefix can not be null!"); this.delimiter = delimiter; @@ -38,6 +54,11 @@ public GroupedPrefixIdConverter(String delimiter, IdConverter actual) { return actual; } + /** + * Gets the delimiter. + * + * @return the delimiter + */ public String getDelimiter() { return delimiter; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/PrefixIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/PrefixIdConverter.java index f0411e2691..3c17b4c5ac 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/PrefixIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/PrefixIdConverter.java @@ -23,30 +23,41 @@ /** - * Converter for setting string ID prefix. + * Converter that prepends a fixed prefix to string IDs. * * @author ahoo wang */ public class PrefixIdConverter implements IdConverter, Decorator { - + private final String prefix; private final IdConverter actual; - + + /** + * Creates a prefix converter. + * + * @param prefix the prefix to prepend + * @param actual the underlying converter + */ public PrefixIdConverter(String prefix, IdConverter actual) { Preconditions.checkNotNull(prefix, "prefix can not be null!"); this.prefix = prefix; this.actual = actual; } - + @Override public @NonNull IdConverter getActual() { return actual; } - + + /** + * Gets the prefix. + * + * @return the prefix + */ public String getPrefix() { return prefix; } - + @Override public @NonNull String asString(long id) { String idStr = actual.asString(id); @@ -55,13 +66,13 @@ public String getPrefix() { } return prefix + idStr; } - + @Override public long asLong(@NonNull String idString) { String idStr = idString.substring(prefix.length()); return actual.asLong(idStr); } - + @Override public Stat stat() { return new PrefixConverterStat(getClass().getSimpleName(), prefix, actual.stat()); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/Radix36IdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/Radix36IdConverter.java index 64e556fb82..f28a2ce513 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/Radix36IdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/Radix36IdConverter.java @@ -14,25 +14,39 @@ package me.ahoo.cosid.converter; /** - * 36 bit string ID converter like this [0-9][A-Z]{13} . + * Radix-36 string ID converter using characters 0-9 and A-Z. + * + *

Encodes long IDs as strings using 36 characters (10 digits + 26 uppercase letters). + * Maximum 13 characters needed for full long range. * * @author ahoo wang */ public final class Radix36IdConverter extends RadixIdConverter { + /** + * Maximum character size (13 for full long range). + */ public static final int MAX_CHAR_SIZE = 13; + /** + * Radix value (36). + */ public static final int RADIX = 36; + /** + * Shared instance without padding. + */ public static final Radix36IdConverter INSTANCE = new Radix36IdConverter(false, MAX_CHAR_SIZE); + /** + * Shared instance with padding. + */ public static final Radix36IdConverter PAD_START = new Radix36IdConverter(true, MAX_CHAR_SIZE); /** - * Return an instance representing the specified parameter. - * If new instances are not required, static cached instances are used to provide space and time efficiency. + * Gets an instance with specified parameters. * - * @param padStart padStart - * @param charSize Size - * @return Radix62IdConverter + * @param padStart whether to pad + * @param charSize character size + * @return converter instance */ public static Radix36IdConverter of(boolean padStart, int charSize) { @@ -47,6 +61,12 @@ public static Radix36IdConverter of(boolean padStart, int charSize) { return new Radix36IdConverter(padStart, charSize); } + /** + * Creates a new converter. + * + * @param padStart whether to pad with leading zeros + * @param charSize the character size + */ public Radix36IdConverter(boolean padStart, int charSize) { super(padStart, charSize); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/RadixIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/RadixIdConverter.java index 4a0fa1d707..67467808d5 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/RadixIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/RadixIdConverter.java @@ -22,34 +22,48 @@ import org.jspecify.annotations.NonNull; +/** + * Abstract base for radix-based ID converters. + * + *

Converts between long IDs and string representations using various radixes + * (base 36, 62, etc.). Supports optional zero-padding for consistent string length. + * + * @author ahoo wang + */ public abstract class RadixIdConverter implements IdConverter { /** - * 48. + * Character '0' (ASCII 48). */ static final char ZERO = '0'; /** - * 57. + * Character '9' (ASCII 57). */ static final char NINE = '9'; /** - * 65. + * Character 'A' (ASCII 65). */ static final char UPPERCASE_A = 'A'; static final int UPPERCASE_OFFSET = 10; /** - * 90. + * Character 'Z' (ASCII 90). */ static final char UPPERCASE_Z = 'Z'; /** - * 97. + * Character 'a' (ASCII 97). */ static final char LOWERCASE_A = 'a'; static final int LOWERCASE_OFFSET = 36; /** - * 122. + * Character 'z' (ASCII 122). */ static final char LOWERCASE_Z = 'z'; + /** + * Character lookup table for radix conversion. + * Index 0-9: digits '0'-'9' + * Index 10-35: uppercase letters 'A'-'Z' + * Index 36-61: lowercase letters 'a'-'z' + */ static final char[] digits = { /* * offset: 0. @@ -70,12 +84,21 @@ public abstract class RadixIdConverter implements IdConverter { 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', LOWERCASE_Z, }; + /** + * Padding character for fixed-width output. + */ public static final char PAD_CHAR = ZERO; private final boolean padStart; private final int charSize; private final long maxId; + /** + * Creates a new RadixIdConverter. + * + * @param padStart whether to pad output with leading zeros + * @param charSize the fixed character size for output + */ protected RadixIdConverter(boolean padStart, int charSize) { Preconditions.checkArgument(charSize > 0 && charSize <= getMaxCharSize(), "charSize cannot be greater than MAX_CHAR_SIZE[%s]!", getMaxCharSize()); this.padStart = padStart; @@ -87,6 +110,12 @@ protected RadixIdConverter(boolean padStart, int charSize) { } } + /** + * Gets the offset value for a digit character. + * + * @param digitChar the character to convert + * @return the offset value (0-61) or -1 if invalid + */ public static int offset(char digitChar) { if (digitChar >= ZERO && digitChar <= NINE) { return digitChar - ZERO; @@ -100,6 +129,13 @@ public static int offset(char digitChar) { return -1; } + /** + * Calculates maximum character size for a given radix and bit count. + * + * @param radix the number base + * @param bits number of bits + * @return maximum characters needed + */ public static int maxCharSize(int radix, int bits) { long maxId = ~(-1L << bits); int divideTimes = 0; @@ -114,16 +150,36 @@ boolean isPadStart() { return padStart; } + /** + * Gets the fixed character size. + * + * @return character size + */ public int getCharSize() { return charSize; } + /** + * Gets the maximum ID representable with this char size. + * + * @return maximum ID + */ public long getMaxId() { return maxId; } + /** + * Gets the radix (base) for this converter. + * + * @return radix value + */ abstract int getRadix(); + /** + * Gets the maximum character size for this converter type. + * + * @return maximum character size + */ abstract int getMaxCharSize(); @Override diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/SnowflakeFriendlyIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/SnowflakeFriendlyIdConverter.java index 91dd8aa947..38666be87f 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/SnowflakeFriendlyIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/SnowflakeFriendlyIdConverter.java @@ -22,20 +22,36 @@ import org.jspecify.annotations.NonNull; /** - * Snowflake FriendlyId Converter. + * Converts Snowflake IDs to human-readable string format. + * + *

Provides friendly string representations of Snowflake IDs + * with embedded timestamp and machine information. * * @author ahoo wang */ public class SnowflakeFriendlyIdConverter implements IdConverter { + /** + * Shared instance using millisecond parser. + */ public static final IdConverter INSTANCE = new SnowflakeFriendlyIdConverter(MillisecondSnowflakeIdStateParser.INSTANCE); private final SnowflakeIdStateParser snowflakeIdStateParser; + /** + * Creates a converter with the specified parser. + * + * @param snowflakeIdStateParser the Snowflake ID state parser + */ public SnowflakeFriendlyIdConverter(SnowflakeIdStateParser snowflakeIdStateParser) { this.snowflakeIdStateParser = snowflakeIdStateParser; } + /** + * Gets the parser. + * + * @return the parser + */ public SnowflakeIdStateParser getParser() { return snowflakeIdStateParser; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/SuffixIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/SuffixIdConverter.java index d6d46835d5..8a46665a77 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/SuffixIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/SuffixIdConverter.java @@ -22,7 +22,7 @@ import org.jspecify.annotations.NonNull; /** - * Suffix IdConverter . + * Converter that appends a fixed suffix to string IDs. * * @author ahoo wang */ @@ -30,6 +30,12 @@ public class SuffixIdConverter implements IdConverter, Decorator { private final String suffix; private final IdConverter actual; + /** + * Creates a suffix converter. + * + * @param suffix the suffix to append + * @param actual the underlying converter + */ public SuffixIdConverter(String suffix, IdConverter actual) { Preconditions.checkNotNull(suffix, "suffix can not be null!"); this.suffix = suffix; @@ -41,6 +47,11 @@ public SuffixIdConverter(String suffix, IdConverter actual) { return actual; } + /** + * Gets the suffix. + * + * @return the suffix + */ public String getSuffix() { return suffix; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/converter/ToStringIdConverter.java b/cosid-core/src/main/java/me/ahoo/cosid/converter/ToStringIdConverter.java index 256df099f2..bd878c7f5d 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/converter/ToStringIdConverter.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/converter/ToStringIdConverter.java @@ -24,16 +24,27 @@ /** - * ToString ID Converter. + * Converts long IDs to/from strings using standard String.valueOf(). + * + *

Optionally pads the string with leading zeros to a specified length. * * @author ahoo wang */ public class ToStringIdConverter implements IdConverter { + /** + * Shared instance without padding. + */ public static final ToStringIdConverter INSTANCE = new ToStringIdConverter(false, 0); private final boolean padStart; private final int charSize; + /** + * Creates a converter with optional padding. + * + * @param padStart whether to pad with leading zeros + * @param charSize the target character size for padding + */ public ToStringIdConverter(boolean padStart, int charSize) { this.padStart = padStart; this.charSize = charSize; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdIdStateParser.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdIdStateParser.java index 1e57ba2048..3ae7bd50d6 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdIdStateParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdIdStateParser.java @@ -15,16 +15,38 @@ /** * Parser for converting {@link CosIdState} to String and vice versa. - *

- * The {@link CosIdState} is a composite of timestamp, machineId, and sequence. - *

+ * + *

The {@link CosIdState} is a composite of timestamp, machineId, and sequence. + * This parser handles bidirectional conversion between the state object and string representations. + * + * @author ahoo wang */ public interface CosIdIdStateParser { - + + /** + * Parses a string representation into a CosIdState. + * + * @param id the string ID to parse + * @return the parsed CosIdState + */ CosIdState asState(String id); - + + /** + * Converts timestamp, machineId, and sequence to a string. + * + * @param lastTimestamp the timestamp in milliseconds + * @param machineId the machine ID + * @param sequence the sequence number + * @return string representation + */ String asString(long lastTimestamp, int machineId, int sequence); - + + /** + * Converts a CosIdState to its string representation. + * + * @param cosIdState the state to convert + * @return string representation + */ default String asString(CosIdState cosIdState) { return asString(cosIdState.getTimestamp(), cosIdState.getMachineId(), cosIdState.getSequence()); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdState.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdState.java index 152c6f83fc..22b52c64ea 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdState.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/CosIdState.java @@ -18,34 +18,62 @@ import java.util.Objects; /** - * CosId State. - *

- * The {@link CosIdState} is a composite of timestamp, machineId, and sequence. - *

+ * Immutable state representing a CosId's decomposed components. + * + *

A CosId is composed of three components: + *

+ * + * @author ahoo wang */ public final class CosIdState implements Comparable { private final long timestamp; private final int machineId; private final int sequence; - + + /** + * Creates a new CosIdState. + * + * @param timestamp the timestamp in milliseconds + * @param machineId the machine ID + * @param sequence the sequence number + */ public CosIdState(long timestamp, int machineId, int sequence) { this.timestamp = timestamp; this.machineId = machineId; this.sequence = sequence; } - + + /** + * Gets the timestamp component. + * + * @return the timestamp in milliseconds + */ public long getTimestamp() { return timestamp; } - + + /** + * Gets the machine ID component. + * + * @return the machine ID + */ public int getMachineId() { return machineId; } - + + /** + * Gets the sequence number component. + * + * @return the sequence number + */ public int getSequence() { return sequence; } - + @Override public int compareTo(CosIdState o) { int timestampCompared = Long.compare(timestamp, o.timestamp); @@ -54,7 +82,7 @@ public int compareTo(CosIdState o) { } return Integer.compare(sequence, o.sequence); } - + @Override public boolean equals(Object o) { if (this == o) { @@ -66,12 +94,12 @@ public boolean equals(Object o) { CosIdState that = (CosIdState) o; return getTimestamp() == that.getTimestamp() && getMachineId() == that.getMachineId() && getSequence() == that.getSequence(); } - + @Override public int hashCode() { return Objects.hash(getTimestamp(), getMachineId(), getSequence()); } - + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyCosIdGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyCosIdGenerator.java index 61e3851367..35c82396eb 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyCosIdGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyCosIdGenerator.java @@ -15,12 +15,38 @@ import java.time.ZoneId; +/** + * CosIdGenerator that produces human-readable string IDs. + * + *

Extends RadixCosIdGenerator with a FriendlyIdStateParser that generates + * IDs in format: {@code yyyyMMddHHmmssSSS-machineId-sequence} + * + * @author ahoo wang + */ public class FriendlyCosIdGenerator extends RadixCosIdGenerator { + /** + * Creates a generator with default bit configuration. + * + * @param machineId the machine ID + * @param zoneId time zone for timestamp formatting + * @param padStart whether to pad numbers with leading zeros + */ public FriendlyCosIdGenerator(int machineId, ZoneId zoneId, boolean padStart) { this(DEFAULT_TIMESTAMP_BIT, DEFAULT_MACHINE_BIT, DEFAULT_SEQUENCE_BIT, machineId, DEFAULT_SEQUENCE_RESET_THRESHOLD, zoneId, padStart); } + /** + * Creates a generator with custom bit configuration. + * + * @param timestampBit number of bits for timestamp + * @param machineIdBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for resetting sequence + * @param zoneId time zone for timestamp formatting + * @param padStart whether to pad numbers with leading zeros + */ public FriendlyCosIdGenerator(int timestampBit, int machineIdBit, int sequenceBit, int machineId, int sequenceResetThreshold, ZoneId zoneId, boolean padStart) { super(timestampBit, machineIdBit, sequenceBit, machineId, sequenceResetThreshold, new FriendlyIdStateParser(zoneId, padStart, machineIdBit, sequenceBit)); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyIdStateParser.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyIdStateParser.java index b93de399e6..d8b1d5bf5f 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyIdStateParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/FriendlyIdStateParser.java @@ -35,12 +35,27 @@ import java.time.format.DateTimeFormatterBuilder; import java.util.List; +/** + * Parser for CosIdState using a human-readable format. + * + *

Converts CosIdState to/from format: {@code yyyyMMddHHmmssSSS-machineId-sequence} + * Example: {@code 20210623131730192-1-0} + * + * @author ahoo wang + */ public class FriendlyIdStateParser implements CosIdIdStateParser { + /** + * Decimal radix for character size calculation. + */ public static final int DECIMAL_RADIX = 10; private final ZoneId zoneId; private final boolean padStart; + /** + * DateTimeFormatter for parsing/generating timestamp strings. + * Format: {@code yyyyMMddHHmmssSSS} + */ public static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .appendValue(YEAR, 4) .appendValue(MONTH_OF_YEAR, 2) @@ -54,6 +69,14 @@ public class FriendlyIdStateParser implements CosIdIdStateParser { private final int machineCharSize; private final int sequenceCharSize; + /** + * Creates a new FriendlyIdStateParser. + * + * @param zoneId time zone for timestamp conversion + * @param padStart whether to pad numbers with leading zeros + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + */ public FriendlyIdStateParser(ZoneId zoneId, boolean padStart, int machineBit, int sequenceBit) { this.zoneId = zoneId; this.padStart = padStart; @@ -74,6 +97,14 @@ public CosIdState asState(String id) { return new CosIdState(timestamp, machineId, sequence); } + /** + * Converts an integer to string with optional padding. + * + * @param padStart whether to pad with leading zeros + * @param value the value to convert + * @param charSize the minimum character size + * @return the formatted string + */ public static String intAsString(boolean padStart, int value, int charSize) { if (padStart) { return Strings.padStart(String.valueOf(value), charSize, PAD_CHAR); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix36CosIdGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix36CosIdGenerator.java index 6b4535e09c..b5cbd002b4 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix36CosIdGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix36CosIdGenerator.java @@ -14,16 +14,35 @@ package me.ahoo.cosid.cosid; /** - * [timestamp(44)]-[machineId-(20)]-[sequence-(16)] = 80 BITS = 17 CHARS. + * CosIdGenerator using radix-36 string encoding. + * + *

Encodes IDs using characters 0-9 and A-Z (36 characters). + * Bit allocation: timestamp(44) + machineId(20) + sequence(16) = 80 bits = 17 chars. + * + * @author ahoo wang */ public class Radix36CosIdGenerator extends RadixCosIdGenerator { + /** + * Creates a generator with default configuration. + * + * @param machineId the machine ID + */ public Radix36CosIdGenerator(int machineId) { this(DEFAULT_TIMESTAMP_BIT, DEFAULT_MACHINE_BIT, DEFAULT_SEQUENCE_BIT, machineId, DEFAULT_SEQUENCE_RESET_THRESHOLD); } - + + /** + * Creates a generator with custom configuration. + * + * @param timestampBit number of bits for timestamp + * @param machineIdBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for resetting sequence + */ public Radix36CosIdGenerator(int timestampBit, int machineIdBit, int sequenceBit, int machineId, int sequenceResetThreshold) { super(timestampBit, machineIdBit, sequenceBit, machineId, sequenceResetThreshold, RadixCosIdStateParser.ofRadix36(timestampBit, machineIdBit, sequenceBit)); } - + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix62CosIdGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix62CosIdGenerator.java index 9ca7965f73..e17c92e9e5 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix62CosIdGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/Radix62CosIdGenerator.java @@ -14,16 +14,35 @@ package me.ahoo.cosid.cosid; /** - * [timestamp(44)]-[machineId-(20)]-[sequence-(16)] = 80 BITS = 15 CHARS. + * CosIdGenerator using radix-62 string encoding. + * + *

Encodes IDs using characters 0-9, A-Z, and a-z (62 characters). + * Bit allocation: timestamp(44) + machineId(20) + sequence(16) = 80 bits = 15 chars. + * + * @author ahoo wang */ public class Radix62CosIdGenerator extends RadixCosIdGenerator { - + + /** + * Creates a generator with default configuration. + * + * @param machineId the machine ID + */ public Radix62CosIdGenerator(int machineId) { this(DEFAULT_TIMESTAMP_BIT, DEFAULT_MACHINE_BIT, DEFAULT_SEQUENCE_BIT, machineId, DEFAULT_SEQUENCE_RESET_THRESHOLD); } - + + /** + * Creates a generator with custom configuration. + * + * @param timestampBit number of bits for timestamp + * @param machineIdBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for resetting sequence + */ public Radix62CosIdGenerator(int timestampBit, int machineIdBit, int sequenceBit, int machineId, int sequenceResetThreshold) { super(timestampBit, machineIdBit, sequenceBit, machineId, sequenceResetThreshold, RadixCosIdStateParser.ofRadix62(timestampBit, machineIdBit, sequenceBit)); } - + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/cosid/RadixCosIdGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/cosid/RadixCosIdGenerator.java index adce8ba288..8f028ab21b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/cosid/RadixCosIdGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/cosid/RadixCosIdGenerator.java @@ -20,11 +20,12 @@ import org.jspecify.annotations.NonNull; /** - * Radix CosIdGenerator. - *

- * It's a simple implementation of {@link CosIdGenerator}. - *

+ * Radix-based CosIdGenerator implementation. * + *

A simpler variant of Snowflake ID that uses configurable bit allocation + * for timestamp, machine ID, and sequence components. + * + * @author ahoo wang * @see CosIdGenerator * @see CosIdIdStateParser * @see CosIdState @@ -32,22 +33,44 @@ * @see TimestampOverflowException */ public class RadixCosIdGenerator implements CosIdGenerator { + /** + * Default timestamp bits (44 bits, ~556 years at millisecond precision). + */ public static final int DEFAULT_TIMESTAMP_BIT = 44; + /** + * Default machine ID bits (20 bits). + */ public static final int DEFAULT_MACHINE_BIT = 20; + /** + * Default sequence bits (16 bits, 65536 per millisecond). + */ public static final int DEFAULT_SEQUENCE_BIT = 16; + /** + * Default sequence reset threshold (half of max sequence). + */ public static final int DEFAULT_SEQUENCE_RESET_THRESHOLD = ~(-1 << (DEFAULT_SEQUENCE_BIT - 1)); - + private final long maxTimestamp; private final int maxMachine; private final int maxSequence; private final int sequenceResetThreshold; - + private final int machineId; private int sequence = 0; private long lastTimestamp = -1L; - + private final CosIdIdStateParser stateParser; - + + /** + * Creates a new RadixCosIdGenerator. + * + * @param timestampBit number of bits for timestamp + * @param machineIdBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for resetting sequence + * @param stateParser the state parser for string conversion + */ public RadixCosIdGenerator(int timestampBit, int machineIdBit, int sequenceBit, @@ -64,23 +87,23 @@ public RadixCosIdGenerator(int timestampBit, this.machineId = machineId; this.stateParser = stateParser; } - + @Override public int getMachineId() { return machineId; } - + @Override public long getLastTimestamp() { return lastTimestamp; } - + @NonNull @Override public CosIdIdStateParser getStateParser() { return stateParser; } - + private long nextTime() { long time = System.currentTimeMillis(); while (time <= lastTimestamp) { @@ -88,29 +111,34 @@ private long nextTime() { } return time; } - + + /** + * Generates the next ID as a state object. + * + * @return the generated CosIdState + */ @NonNull public synchronized CosIdState generateAsState() { long currentTimestamp = System.currentTimeMillis(); if (currentTimestamp < lastTimestamp) { throw new ClockBackwardsException(lastTimestamp, currentTimestamp); } - + //region Reset sequence based on sequence reset threshold,Optimize the problem of uneven sharding. - + if (currentTimestamp > lastTimestamp && sequence >= sequenceResetThreshold) { sequence = 0; } - + sequence = (sequence + 1) & maxSequence; - + if (sequence == 0) { currentTimestamp = nextTime(); } - + //endregion - + if (currentTimestamp > maxTimestamp) { throw new TimestampOverflowException(0, currentTimestamp, maxTimestamp); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/jvm/AtomicLongGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/jvm/AtomicLongGenerator.java index 95f947dec6..2f532cf57a 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/jvm/AtomicLongGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/jvm/AtomicLongGenerator.java @@ -18,11 +18,17 @@ import java.util.concurrent.atomic.AtomicLong; /** - * AtomicLong Generator. + * AtomicLong-based ID generator. + * + *

Generates unique IDs using an atomic counter. + * Thread-safe and suitable for single-JVM ID generation. * * @author ahoo wang */ public class AtomicLongGenerator implements IdGenerator { + /** + * Shared singleton instance. + */ public static final IdGenerator INSTANCE = new AtomicLongGenerator(); private final AtomicLong idGen = new AtomicLong(); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/jvm/UuidGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/jvm/UuidGenerator.java index 208c0a0972..66cd52a73f 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/jvm/UuidGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/jvm/UuidGenerator.java @@ -22,10 +22,16 @@ /** * UUID ID Generator. * + *

Generates UUIDs as string representations. + * Note: Does not support {@link #generate()} for long IDs. + * * @author ahoo wang */ public class UuidGenerator implements IdGenerator { + /** + * Shared singleton instance. + */ public static final IdGenerator INSTANCE = new UuidGenerator(); @Override diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/ClockBackwardsSynchronizer.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/ClockBackwardsSynchronizer.java index 4cec05fc3b..3e83a3709c 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/ClockBackwardsSynchronizer.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/ClockBackwardsSynchronizer.java @@ -18,18 +18,43 @@ import com.google.errorprone.annotations.ThreadSafe; /** - * Clock Backwards Synchronizer. + * Synchronizer for handling clock backwards issues in Snowflake ID generation. + * + *

When system clock moves backwards, this synchronizer waits until + * the clock catches up to ensure unique IDs. * * @author ahoo wang */ @ThreadSafe public interface ClockBackwardsSynchronizer { + /** + * Default synchronizer instance. + */ ClockBackwardsSynchronizer DEFAULT = new DefaultClockBackwardsSynchronizer(); + /** + * Synchronizes clock by waiting until current time exceeds lastTimestamp. + * + * @param lastTimestamp the last timestamp that was generated + * @throws InterruptedException if thread is interrupted while waiting + * @throws ClockTooManyBackwardsException if clock backwards exceeds threshold + */ void sync(long lastTimestamp) throws InterruptedException, ClockTooManyBackwardsException; + /** + * Synchronizes clock without throwing InterruptedException. + * + * @param lastTimestamp the last timestamp that was generated + * @throws ClockTooManyBackwardsException if clock backwards exceeds threshold + */ void syncUninterruptibly(long lastTimestamp) throws ClockTooManyBackwardsException; + /** + * Calculates how far backwards the clock has moved. + * + * @param lastTimestamp the last timestamp + * @return the backwards duration in milliseconds + */ static long getBackwardsTimeStamp(long lastTimestamp) { return lastTimestamp - System.currentTimeMillis(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/HostAddressSupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/HostAddressSupplier.java index 4448b56c96..fa257ee78b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/HostAddressSupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/HostAddressSupplier.java @@ -13,8 +13,18 @@ package me.ahoo.cosid.machine; +/** + * Functional interface for providing host address. + * + * @author ahoo wang + */ @FunctionalInterface public interface HostAddressSupplier { + /** + * Gets the host address. + * + * @return the host address + */ String getHostAddress(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/InMemoryMachineStateStorage.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/InMemoryMachineStateStorage.java index d7cc5c113c..baa62d6c9d 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/InMemoryMachineStateStorage.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/InMemoryMachineStateStorage.java @@ -18,15 +18,24 @@ import java.util.concurrent.ConcurrentHashMap; +/** + * In-memory implementation of {@link MachineStateStorage}. + * + *

Stores machine states in a {@link ConcurrentHashMap} for fast access. + * This implementation is not persistent and should only be used for testing + * or single-instance deployments. + * + * @author ahoo wang + */ @Slf4j public class InMemoryMachineStateStorage implements MachineStateStorage { private final ConcurrentHashMap states = new ConcurrentHashMap<>(); - + @Override public @NonNull MachineState get(String namespace, InstanceId instanceId) { return states.getOrDefault(new NamespacedInstanceId(namespace, instanceId), MachineState.NOT_FOUND); } - + @Override public void set(String namespace, int machineId, InstanceId instanceId) { NamespacedInstanceId namespacedInstanceId = new NamespacedInstanceId(namespace, instanceId); @@ -36,7 +45,7 @@ public void set(String namespace, int machineId, InstanceId instanceId) { } states.put(namespacedInstanceId, machineState); } - + @Override public void remove(String namespace, InstanceId instanceId) { NamespacedInstanceId namespacedInstanceId = new NamespacedInstanceId(namespace, instanceId); @@ -45,7 +54,7 @@ public void remove(String namespace, InstanceId instanceId) { } states.remove(namespacedInstanceId); } - + @Override public void clear(String namespace) { if (log.isInfoEnabled()) { @@ -53,12 +62,12 @@ public void clear(String namespace) { } states.clear(); } - + @Override public int size(String namespace) { return states.size(); } - + @Override public boolean exists(String namespace, InstanceId instanceId) { return states.containsKey(new NamespacedInstanceId(namespace, instanceId)); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/InstanceId.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/InstanceId.java index 3e0a95d35c..880ac2444b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/InstanceId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/InstanceId.java @@ -18,36 +18,57 @@ import com.google.errorprone.annotations.Immutable; /** - * InstanceId. + * Represents a specific deployment instance of a service. + * + *

An InstanceId identifies a particular running instance of a service, which may + * be part of a deployment that provides stable machine IDs (stable=true) or a + * dynamic instance where machine IDs may change (stable=false). * * @author ahoo wang * @see MachineId */ @Immutable public class InstanceId { + /** + * Sentinel value representing no instance. + */ public static final InstanceId NONE = new InstanceId("none", false); - + private final String instanceId; private final boolean stable; - + + /** + * Creates a new InstanceId. + * + * @param instanceId the instance identifier string + * @param stable whether this instance has a stable identity + */ public InstanceId(String instanceId, boolean stable) { this.instanceId = instanceId; this.stable = stable; } - + /** - * 稳定的的实例拥有稳定的机器号. + * Checks if this instance has a stable identity. + * + *

Stable instances (stable=true) are deployed with stable identities (e.g., Kubernetes StatefulSet) + * and can rely on having consistent machine IDs across restarts. * - * @return Is the instance deployment status stable? + * @return true if the instance has a stable identity */ public boolean isStable() { return stable; } - + + /** + * Gets the instance identifier string. + * + * @return the instance ID + */ public String getInstanceId() { return instanceId; } - + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -55,16 +76,31 @@ public String toString() { .add("stable", stable) .toString(); } - + + /** + * Creates an InstanceId from host and port. + * + * @param host the host address + * @param port the port number + * @param stable whether this instance has a stable identity + * @return a new InstanceId + */ public static InstanceId of(String host, int port, boolean stable) { String instanceIdStr = String.format("%s:%s", host, port); return of(instanceIdStr, stable); } - + + /** + * Creates an InstanceId from an instance ID string. + * + * @param instanceId the instance identifier + * @param stable whether this instance has a stable identity + * @return a new InstanceId + */ public static InstanceId of(String instanceId, boolean stable) { return new InstanceId(instanceId, stable); } - + @Override public boolean equals(Object o) { if (this == o) { @@ -76,7 +112,7 @@ public boolean equals(Object o) { InstanceId that = (InstanceId) o; return stable == that.stable && Objects.equal(instanceId, that.instanceId); } - + @Override public int hashCode() { return Objects.hashCode(instanceId, stable); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/LocalMachineStateStorage.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/LocalMachineStateStorage.java index b4950a829b..9aa45ce616 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/LocalMachineStateStorage.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/LocalMachineStateStorage.java @@ -29,19 +29,36 @@ import java.nio.file.Paths; /** - * LocalMachine State Storage. + * File-based machine state storage. + * + *

Stores machine state in local files, using base64-encoded + * filenames for namespace/instance encoding. * * @author ahoo wang */ @Slf4j public class LocalMachineStateStorage implements MachineStateStorage { + /** + * Default state location in user home directory. + */ public static final String DEFAULT_STATE_LOCATION_PATH = Paths.get(System.getProperty("user.home"), ".cosid-machine-state").toString(); + /** + * The state location path. + */ public final String stateLocation; + /** + * Creates storage with specified location. + * + * @param stateLocation the directory path for state files + */ public LocalMachineStateStorage(String stateLocation) { this.stateLocation = stateLocation; } + /** + * Creates storage with default location. + */ public LocalMachineStateStorage() { this(DEFAULT_STATE_LOCATION_PATH); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineId.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineId.java index 790f4a4269..f943e836ed 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineId.java @@ -17,7 +17,11 @@ import com.google.errorprone.annotations.Immutable; /** - * 逻辑概念的机器号,并不一定跟物理机/虚拟机一一对应,运行进程的唯一性编号(不同业务领域/服务使用 namespace 隔离). + * Logical machine identifier for distributed ID generation. + * + *

This represents a logical machine ID that is not necessarily tied to a physical + * or virtual machine. It provides uniqueness across different processes/services + * which are isolated using namespaces. * * @author ahoo wang * @see InstanceId @@ -26,10 +30,20 @@ public class MachineId { private final int machineId; + /** + * Creates a new MachineId. + * + * @param machineId the machine ID value + */ public MachineId(int machineId) { this.machineId = machineId; } + /** + * Gets the machine ID value. + * + * @return the machine ID + */ public int getMachineId() { return machineId; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdLostException.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdLostException.java index 9170d469e2..7447371bd3 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdLostException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdLostException.java @@ -19,7 +19,10 @@ import org.jspecify.annotations.Nullable; /** - * MachineId Lost Exception . + * Exception thrown when a machine ID is lost. + * + *

Indicates that a machine ID that was previously allocated + * can no longer be found in the distributed store. * * @author ahoo wang */ @@ -27,22 +30,44 @@ public class MachineIdLostException extends CosIdException { private final String namespace; private final InstanceId instanceId; private final MachineState machineState; - + + /** + * Creates a new exception. + * + * @param namespace the namespace + * @param instanceId the instance ID + * @param machineState the machine state (may be null) + */ public MachineIdLostException(String namespace, InstanceId instanceId, @Nullable MachineState machineState) { super(Strings.lenientFormat("The machine id[%s] bound to the instance[%s]@[%s] has been lost!.", machineState, instanceId, namespace)); this.namespace = namespace; this.instanceId = instanceId; this.machineState = machineState; } - + + /** + * Gets the namespace. + * + * @return the namespace + */ public String getNamespace() { return namespace; } - + + /** + * Gets the instance ID. + * + * @return the instance ID + */ public InstanceId getInstanceId() { return instanceId; } - + + /** + * Gets the machine state. + * + * @return the machine state + */ public MachineState getMachineState() { return machineState; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdOverflowException.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdOverflowException.java index 0429af4a52..8b086beb66 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdOverflowException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineIdOverflowException.java @@ -18,7 +18,10 @@ import com.google.common.base.Strings; /** - * MachineId Overflow Exception. + * Exception thrown when machine ID cannot be allocated. + * + *

Indicates that all available machine IDs have been allocated + * and no more can be distributed. * * @author ahoo wang */ @@ -26,16 +29,32 @@ public class MachineIdOverflowException extends CosIdException { private final int totalMachineIds; private final InstanceId instanceId; + /** + * Creates a new exception. + * + * @param totalMachineIds the total number of available machine IDs + * @param instanceId the instance that failed to get an ID + */ public MachineIdOverflowException(int totalMachineIds, InstanceId instanceId) { super(Strings.lenientFormat("InstanceId:[%s] - distribution failed - totalMachineIds:[%s]", instanceId, totalMachineIds)); this.totalMachineIds = totalMachineIds; this.instanceId = instanceId; } + /** + * Gets the total number of machine IDs. + * + * @return the total machine IDs + */ public int getTotalMachineIds() { return totalMachineIds; } + /** + * Gets the instance ID. + * + * @return the instance ID + */ public InstanceId getInstanceId() { return instanceId; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineStateStorage.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineStateStorage.java index e453b459c9..bf12b73353 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineStateStorage.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/MachineStateStorage.java @@ -17,25 +17,73 @@ import org.jspecify.annotations.NonNull; /** - * Machine State Storage. + * Machine state storage for persisting machine state across restarts. + * + *

Provides an interface for storing and retrieving machine state information, + * which is essential for maintaining machine ID allocations in distributed + * ID generation systems. * * @author ahoo wang */ @ThreadSafe public interface MachineStateStorage { + /** + * Local machine state storage instance. + */ MachineStateStorage LOCAL = new LocalMachineStateStorage(); + /** + * In-memory machine state storage instance. + */ MachineStateStorage IN_MEMORY = new InMemoryMachineStateStorage(); + /** + * Gets the machine state for a given namespace and instance. + * + * @param namespace the namespace + * @param instanceId the instance ID + * @return the machine state, or NOT_FOUND if not found + */ @NonNull MachineState get(String namespace, InstanceId instanceId); + /** + * Sets the machine state for a given namespace and instance. + * + * @param namespace the namespace + * @param machineId the machine ID + * @param instanceId the instance ID + */ void set(String namespace, int machineId, InstanceId instanceId); + /** + * Removes the machine state for a given namespace and instance. + * + * @param namespace the namespace + * @param instanceId the instance ID + */ void remove(String namespace, InstanceId instanceId); + /** + * Clears all machine states for a given namespace. + * + * @param namespace the namespace + */ void clear(String namespace); + /** + * Gets the number of machine states in a namespace. + * + * @param namespace the namespace + * @return the number of machine states + */ int size(String namespace); + /** + * Checks if a machine state exists for a given namespace and instance. + * + * @param namespace the namespace + * @param instanceId the instance ID + * @return true if the state exists + */ boolean exists(String namespace, InstanceId instanceId); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/ManualMachineIdDistributor.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/ManualMachineIdDistributor.java index e78812c3d5..24ad5640c7 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/ManualMachineIdDistributor.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/ManualMachineIdDistributor.java @@ -18,26 +18,41 @@ import java.time.Duration; /** - * Manual Machine Id Distributor. + * Manual machine ID distributor. + * + *

Uses a manually configured machine ID instead of dynamically + * distributing from a centralized store. * * @author ahoo wang */ @Slf4j public class ManualMachineIdDistributor extends AbstractMachineIdDistributor { - + private final int machineId; private final MachineState machineState; - + + /** + * Creates a manual distributor. + * + * @param machineId the fixed machine ID to use + * @param machineStateStorage the state storage + * @param clockBackwardsSynchronizer the clock synchronizer + */ public ManualMachineIdDistributor(int machineId, MachineStateStorage machineStateStorage, ClockBackwardsSynchronizer clockBackwardsSynchronizer) { super(machineStateStorage, clockBackwardsSynchronizer); this.machineId = machineId; this.machineState = MachineState.of(machineId, NOT_FOUND_LAST_STAMP); } - + + /** + * Gets the machine ID. + * + * @return the machine ID + */ public int getMachineId() { return machineId; } - + @Override protected MachineState distributeRemote(String namespace, int machineBit, InstanceId instanceId, Duration safeGuardDuration) { if (log.isInfoEnabled()) { @@ -45,16 +60,16 @@ protected MachineState distributeRemote(String namespace, int machineBit, Instan } return machineState; } - + @Override protected void revertRemote(String namespace, InstanceId instanceId, MachineState machineState) { - + } - + @Override protected void guardRemote(String namespace, InstanceId instanceId, MachineState machineState, Duration safeGuardDuration) { - + } - - + + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/NamespacedInstanceId.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/NamespacedInstanceId.java index 46e619431f..304caf0985 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/NamespacedInstanceId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/NamespacedInstanceId.java @@ -17,28 +17,47 @@ import com.google.common.base.Objects; /** - * NamespacedInstanceId . + * Combines namespace with instance ID for unique identification. + * + *

Used to ensure machine IDs are unique across different + * namespaces/business domains. * * @author ahoo wang */ public class NamespacedInstanceId { private final String namespace; - + private final InstanceId instanceId; - + + /** + * Creates a new namespaced instance ID. + * + * @param namespace the namespace + * @param instanceId the instance ID + */ public NamespacedInstanceId(String namespace, InstanceId instanceId) { this.namespace = namespace; this.instanceId = instanceId; } - + + /** + * Gets the namespace. + * + * @return the namespace + */ public String getNamespace() { return namespace; } - + + /** + * Gets the instance ID. + * + * @return the instance ID + */ public InstanceId getInstanceId() { return instanceId; } - + @Override public boolean equals(Object o) { if (this == o) { @@ -50,12 +69,12 @@ public boolean equals(Object o) { NamespacedInstanceId that = (NamespacedInstanceId) o; return Objects.equal(getNamespace(), that.getNamespace()) && Objects.equal(getInstanceId(), that.getInstanceId()); } - + @Override public int hashCode() { return Objects.hashCode(getNamespace(), getInstanceId()); } - + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/cosid-core/src/main/java/me/ahoo/cosid/machine/NotFoundMachineStateException.java b/cosid-core/src/main/java/me/ahoo/cosid/machine/NotFoundMachineStateException.java index c612085db3..abca1c82c6 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/machine/NotFoundMachineStateException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/machine/NotFoundMachineStateException.java @@ -18,24 +18,43 @@ import com.google.common.base.Strings; /** - * NotFoundMachineStateException . + * Exception thrown when machine state is not found. + * + *

Indicates that the machine state for a specific instance + * could not be found in the distributed store. * * @author ahoo wang */ public class NotFoundMachineStateException extends CosIdException { private final String namespace; private final InstanceId instanceId; - + + /** + * Creates a new exception. + * + * @param namespace the namespace + * @param instanceId the instance ID + */ public NotFoundMachineStateException(String namespace, InstanceId instanceId) { super(Strings.lenientFormat("Not found the MachineState of instance[%s]@[%s]!", instanceId, namespace)); this.namespace = namespace; this.instanceId = instanceId; } - + + /** + * Gets the namespace. + * + * @return the namespace + */ public String getNamespace() { return namespace; } - + + /** + * Gets the instance ID. + * + * @return the instance ID + */ public InstanceId getInstanceId() { return instanceId; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/provider/DefaultIdGeneratorProvider.java b/cosid-core/src/main/java/me/ahoo/cosid/provider/DefaultIdGeneratorProvider.java index 0fdc6882ed..2bd40ae134 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/provider/DefaultIdGeneratorProvider.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/provider/DefaultIdGeneratorProvider.java @@ -26,43 +26,52 @@ /** * Default {@link IdGeneratorProvider} implementation. * + *

Thread-safe registry for managing named ID generator instances + * using a {@link ConcurrentHashMap} for concurrent access. + * * @author ahoo wang */ @ThreadSafe public class DefaultIdGeneratorProvider implements IdGeneratorProvider { - + + /** + * Shared singleton instance. + */ public static final IdGeneratorProvider INSTANCE = new DefaultIdGeneratorProvider(); private volatile IdGenerator shareIdGenerator; - + private final ConcurrentHashMap nameMapIdGen; - + + /** + * Creates a new instance with an empty registry. + */ public DefaultIdGeneratorProvider() { nameMapIdGen = new ConcurrentHashMap<>(); } - + @Override public IdGenerator getShare() { return shareIdGenerator; } - + @Override public void setShare(IdGenerator idGenerator) { shareIdGenerator = idGenerator; nameMapIdGen.put(SHARE, idGenerator); } - + @Override public IdGenerator removeShare() { shareIdGenerator = null; return nameMapIdGen.remove(SHARE); } - + @Override public Optional get(String name) { IdGenerator idGen = nameMapIdGen.get(name); return Optional.ofNullable(idGen); } - + @Override public IdGenerator remove(String name) { if (SHARE.equals(name)) { @@ -70,7 +79,7 @@ public IdGenerator remove(String name) { } return nameMapIdGen.remove(name); } - + @Override public void set(String name, IdGenerator idGenerator) { if (SHARE.equals(name)) { @@ -79,21 +88,21 @@ public void set(String name, IdGenerator idGenerator) { } nameMapIdGen.put(name, idGenerator); } - + @Override public void clear() { shareIdGenerator = null; nameMapIdGen.clear(); } - + @Override public Set> entries() { return nameMapIdGen.entrySet(); } - + @Override public Collection getAll() { return nameMapIdGen.values(); } - + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/provider/LazyIdGenerator.java b/cosid-core/src/main/java/me/ahoo/cosid/provider/LazyIdGenerator.java index 9beaa80905..917ef8c4ff 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/provider/LazyIdGenerator.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/provider/LazyIdGenerator.java @@ -29,29 +29,55 @@ /** * Lazy loading IdGenerator. * + *

Delays the lookup of an ID generator from the provider until first access. + * This is useful when the generator might not be immediately available at startup. + * * @author ahoo wang */ public final class LazyIdGenerator implements IdGeneratorDecorator { - + private final String generatorName; - + private volatile IdGenerator lazyIdGen; - + private final IdGeneratorProvider idGeneratorProvider; - + + /** + * Creates a lazy generator with default provider. + * + * @param generatorName the name of the generator to lookup + */ public LazyIdGenerator(String generatorName) { this(generatorName, DefaultIdGeneratorProvider.INSTANCE); } - + + /** + * Creates a lazy generator with custom provider. + * + * @param generatorName the name of the generator to lookup + * @param idGeneratorProvider the provider to use for lookup + */ public LazyIdGenerator(String generatorName, IdGeneratorProvider idGeneratorProvider) { this.generatorName = generatorName; this.idGeneratorProvider = idGeneratorProvider; } - + + /** + * Gets the generator name. + * + * @return the generator name + */ public String getGeneratorName() { return generatorName; } - + + /** + * Attempts to get the generator, optionally throwing if not found. + * + * @param required if true, throws NotFoundIdGeneratorException if not found + * @return the generator or null if not required and not found + * @throws NotFoundIdGeneratorException if required and not found + */ public IdGenerator tryGet(boolean required) { if (null != lazyIdGen) { return lazyIdGen; @@ -66,7 +92,14 @@ public IdGenerator tryGet(boolean required) { } return null; } - + + /** + * Gets this generator as a SnowflakeId. + * + * @param required if true, throws if not a SnowflakeId + * @return the SnowflakeId or null + * @throws CosIdException if not a SnowflakeId when required + */ public SnowflakeId asSnowflakeId(boolean required) { IdGenerator idGenerator = tryGet(required); if (null == idGenerator) { @@ -77,7 +110,14 @@ public SnowflakeId asSnowflakeId(boolean required) { } throw new CosIdException(Strings.lenientFormat("IdGenerator:[%s] is not instanceof SnowflakeId!", generatorName)); } - + + /** + * Gets this generator as a SnowflakeFriendlyId. + * + * @param required if true, throws if not a SnowflakeFriendlyId + * @return the SnowflakeFriendlyId or null + * @throws CosIdException if not a SnowflakeFriendlyId when required + */ public SnowflakeFriendlyId asFriendlyId(boolean required) { IdGenerator idGenerator = tryGet(required); if (null == idGenerator) { @@ -88,7 +128,14 @@ public SnowflakeFriendlyId asFriendlyId(boolean required) { } throw new CosIdException(Strings.lenientFormat("IdGenerator:[%s] is not instanceof SnowflakeFriendlyId!", generatorName)); } - + + /** + * Gets this generator as a SegmentId. + * + * @param required if true, throws if not a SegmentId + * @return the SegmentId or null + * @throws CosIdException if not a SegmentId when required + */ public SegmentId asSegmentId(boolean required) { IdGenerator idGenerator = tryGet(required); if (null == idGenerator) { @@ -99,12 +146,12 @@ public SegmentId asSegmentId(boolean required) { } throw new CosIdException(Strings.lenientFormat("IdGenerator:[%s] is not instanceof SegmentId!", generatorName)); } - + @Override public @NonNull IdGenerator getActual() { return tryGet(true); } - + @Override public @NonNull IdConverter idConverter() { return getActual().idConverter(); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/provider/NotFoundIdGeneratorException.java b/cosid-core/src/main/java/me/ahoo/cosid/provider/NotFoundIdGeneratorException.java index daf823247c..96a25d770c 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/provider/NotFoundIdGeneratorException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/provider/NotFoundIdGeneratorException.java @@ -16,18 +16,28 @@ import me.ahoo.cosid.CosIdException; /** - * Not Found IdGenerator Exception. + * Exception thrown when an ID generator is not found. * * @author ahoo wang */ public class NotFoundIdGeneratorException extends CosIdException { private final String generatorName; + /** + * Creates a new exception. + * + * @param generatorName the name of the generator that was not found + */ public NotFoundIdGeneratorException(String generatorName) { super(String.format("IdGenerator name:[%s] not found.", generatorName)); this.generatorName = generatorName; } + /** + * Gets the generator name that was not found. + * + * @return the generator name + */ public String getGeneratorName() { return generatorName; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/DefaultSegmentId.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/DefaultSegmentId.java index 08c1b22098..217d806c4b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/DefaultSegmentId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/DefaultSegmentId.java @@ -25,6 +25,10 @@ /** * Default segment algorithm ID generator. * + *

Provides thread-safe ID generation using a segment-based approach. + * Allocates ID segments from a central distributor and generates IDs + * locally within each segment for high throughput. + * * @author ahoo wang */ @Slf4j @@ -36,10 +40,21 @@ public class DefaultSegmentId implements SegmentId { @GuardedBy("this") private volatile IdSegment segment = DefaultIdSegment.OVERFLOW; + /** + * Creates a generator with infinite segment TTL. + * + * @param maxIdDistributor the segment distributor + */ public DefaultSegmentId(IdSegmentDistributor maxIdDistributor) { this(TIME_TO_LIVE_FOREVER, maxIdDistributor); } + /** + * Creates a generator with specified segment TTL. + * + * @param idSegmentTtl segment time-to-live in seconds + * @param maxIdDistributor the segment distributor + */ public DefaultSegmentId(long idSegmentTtl, IdSegmentDistributor maxIdDistributor) { Preconditions.checkArgument(idSegmentTtl > 0, "idSegmentTtl:[%s] must be greater than 0.", idSegmentTtl); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentChain.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentChain.java index 2ad4b03c97..9536a9e30b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentChain.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentChain.java @@ -20,43 +20,68 @@ import java.util.function.Function; /** - * Chained ID segment. + * Chained ID segment for lock-free segment chain ID generation. + * + *

This class chains multiple ID segments together, allowing for seamless + * transition between segments without blocking. When one segment is exhausted, + * the next segment in the chain is used. * * @author ahoo wang */ public class IdSegmentChain implements IdSegment { + /** + * Version number for the root chain. + */ public static final int ROOT_VERSION = -1; + /** + * Sentinel value indicating next has not been set. + */ public static final IdSegmentChain NOT_SET = null; - + private final long version; private final IdSegment idSegment; @GuardedBy("this") private volatile IdSegmentChain next; private final boolean allowReset; + /** + * Creates a new chain segment linked to a previous chain. + * + * @param previousChain the previous chain in the link + * @param idSegment the ID segment for this chain + * @param allowReset whether reset is allowed + */ public IdSegmentChain(IdSegmentChain previousChain, IdSegment idSegment, boolean allowReset) { this(previousChain.getVersion() + 1, idSegment, allowReset); } - + + /** + * Creates a new chain segment with explicit version. + * + * @param version the version number + * @param idSegment the ID segment for this chain + * @param allowReset whether reset is allowed + */ public IdSegmentChain(long version, IdSegment idSegment, boolean allowReset) { this.version = version; this.idSegment = idSegment; this.allowReset = allowReset; } - + /** - * try set next Chained ID segment. + * Attempts to set the next segment in the chain. * - * @param idSegmentChainSupplier {@link IdSegmentChain} supplier - * @return true if set successfully - * @throws NextIdSegmentExpiredException This exception is thrown - * if the provided {@link IdSegmentChain} has expired. + *

If next is already set, returns false without modifying. + * + * @param idSegmentChainSupplier supplier that creates the next segment based on this + * @return true if set successfully, false if next was already set + * @throws NextIdSegmentExpiredException if the provided segment has expired */ public boolean trySetNext(Function idSegmentChainSupplier) throws NextIdSegmentExpiredException { if (NOT_SET != next) { return false; } - + synchronized (this) { if (NOT_SET != next) { return false; @@ -66,15 +91,27 @@ public boolean trySetNext(Function idSegmentChai return true; } } - + + /** + * Sets the next segment in the chain. + * + * @param nextIdSegmentChain the next segment + */ public void setNext(IdSegmentChain nextIdSegmentChain) { if (!allowReset) { ensureNextIdSegment(nextIdSegmentChain); } - + next = nextIdSegmentChain; } - + + /** + * Ensures the next segment is set, retrying until successful. + * + * @param idSegmentChainSupplier supplier that creates the next segment + * @return the chain that has next set + * @throws NextIdSegmentExpiredException if all segments expire + */ public IdSegmentChain ensureSetNext(Function idSegmentChainSupplier) throws NextIdSegmentExpiredException { IdSegmentChain currentChain = this; while (!currentChain.trySetNext(idSegmentChainSupplier)) { @@ -82,67 +119,95 @@ public IdSegmentChain ensureSetNext(Function idS } return currentChain; } - + + /** + * Gets the next segment in the chain. + * + * @return the next segment or null if not yet set + */ public IdSegmentChain getNext() { return next; } - + + /** + * Gets the ID segment for this chain. + * + * @return the ID segment + */ public IdSegment getIdSegment() { return idSegment; } - + @Override public GroupedKey group() { return idSegment.group(); } - + + /** + * Gets the version number of this chain. + * + * @return the version + */ public long getVersion() { return version; } - + + /** + * Calculates the gap between this chain's sequence and another's max ID. + * + * @param end the end chain + * @param step the step size + * @return the number of IDs between sequences + */ public int gap(IdSegmentChain end, long step) { return (int) ((end.getMaxId() - getSequence()) / step); } - + + /** + * Creates a new root chain. + * + * @param allowReset whether reset is allowed + * @return a new root chain + */ public static IdSegmentChain newRoot(boolean allowReset) { return new IdSegmentChain(IdSegmentChain.ROOT_VERSION, DefaultIdSegment.OVERFLOW, allowReset); } - + @Override public long getFetchTime() { return idSegment.getFetchTime(); } - + @Override public long getTtl() { return idSegment.getTtl(); } - + @Override public long getMaxId() { return idSegment.getMaxId(); } - + @Override public long getOffset() { return idSegment.getOffset(); } - + @Override public long getSequence() { return idSegment.getSequence(); } - + @Override public long getStep() { return idSegment.getStep(); } - + @Override public long incrementAndGet() { return idSegment.incrementAndGet(); } - + @Override public String toString() { return "IdSegmentChain{" diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorDefinition.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorDefinition.java index 3523891caf..1d05be9867 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorDefinition.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorDefinition.java @@ -14,7 +14,10 @@ package me.ahoo.cosid.segment; /** - * IdSegment Distributor Definition. + * Definition for an ID segment distributor configuration. + * + *

Holds the configuration parameters needed to allocate ID segments + * from a distributor (namespace, name, offset, step). * * @author ahoo wang */ @@ -24,6 +27,14 @@ public class IdSegmentDistributorDefinition { private final long offset; private final long step; + /** + * Creates a new definition. + * + * @param namespace the namespace + * @param name the segment name + * @param offset the starting offset + * @param step the step size for segment allocation + */ public IdSegmentDistributorDefinition(String namespace, String name, long offset, long step) { this.namespace = namespace; this.name = name; @@ -31,22 +42,47 @@ public IdSegmentDistributorDefinition(String namespace, String name, long offset this.step = step; } + /** + * Gets the namespace. + * + * @return the namespace + */ public String getNamespace() { return namespace; } + /** + * Gets the segment name. + * + * @return the name + */ public String getName() { return name; } + /** + * Gets the full namespaced name. + * + * @return namespace.name + */ public String getNamespacedName() { return IdSegmentDistributor.getNamespacedName(getNamespace(), getName()); } + /** + * Gets the starting offset. + * + * @return the offset + */ public long getOffset() { return offset; } + /** + * Gets the step size. + * + * @return the step + */ public long getStep() { return step; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorFactory.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorFactory.java index 8a5ce6f010..1c7aa0e4aa 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorFactory.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/IdSegmentDistributorFactory.java @@ -17,12 +17,18 @@ import org.jspecify.annotations.NonNull; /** - * {@link IdSegmentDistributor} Factory. + * Factory interface for creating {@link IdSegmentDistributor} instances. * * @author ahoo wang */ @FunctionalInterface public interface IdSegmentDistributorFactory { + /** + * Creates an ID segment distributor from a definition. + * + * @param definition the distributor definition + * @return the created distributor + */ @NonNull IdSegmentDistributor create(IdSegmentDistributorDefinition definition); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/MergedIdSegment.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/MergedIdSegment.java index 974d511765..bd43c977d3 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/MergedIdSegment.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/MergedIdSegment.java @@ -18,35 +18,54 @@ import java.util.concurrent.TimeUnit; /** - * Merged IdSegment. + * Merged ID segment that divides a segment into multiple sub-segments. + * + *

This allows a single allocated segment to be shared among multiple + * consumers or purposes by splitting the step into equal portions. * * @author ahoo wang */ public class MergedIdSegment implements IdSegment { - + private final int segments; private final IdSegment idSegment; private final long singleStep; - + + /** + * Creates a merged segment. + * + * @param segments number of sub-segments to create + * @param idSegment the underlying segment + */ public MergedIdSegment(int segments, IdSegment idSegment) { this.segments = segments; this.idSegment = idSegment; this.singleStep = idSegment.getStep() / segments; } - + @Override public GroupedKey group() { return idSegment.group(); } - + + /** + * Gets the number of sub-segments. + * + * @return number of segments + */ public int getSegments() { return segments; } - + + /** + * Gets the step size for each sub-segment. + * + * @return single step size + */ public long getSingleStep() { return singleStep; } - + /** * ID segment fetch time. * unit {@link TimeUnit#MILLISECONDS} @@ -57,37 +76,37 @@ public long getSingleStep() { public long getFetchTime() { return idSegment.getFetchTime(); } - + @Override public long getMaxId() { return idSegment.getMaxId(); } - + @Override public long getOffset() { return idSegment.getOffset(); } - + @Override public long getSequence() { return idSegment.getSequence(); } - + @Override public long getStep() { return idSegment.getStep(); } - + @Override public long getTtl() { return idSegment.getTtl(); } - + @Override public long incrementAndGet() { return idSegment.incrementAndGet(); } - + @Override public String toString() { return "MergedIdSegment{" diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/NextIdSegmentExpiredException.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/NextIdSegmentExpiredException.java index 0d39aec9d0..d3b357ffd3 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/NextIdSegmentExpiredException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/NextIdSegmentExpiredException.java @@ -20,7 +20,11 @@ import java.util.concurrent.atomic.AtomicLong; /** - * Next IdSegment Expired Exception. + * Exception thrown when a next ID segment is invalid. + * + *

This exception indicates that the provided next segment has an offset + * that is not greater than the current segment's offset, which would cause + * ID conflicts or duplication. * * @author ahoo wang */ @@ -29,6 +33,12 @@ public class NextIdSegmentExpiredException extends CosIdException { private final IdSegment current; private final IdSegment next; + /** + * Creates a new exception. + * + * @param current the current segment + * @param next the invalid next segment + */ public NextIdSegmentExpiredException(IdSegment current, IdSegment next) { super(Strings.lenientFormat("The next IdSegment:[%s] cannot be before the current IdSegment:[%s]-- times:[%s].", next, @@ -39,10 +49,20 @@ public NextIdSegmentExpiredException(IdSegment current, IdSegment next) { this.next = next; } + /** + * Gets the current segment. + * + * @return the current segment + */ public IdSegment getCurrent() { return current; } + /** + * Gets the invalid next segment. + * + * @return the next segment + */ public IdSegment getNext() { return next; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/StringSegmentId.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/StringSegmentId.java index 175a7cef29..964c8e6619 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/StringSegmentId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/StringSegmentId.java @@ -18,18 +18,27 @@ import me.ahoo.cosid.stat.generator.IdGeneratorStat; /** - * String SegmentId. + * String-based SegmentId wrapper. + * + *

Wraps a SegmentId with a string converter for generating + * string-based IDs while maintaining segment functionality. * * @author ahoo wang */ public class StringSegmentId extends StringIdGeneratorDecorator implements SegmentId { private final SegmentId actualSegmentId; - + + /** + * Creates a new StringSegmentId. + * + * @param actual the underlying segment ID + * @param idConverter the converter for string generation + */ public StringSegmentId(SegmentId actual, IdConverter idConverter) { super(actual, idConverter); this.actualSegmentId = actual; } - + @Override public IdSegment current() { return actualSegmentId.current(); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/AffinityJob.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/AffinityJob.java index c26593df92..f3e4992808 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/AffinityJob.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/AffinityJob.java @@ -16,32 +16,58 @@ import me.ahoo.cosid.util.Clock; /** - * Affinity Job. + * Job with affinity for prefetch worker assignment. + * + *

Represents a segment prefetch task that has affinity to a specific + * worker instance for consistent segment allocation. * * @author ahoo wang */ public interface AffinityJob extends Runnable { - + + /** + * Gets the unique job identifier. + * + * @return the job ID + */ String getJobId(); - + + /** + * Gets the affinity key for worker assignment. + * + * @return the affinity key (defaults to job ID) + */ default String affinity() { return getJobId(); } - + + /** + * Signals this job is hungry and needs prefetch. + */ default void hungry() { setHungerTime(Clock.CACHE.secondTime()); getPrefetchWorker().wakeup(this); } - + /** - * set hunger time. + * Sets the hunger time for this job. * - * @param hungerTime {@link java.util.concurrent.TimeUnit#SECONDS} + * @param hungerTime time in seconds since epoch */ void setHungerTime(long hungerTime); - + + /** + * Gets the prefetch worker for this job. + * + * @return the prefetch worker + */ PrefetchWorker getPrefetchWorker(); - + + /** + * Sets the prefetch worker for this job. + * + * @param prefetchWorker the worker to set + */ void setPrefetchWorker(PrefetchWorker prefetchWorker); - + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/DefaultPrefetchWorker.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/DefaultPrefetchWorker.java index af108f22ac..ec29b9764a 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/DefaultPrefetchWorker.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/DefaultPrefetchWorker.java @@ -22,7 +22,10 @@ import java.util.concurrent.locks.LockSupport; /** - * Default Prefetch Worker. + * Default prefetch worker implementation. + * + *

Runs a background thread that periodically executes affinity jobs + * to prefetch ID segments before they are exhausted. * * @author ahoo wang */ @@ -34,6 +37,11 @@ public class DefaultPrefetchWorker extends Thread implements PrefetchWorker { private final Duration prefetchPeriod; private final CopyOnWriteArraySet affinityJobs = new CopyOnWriteArraySet<>(); + /** + * Creates a worker with specified prefetch period. + * + * @param prefetchPeriod the period between prefetch runs + */ public DefaultPrefetchWorker(Duration prefetchPeriod) { super(Strings.lenientFormat("DefaultPrefetchWorker-" + THREAD_COUNTER.incrementAndGet())); this.prefetchPeriod = prefetchPeriod; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/PrefetchWorker.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/PrefetchWorker.java index cd10ce0af5..15c91adcba 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/PrefetchWorker.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/concurrent/PrefetchWorker.java @@ -16,21 +16,47 @@ import com.google.errorprone.annotations.ThreadSafe; /** - * Prefetch Worker. + * Worker for prefetching ID segments. + * + *

Manages background prefetching of ID segments to ensure + * segments are available before they are exhausted. * * @author ahoo wang */ @ThreadSafe public interface PrefetchWorker { + /** + * Gets the worker name. + * + * @return the worker name + */ String getName(); + /** + * Submits a job for prefetching. + * + * @param affinityJob the job to submit + */ void submit(AffinityJob affinityJob); + /** + * Cancels a prefetch job. + * + * @param affinityJob the job to cancel + */ void cancel(AffinityJob affinityJob); + /** + * Wakes up a job for immediate processing. + * + * @param affinityJob the job to wake up + */ void wakeup(AffinityJob affinityJob); + /** + * Shuts down the worker. + */ void shutdown(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/DefaultGroupedIdSegmentDistributor.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/DefaultGroupedIdSegmentDistributor.java index f7a05f9c75..13cc6de6a9 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/DefaultGroupedIdSegmentDistributor.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/DefaultGroupedIdSegmentDistributor.java @@ -21,19 +21,34 @@ import org.jspecify.annotations.NonNull; +/** + * Default grouped ID segment distributor implementation. + * + *

Manages segment distribution with group-based isolation, + * creating separate distributors for each group. + * + * @author ahoo wang + */ public class DefaultGroupedIdSegmentDistributor implements GroupedIdSegmentDistributor { private final GroupBySupplier groupBySupplier; private final IdSegmentDistributorDefinition idSegmentDistributorDefinition; private final IdSegmentDistributorFactory idSegmentDistributorFactory; private volatile GroupedBinding currentGroup; - + + /** + * Creates a grouped distributor. + * + * @param groupBySupplier the group supplier + * @param idSegmentDistributorDefinition the definition + * @param idSegmentDistributorFactory the factory + */ public DefaultGroupedIdSegmentDistributor(GroupBySupplier groupBySupplier, IdSegmentDistributorDefinition idSegmentDistributorDefinition, IdSegmentDistributorFactory idSegmentDistributorFactory) { this.groupBySupplier = groupBySupplier; this.idSegmentDistributorDefinition = idSegmentDistributorDefinition; this.idSegmentDistributorFactory = idSegmentDistributorFactory; this.ensureGroupedBinding(); } - + private GroupedBinding ensureGroupedBinding() { GroupedKey groupedKey = groupBySupplier.get(); if (currentGroup != null && currentGroup.group().equals(groupedKey)) { @@ -50,121 +65,135 @@ private GroupedBinding ensureGroupedBinding() { idSegmentDistributorDefinition.getStep()); this.currentGroup = new GroupedBinding(groupedKey, idSegmentDistributorFactory.create(groupedDef)); } - + return currentGroup; } - + + /** + * Gets the group supplier. + * + * @return the supplier + */ public GroupBySupplier groupBySupplier() { return groupBySupplier; } - + @Override public @NonNull String getNamespace() { return this.idSegmentDistributorDefinition.getNamespace(); } - + @Override public @NonNull String getName() { return this.idSegmentDistributorDefinition.getName(); } - + @Override public long getStep() { return this.idSegmentDistributorDefinition.getStep(); } - + @Override public GroupedKey group() { return this.ensureGroupedBinding().group(); } - + @Override public long nextMaxId() { return this.ensureGroupedBinding().nextMaxId(); } - + @Override public long nextMaxId(long step) { return this.ensureGroupedBinding().nextMaxId(step); } - + @Override public @NonNull IdSegment nextIdSegment() { return this.ensureGroupedBinding().nextIdSegment(); } - + @Override public @NonNull IdSegment nextIdSegment(long ttl) { return this.ensureGroupedBinding().nextIdSegment(ttl); } - + @Override public @NonNull IdSegment nextIdSegment(int segments, long ttl) { return this.ensureGroupedBinding().nextIdSegment(segments, ttl); } - + @Override public @NonNull IdSegmentChain nextIdSegmentChain(IdSegmentChain previousChain, int segments, long ttl) { return this.ensureGroupedBinding().nextIdSegmentChain(previousChain, segments, ttl); } - + @Override public @NonNull IdSegmentChain nextIdSegmentChain(IdSegmentChain previousChain) { return this.ensureGroupedBinding().nextIdSegmentChain(previousChain); } - + + /** + * Holds a group binding with its distributor. + */ public static class GroupedBinding implements GroupedIdSegmentDistributor { - + private final GroupedKey group; private final IdSegmentDistributor idSegmentDistributor; - + + /** + * Creates a binding. + * + * @param group the group key + * @param idSegmentDistributor the distributor + */ public GroupedBinding(GroupedKey group, IdSegmentDistributor idSegmentDistributor) { this.group = group; this.idSegmentDistributor = idSegmentDistributor; } - + @Override public GroupedKey group() { return group; } - + @Override public @NonNull String getNamespace() { return idSegmentDistributor.getNamespace(); } - + @Override public @NonNull String getName() { return idSegmentDistributor.getName(); } - + @Override public long getStep() { return idSegmentDistributor.getStep(); } - + @Override public long nextMaxId(long step) { return idSegmentDistributor.nextMaxId(step); } - + private long getMinTtl(long ttl) { long groupedTtl = group.ttl(); return Math.min(groupedTtl, ttl); } - + @Override public @NonNull IdSegment nextIdSegment(long ttl) { long minTtl = getMinTtl(ttl); return GroupedIdSegmentDistributor.super.nextIdSegment(minTtl); } - + @Override public @NonNull IdSegment nextIdSegment(int segments, long ttl) { long minTtl = getMinTtl(ttl); return GroupedIdSegmentDistributor.super.nextIdSegment(segments, minTtl); } - + @Override public @NonNull IdSegmentChain nextIdSegmentChain(IdSegmentChain previousChain, int segments, long ttl) { long minTtl = getMinTtl(ttl); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupBySupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupBySupplier.java index 38e3fa7d2b..fab2be8dca 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupBySupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupBySupplier.java @@ -15,6 +15,9 @@ import java.util.function.Supplier; +/** + * Supplier of {@link GroupedKey} for grouping related ID segments. + */ public interface GroupBySupplier extends Supplier { } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/Grouped.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/Grouped.java index 0ea853a283..0f83e197c1 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/Grouped.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/Grouped.java @@ -13,7 +13,22 @@ package me.ahoo.cosid.segment.grouped; +/** + * Interface for objects that can be grouped for sharding purposes. + * + *

Implementations return a {@link GroupedKey} that represents the sharding + * key for this object. This is used by segmented ID generators to organize + * IDs into logical groups. + */ public interface Grouped { + /** + * Gets the grouping key for this object. + * + *

Default implementation returns {@link GroupedKey#NEVER}, indicating + * this object should not be grouped. + * + * @return the grouping key + */ default GroupedKey group() { return GroupedKey.NEVER; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedAccessor.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedAccessor.java index e629d5e7a8..3ea78cb339 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedAccessor.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedAccessor.java @@ -18,14 +18,32 @@ import java.util.Objects; +/** + * Thread-local accessor for managing grouped key context. + * + *

Provides thread-local storage for the current group key, + * allowing different threads to work with different group contexts. + * + * @author ahoo wang + */ @ThreadSafe public final class GroupedAccessor { private static final ThreadLocal CURRENT = new ThreadLocal<>(); + /** + * Sets the grouped key for the current thread. + * + * @param groupedKey the key to set + */ public static void set(GroupedKey groupedKey) { CURRENT.set(groupedKey); } + /** + * Sets the grouped key if it's not NEVER. + * + * @param groupedKey the key to set + */ public static void setIfNotNever(GroupedKey groupedKey) { if (GroupedKey.NEVER.equals(groupedKey)) { return; @@ -33,15 +51,29 @@ public static void setIfNotNever(GroupedKey groupedKey) { set(groupedKey); } + /** + * Gets the grouped key for the current thread. + * + * @return the key or null if not set + */ public static GroupedKey get() { return CURRENT.get(); } + /** + * Gets the grouped key, throwing if not set. + * + * @return the key + * @throws NullPointerException if not set + */ @NonNull public static GroupedKey requiredGet() { return Objects.requireNonNull(get(), "The current thread has not set the GroupedKey."); } + /** + * Clears the grouped key for the current thread. + */ public static void clear() { CURRENT.remove(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributor.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributor.java index 6826dd86d1..7308274949 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributor.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributor.java @@ -15,8 +15,16 @@ import me.ahoo.cosid.segment.IdSegmentDistributor; +/** + * Extension of IdSegmentDistributor that supports ID grouping. + * + *

Grouped distributors allow ID segments to be partitioned by a grouping key + * (e.g., time-based buckets like "2024-01" for monthly sharding). + * + * @author ahoo wang + */ public interface GroupedIdSegmentDistributor extends IdSegmentDistributor { - + @Override default boolean allowReset() { return true; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributorFactory.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributorFactory.java index 69b5def5ba..3d04c5fcb0 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributorFactory.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedIdSegmentDistributorFactory.java @@ -19,15 +19,28 @@ import org.jspecify.annotations.NonNull; +/** + * Factory for creating grouped ID segment distributors. + * + *

Wraps an existing factory with grouping support. + * + * @author ahoo wang + */ public class GroupedIdSegmentDistributorFactory implements IdSegmentDistributorFactory { private final GroupBySupplier groupBySupplier; private final IdSegmentDistributorFactory actual; - + + /** + * Creates a factory with grouping support. + * + * @param groupBySupplier the group supplier + * @param actual the underlying factory + */ public GroupedIdSegmentDistributorFactory(GroupBySupplier groupBySupplier, IdSegmentDistributorFactory actual) { this.groupBySupplier = groupBySupplier; this.actual = actual; } - + @Override public @NonNull IdSegmentDistributor create(IdSegmentDistributorDefinition definition) { return new DefaultGroupedIdSegmentDistributor(groupBySupplier, definition, actual); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedKey.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedKey.java index 269f7abc23..d6b62e40c0 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedKey.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/GroupedKey.java @@ -20,34 +20,61 @@ import java.util.Objects; +/** + * Key used for grouping/sharding of ID segments. + * + *

Represents a logical grouping key with an optional TTL for time-based + * sharding strategies (e.g., daily or monthly buckets). + * + * @author ahoo wang + */ public final class GroupedKey { + /** + * Sentinel value indicating no grouping. + */ public static final GroupedKey NEVER = new GroupedKey("", IdSegment.TIME_TO_LIVE_FOREVER); private final String key; private final long ttlAt; - + + /** + * Creates a new GroupedKey. + * + * @param key the grouping key (e.g., "2024-01" for monthly) + * @param ttlAt the time-to-live expiration timestamp in seconds + */ public GroupedKey(String key, long ttlAt) { this.key = key; this.ttlAt = ttlAt; } - + + /** + * Gets the grouping key. + * + * @return the key + */ public String getKey() { return key; } - + /** - * get ttlAt of group. + * Gets the TTL expiration timestamp. * - * @return ttlAt + * @return TTL timestamp in seconds * @see IdSegment#getTtl() */ public long getTtlAt() { return ttlAt; } - + + /** + * Calculates remaining TTL from current time. + * + * @return remaining TTL in seconds, or 0 if expired + */ public long ttl() { return ttlAt - Clock.CACHE.secondTime(); } - + @Override public boolean equals(Object o) { if (this == o) { @@ -59,12 +86,12 @@ public boolean equals(Object o) { GroupedKey that = (GroupedKey) o; return ttlAt == that.ttlAt && Objects.equals(key, that.key); } - + @Override public int hashCode() { return Objects.hash(key, ttlAt); } - + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -72,7 +99,13 @@ public String toString() { .add("ttlAt", ttlAt) .toString(); } - + + /** + * Creates a GroupedKey that never expires. + * + * @param key the grouping key + * @return a forever GroupedKey + */ public static GroupedKey forever(String key) { return new GroupedKey(key, IdSegment.TIME_TO_LIVE_FOREVER); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/AbstractDateGroupBySupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/AbstractDateGroupBySupplier.java index fa196f6a9a..238113c77a 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/AbstractDateGroupBySupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/AbstractDateGroupBySupplier.java @@ -21,15 +21,37 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; +/** + * Abstract base class for date-based group key suppliers. + * + * @param the temporal type + * @author ahoo wang + */ public abstract class AbstractDateGroupBySupplier implements GroupBySupplier { protected final DateTimeFormatter formatter; + /** + * Creates a supplier with the specified formatter. + * + * @param formatter the date formatter + */ public AbstractDateGroupBySupplier(DateTimeFormatter formatter) { this.formatter = formatter; } + /** + * Gets the current date/time. + * + * @return the current value + */ abstract D now(); + /** + * Gets the last timestamp for the given date. + * + * @param date the date + * @return the last timestamp + */ abstract LocalDateTime lastTimestamp(D date); @Override diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearGroupBySupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearGroupBySupplier.java index 6bf2110091..9d41ed8e3f 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearGroupBySupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearGroupBySupplier.java @@ -20,12 +20,27 @@ import java.time.Year; import java.time.format.DateTimeFormatter; +/** + * Year-based group key supplier. + * + * @author ahoo wang + */ public class YearGroupBySupplier extends AbstractDateGroupBySupplier { + /** + * Creates a supplier with the specified formatter. + * + * @param formatter the date formatter + */ public YearGroupBySupplier(DateTimeFormatter formatter) { super(formatter); } + /** + * Creates a supplier with the specified pattern. + * + * @param pattern the date format pattern + */ public YearGroupBySupplier(String pattern) { this(DateTimeFormatter.ofPattern(pattern)); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthDayGroupBySupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthDayGroupBySupplier.java index 7f5307f180..f564ea10ff 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthDayGroupBySupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthDayGroupBySupplier.java @@ -18,12 +18,27 @@ import java.time.LocalTime; import java.time.format.DateTimeFormatter; +/** + * Year-month-day based group key supplier. + * + * @author ahoo wang + */ public class YearMonthDayGroupBySupplier extends AbstractDateGroupBySupplier { + /** + * Creates a supplier with the specified formatter. + * + * @param formatter the date formatter + */ public YearMonthDayGroupBySupplier(DateTimeFormatter formatter) { super(formatter); } + /** + * Creates a supplier with the specified pattern. + * + * @param pattern the date format pattern + */ public YearMonthDayGroupBySupplier(String pattern) { this(DateTimeFormatter.ofPattern(pattern)); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthGroupBySupplier.java b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthGroupBySupplier.java index 261f1c07e8..3f35833cff 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthGroupBySupplier.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/segment/grouped/date/YearMonthGroupBySupplier.java @@ -20,11 +20,26 @@ import java.time.format.DateTimeFormatter; +/** + * Year-month based group key supplier. + * + * @author ahoo wang + */ public class YearMonthGroupBySupplier extends AbstractDateGroupBySupplier { + /** + * Creates a supplier with the specified formatter. + * + * @param formatter the date formatter + */ public YearMonthGroupBySupplier(DateTimeFormatter formatter) { super(formatter); } + /** + * Creates a supplier with the specified pattern. + * + * @param pattern the date format pattern + */ public YearMonthGroupBySupplier(String pattern) { this(DateTimeFormatter.ofPattern(pattern)); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/CachedSharding.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/CachedSharding.java index f04f44b7ef..867af2bb91 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/CachedSharding.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/CachedSharding.java @@ -23,8 +23,12 @@ import java.util.Collection; /** - * Cached Sharding. + * Caching wrapper for sharding algorithms. * + *

Caches range sharding results to reduce computation overhead + * for frequently accessed ranges. + * + * @param the type of comparable sharding value * @author ahoo wang */ @Beta @@ -33,6 +37,11 @@ public class CachedSharding> implements Sharding { private final Sharding actual; private final LoadingCache, Collection> shardingCache; + /** + * Creates a cached wrapper around the specified sharding algorithm. + * + * @param actual the underlying sharding algorithm + */ public CachedSharding(Sharding actual) { this.actual = actual; shardingCache = CacheBuilder diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/ExactCollection.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/ExactCollection.java index 7385732425..c4e6590434 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/ExactCollection.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/ExactCollection.java @@ -23,13 +23,11 @@ import java.util.RandomAccess; /** - * 准确式集合. - * 用于 Interval 算法、Mod 算法可以提前预知节点数量的场景。 - *

- * 主要针对以下问题:
- * -- 使用{@link java.util.HashSet}导致的内存空间浪费
- * -- 添加元素时导致的集合膨胀(也可以通过给定 expectedSize 计算准确 capacity 就像 {@link Sets#newHashSetWithExpectedSize(int)})
- * 
+ * Exact-size collection optimized for known node counts. + * + *

Used by Interval and Mod algorithms where the number of + * nodes is known in advance, avoiding the memory overhead + * of HashSet and unnecessary array expansion. * * @author ahoo wang */ diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalStep.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalStep.java index 088e0f7af2..f8b3cfe5ce 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalStep.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalStep.java @@ -19,39 +19,67 @@ import java.time.temporal.ChronoUnit; /** - * Interval Step. + * Represents a time interval step for sharding operations. + * + *

Defines the granularity and size of time-based sharding intervals. + * Used for interval sharding algorithms in ShardingSphere integration. * * @author ahoo wang */ @Immutable public class IntervalStep { + /** + * Default amount of 1. + */ public static final int DEFAULT_AMOUNT = 1; - + private final ChronoUnit unit; private final int amount; - + + /** + * Creates an interval step. + * + * @param unit the time unit (years, months, days, etc.) + * @param amount the number of units per step + */ public IntervalStep(ChronoUnit unit, int amount) { this.unit = unit; this.amount = amount; } - + + /** + * Gets the time unit. + * + * @return the unit + */ public ChronoUnit getUnit() { return unit; } - + + /** + * Gets the amount. + * + * @return the amount + */ public int getAmount() { return amount; } - + + /** + * Calculates the next time by adding the interval. + * + * @param previous the previous time + * @return the next time + */ public LocalDateTime next(LocalDateTime previous) { return previous.plus(amount, unit); } - + /** - * 按照 {@link #unit} 保留单位时间精度. + * Truncates time to the precision of the unit. * - * @param time time - * @return Unit precision LocalDateTime + * @param time the time to truncate + * @return time truncated to unit precision */ public LocalDateTime floorUnit(LocalDateTime time) { switch (unit) { @@ -77,23 +105,35 @@ public LocalDateTime floorUnit(LocalDateTime time) { throw new IllegalStateException("Unexpected value: " + unit); } } - + /** - * 计算单位偏移量. - * Start with 0 + * Calculates the offset from start to time in unit increments. * - * @param start 最小值 - * @param time time - * @return offset + * @param start the start time + * @param time the target time + * @return the offset in units */ public int offsetUnit(LocalDateTime start, LocalDateTime time) { return (int) (start.until(time, unit) / amount); } - + + /** + * Creates an interval step with default amount of 1. + * + * @param unit the time unit + * @return the interval step + */ public static IntervalStep of(ChronoUnit unit) { return new IntervalStep(unit, DEFAULT_AMOUNT); } - + + /** + * Creates an interval step with custom amount. + * + * @param unit the time unit + * @param amount the number of units + * @return the interval step + */ public static IntervalStep of(ChronoUnit unit, int amount) { return new IntervalStep(unit, amount); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalTimeline.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalTimeline.java index 409ca1f626..97d8d8f6ac 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalTimeline.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/IntervalTimeline.java @@ -26,7 +26,11 @@ import java.util.List; /** - * Interval Timeline. + * Timeline-based interval sharding algorithm. + * + *

Distributes IDs across time-based intervals, where each interval + * maps to a specific node. The intervals are calculated based on a + * configured step size (e.g., daily, monthly). * *

CosIdIntervalShardingAlgorithm

* @@ -43,6 +47,14 @@ public class IntervalTimeline implements Sharding { private final DateTimeFormatter suffixFormatter; private final ExactCollection effectiveNodes; + /** + * Creates an IntervalTimeline sharding algorithm. + * + * @param logicNamePrefix the prefix for node names + * @param effectiveInterval the effective time range + * @param step the interval step configuration + * @param suffixFormatter formatter for interval suffixes + */ public IntervalTimeline(String logicNamePrefix, Range effectiveInterval, IntervalStep step, DateTimeFormatter suffixFormatter) { this.effectiveInterval = effectiveInterval; this.step = step; @@ -73,14 +85,30 @@ private static ExactCollection initEffectiveNodes(Interval[] effectiveIn return effectiveNodes; } + /** + * Gets the number of intervals. + * + * @return the number of intervals + */ public int size() { return effectiveIntervals.length; } + /** + * Checks if the given time is within the effective interval. + * + * @param time the time to check + * @return true if within effective interval + */ public boolean contains(LocalDateTime time) { return effectiveInterval.contains(time); } + /** + * Gets the start interval. + * + * @return the start interval + */ public Interval getStartInterval() { return startInterval; } @@ -156,20 +184,39 @@ public Interval getStartInterval() { return nodes; } + /** + * Represents a time interval with an associated node name. + */ public static class Interval { private final LocalDateTime lower; private final String node; + /** + * Creates an interval. + * + * @param lower the lower bound of the interval + * @param node the associated node name + */ public Interval(LocalDateTime lower, String node) { this.lower = lower; this.node = node; } + /** + * Gets the lower bound. + * + * @return the lower bound + */ public LocalDateTime getLower() { return lower; } + /** + * Gets the node name. + * + * @return the node name + */ public String getNode() { return node; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/ModCycle.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/ModCycle.java index daebf2a3c7..9f5611e0c4 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/ModCycle.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/ModCycle.java @@ -21,10 +21,14 @@ import java.util.Collection; /** - * ModCycle. + * Modulo-based cyclical sharding algorithm. + * + *

Distributes IDs across nodes using modulo arithmetic. + * Maps a sharding value to a node by computing: {@code value % divisor}. * *

CosIdModShardingAlgorithm

* + * @param the type of number that is comparable * @author ahoo wang */ public class ModCycle> implements Sharding { @@ -32,6 +36,12 @@ public class ModCycle> implements Sharding { private final String logicNamePrefix; private final ExactCollection effectiveNodes; + /** + * Creates a ModCycle sharding algorithm. + * + * @param divisor the number of nodes to distribute across + * @param logicNamePrefix the prefix for node names + */ public ModCycle(int divisor, String logicNamePrefix) { Preconditions.checkArgument(divisor > 0, "divisor must be greater than 0!"); this.divisor = divisor; @@ -50,6 +60,11 @@ private static ExactCollection initNodes(int divisor, String logicNamePr return modNodes; } + /** + * Gets the divisor. + * + * @return the divisor + */ public int getDivisor() { return divisor; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/PreciseSharding.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/PreciseSharding.java index 95be90751e..87bd1a00af 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/PreciseSharding.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/PreciseSharding.java @@ -15,7 +15,18 @@ import org.jspecify.annotations.NonNull; +/** + * Single-value sharding algorithm interface. + * + * @param the type of comparable sharding value + */ public interface PreciseSharding> { + /** + * Gets the node for a single sharding value. + * + * @param shardingValue the sharding value + * @return the node name + */ @NonNull String sharding(T shardingValue); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/RangeSharding.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/RangeSharding.java index 1bc3c981f9..2b92dbea68 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/RangeSharding.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/RangeSharding.java @@ -18,7 +18,20 @@ import java.util.Collection; +/** + * Range-based sharding algorithm interface. + * + *

Determines which node(s) should handle a range of ID values. + * + * @param the type of comparable sharding value + */ public interface RangeSharding> { + /** + * Gets the nodes that should handle the given range of IDs. + * + * @param shardingValue the range of sharding values + * @return collection of node names that should handle the range + */ @NonNull Collection sharding(Range shardingValue); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/sharding/Sharding.java b/cosid-core/src/main/java/me/ahoo/cosid/sharding/Sharding.java index 84460c6aec..554133c1a3 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/sharding/Sharding.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/sharding/Sharding.java @@ -19,7 +19,10 @@ import java.util.Collection; /** - * Sharding algorithm interface. + * Sharding algorithm interface for distributing data across multiple nodes. + * + *

Combines both precise sharding (single key) and range sharding (key range) + * capabilities to determine which node(s) should handle a given ID or ID range. * *

Sharding

* @@ -27,6 +30,11 @@ */ @ThreadSafe public interface Sharding> extends PreciseSharding, RangeSharding { + /** + * Gets all effective node names. + * + * @return collection of effective node names + */ @NonNull Collection getEffectiveNodes(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/AbstractSnowflakeId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/AbstractSnowflakeId.java index 74d76ae8bf..8195621869 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/AbstractSnowflakeId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/AbstractSnowflakeId.java @@ -19,31 +19,88 @@ import com.google.common.base.Strings; /** - * Abstract SnowflakeId. + * Abstract SnowflakeId implementation. + * + *

This abstract class provides the base implementation for Snowflake ID generation, + * handling the common logic for timestamp management, sequence counting, and ID assembly. + * Subclasses implement {@link #getCurrentTime()} to provide time in different units + * (milliseconds, seconds, etc.). + * + *

The ID is composed of: timestamp (configurable bits) + machine ID (configurable bits) + sequence (configurable bits) * * @author ahoo wang */ public abstract class AbstractSnowflakeId implements SnowflakeId { + /** + * Epoch timestamp used as the base for time calculations. + */ protected final long epoch; + /** + * Number of bits allocated for the timestamp portion. + */ protected final int timestampBit; + /** + * Number of bits allocated for the machine ID portion. + */ protected final int machineBit; + /** + * Number of bits allocated for the sequence portion. + */ protected final int sequenceBit; + /** + * Maximum timestamp value representable by the timestamp bits. + */ protected final long maxTimestamp; + /** + * Maximum sequence value representable by the sequence bits. + */ protected final long maxSequence; + /** + * Maximum machine ID value representable by the machine ID bits. + */ protected final int maxMachineId; + /** + * Number of bits to shift machine ID left (equal to sequenceBit). + */ protected final long machineLeft; + /** + * Number of bits to shift timestamp left (equal to sequenceBit + machineBit). + */ protected final long timestampLeft; /** - * WARN:machineLeft greater than 30 will cause overflow, so machineId should be long when calculating. + * The machine ID value for this instance. + * + * @implNote When machineLeft is greater than 30, overflow can occur during calculation, + * so machineId should be kept as long during arithmetic operations. */ protected final long machineId; + /** + * Threshold for resetting sequence counter when timestamp advances. + */ private final long sequenceResetThreshold; + /** + * Current sequence counter value. + */ protected long sequence = 0L; + /** + * Timestamp of the last generated ID. + */ protected long lastTimestamp = -1L; + /** + * Creates a new AbstractSnowflakeId. + * + * @param epoch epoch timestamp in milliseconds + * @param timestampBit number of bits for timestamp + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID value + * @param sequenceResetThreshold threshold for resetting sequence on timestamp advance + * @throws IllegalArgumentException if total bits exceed 63 or machineId is invalid + */ public AbstractSnowflakeId(long epoch, int timestampBit, int machineBit, @@ -69,6 +126,11 @@ public AbstractSnowflakeId(long epoch, this.sequenceResetThreshold = sequenceResetThreshold; } + /** + * Waits until the current time is greater than the last timestamp. + * + * @return the next valid timestamp + */ protected long nextTime() { long time = getCurrentTime(); while (time <= lastTimestamp) { @@ -78,12 +140,21 @@ protected long nextTime() { } /** - * get current timestamp. + * Gets the current time in the appropriate unit for this snowflake ID variant. * - * @return current timestamp + * @return current time value */ protected abstract long getCurrentTime(); + /** + * Generates the next unique ID. + * + *

This method is synchronized to ensure thread-safe ID generation. + * + * @return a unique snowflake ID + * @throws ClockBackwardsException if system clock has moved backwards + * @throws TimestampOverflowException if timestamp exceeds maximum value + */ @Override public synchronized long generate() { long currentTimestamp = getCurrentTime(); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/DefaultSnowflakeFriendlyId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/DefaultSnowflakeFriendlyId.java index 61d6191440..d913cee126 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/DefaultSnowflakeFriendlyId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/DefaultSnowflakeFriendlyId.java @@ -21,7 +21,10 @@ import java.time.ZoneId; /** - * Default Snowflake FriendlyId. + * Default implementation of {@link SnowflakeFriendlyId}. + * + *

Wraps a {@link SnowflakeId} and provides human-readable + * string conversion using a {@link SnowflakeIdStateParser}. * * @author ahoo wang */ @@ -29,18 +32,42 @@ public class DefaultSnowflakeFriendlyId extends StringSnowflakeId implements Sno private final SnowflakeIdStateParser snowflakeIdStateParser; + /** + * Creates an instance with system default zone. + * + * @param actual the underlying Snowflake ID + */ public DefaultSnowflakeFriendlyId(SnowflakeId actual) { this(actual, ZoneId.systemDefault()); } + /** + * Creates an instance with specified zone. + * + * @param actual the underlying Snowflake ID + * @param zoneId the time zone + */ public DefaultSnowflakeFriendlyId(SnowflakeId actual, ZoneId zoneId) { this(actual, SnowflakeIdStateParser.of(actual, zoneId, false)); } + /** + * Creates an instance with specified parser. + * + * @param actual the underlying Snowflake ID + * @param snowflakeIdStateParser the state parser + */ public DefaultSnowflakeFriendlyId(SnowflakeId actual, SnowflakeIdStateParser snowflakeIdStateParser) { this(actual, new SnowflakeFriendlyIdConverter(snowflakeIdStateParser), snowflakeIdStateParser); } + /** + * Creates an instance with specified converter and parser. + * + * @param actual the underlying Snowflake ID + * @param converter the ID converter + * @param snowflakeIdStateParser the state parser + */ public DefaultSnowflakeFriendlyId(SnowflakeId actual, IdConverter converter, SnowflakeIdStateParser snowflakeIdStateParser) { super(actual, converter); this.snowflakeIdStateParser = snowflakeIdStateParser; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/MillisecondSnowflakeIdStateParser.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/MillisecondSnowflakeIdStateParser.java index f00d849e1d..a5e08a8984 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/MillisecondSnowflakeIdStateParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/MillisecondSnowflakeIdStateParser.java @@ -31,12 +31,18 @@ import java.time.format.DateTimeFormatterBuilder; /** - * Millisecond SnowflakeId State Parser. + * Parser for millisecond-based SnowflakeId state. + * + *

Handles conversion between millisecond SnowflakeIds and their string + * representations using format: {@code yyyyMMddHHmmssSSS-machineId-sequence}. * * @author ahoo wang */ public class MillisecondSnowflakeIdStateParser extends SnowflakeIdStateParser { + /** + * Default parser instance with CosId epoch. + */ public static final SnowflakeIdStateParser INSTANCE = new MillisecondSnowflakeIdStateParser( CosId.COSID_EPOCH, MillisecondSnowflakeId.DEFAULT_TIMESTAMP_BIT, @@ -44,6 +50,9 @@ public class MillisecondSnowflakeIdStateParser extends SnowflakeIdStateParser { MillisecondSnowflakeId.DEFAULT_SEQUENCE_BIT ); + /** + * DateTimeFormatter for timestamps: {@code yyyyMMddHHmmssSSS}. + */ public static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .appendValue(YEAR, 4) .appendValue(MONTH_OF_YEAR, 2) @@ -54,10 +63,28 @@ public class MillisecondSnowflakeIdStateParser extends SnowflakeIdStateParser { .appendValue(MILLI_OF_SECOND, 3) .toFormatter(); + /** + * Creates a parser with default zone and no padding. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + */ public MillisecondSnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit) { this(epoch, timestampBit, machineBit, sequenceBit, ZoneId.systemDefault(), false); } + /** + * Creates a parser with custom zone and padding. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + * @param zoneId time zone + * @param padStart whether to pad + */ public MillisecondSnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit, ZoneId zoneId, boolean padStart) { super(epoch, timestampBit, machineBit, sequenceBit, zoneId, padStart); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SafeJavaScriptSnowflakeId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SafeJavaScriptSnowflakeId.java index be43373861..d20c625dc7 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SafeJavaScriptSnowflakeId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SafeJavaScriptSnowflakeId.java @@ -16,32 +16,63 @@ import me.ahoo.cosid.CosId; /** - * Safe JavaScript Number ID. - * Number.MAX_SAFE_INTEGER = 9007199254740991 - * Math.log2(Number.MAX_SAFE_INTEGER) = 53 + * Safe JavaScript Snowflake ID generators. + * + *

JavaScript Numbers can only safely represent integers up to 2^53 - 1 + * (Number.MAX_SAFE_INTEGER = 9007199254740991). This class provides factory + * methods for creating SnowflakeId instances that stay within this limit + * by reducing total bits to 53 or fewer. * * @author ahoo wang **/ public final class SafeJavaScriptSnowflakeId { - + + /** + * Maximum safe JavaScript number bit count. + */ public static final int JAVA_SCRIPT_MAX_SAFE_NUMBER_BIT = 53; + /** + * Maximum safe JavaScript number value. + */ public static final long JAVA_SCRIPT_MAX_SAFE_NUMBER = 9007199254740991L; - + + /** + * Checks if an ID is safe for JavaScript. + * + * @param id the ID to check + * @return true if less than MAX_SAFE_NUMBER + */ public static boolean isSafeJavaScript(long id) { return id < JAVA_SCRIPT_MAX_SAFE_NUMBER; } - + + /** + * Creates a safe millisecond SnowflakeId. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for sequence reset + * @return a new MillisecondSnowflakeId + */ public static MillisecondSnowflakeId ofMillisecond(long epoch, int timestampBit, int machineBit, int sequenceBit, int machineId, long sequenceResetThreshold) { checkTotalBit(timestampBit, machineBit, sequenceBit); return new MillisecondSnowflakeId(epoch, timestampBit, machineBit, sequenceBit, machineId, sequenceResetThreshold); } - + /** - * Max Sequence (9 bits) = ((1<<)*1000) = 512000 (TPS) - * Max Machine (3 bits) = 1<<3 = 8 - * Max Timestamp = 2199023255551 ms ~~ 69.7 years + * Creates a safe millisecond SnowflakeId with default safe configuration. * - * @param machineId 服务实例编号 + *

Default safe configuration: + *

    + *
  • Timestamp: 41 bits
  • + *
  • Machine: 3 bits
  • + *
  • Sequence: 9 bits
  • + *
+ * + * @param machineId the machine ID (max 7) * @return MillisecondSnowflakeId */ public static MillisecondSnowflakeId ofMillisecond(int machineId) { @@ -51,18 +82,34 @@ public static MillisecondSnowflakeId ofMillisecond(int machineId) { checkTotalBit(timestampBit, machineBit, sequenceBit); return ofMillisecond(CosId.COSID_EPOCH_SECOND, timestampBit, machineBit, sequenceBit, machineId, SnowflakeId.defaultSequenceResetThreshold(sequenceBit)); } - + + /** + * Creates a safe second SnowflakeId. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for sequence reset + * @return a new SecondSnowflakeId + */ public static SecondSnowflakeId ofSecond(long epoch, int timestampBit, int machineBit, int sequenceBit, int machineId, long sequenceResetThreshold) { checkTotalBit(timestampBit, machineBit, sequenceBit); return new SecondSnowflakeId(epoch, timestampBit, machineBit, sequenceBit, machineId, sequenceResetThreshold); } - + /** - * Max Sequence (19 bits) = (1<<19) = 524288 (TPS). - * Max Machine (3 bits) = 1<<3 = 8 - * Max Timestamp = 2147483647 s ~~ 68 years + * Creates a safe second SnowflakeId with default safe configuration. * - * @param machineId 服务实例编号 + *

Default safe configuration: + *

    + *
  • Timestamp: 31 bits
  • + *
  • Machine: 3 bits
  • + *
  • Sequence: 19 bits
  • + *
+ * + * @param machineId the machine ID (max 7) * @return SecondSnowflakeId */ public static SecondSnowflakeId ofSecond(int machineId) { @@ -72,7 +119,7 @@ public static SecondSnowflakeId ofSecond(int machineId) { checkTotalBit(timestampBit, machineBit, sequenceBit); return ofSecond(CosId.COSID_EPOCH_SECOND, timestampBit, machineBit, sequenceBit, machineId, SnowflakeId.defaultSequenceResetThreshold(sequenceBit)); } - + private static void checkTotalBit(int timestampBit, int machineBit, int sequenceBit) { if (timestampBit + machineBit + sequenceBit > JAVA_SCRIPT_MAX_SAFE_NUMBER_BIT) { throw new IllegalArgumentException(String.format("total bit can't be greater than JAVA_SCRIPT_MAX_SAFE_NUMBER_BIT:[%s].", JAVA_SCRIPT_MAX_SAFE_NUMBER_BIT)); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeId.java index a59201894e..9690c5b0c7 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeId.java @@ -18,33 +18,86 @@ import java.util.concurrent.TimeUnit; /** - * Second SnowflakeId. + * Second-based Snowflake ID generator. + * + *

Similar to {@link MillisecondSnowflakeId} but uses seconds instead of milliseconds + * as the time unit. This allows for a longer timestamp range but with lower + * time precision. + * + *

Default configuration: + *

    + *
  • Timestamp bits: 31 (about 68 years with second precision)
  • + *
  • Machine ID bits: 10 (1024 unique machines)
  • + *
  • Sequence bits: 22 (about 4 million IDs per second per machine)
  • + *
* * @author ahoo wang - **/ + */ public class SecondSnowflakeId extends AbstractSnowflakeId { - + + /** + * Default number of timestamp bits (31 bits). + */ public static final int DEFAULT_TIMESTAMP_BIT = 31; + /** + * Default number of machine ID bits (10 bits). + */ public static final int DEFAULT_MACHINE_BIT = 10; + /** + * Default number of sequence bits (22 bits). + */ public static final int DEFAULT_SEQUENCE_BIT = 22; + /** + * Default sequence reset threshold (half of max sequence). + */ public static final long DEFAULT_SEQUENCE_RESET_THRESHOLD = ~(-1L << (DEFAULT_SEQUENCE_BIT - 1)); - + + /** + * Creates a SecondSnowflakeId with default configuration. + * + * @param machineId the machine ID + */ public SecondSnowflakeId(int machineId) { this(CosId.COSID_EPOCH_SECOND, DEFAULT_TIMESTAMP_BIT, DEFAULT_MACHINE_BIT, DEFAULT_SEQUENCE_BIT, machineId, DEFAULT_SEQUENCE_RESET_THRESHOLD); } - + + /** + * Creates a SecondSnowflakeId with custom machine bits. + * + * @param machineBit the number of bits for machine ID + * @param machineId the machine ID + */ public SecondSnowflakeId(int machineBit, int machineId) { super(CosId.COSID_EPOCH_SECOND, DEFAULT_TIMESTAMP_BIT, machineBit, DEFAULT_SEQUENCE_BIT, machineId, DEFAULT_SEQUENCE_RESET_THRESHOLD); } - + + /** + * Creates a SecondSnowflakeId with custom bit configuration. + * + * @param epoch epoch timestamp in milliseconds + * @param timestampBit number of bits for timestamp + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + */ public SecondSnowflakeId(long epoch, int timestampBit, int machineBit, int sequenceBit, int machineId) { super(epoch, timestampBit, machineBit, sequenceBit, machineId, SnowflakeId.defaultSequenceResetThreshold(sequenceBit)); } - + + /** + * Creates a SecondSnowflakeId with full custom configuration. + * + * @param epoch epoch timestamp in milliseconds + * @param timestampBit number of bits for timestamp + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param machineId the machine ID + * @param sequenceResetThreshold threshold for resetting sequence + */ public SecondSnowflakeId(long epoch, int timestampBit, int machineBit, int sequenceBit, int machineId, long sequenceResetThreshold) { super(epoch, timestampBit, machineBit, sequenceBit, machineId, sequenceResetThreshold); } - + @Override protected long getCurrentTime() { return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeIdStateParser.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeIdStateParser.java index 63a08cf99c..e8c35cb092 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeIdStateParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SecondSnowflakeIdStateParser.java @@ -28,12 +28,18 @@ import java.time.format.DateTimeFormatterBuilder; /** - * Second SnowflakeId State Parser. + * Parser for second-based SnowflakeId state. + * + *

Handles conversion between second SnowflakeIds and their string + * representations using format: {@code yyyyMMddHHmmss-machineId-sequence}. * * @author ahoo wang */ public class SecondSnowflakeIdStateParser extends SnowflakeIdStateParser { + /** + * DateTimeFormatter for timestamps: {@code yyyyMMddHHmmss}. + */ public static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() .appendValue(YEAR, 4) .appendValue(MONTH_OF_YEAR, 2) @@ -43,10 +49,28 @@ public class SecondSnowflakeIdStateParser extends SnowflakeIdStateParser { .appendValue(SECOND_OF_MINUTE, 2) .toFormatter(); + /** + * Creates a parser with default zone and no padding. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + */ public SecondSnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit) { this(epoch, timestampBit, machineBit, sequenceBit, ZoneId.systemDefault(), false); } + /** + * Creates a parser with custom zone and padding. + * + * @param epoch epoch timestamp + * @param timestampBit bits for timestamp + * @param machineBit bits for machine ID + * @param sequenceBit bits for sequence + * @param zoneId time zone + * @param padStart whether to pad + */ public SecondSnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit, ZoneId zoneId, boolean padStart) { super(epoch, timestampBit, machineBit, sequenceBit, zoneId, padStart); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeFriendlyId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeFriendlyId.java index 5bf2fcbbda..cebfcb99a3 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeFriendlyId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeFriendlyId.java @@ -16,29 +16,54 @@ import org.jspecify.annotations.NonNull; /** - * Snowflake FriendlyId. + * Snowflake ID with human-readable string representation. + * + *

Provides methods to convert Snowflake IDs to and from + * a friendly string format containing timestamp, machine ID, and sequence. * * @author ahoo wang */ public interface SnowflakeFriendlyId extends SnowflakeId { - + + /** + * Gets the state parser. + * + * @return the parser + */ @NonNull SnowflakeIdStateParser getParser(); - + + /** + * Parses a raw ID to friendly state. + * + * @param id the raw ID + * @return the friendly state + */ @NonNull default SnowflakeIdState friendlyId(long id) { return getParser().parse(id); } - + + /** + * Generates an ID and returns its friendly state. + * + * @return the friendly state + */ @NonNull default SnowflakeIdState friendlyId() { long id = generate(); return friendlyId(id); } - + + /** + * Parses a friendly ID string to state. + * + * @param friendlyId the friendly ID string + * @return the friendly state + */ @NonNull default SnowflakeIdState ofFriendlyId(String friendlyId) { return getParser().parse(friendlyId); } - + } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdState.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdState.java index db96a4f2ef..a25363c295 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdState.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdState.java @@ -20,25 +20,42 @@ import java.util.Objects; /** - * SnowflakeId State. + * Immutable state object representing a parsed Snowflake ID. + * + *

This class holds the decomposed components of a Snowflake ID: + * the raw ID, machine ID, sequence number, timestamp, and a human-readable + * friendly format ({@code timestamp-machineId-sequence}). * * @author ahoo wang */ @Immutable public class SnowflakeIdState implements Comparable { - + + /** + * The raw 64-bit snowflake ID value. + */ private final long id; - + + /** + * The machine ID portion of the ID. + */ private final int machineId; - + + /** + * The sequence number portion of the ID. + */ private final long sequence; - + + /** + * The timestamp when this ID was generated. + */ private final LocalDateTime timestamp; + /** - * {@link #timestamp}-{@link #machineId}-{@link #sequence} . + * Human-readable representation in format {@code timestamp-machineId-sequence}. */ private final String friendlyId; - + SnowflakeIdState(long id, int machineId, long sequence, LocalDateTime timestamp, String friendlyId) { this.id = id; this.machineId = machineId; @@ -46,33 +63,63 @@ public class SnowflakeIdState implements Comparable { this.timestamp = timestamp; this.friendlyId = friendlyId; } - + + /** + * Creates a new builder for SnowflakeIdState. + * + * @return a new builder instance + */ public static SnowflakeIdStateBuilder builder() { return new SnowflakeIdStateBuilder(); } - + + /** + * Gets the raw snowflake ID value. + * + * @return the raw 64-bit ID + */ public long getId() { return id; } - + + /** + * Gets the machine ID component. + * + * @return the machine ID + */ public int getMachineId() { return machineId; } - + + /** + * Gets the sequence number component. + * + * @return the sequence number + */ public long getSequence() { return sequence; } - + + /** + * Gets the timestamp when this ID was generated. + * + * @return the timestamp as LocalDateTime + */ @NonNull public LocalDateTime getTimestamp() { return timestamp; } - + + /** + * Gets the human-readable form of this ID. + * + * @return friendly ID in format {@code timestamp-machineId-sequence} + */ @NonNull public String getFriendlyId() { return friendlyId; } - + @Override public boolean equals(Object other) { if (this == other) { @@ -84,61 +131,106 @@ public boolean equals(Object other) { SnowflakeIdState that = (SnowflakeIdState) other; return id == that.id; } - + @Override public int hashCode() { return Objects.hashCode(id); } - + @Override public String toString() { return friendlyId; } - + + /** + * Compares this state to another by raw ID value. + * + * @param other the other SnowflakeIdState to compare + * @return comparison result + */ @Override public int compareTo(SnowflakeIdState other) { return Long.compare(this.id, other.id); } - + + /** + * Builder for SnowflakeIdState. + */ public static class SnowflakeIdStateBuilder { private long id; private int machineId; private long sequence; private LocalDateTime timestamp; private String friendlyId; - + SnowflakeIdStateBuilder() { } - + + /** + * Sets the raw snowflake ID value. + * + * @param id the raw 64-bit ID + * @return this builder + */ public SnowflakeIdStateBuilder id(long id) { this.id = id; return this; } - + + /** + * Sets the machine ID component. + * + * @param machineId the machine ID + * @return this builder + */ public SnowflakeIdStateBuilder machineId(int machineId) { this.machineId = machineId; return this; } - + + /** + * Sets the sequence number component. + * + * @param sequence the sequence number + * @return this builder + */ public SnowflakeIdStateBuilder sequence(long sequence) { this.sequence = sequence; return this; } - + + /** + * Sets the timestamp component. + * + * @param timestamp the timestamp + * @return this builder + */ public SnowflakeIdStateBuilder timestamp(LocalDateTime timestamp) { this.timestamp = timestamp; return this; } - + + /** + * Sets the friendly ID string. + * + * @param friendlyId the friendly ID string + * @return this builder + */ public SnowflakeIdStateBuilder friendlyId(String friendlyId) { this.friendlyId = friendlyId; return this; } - + + /** + * Builds the SnowflakeIdState instance. + * + * @return the built instance + */ public SnowflakeIdState build() { return new SnowflakeIdState(id, machineId, sequence, timestamp, friendlyId); } - + + @Override public String toString() { return "SnowflakeIdState.SnowflakeIdStateBuilder(id=" + this.id diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdStateParser.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdStateParser.java index f6eb799881..2caa885727 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdStateParser.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/SnowflakeIdStateParser.java @@ -31,35 +31,95 @@ /** - * SnowflakeId State Parser. + * Abstract parser for converting between Snowflake IDs and their component state. + * + *

This class provides methods to parse a snowflake ID into its components + * (timestamp, machineId, sequence) and to reconstruct the raw ID from a + * friendly string format. + * + *

The friendly format is: {@code timestamp-machineId-sequence} (e.g., "20210623131730192-1-0") * * @author ahoo wang */ @ThreadSafe public abstract class SnowflakeIdStateParser { + /** + * Delimiter used in friendly ID format. + */ public static final String DELIMITER = "-"; + /** + * Time zone used for timestamp conversion. + */ protected final ZoneId zoneId; + /** + * Epoch timestamp in milliseconds. + */ protected final long epoch; + /** + * Number of bits for sequence portion. + */ protected final int sequenceBit; + /** + * Mask for extracting sequence from raw ID. + */ protected final long sequenceMask; + /** + * Number of bits for machine ID portion. + */ protected final int machineBit; + /** + * Mask for extracting machine ID from raw ID. + */ protected final long machineMask; + /** + * Number of bits to shift machine ID left. + */ protected final int machineLeft; + /** + * Number of bits for timestamp portion. + */ protected final int timestampBit; + /** + * Mask for extracting timestamp from raw ID. + */ protected final long timestampMask; + /** + * Number of bits to shift timestamp left. + */ protected final int timestampLeft; + /** + * Whether to pad numeric fields with leading zeros. + */ protected final boolean padStart; private final int machineCharSize; private final int sequenceCharSize; + /** + * Creates a parser with default zone and no padding. + * + * @param epoch epoch timestamp in milliseconds + * @param timestampBit number of bits for timestamp + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + */ public SnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit) { this(epoch, timestampBit, machineBit, sequenceBit, ZoneId.systemDefault(), false); } + /** + * Creates a parser with custom zone and padding settings. + * + * @param epoch epoch timestamp in milliseconds + * @param timestampBit number of bits for timestamp + * @param machineBit number of bits for machine ID + * @param sequenceBit number of bits for sequence + * @param zoneId time zone for timestamp conversion + * @param padStart whether to pad numeric fields with leading zeros + */ public SnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int sequenceBit, ZoneId zoneId, boolean padStart) { this.epoch = epoch; this.sequenceMask = getMask(sequenceBit); @@ -76,28 +136,75 @@ public SnowflakeIdStateParser(long epoch, int timestampBit, int machineBit, int this.sequenceCharSize = RadixIdConverter.maxCharSize(DECIMAL_RADIX, sequenceBit); } + /** + * Gets the time zone used for timestamp conversion. + * + * @return the zone ID + */ public ZoneId getZoneId() { return zoneId; } + /** + * Checks if numeric fields are padded with leading zeros. + * + * @return true if padding is enabled + */ public boolean isPadStart() { return padStart; } + /** + * Gets the maximum character size for machine ID in decimal representation. + * + * @return the machine ID character size + */ public int getMachineCharSize() { return machineCharSize; } + /** + * Gets the maximum character size for sequence in decimal representation. + * + * @return the sequence character size + */ public int getSequenceCharSize() { return sequenceCharSize; } + /** + * Gets the date time formatter for parsing timestamps. + * + * @return the date time formatter + */ protected abstract DateTimeFormatter getDateTimeFormatter(); + /** + * Converts a time difference to a LocalDateTime. + * + * @param diffTime time difference from epoch in the appropriate unit + * @return the corresponding LocalDateTime + */ protected abstract LocalDateTime getTimestamp(long diffTime); + /** + * Converts a LocalDateTime to time difference from epoch. + * + * @param timestamp the LocalDateTime to convert + * @return time difference from epoch + */ protected abstract long getDiffTime(LocalDateTime timestamp); + /** + * Parses a friendly ID string into SnowflakeIdState. + * + *

Expected format: {@code timestamp-machineId-sequence} + * + * @param friendlyId the friendly ID string to parse + * @return the parsed state + * @throws IllegalArgumentException if format is invalid + * @throws NullPointerException if friendlyId is null + */ public SnowflakeIdState parse(String friendlyId) { Preconditions.checkNotNull(friendlyId, "friendlyId can not be null!"); List segments = Splitter.on(DELIMITER).trimResults().omitEmptyStrings().splitToList(friendlyId); @@ -124,6 +231,12 @@ public SnowflakeIdState parse(String friendlyId) { .build(); } + /** + * Parses a raw snowflake ID into SnowflakeIdState. + * + * @param id the raw snowflake ID + * @return the parsed state + */ public SnowflakeIdState parse(long id) { int machineId = parseMachineId(id); long sequence = parseSequence(id); @@ -149,23 +262,55 @@ private long getMask(long bits) { return ~(-1L << bits); } + /** + * Extracts and parses the timestamp portion of an ID. + * + * @param id the raw snowflake ID + * @return the parsed timestamp as LocalDateTime + */ public LocalDateTime parseTimestamp(long id) { long diffTime = (id >> timestampLeft) & timestampMask; return getTimestamp(diffTime); } + /** + * Extracts the machine ID portion of an ID. + * + * @param id the raw snowflake ID + * @return the machine ID + */ public int parseMachineId(long id) { return (int) ((id >> machineLeft) & machineMask); } + /** + * Extracts the sequence portion of an ID. + * + * @param id the raw snowflake ID + * @return the sequence number + */ public long parseSequence(long id) { return id & sequenceMask; } + /** + * Creates a parser for the given SnowflakeId instance. + * + * @param snowflakeId the snowflake ID to create a parser for + * @return the appropriate parser for the snowflake ID type + */ public static SnowflakeIdStateParser of(SnowflakeId snowflakeId) { return of(snowflakeId, ZoneId.systemDefault(), false); } + /** + * Creates a parser for the given SnowflakeId instance with custom settings. + * + * @param snowflakeId the snowflake ID to create a parser for + * @param zoneId time zone for timestamp conversion + * @param padStart whether to pad numeric fields + * @return the appropriate parser for the snowflake ID type + */ public static SnowflakeIdStateParser of(SnowflakeId snowflakeId, ZoneId zoneId, boolean padStart) { SnowflakeId actual = IdGeneratorDecorator.getActual(snowflakeId); diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/StringSnowflakeId.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/StringSnowflakeId.java index ef2fc765f1..bb580ff972 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/StringSnowflakeId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/StringSnowflakeId.java @@ -18,13 +18,22 @@ import me.ahoo.cosid.stat.generator.IdGeneratorStat; /** - * String SnowflakeId. + * String-based SnowflakeId wrapper. + * + *

Wraps a SnowflakeId with a string converter for generating + * string-based IDs while maintaining SnowflakeId properties. * * @author ahoo wang */ public class StringSnowflakeId extends StringIdGeneratorDecorator implements SnowflakeId { private final SnowflakeId snowflakeId; + /** + * Creates a new StringSnowflakeId. + * + * @param actual the underlying SnowflakeId + * @param idConverter the converter for string generation + */ public StringSnowflakeId(SnowflakeId actual, IdConverter idConverter) { super(actual, idConverter); this.snowflakeId = actual; diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockBackwardsException.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockBackwardsException.java index 88463d99d5..3a8639c65b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockBackwardsException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockBackwardsException.java @@ -16,7 +16,10 @@ import me.ahoo.cosid.CosIdException; /** - * Clock Backwards Exception. + * Exception thrown when system clock moves backwards. + * + *

Indicates that the current system time is less than the last timestamp + * used for ID generation, which could cause ID duplication. * * @author ahoo wang */ @@ -24,16 +27,32 @@ public class ClockBackwardsException extends CosIdException { private final long lastTimestamp; private final long currentTimestamp; + /** + * Creates a new exception. + * + * @param lastTimestamp the last generated timestamp + * @param currentTimestamp the current system timestamp + */ public ClockBackwardsException(long lastTimestamp, long currentTimestamp) { super(String.format("Clock moved backwards. Refusing to generate id. lastTimestamp:[%s] | currentTimestamp:[%s]", lastTimestamp, currentTimestamp)); this.lastTimestamp = lastTimestamp; this.currentTimestamp = currentTimestamp; } + /** + * Gets the last timestamp. + * + * @return the last timestamp + */ public long getLastTimestamp() { return lastTimestamp; } + /** + * Gets the current timestamp. + * + * @return the current timestamp + */ public long getCurrentTimestamp() { return currentTimestamp; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockTooManyBackwardsException.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockTooManyBackwardsException.java index 1edfe9c3ad..607fabd1e2 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockTooManyBackwardsException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/ClockTooManyBackwardsException.java @@ -16,7 +16,10 @@ import me.ahoo.cosid.CosIdException; /** - * Clock Too Many Backwards Exception. + * Exception thrown when clock backwards exceeds threshold. + * + *

Indicates that the system clock has moved backwards by more than + * the configured broken threshold, and the generator cannot recover. * * @author ahoo wang */ @@ -26,6 +29,13 @@ public class ClockTooManyBackwardsException extends CosIdException { private final long currentTimestamp; private final long brokenThreshold; + /** + * Creates a new exception. + * + * @param lastTimestamp the last generated timestamp + * @param currentTimestamp the current system timestamp + * @param brokenThreshold the configured broken threshold + */ public ClockTooManyBackwardsException(long lastTimestamp, long currentTimestamp, long brokenThreshold) { super(String.format("Clock moved backwards too many. brokenThreshold:[%s] | lastTimestamp:[%s] | currentTimestamp:[%s]", brokenThreshold, lastTimestamp, currentTimestamp)); this.lastTimestamp = lastTimestamp; @@ -33,14 +43,29 @@ public ClockTooManyBackwardsException(long lastTimestamp, long currentTimestamp, this.brokenThreshold = brokenThreshold; } + /** + * Gets the last timestamp. + * + * @return the last timestamp + */ public long getLastTimestamp() { return lastTimestamp; } + /** + * Gets the current timestamp. + * + * @return the current timestamp + */ public long getCurrentTimestamp() { return currentTimestamp; } + /** + * Gets the broken threshold. + * + * @return the broken threshold + */ public long getBrokenThreshold() { return brokenThreshold; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/TimestampOverflowException.java b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/TimestampOverflowException.java index 6e08b79685..edcedc41c6 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/TimestampOverflowException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/snowflake/exception/TimestampOverflowException.java @@ -18,7 +18,11 @@ import com.google.common.base.Strings; /** - * Timestamp Overflow Exception. + * Exception thrown when timestamp exceeds maximum value. + * + *

Indicates that the timestamp portion of the ID has overflowed, + * meaning the generator has been in use for longer than the configured + * time range allows. * * @author ahoo wang */ @@ -27,6 +31,13 @@ public class TimestampOverflowException extends CosIdException { private final long diffTimestamp; private final long maxTimestamp; + /** + * Creates a new exception. + * + * @param epoch the configured epoch + * @param diffTimestamp the calculated timestamp difference + * @param maxTimestamp the maximum representable timestamp + */ public TimestampOverflowException(long epoch, long diffTimestamp, long maxTimestamp) { super(Strings.lenientFormat("epoch:[%s] - diffTimestamp:[%s] can't be greater than maxTimestamp:[%s]", epoch, diffTimestamp, maxTimestamp)); this.epoch = epoch; @@ -34,14 +45,29 @@ public TimestampOverflowException(long epoch, long diffTimestamp, long maxTimest this.maxTimestamp = maxTimestamp; } + /** + * Gets the epoch. + * + * @return the epoch + */ public long getEpoch() { return epoch; } + /** + * Gets the diff timestamp. + * + * @return the diff timestamp + */ public long getDiffTimestamp() { return diffTimestamp; } + /** + * Gets the max timestamp. + * + * @return the max timestamp + */ public long getMaxTimestamp() { return maxTimestamp; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/SimpleStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/SimpleStat.java index 8982c12aec..2a154a40ce 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/SimpleStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/SimpleStat.java @@ -16,6 +16,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Simple stat implementation wrapping another stat. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public class SimpleStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/Stat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/Stat.java index 14e430eed2..46cdb11366 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/Stat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/Stat.java @@ -15,19 +15,45 @@ import org.jspecify.annotations.Nullable; +/** + * Statistical information interface. + */ public interface Stat { + /** + * Gets the kind/type of this stat. + * + * @return the kind + */ String getKind(); + /** + * Gets the wrapped actual stat. + * + * @return the actual stat or null + */ @Nullable default Stat getActual() { return null; } + /** + * Creates a simple stat with the specified kind and actual. + * + * @param kind the kind + * @param actual the actual stat + * @return the stat + */ static Stat simple(String kind, @Nullable Stat actual) { return new SimpleStat(kind, actual); } + /** + * Creates a simple stat with the specified kind. + * + * @param kind the kind + * @return the stat + */ static Stat simple(String kind) { return simple(kind, null); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/Statistical.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/Statistical.java index abc45f0ac0..d2cbf066e1 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/Statistical.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/Statistical.java @@ -13,7 +13,15 @@ package me.ahoo.cosid.stat; +/** + * Interface for objects that can provide statistical information. + */ @FunctionalInterface public interface Statistical { + /** + * Gets statistical information. + * + * @return the stat + */ Stat stat(); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/DatePrefixConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/DatePrefixConverterStat.java index 6a42b66191..4f731d41dc 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/DatePrefixConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/DatePrefixConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Date prefix converter stat with pattern configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class DatePrefixConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/GroupedPrefixConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/GroupedPrefixConverterStat.java index a48d307ee1..a8cfb027fd 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/GroupedPrefixConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/GroupedPrefixConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Grouped prefix converter stat with delimiter configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class GroupedPrefixConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/PrefixConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/PrefixConverterStat.java index 28089cf4f5..c5b71c7c41 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/PrefixConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/PrefixConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Prefix converter stat with prefix configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class PrefixConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/RadixConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/RadixConverterStat.java index dc3f8e9e71..8076770d85 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/RadixConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/RadixConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Radix converter stat with radix configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class RadixConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SnowflakeFriendlyIdConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SnowflakeFriendlyIdConverterStat.java index 5a333f7355..2740f9a38b 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SnowflakeFriendlyIdConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SnowflakeFriendlyIdConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Snowflake friendly ID converter stat with configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public class SnowflakeFriendlyIdConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SuffixConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SuffixConverterStat.java index 59e7f08461..a337ece21e 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SuffixConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/SuffixConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Suffix converter stat with suffix configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class SuffixConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/ToStringConverterStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/ToStringConverterStat.java index ed9bdf22ff..cad80edad9 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/ToStringConverterStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/converter/ToStringConverterStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * ToString converter stat with padding configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class ToStringConverterStat implements Stat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/CosIdGeneratorStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/CosIdGeneratorStat.java index 14a18519a9..51a322eb5e 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/CosIdGeneratorStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/CosIdGeneratorStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * CosId generator stat with machine and timestamp info. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class CosIdGeneratorStat implements IdGeneratorStat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/IdGeneratorStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/IdGeneratorStat.java index 69639184bc..995bc060d4 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/IdGeneratorStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/IdGeneratorStat.java @@ -17,20 +17,43 @@ import org.jspecify.annotations.Nullable; +/** + * Statistical information for ID generators. + */ public interface IdGeneratorStat extends Stat { @Nullable @Override default IdGeneratorStat getActual() { return null; } - + + /** + * Gets the converter stat. + * + * @return the converter stat or null + */ @Nullable Stat getConverter(); - + + /** + * Creates a simple stat with the specified kind and converter. + * + * @param kind the kind + * @param actual the actual stat + * @param converter the converter stat + * @return the stat + */ static IdGeneratorStat simple(String kind, @Nullable IdGeneratorStat actual, Stat converter) { return new SimpleIdGeneratorStat(kind, actual, converter); } - + + /** + * Creates a simple stat with the specified kind and converter. + * + * @param kind the kind + * @param converter the converter stat + * @return the stat + */ static IdGeneratorStat simple(String kind, Stat converter) { return simple(kind, null, converter); } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SegmentIdStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SegmentIdStat.java index 8d78fb92ac..c216e765fd 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SegmentIdStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SegmentIdStat.java @@ -19,6 +19,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Segment ID generator stat with segment metadata. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class SegmentIdStat implements IdGeneratorStat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SimpleIdGeneratorStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SimpleIdGeneratorStat.java index 517b765617..8d476eb6be 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SimpleIdGeneratorStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SimpleIdGeneratorStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Simple ID generator stat implementation. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class SimpleIdGeneratorStat implements IdGeneratorStat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SnowflakeIdStat.java b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SnowflakeIdStat.java index 6a92bfdd3f..c94ec19011 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SnowflakeIdStat.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/stat/generator/SnowflakeIdStat.java @@ -18,6 +18,11 @@ import lombok.AllArgsConstructor; import lombok.Data; +/** + * Snowflake ID generator stat with Snowflake configuration. + * + * @author ahoo wang + */ @AllArgsConstructor @Data public final class SnowflakeIdStat implements IdGeneratorStat { diff --git a/cosid-core/src/main/java/me/ahoo/cosid/uncertainty/OriginalIdOverflowException.java b/cosid-core/src/main/java/me/ahoo/cosid/uncertainty/OriginalIdOverflowException.java index 0f80c1c3bf..bd51fbfcb5 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/uncertainty/OriginalIdOverflowException.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/uncertainty/OriginalIdOverflowException.java @@ -17,26 +17,53 @@ import com.google.common.base.Strings; +/** + * Exception thrown when original ID exceeds maximum representable value. + * + * @author ahoo wang + */ public class OriginalIdOverflowException extends CosIdException { private final long originalId; private final int originalIdBits; private final long maxOriginalId; - + + /** + * Creates a new exception. + * + * @param originalId the original ID value + * @param originalIdBits the number of bits for the original ID + * @param maxOriginalId the maximum representable ID + */ public OriginalIdOverflowException(long originalId, int originalIdBits, long maxOriginalId) { super(Strings.lenientFormat("OriginalId[%s] overflow - originalIdBits[%s] - maxOriginalId[%s].", originalId, originalIdBits, maxOriginalId)); this.originalId = originalId; this.originalIdBits = originalIdBits; this.maxOriginalId = maxOriginalId; } - + + /** + * Gets the original ID. + * + * @return the original ID + */ public long originalId() { return originalId; } - + + /** + * Gets the original ID bits. + * + * @return the bits + */ public int originalIdBits() { return originalIdBits; } - + + /** + * Gets the max original ID. + * + * @return the max ID + */ public long maxOriginalId() { return maxOriginalId; } diff --git a/cosid-core/src/main/java/me/ahoo/cosid/util/ProcessId.java b/cosid-core/src/main/java/me/ahoo/cosid/util/ProcessId.java index 9b5c1264de..b4ccdfaac9 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/util/ProcessId.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/util/ProcessId.java @@ -18,37 +18,48 @@ import java.lang.management.ManagementFactory; /** - * get current process id . + * Utility for getting current process information. + * + *

Provides access to the current process ID and process name, + * useful for machine identification in distributed ID generation. * * @author ahoo wang */ @Immutable public enum ProcessId { + /** + * Singleton instance for current process. + */ CURRENT; - + private final int processId; - + ProcessId() { this.processId = getCurrentProcessId(); } - + + /** + * Gets the process ID. + * + * @return the process ID + */ public int getProcessId() { return processId; } - + /** - * get current process name . + * Gets the current process name. * - * @return process name + * @return process name (typically in format pid@hostname) */ public static String getCurrentProcessName() { return ManagementFactory.getRuntimeMXBean().getName(); } - + /** - * get current process id . + * Gets the current process ID. * - * @return process id + * @return the process ID extracted from process name */ public static int getCurrentProcessId() { String processName = getCurrentProcessName();