mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-04 23:45:34 +00:00
80 lines
1.7 KiB
Go
80 lines
1.7 KiB
Go
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
package cache
|
||
|
|
||
|
import (
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Single is a simple in-memory cache that stores a single value until a
|
||
|
// defined time before it is re-fetched. It also supports returning a
|
||
|
// previously-expired value if refreshing the value in the cache fails.
|
||
|
//
|
||
|
// Single is not safe for concurrent use.
|
||
|
type Single[K comparable, V any] struct {
|
||
|
key K
|
||
|
val V
|
||
|
goodUntil time.Time
|
||
|
timeNow func() time.Time // for tests
|
||
|
|
||
|
// ServeExpired indicates that if an error occurs when filling the
|
||
|
// cache, an expired value can be returned instead of an error.
|
||
|
//
|
||
|
// This value should only be set when this struct is created.
|
||
|
ServeExpired bool
|
||
|
}
|
||
|
|
||
|
// Get will return the cached value, if any, or fill the cache by calling f and
|
||
|
// return the corresponding value. If f returns an error and c.ServeExpired is
|
||
|
// true, then a previous expired value can be returned with no error.
|
||
|
func (c *Single[K, V]) Get(key K, f FillFunc[V]) (V, error) {
|
||
|
var now time.Time
|
||
|
if c.timeNow != nil {
|
||
|
now = c.timeNow()
|
||
|
} else {
|
||
|
now = time.Now()
|
||
|
}
|
||
|
|
||
|
if c.key == key && now.Before(c.goodUntil) {
|
||
|
return c.val, nil
|
||
|
}
|
||
|
|
||
|
// Re-fill cached entry
|
||
|
val, until, err := f()
|
||
|
if err == nil {
|
||
|
c.key = key
|
||
|
c.val = val
|
||
|
c.goodUntil = until
|
||
|
return val, nil
|
||
|
}
|
||
|
|
||
|
// Never serve an expired entry for the wrong key.
|
||
|
if c.key == key && c.ServeExpired && !c.goodUntil.IsZero() {
|
||
|
return c.val, nil
|
||
|
}
|
||
|
|
||
|
var zero V
|
||
|
return zero, err
|
||
|
}
|
||
|
|
||
|
// Forget implements Cache.
|
||
|
func (c *Single[K, V]) Forget(key K) {
|
||
|
if c.key != key {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
c.Empty()
|
||
|
}
|
||
|
|
||
|
// Empty implements Cache.
|
||
|
func (c *Single[K, V]) Empty() {
|
||
|
c.goodUntil = time.Time{}
|
||
|
|
||
|
var zeroKey K
|
||
|
c.key = zeroKey
|
||
|
|
||
|
var zeroVal V
|
||
|
c.val = zeroVal
|
||
|
}
|