github.com/aergoio/aergo@v1.3.1/pkg/component/hub.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package component
     7  
     8  import (
     9  	"errors"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/aergoio/aergo-actor/actor"
    14  	"github.com/aergoio/aergo-lib/log"
    15  	"github.com/opentracing/opentracing-go"
    16  	"github.com/gofrs/uuid"
    17  )
    18  
    19  var (
    20  	ErrHubUnregistered = errors.New("Unregistered Component")
    21  	logger             = log.NewLogger("actor")
    22  )
    23  
    24  // ICompSyncRequester is the interface that wraps the RequestFuture method.
    25  type ICompSyncRequester interface {
    26  	Tell(targetName string, message interface{})
    27  	RequestFuture(targetName string, message interface{}, timeout time.Duration, tip string) *actor.Future
    28  }
    29  
    30  // ComponentHub keeps a list of registered components
    31  type ComponentHub struct {
    32  	components map[string]IComponent
    33  	spanLock   sync.Mutex
    34  	spans      map[string]*opentracing.Span
    35  }
    36  
    37  type hubInitSync struct {
    38  	sync.WaitGroup
    39  	finished chan interface{}
    40  }
    41  
    42  var hubInit hubInitSync
    43  
    44  // NewComponentHub creates and returns ComponentHub instance
    45  func NewComponentHub() *ComponentHub {
    46  	hub := ComponentHub{
    47  		components: make(map[string]IComponent),
    48  		spans:      make(map[string]*opentracing.Span),
    49  	}
    50  	return &hub
    51  }
    52  
    53  func (h *hubInitSync) begin(n int) {
    54  	h.finished = make(chan interface{})
    55  	h.Add(n)
    56  }
    57  
    58  func (h *hubInitSync) end() {
    59  	h.Wait()
    60  	close(h.finished)
    61  }
    62  
    63  func (h *hubInitSync) wait() {
    64  	h.Done()
    65  	<-h.finished
    66  }
    67  
    68  func (hub *ComponentHub) SaveSpan(span opentracing.Span) string {
    69  	id := uuid.Must(uuid.NewV4()).String()
    70  	hub.spanLock.Lock()
    71  	defer hub.spanLock.Unlock()
    72  	hub.spans[id] = &span
    73  	return id
    74  }
    75  
    76  func (hub *ComponentHub) RestoreSpan(id string) *opentracing.Span {
    77  	hub.spanLock.Lock()
    78  	defer hub.spanLock.Unlock()
    79  	return hub.spans[id]
    80  }
    81  
    82  func (hub *ComponentHub) DestroySpan(id string) {
    83  	hub.spanLock.Lock()
    84  	defer hub.spanLock.Unlock()
    85  	span := hub.spans[id]
    86  	if nil != span {
    87  		(*span).Finish()
    88  		delete(hub.spans, id)
    89  	}
    90  }
    91  
    92  // Start invokes start funcs of registered components at this hub
    93  func (hub *ComponentHub) Start() {
    94  	hubInit.begin(len(hub.components))
    95  	for _, comp := range hub.components {
    96  		go comp.Start()
    97  	}
    98  	hubInit.end()
    99  }
   100  
   101  // Stop invokes stop funcs of registered components at this hub
   102  func (hub *ComponentHub) Stop() {
   103  	for _, comp := range hub.components {
   104  		comp.Stop()
   105  	}
   106  }
   107  
   108  // Register assigns a component to this hub for management
   109  func (hub *ComponentHub) Register(components ...IComponent) {
   110  	for _, component := range components {
   111  		if component != nil {
   112  			hub.components[component.GetName()] = component
   113  			component.SetHub(hub)
   114  		}
   115  	}
   116  }
   117  
   118  // Statistics invoke requests to all registered components,
   119  // collect and return it's response
   120  // An input argument, timeout, is used to set actor request's timeout.
   121  // If it is over, than future: timeout string set at error field
   122  func (hub *ComponentHub) Statistics(timeOutSec time.Duration, target string) (map[string]*CompStatRsp, error) {
   123  	var components map[string]IComponent
   124  	components = make(map[string]IComponent)
   125  
   126  	if len(target) > 0 {
   127  		component, ok := hub.components[target]
   128  		if ok {
   129  			components[target] = component
   130  		} else {
   131  			return nil, ErrHubUnregistered
   132  		}
   133  	} else {
   134  		components = hub.components
   135  	}
   136  
   137  
   138  	var compStatus map[string]Status
   139  	compStatus = make(map[string]Status)
   140  
   141  	// check a status of all components before ask a profiling
   142  	// request the profiling to only alive components
   143  	for _, comp := range components {
   144  		compStatus[comp.GetName()] = comp.Status()
   145  	}
   146  
   147  	// get current time and add this to a request
   148  	// to estimate standing time at an actor's mailbox
   149  	msgQueuedTime := time.Now()
   150  
   151  	var jobMap map[string]*actor.Future
   152  	jobMap = make(map[string]*actor.Future)
   153  	var retCompStatistics map[string]*CompStatRsp
   154  	retCompStatistics = make(map[string]*CompStatRsp)
   155  
   156  	for name, comp := range components {
   157  		if compStatus[name] == StartedStatus {
   158  			// send a request to all component asynchronously
   159  			jobMap[name] = comp.RequestFuture(
   160  				&CompStatReq{msgQueuedTime},
   161  				timeOutSec,
   162  				"pkg/component/hub.Status")
   163  		} else {
   164  			// in the case of non-started components, just record its status
   165  			retCompStatistics[name] = &CompStatRsp{
   166  				Status: StatusToString(compStatus[name]),
   167  			}
   168  		}
   169  	}
   170  
   171  	// for each asynchronously thrown jobs
   172  	for name, job := range jobMap {
   173  		// wait and get a result
   174  		result, err := job.Result()
   175  		if err != nil {
   176  			// when error is occurred, record it.
   177  			// the most frequently occurred error will be a timeout error
   178  			retCompStatistics[name] = &CompStatRsp{
   179  				Status:      StatusToString(compStatus[name]),
   180  				MsgQueueLen: uint64(hub.Get(name).MsgQueueLen()),
   181  				Error:       err.Error(),
   182  			}
   183  		} else {
   184  			// in normal case, success, record response
   185  			retCompStatistics[name] = result.(*CompStatRsp)
   186  		}
   187  	}
   188  
   189  	return retCompStatistics, nil
   190  }
   191  
   192  // Tell pass and forget a message to a component, which has a targetName
   193  func (hub *ComponentHub) Tell(targetName string, message interface{}) {
   194  	targetComponent := hub.components[targetName]
   195  	if targetComponent == nil {
   196  		panic("Unregistered Component")
   197  	}
   198  
   199  	targetComponent.Tell(message)
   200  }
   201  
   202  // RequestFuture pass a message to a component, which has a targetName
   203  // And this returns a future instance to be used in waiting a response
   204  func (hub *ComponentHub) RequestFuture(
   205  	targetName string, message interface{}, timeout time.Duration, tip string) *actor.Future {
   206  
   207  	targetComponent := hub.components[targetName]
   208  	if targetComponent == nil {
   209  		err := actor.NewFuture(timeout)
   210  		err.PID().Tell(ErrHubUnregistered)
   211  		return err
   212  	}
   213  
   214  	return targetComponent.RequestFuture(message, timeout, tip)
   215  }
   216  
   217  func (hub *ComponentHub) RequestFutureResult(
   218  	targetName string, message interface{}, timeout time.Duration, tip string) (interface{}, error) {
   219  
   220  	targetComponent := hub.components[targetName]
   221  	if targetComponent == nil {
   222  		return nil, ErrHubUnregistered
   223  	}
   224  
   225  	return targetComponent.RequestFuture(message, timeout, tip).Result()
   226  }
   227  
   228  // Get returns a component which has a targetName
   229  func (hub *ComponentHub) Get(targetName string) IComponent {
   230  	targetComponent := hub.components[targetName]
   231  	if targetComponent == nil {
   232  		panic("Unregistered Component")
   233  	}
   234  
   235  	return targetComponent
   236  }