Skip to content

Commit

Permalink
[FAB-5880] MSP cache support
Browse files Browse the repository at this point in the history
This change-set does the following:
- it introduces an MSP wrapper that intercepts
the validation functions and cache their results.
LRU caches are used.
- adds tests to validate the implementation

This change-set is a joint work with Senthil Nathan.

Change-Id: I2027d6ab4331b4923bb00e961ef15a38ce4c8e8b
Signed-off-by: Angelo De Caro <[email protected]>
  • Loading branch information
adecaro committed Aug 28, 2017
1 parent 80dc620 commit 85cb1bd
Show file tree
Hide file tree
Showing 10 changed files with 850 additions and 5 deletions.
8 changes: 7 additions & 1 deletion common/config/channel/msp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"sync"

"github.com/hyperledger/fabric/msp"
"github.com/hyperledger/fabric/msp/cache"
mspprotos "github.com/hyperledger/fabric/protos/msp"
)

Expand Down Expand Up @@ -96,7 +97,12 @@ func (bh *MSPConfigHandler) ProposeMSP(tx interface{}, mspConfig *mspprotos.MSPC
}

// create the msp instance
mspInst, err := msp.NewBccspMsp()
bccspMSP, err := msp.NewBccspMsp()
if err != nil {
return nil, fmt.Errorf("Creating the MSP manager failed, err %s", err)
}

mspInst, err := cache.New(bccspMSP)
if err != nil {
return nil, fmt.Errorf("Creating the MSP manager failed, err %s", err)
}
Expand Down
8 changes: 7 additions & 1 deletion common/config/channel/msp/config_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/hyperledger/fabric/protos/utils"

"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/msp/cache"
)

var logger = flogging.MustGetLogger("configvalues/msp")
Expand Down Expand Up @@ -51,7 +52,12 @@ func TemplateGroupMSPWithAdminRolePrincipal(configPath []string, mspConfig *mspp
}

// create the msp instance
mspInst, err := msp.NewBccspMsp()
bccspMSP, err := msp.NewBccspMsp()
if err != nil {
logger.Panicf("Creating the MSP manager failed, err %s", err)
}

mspInst, err := cache.New(bccspMSP)
if err != nil {
logger.Panicf("Creating the MSP manager failed, err %s", err)
}
Expand Down
138 changes: 138 additions & 0 deletions msp/cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package cache

import (
"fmt"

"sync"

"github.com/golang/groupcache/lru"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/msp"
pmsp "github.com/hyperledger/fabric/protos/msp"
)

const (
deserializeIdentityCacheSize = 100
validateIdentityCacheSize = 100
satisfiesPrincipalCacheSize = 100
)

var mspLogger = flogging.MustGetLogger("msp")

func New(o msp.MSP) (msp.MSP, error) {
mspLogger.Debugf("Creating Cache-MSP instance")
if o == nil {
return nil, fmt.Errorf("Invalid passed MSP. It must be different from nil.")
}

theMsp := &cachedMSP{MSP: o}
theMsp.deserializeIdentityCache = lru.New(deserializeIdentityCacheSize)
theMsp.satisfiesPrincipalCache = lru.New(satisfiesPrincipalCacheSize)
theMsp.validateIdentityCache = lru.New(validateIdentityCacheSize)

return theMsp, nil
}

type cachedMSP struct {
msp.MSP

// cache for DeserializeIdentity.
deserializeIdentityCache *lru.Cache

dicMutex sync.RWMutex // synchronize access to cache

// cache for validateIdentity
validateIdentityCache *lru.Cache

vicMutex sync.RWMutex // synchronize access to cache

// basically a map of principals=>identities=>stringified to booleans
// specifying whether this identity satisfies this principal
satisfiesPrincipalCache *lru.Cache

spcMutex sync.RWMutex // synchronize access to cache
}

func (c *cachedMSP) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
c.dicMutex.RLock()
cached, ok := c.deserializeIdentityCache.Get(string(serializedIdentity))
c.dicMutex.RUnlock()
if ok {
return cached.(msp.Identity), nil
}

id, err := c.MSP.DeserializeIdentity(serializedIdentity)
if err == nil {
c.dicMutex.Lock()
defer c.dicMutex.Unlock()
c.deserializeIdentityCache.Add(string(serializedIdentity), id)
}
return id, err
}

func (c *cachedMSP) Setup(config *pmsp.MSPConfig) error {
c.cleanCash()

return c.MSP.Setup(config)
}

func (c *cachedMSP) Validate(id msp.Identity) error {
identifier := id.GetIdentifier()
key := string(identifier.Mspid + ":" + identifier.Id)

c.vicMutex.RLock()
_, ok := c.validateIdentityCache.Get(key)
c.vicMutex.RUnlock()
if ok {
// cache only stores if the identity is valid.
return nil
}

err := c.MSP.Validate(id)
if err == nil {
c.vicMutex.Lock()
defer c.vicMutex.Unlock()
c.validateIdentityCache.Add(key, true)
}

return err
}

func (c *cachedMSP) SatisfiesPrincipal(id msp.Identity, principal *pmsp.MSPPrincipal) error {
identifier := id.GetIdentifier()
identityKey := string(identifier.Mspid + ":" + identifier.Id)
principalKey := string(principal.PrincipalClassification) + string(principal.Principal)
key := identityKey + principalKey

c.spcMutex.RLock()
v, ok := c.satisfiesPrincipalCache.Get(key)
c.spcMutex.RUnlock()
if ok {
if v == nil {
return nil
}

return v.(error)
}

err := c.MSP.SatisfiesPrincipal(id, principal)

c.spcMutex.Lock()
defer c.spcMutex.Unlock()
c.satisfiesPrincipalCache.Add(key, err)
return err
}

func (c *cachedMSP) cleanCash() error {
c.deserializeIdentityCache = lru.New(deserializeIdentityCacheSize)
c.satisfiesPrincipalCache = lru.New(satisfiesPrincipalCacheSize)
c.validateIdentityCache = lru.New(validateIdentityCacheSize)

return nil
}
Loading

0 comments on commit 85cb1bd

Please sign in to comment.