2019-08-06 23:56:12 +01:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package overlay
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"sync"
|
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/pb"
|
|
|
|
"storj.io/common/storj"
|
2019-08-06 23:56:12 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type addressInfo struct {
|
|
|
|
address string
|
|
|
|
lastIP string
|
|
|
|
transport pb.NodeTransport
|
|
|
|
}
|
|
|
|
|
2019-09-10 14:24:16 +01:00
|
|
|
var _ DB = (*CombinedCache)(nil)
|
|
|
|
|
2019-08-06 23:56:12 +01:00
|
|
|
// 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
|
2019-08-06 23:56:12 +01:00
|
|
|
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
|
|
|
|
}
|