Generating UUID in Golang and Insert Read ID's using BBolt DB.
![]() |
UUID is the most commonly used in backend development. for generating unique ids in the system for different components. to uniquely identify each component. read more about UUID
In this article, we will see how to generate a UUID in golang based on user input.
user will provide how many character id want to generate and how many id's want to create.
I create it has a package you can directly use it.
code is self-explained with comments for each function.
this is more practical rather than theory.
The small project contains.
1. Generating UUID based on Base62 algorithm you can use more efficient third party packages for the same some examples. I keep STD package
2. Putting ( inserting generated ids into a bucket ( we are using bbolt database) sequential write.
3. Checking Duplicate ID's While inserting make sure there should not a duplicate id we know it should be unique but for the sanity check also good to test (but time-consuming)
4. Reading IDs from Bucket ( Sequential Read its super-fast as compare to other databases)
BBolt is Key-Value database can read more BBolt DB
5.Unit Test and Benchmark Test
2. Putting ( inserting generated ids into a bucket ( we are using bbolt database) sequential write.
3. Checking Duplicate ID's While inserting make sure there should not a duplicate id we know it should be unique but for the sanity check also good to test (but time-consuming)
4. Reading IDs from Bucket ( Sequential Read its super-fast as compare to other databases)
BBolt is Key-Value database can read more BBolt DB
5.Unit Test and Benchmark Test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package db | |
import "go.etcd.io/bbolt" | |
// BBoltDB stucture | |
type BBoltDB struct { | |
DB *bbolt.DB | |
} | |
// Connection opens a new connection | |
// It requires db path and it returns databse object and error | |
func Connection(dbPath string) (*BBoltDB, error) { | |
db, err := bbolt.Open(dbPath+"/uuid.db", 0600, nil) | |
if err != nil { | |
return nil, err | |
} | |
return &BBoltDB{DB: db}, nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package db | |
import ( | |
"bytes" | |
"fmt" | |
"github.com/amolasg/golang-programs/generating-uid-in-go/id" | |
"go.etcd.io/bbolt" | |
) | |
// UUIDBucket as Root Bucket | |
// DBConnectionPath is a Database path. | |
const ( | |
UUIDBucket RootBucket = "uuid" | |
DBConnectionPath string = "/home/amol/Desktop/databases" | |
) | |
// RootBucket its type used for Bucket | |
type RootBucket string | |
// checkDuplicate - Check Is any duplicate UUID at time of inserting an UUIDs | |
// It requires Cursor for iterate over bucket with an new UUID for comparing | |
// It returns a true or false | |
func checkDuplicate(c *bbolt.Cursor, newUUID string) bool { | |
for k, v := c.First(); k != nil; k, v = c.Next() { | |
if bytes.Equal([]byte(newUUID), v) { | |
fmt.Println("duplicated key found", newUUID) | |
return true | |
} | |
} | |
return false | |
} | |
// Get - It displays (Reterive) an UUIDs which are in a bucket. | |
// It Requires RootBucket which contains a Key(UUIDs) | |
// It returns an error | |
func (b *BBoltDB) Get(rootbucket RootBucket) (err error) { | |
db := b.DB | |
if err := db.View(func(tx *bbolt.Tx) error { | |
bucket := tx.Bucket([]byte(rootbucket)) | |
if bucket == nil { | |
return fmt.Errorf("Bucket %v not found", rootbucket) | |
} | |
if err := bucket.ForEach(func(k, v []byte) error { | |
fmt.Printf("key=%s, value=%s\n", k, v) | |
return nil | |
}); err != nil { | |
return err | |
} | |
return nil | |
}); err != nil { | |
return err | |
} | |
return nil | |
} | |
// Put - Inserts an UUID into a Bucket. | |
// It Require a Bucket rootbucket,no of maxChar and No of records as Param and returns an error. | |
func (b *BBoltDB) Put(rootbucket RootBucket, maxChar, records int) (err error) { | |
db := b.DB | |
for i := 0; i < records; i++ { | |
newUUID := id.GetNewID(maxChar) | |
if err := db.Update(func(tx *bbolt.Tx) error { | |
// Check bucket is exist or not | |
bucket, err := tx.CreateBucketIfNotExists([]byte(rootbucket)) | |
if err != nil { | |
return err | |
} | |
// Validate duplicate uuid | |
if !checkDuplicate(bucket.Cursor(), newUUID) { | |
err = bucket.Put([]byte(newUUID), []byte(newUUID)) | |
if err != nil { | |
return err | |
} | |
} | |
return nil | |
}); err != nil { | |
return err | |
} | |
} | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package db | |
import ( | |
"log" | |
"testing" | |
"go.etcd.io/bbolt" | |
) | |
func NewTestConnection() *BBoltDB { | |
conn, err := bbolt.Open("/home/amol/Desktop/testDatabases/testDb.db", 0644, nil) | |
if err != nil { | |
log.Panic(err) | |
} | |
return &BBoltDB{DB: conn} | |
} | |
func TestBBoltDB_Put(t *testing.T) { | |
testDbConn := NewTestConnection() | |
defer testDbConn.DB.Close() | |
type fields struct { | |
DB *bbolt.DB | |
} | |
type args struct { | |
name RootBucket | |
maxChar int | |
record int | |
} | |
tests := []struct { | |
name string | |
fields fields | |
args args | |
wantErr bool | |
}{ | |
{ | |
name: "test", | |
fields: fields{ | |
DB: testDbConn.DB, | |
}, | |
args: args{ | |
name: "testBucket", | |
maxChar: 8, | |
record: 1000, | |
}, | |
wantErr: false, | |
}, | |
{ | |
name: "test", | |
fields: fields{ | |
DB: testDbConn.DB, | |
}, | |
args: args{ | |
name: "testBucket", | |
maxChar: 16, | |
record: 100, | |
}, | |
wantErr: false, | |
}, | |
{ | |
name: "test", | |
fields: fields{ | |
DB: testDbConn.DB, | |
}, | |
args: args{ | |
name: "testBucket", | |
maxChar: 22, | |
record: 1000, | |
}, | |
wantErr: false, | |
}, | |
} | |
for _, tt := range tests { | |
t.Run(tt.name, func(t *testing.T) { | |
b := &BBoltDB{ | |
DB: tt.fields.DB, | |
} | |
if err := b.Put(tt.args.name, tt.args.maxChar, tt.args.record); (err != nil) != tt.wantErr { | |
t.Errorf("BBoltDB.Put() error = %v, wantErr %v", err, tt.wantErr) | |
} | |
if err := b.Get(tt.args.name); (err != nil) != tt.wantErr { | |
t.Errorf("BBoltDB.Get() error = %v, wantErr %v", err, tt.wantErr) | |
} | |
}) | |
} | |
} | |
// Benchmark | |
func BenchmarkTestBBoltDB_Put(b *testing.B) { | |
for n := 0; n < b.N; n++ { | |
testDbConn := NewTestConnection() | |
defer testDbConn.DB.Close() | |
type fields struct { | |
DB *bbolt.DB | |
} | |
type args struct { | |
name RootBucket | |
maxChar int | |
record int | |
} | |
tests := []struct { | |
name string | |
fields fields | |
args args | |
wantErr bool | |
}{ | |
{ | |
name: "test", | |
fields: fields{ | |
DB: testDbConn.DB, | |
}, | |
args: args{ | |
name: "testBucket", | |
}, | |
wantErr: false, | |
}, | |
} | |
for _, tt := range tests { | |
b.Run(tt.name, func(b *testing.B) { | |
bb := &BBoltDB{ | |
DB: tt.fields.DB, | |
} | |
if err := bb.Put(tt.args.name, tt.args.maxChar, tt.args.record); (err != nil) != tt.wantErr { | |
b.Errorf("BBoltDB.Put() error = %v, wantErr %v", err, tt.wantErr) | |
} | |
if err := bb.Get(tt.args.name); (err != nil) != tt.wantErr { | |
b.Errorf("BBoltDB.Get() error = %v, wantErr %v", err, tt.wantErr) | |
} | |
}) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package id | |
// GetNewID is public function can access anywhere | |
// it gives an new unique id | |
func GetNewID(maxChar int) string { | |
return newID(maxChar) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package id | |
import ( | |
"bytes" | |
"math/rand" | |
"time" | |
) | |
// Constants | |
const ( | |
alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
base62 = 62 | |
) | |
// GetNewID - Gives a new id based on param | |
// maxChar is provided by command line arg, to generate that much long Char id. | |
func newID(maxChar int) string { | |
currentID := make([]int, maxChar) | |
rand.Seed(time.Now().UTC().UnixNano()) | |
for j := maxChar - 1; j >= 0; j-- { | |
currentID[j] = rand.Intn(base62) | |
} | |
var buffer bytes.Buffer | |
// Encodes each integer in the current array to a base62 token | |
// and adds it to the buffer | |
for i := 0; i < maxChar; i++ { | |
buffer.WriteString(encode(currentID[i])) | |
} | |
return buffer.String() | |
} | |
// Converts int to base62 token | |
func encode(number int) string { | |
if number == 0 { | |
return string(alphabet[0]) | |
} | |
chars := make([]byte, 0) | |
length := len(alphabet) | |
for number > 0 { | |
result := number / length | |
remainder := number % length | |
chars = append(chars, alphabet[remainder]) | |
number = result | |
} | |
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { | |
chars[i], chars[j] = chars[j], chars[i] | |
} | |
return string(chars) | |
} |
Contributor @Ashish_Nikalje
https://www.linkedin.com/in/ashish-nikalje-815858122/
Comments
Post a Comment