internal/frontend: cache slow symbol searches for a long time

If the request is for a symbol that takes a long time to search, cache
the results for a long time.

Change-Id: I68a901b81c854c30af01b626e00cf4d0de1ee59c
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/356531
Trust: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/frontend/server.go b/internal/frontend/server.go
index 5f8cabd..f7b3f48 100644
--- a/internal/frontend/server.go
+++ b/internal/frontend/server.go
@@ -120,7 +120,7 @@
 	)
 	if redisClient != nil {
 		detailHandler = middleware.Cache("details", redisClient, detailsTTL, authValues)(detailHandler)
-		searchHandler = middleware.Cache("search", redisClient, middleware.TTL(defaultTTL), authValues)(searchHandler)
+		searchHandler = middleware.Cache("search", redisClient, searchTTL, authValues)(searchHandler)
 	}
 	// Each AppEngine instance is created in response to a start request, which
 	// is an empty HTTP GET request to /_ah/start when scaling is set to manual
@@ -229,6 +229,22 @@
 	return longTTL
 }
 
+var slowSymbolSearches = map[string]bool{
+	"new": true,
+}
+
+// searchTTL assigns the cache TTL for search requests.
+func searchTTL(r *http.Request) time.Duration {
+	if searchMode(r) == searchModeSymbol {
+		q, _ := searchQueryAndFilters(r)
+		if slowSymbolSearches[strings.ToLower(q)] {
+			// Slow searches should be computed on deploy. Cache them for a long time.
+			return 14 * 24 * time.Hour
+		}
+	}
+	return defaultTTL
+}
+
 // TagRoute categorizes incoming requests to the frontend for use in
 // monitoring.
 func TagRoute(route string, r *http.Request) string {