X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/c16b905d89fb7c2b9ed8f91ca299f08874b58dab..40dcd44173ea6d6cdc53206766875151eac7b0fd:/lib/controller/handler_test.go diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go index 654eaffe1d..0c50a6c4be 100644 --- a/lib/controller/handler_test.go +++ b/lib/controller/handler_test.go @@ -139,11 +139,6 @@ func (s *HandlerSuite) TestDiscoveryDocCache(c *check.C) { ent.refreshLock.Unlock() } } - expireCache := func() { - for _, ent := range s.handler.cache { - ent.refreshAfter = time.Now() - } - } waitPendingUpdates := func() { for _, ent := range s.handler.cache { ent.refreshLock.Lock() @@ -152,6 +147,18 @@ func (s *HandlerSuite) TestDiscoveryDocCache(c *check.C) { defer ent.mtx.Unlock() } } + refreshNow := func() { + waitPendingUpdates() + for _, ent := range s.handler.cache { + ent.refreshAfter = time.Now() + } + } + expireNow := func() { + waitPendingUpdates() + for _, ent := range s.handler.cache { + ent.expireAfter = time.Now() + } + } // Easy path: first req fetches, subsequent reqs use cache. c.Check(countRailsReqs(), check.Equals, 0) @@ -181,7 +188,7 @@ func (s *HandlerSuite) TestDiscoveryDocCache(c *check.C) { // Race after expiry: concurrent reqs return the cached data // but initiate a new fetch in the background. - expireCache() + refreshNow() holdReqs = make(chan struct{}) wg = getDDConcurrently(5, http.StatusOK, check.Commentf("race after expiry")) reqsBefore = countRailsReqs() @@ -208,7 +215,7 @@ func (s *HandlerSuite) TestDiscoveryDocCache(c *check.C) { // make an upstream attempt for each incoming request because // we have nothing better to return clearCache() - wantError = true + wantError, wantBadContent = true, false reqsBefore = countRailsReqs() holdReqs = make(chan struct{}) wg = getDDConcurrently(5, http.StatusBadGateway, check.Commentf("error at startup")) @@ -217,47 +224,75 @@ func (s *HandlerSuite) TestDiscoveryDocCache(c *check.C) { c.Check(countRailsReqs(), check.Equals, reqsBefore+5) // Response status is OK but body is not a discovery document - wantError = false - wantBadContent = true + wantError, wantBadContent = false, true reqsBefore = countRailsReqs() c.Check(getDD(), check.Equals, http.StatusBadGateway) c.Check(countRailsReqs(), check.Equals, reqsBefore+1) // Error condition clears => caller gets OK, cache is warmed // up - wantBadContent = false + wantError, wantBadContent = false, false reqsBefore = countRailsReqs() getDDConcurrently(5, http.StatusOK, check.Commentf("success after errors at startup")).Wait() c.Check(countRailsReqs(), check.Equals, reqsBefore+1) // Error with warm cache => caller gets OK (with no attempt to // re-fetch) - wantError = true + wantError, wantBadContent = true, false reqsBefore = countRailsReqs() getDDConcurrently(5, http.StatusOK, check.Commentf("error with warm cache")).Wait() c.Check(countRailsReqs(), check.Equals, reqsBefore) - expireCache() - // Error with expired cache => caller gets OK with stale data + // Error with stale cache => caller gets OK with stale data // while the re-fetch is attempted in the background + refreshNow() + wantError, wantBadContent = true, false reqsBefore = countRailsReqs() holdReqs = make(chan struct{}) - getDDConcurrently(5, http.StatusOK, check.Commentf("error with expired cache")).Wait() + getDDConcurrently(5, http.StatusOK, check.Commentf("error with stale cache")).Wait() close(holdReqs) // Only one attempt to re-fetch (holdReqs ensured the first // update took long enough for the last incoming request to // arrive) c.Check(countRailsReqs(), check.Equals, reqsBefore+1) - waitPendingUpdates() - expireCache() - wantError = false + refreshNow() + wantError, wantBadContent = false, false reqsBefore = countRailsReqs() holdReqs = make(chan struct{}) getDDConcurrently(5, http.StatusOK, check.Commentf("refresh cache after error condition clears")).Wait() close(holdReqs) waitPendingUpdates() c.Check(countRailsReqs(), check.Equals, reqsBefore+1) + + // Make sure expireAfter is getting set + waitPendingUpdates() + exp := s.handler.cache["/discovery/v1/apis/arvados/v1/rest"].expireAfter.Sub(time.Now()) + c.Check(exp > cacheTTL, check.Equals, true) + c.Check(exp < cacheExpire, check.Equals, true) + + // After the cache *expires* it behaves as if uninitialized: + // each incoming request does a new upstream request until one + // succeeds. + // + // First check failure after expiry: + expireNow() + wantError, wantBadContent = true, false + reqsBefore = countRailsReqs() + holdReqs = make(chan struct{}) + wg = getDDConcurrently(5, http.StatusBadGateway, check.Commentf("error after expiry")) + close(holdReqs) + wg.Wait() + c.Check(countRailsReqs(), check.Equals, reqsBefore+5) + + // Success after expiry: + wantError, wantBadContent = false, false + reqsBefore = countRailsReqs() + holdReqs = make(chan struct{}) + wg = getDDConcurrently(5, http.StatusOK, check.Commentf("success after expiry")) + close(holdReqs) + wg.Wait() + c.Check(countRailsReqs(), check.Equals, reqsBefore+1) } func (s *HandlerSuite) TestVocabularyExport(c *check.C) { @@ -604,7 +639,7 @@ func (s *HandlerSuite) TestGetObjects(c *check.C) { testCases := map[string]map[string]bool{ "api_clients/" + arvadostest.TrustedWorkbenchAPIClientUUID: nil, "api_client_authorizations/" + auth.UUID: {"href": true, "modified_by_client_uuid": true, "modified_by_user_uuid": true}, - "authorized_keys/" + arvadostest.AdminAuthorizedKeysUUID: nil, + "authorized_keys/" + arvadostest.AdminAuthorizedKeysUUID: {"href": true}, "collections/" + arvadostest.CollectionWithUniqueWordsUUID: {"href": true}, "containers/" + arvadostest.RunningContainerUUID: nil, "container_requests/" + arvadostest.QueuedContainerRequestUUID: nil,