Skip to content

Instantly share code, notes, and snippets.

@PaulBradley
Last active March 11, 2025 14:45
Show Gist options
  • Save PaulBradley/08598aa755a6845f46691ab363ddf7f6 to your computer and use it in GitHub Desktop.
Save PaulBradley/08598aa755a6845f46691ab363ddf7f6 to your computer and use it in GitHub Desktop.
Generating Deterministic UUID's Within Go #golang

Generating Deterministic UUID's Within Go

I recently worked on a project which needed to interface five hospital systems to one national system. As part of the SOAP submission to the national system I needed to provide a unique UUID which would identify each user from the source hospital system. I wanted to generate the UUID on the fly as the message was being created - so I needed a way to regenerate the same UUID for the same user.

Each hospital system had their own unique user account reference. So by combining this unique account reference with an organisation identifier, I could generate a unique UUID for users with the same account reference across different facilities.

This is the function I came up with.

Updated (11.03.2025)

func deterministicGUID(organisation string, account string) uuid.UUID {
  var hash [16]byte
  var guid uuid.UUID

  hash = xxh3.HashString128(organisation + account).Bytes()

  // uuid.FromBytes returns an error if the slice
  // of bytes is not 16 - as hash is defined as
  // [16]byte then we can ignore checking the error
  guid, _ = uuid.FromBytes(hash[:])
  return guid
}

Calling the function

package main

import (
  "fmt"

  "github.com/google/uuid"
  "github.com/zeebo/xxh3"
)

func main() {
  fmt.Println("UUID for user 357  @ LTH : ", deterministicGUID("LTH", "357").String())
  fmt.Println("UUID for user 5689 @ BHT : ", deterministicGUID("BHT", "5689").String())
}

Sample output

UUID for user 357  @ LTH : c737ccd9-62da-f7b0-a6ad-6d21573b72e7
UUID for user 5689 @ BHT : 78e67353-64c6-2507-5683-4612bddcfe6f
@fieldse
Copy link

fieldse commented Jan 29, 2023

Useful!

@kmlebedev
Copy link

+1

@StephaneBunel
Copy link

StephaneBunel commented Sep 7, 2023

package main

import "github.com/google/uuid"
import "github.com/zeebo/xxh3"
import "time"

func main() {
        data := "LTH" + " " + "357"

        start := time.Now()
        guid := uuid.NewMD5(uuid.NameSpaceOID, []byte(data))
        println("md5:", time.Since(start).String(), guid.String())

        start = time.Now()
        h := xxh3.HashString128(data).Bytes()
        guid, _ = uuid.FromBytes(h[:])
        println("xxh128:", time.Since(start).String(), guid.String())
}

md5: 666ns 27b39a40-7b46-3ed5-b058-369835a583f5
xxh128: 95ns 97fa2c81-2f9e-199a-929a-3f809fa5b9d1

md5 seems evil :-)

@PaulBradley
Copy link
Author

Thanks, the xxh3 approach seems much better.

@bartdorlandt
Copy link

I had been using this as well and I was wondering when looking back into this yesterday. Why would you go to a string and take a portion of it, instead of taking the []byte array, which is 16 long as expected by uuid.

Resulting in this piece of code:

func genUuid(s string) uuid.UUID {
	md5hash := md5.New()
	md5hash.Write([]byte(s))
	// log.Print(md5hash.Sum(nil))
	// log.Print(hex.EncodeToString(md5hash.Sum(nil)))

	u, err := uuid.FromBytes(md5hash.Sum(nil))
	if err != nil {
		log.Fatal(err)
	}

	return uuid.UUID(u.Bytes())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment