| Index: build_scheduler/go/db/memory.go
|
| diff --git a/build_scheduler/go/db/memory.go b/build_scheduler/go/db/memory.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..21bbb219f4e6f3e5ff96129d5058d1496eab4c31
|
| --- /dev/null
|
| +++ b/build_scheduler/go/db/memory.go
|
| @@ -0,0 +1,149 @@
|
| +package db
|
| +
|
| +import (
|
| + "sort"
|
| + "sync"
|
| + "time"
|
| +
|
| + "github.com/satori/go.uuid"
|
| + "github.com/skia-dev/glog"
|
| +)
|
| +
|
| +type BuildSlice []*Build
|
| +
|
| +func (s BuildSlice) Len() int { return len(s) }
|
| +
|
| +func (s BuildSlice) Less(i, j int) bool {
|
| + ts1, err := s[i].Created()
|
| + if err != nil {
|
| + glog.Errorf("Failed to parse CreatedTimestamp: %v", s[i])
|
| + }
|
| + ts2, err := s[j].Created()
|
| + if err != nil {
|
| + glog.Errorf("Failed to parse CreatedTimestamp: %v", s[j])
|
| + }
|
| + return ts1.Before(ts2)
|
| +}
|
| +
|
| +func (s BuildSlice) Swap(i, j int) {
|
| + s[i], s[j] = s[j], s[i]
|
| +}
|
| +
|
| +type inMemoryDB struct {
|
| + builds map[string]*Build
|
| + buildsMtx sync.RWMutex
|
| + modBuilds map[string]map[string]*Build
|
| + modExpire map[string]time.Time
|
| + modMtx sync.RWMutex
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) Close() error {
|
| + return nil
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) GetBuildsFromDateRange(start, end time.Time) ([]*Build, error) {
|
| + db.buildsMtx.RLock()
|
| + defer db.buildsMtx.RUnlock()
|
| +
|
| + rv := []*Build{}
|
| + // TODO(borenet): Binary search.
|
| + for _, b := range db.builds {
|
| + created, err := b.Created()
|
| + if err != nil {
|
| + return nil, err
|
| + }
|
| + if (created.Equal(start) || created.After(start)) && created.Before(end) {
|
| + rv = append(rv, b.Copy())
|
| + }
|
| + }
|
| + sort.Sort(BuildSlice(rv))
|
| + return rv, nil
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) GetModifiedBuilds(id string) ([]*Build, error) {
|
| + db.modMtx.Lock()
|
| + defer db.modMtx.Unlock()
|
| + modifiedBuilds, ok := db.modBuilds[id]
|
| + if !ok {
|
| + return nil, ErrUnknownId
|
| + }
|
| + rv := make([]*Build, 0, len(modifiedBuilds))
|
| + for _, b := range modifiedBuilds {
|
| + rv = append(rv, b.Copy())
|
| + }
|
| + db.modExpire[id] = time.Now().Add(MODIFIED_BUILDS_TIMEOUT)
|
| + db.modBuilds[id] = map[string]*Build{}
|
| + sort.Sort(BuildSlice(rv))
|
| + return rv, nil
|
| +}
|
| +
|
| +func (db *inMemoryDB) clearExpiredModifiedUsers() {
|
| + db.modMtx.Lock()
|
| + defer db.modMtx.Unlock()
|
| + for id, t := range db.modExpire {
|
| + if time.Now().After(t) {
|
| + delete(db.modBuilds, id)
|
| + delete(db.modExpire, id)
|
| + }
|
| + }
|
| +}
|
| +
|
| +func (db *inMemoryDB) modify(b *Build) {
|
| + db.modMtx.Lock()
|
| + defer db.modMtx.Unlock()
|
| + for _, modBuilds := range db.modBuilds {
|
| + modBuilds[b.Id] = b.Copy()
|
| + }
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) PutBuild(build *Build) error {
|
| + db.buildsMtx.Lock()
|
| + defer db.buildsMtx.Unlock()
|
| +
|
| + // TODO(borenet): Keep builds in a sorted slice.
|
| + db.builds[build.Id] = build
|
| + db.modify(build)
|
| + return nil
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) PutBuilds(builds []*Build) error {
|
| + for _, b := range builds {
|
| + if err := db.PutBuild(b); err != nil {
|
| + return err
|
| + }
|
| + }
|
| + return nil
|
| +}
|
| +
|
| +// See docs for DB interface.
|
| +func (db *inMemoryDB) StartTrackingModifiedBuilds() (string, error) {
|
| + db.modMtx.Lock()
|
| + defer db.modMtx.Unlock()
|
| + if len(db.modBuilds) >= MAX_MODIFIED_BUILDS_USERS {
|
| + return "", ErrTooManyUsers
|
| + }
|
| + id := uuid.NewV5(uuid.NewV1(), uuid.NewV4().String()).String()
|
| + db.modBuilds[id] = map[string]*Build{}
|
| + db.modExpire[id] = time.Now().Add(MODIFIED_BUILDS_TIMEOUT)
|
| + return id, nil
|
| +}
|
| +
|
| +// NewInMemoryDB returns an extremely simple, inefficient, in-memory DB implementation.
|
| +func NewInMemoryDB() DB {
|
| + db := &inMemoryDB{
|
| + builds: map[string]*Build{},
|
| + modBuilds: map[string]map[string]*Build{},
|
| + modExpire: map[string]time.Time{},
|
| + }
|
| + go func() {
|
| + for _ = range time.Tick(time.Minute) {
|
| + db.clearExpiredModifiedUsers()
|
| + }
|
| + }()
|
| + return db
|
| +}
|
|
|