Here are a few options to consider:
- Atomic counter (single process) A simple, thread-safe counter starting from the current time in milliseconds.
private static long _messageIdCounter = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
public static long GetNextMessageId()
{
return Interlocked.Increment(ref _messageIdCounter);
}
- Produces 13-digit numbers.
- Unique within the current process runtime.
- Not safe across multiple processes or restarts.
- Timestamp with random suffix (safe for distributed systems) Combines current time with a random number to reduce the chance of collisions.
public static long GenerateMessageId()
{
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // 13 digits
int random = RandomNumberGenerator.GetInt32(0, 100); // 2-digit random number
return long.Parse($"{timestamp}{random:D2}");
}
- Results in 15-digit numbers.
- Very low probability of collision, even across systems.
- GUID to numeric conversion
Use a GUID and convert it to a
long
, then use modulo to limit the number to a maximum of 16 digits.
public static long GenerateNumericFromGuid()
{
Guid guid = Guid.NewGuid();
byte[] bytes = guid.ToByteArray();
long raw = BitConverter.ToInt64(bytes, 0);
// Ensure it's positive and within 16 digits
return Math.Abs(raw % 1_000_000_000_000_0000); // max 16 digits
}
- While GUIDs have very high uniqueness, using modulo slightly reduces that guarantee.
- Still safe for most practical applications.
If the above response helps answer your question, remember to "Accept Answer" so that others in the community facing similar issues can easily find the solution. Your contribution is highly appreciated.
hth
Marcin