martinthenth

martinthenth

UUIDv7 - A UUID v7 implementation and Ecto.Type for Elixir - based on Rust

Hello everybody :wave:

Recently, some of my colleagues talked about database ids and uuids and their problems, and I remembered the pain of working with randomly distributed primary keys. They’re nice at first but then you have to index and order by a different field like created_at, while serial and bigserial already have an order and are indexed as the primary key.

UUIDv6, UUIDv7 and UUIDv8 are new standards to deal with issues found in UUIDv4 and earlier. I especially liked this post that analyzes the new standards: https://blog.devgenius.io/analyzing-new-unique-identifier-formats-uuidv6-uuidv7-and-uuidv8-d6cc5cd7391a

My favourite is UUIDv7 because they’re like UUIDv4 but the first characters are based on a millisecond timestamp; it seems like a very small change.

Elixir doesn’t have a common implementation of UUIDv7 yet, and Ecto is based on UUIDv4. So I decided to build one based on a Rust package crates.io: Rust Package Registry, which is relatively mature.

The new library is called UUIDv7 and is available on Hex UUIDv7 - Hex.

Because it’s based on Rust, the UUID generation is a whopping 72% faster than the default Ecto.UUID version 4 generator. NIFs are precompiled and generated for most platforms.

It’s easy to set up, you only have to change one line:

def App.Schemas.User do
  use Ecto.Schema
  @primary_key {:id, UUIDv7, autogenerate: true}
end

You can verify the UUIDs are ordered by running a small test:

uuid1 = UUIDv7.generate()
uuid2 = UUIDv7.generate()
uuid3 = UUIDv7.generate()
uuid4 = UUIDv7.generate()

assert uuid1 < uuid2
assert uuid2 < uuid3
assert uuid3 < uuid4

Though you may have to add Process.sleep(1) between the generations to skip one millisecond (UUIDv7 is based on milliseconds and ends with random bits)

Since the performance difference between Rust-based UUID generation and Ecto.UUID is so large, maybe it could be a motivation to write other more commonly used functions as NIFs from more compute-efficient languages?

You can check the benchmark here: UUIDv7 - Benchmark

GitHub: UUIDv7 - GitHub
Hex: UUIDv7 - Hex

Most Liked

martinthenth

martinthenth

Yup, UUIDv7 and Uniq are compatible because of the shared standard, but uniq is a bit slower because it’s 100% Elixir. It’s comparable with Ecto.UUID.

iex(1)> UUIDv7.generate()
"0188f846-191e-7f32-81f1-871f64b71d6b"
iex(2)> Uniq.UUID.uuid7()
"0188f846-1ae4-7205-8cf4-ac0be8a620a1"

Benchmark:

Name                     ips        average  deviation         median         99th %
uuidv7                1.75 M      570.22 ns  ±3940.19%         500 ns         667 ns
uniq (uuid v7)        1.07 M      937.20 ns  ±1852.78%         916 ns        1000 ns
ecto (uuid v4)        1.02 M      978.17 ns  ±1593.54%         958 ns        1042 ns

Comparison:
uuidv7                1.75 M
uniq (uuid v7)        1.07 M - 1.64x slower +366.98 ns
ecto (uuid v4)        1.02 M - 1.72x slower +407.95 ns
c4710n

c4710n

A quote from https://uuid7.com/

  • Concurrency and Distribution: In distributed systems, generating unique, sequential IDs can be a challenge. UUIDv7 can be generated concurrently across multiple nodes without the risk of collisions, making it suitable for distributed architectures.
sbuttgereit

sbuttgereit

New proposed standards. While it looks like UUID v6+ have made a lot of progress, they haven’t been adopted as standards yet: RFC 9562 - Universally Unique IDentifiers (UUIDs)

This isn’t to say that what ends up being adopted will be much different than has appeared in the various drafts, but for some the formality can matter.

As an aside, it looks like the scope of the proposed standard has expanded as they’ve progressed through the process to actually revising the UUID v1 through v5 standards as well: Revise Universally Unique Identifier Definitions (uuidrev)… the revisions to previous standards are very limited to doing things like correcting errata (unsurprisingly).

Where Next?

Popular in Announcing Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
josevalim
EDIT: since Ecto 3.0 final version is out, this post was amended to use the final versions in the instructions below. Hi everyone, We a...
New
zorbash
I created Kitto a framework for dashboards inspired by Dashing. The distributed characteristics of Elixir and the low memory footprint...
New
Crowdhailer
Raxx is an alternative to Plug and is inspired by projects such as Rack(Ruby) and Ring(Clojure). 1.0-rc.1 is now available. To use it re...
New
sbs
Only 650 LOC, wrote for fun :slight_smile: https://github.com/sunboshan/qrcode
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
treble37
Just looking for a little feedback on a tiny helper library I built - Sometimes I find the need to convert maps with atom keys to maps w...
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
144 10187 141
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement