Skip to content

Commit

Permalink
node: Do not update cache twice
Browse files Browse the repository at this point in the history
Do not request morph values on morph cache misses concurrently.
Closes #1248.

Signed-off-by: Pavel Karpy <[email protected]>
  • Loading branch information
carpawell committed Jul 10, 2023
1 parent 967e273 commit aecce1b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Changelog for NeoFS Node

### Fixed
- `neo-go` RPC connection lost handling by IR (#1337)
- Concurrent morph cache misses (#1248)

### Removed
- Deprecated `morph.rpc_endpoint` SN and `morph.endpoint.client` IR config sections (#2400)
Expand Down
59 changes: 53 additions & 6 deletions cmd/neofs-node/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,19 @@ type valueWithTime[V any] struct {
e error
}

// valueInProgress is a struct that contains
// values that are being fetched/updated.
type valueInProgress[V any] struct {
m sync.RWMutex
v V
e error
}

// entity that provides TTL cache interface.
type ttlNetCache[K comparable, V any] struct {
ttl time.Duration
m *sync.RWMutex // protects progMap
progMap map[K]*valueInProgress[V] // contains fetch-in-progress keys
ttl time.Duration

sz int

Expand All @@ -41,13 +51,22 @@ func newNetworkTTLCache[K comparable, V any](sz int, ttl time.Duration, netRdr n
fatalOnErr(err)

return ttlNetCache[K, V]{
ttl: ttl,
sz: sz,
cache: cache,
netRdr: netRdr,
ttl: ttl,
sz: sz,
cache: cache,
netRdr: netRdr,
m: &sync.RWMutex{},
progMap: make(map[K]*valueInProgress[V]),
}
}

func waitForUpdate[V any](vip *valueInProgress[V]) (V, error) {
vip.m.RLock()
defer vip.m.RUnlock()

return vip.v, vip.e
}

// reads value by the key.
//
// updates the value from the network on cache miss or by TTL.
Expand All @@ -63,10 +82,38 @@ func (c *ttlNetCache[K, V]) get(key K) (V, error) {
c.cache.Remove(key)
}

netVal, err := c.netRdr(key)
c.m.RLock()
valInProg, ok := c.progMap[key]
c.m.RUnlock()

if ok {
return waitForUpdate(valInProg)
}

c.m.Lock()
valInProg, ok = c.progMap[key]
if ok {
c.m.Unlock()
return waitForUpdate(valInProg)
}

valInProg = &valueInProgress[V]{}
valInProg.m.Lock()
c.progMap[key] = valInProg

c.m.Unlock()

netVal, err := c.netRdr(key)
c.set(key, netVal, err)

valInProg.v = netVal
valInProg.e = err
valInProg.m.Unlock()

c.m.Lock()
delete(c.progMap, key)
c.m.Unlock()

return netVal, err
}

Expand Down

0 comments on commit aecce1b

Please sign in to comment.