Chromium Code Reviews| Index: build_scheduler/go/db/cache.go |
| diff --git a/build_scheduler/go/db/cache.go b/build_scheduler/go/db/cache.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fa29113391ad7329907629d7cea8b7f7ca1e9ac6 |
| --- /dev/null |
| +++ b/build_scheduler/go/db/cache.go |
| @@ -0,0 +1,114 @@ |
| +package db |
| + |
| +import ( |
| + "sync" |
| + "time" |
| +) |
| + |
| +type BuildCache struct { |
| + builds map[string]*Build |
| + buildsByCommit map[string]map[string]*Build |
| + db DB |
| + lastUpdate time.Time |
| + mtx sync.RWMutex |
| + queryId string |
| +} |
| + |
| +// GetBuildsForCommits retrieves all builds which first included each of the |
|
dogben
2016/08/08 19:22:38
There are a couple things that I don't understand
borenet
2016/08/08 19:51:17
Historically, newer builds "steal" from the blamel
dogben
2016/08/08 20:16:07
Is it worthwhile to mention here that we assume th
borenet
2016/08/09 11:22:55
Wrote some more doc, hopefully it's clearer now.
dogben
2016/08/09 13:56:06
Awesome! Thanks!
|
| +// given commits. Returns a map whose keys are commit hashes and values are |
| +// sub-maps whose keys are builder names and values are builds. |
| +func (c *BuildCache) GetBuildsForCommits(commits []string) (map[string]map[string]*Build, error) { |
| + c.mtx.RLock() |
| + defer c.mtx.RUnlock() |
| + |
| + rv := make(map[string]map[string]*Build, len(commits)) |
| + for _, commit := range commits { |
| + if builds, ok := c.buildsByCommit[commit]; ok { |
| + rv[commit] = make(map[string]*Build, len(builds)) |
| + for k, v := range builds { |
| + rv[commit][k] = v.Copy() |
|
dogben
2016/08/08 19:22:38
Method doc says the sub-map key is the builder nam
borenet
2016/08/08 19:51:18
Fixed.
|
| + } |
| + } else { |
| + rv[commit] = map[string]*Build{} |
| + } |
| + } |
| + return rv, nil |
| +} |
| + |
| +// update inserts the new/updated builds into the cache. Assumes the caller |
| +// holds a lock. |
| +func (c *BuildCache) update(builds []*Build) error { |
| + for _, b := range builds { |
| + // If we already know about this build, the blamelist might, |
| + // have changed, so we need to remove it from buildsByCommit |
| + // and re-insert where needed. |
| + if old, ok := c.builds[b.Id]; ok { |
|
dogben
2016/08/08 19:22:38
We should think about whether we want to use Build
borenet
2016/08/08 19:51:17
Agreed. I'm just using Buildbucket's IDs for now f
|
| + for _, commit := range old.Commits { |
| + delete(c.buildsByCommit[commit], b.Id) |
| + } |
| + } |
| + |
| + // Insert the new build into the main map. |
| + c.builds[b.Id] = b.Copy() |
| + |
| + // Insert the build into buildsByCommits. |
| + for _, commit := range b.Commits { |
| + if _, ok := c.buildsByCommit[commit]; !ok { |
| + c.buildsByCommit[commit] = map[string]*Build{} |
| + } |
| + c.buildsByCommit[commit][b.Id] = b.Copy() |
|
dogben
2016/08/08 19:22:38
We should make a single copy and store it in both
borenet
2016/08/08 19:51:18
Done.
|
| + } |
| + } |
| + return nil |
| +} |
| + |
| +// Load new builds from the database. |
| +func (c *BuildCache) Update() error { |
| + newBuilds, err := c.db.GetModifiedBuilds(c.queryId) |
|
dogben
2016/08/08 19:22:38
nit:
now := time.Now()
newBuilds, err := c.db.Get
borenet
2016/08/08 19:51:18
Good catch, done.
|
| + c.mtx.Lock() |
| + defer c.mtx.Unlock() |
| + if err != nil { |
| + if err.Error() == ErrUnknownId.Error() { |
| + // The database may have restarted. Attempt to re-establish connection. |
| + queryId, err := c.db.StartTrackingModifiedBuilds() |
| + if err != nil { |
| + return err |
| + } |
| + c.queryId = queryId |
| + // We may have missed something. Query for builds since the last |
| + // successful query. |
| + builds, err := c.db.GetBuildsFromDateRange(c.lastUpdate, time.Now()) |
| + if err != nil { |
| + return err |
| + } |
| + return c.update(builds) |
| + } else { |
| + return err |
| + } |
| + } |
| + return c.update(newBuilds) |
| +} |
| + |
| +// NewBuildCache returns a local cache which provides more convenient views of |
| +// build data than the database can provide. |
| +func NewBuildCache(db DB, timePeriod time.Duration) (*BuildCache, error) { |
| + queryId, err := db.StartTrackingModifiedBuilds() |
| + if err != nil { |
| + return nil, err |
| + } |
| + now := time.Now() |
| + start := now.Add(-timePeriod) |
| + builds, err := db.GetBuildsFromDateRange(start, now) |
| + if err != nil { |
| + return nil, err |
| + } |
| + bc := &BuildCache{ |
| + builds: map[string]*Build{}, |
| + buildsByCommit: map[string]map[string]*Build{}, |
| + db: db, |
| + lastUpdate: now, |
| + queryId: queryId, |
| + } |
| + bc.update(builds) |
|
dogben
2016/08/08 19:22:38
nit: check error (or change update to not return e
borenet
2016/08/08 19:51:17
Done.
|
| + return bc, nil |
| +} |