Skip to content
/ unixid Public

A Go library for generating unique, time-based IDs using Unix timestamps at nanosecond precision

License

Notifications You must be signed in to change notification settings

cdvelop/unixid

Repository files navigation

UnixID

Project Badges

A Go library for generating unique, time-based IDs using Unix timestamps at nanosecond precision.

Overview

UnixID provides functionality for generating and managing unique identifiers with the following features:

  • High-performance ID generation based on Unix nanosecond timestamps
  • Thread-safe concurrent ID generation
  • Built-in collision avoidance through sequential numbering
  • Support for both server-side and client-side (WebAssembly) environments
  • Date conversion utilities for timestamp-to-date formatting
  • Smart environment detection for automatic configuration
  • Versatile ID assignment for strings, struct fields and byte slices

Installation

go get github.com/cdvelop/unixid

Quick Start

Server-side Usage

package main

import (
	"github.com/cdvelop/unixid"
)

func main() {
	// Create a new UnixID handler (server-side)
	idHandler, err := unixid.NewUnixID()
	if err != nil {
		panic(err)
	}

	// Generate a new unique ID
	id := idHandler.GetNewID()

	fmt.Printf("Generated ID: %s\n", id)
	// Output: Generated ID: 1624397134562544800

	// Convert an ID to a human-readable date
	dateStr, err := idHandler.UnixNanoToStringDate(id)
	if err != nil {
		panic(err)
	}

	println("ID timestamp represents: ", dateStr)
	// Output: ID timestamp represents: 2021-06-23 15:38:54
}

Client-side (WebAssembly) Usage

For WebAssembly environments, you need to provide a session number handler:

// Example session handler implementation
type sessionHandler struct{}

func (sessionHandler) userSessionNumber() string {
	// In a real application, this would return the user's session number
	return "42"
}

// Create a new UnixID handler with session handler
idHandler, err := unixid.NewUnixID(&sessionHandler{})

ID Format

The generated IDs follow this format:

  • Server-side: [unix_timestamp_in_nanoseconds] (e.g., 1624397134562544800)
  • Client-side: [unix_timestamp_in_nanoseconds].[user_session_number] (e.g., 1624397134562544800.42)

Thread Safety & Avoiding Deadlocks

The library handles concurrent ID generation safely through mutex locking in server-side environments.

IMPORTANT: When integrating this library with other libraries that also use sync.Mutex, infinite deadlocks can occur. To avoid this issue, you can pass an existing mutex when initializing UnixID:

package main

import (
	"fmt"
	"sync"
	"github.com/cdvelop/unixid"
	"github.com/someother/library"
)

func main() {
	// Create a shared mutex
	var mu sync.Mutex
	
	// Pass the shared mutex to UnixID
	idHandler, err := unixid.NewUnixID(&mu)
	if (err != nil) {
		panic(err)
	}
	
	// Pass the same mutex to other libraries if they support it
	otherLib := library.New(&mu)
	
	// Now both libraries will use the same mutex,
	// preventing deadlocks when they need to lock resources
}

Deadlock Prevention with External Mutex

When an external mutex is provided to NewUnixID(), the library automatically detects this and changes its behavior:

  1. Instead of using the provided mutex internally, it switches to a no-op mutex that doesn't perform any actual locking.
  2. This allows GetNewID() to be safely called from within a context that has already acquired the same mutex.

Example of using GetNewID() inside a locked context:

var mu sync.Mutex
idHandler, err := unixid.NewUnixID(&mu)
if err != nil {
    panic(err)
}

// Later in your code...
mu.Lock()
defer mu.Unlock()

// This won't deadlock because internally the library uses a no-op mutex
// when an external mutex is provided
id := idHandler.GetNewID()
// Do something with id...

This behavior assumes that external synchronization is being properly handled by the caller, eliminating the risk of deadlocks when the same mutex is used in nested contexts.

API Reference

Core Functions

  • NewUnixID(...): Creates a new UnixID handler for ID generation with automatic environment detection

    • In server environments, no parameters are required
    • In WebAssembly environments, requires a userSessionNumber implementation
    • Uses build tags (wasm or !wasm) to determine the appropriate implementation
    • Thread-safe in server environments with mutex locking
    • No mutex in WebAssembly as JavaScript is single-threaded
    • Can accept an existing sync.Mutex or *sync.Mutex to prevent deadlocks when integrating with other libraries
  • GetNewID(): Generates a new unique ID

    • Returns a string representation of the ID
    • In WebAssembly builds, appends a user session number to the timestamp
  • SetNewID(target any): Sets a new unique ID to various target types

    • Accepts pointers to string, tinyreflect.Value, or byte slices
    • Thread-safe in server environments
    • Example usages:
      // Set ID to a string variable
      var id string
      idHandler.SetNewID(&id)
      
      // Set ID to a struct field
      type User struct { ID string }
      user := User{}
      idHandler.SetNewID(&user.ID)
      
      // Append ID to a byte slice
      buf := make([]byte, 0, 64)
      idHandler.SetNewID(buf)
      
      // Set ID to a tinyreflect.Value
      v := tinyreflect.ValueOf(&user)
      // The ValueOf(&data) returns a Ptr. We need to get the element it points to
      // before we can access its fields. This is what Elem() does.
      structVal, err := v.Elem()
      if err != nil {
      	// Failed to get element from pointer value:...
      }
      
      IDField, err := structVal.Field(0)
      if err != nil {
      	// Failed to get field 'ID':...
      }
      idHandler.SetNewID(&IDField)
  • UnixNanoToStringDate(unixNanoStr): Converts a Unix nanosecond timestamp ID to a human-readable date

Additional Utility Functions

  • UnixSecondsToTime(unixSeconds any) string: Converts a Unix timestamp in seconds to a formatted time string (HH:mm:ss). e.g., 1624397134 -> 15:38:54 supports int64, string, and float64 types

Validate ID

The ValidateID function validates and parses a given ID string. It returns the parsed ID as an int64 and an error if the ID is invalid.

Example

package main

import (
	"fmt"
	"github.com/cdvelop/unixid"
)

func main() {
	id := "1624397134562544800"
	parsedID, err := unixid.ValidateID(id)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Parsed ID: %d\n", parsedID)
	// Output: Parsed ID: 1624397134562544800
}

Environment-Based Configuration

UnixID automatically detects the compilation environment and configures itself appropriately:

  • Server-side (!wasm build tag):

    • Uses Go's standard time package
    • Implements mutex-based thread safety
    • Generates simple timestamp-based IDs
  • WebAssembly (wasm build tag):

    • Uses JavaScript's Date API through syscall/js
    • Requires a session handler to manage user identifiers
    • Appends a user session number to IDs for cross-client uniqueness

This automatic configuration allows you to use the same API in both environments while the library handles the implementation details internally.

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

A Go library for generating unique, time-based IDs using Unix timestamps at nanosecond precision

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages