Skip to content

Commit 022fab2

Browse files
refactor: enhanced uuid (#1095)
enhance!: better uuids - switch to github.com/google/uuid for uuids - more allocation friendly uuid string function - uuid benchmarks - remove GenerateUUIDOnlyLetters - Close/terminate vault in unit tests Signed-off-by: Marcus Brandenburger <[email protected]> --------- Signed-off-by: Marcus Brandenburger <[email protected]>
1 parent 6cc7726 commit 022fab2

File tree

4 files changed

+113
-63
lines changed

4 files changed

+113
-63
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ require (
143143
github.com/google/gopacket v1.1.19 // indirect
144144
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
145145
github.com/google/s2a-go v0.1.8 // indirect
146-
github.com/google/uuid v1.6.0 // indirect
146+
github.com/google/uuid v1.6.0
147147
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
148148
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
149149
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect

pkg/utils/uuid.go

Lines changed: 15 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,28 @@ SPDX-License-Identifier: Apache-2.0
77
package utils
88

99
import (
10-
"crypto/rand"
11-
"fmt"
12-
"io"
10+
"github.com/google/uuid"
1311
)
1412

15-
var randChar = &poolRandReader{
16-
randReader: rand.Reader,
17-
pool: []byte(`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`),
18-
}
19-
20-
type poolRandReader struct {
21-
randReader io.Reader
22-
pool []byte
23-
}
13+
func init() {
14+
// we enable rand pool for better performance.
15+
// note that the pooled random bytes are stored in heap;
16+
// for more details see uuid.EnableRandPool docs.
2417

25-
func (r *poolRandReader) Read(p []byte) (int, error) {
26-
n, err := r.randReader.Read(p)
27-
if err != nil {
28-
return n, err
29-
}
30-
31-
l := uint8(len(r.pool))
32-
for i := range p {
33-
p[i] = r.pool[p[i]%l]
34-
}
35-
return n, nil
18+
// BenchmarkUUID/google_uuid_(pooled)
19+
// BenchmarkUUID/google_uuid_(pooled)-10 18897972 63.14 ns/op 48 B/op 1 allocs/op
20+
// BenchmarkUUID/google_uuid_(non-pooled)
21+
// BenchmarkUUID/google_uuid_(non-pooled)-10 3658857 326.3 ns/op 64 B/op 2 allocs/op
22+
uuid.EnableRandPool()
3623
}
3724

38-
// GenerateBytesUUID returns a UUID based on RFC 4122 returning the generated bytes
25+
// GenerateBytesUUID creates a new random UUID and returns it as []byte
3926
func GenerateBytesUUID() []byte {
40-
uuid := make([]byte, 16)
41-
_, err := io.ReadFull(rand.Reader, uuid)
42-
if err != nil {
43-
panic(fmt.Sprintf("Error generating UUID: %s", err))
44-
}
45-
46-
// variant bits; see section 4.1.1
47-
uuid[8] = uuid[8]&^0xc0 | 0x80
48-
49-
// version 4 (pseudo-random); see section 4.1.3
50-
uuid[6] = uuid[6]&^0xf0 | 0x40
51-
52-
return uuid
27+
u := uuid.New()
28+
return u[:]
5329
}
5430

55-
// GenerateUUID returns a UUID based on RFC 4122
31+
// GenerateUUID creates a new random UUID and returns it as a string
5632
func GenerateUUID() string {
57-
uuid := GenerateBytesUUID()
58-
return idBytesToStr(uuid)
59-
}
60-
61-
func idBytesToStr(id []byte) string {
62-
return fmt.Sprintf("%x-%x-%x-%x-%x", id[0:4], id[4:6], id[6:8], id[8:10], id[10:])
63-
}
64-
65-
// GenerateUUIDOnlyLetters returns a UUID without digits
66-
func GenerateUUIDOnlyLetters() string {
67-
uuid := make([]byte, 16)
68-
if _, err := io.ReadFull(randChar, uuid); err != nil {
69-
panic(err)
70-
}
71-
return string(uuid)
33+
return uuid.NewString()
7234
}

pkg/utils/uuid_test.go

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,85 @@ SPDX-License-Identifier: Apache-2.0
77
package utils
88

99
import (
10+
"crypto/rand"
11+
"fmt"
12+
"io"
1013
"testing"
1114

12-
"github.com/stretchr/testify/assert"
15+
"github.com/google/uuid"
1316
)
1417

15-
func TestUUIDLettersOnly(t *testing.T) {
16-
assert.Regexp(t, "^[a-zA-Z]{16}$", GenerateUUIDOnlyLetters())
18+
func BenchmarkUUID(b *testing.B) {
19+
// generateUUIDv1 is our reference impl for rand UUID based on previous version of this code
20+
oldGenerateUUID := func() string {
21+
uuid := make([]byte, 16)
22+
23+
_, err := io.ReadFull(rand.Reader, uuid[:])
24+
if err != nil {
25+
panic(fmt.Sprintf("Error generating UUID: %s", err))
26+
}
27+
28+
// variant bits; see section 4.1.1
29+
uuid[8] = uuid[8]&^0xc0 | 0x80
30+
31+
// version 4 (pseudo-random); see section 4.1.3
32+
uuid[6] = uuid[6]&^0xf0 | 0x40
33+
34+
id := uuid
35+
36+
return fmt.Sprintf("%x-%x-%x-%x-%x", id[0:4], id[4:6], id[6:8], id[8:10], id[10:])
37+
}
38+
39+
b.Run("oldGenerateUUID", func(b *testing.B) {
40+
report(b)
41+
for b.Loop() {
42+
_ = oldGenerateUUID()
43+
}
44+
})
45+
46+
b.Run("GenerateBytesUUID", func(b *testing.B) {
47+
report(b)
48+
for b.Loop() {
49+
_ = GenerateBytesUUID()
50+
}
51+
})
52+
53+
b.Run("GenerateUUID", func(b *testing.B) {
54+
report(b)
55+
for b.Loop() {
56+
_ = GenerateUUID()
57+
}
58+
})
59+
60+
b.Run("GenerateUUID (non-pooled)", func(b *testing.B) {
61+
report(b)
62+
uuid.DisableRandPool()
63+
for b.Loop() {
64+
_ = GenerateUUID()
65+
}
66+
})
67+
68+
b.Run("GenerateUUID parallel", func(b *testing.B) {
69+
report(b)
70+
uuid.EnableRandPool()
71+
b.RunParallel(func(pb *testing.PB) {
72+
for pb.Next() {
73+
_ = GenerateUUID()
74+
}
75+
})
76+
})
77+
78+
b.Run("GenerateUUID parallel (non-pooled)", func(b *testing.B) {
79+
report(b)
80+
uuid.DisableRandPool()
81+
b.RunParallel(func(pb *testing.PB) {
82+
for pb.Next() {
83+
_ = GenerateUUID()
84+
}
85+
})
86+
})
87+
}
88+
89+
func report(b *testing.B) {
90+
b.ReportAllocs()
1791
}

platform/fabric/services/storage/vault/vaultstore_test.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"context"
1111
"testing"
1212

13-
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils"
1413
"github.com/hyperledger-labs/fabric-smart-client/platform/common/driver"
1514
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/collections"
1615
q "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/storage/driver/sql/query"
@@ -265,9 +264,12 @@ func testPagination(store driver.VaultStore) {
265264

266265
func TestPaginationStoreMem(t *testing.T) {
267266
RegisterTestingT(t)
268-
db, err := OpenMemoryVault(utils.GenerateUUIDOnlyLetters())
267+
db, err := OpenMemoryVault("testdb")
269268
assert.NoError(t, err)
270269
assert.NotNil(t, db)
270+
t.Cleanup(func() {
271+
_ = db.Close()
272+
})
271273

272274
testPagination(db)
273275
}
@@ -277,6 +279,9 @@ func TestPaginationStoreSqlite(t *testing.T) {
277279
db, err := OpenSqliteVault("testdb", t.TempDir())
278280
assert.NoError(t, err)
279281
assert.NotNil(t, db)
282+
t.Cleanup(func() {
283+
_ = db.Close()
284+
})
280285

281286
testPagination(db)
282287
}
@@ -286,16 +291,19 @@ func TestPaginationStoreSPostgres(t *testing.T) {
286291
db, terminate, err := OpenPostgresVault("testdb")
287292
assert.NoError(t, err)
288293
assert.NotNil(t, db)
289-
defer terminate()
294+
t.Cleanup(terminate)
290295

291296
testPagination(db)
292297
}
293298

294299
func TestVaultStoreMem(t *testing.T) {
295300
RegisterTestingT(t)
296-
db, err := OpenMemoryVault(utils.GenerateUUIDOnlyLetters())
301+
db, err := OpenMemoryVault("testdb")
297302
assert.NoError(t, err)
298303
assert.NotNil(t, db)
304+
t.Cleanup(func() {
305+
_ = db.Close()
306+
})
299307

300308
testVaultStore(t, db)
301309
testOneMore(t, db)
@@ -306,8 +314,10 @@ func TestVaultStoreSqlite(t *testing.T) {
306314
db, err := OpenSqliteVault("testdb", t.TempDir())
307315
assert.NoError(t, err)
308316
assert.NotNil(t, db)
317+
t.Cleanup(func() {
318+
_ = db.Close()
319+
})
309320

310-
assert.NotNil(t, db)
311321
testVaultStore(t, db)
312322
testOneMore(t, db)
313323
}
@@ -317,13 +327,15 @@ func TestVaultStorePostgres(t *testing.T) {
317327
db, terminate, err := OpenPostgresVault("testdb")
318328
assert.NoError(t, err)
319329
assert.NotNil(t, db)
320-
defer terminate()
330+
t.Cleanup(terminate)
321331

322332
testVaultStore(t, db)
323333
testOneMore(t, db)
324334
}
325335

326336
func testOneMore(t *testing.T, store driver.VaultStore) {
337+
t.Helper()
338+
327339
err := store.SetStatuses(context.Background(), driver.TxStatusCode(valid), "", "txid3")
328340
assert.NoError(t, err)
329341

@@ -383,6 +395,8 @@ func fetchAll(store driver.VaultStore) ([]driver.TxID, error) {
383395
}
384396

385397
func testVaultStore(t *testing.T, store driver.VaultStore) {
398+
t.Helper()
399+
386400
txids, err := fetchAll(store)
387401
assert.NoError(t, err)
388402
assert.Empty(t, txids)

0 commit comments

Comments
 (0)