Skip to content

Potential race condition in clock sequence generation #216

@atlet99

Description

@atlet99

Welcome

  • Yes, I'm using the latest release.
  • Yes, I've searched similar issues on GitHub and didn't find any.

What did you expect to see?

Expected all V1 UUIDs to be unique when generated concurrently, even with a fixed timestamp. The clock sequence mechanism should ensure uniqueness by incrementing when the timestamp doesn't advance.
According to RFC-9562, v1 UUIDs should be unique even when generated in rapid succession using clock sequence increments.

What did you see instead?

Massive UUID duplication in v1 generation under concurrent load with fixed timestamp:

  • V1 UUIDs: 83,616 duplicates out of 100,000 total UUIDs (83.6% duplication rate!)
  • V7 UUIDs: 100,000 unique UUIDs (0% duplication rate)

The duplicate v1 UUIDs only differ in the clock sequence bytes (positions 8-9), but many have identical values, indicating the clock sequence mechanism is failing to provide uniqueness.

Example duplicate UUIDs:

70d9b500-fa26-11dd-876f-322495cdf0f6
70d9b500-fa26-11dd-8770-322495cdf0f6
70d9b500-fa26-11dd-8771-322495cdf0f6

The problem appears to be specific to v1 UUID generation logic, not a general race condition, since v7 UUIDs work correctly using the same getClockSequence() function.

Reproduction steps

package main

import (
    "sync"
    "testing"
    "time"
    "github.com/gofrs/uuid/v5"
)

func TestV1DuplicationBug(t *testing.T) {
    const numGoroutines = 100
    const numUUIDs = 1000
    
    // Test with fixed time to force clock sequence usage
    fixedTime := time.Unix(1234567890, 0)
    gen := uuid.NewGenWithOptions(
        uuid.WithEpochFunc(func() time.Time {
            return fixedTime // Always return same time to force clock sequence increment
        }),
    )
    
    var wg sync.WaitGroup
    uuidChan := make(chan uuid.UUID, numGoroutines*numUUIDs)
    
    // Launch multiple goroutines generating V1 UUIDs simultaneously
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < numUUIDs; j++ {
                u, err := gen.NewV1()
                if err != nil {
                    t.Errorf("Failed to generate UUID: %v", err)
                    return
                }
                uuidChan <- u
            }
        }()
    }
    
    wg.Wait()
    close(uuidChan)
    
    // Check for duplicates
    seen := make(map[uuid.UUID]bool)
    duplicates := 0
    
    for u := range uuidChan {
        if seen[u] {
            duplicates++
        }
        seen[u] = true
    }
    
    if duplicates > 0 {
        t.Errorf("Found %d duplicate UUIDs out of %d total", duplicates, numGoroutines*numUUIDs)
    }
}

Version of flock

latest master branch (commit id: 78d4142)

Logs

<details>


$ go test -race -run TestRaceCondition -v
=== RUN   TestRaceConditionUUIDGeneration
    race_test.go:56: Found 83616 duplicate UUIDs out of 100000 total
    race_test.go:59: Generated 16384 unique UUIDs across 100 goroutines
--- FAIL: TestRaceConditionUUIDGeneration (1.25s)
=== RUN   TestRaceConditionV7UUIDGeneration
    race_test.go:112: Generated 100000 unique V7 UUIDs across 100 goroutines
--- PASS: TestRaceConditionV7UUIDGeneration (0.16s)
FAIL


</details>

Go environment

$ go version && go env
go version go1.24.4 darwin/arm64
GOARCH="arm64"
GOOS="darwin"
GOVERSION="go1.24.4"

Validation

  • Yes, I've included all information above (version, config, etc.).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions