Module ulid | Tarantool

Module ulid

Since version 3.6.0.

The ulid module implements ULID (Universally Unique Lexicographically Sortable Identifier) support in Tarantool. A ULID is a 128-bit identifier consisting of:

  • a 48-bit timestamp in milliseconds since the Unix epoch
  • an 80-bit random (entropy) component

ULID strings are encoded using Crockford Base32, a compact and human-friendly alphabet that excludes visually ambiguous characters.

In binary form, ULIDs are represented as 16-byte values in big-endian byte order. This ensures that the lexicographical order of binary ULIDs matches their chronological order, in accordance with the ULID specification.

ULIDs have several useful properties:

  • They are lexicographically sortable by creation time.
  • They fit entirely into 26 ASCII characters.
  • They avoid visually ambiguous symbols (I, L, O, U).
  • They have 128 bits of total uniqueness-same as UUID v4.

Tarantool uses a monotonic ULID generator. This ensures that multiple ULIDs created within the same millisecond are strictly increasing and preserve sort order: for any two ULIDs generated in the same millisecond, the one created later is greater than the earlier one.

Internally, the monotonic generator keeps the last generated ULID for the current millisecond and increments the 80-bit random part for each subsequent ULID. A real overflow of the random part can happen only after generating 2^80 ULIDs within the same millisecond, which is practically impossible on real hardware. However, for strict correctness of the ULID specification and to avoid silent wrap-around, the implementation detects this overflow and fails the next generation attempt with a Lua error (ULID random component overflow).

To use this module, run the following command:

ulid = require('ulid')

ULID objects support the full set of Lua comparison operators:

  • == and ~= - equality and inequality.
  • < and <= - lexicographical comparison.
  • > and >= - lexicographical comparison.

The comparison is based on the internal 16-byte representation in big-endian order and is consistent with the ULID specification: for ULIDs created by the monotonic generator, later ULIDs are greater than earlier ones, including ULIDs generated within the same millisecond.

Comparison works both between ULID objects and between a ULID object and a ULID string:

  • u1 == u2 compares two ULID objects directly.
  • u1 == "01..." converts the string to ULID and compares values.
  • u1 < "01..." or "01..." < u1 convert the string argument to ULID and perform lexicographical comparison.

Examples:

tarantool> u1 = ulid.new()
tarantool> u2 = ulid.new()
tarantool> u1 < u2, u1 <= u2, u1 == u2, u1 ~= u2, u1 > u2, u1 >= u2
---
- true
- true
- false
- true
- false
- false
...

tarantool> u = ulid.new()
tarantool> s = u:str()
tarantool> u == s, u < s, u > s
---
- true
- false
- false
...

tarantool> u == "not-a-valid-ulid"
---
- false
...

tarantool> u < "not-a-valid-ulid"
---
- error: '[string "return u < "not-a-valid-ulid""]:1: incorrect value to convert to
    ulid as 2 argument'
...

Below is list of all ulid functions and members.

Name Use
ulid.NULL A nil ULID object
ulid.ulid()
ulid.bin()
ulid.str()
Shortcuts to create a new ULID value
ulid.new() Create a new ULID object
ulid.fromstr()
ulid.frombin()
ulid_object:bin()
ulid_object:str()
Convert between string, binary, and ULID object forms
ulid.is_ulid()
ulid_object:isnil()
Check ULID type or nil value
ulid.NULL

A nil ULID object - a ULID that contains 16 zero bytes.

Return:the nil ULID value
Rtype:cdata

Example:

tarantool> ulid.NULL
---
- 00000000000000000000000000
...
ulid.new()

Create a new ULID object.

This function uses the monotonic generator described in the Overview. Multiple ULIDs created within the same millisecond are strictly increasing.

Return:a new ULID object
Rtype:cdata

Example:

tarantool> ulid.new()
---
- 06DGE3YNDCM2PPWJT3SKTTRNZR
...
ulid.ulid()

Calling the module directly is the same as calling ulid.new().

In other words, ulid() is a shortcut for ulid.new().

Return:a new ULID object (same as ulid.new())
Rtype:cdata

Example:

tarantool> ulid()
---
- 06DGE41G63GAZ6F0TV4WRSVCCW
...
ulid.str()

Create a new ULID and return its string representation.

This is a shortcut for ulid.new():str().

The result is always 26 characters, encoded using Crockford Base32.

Return:a ULID string
Rtype:26-byte string

Example:

tarantool> ulid.str()
---
- 06DGE480BWZ6H5BKX0KS3Q8S2G
...
ulid.bin()

Create a new ULID and return its binary representation as a 16-byte string.

This is a shortcut for ulid.new():bin().

Return:a ULID in binary form
Rtype:16-byte string

Example:

tarantool> #ulid.bin()
---
- 16
...
ulid.fromstr(ulid_string)

Create a ULID object from a 26-character string.

The input must be a valid ULID string encoded using Crockford Base32. If the string is invalid (wrong length or invalid symbols), nil is returned.

Parameters:
  • ulid_string (string) – ULID in 26-character string form
Return:

converted ULID or nil

Rtype:

cdata or nil

Example:

tarantool> u = ulid.fromstr('06DGE4FH80PHA28YZVV5Z473T4')
tarantool> u
---
- 06DGE4FH80PHA28YZVV5Z473T4
...
ulid.frombin(ulid_bin)

Create a ULID object from a 16-byte binary string.

Parameters:
  • ulid_bin (string) – ULID in 16-byte binary string form
Return:

converted ULID

Rtype:

cdata

Example:

tarantool> u1 = ulid.new()
tarantool> b = u1:bin()
tarantool> u2 = ulid.frombin(b)
tarantool> u1 == u2
---
- true
...
ulid.is_ulid(value)

Check if the given value is a ULID cdata object.

Parameters:
  • value – a value of any type
Return:

true if the value is a ULID, otherwise false

Rtype:

boolean

Example:

tarantool> ulid.is_ulid(ulid.new())
---
- true
...

tarantool> ulid.is_ulid("string")
---
- false
...

A ULID object returned by ulid.new(), ulid.fromstr(), or ulid.frombin() provides the following methods:

ulid_object:bin()

Return the ULID as a 16-byte binary string.

Return:ULID in binary form
Rtype:16-byte string

Example:

tarantool> u = ulid.new()
tarantool> b = u:bin()
tarantool> #b, b
---
- 16
- "\x01\x9B\a\xAD==\x81u۶-\x93hPa\xAE"
...
ulid_object:str()

Return the ULID as a 26-character string.

Return:ULID in string form
Rtype:26-byte string

ULID objects also implement the standard Lua __tostring metamethod. This means that calling tostring(u) for a ULID object u returns the same value as u:str(), and ULID objects are automatically converted to their 26-character string representation when needed in string context.

Example:

tarantool> u = ulid.new()
tarantool> u:str(), tostring(u)
---
- 06DGFBE3J07B7DB5A3JP4WQ9CM
- 06DGFBE3J07B7DB5A3JP4WQ9CM
...
ulid_object:isnil()

Check if the ULID is the nil ULID (all 16 bytes are zero).

Return:true for ulid.NULL, otherwise false
Rtype:boolean

Example:

tarantool> ulid.NULL:isnil()
---
- true
...

tarantool> ulid.new():isnil()
---
- false
...

Basic usage:

tarantool> u_obj = ulid.new()
tarantool> u_str = ulid.str()
tarantool> u_bin = ulid.bin()

tarantool> u_obj, u_str, u_bin
---
- 06DGE6SEZDCFFSFEAJFMC9YQAR
- 06DGE6T0DW0N2N6KMPVZ8SGE4W
- "\x01\x9B\a\eSz8\xBA\xB3\xC5\xCC\xFE\x18\xBB1\xD6"
...

Creating a ULID object and inspecting it:

tarantool> u = ulid()
---
...

tarantool> #u:bin(), #u:str(), type(u), u:isnil()
---
- 16
- 26
- cdata
- false
...

tarantool> tostring(u) == u:str()
---
- true
...

Converting between string and binary formats:

tarantool> s = ulid.str()
tarantool> s
---
- 06DGE70CPFDV344XX43687N1SM
...

tarantool> u = ulid.fromstr(s)
tarantool> u:str() == s
---
- true
...

tarantool> b = u:bin()
tarantool> u2 = ulid.frombin(b)
tarantool> u2 == u
---
- true
...

Working with ulid.NULL:

tarantool> ulid.NULL
---
- 00000000000000000000000000
...

tarantool> ulid.NULL:isnil()
---
- true
...

tarantool> ulid.new():isnil()
---
- false
...

Comparison operators:

tarantool> u1 = ulid.new()
tarantool> u2 = ulid.new()
tarantool> u1 < u2
---
- true
...

Checking types:

tarantool> u = ulid.new()
tarantool> ulid.is_ulid(u)
---
- true
...

tarantool> ulid.is_ulid("06DGE7RJK8QWJE27X5VVCC5VDW")
---
- false
...

Generating many ULIDs:

tarantool> for i = 1,5 do print(ulid.str()) end
---
06DGE7T8AJD6EDNJ3VQ2ZYB3R4
06DGE7T8AJD6EDNJ3VQ2ZYB3R8
06DGE7T8AJD6EDNJ3VQ2ZYB3RC
06DGE7T8AJD6EDNJ3VQ2ZYB3RG
06DGE7T8AJD6EDNJ3VQ2ZYB3RM
...
Found what you were looking for?
Feedback