storj/satellite/overlay/combinedcache.go

95 lines
2.3 KiB
Go
Raw Normal View History

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package overlay
import (
"context"
"sync"
"storj.io/common/pb"
"storj.io/common/storj"
)
type addressInfo struct {
address string
lastIP string
transport pb.NodeTransport
}
2019-09-10 14:24:16 +01:00
var _ DB = (*CombinedCache)(nil)
// CombinedCache is a simple caching mechanism for overlaycache updates. It
// provdes methods to help reduce calls to UpdateAddress and UpdateTime, but can
// be extended for other calls in the future.
2019-09-10 14:24:16 +01:00
//
// architecture: Service
type CombinedCache struct {
DB
addressLock sync.RWMutex
addressCache map[storj.NodeID]*addressInfo
keyLock *KeyLock
}
// NewCombinedCache instantiates a new CombinedCache
func NewCombinedCache(db DB) *CombinedCache {
return &CombinedCache{
DB: db,
addressCache: make(map[storj.NodeID]*addressInfo),
keyLock: NewKeyLock(),
}
}
// UpdateAddress overrides the underlying db.UpdateAddress and provides a simple
// caching layer to reduce calls to the underlying db. The cache is guaranteed
// to match the values held in the database; however this code does not
// guarantee that concurrent UpdateAddress calls will be handled in any
// particular order.
func (c *CombinedCache) UpdateAddress(ctx context.Context, info *pb.Node, defaults NodeSelectionConfig) (err error) {
// Update internal cache and check if this call requires a db call
if info == nil {
return ErrEmptyNode
}
address := info.Address
if address == nil {
address = &pb.NodeAddress{}
}
c.addressLock.RLock()
cached, ok := c.addressCache[info.Id]
c.addressLock.RUnlock()
if ok &&
address.Address == cached.address &&
address.Transport == cached.transport &&
info.LastIp == cached.lastIP {
return nil
}
// Acquire lock for this node ID. This prevents a concurrent db update to
// this same node ID and guarantees the cache and database stay in sync.
// This solution works so long as calls to this code are occurring within a
// single process.
unlockFunc := c.keyLock.Lock(info.Id)
defer unlockFunc()
err = c.DB.UpdateAddress(ctx, info, defaults)
if err != nil {
return err
}
c.addressLock.Lock()
c.addressCache[info.Id] = &addressInfo{
address: address.Address,
lastIP: info.LastIp,
transport: address.Transport,
}
c.addressLock.Unlock()
return nil
}