Package lazyslot implements a caching scheme for globally shared objects that take significant time to refresh.
The defining property of the implementation is that only one goroutine will block when refreshing such object, while all others will use a slightly stale cached copy.
ExpiresImmediately can be returned by the fetcher callback to indicate that the item must be refreshed on the next access.
This is sometimes useful in tests with "frozen" time to disable caching.
Fetcher knows how to load a new value or refresh the existing one.
It receives the previously known value when refreshing it.
If the returned expiration duration is zero, the returned value never expires. If the returned expiration duration is equal to ExpiresImmediately, then the very next Get(...) will trigger another refresh (this is sometimes useful in tests with "frozen" time to disable caching).
Slot holds a cached value and refreshes it when it expires.
Only one goroutine will be busy refreshing, all others will see a slightly stale copy of the value during the refresh.
Get returns stored value if it is still fresh or refetches it if it's stale.
It may return slightly stale copy if some other goroutine is fetching a new copy now. If there's no cached copy at all, blocks until it is retrieved.
Returns an error only when there's no cached copy yet and Fetcher returns an error.
If there's an expired cached copy, and Fetcher returns an error when trying to refresh it, logs the error and returns the existing cached copy (which is stale at this point). We assume callers prefer stale copy over a hard error.
On refetch errors bumps expiration time of the cached copy to RetryDelay seconds from now, effectively scheduling a retry at some later time. RetryDelay is 5 sec by default.
The passed context is used for logging and for getting time.