Hacker News new | past | comments | ask | show | jobs | submit login

I also spent some time evaluating them, and came to the conclusion that KSUIDs (https://github.com/segmentio/ksuid) were strictly better than ULIDs.

The differences aren't huge in practice, but the ULID spec has a couple of highly questionable choices. Specifically, according to the spec:

1. The first time you generate a ULID in a given millisecond on a node, you should fill in the random component randomly.

2. But if you generate more than one ULID per milliscond, subsequent ULIDs should use the first ULIDs random component and just increment the least significant bit.

3. If that would overflow, then you just can't generate any more ULIDs that millisecond.

The goal is that ULIDs will be strictly sortable even when you're generating multiple per millisecond...except for that to work, you'd have to only generate them in a single thread (or at any rate on a single node). If you're just incrementing a numbers on a single node, then uh, why not just use an autoincrementing integer column in a DB? Of course, if you're generating multiple IDs per millisecond, you're probably using multiple nodes, and if you are, then wall clock skew means the IDs will never be strictly sortable. Except actually it's even worse than that; even if you ARE only using a single node, your code probably can't assume that every ULID it will ever encounter was generated in that way, so you still can't rely on the sorting guarantee. Meanwhile, a compliant ULID generator has to have state and, if it's going to be called in a multithreaded way, to deal with some complex race conditions, which makes it a lot harder to run.

Meanwhile, you could get 100% of the benefits and none of the drawbacks by just...using random bits for the random component every time.

Anyhow, the idea of slapping a timestamp and a random string of bits together is a good one. I just think the KSUID spec is a bit better thought out.




The problem I had with KSUID is that they have a custom format, so not optimised in Postgres, while ULIDs are fully interoperable with other kinds of UUIDs. So if your requirements change later, it's just a change to the function generating your UIDs.

Also I don't use monotonic generators, because in pratice, in my applications, it never make business sense to sort entities with a greater granularity than a millisecond, so even if I generate 10 entities' IDs in a millisecond, I will let the random part randomly sort the entities generated during this millisecond.

Monotonic generators, are in my opinion only useful when you require absolute ordering, like with events, and thus rarely are a concern.


> The problem I had with KSUID is that they have a custom format, so not optimised in Postgres

As far as I'm aware, Postgres doesn't really have any "optimisations" for UUIDs. The type just provides some help for normalising and converting them to and from their canonical form.

The only DB I know of with actual UUID optimisations is MS SQL, and that would be a bit of a trap, because its optimisation is to re-order the bits so the timestamp part of a classic UUID is at the front when it's stored...which means trying to store a ULID as a UUID in MS SQL would actually be slower than storing it as plain byes.


The ordering of bytes within UUID/GUID types is one of the differences between the two, and also causes no end to grief when importing data across systems (or from hex representation to binary).

I wrote about some of the pitfalls and how to properly convert MS guid types to hex for interop with, eg, SQLite here: https://neosmart.net/blog/2018/converting-a-binary-blob-guid...

The difference in big vs little endian can cause huge problems in practice aside from just not having identical values - as you note, the order of the random part and the timestamp part is switched around, which will actually cause guids to lose their randomness and cluster around not just the time but the inverted time - the last part of which may often even be null.


The monotonicity component of ULID is - confusingly - an optional part of the spec (which makes the spec weird). There is discussion to drop this component from the official spec altogether.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: