Skip to content
This repository has been archived by the owner on Apr 27, 2024. It is now read-only.
/ tsid Public archive

Implementation of TSID (Time-Sorted ID) based on Snowflake ID.

License

Notifications You must be signed in to change notification settings

vincentdaogithub/tsid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TSID (Time-Sorted ID)

Implementation of TSID based on Snowflake ID.

Important notice

This project/repository will be archived and migrated to another project.

What is TSID?

TSID (stands for Time-Sorted ID) is a special type of ID that looks like UUID but is actually sortable and incremental. This ensures the usage of indexing in the database while preserving the uniqueness of UUID (or mostly the look of it).

TSID is basically a signed 64-bit integer (long in Java). The implementation is heavily inspired by the following sources:

Implementation Details

The current implementation of TSID consists of three parts:

  • The first 42 bits represent the timestamp of the creation of the TSID. Specifically, it comprises a 1-bit signed value and a 41-bit timestamp. The timestamp is calculated by getting the current UTC in milliseconds since Unix epoch and subtracting the custom epoch value (if defined).
  • The next 10 bits represent the node ID for the current machine/node/worker. This allocation is particularly useful in systems where multiple instances of applications are deployed, and each utilizes the TsidFactory for generating the Tsid.
  • Finally, the last 12 bits represent an incremental sequence number for multiple TSIDs generated within the same millisecond (timestamp), if such occurrences arise. The starting sequence for each timestamp is securely randomized to minimize the predictability of the TSID.

The generation of Tsid per node is guaranteed to be thread-safe.

Usage

To generate an instance of Tsid, we use TsidFactory:

import com.vincentdao.tsid.Tsid;
import com.vincentdao.tsid.TsidFactory;

public static void main(String[] args) {
    // Quickly generate a new one (NOT THREAD-SAFE!)
    Tsid quickGeneratedTsid = TsidFactory.instance().quickGenerate();

    // Generate new Tsid (thread-safe)
    Tsid tsid = TsidFactory.instance().generate();

    // Reset the factory
    TsidFactory.reset();

    // Configure the factory using its Builder
    TsidFactory.builder()
            .withNode()
            .customizedAs(69L)
            .withEpoch()
            .customizedAs(1000000L)
            .build();

    // Now, the new Tsid will have its node set to 69 and timestamp calculated from epoch 1000000
    Tsid configuredTsid = TsidFactory.instance().generate();
}

If we already have a Tsid value (long or String), we can use Tsid static methods:

import com.vincentdao.tsid.Tsid;

public static void main(String[] args) {
    // The value must be 64-bit integer!
    Tsid fromLongTsid = Tsid.fromLong(1541815603606036480L);

    // String must be in Crockford's encoding
    Tsid fromStringTsid = Tsid.fromString("2NJT27V22YG00");
}

Using Tsid in database

Since Tsid values are essentially 64-bit integers (i.e., long), we store them as such in the database. However, each database vendor has its own syntax for representing 64-bit integers. Below are the syntaxes for the long type used by some well-known database vendors:

  • MySQL, MariaDB, SQL Server, IBM DB2: BIGINT
  • SQLite: INTEGER (Note: SQLite uses dynamic typing, but INTEGER can store up to 64-bit values.)
  • Oracle: NUMBER (It's worth noting that Oracle's NUMBER type is capable of representing a wide range of values, so specifying precision and scale might be necessary depending on the use case.)

On the application level, we can retrieve the Tsid from the stored long value and present it back to the users in whatever format is most suitable.

Configure the factory

There are currently two configurations for the factory:

  • Node ID: Indicates the current machine/node/worker. As TSIDs can be generated by multiple instances in the system, it is crucial to clearly define each node with its ID to ensure safe generation (i.e., no collision). The default value is the current thread's ID where the call to obtain the factory instance occurs.
  • Custom epoch: Specifies the epoch for calculating the timestamp. The default is the Unix epoch.

These values can be defined at 3 places: system's environment, system's properties (for current running Java app), and on code-level using TsidFactory.Builder.

Configuring for the properties is used through -D argument:

java -D

Here are the name of these variables:

  • Node:
    • TSID_NODE for env
    • tsid.node for property
  • Epoch:
    • TSID_EPOCH for env
    • tsid.epoch for property

String Representation

The String representation of TSID is based on Crockford's Base32:

import com.vincentdao.tsid.Tsid;
import com.vincentdao.tsid.TsidFactory;

public static void main(String[] args) {
    // Example for Tsid with value "1541815603606036480"
    Tsid id = TsidFactory.instance().generate();

    System.out.println(id.asLong());                  // 1541815603606036480
    System.out.println(id.asString());                // "2NJT27V22YG00"
    System.out.println(id);                           // "2NJT27V22YG00"
    System.out.println(id.asLowercaseString());       // "2njt27v22yg00"
}

License

This project is licensed under MIT License.

References