
ByteAether.Ulid v1.3.0: Enhanced ULID Generation Control and Security
ByteAether.Ulid v1.3.0, a significant update to our high-performance .NET ULID implementation, has been released. This version introduces enhanced flexibility and security for ULID generation, reinforcing its suitability for modern applications.
ByteAether aims to provide a compliant and dependable ULID solution for .NET developers. ULIDs combine universal uniqueness with lexicographical sortability, making them suitable for distributed systems and time-ordered data by optimizing indexing and querying.
Enhanced Generation Control with GenerationOptions
Previous versions of ByteAether.Ulid utilized a bool? isMonotonic
and Ulid.DefaultIsMonotonic
flags for monotonic generation control. Version 1.3.0 replaces this with a new configuration object, GenerationOptions
, offering granular control over ULID generation. GenerationOptions
includes three parameters: Monotonicity
, InitialRandomSource
, and IncrementRandomSource
.
GenerationOptions
Explained
Let's explore the new GenerationOptions
in detail:
Monotonicity
This option controls the monotonic behavior of ULIDs, offering choices to balance predictability and uniqueness:
NonMonotonic
Generates a fully randomRandom
component, suitable when strict sequential ordering is not required.MonotonicIncrement
(Default)
Maintains strict monotonic progression by incrementing the random portion of the ULID by 1, mirroring previous monotonic generation behavior.MonotonicRandom1Byte
MonotonicRandom2Byte
MonotonicRandom3Byte
MonotonicRandom4Byte
These options enhance security and unpredictability. As discussed in ULID specification issue #105, incrementing the random part by1
can lead to predictable ULIDs within the same millisecond, potentially enabling enumeration attacks. To mitigate this, these options allow configuring a random increment from 1-byte (1–256) to 4-byte (1–4,294,967,296) range using theIncrementRandomSource
. This adds a layer of randomness, making ULIDs more difficult to guess while preserving lexicographical sortability, directly addressing enumeration mitigation concerns in the ULID specification.
InitialRandomSource
and IncrementRandomSource
Both InitialRandomSource
and IncrementRandomSource
properties are of type IRandomProvider
. This design provides a flexible way to define how randomness is produced, allowing users to implement and provide their own custom random number generators.
The InitialRandomSource
option specifies the source of randomness for the initial random component of the ULID. The IncrementRandomSource
setting determines the source of randomness for the increment value when using MonotonicRandomXByte
options for monotonicity.
CryptographicallySecureRandomProvider
(Default forInitialRandomSource
)
Utilizes a cryptographically secure random number generator, ensuring high entropy and suitability for security-sensitive applications.PseudoRandomProvider
(Default forIncrementRandomSource
)
Employs a faster pseudo-random algorithm, suitable for scenarios where cryptographic security isn't a primary concern, and performance is prioritized.
Default Behavior and Backward Compatibility
Version 1.3.0's default settings ensure a seamless transition, matching previous ByteAether.Ulid monotonic generation behavior:
Ulid.DefaultGenerationOptions = new Ulid.GenerationOptions
{
Monotonicity = MonotonicityOptions.MonotonicIncrement,
InitialRandomSource = new CryptographicallySecureRandomProvider(),
IncrementRandomSource = new PseudoRandomProvider()
}
Upgrading to v1.3.0 will not alter existing ULID generation logic unless GenerationOptions
are explicitly configured and assigned to Ulid.DefaultGenerationOptions
.
For backward compatibility, public API methods with a bool? isMonotonic
parameter and the Ulid.DefaultIsMonotonic
static property remain, but are marked [Obsolete]
. These are deprecated to encourage migration to the GenerationOptions
-based methods for enhanced control and flexibility. Obsolete methods and properties will be removed in a future version and users are encouraged to update their code for future compatibility and to leverage new capabilities.
Why ULIDs?
Identifier selection is crucial in distributed systems. GUIDs provide uniqueness but lack sortability, hindering database indexing and querying. Integer IDs are sortable but may conflict in distributed environments due to their limited scope.
ULIDs combine universal uniqueness with lexicographical sortability:
- Universally Unique: Ensures global distinctness across systems.
- Lexicographically Sortable: Enables efficient time-based sorting for database performance and chronological data retrieval.
- Human-Readable: More manageable than GUIDs.
ByteAether.Ulid is a robust and compliant .NET implementation. It addresses potential issues from the official ULID specification, such as the "random part overflow" issue, by allowing timestamp increments to ensure unique ULIDs even at high generation rates.
Benchmarking against other .NET ULID implementations (e.g., NetUlid
, Ulid
, NUlid
), Guid
, and GuidV7
consistently shows ByteAether.Ulid as highly performant and fully compliant with the ULID specification, ensuring dependable generation.
Installation and Usage
Install ByteAether.Ulid v1.3.0 via NuGet:
dotnet add package ByteAether.Ulid
Example of GenerationOptions
usage:
using ByteAether.Ulid;
using static ByteAether.Ulid.Ulid.GenerationOptions;
var optionsWithRandomIncrement = new Ulid.GenerationOptions
{
// 4-byte random increment
Monotonicity = MonotonicityOptions.MonotonicRandom4Byte,
// Cryptographically secure initial random
InitialRandomSource = new CryptographicallySecureRandomProvider(),
// Cryptographically secure increment random
IncrementRandomSource = new CryptographicallySecureRandomProvider()
};
var ulidWithRandomInc = Ulid.New(optionsWithRandomIncrement);
Console.WriteLine($"ULID with random increment: {ulidWithRandomInc}");
// Set default generation options globally
Ulid.DefaultGenerationOptions = new()
{
Monotonicity = MonotonicityOptions.MonotonicIncrement,
// Pseudo-randomness for speed
InitialRandomSource = new PseudoRandomProvider(),
// IncrementRandomSource is not used by MonotonicIncrement
};
var defaultUlid = Ulid.New();
Console.WriteLine($"ULID from default pseudo-random source: {defaultUlid}");
Ecosystem Integration
ByteAether.Ulid integrates with popular .NET libraries and frameworks:
- ASP.NET Core: Integrates as a route or query parameter with a built-in
TypeConverter
. - System.Text.Json (.NET 5.0+): Includes a
JsonConverter
for serialization and deserialization. - EF Core: Supports ULIDs as primary keys or properties via a custom
ValueConverter
(Ulid
tobyte[]
). - Dapper: Supports custom
TypeHandler
forUlid
tobyte[]
conversion. - MessagePack: Provides a custom
MessagePackResolver
for Ulid (asbyte[]
) serialization and deserialization. - Newtonsoft.Json: A custom
JsonConverter
enables Ulid (asstring
) serialization and deserialization.
Contribute to ByteAether.Ulid
We are committed to the continuous improvement of ByteAether.Ulid. Contributions, including pull requests, bug reports, and feature suggestions, are welcome. Your input is valuable for shaping the project's future.
Upgrade to ByteAether.Ulid v1.3.0 for enhanced control, security, and performance in ULID generation.