Excerpt
Using UUIDs for primary keys offers many benefits, but there are some downsides to consider. The most widely-used UUIDv4 is fully random, which is ideal for minimizing the risk of collision. However, random IDs as primary keys do not index and sort efficiently, leading to index bloat and performance issues.
There is a new standard that addresses this problem by including a UNIX timestamp in the initial bits: UUIDv7.
10.times { puts UUID7.generate }
# 018c2b95-b764-7615-a924-cc5b910ed1e5
# 018c2b95-b764-713f-aff1-e2668182bc68
# 018c2b95-b764-7b43-bec8-3c204a04433a
# 018c2b95-b764-7521-b1de-0638499f2f75
# 018c2b95-b764-7380-895d-4416494f9a05
# 018c2b95-b764-7cb1-a91f-24e598a4bc36
# 018c2b95-b764-7218-a1d1-fa20a475d6b0
# 018c2b95-b764-7eab-9e08-d0b321a69240
# 018c2b95-b764-7d25-a033-845c81610339
# 018c2b95-b764-7143-af66-0a849883f9d0
In the example above, notice the 7 following the second hyphen: this indicates the UUID version.
How to set up UUIDv7 in Rails
As of this writing, se
Using UUIDs for primary keys offers many benefits, but there are some downsides to consider. The most widely-used UUIDv4 is fully random, which is ideal for minimizing the risk of collision. However, random IDs as primary keys do not index and sort efficiently, leading to index bloat and performance issues.
There is a new standard that addresses this problem by including a UNIX timestamp in the initial bits: UUIDv7.
10.times { puts UUID7.generate }
# 018c2b95-b764-7615-a924-cc5b910ed1e5
# 018c2b95-b764-713f-aff1-e2668182bc68
# 018c2b95-b764-7b43-bec8-3c204a04433a
# 018c2b95-b764-7521-b1de-0638499f2f75
# 018c2b95-b764-7380-895d-4416494f9a05
# 018c2b95-b764-7cb1-a91f-24e598a4bc36
# 018c2b95-b764-7218-a1d1-fa20a475d6b0
# 018c2b95-b764-7eab-9e08-d0b321a69240
# 018c2b95-b764-7d25-a033-845c81610339
# 018c2b95-b764-7143-af66-0a849883f9d0
In the example above, notice the 7 following the second hyphen: this indicates the UUID version.
How to set up UUIDv7 in Rails
As of this writing, selecting id: :uuid in Rails defaults to using UUIDv4 for primary keys, with the generation deferred to Postgres.
While Postgres does not currently offer a built-in function for generating UUIDv7, we can still use the uuid type to store values. One advantage of UUIDs over sequential IDs is that they can be generated anywhere — in this example, we’ll use our Rails backend.
First, let’s set up our Rails app to use UUID as the primary key. We’re going to enable pgcrypto. This isn’t strictly necessary, but Rails will set up gen_random_uuid() as the default when you set id: :uuid.
rails g migration EnablePgcrypto
Here’s the migration:
class EnablePgcrypto < ActiveRecord::Migration[7.1]
def change
enable_extension 'pgcrypto'
end
end
Then, add the following to config/application.rb:
config.generators do |generate|
generate.orm :active_record, primary_key_type: :uuid
end
From now on, Rails will use the UUID type for primary keys for new models created with rails generate model.
At this point, we’re all set up, but we’re still using UUIDv4. Let’s change that:
To use UUIDv7 for our primary keys, we have to create them before they are saved to the database. This can be achieved with a before_create callback in the ApplicationRecord:
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
before_create :generate_uuid_v7
private
def generate_uuid_v7
return if self.class.attribute_types['id'].type != :uuid
self.id ||= UUID7.generate
end
end
That’s it! From now on, all new records will have a UUIDv7 primary key, without affecting the existing tables that may use sequential (integer) IDs.
UUIDv7 in Ruby 3.3
Ruby 3.3+ will no longer require a third-party gem for this:
require 'random/formatter'
Random.uuid_v7
# or
require 'securerandom'
SecureRandom.uuid_v7
Further reading
Also, check out BasedUUID.