agent/cache: on error, return from Get immediately, don't block forever

This commit is contained in:
Mitchell Hashimoto 2018-04-19 09:19:55 -07:00
parent ecb05cc957
commit db4c47df27
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
2 changed files with 47 additions and 0 deletions

15
agent/cache/cache.go vendored
View File

@ -193,6 +193,15 @@ RETRY_GET:
} }
} }
// If this isn't our first time through and our last value has an error,
// then we return the error. This has the behavior that we don't sit in
// a retry loop getting the same error for the entire duration of the
// timeout. Instead, we make one effort to fetch a new value, and if
// there was an error, we return.
if !first && entry.Error != nil {
return entry.Value, entry.Error
}
if first { if first {
// Record the miss if its our first time through // Record the miss if its our first time through
atomic.AddUint64(&c.misses, 1) atomic.AddUint64(&c.misses, 1)
@ -308,6 +317,12 @@ func (c *Cache) fetch(t, key string, r Request) (<-chan struct{}, error) {
newEntry.Valid = true newEntry.Valid = true
} }
// If we have an error and the prior entry wasn't valid, then we
// set the error at least.
if err != nil && !newEntry.Valid {
newEntry.Error = err
}
// Create a new waiter that will be used for the next fetch. // Create a new waiter that will be used for the next fetch.
newEntry.Waiter = make(chan struct{}) newEntry.Waiter = make(chan struct{})

View File

@ -42,6 +42,38 @@ func TestCacheGet_noIndex(t *testing.T) {
typ.AssertExpectations(t) typ.AssertExpectations(t)
} }
// Test a basic Get with no index and a failed fetch.
func TestCacheGet_initError(t *testing.T) {
t.Parallel()
require := require.New(t)
typ := TestType(t)
defer typ.AssertExpectations(t)
c := TestCache(t)
c.RegisterType("t", typ, nil)
// Configure the type
fetcherr := fmt.Errorf("error")
typ.Static(FetchResult{}, fetcherr).Times(2)
// Get, should fetch
req := TestRequest(t, RequestInfo{Key: "hello"})
result, err := c.Get("t", req)
require.Error(err)
require.Nil(result)
// Get, should fetch again since our last fetch was an error
result, err = c.Get("t", req)
require.Error(err)
require.Nil(result)
// Sleep a tiny bit just to let maybe some background calls happen
// then verify that we still only got the one call
time.Sleep(20 * time.Millisecond)
typ.AssertExpectations(t)
}
// Test a Get with a request that returns a blank cache key. This should // Test a Get with a request that returns a blank cache key. This should
// force a backend request and skip the cache entirely. // force a backend request and skip the cache entirely.
func TestCacheGet_blankCacheKey(t *testing.T) { func TestCacheGet_blankCacheKey(t *testing.T) {