Skip to content

Commit 97e59a2

Browse files
authored
introduce nocache and ristretto-based cache #1173 (#1174)
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
1 parent 3d81881 commit 97e59a2

File tree

10 files changed

+386
-42
lines changed

10 files changed

+386
-42
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/IBM/idemix v0.0.2-0.20250313153527-832db18b9478
1010
github.com/IBM/idemix/bccsp/types v0.0.0-20250313153527-832db18b9478
1111
github.com/IBM/mathlib v0.0.3-0.20241219051532-81539b287cf5
12+
github.com/dgraph-io/ristretto/v2 v2.2.0
1213
github.com/gin-gonic/gin v1.10.0
1314
github.com/gobuffalo/packr/v2 v2.7.1
1415
github.com/hashicorp/go-uuid v1.0.3
@@ -34,6 +35,7 @@ require (
3435
go.uber.org/dig v1.18.0
3536
go.uber.org/zap v1.27.0
3637
golang.org/x/crypto v0.38.0
38+
golang.org/x/sync v0.14.0
3739
google.golang.org/protobuf v1.36.6
3840
gopkg.in/yaml.v2 v2.4.0
3941
modernc.org/sqlite v1.33.1
@@ -274,7 +276,6 @@ require (
274276
golang.org/x/mod v0.24.0 // indirect
275277
golang.org/x/net v0.40.0 // indirect
276278
golang.org/x/oauth2 v0.27.0 // indirect
277-
golang.org/x/sync v0.14.0 // indirect
278279
golang.org/x/sys v0.33.0 // indirect
279280
golang.org/x/text v0.25.0 // indirect
280281
golang.org/x/time v0.8.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il
761761
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
762762
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
763763
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
764+
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
765+
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
766+
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
767+
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
764768
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
765769
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
766770
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=

token/services/db/sql/common/identity.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
tdriver "github.com/hyperledger-labs/fabric-token-sdk/token/driver"
2626
"github.com/hyperledger-labs/fabric-token-sdk/token/services/db/driver"
2727
driver3 "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity/driver"
28+
cache2 "github.com/hyperledger-labs/fabric-token-sdk/token/services/utils/cache"
2829
"github.com/pkg/errors"
2930
)
3031

@@ -73,6 +74,17 @@ func NewCachedIdentityStore(readDB, writeDB *sql.DB, tables TableNames, ci commo
7374
)
7475
}
7576

77+
func NewNoCacheIdentityStore(readDB, writeDB *sql.DB, tables TableNames, ci common3.CondInterpreter) (*IdentityStore, error) {
78+
return NewIdentityStore(
79+
readDB,
80+
writeDB,
81+
tables,
82+
cache2.NewNoCache[bool](),
83+
cache2.NewNoCache[[]byte](),
84+
ci,
85+
)
86+
}
87+
7688
func NewIdentityStore(readDB, writeDB *sql.DB, tables TableNames, signerInfoCache cache[bool], auditInfoCache cache[[]byte], ci common3.CondInterpreter) (*IdentityStore, error) {
7789
return newIdentityStore(
7890
readDB,

token/services/identity/provider.go

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import (
1010
"context"
1111
"runtime/debug"
1212
"slices"
13-
"sync"
1413

14+
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils/cache/secondcache"
1515
"github.com/hyperledger-labs/fabric-token-sdk/token/driver"
1616
idriver "github.com/hyperledger-labs/fabric-token-sdk/token/services/identity/driver"
1717
"github.com/hyperledger-labs/fabric-token-sdk/token/services/logging"
@@ -46,6 +46,11 @@ type storage interface {
4646
StoreIdentityData(ctx context.Context, id []byte, identityAudit []byte, tokenMetadata []byte, tokenMetadataAudit []byte) error
4747
}
4848

49+
type cache[T any] interface {
50+
Get(key string) (T, bool)
51+
Add(key string, value T)
52+
}
53+
4954
// Provider implements the driver.IdentityProvider interface.
5055
// Provider handles the long-term identities on top of which wallets are defined.
5156
type Provider struct {
@@ -55,8 +60,7 @@ type Provider struct {
5560
Storage storage
5661

5762
enrollmentIDUnmarshaler enrollmentIDUnmarshaler
58-
isMeCacheLock sync.RWMutex
59-
isMeCache map[string]bool
63+
isMeCache cache[bool]
6064
}
6165

6266
// NewProvider creates a new identity provider implementing the driver.IdentityProvider interface.
@@ -74,7 +78,7 @@ func NewProvider(
7478
SigService: sigService,
7579
Binder: binder,
7680
enrollmentIDUnmarshaler: enrollmentIDUnmarshaler,
77-
isMeCache: make(map[string]bool),
81+
isMeCache: secondcache.NewTyped[bool](1000),
7882
}
7983
}
8084

@@ -96,9 +100,7 @@ func (p *Provider) RegisterRecipientData(ctx context.Context, data *driver.Recip
96100

97101
func (p *Provider) RegisterSigner(ctx context.Context, identity driver.Identity, signer driver.Signer, verifier driver.Verifier, signerInfo []byte) error {
98102
defer func() {
99-
p.isMeCacheLock.Lock()
100-
p.isMeCache[identity.String()] = true
101-
p.isMeCacheLock.Unlock()
103+
p.isMeCache.Add(identity.UniqueID(), true)
102104
}()
103105
return p.SigService.RegisterSigner(ctx, identity, signer, verifier, signerInfo)
104106
}
@@ -109,29 +111,22 @@ func (p *Provider) AreMe(ctx context.Context, identities ...driver.Identity) []s
109111
result := make([]string, 0)
110112
notFound := make([]driver.Identity, 0)
111113

112-
p.isMeCacheLock.RLock()
113114
for _, id := range identities {
114-
if isMe, ok := p.isMeCache[id.UniqueID()]; !ok {
115+
uniqueID := id.UniqueID()
116+
if isMe, ok := p.isMeCache.Get(uniqueID); !ok {
115117
notFound = append(notFound, id)
116118
} else if isMe {
117-
result = append(result, id.UniqueID())
119+
result = append(result, uniqueID)
118120
}
119121
}
120122
if len(notFound) == 0 {
121-
defer p.isMeCacheLock.RUnlock()
122123
return result
123124
}
124-
p.isMeCacheLock.RUnlock()
125-
126-
p.isMeCacheLock.Lock()
127-
128-
// TODO: Look up cache under write lock
129-
130-
defer p.isMeCacheLock.Unlock()
131125

132126
found := p.SigService.AreMe(ctx, notFound...)
133127
for _, id := range notFound {
134-
p.isMeCache[id.UniqueID()] = slices.Contains(found, id.UniqueID())
128+
uniqueID := id.UniqueID()
129+
p.isMeCache.Add(uniqueID, slices.Contains(found, uniqueID))
135130
}
136131
return append(result, found...)
137132
}
@@ -142,18 +137,14 @@ func (p *Provider) IsMe(ctx context.Context, identity driver.Identity) bool {
142137

143138
func (p *Provider) RegisterRecipientIdentity(id driver.Identity) error {
144139
p.Logger.Debugf("Registering identity [%s]", id)
145-
p.isMeCacheLock.Lock()
146-
p.isMeCache[id.String()] = false
147-
p.isMeCacheLock.Unlock()
140+
p.isMeCache.Add(id.UniqueID(), false)
148141
return nil
149142
}
150143

151144
func (p *Provider) GetSigner(ctx context.Context, identity driver.Identity) (driver.Signer, error) {
152145
found := false
153146
defer func() {
154-
p.isMeCacheLock.Lock()
155-
p.isMeCache[identity.String()] = found
156-
p.isMeCacheLock.Unlock()
147+
p.isMeCache.Add(identity.UniqueID(), found)
157148
}()
158149
signer, err := p.SigService.GetSigner(ctx, identity)
159150
if err != nil {

token/services/network/common/finality.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func (t *FinalityListener) runOnStatus(ctx context.Context, txID string, status
7070
txStatus = driver.Confirmed
7171
t.logger.DebugfContext(ctx, "get token request for [%s]", txID)
7272

73-
tr := t.tokens.GetCachedTokenRequest(txID)
73+
tr, msgToSign := t.tokens.GetCachedTokenRequest(txID)
7474
if tr == nil {
7575
// load it
7676
tokenRequestRaw, err := t.ttxDB.GetTokenRequest(ctx, txID)
@@ -87,9 +87,13 @@ func (t *FinalityListener) runOnStatus(ctx context.Context, txID string, status
8787
if err != nil {
8888
return fmt.Errorf("failed retrieving token request [%s]: [%w]", txID, err)
8989
}
90+
msgToSign, err = tr.MarshalToSign()
91+
if err != nil {
92+
return fmt.Errorf("failed retrieving token request [%s]: [%w]", txID, err)
93+
}
9094
}
9195
t.logger.DebugfContext(ctx, "Check token request")
92-
if err := t.checkTokenRequest(txID, tr, tokenRequestHash); err != nil {
96+
if err := t.checkTokenRequest(txID, msgToSign, tokenRequestHash); err != nil {
9397
t.logger.ErrorfContext(ctx, "tx [%s], %s", txID, err)
9498
txStatus = driver.Deleted
9599
message = err.Error()
@@ -113,11 +117,7 @@ func (t *FinalityListener) runOnStatus(ctx context.Context, txID string, status
113117
return nil
114118
}
115119

116-
func (t *FinalityListener) checkTokenRequest(txID string, request *token.Request, reference []byte) error {
117-
trToSign, err := request.MarshalToSign()
118-
if err != nil {
119-
return errors.Errorf("can't get request hash '%s'", txID)
120-
}
120+
func (t *FinalityListener) checkTokenRequest(txID string, trToSign []byte, reference []byte) error {
121121
if base64.StdEncoding.EncodeToString(reference) != hash.Hashable(trToSign).String() {
122122
t.logger.Errorf("tx [%s], tr hashes [%s][%s]", txID, base64.StdEncoding.EncodeToString(reference), hash.Hashable(trToSign))
123123
// no further processing of the tokens of these transactions

token/services/tokens/tokens.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ type Cache interface {
4646
}
4747

4848
type CacheEntry struct {
49-
Request *token.Request
50-
ToSpend []*token2.ID
51-
ToAppend []TokenToAppend
49+
Request *token.Request
50+
ToSpend []*token2.ID
51+
ToAppend []TokenToAppend
52+
MsgToSign []byte
5253
}
5354

5455
// Service is the interface for the token service
@@ -147,20 +148,25 @@ func (t *Service) CacheRequest(ctx context.Context, tmsID token.TMSID, request *
147148
}
148149
logger.DebugfContext(ctx, "cache request [%s]", request.ID())
149150
// append to cache
151+
msgToSign, err := request.MarshalToSign()
152+
if err != nil {
153+
return errors.WithMessagef(err, "failed to marshal token request [%s]", request.ID())
154+
}
150155
t.RequestsCache.Add(string(request.Anchor), &CacheEntry{
151-
Request: request,
152-
ToSpend: toSpend,
153-
ToAppend: toAppend,
156+
Request: request,
157+
ToSpend: toSpend,
158+
ToAppend: toAppend,
159+
MsgToSign: msgToSign,
154160
})
155161
return nil
156162
}
157163

158-
func (t *Service) GetCachedTokenRequest(txID string) *token.Request {
164+
func (t *Service) GetCachedTokenRequest(txID string) (*token.Request, []byte) {
159165
res, ok := t.RequestsCache.Get(txID)
160166
if !ok {
161-
return nil
167+
return nil, nil
162168
}
163-
return res.Request
169+
return res.Request, res.MsgToSign
164170
}
165171

166172
// AppendTransaction appends the content of the passed transaction to the token db.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package cache
8+
9+
import (
10+
"github.com/hyperledger-labs/fabric-smart-client/platform/common/utils"
11+
)
12+
13+
// NoCache implements a dummy cache that does nothing
14+
type NoCache[T any] struct {
15+
}
16+
17+
// NewNoCache returns a new instance of NoCache
18+
func NewNoCache[T any]() *NoCache[T] {
19+
return &NoCache[T]{}
20+
}
21+
22+
func (n *NoCache[T]) Get(key string) (T, bool) {
23+
return utils.Zero[T](), false
24+
}
25+
26+
func (n *NoCache[T]) GetOrLoad(key string, loader func() (T, error)) (T, bool, error) {
27+
v, err := loader()
28+
return v, false, err
29+
}
30+
31+
func (n *NoCache[T]) Add(key string, value T) {
32+
}
33+
34+
func (n *NoCache[T]) Delete(key string) {
35+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package cache
8+
9+
import (
10+
"errors"
11+
"testing"
12+
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func TestNoCache_Get_Int(t *testing.T) {
17+
cache := NewNoCache[int]()
18+
19+
val, found := cache.Get("pineapple")
20+
21+
require.False(t, found)
22+
require.Equal(t, 0, val)
23+
}
24+
25+
func TestNoCache_Get_String(t *testing.T) {
26+
cache := NewNoCache[string]()
27+
28+
val, found := cache.Get("pineapple")
29+
30+
require.False(t, found)
31+
require.Equal(t, "", val)
32+
}
33+
34+
func TestNoCache_GetOrLoad_Success(t *testing.T) {
35+
cache := NewNoCache[string]()
36+
37+
loader := func() (string, error) {
38+
return "loaded", nil
39+
}
40+
41+
val, found, err := cache.GetOrLoad("key", loader)
42+
43+
require.NoError(t, err)
44+
require.False(t, found)
45+
require.Equal(t, "loaded", val)
46+
}
47+
48+
func TestNoCache_GetOrLoad_Error(t *testing.T) {
49+
cache := NewNoCache[float64]()
50+
51+
expectedErr := errors.New("load failed")
52+
53+
loader := func() (float64, error) {
54+
return 0.0, expectedErr
55+
}
56+
57+
val, found, err := cache.GetOrLoad("key", loader)
58+
59+
require.ErrorIs(t, err, expectedErr)
60+
require.False(t, found)
61+
require.Equal(t, 0.0, val)
62+
}
63+
64+
func TestNoCache_Add_DoesNothing(t *testing.T) {
65+
cache := NewNoCache[bool]()
66+
67+
cache.Add("key", true)
68+
69+
val, found := cache.Get("key")
70+
require.False(t, found)
71+
require.Equal(t, false, val)
72+
}
73+
74+
func TestNoCache_Delete_DoesNothing(t *testing.T) {
75+
cache := NewNoCache[string]()
76+
77+
cache.Delete("key")
78+
79+
val, found := cache.Get("key")
80+
require.False(t, found)
81+
require.Equal(t, "", val)
82+
}

0 commit comments

Comments
 (0)