github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/task_queue.go (about) 1 package model 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/evergreen-ci/evergreen/db" 8 "github.com/evergreen-ci/evergreen/db/bsonutil" 9 "github.com/pkg/errors" 10 "gopkg.in/mgo.v2" 11 "gopkg.in/mgo.v2/bson" 12 ) 13 14 var ( 15 _ fmt.Stringer = nil 16 ) 17 18 const ( 19 TaskQueuesCollection = "task_queues" 20 ) 21 22 // represents the next n tasks to be run on hosts of the distro 23 type TaskQueue struct { 24 Id bson.ObjectId `bson:"_id,omitempty" json:"_id"` 25 Distro string `bson:"distro" json:"distro"` 26 Queue []TaskQueueItem `bson:"queue" json:"queue"` 27 } 28 29 type TaskDep struct { 30 Id string `bson:"task_id,omitempty" json:"task_id"` 31 DisplayName string `bson:"display_name" json:"display_name"` 32 } 33 type TaskQueueItem struct { 34 Id string `bson:"_id" json:"_id"` 35 DisplayName string `bson:"display_name" json:"display_name"` 36 BuildVariant string `bson:"build_variant" json:"build_variant"` 37 RevisionOrderNumber int `bson:"order" json:"order"` 38 Requester string `bson:"requester" json:"requester"` 39 Revision string `bson:"gitspec" json:"gitspec"` 40 Project string `bson:"project" json:"project"` 41 ExpectedDuration time.Duration `bson:"exp_dur" json:"exp_dur"` 42 Priority int64 `bson:"priority" json:"priority"` 43 } 44 45 var ( 46 // bson fields for the task queue struct 47 TaskQueueIdKey = bsonutil.MustHaveTag(TaskQueue{}, "Id") 48 TaskQueueDistroKey = bsonutil.MustHaveTag(TaskQueue{}, "Distro") 49 TaskQueueQueueKey = bsonutil.MustHaveTag(TaskQueue{}, "Queue") 50 51 // bson fields for the individual task queue items 52 TaskQueueItemIdKey = bsonutil.MustHaveTag(TaskQueueItem{}, "Id") 53 TaskQueueItemDisplayNameKey = bsonutil.MustHaveTag(TaskQueueItem{}, 54 "DisplayName") 55 TaskQueueItemBuildVariantKey = bsonutil.MustHaveTag(TaskQueueItem{}, 56 "BuildVariant") 57 TaskQueueItemConKey = bsonutil.MustHaveTag(TaskQueueItem{}, 58 "RevisionOrderNumber") 59 TaskQueueItemRequesterKey = bsonutil.MustHaveTag(TaskQueueItem{}, 60 "Requester") 61 TaskQueueItemRevisionKey = bsonutil.MustHaveTag(TaskQueueItem{}, 62 "Revision") 63 TaskQueueItemProjectKey = bsonutil.MustHaveTag(TaskQueueItem{}, 64 "Project") 65 TaskQueueItemExpDurationKey = bsonutil.MustHaveTag(TaskQueueItem{}, 66 "ExpectedDuration") 67 TaskQueuePriorityKey = bsonutil.MustHaveTag(TaskQueueItem{}, 68 "Priority") 69 ) 70 71 func (self *TaskQueue) Length() int { 72 return len(self.Queue) 73 } 74 75 func (self *TaskQueue) IsEmpty() bool { 76 return len(self.Queue) == 0 77 } 78 79 func (self *TaskQueue) NextTask() TaskQueueItem { 80 return self.Queue[0] 81 } 82 83 func (self *TaskQueue) Save() error { 84 return UpdateTaskQueue(self.Distro, self.Queue) 85 } 86 87 func UpdateTaskQueue(distro string, taskQueue []TaskQueueItem) error { 88 _, err := db.Upsert( 89 TaskQueuesCollection, 90 bson.M{ 91 TaskQueueDistroKey: distro, 92 }, 93 bson.M{ 94 "$set": bson.M{ 95 TaskQueueQueueKey: taskQueue, 96 }, 97 }, 98 ) 99 return err 100 } 101 102 func FindTaskQueueForDistro(distroId string) (*TaskQueue, error) { 103 taskQueue := &TaskQueue{} 104 err := db.FindOne( 105 TaskQueuesCollection, 106 bson.M{ 107 TaskQueueDistroKey: distroId, 108 }, 109 db.NoProjection, 110 db.NoSort, 111 taskQueue, 112 ) 113 if err == mgo.ErrNotFound { 114 return nil, nil 115 } 116 return taskQueue, err 117 } 118 119 // FindMinimumQueuePositionForTask finds the position of a task in the many task queues 120 // where its position is the lowest. It returns an error if the aggregation it runs fails. 121 func FindMinimumQueuePositionForTask(taskId string) (int, error) { 122 var results []struct { 123 Index int `bson:"index"` 124 } 125 queueItemIdKey := fmt.Sprintf("%v.%v", TaskQueueQueueKey, TaskQueueItemIdKey) 126 127 // NOTE: this aggregation requires 3.2+ because of its use of 128 // $unwind's 'path' and 'includeArrayIndex' 129 pipeline := []bson.M{ 130 {"$match": bson.M{ 131 queueItemIdKey: taskId}}, 132 {"$unwind": bson.M{ 133 "path": fmt.Sprintf("$%s", TaskQueueQueueKey), 134 "includeArrayIndex": "index"}}, 135 {"$match": bson.M{ 136 queueItemIdKey: taskId}}, 137 {"$sort": bson.M{ 138 "index": 1}}, 139 {"$limit": 1}, 140 } 141 142 err := db.Aggregate(TaskQueuesCollection, pipeline, &results) 143 144 if len(results) == 0 { 145 return -1, err 146 } 147 148 return (results[0].Index + 1), err 149 } 150 151 func FindAllTaskQueues() ([]TaskQueue, error) { 152 taskQueues := []TaskQueue{} 153 err := db.FindAll( 154 TaskQueuesCollection, 155 bson.M{}, 156 db.NoProjection, 157 db.NoSort, 158 db.NoSkip, 159 db.NoLimit, 160 &taskQueues, 161 ) 162 return taskQueues, err 163 } 164 165 // pull out the task with the specified id from both the in-memory and db 166 // versions of the task queue 167 func (self *TaskQueue) DequeueTask(taskId string) error { 168 169 // first, remove from the in-memory queue 170 found := false 171 for idx, queueItem := range self.Queue { 172 if queueItem.Id == taskId { 173 found = true 174 self.Queue = append(self.Queue[:idx], self.Queue[idx+1:]...) 175 continue 176 } 177 } 178 179 // validate that the task is there 180 if !found { 181 return errors.Errorf("task id %s was not present in queue for distro %s", 182 taskId, self.Distro) 183 } 184 185 return errors.WithStack(db.Update( 186 TaskQueuesCollection, 187 bson.M{ 188 TaskQueueDistroKey: self.Distro, 189 }, 190 bson.M{ 191 "$pull": bson.M{ 192 TaskQueueQueueKey: bson.M{ 193 TaskQueueItemIdKey: taskId, 194 }, 195 }, 196 }, 197 )) 198 }