internal/{frontend,vuln}: only fetch 5 most recent vulns on /vuln page
Previously, all vulns were fetched and all but 5 were discarded, an
expensive operation. With this change, only the 5 most recent vulns
are fetched.
Change-Id: Ia14992bbbdd4fb45a7512127e0f350fcb62b021d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/484537
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Run-TryBot: Tatiana Bradley <tatianabradley@google.com>
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index 056c610..766d5a2 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -373,7 +373,7 @@
if mode != searchModeVuln || client == nil {
return nil, nil
}
- allEntries, err := client.Entries(ctx)
+ allEntries, err := client.Entries(ctx, -1)
if err != nil {
return nil, err
}
@@ -403,8 +403,6 @@
}
}
- sortVulnEntries(entries)
-
return &searchAction{
title: fmt.Sprintf("%s - Vulnerability Reports", query),
template: "vuln/list",
diff --git a/internal/frontend/vulns.go b/internal/frontend/vulns.go
index 16ae3ae..97e6afe 100644
--- a/internal/frontend/vulns.go
+++ b/internal/frontend/vulns.go
@@ -7,7 +7,6 @@
import (
"context"
"net/http"
- "sort"
"strings"
"golang.org/x/pkgsite/internal"
@@ -44,19 +43,16 @@
path := strings.TrimPrefix(r.URL.Path, "/vuln")
switch path {
case "/":
- // Serve a list of most recent entries.
- vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient)
+ // Serve a list of the 5 most recent entries.
+ vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient, 5)
if err != nil {
return &serverError{status: derrors.ToStatus(err)}
}
- if len(vulnListPage.Entries) > 5 {
- vulnListPage.Entries = vulnListPage.Entries[:5]
- }
vulnListPage.basePage = s.newBasePage(r, "Go Vulnerability Database")
s.servePage(r.Context(), w, "vuln/main", vulnListPage)
case "/list":
// Serve a list of all entries.
- vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient)
+ vulnListPage, err := newVulnListPage(r.Context(), s.vulnClient, -1)
if err != nil {
return &serverError{status: derrors.ToStatus(err)}
}
@@ -99,20 +95,14 @@
}, nil
}
-func newVulnListPage(ctx context.Context, client *vuln.Client) (*VulnListPage, error) {
- entries, err := client.Entries(ctx)
+func newVulnListPage(ctx context.Context, client *vuln.Client, n int) (*VulnListPage, error) {
+ entries, err := client.Entries(ctx, n)
if err != nil {
return nil, derrors.VulnDBError
}
- sortVulnEntries(entries)
return &VulnListPage{Entries: entries}, nil
}
-// Sort vuln entries in descending order by Go ID.
-func sortVulnEntries(entries []*osv.Entry) {
- sort.Slice(entries, func(i, j int) bool { return entries[i].ID > entries[j].ID })
-}
-
// aliasLinks generates links to reference pages for vuln aliases.
func aliasLinks(e *osv.Entry) []link {
var links []link
diff --git a/internal/frontend/vulns_test.go b/internal/frontend/vulns_test.go
index ca3c844..07c1199 100644
--- a/internal/frontend/vulns_test.go
+++ b/internal/frontend/vulns_test.go
@@ -61,7 +61,7 @@
if err != nil {
t.Fatal(err)
}
- got, err := newVulnListPage(ctx, c)
+ got, err := newVulnListPage(ctx, c, -1)
if err != nil {
t.Fatal(err)
}
diff --git a/internal/vuln/client.go b/internal/vuln/client.go
index f2b1403..1b43079 100644
--- a/internal/vuln/client.go
+++ b/internal/vuln/client.go
@@ -214,15 +214,31 @@
return "", derrors.NotFound
}
-// Entries returns all entries in the database.
-func (c *Client) Entries(ctx context.Context) (_ []*osv.Entry, err error) {
- derrors.Wrap(&err, "Entries()")
+// Entries returns all entries in the database, sorted in descending
+// order by Go ID (most recent to least recent).
+// If n >= 0, only the n most recent entries are returned.
+func (c *Client) Entries(ctx context.Context, n int) (_ []*osv.Entry, err error) {
+ derrors.Wrap(&err, "Entries(n=%d)", n)
+
+ if n == 0 {
+ return nil, nil
+ }
ids, err := c.ids(ctx)
if err != nil {
return nil, err
}
+ sort.Slice(ids, func(i, j int) bool { return ids[i] > ids[j] })
+
+ if n >= 0 && len(ids) > n {
+ ids = ids[:n]
+ }
+
+ return c.byIDs(ctx, ids)
+}
+
+func (c *Client) byIDs(ctx context.Context, ids []string) (_ []*osv.Entry, err error) {
entries := make([]*osv.Entry, len(ids))
g, gctx := errgroup.WithContext(ctx)
g.SetLimit(4)
diff --git a/internal/vuln/client_test.go b/internal/vuln/client_test.go
index a19c200..be630b6 100644
--- a/internal/vuln/client_test.go
+++ b/internal/vuln/client_test.go
@@ -8,6 +8,7 @@
"bytes"
"context"
"encoding/json"
+ "fmt"
"reflect"
"strings"
"testing"
@@ -346,14 +347,42 @@
t.Fatal(err)
}
- got, err := c.Entries(ctx)
- if err != nil {
- t.Fatal(err)
+ tests := []struct {
+ n int
+ want []*osv.Entry
+ }{
+ {
+ n: -1,
+ want: []*osv.Entry{&testOSV3, &testOSV2, &testOSV1},
+ },
+ {
+ n: 0,
+ want: nil,
+ },
+ {
+ n: 2,
+ want: []*osv.Entry{&testOSV3, &testOSV2},
+ },
+ {
+ n: 3,
+ want: []*osv.Entry{&testOSV3, &testOSV2, &testOSV1},
+ },
+ {
+ n: 4,
+ want: []*osv.Entry{&testOSV3, &testOSV2, &testOSV1},
+ },
}
+ for _, tc := range tests {
+ t.Run(fmt.Sprintf("n=%d", tc.n), func(t *testing.T) {
+ got, err := c.Entries(ctx, tc.n)
+ if err != nil {
+ t.Fatal(err)
+ }
- want := []*osv.Entry{&testOSV1, &testOSV2, &testOSV3}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("Entries = %#v, want %#v", got, want)
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("Entries = %#v, want %#v", got, tc.want)
+ }
+ })
}
}