Random and time-ordered UUID generation in Swift

Related tags

Utility uniqueid
Overview

UniqueID

UUIDv4 and v6* generation in Swift.

[API Reference]

A UUID is an identifier that is unique across both space and time, with respect to the space of all UUIDs. They are used for multiple purposes, from tagging objects with an extremely short lifetime, to reliably identifying very persistent objects across a network.

UniqueID supports any 128-bit UUID, and is fully compatible with Foundation's UUID. It also includes features to generate 2 kinds of ID:

  • random: As defined in RFC-4122 (UUIDv4). A 128-bit identifier, consisting of 122 random or pseudo-random bits. These are the most common form of UUIDs; for example, they are the ones Foundation's UUID type creates by default. The idea is that because this is such a large number, the chance of a system observing a collision is so low that it can be safely ignored. That said, they rely heavily on the amount of entropy in the random bits, and when a system is ingesting IDs created by distributed nodes or devices,the chances of collision may be higher.

  • time-ordered: Generated according to a draft update of RFC-4122 (UUIDv6). A 128-bit identifier, consisting of a fixed-precision timestamp, per-process sequencing number, and 47-bit node ID (which may be random or pseudo-random bits). Whilst RFC-4122 did include time-based UUIDs (UUIDv1), it ordered the bits such that they had poor locality and couldn't be sorted easily. UUIDv6 rearranges these bits,which dramatically improves their usability as database keys. The node ID can be configured to provide even better resilience against collisions.

Using UniqueID in your project

To use this package in a SwiftPM project, you need to set it up as a package dependency:

// swift-tools-version:5.5
import PackageDescription

let package = Package(
  name: "MyPackage",
  dependencies: [
    .package(
      url: "https://github.com/karwa/uniqueid",
      .upToNextMajor(from: "1.0.0")
    )
  ],
  targets: [
    .target(
      name: "MyTarget",
      dependencies: [
        .product(name: "UniqueID", package: "uniqueid")
      ]
    )
  ]
)

And with that, you're ready to start using UniqueID. One way to get easily experiment with time-ordered (v6) UUIDs is to use Foundation compatibility to simply change how you create UUIDs:

import Foundation
import UniqueID

// Change from UUID() to UUID(.timeOrdered()).
struct MyRecord {
  var id: UUID = UUID(.timeOrdered())
  var name: String
}

// Read the timestamp by converting to UniqueID.
let uniqueID = UniqueID(myRecord.id)
print(uniqueID.components(.timeOrdered)?.timestamp)

Bear in mind that v6 UUIDs are not yet an official standard, and the layout may change before it becomes an approved internet standard. This implementation aligns with draft 02, from 7 October 2021. Check the latest status here.

Why new UUIDs?

The IETF draft has a really good summary of why using time-ordered UUIDs can be beneficial. You should read it - at least the "Background" section.

A lot of things have changed in the time since UUIDs were originally created. Modern applications have a need to use (and many have already implemented) UUIDs as database primary keys.

The motivation for using UUIDs as database keys stems primarily from the fact that applications are increasingly distributed in nature. Simplistic "auto increment" schemes with integers in sequence do not work well in a distributed system since the effort required to synchronize such numbers across a network can easily become a burden. The fact that UUIDs can be used to create unique and reasonably short values in distributed systems without requiring synchronization makes them a good candidate for use as a database key in such environments.

However some properties of RFC4122 UUIDs are not well suited to this task. First, most of the existing UUID versions such as UUIDv4 have poor database index locality. Meaning new values created in succession are not close to each other in the index and thus require inserts to be performed at random locations. The negative performance effects of which on common structures used for this (B-tree and its variants) can be dramatic. As such newly inserted values SHOULD be time-ordered to address this.

Previous time-ordered UUIDs, such as version 1 UUIDs from RFC-4122, store their timestamps in a convoluted format, so you can't just sort UUIDs based on their bytes and arrive at a time-sorted list of UUIDs. Version 6 improves on that.

Let's compare 10 UUIDv4s against 10 UUIDv6s:

for _ in 0..<10 {
  print(UniqueID.random())
}

DFFC75B4-C92F-4DA9-97CA-7F0EEF067FF2
67E5F28C-5083-4908-BD69-D7E27C8BABA4
3BA8EEF0-DFBE-4AE0-A646-E165FCA9054C
DF92B4B0-F5EE-42E5-9577-A9FC373C71A4
A2F8DD26-D513-4AE6-9E5C-58363885CCB6
BB0B5841-2BC0-49E2-BC5C-362CC34D7225
B08AF1F7-E2D3-4175-913D-369140612FF5
A453FB62-DF71-436F-9AC1-0414793DFA16
485EEB84-A4BA-44FE-BE3B-AD90390B0523
8A9AE1FA-4104-442C-B459-8F682E77F2F4
for _ in 0..<10 {
  print(UniqueID.timeOrdered())
}

1EC3C81E-A35C-69E2-BB38-EDDC5E7E5F5E
1EC3C81E-A361-658C-BB38-65AAEF71CFCF
1EC3C81E-A361-6F6E-BB38-6DE69B9BCA1B
1EC3C81E-A362-698C-BB38-050642A95C73
1EC3C81E-A363-6152-BB38-F105ED78927F
1EC3C81E-A363-6A94-BB38-4DAB2CAE46CD
1EC3C81E-A364-63D6-BB38-6114031916EF
1EC3C81E-A364-6D04-BB38-435A854C2E42
1EC3C81E-A365-66AA-BB38-03504FA2F6FE
1EC3C81E-A365-6F74-BB38-1F5AE9E10389

Both lists are unique, and unique with respect to each other, but the time-ordered ones, naturally, came out in order of creation time. We can even extract the embedded timestamp - in this case, it says the UUID was created on the 3rd of November, 2021 at 08:42:01 UTC (down to 100ns precision, theoretically).

The combination of temporal and spacial components means these UUIDs are still robust to collisions - a new 60-bit universe exists every 100ns, and the IDs within that universe are still alloted based on random bits with high entropy. It's tempting to think you might be paying a high cost in collisions for the ease of use, but it's not as simple as that.

You might also like...
Swift-DocC is a documentation compiler for Swift frameworks and packages aimed at making it easy to write and publish great developer documentation.

Swift-DocC is a documentation compiler for Swift frameworks and packages aimed at making it easy to write and publish great developer docum

BCSwiftTor - Opinionated pure Swift controller for Tor, including full support for Swift 5.5 and Swift Concurrency

BCSwiftTor Opinionated pure Swift controller for Tor, including full support for

 Zip - A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip.
Zip - A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip.

Zip A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip. Usage Import Zip at the top of the Swift file

Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects

SwiftyPick 🦅 🍒 Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects. Purpose The idea behind t

A handy collection of Swift method and Tools to build project faster and more efficient.

SwifterKnife is a collection of Swift extension method and some tools that often use in develop project, with them you might build project faster and

TypeStyle is a handy app for iPhone and iPad that generates text using different styles and decorations. It is a native Swift iOS app.
TypeStyle is a handy app for iPhone and iPad that generates text using different styles and decorations. It is a native Swift iOS app.

TypeStyle TypeStyle is a handy app for iPhone and iPad that generates text using different styles and decorations. It is a native Swift iOS app. Featu

Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures
Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures

Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures. Have the best of both worlds

A lightweight extension to Swift's CollectionDifference, supporting moves in addition to removals and insertions, critical when updating interfaces and managing reference types.

DifferenceTracker is a lightweight extension to Swift's CollectionDifference. It defines moves in addition to removals and insertions, critical when updating interfaces and managing reference types.

Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

Swift Markdown is a Swift package for parsing, building, editing, and analyzing Markdown documents.

Comments
  • Use pthread_mutex/os_unfair_lock

    Use pthread_mutex/os_unfair_lock

    The current implementation uses atomics to create a spinlock in userspace, which is not a good idea.

    It should be switched to an actual sleeping lock.

    opened by karwa 1
  • Perf tweaks

    Perf tweaks

    1. Tweak timestamp generation to avoid implicit traps if the system returns a negative timestamp. This results in better inlining.
    2. Tweak random UUID generation so the withUnsafeMutableBytes closure does not get outlined (I hate that the compiler does this! Gah! Why?!)
    3. Disable priority inheritance on Linux.

    These are mostly minor, but priority inheritance can have more significant overheads so is worth disabling unless/until we have a reason to change it.

    opened by karwa 0
  • [Won't merge] Use stdlib random API directly via a C shim

    [Won't merge] Use stdlib random API directly via a C shim

    Use the standard library's random API via a C shim, so we're not limited to 8 bytes per syscall.

    Technically, the compiler headers say:

    /// SWIFT_RUNTIME_STDLIB_API functions are called by compiler-generated code
    /// or by @inlinable Swift code.
    /// Such functions must be exported and must be supported forever as API.
    /// The function name should be prefixed with `swift_`.
    

    So... it's not unsafe or unsupported ("must be supported forever"). But it's hacky.

    It's interesting for benchmarks, but I don't think this should be merged. We should fix the standard library instead.

    opened by karwa 0
Releases(1.0.4)
  • 1.0.4(Apr 27, 2022)

    Fixed some availability issues on Apple platforms.

    What's Changed

    • Add .iOS(.v10) as a platform so the package compiles for iOS (os_unfa… by @arex666 in https://github.com/karwa/uniqueid/pull/5
    • Fix some availability issues by @karwa in https://github.com/karwa/uniqueid/pull/6
    Source code(tar.gz)
    Source code(zip)
  • 1.0.3(Dec 18, 2021)

  • 1.0.2(Dec 18, 2021)

  • 1.0.1(Nov 4, 2021)

    Added a NO_FOUNDATION_COMPAT build setting to remove the dependency on Foundation. To build in this mode, enable the setting from the Package.swift file, or build with the command-line flag:

    swift build -Xswiftc -DNO_FOUNDATION_COMPAT

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Nov 3, 2021)

Owner
Karl
Karl
A Swift collection of unique, ordered objects

Introduction OrderedSet is essentially the Swift equivalent of Foundation's NSOrderedSet/NSMutableOrderedSet. It was created so Swift would have a uni

Weebly 248 Sep 14, 2022
RandomKit is a Swift framework that makes random data generation simple and easy.

RandomKit is a Swift framework that makes random data generation simple and easy. Build Status Installation Compatibility Swift Package Manager CocoaP

Nikolai Vazquez 1.5k Dec 29, 2022
A tiny generator of random data for swift

SwiftRandom SwiftRandom is a tiny help suite for generating random data such as Random human stuff like: names, gender, titles, tags, conversations Ra

Kan Yilmaz 559 Dec 29, 2022
Generates Heroku-style random project names in Swift

RandomProjectName.swift Generates Heroku-style random project names in Swift. Usage Just call String.randomProjectName(), and specify the optional suf

NLUDB 0 Dec 6, 2021
Generates a random photo after you click the button

Swift Random Photo Generator Generates a random photo after you click the button! Things you need to do ?? clone this repository git clone https://git

Tsuen Hsueh 2 Aug 16, 2022
*Random Smooth Cloudy* Noise for SwiftUI

Noise Generate random smooth cloudy noise. Install Swift Package .

Anton Heestand 7 Dec 7, 2022
An offline random passcode generator.

Passcode-Generator An offline random passcode generator. Usage Generates random passcode. Install Files and source code could be found in releases. Pr

Vaida 0 Dec 10, 2021
MetricTime is designed to be one universal timekeeping system that eliminates the hassle of calculating time since most of its convertions as simple as converting meters to centimeters

MetricTime MetricTime is designed to be one universal timekeeping system that eliminates the hassle of calculating time since most of its convertions

Adrian Edwards 4 Feb 10, 2022
An application focused on managing men's haircuts. It has never been so easy to keep the cut on time

An application focused on managing men's haircuts. It has never been so easy to keep the cut on time

Yago Marques 2 Oct 13, 2022
⏲ A tiny package to measure code execution time. Only 20 lines of code.

Measure ⏲ A tiny package to measure code execution time. Measure.start("create-user") let user = User() Measure.finish("create-user") Console // ⏲ Mea

Mezhevikin Alexey 3 Oct 1, 2022