internal/frontend: run GetLatestInfo queries concurrently
Fold in the functions called by GetLatestInfo, and run their queries
in separate goroutines to reduce latency.
Change-Id: I42f0c850d3a51606dc63b69315c3b294611ecf07
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/279456
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/frontend/latest_version.go b/internal/frontend/latest_version.go
index 8a0e9cd..5dff358 100644
--- a/internal/frontend/latest_version.go
+++ b/internal/frontend/latest_version.go
@@ -7,6 +7,7 @@
import (
"context"
"errors"
+ "sync"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/derrors"
@@ -14,42 +15,42 @@
"golang.org/x/pkgsite/internal/middleware"
)
-func (s *Server) GetLatestInfo(ctx context.Context, fullPath, modulePath string) (latest middleware.LatestInfo) {
- latest.MinorVersion = s.getLatestMinorVersion(ctx, fullPath, internal.UnknownModulePath)
- latest.MajorModulePath, latest.MajorUnitPath = s.getLatestMajorVersion(ctx, fullPath, modulePath)
- return latest
-}
-
-// getLatestMajorVersion returns the latest module path and the full unit path
-// of any major version found given the fullPath and the modulePath.
+// GetLatestInfo returns various pieces of information about the latest
+// versions of a unit and module:
+// - The linkable form of the minor version of the unit.
+// - The latest module path and the full unit path of any major version found given the
+// fullPath and the modulePath.
+// It returns empty strings on error.
// It is intended to be used as an argument to middleware.LatestVersions.
-func (s *Server) getLatestMajorVersion(ctx context.Context, unitPath, modulePath string) (_ string, _ string) {
- latestModulePath, latestPackagePath, err := s.getDataSource(ctx).GetLatestMajorVersion(ctx, unitPath, modulePath)
- if err != nil {
- if !errors.Is(err, derrors.NotFound) {
+func (s *Server) GetLatestInfo(ctx context.Context, unitPath, modulePath string) (latest middleware.LatestInfo) {
+ // It is okay to use a different DataSource (DB connection) than the rest of the
+ // request, because this makes self-contained calls on the DB.
+ ds := s.getDataSource(ctx)
+
+ var wg sync.WaitGroup
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ var err error
+ latest.MinorVersion, err = latestMinorVersion(ctx, ds, unitPath, internal.UnknownModulePath)
+ if err != nil {
+ log.Errorf(ctx, "latestMinorVersion: %v", err)
+ }
+ }()
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ var err error
+ latest.MajorModulePath, latest.MajorUnitPath, err = ds.GetLatestMajorVersion(ctx, unitPath, modulePath)
+ if err != nil && !errors.Is(err, derrors.NotFound) {
log.Errorf(ctx, "GetLatestMajorVersion: %v", err)
}
- return "", ""
- }
- return latestModulePath, latestPackagePath
-}
+ }()
-// getLatestMinorVersion returns the latest minor version of the unit.
-// The linkable form of the minor version is returned and is an empty string on error.
-// It is intended to be used as an argument to middleware.LatestVersions.
-func (s *Server) getLatestMinorVersion(ctx context.Context, unitPath, modulePath string) string {
- // It is okay to use a different DataSource (DB connection) than the rest of the
- // request, because this makes a self-contained call on the DB.
- v, err := latestMinorVersion(ctx, s.getDataSource(ctx), unitPath, modulePath)
- if err != nil {
- // We get NotFound errors from directories; they clutter the log.
- if !errors.Is(err, derrors.NotFound) {
- log.Errorf(ctx, "GetLatestMinorVersion: %v", err)
- }
- return ""
- }
-
- return v
+ wg.Wait()
+ return latest
}
// TODO(https://github.com/golang/go/issues/40107): this is currently tested in server_test.go, but