You might be thinking what is ULID? or Why not use UUID? This is why I don't use UUID for identity.
ULID is essentially a sortable version of UUID4 with timestamp. It having a sortable property works out really well for indexes that rely on sortability like a B-Tree Index in MySQL or less so a Skiplist index in RocksDB. I use RocksDB a lot these days, still use MySQL. I'm using both of these databases for Project Starflash, in fact. Anyway, ULID is an absolute improvement over UUID4 for database indexes, but it uses 2x (100% increase) space of a 64 bit integer.
I figured there's no users, so why the hell am I planning for the future using 128bits? Why not plan for now and use less space. Entering in ULID64, cutting down the usage for storage and indices while being able to be put into roaring bitmaps, bloom filters and very much column compressible!
It was only a matter of time for me right? 128bit integers are not widely supported by clients, serializers such as JSON, compressible data structures today. Most of them take in 32, 64 bit integers. Usually need to turn the number into a string to support moving things around.
One big thing is that it's really hard to exhaust the space of 64 bit integers. It's even harder when you have a custom start epoch! The UNIX Timestamp is set to exhaust the 32bit space by 2038, so I figured why not pick a timestamp within that space and use it as the start epoch. Now any time you generate is relative to that start epoch and if you wanted to go back to unix timestamp, just add those two. Keep in mind, that these timestamps are 64 bit and are in seconds, so they are not running out any time soon.
ULID is predicated on timestamp + random bits. ULID64 has the same layout. I predicated it on reserving a majority of bits for timestamp and a minority for random bits. I personally like having aligned bit in my layouts, so like 4, 8, 16, 32, etc. You'd want greater than or equal to 32 bits of timestamp to prevent issues, so anything from 8-32 of random bits will do depending on how many ids you need available per second. If you chose 24 random bits, then (64 - 24 = 40) bits are available for the timestamp. This is adjustable and I suggest a ULID64 variant 1, v2, v3 would be sensible in naming if you wanted different timestamp + random bits layouts.
You can see here that it took a little bit of work, but we compressed both time and the ID into a 64 bit ID. Time compression works out generally for storage because you don't need 64 bits to store in a database anymore, you can use 32 bit timestamps now, so you have around 136 years from that custom start epoch. Your web service might not even last that long and a migration to 64bit timestamp wouldn't be a difficult endeavor if your application does last that long. Yeah I use 32 bits now to store time metadata all over my project now. I digress.
You'll have to check for collisions with ULID64, but it's not a big deal since new ids are available every second. It's easy enough these days to write a stored procedure to do it. You don't have the scale to not afford checking for collisions if you do not desire to use 128 bits anyway. One should check for collisions regardless of id generation source. A low probability of collision does not mean it will never happen especially if the generator uses some crap random source.
ULID64 is a space saving version of ULID! 50% savings and 136 years of non colliding timestamp? I'll take that.