internal/frontend: enqueue fetch request for all requests to path@master
Once we turn on the feature flag for supporting requests to master, we
need a way to ensure that master is not stale for a given path.
As a result, all requests to path@master will be enqueued, once we
determine that it is a valid path.
Updates golang/go#36811
Change-Id: I498179586ee46acbd7347eb70a90104fe9e0b8a8
Reviewed-on: https://team-review.git.corp.google.com/c/golang/discovery/+/769967
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/frontend/details.go b/internal/frontend/details.go
index 138de81..868f210 100644
--- a/internal/frontend/details.go
+++ b/internal/frontend/details.go
@@ -16,6 +16,7 @@
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/experiment"
+ "golang.org/x/pkgsite/internal/log"
"golang.org/x/pkgsite/internal/postgres"
"golang.org/x/pkgsite/internal/stdlib"
)
@@ -88,6 +89,21 @@
if err := checkPathAndVersion(ctx, s.ds, fullPath, requestedVersion); err != nil {
return err
}
+ if isActivePathAtMaster(ctx) && requestedVersion == internal.MasterVersion {
+ // Since path@master is a moving target, we don't want it to be stale.
+ // As a result, we enqueue every request of path@master to the frontend
+ // task queue, which will initiate a fetch request depending on the
+ // last time we tried to fetch this module version.
+ go func() {
+ status, responseText := s.fetchAndPoll(r.Context(), modulePath, fullPath, requestedVersion)
+ logf := log.Infof
+ if status == http.StatusInternalServerError {
+ logf = log.Errorf
+ }
+ logf(ctx, "fetchAndPoll(%q, %q, %q) result from serveDetails(%q): %d %q",
+ modulePath, fullPath, requestedVersion, r.URL.Path, status, responseText)
+ }()
+ }
// Depending on what the request was for, return the module or package page.
if isModule || fullPath == stdlib.ModulePath {
return s.serveModulePage(w, r, fullPath, requestedVersion)
diff --git a/internal/frontend/fetch.go b/internal/frontend/fetch.go
index 99de031..6686bb7 100644
--- a/internal/frontend/fetch.go
+++ b/internal/frontend/fetch.go
@@ -40,7 +40,9 @@
fetchTimeout = 30 * time.Second
pollEvery = 500 * time.Millisecond
- // keyFrontendFetchStatus is a census tag for frontend fetch query types.
+ // keyFrontendFetchVersion is a census tag for frontend fetch version types.
+ keyFrontendFetchVersion = tag.MustNewKey("frontend-fetch.version")
+ // keyFrontendFetchStatus is a census tag for frontend fetch status types.
keyFrontendFetchStatus = tag.MustNewKey("frontend-fetch.status")
// keyFrontendFetchLatency holds observed latency in individual
// frontend fetch queries.
@@ -82,7 +84,8 @@
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
- if !isActiveFrontendFetch(r.Context()) {
+ ctx := r.Context()
+ if !isActiveFrontendFetch(ctx) {
// If the experiment flag is not on, treat this as a request for the
// "fetch" package, which does not exist.
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
@@ -95,9 +98,11 @@
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
- start := time.Now()
+ if !isActivePathAtMaster(ctx) && requestedVersion != internal.MasterVersion {
+ http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
+ return
+ }
status, responseText := s.fetchAndPoll(r.Context(), modulePath, fullPath, requestedVersion)
- recordFrontendFetchMetric(status, time.Since(start))
if status != http.StatusOK {
http.Error(w, responseText, status)
return
@@ -118,9 +123,11 @@
}
func (s *Server) fetchAndPoll(parentCtx context.Context, modulePath, fullPath, requestedVersion string) (status int, responseText string) {
+ start := time.Now()
defer func() {
log.Infof(parentCtx, "fetchAndPoll(ctx, ds, q, %q, %q, %q): status=%d, responseText=%q",
modulePath, fullPath, requestedVersion, status, responseText)
+ recordFrontendFetchMetric(status, requestedVersion, time.Since(start))
}()
if !semver.IsValid(requestedVersion) &&
@@ -274,6 +281,8 @@
// Check the version_map table to see if a row exists for modulePath and
// requestedVersion.
+ // TODO(golang/go#37002): update db.GetVersionMap to return updated_at,
+ // so that we can determine if a module version is stale.
vm, err := db.GetVersionMap(ctx, modulePath, requestedVersion)
if err != nil {
// If an error is returned, there are two possibilities:
@@ -460,9 +469,16 @@
experiment.IsActive(ctx, internal.ExperimentInsertDirectories)
}
-func recordFrontendFetchMetric(status int, latency time.Duration) {
+func recordFrontendFetchMetric(status int, version string, latency time.Duration) {
l := float64(latency) / float64(time.Millisecond)
+
+ // Tag versions based on latest, master and semver.
+ v := version
+ if semver.IsValid(v) {
+ v = "semver"
+ }
stats.RecordWithTags(context.Background(), []tag.Mutator{
tag.Upsert(keyFrontendFetchStatus, strconv.Itoa(status)),
+ tag.Upsert(keyFrontendFetchVersion, v),
}, keyFrontendFetchLatency.M(l))
}