diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-10-17 14:52:17 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-10-23 12:07:54 +0000 |
| commit | af8d2e46418eefb127e9fa9309a63fa60ef7fc66 (patch) | |
| tree | 142d18038b84a3507742c94d84488d45c29d20e2 /dashboard/app/cache.go | |
| parent | 6346f51eb10624fc17701d3a24d9f35c902b29a1 (diff) | |
dashboard: optionally cache displayed bug groups
For upstream Linux namespace, it sometimes takes up to 5-10 seconds to
load the main page. That is too much and the reason for this is
datastore not being intended for frequent querying of thousands of
entities from the database.
Let's take a step forward and at least cache the bugs we display on the
main page. Once in a minute, query them for all access levels, compress
and save to the memcached.
Only do it for non-filtered bugs, because otherwise it works fast
enough.
As the next step we could also take care of terminal pages.
Diffstat (limited to 'dashboard/app/cache.go')
| -rw-r--r-- | dashboard/app/cache.go | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/dashboard/app/cache.go b/dashboard/app/cache.go index b9ed0d54f..4e77d55cf 100644 --- a/dashboard/app/cache.go +++ b/dashboard/app/cache.go @@ -4,10 +4,12 @@ package main import ( + "encoding/json" "fmt" "net/http" "time" + "github.com/google/syzkaller/pkg/image" "golang.org/x/net/context" "google.golang.org/appengine/v2" "google.golang.org/appengine/v2/log" @@ -48,6 +50,8 @@ func CacheGet(c context.Context, r *http.Request, ns string) (*Cached, error) { return buildAndStoreCached(c, bugs, backports, ns, accessLevel) } +var cacheAccessLevels = []AccessLevel{AccessPublic, AccessUser, AccessAdmin} + // cacheUpdate updates memcache every hour (called by cron.yaml). // Cache update is slow and we don't want to slow down user requests. func cacheUpdate(w http.ResponseWriter, r *http.Request) { @@ -63,7 +67,7 @@ func cacheUpdate(w http.ResponseWriter, r *http.Request) { log.Errorf(c, "failed load ns=%v bugs: %v", ns, err) continue } - for _, accessLevel := range []AccessLevel{AccessPublic, AccessUser, AccessAdmin} { + for _, accessLevel := range cacheAccessLevels { _, err := buildAndStoreCached(c, bugs, backports, ns, accessLevel) if err != nil { log.Errorf(c, "failed to build cached for ns=%v access=%v: %v", ns, accessLevel, err) @@ -104,6 +108,7 @@ func buildAndStoreCached(c context.Context, bugs []*Bug, backports []*rawBackpor } } } + item := &memcache.Item{ Key: cacheKey(ns, accessLevel), Object: v, @@ -133,3 +138,71 @@ func (c *CachedBugStats) Record(bug *Bug) { func cacheKey(ns string, accessLevel AccessLevel) string { return fmt.Sprintf("%v-%v", ns, accessLevel) } + +func CachedBugGroups(c context.Context, ns string, accessLevel AccessLevel) ([]*uiBugGroup, error) { + item, err := memcache.Get(c, cachedBugGroupsKey(ns, accessLevel)) + if err == memcache.ErrCacheMiss { + return nil, nil + } + if err != nil { + return nil, err + } + + jsonData, destructor := image.MustDecompress(item.Value) + defer destructor() + + var ret []*uiBugGroup + err = json.Unmarshal(jsonData, &ret) + return ret, err +} + +func cachedBugGroupsKey(ns string, accessLevel AccessLevel) string { + return fmt.Sprintf("%v-%v-bug-groups", ns, accessLevel) +} + +// minuteCacheUpdate updates memcache every minute (called by cron.yaml). +func handleMinuteCacheUpdate(w http.ResponseWriter, r *http.Request) { + c := appengine.NewContext(r) + for ns, nsConfig := range getConfig(c).Namespaces { + if !nsConfig.CacheUIPages { + continue + } + err := minuteCacheNsUpdate(c, ns) + if err != nil { + http.Error(w, fmt.Sprintf("bug groups cache update for %s failed: %v", ns, err), + http.StatusInternalServerError) + return + } + } +} + +func minuteCacheNsUpdate(c context.Context, ns string) error { + bugs, err := loadVisibleBugs(c, ns, nil) + if err != nil { + return err + } + managers, err := managerList(c, ns) + if err != nil { + return err + } + for _, accessLevel := range cacheAccessLevels { + groups, err := prepareBugGroups(c, bugs, managers, accessLevel, ns) + if err != nil { + return fmt.Errorf("failed to fetch groups: %w", err) + } + encoded, err := json.Marshal(groups) + if err != nil { + return fmt.Errorf("failed to marshal: %w", err) + } + item := &memcache.Item{ + Key: cachedBugGroupsKey(ns, accessLevel), + // The resulting blob can be quite big, so let's compress. + Value: image.Compress(encoded), + Expiration: 2 * time.Minute, // supposed to be updated by cron every minute + } + if err := memcache.Set(c, item); err != nil { + return err + } + } + return nil +} |
