github.com/TIBCOSoftware/flogo-lib@v0.5.9/engine/runner/pooled.go (about)

     1  package runner
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/TIBCOSoftware/flogo-lib/core/action"
     8  	"github.com/TIBCOSoftware/flogo-lib/core/data"
     9  	"github.com/TIBCOSoftware/flogo-lib/logger"
    10  )
    11  
    12  // PooledRunner is a action runner that queues and runs a action in a worker pool
    13  type PooledRunner struct {
    14  	workerQueue chan chan ActionWorkRequest
    15  	workQueue   chan ActionWorkRequest
    16  	numWorkers  int
    17  	workers     []*ActionWorker
    18  	active      bool
    19  
    20  	directRunner *DirectRunner
    21  }
    22  
    23  // PooledConfig is the configuration object for a PooledRunner
    24  type PooledConfig struct {
    25  	NumWorkers    int `json:"numWorkers"`
    26  	WorkQueueSize int `json:"workQueueSize"`
    27  }
    28  
    29  // NewPooledRunner create a new pooled
    30  func NewPooled(config *PooledConfig) *PooledRunner {
    31  
    32  	var pooledRunner PooledRunner
    33  	pooledRunner.directRunner = NewDirect()
    34  
    35  	// config via engine config
    36  	pooledRunner.numWorkers = config.NumWorkers
    37  	pooledRunner.workQueue = make(chan ActionWorkRequest, config.WorkQueueSize)
    38  
    39  	return &pooledRunner
    40  }
    41  
    42  // Start will start the engine, by starting all of its workers
    43  func (runner *PooledRunner) Start() error {
    44  
    45  	if !runner.active {
    46  
    47  		runner.workerQueue = make(chan chan ActionWorkRequest, runner.numWorkers)
    48  
    49  		runner.workers = make([]*ActionWorker, runner.numWorkers)
    50  
    51  		for i := 0; i < runner.numWorkers; i++ {
    52  			id := i + 1
    53  			logger.Debugf("Starting worker with id '%d'", id)
    54  			worker := NewWorker(id, runner.directRunner, runner.workerQueue)
    55  			runner.workers[i] = &worker
    56  			worker.Start()
    57  		}
    58  
    59  		go func() {
    60  			for {
    61  				select {
    62  				case work := <-runner.workQueue:
    63  					logger.Debug("Received work request")
    64  
    65  					//todo fix, this creates unbounded go routines waiting to be serviced by worker queue
    66  					go func() {
    67  						worker := <-runner.workerQueue
    68  
    69  						logger.Debug("Dispatching work request")
    70  						worker <- work
    71  					}()
    72  				}
    73  			}
    74  		}()
    75  
    76  		runner.active = true
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  // Stop will stop the engine, by stopping all of its workers
    83  func (runner *PooledRunner) Stop() error {
    84  
    85  	if runner.active {
    86  
    87  		runner.active = false
    88  
    89  		for _, worker := range runner.workers {
    90  			logger.Debug("Stopping worker", worker.ID)
    91  			worker.Stop()
    92  		}
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // Deprecated: Use Execute() instead
    99  func (runner *PooledRunner) Run(ctx context.Context, act action.Action, uri string, options interface{}) (code int, data interface{}, err error) {
   100  
   101  	return 0, nil, errors.New("unsupported")
   102  }
   103  
   104  // Deprecated: Use Execute() instead
   105  func (runner *PooledRunner) RunAction(ctx context.Context, act action.Action, options map[string]interface{}) (results map[string]*data.Attribute, err error) {
   106  
   107  	return nil, errors.New("unsupported")
   108  }
   109  
   110  // Execute implements action.Runner.Execute
   111  func (runner *PooledRunner) Execute(ctx context.Context, act action.Action, inputs map[string]*data.Attribute) (results map[string]*data.Attribute, err error) {
   112  
   113  	if act == nil {
   114  		return nil, errors.New("Action not specified")
   115  	}
   116  
   117  	if runner.active {
   118  
   119  		actionData := &ActionData{context: ctx, action: act, inputs: inputs, arc: make(chan *ActionResult, 1)}
   120  		work := ActionWorkRequest{ReqType: RtRun, actionData: actionData}
   121  
   122  		md := action.GetMetadata(act)
   123  
   124  		runner.workQueue <- work
   125  		logger.Debugf("Action '%s' queued", md.ID)
   126  
   127  		reply := <-actionData.arc
   128  		logger.Debugf("Action '%s' returned", md.ID)
   129  
   130  		return reply.results, reply.err
   131  	}
   132  
   133  	//Run rejected
   134  	return nil, errors.New("Runner not active")
   135  }