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  }