Universally Unique Lexicographically Sortable Identifier (ULID) in Go

Related tags

UUID Libraries ulid
Overview

Universally Unique Lexicographically Sortable Identifier

Project status Build Status Go Report Card Coverage Status go.dev reference Apache 2 licensed

A Go port of alizain/ulid with binary format implemented.

Background

A GUID/UUID can be suboptimal for many use-cases because:

  • It isn't the most character efficient way of encoding 128 bits
  • UUID v1/v2 is impractical in many environments, as it requires access to a unique, stable MAC address
  • UUID v3/v5 requires a unique seed and produces randomly distributed IDs, which can cause fragmentation in many data structures
  • UUID v4 provides no other information than randomness which can cause fragmentation in many data structures

A ULID however:

  • Is compatible with UUID/GUID's
  • 1.21e+24 unique ULIDs per millisecond (1,208,925,819,614,629,174,706,176 to be exact)
  • Lexicographically sortable
  • Canonically encoded as a 26 character string, as opposed to the 36 character UUID
  • Uses Crockford's base32 for better efficiency and readability (5 bits per character)
  • Case insensitive
  • No special characters (URL safe)
  • Monotonic sort order (correctly detects and handles the same millisecond)

Install

This package requires Go modules.

go get github.com/oklog/ulid/v2

Usage

An ULID is constructed with a time.Time and an io.Reader entropy source. This design allows for greater flexibility in choosing your trade-offs.

Please note that rand.Rand from the math package is not safe for concurrent use. Instantiate one per long living go-routine or use a sync.Pool if you want to avoid the potential contention of a locked rand.Source as its been frequently observed in the package level functions.

func ExampleULID() {
	t := time.Unix(1000000, 0)
	entropy := ulid.Monotonic(rand.New(rand.NewSource(t.UnixNano())), 0)
	fmt.Println(ulid.MustNew(ulid.Timestamp(t), entropy))
	// Output: 0000XSNJG0MQJHBF4QX1EFD6Y3
}

Commandline tool

This repo also provides a tool to generate and parse ULIDs at the command line. These commands should install the latest version of the tool at bin/ulid:

cd $(mktemp -d)
env GOPATH=$(pwd) GO111MODULE=on go get -v github.com/oklog/ulid/v2/cmd/ulid

Usage:

Usage: ulid [-hlqz] [-f <format>] [parameters ...]
 -f, --format=<format>  when parsing, show times in this format: default, rfc3339, unix, ms
 -h, --help             print this help text
 -l, --local            when parsing, show local time instead of UTC
 -q, --quick            when generating, use non-crypto-grade entropy
 -z, --zero             when generating, fix entropy to all-zeroes

Examples:

$ ulid
01D78XYFJ1PRM1WPBCBT3VHMNV
$ ulid -z
01D78XZ44G0000000000000000
$ ulid 01D78XZ44G0000000000000000
Sun Mar 31 03:51:23.536 UTC 2019
$ ulid --format=rfc3339 --local 01D78XZ44G0000000000000000
2019-03-30T20:51:23.536PDT

Specification

Below is the current specification of ULID as implemented in this repository.

Components

Timestamp

  • 48 bits
  • UNIX-time in milliseconds
  • Won't run out of space till the year 10889 AD

Entropy

  • 80 bits
  • User defined entropy source.
  • Monotonicity within the same millisecond with ulid.Monotonic

Encoding

Crockford's Base32 is used as shown. This alphabet excludes the letters I, L, O, and U to avoid confusion and abuse.

0123456789ABCDEFGHJKMNPQRSTVWXYZ

Binary Layout and Byte Order

The components are encoded as 16 octets. Each component is encoded with the Most Significant Byte first (network byte order).

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      32_bit_uint_time_high                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     16_bit_uint_time_low      |       16_bit_uint_random      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

String Representation

 01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 Timestamp           Entropy
  10 chars           16 chars
   48bits             80bits
   base32             base32

Test

go test ./...

Benchmarks

On a Intel Core i7 Ivy Bridge 2.7 GHz, MacOS 10.12.1 and Go 1.8.0beta1

BenchmarkNew/WithCryptoEntropy-8      2000000        771 ns/op      20.73 MB/s   16 B/op   1 allocs/op
BenchmarkNew/WithEntropy-8            20000000      65.8 ns/op     243.01 MB/s   16 B/op   1 allocs/op
BenchmarkNew/WithoutEntropy-8         50000000      30.0 ns/op     534.06 MB/s   16 B/op   1 allocs/op
BenchmarkMustNew/WithCryptoEntropy-8  2000000        781 ns/op      20.48 MB/s   16 B/op   1 allocs/op
BenchmarkMustNew/WithEntropy-8        20000000      70.0 ns/op     228.51 MB/s   16 B/op   1 allocs/op
BenchmarkMustNew/WithoutEntropy-8     50000000      34.6 ns/op     462.98 MB/s   16 B/op   1 allocs/op
BenchmarkParse-8                      50000000      30.0 ns/op     866.16 MB/s    0 B/op   0 allocs/op
BenchmarkMustParse-8                  50000000      35.2 ns/op     738.94 MB/s    0 B/op   0 allocs/op
BenchmarkString-8                     20000000      64.9 ns/op     246.40 MB/s   32 B/op   1 allocs/op
BenchmarkMarshal/Text-8               20000000      55.8 ns/op     286.84 MB/s   32 B/op   1 allocs/op
BenchmarkMarshal/TextTo-8             100000000     22.4 ns/op     714.91 MB/s    0 B/op   0 allocs/op
BenchmarkMarshal/Binary-8             300000000     4.02 ns/op    3981.77 MB/s    0 B/op   0 allocs/op
BenchmarkMarshal/BinaryTo-8           2000000000    1.18 ns/op   13551.75 MB/s    0 B/op   0 allocs/op
BenchmarkUnmarshal/Text-8             100000000     20.5 ns/op    1265.27 MB/s    0 B/op   0 allocs/op
BenchmarkUnmarshal/Binary-8           300000000     4.94 ns/op    3240.01 MB/s    0 B/op   0 allocs/op
BenchmarkNow-8                        100000000     15.1 ns/op     528.09 MB/s    0 B/op   0 allocs/op
BenchmarkTimestamp-8                  2000000000    0.29 ns/op   27271.59 MB/s    0 B/op   0 allocs/op
BenchmarkTime-8                       2000000000    0.58 ns/op   13717.80 MB/s    0 B/op   0 allocs/op
BenchmarkSetTime-8                    2000000000    0.89 ns/op    9023.95 MB/s    0 B/op   0 allocs/op
BenchmarkEntropy-8                    200000000     7.62 ns/op    1311.66 MB/s    0 B/op   0 allocs/op
BenchmarkSetEntropy-8                 2000000000    0.88 ns/op   11376.54 MB/s    0 B/op   0 allocs/op
BenchmarkCompare-8                    200000000     7.34 ns/op    4359.23 MB/s    0 B/op   0 allocs/op

Prior Art

Comments
  • When parsing, validate that chars are in expected Base32 range.

    When parsing, validate that chars are in expected Base32 range.

    This adds new validation in UnmarshalText and Parse that returns an ErrInvalidCharacters error when called with input bytes outside the expected Base32 encoding set. See https://github.com/oklog/ulid/issues/21 for details.

    This does unfortunately end up being a bit slower. Some basic benchmarks from my laptop:

    Before

    BenchmarkUnmarshal/Text-8         	1000000000	        17.3 ns/op	1506.08 MB/s
    

    With a simple loop

    BenchmarkUnmarshal/Text-8         	500000000	        32.5 ns/op	 799.66 MB/s
    

    With a manually unrolled loop (this change)

    BenchmarkUnmarshal/Text-8         	500000000	        26.6 ns/op	 976.36 MB/s
    

    Comparison from original to this version

    benchmark                       old ns/op     new ns/op     delta
    BenchmarkUnmarshal/Text-8       17.3          26.6          +53.76%
    
    benchmark                       old MB/s     new MB/s     speedup
    BenchmarkUnmarshal/Text-8       1506.08      976.36       0.65x
    

    Fixes https://github.com/oklog/ulid/issues/21.

    opened by mattmoyer 25
  • Duplicate ID's

    Duplicate ID's

    I'm adding to the DB in very quick succession around 1000 rows.

    I decided to output the ULIDs so I could catch if there were dupes:

    Here are the relevant parts:

    01F9VG0NT0ACNSHJM2J4G6WH3E
    01F9VG0NT0ACNSHJM2J8WRTTJE
    01F9VG0NT0ACNSHJM2JAC9W6YT
    01F9VG0NT0ACNSHJM2JDWKQS65
    01F9VG0NT0ACNSHJM2J8KV2SB2
    01F9VG0NT0ACNSHJM2J4G6WH3E
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM2J4G6WH3E' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM2N53XTXJH
    01F9VG0NT0ACNSHJM2N8BWJXPF
    01F9VG0NT0ACNSHJM2N8QXRBDM
    01F9VG0NT0ACNSHJM2N53XTXJH
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM2N53XTXJH' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM2SMR13HZ2
    01F9VG0NT0ACNSHJM2SQVZW987
    01F9VG0NT0ACNSHJM2SVQJ87QQ
    01F9VG0NT0ACNSHJM2SMR13HZ2
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM2SMR13HZ2' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM30062WA6F
    01F9VG0NT0ACNSHJM30297Z6DR
    01F9VG0NT0ACNSHJM30062WA6F
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM30062WA6F' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM39T0D5B1B
    01F9VG0NT0ACNSHJM39ZVD159R
    01F9VG0NT0ACNSHJM3A59EMVZC
    01F9VG0NT0ACNSHJM39XSN0A84
    01F9VG0NT0ACNSHJM3A34FHCAR
    01F9VG0NT0ACNSHJM3A5CKZKJT
    01F9VG0NT0ACNSHJM39T0D5B1B
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM39T0D5B1B' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM3APRCBXA7
    01F9VG0NT0ACNSHJM3ATGM0XHQ
    01F9VG0NT0ACNSHJM3APRCBXA7
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM3APRCBXA7' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM3CTT1B09C
    01F9VG0NT0ACNSHJM3CZHHK5QT
    01F9VG0NT0ACNSHJM3D12FM3C4
    01F9VG0NT0ACNSHJM3CXQGM6GM
    01F9VG0NT0ACNSHJM3D3H1M9EE
    01F9VG0NT0ACNSHJM3D4REME36
    01F9VG0NT0ACNSHJM3CTT1B09C
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM3CTT1B09C' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM3DPF00J7M
    01F9VG0NT0ACNSHJM3DG1C0KAC
    01F9VG0NT0ACNSHJM3DQR5G89S
    01F9VG0NT0ACNSHJM3DV3SHQ6M
    01F9VG0NT0ACNSHJM3DY6049MW
    01F9VG0NT0ACNSHJM3DPF00J7M
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM3DPF00J7M' for key 'PRIMARY'
    
    01F9VG0NT0ACNSHJM3VYDT1FKJ
    01F9VG0NT0ACNSHJM3VYDT1FKJ
    Error 1062: Duplicate entry '01F9VG0NT0ACNSHJM3VYDT1FKJ' for key 'PRIMARY'
    

    My code is broken into three parts, as it's a complex long running process:

    stored in a struct:

    now := time.Now().UTC()
    entropy := ulid.Monotonic(rand.New(rand.NewSource(now.UnixNano())), 0)
    

    I generate the ID (m is a struct):

    job.ID = common.GenUlid(m.now, m.entropy)
    

    Func:

    func GenUlid(now time.Time, entropy io.Reader) string {
    	return ulid.MustNew(ulid.Timestamp(now), entropy).String()
    }
    

    Thanks for your time.

    opened by paulm17 24
  • Error Unmarshaling from sqlx queries

    Error Unmarshaling from sqlx queries

    type Team struct {
    	ID         ulid.ULID `db:"id"`
    	Name       string    `db:"name"`
    	Status     string    `db:"status"`
    	CreatedAt  time.Time `db:"created_at"`
    	UpdatedAt  time.Time `db:"updated_at"`
    	Archivedat time.Time `db:"archived_at"`
    }
    

    Usage:

    	query := "SELECT * FROM teams WHERE status = 'active'"
    	teams := []Team{}
    	if err := r.DbConn.SelectContext(ctx, &teams, query); err != nil {
    		return nil, errors.Wrap(err, "selecting teams")
    	}
    

    Result: error retrieving data selecting teams: sql: Scan error on column index 0, name "id": ulid: bad data size when unmarshaling

    Table Structure:

    create table teams (
            id varchar(26) not null,
            name varchar(255) not null,
            status varchar(26) not null,
            created_at datetime not null default now(),
            updated_at datetime not null default now(),
            archived_at datetime null
          ) default character set utf8mb4 engine = InnoDB;
    
    opened by pragmaticivan 21
  • Add a generator that ensures monotonicity of ULIDs.

    Add a generator that ensures monotonicity of ULIDs.

    This implements the method described by @alizain (and used in his ulid package) to enforce monotonicity of ULIDs within a same millisecond.

    I'm not sure if the API is to your liking, I'm happy to reshape it into something aesthetically pleasing to you.

    Addresses issue #6.

    r: @peterbourgon @tsenart cc: @alizain

    opened by aybabtme 18
  • Add ParseStrict and MustParseStrict methods.

    Add ParseStrict and MustParseStrict methods.

    These are methods like Parse and MustParse, but validate that their inputs contain only valid base32 characters. On invalid input, they return a new ErrInvalidCharacters error rather than producing an undefined ULID.

    A comparison benchmark:

    $ go test -v -run=^$ -benchtime 10s -bench BenchmarkParse ./...
    goos: darwin
    goarch: amd64
    pkg: github.com/oklog/ulid
    BenchmarkParse-8         	500000000	        26.1 ns/op	 994.49 MB/s
    BenchmarkParseStrict-8   	500000000	        39.2 ns/op	 662.87 MB/s
    PASS
    ok  	github.com/oklog/ulid	39.221s
    ?   	github.com/oklog/ulid/cmd/ulid	[no test files]
    

    This is an alternate fix for https://github.com/oklog/ulid/issues/21. I originally approached this issue by adding this validation to Parse and UnmarshalText (https://github.com/oklog/ulid/pull/22), but there was some concern about the performance impact.

    Fixes https://github.com/oklog/ulid/issues/21.

    opened by mattmoyer 15
  • Standalone executable

    Standalone executable

    Are you publishing a standalone executable for the go version? Someone asked over on my repo, and I wanted to send them your way, because doing it with go would be pretty straightforward.

    opened by alizain 14
  • Continuous fuzzing

    Continuous fuzzing

    Proposing to integrate continuous fuzzing. Fuzzit gives free service for open source projects.

    This targets Parse and ParseStrict by input + New by fuzzing the timestamp and entropy.

    Setup is like this:

    • In Fuzzit create targets ulid-new ulid-parse ulid-parse-strict.
    • In Fuzzit settings grab an API key. On the repo settings in Travis paste it to envvar FUZZIT_API_KEY.

    Thanks for considering.

    @peterbourgon @tsenart

    opened by bookmoons 12
  • Getting duplicate ID's

    Getting duplicate ID's

    I'm using this library to generate ULID's for a primary key in a database and when I run my benchmarks I'm finding a lot of duplicate primary key errors from my database. I'm assuming I'm using this library wrong because. What is the guidance on how to use this to avoid generating duplicate keys?

    I'm doing the following.

    now := time.Now()
    entropy := ulid.Monotonic(rand.New(rand.NewSource(now.UnixNano())), 0)
    
    transaction, err := pool.Begin()
    
    for i := 0; i < b.N; i++ {
        id := ulid.MustNew(ulid.Timestamp(now), entropy)
        
        // INSERT SQL
        tx.Exec(INSERT_SQL_USING_id)
    }
    
    tx.Commit()
    

    What have I done wrong that generates duplicate ULID's?

    opened by kellabyte 11
  • Support parsing UUID strings

    Support parsing UUID strings

    Has there been any consideration into allowing string UUIDs be parsed within the unexported parse function: func parse(v []byte, strict bool, id *ULID) error

    The reason I'm raising this is because I'd like to use this library for my primary keys, however CockroachDB returns UUID columns as UUID strings, not []byte, so ulid.ULID#Scan(src) fails to parse the value.

    My workaround is to implement a wrapper ULID type which has a Scan function (implementation shown below) however it would be nice to not have to do this, as I have to do things like ULID{ULID: ulid.MustParse(...)} which is a real annoyance:

    func (id *ULID) Scan(src interface{}) error {
    	if str, ok := src.(string); ok {
    		if len(str) != ulid.EncodedSize {
    			bytes, err := uuid.Parse(str)
    			if err != nil {
    				return err
    			}
    
    			return id.UnmarshalBinary(bytes[:])
    		}
    	}
    
    	return id.ULID.Scan(src)
    }
    

    If you don't want to implement UUID parsing or add an extra dependency, how about some mechanism where users can register custom parsers?

    opened by dan-j 10
  • Issues generating ULID with freq. <1ms

    Issues generating ULID with freq. <1ms

    Hi,

    I've been experimenting with your library to generate ULIDs as keys for a database. My code was doing 20+ row insertions per 1ms, and results, i.e. row ULIDs, are not in a strict sequence, not sorted (image below). I was suggested that this may be due to "Within the same millisecond, sort order is not guaranteed" on https://github.com/alizain/ulid which, as I understand, served as a basis for oklog/ulid.

    Could you please confirm (or not) randomness within 1 ms? If not, below is my code, I would appreciate your check if IDs are generated correctly:

    func GetNewID() string {
    	entropy := rand.New(rand.NewSource(time.Now().UnixNano()))
    	value := ulid.MustNew(ulid.Now(), entropy)
    	return value.String()
    }
    

    This is a select in descending order by id (generated by ULID), where post_id column is filled with a "for" loop key. With correct generation, post_id should be a in a strict desc. order. screen shot 2017-02-20 at 11 34 06 pm

    Thank you! D.

    opened by oharlem 9
  • big bug: ulid is alway not sortable

    big bug: ulid is alway not sortable

    var list = make([]string, 0) var monotonic = ulid.Monotonic(rand.New(rand.NewSource(time.Now().UnixNano())), 1) var busy = make(chan bool, 1)

    func AddUlid2List(index string) { busy <- true id, _ := ulid.New(ulid.Timestamp(time.Now()), monotonic) list = append(list, id.String()+" "+index) <-busy }

    func Test_test6(t *testing.T) { wg := sync.WaitGroup{} wg.Add(2) go func() { for i := 0; i < 1000000; i++ { AddUlid2List("1") } wg.Done() }() go func() { for i := 0; i < 1000000; i++ { AddUlid2List("2") } wg.Done() }() wg.Wait() for i := 0; i < len(list); i++ { if i+1 < len(list) && list[i] > list[i+1] { //63718 01GH13A2V6MBDBNBG8TSH0B0ZD 2 01GH13A2V5W3TXG0EX5GF9Q4MP 1 fmt.Println(i, list[i], list[i+1]) t.Error("big bug") } } if list[len(list)-2] > list[len(list)-1] { t.Error("big bug2") fmt.Println(list[len(list)-2], list[len(list)-1]) } fmt.Println("END") }

    func Test_ulid(t *testing.T) { i := 0 for { i++ Test_test6(t) if i == 10 { break } } }

    run the method Test_ulid, it print: 63718 01GH13A2V6MBDBNBG8TSH0B0ZD 2 01GH13A2V5W3TXG0EX5GF9Q4MP 1 big bug

    opened by ABOB9 8
  • ULIDs contains `ILOU` will be parsed as weird timestamps

    ULIDs contains `ILOU` will be parsed as weird timestamps

    Hi! I'm writing a new Ruby library for handling ULID in these days. Now I’m testing other implementations examples in https://github.com/kachick/ruby-ulid/issues/53.

    And I have found weird examples in original repository as https://github.com/ulid/javascript/pull/85.

    And then checked the parser of this library, because I'm using this in a Go project, it is so useful! 😄

    Using this command line tool as below, the version is https://github.com/oklog/ulid/tree/e7ac4de44d238ff4707cc84b9c98ae471f31e2d1

    $ ulid -h
    Usage: ulid [-hlqz] [-f <format>] [parameters ...]
     -f, --format=<format>  when parsing, show times in this format: default, rfc3339, unix, ms
     -h, --help             print this help text
     -l, --local            when parsing, show local time instead of UTC
     -q, --quick            when generating, use non-crypto-grade entropy
     -z, --zero             when generating, fix entropy to all-zeroes
    
    $ ulid 01111111111111111111111111
    Mon Dec 19 08:09:04.801 UTC 2005
    
    $ ulid 0LLLLLLLLLLLLLLLLLLLLLLLLL # `L` is same as `1` in https://www.crockford.com/base32.html, but returned different value
    Tue Aug 02 05:31:50.655 UTC 10889
    
    $ ulid 0UUUUUUUUUUUUUUUUUUUUUUUUU # `U` is invalid in https://www.crockford.com/base32.html, but does not raise error
    Tue Aug 02 05:31:50.655 UTC 10889
    
    $ ulid 00000000000000000000000000
    Thu Jan 01 00:00:00 UTC 1970
    
    $ ulid 0OOOOOOOOOOOOOOOOOOOOOOOOO # `O` is same as `0` in https://www.crockford.com/base32.html, but returned different value
    Tue Aug 02 05:31:50.655 UTC 10889
    

    In my understanding, Crockford's base32 does not contain L I O for the encoded product. So I think ULID can handle them as invalid values 🤔 ref: https://github.com/ulid/spec/issues/38, https://github.com/kachick/ruby-ulid/issues/57

    opened by kachick 4
Releases(v2.1.0)
  • v2.1.0(Jul 11, 2022)

    Full release of v2.1.0, thanks to our testers.

    What's Changed

    • Adding Bytes() to get slice representation of ULID by @sabify in https://github.com/oklog/ulid/pull/49
    • Update README to mention v2 by @lmas in https://github.com/oklog/ulid/pull/48
    • Continuous fuzzing by @bookmoons in https://github.com/oklog/ulid/pull/55
    • Long-overdue tidying up by @peterbourgon in https://github.com/oklog/ulid/pull/61
    • Update Value docs re: zero-value behavior by @peterbourgon in https://github.com/oklog/ulid/pull/60
    • Fix max theorical date in the README by @fancyweb in https://github.com/oklog/ulid/pull/65
    • Refer updated github link of alizain/ulid by @kachick in https://github.com/oklog/ulid/pull/67
    • Fix timezone formatting in RFC 3339 output by @scop in https://github.com/oklog/ulid/pull/74
    • Linter fixes by @scop in https://github.com/oklog/ulid/pull/75
    • ulid: add DefaultEntropy() and Make() by @tsenart in https://github.com/oklog/ulid/pull/81

    New Contributors

    • @sabify made their first contribution in https://github.com/oklog/ulid/pull/49
    • @lmas made their first contribution in https://github.com/oklog/ulid/pull/48
    • @bookmoons made their first contribution in https://github.com/oklog/ulid/pull/55
    • @fancyweb made their first contribution in https://github.com/oklog/ulid/pull/65
    • @kachick made their first contribution in https://github.com/oklog/ulid/pull/67
    • @scop made their first contribution in https://github.com/oklog/ulid/pull/74

    Full Changelog: https://github.com/oklog/ulid/compare/v2.0.2...v2.1.0

    Source code(tar.gz)
    Source code(zip)
  • v2.1.0-alpha.1(Jul 3, 2022)

    Alpha release of v2.1.0, try it out!

    What's Changed

    • Adding Bytes() to get slice representation of ULID by @sabify in https://github.com/oklog/ulid/pull/49
    • Update README to mention v2 by @lmas in https://github.com/oklog/ulid/pull/48
    • Continuous fuzzing by @bookmoons in https://github.com/oklog/ulid/pull/55
    • Long-overdue tidying up by @peterbourgon in https://github.com/oklog/ulid/pull/61
    • Update Value docs re: zero-value behavior by @peterbourgon in https://github.com/oklog/ulid/pull/60
    • Fix max theorical date in the README by @fancyweb in https://github.com/oklog/ulid/pull/65
    • Refer updated github link of alizain/ulid by @kachick in https://github.com/oklog/ulid/pull/67
    • Fix timezone formatting in RFC 3339 output by @scop in https://github.com/oklog/ulid/pull/74
    • Linter fixes by @scop in https://github.com/oklog/ulid/pull/75
    • ulid: add DefaultEntropy() and Make() by @tsenart in https://github.com/oklog/ulid/pull/81

    New Contributors

    • @sabify made their first contribution in https://github.com/oklog/ulid/pull/49
    • @lmas made their first contribution in https://github.com/oklog/ulid/pull/48
    • @bookmoons made their first contribution in https://github.com/oklog/ulid/pull/55
    • @fancyweb made their first contribution in https://github.com/oklog/ulid/pull/65
    • @kachick made their first contribution in https://github.com/oklog/ulid/pull/67
    • @scop made their first contribution in https://github.com/oklog/ulid/pull/74

    Full Changelog: https://github.com/oklog/ulid/compare/v2.0.2...v2.1.0-alpha.1

    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(May 9, 2019)

    Identical to v2.0.1, except uses the proper /v2 suffix on the ulid import in ulid_test.go. Without this change, anyone who imported oklog/ulid at e.g. v2.0.1 into their project would also get oklog/ulid at v0-something due to the inadvertent transitive dependency.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(May 7, 2019)

  • v2.0.0(May 6, 2019)

Owner
OK Log
The logs are gonna be okay
OK Log
K-Sortable Globally Unique IDs

ksuid ksuid is an efficient, comprehensive, battle-tested Go library for generating and parsing a specific kind of globally unique identifier called a

Segment 3.9k Jan 9, 2023
Generate, encode, and decode UUIDs v1 with fast or cryptographic-quality random node identifier.

A Go package for generating and manipulating UUIDs Generate, encode, and decode UUIDs v1, as defined in RFC 4122, in Go. Project Status v1.1.0 Stable:

AGExt 15 Sep 27, 2022
A tiny and fast Go unique string generator

Nano ID A tiny and fast Go unique string generator Safe. It uses cryptographically strong random APIs and tests distribution of symbols. Compact. It u

Dair Aidarkhanov 56 Nov 11, 2022
High performance unique number generator powered by Go

SEQSVR High performance unique number generator powered by Go 中文 README Features Distributed: Can be scaled horizontally High performance: Allocation

xin zhao 60 Nov 16, 2022
✨ Generate unique IDs (Port of Node package "generate-snowflake" to Golang)

✨ Generate Snowflake Generate unique IDs. Inspired by Twitter's Snowflake system. ?? Installation Initialize your project (go mod init example.com/exa

Barış DEMİRCİ 6 Feb 11, 2022
A network service for generating unique ID numbers inspired by Twitter's Snowflake.

Hanabira Hanabira is a network service for generating unique ID numbers inspired by Twitter's Snowflake. How to run hanabira-cluster and etcd-cluster

Shooketh 0 Jan 13, 2022
Curtis Milo 2 May 8, 2022
Vex is a variable-length, lexicographically-sortable hex format for uint64 values

Vex is a variable-length, lexicographically-sortable hex format for uint64 values. It can be used instead of fmt.Sprintf("%016x", v) for shorter s

Ben Johnson 13 Mar 3, 2022
Compact, sortable and fast unique IDs with embedded metadata.

A spec for unique IDs in distributed systems based on the Snowflake design, i.e. a coordination-based ID variant. It aims to be friendly to both machi

null 75 Dec 22, 2022
PKSUID is a small extension for KSUID (K-Sortable Globally Unique IDs) which allows prefixing it with arbitrary up to 16 bytes strings

PKSUID is a small extension for KSUID (K-Sortable Globally Unique IDs) which allows prefixing it with arbitrary up to 16 bytes strings

Velmie 2 Dec 1, 2022
K-Sortable Globally Unique IDs

ksuid ksuid is an efficient, comprehensive, battle-tested Go library for generating and parsing a specific kind of globally unique identifier called a

Segment 3.9k Jan 9, 2023
🍫 A customisable, universally compatible terminal status bar

Shox: Terminal Status Bar A customisable terminal status bar with universal shell/terminal compatibility. Currently works on Mac/Linux. Installation N

Liam Galvin 679 Dec 27, 2022
Generate, encode, and decode UUIDs v1 with fast or cryptographic-quality random node identifier.

A Go package for generating and manipulating UUIDs Generate, encode, and decode UUIDs v1, as defined in RFC 4122, in Go. Project Status v1.1.0 Stable:

AGExt 15 Sep 27, 2022
Microservice generates pair of access and refresh JSON web tokens signed by user identifier.

go-jwt-issuer Microservice generates pair access and refresh JSON web tokens signed by user identifier. ?? Deployed on Heroku Run tests: export SECRET

Oleksii Velychko 29 Nov 21, 2022
Multi-Identifier Router

mir-go 1. Usage 首先调用脚本安装 ./install.sh 然后设置或者修改配置文件中的默认身份 => /usr/local/etc/mir/mirconf.ini 接着调用 mirgen 设置或修改默认身份的密码 验证或者设置: sudo mirgen 修改默认身份的密码: sud

null 10 May 1, 2022
Minlib - Multi-Identifier Network Development Library

minlib 1. Install git clone https://gitea.qjm253.cn/PKUSZ-future-network-lab/min

null 9 Jan 7, 2022
Forms is a fast, powerful, flexible, sortable web form rendering library written in golang.

forms Description forms makes form creation and handling easy. It allows the creation of form without having to write HTML code or bother to make the

coscms 21 Oct 2, 2022
Singlestore event analytics - Evaluation of sortable ID generation schemes

Singlestore event analytics - Evaluation of sortable ID generation schemes

Carl Sverre 1 Jan 25, 2022
A distributed unique ID generator of using Sonyflake and encoded by Base58

Indigo About A distributed unique ID generator of using Sonyflake and encoded by Base58. ID max length is 11 characters by unsigned int64 max value. A

Osamu TONOMORI 98 Nov 24, 2022
Super short, fully unique, non-sequential and URL friendly Ids

Generator of unique non-sequential short Ids The package shortidenables the generation of short, fully unique, non-sequential and by default URL frien

teris.io 831 Dec 30, 2022