github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/workspace_queue.go (about)

     1  package ots
     2  
     3  type PlanEnqueuer interface {
     4  	EnqueuePlan(id string) error
     5  }
     6  
     7  // Queue implementations are able to add and remove runs from a queue-like
     8  // structure
     9  type Queue interface {
    10  	Add(*Run) error
    11  	Remove(*Run) error
    12  }
    13  
    14  // WorkspaceQueue is the queue of runs for a workspace. The queue has at most
    15  // one active run, which blocks other pending runs. Speculative runs do not
    16  // block and are therefore not added to the queue.
    17  type WorkspaceQueue struct {
    18  	// Active is the currently active run.
    19  	Active *Run
    20  	// Pending is the list of pending runs waiting for the active run to
    21  	// complete.
    22  	Pending []*Run
    23  	// PlanEnqueuer enqueues a plan onto the global queue
    24  	PlanEnqueuer
    25  }
    26  
    27  // Add adds a run to the workspace queue.
    28  func (q *WorkspaceQueue) Add(run *Run) error {
    29  	// Enqueue speculative runs onto (global) queue but don't make them active
    30  	// because they do not block pending runs
    31  	if run.IsSpeculative() {
    32  		return q.EnqueuePlan(run.ID)
    33  	}
    34  
    35  	// No run is current active, so make this run active
    36  	if q.Active == nil {
    37  		if err := q.EnqueuePlan(run.ID); err != nil {
    38  			return err
    39  		}
    40  
    41  		q.Active = run
    42  		return nil
    43  	}
    44  
    45  	// Other add run to pending queue
    46  	q.Pending = append(q.Pending, run)
    47  
    48  	return nil
    49  }
    50  
    51  // Remove removes a run from the queue.
    52  func (q *WorkspaceQueue) Remove(run *Run) error {
    53  	// Speculative runs are never added to the queue in the first place so they
    54  	// do not need to be removed
    55  	if run.IsSpeculative() {
    56  		return nil
    57  	}
    58  
    59  	// Remove active run and make the first pending run the active run
    60  	if q.Active.ID == run.ID {
    61  		q.Active = nil
    62  		if len(q.Pending) > 0 {
    63  			if err := q.EnqueuePlan(q.Pending[0].ID); err != nil {
    64  				return err
    65  			}
    66  
    67  			q.Active = q.Pending[0]
    68  			q.Pending = q.Pending[1:]
    69  		}
    70  		return nil
    71  	}
    72  
    73  	// Remove run from pending queue
    74  	for idx, p := range q.Pending {
    75  		if p.ID == run.ID {
    76  			q.Pending = append(q.Pending[:idx], q.Pending[idx+1:]...)
    77  			return nil
    78  		}
    79  	}
    80  
    81  	return nil
    82  }