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 }