github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/services/manager.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package services 19 20 import ( 21 "fmt" 22 "github.com/aacfactory/errors" 23 "github.com/aacfactory/fns/commons/bytex" 24 "github.com/aacfactory/fns/commons/futures" 25 "github.com/aacfactory/fns/commons/versions" 26 "github.com/aacfactory/fns/context" 27 fLog "github.com/aacfactory/fns/logs" 28 "github.com/aacfactory/fns/services/tracings" 29 "github.com/aacfactory/logs" 30 "github.com/aacfactory/workers" 31 "sort" 32 "strings" 33 "sync" 34 "time" 35 ) 36 37 func New(id string, version versions.Version, log logs.Logger, config Config, worker workers.Workers) EndpointsManager { 38 return &Manager{ 39 log: log, 40 config: config, 41 id: id, 42 version: version, 43 values: make(Services, 0, 1), 44 infos: make(EndpointInfos, 0, 1), 45 worker: worker, 46 } 47 } 48 49 type EndpointsManager interface { 50 Endpoints 51 Add(service Service) (err error) 52 Listen(ctx context.Context) (err error) 53 Shutdown(ctx context.Context) 54 } 55 56 type Manager struct { 57 log logs.Logger 58 config Config 59 id string 60 version versions.Version 61 values Services 62 infos EndpointInfos 63 worker workers.Workers 64 } 65 66 func (manager *Manager) Add(service Service) (err error) { 67 name := strings.TrimSpace(service.Name()) 68 if _, has := manager.values.Find([]byte(name)); has { 69 err = errors.Warning("fns: services add service failed").WithMeta("service", name).WithCause(fmt.Errorf("service has added")) 70 return 71 } 72 config, configErr := manager.config.Get(name) 73 if configErr != nil { 74 err = errors.Warning("fns: services add service failed").WithMeta("service", name).WithCause(configErr) 75 return 76 } 77 constructErr := service.Construct(Options{ 78 Id: manager.id, 79 Version: manager.version, 80 Log: manager.log.With("service", name), 81 Config: config, 82 }) 83 if constructErr != nil { 84 err = errors.Warning("fns: services add service failed").WithMeta("service", name).WithCause(constructErr) 85 return 86 } 87 manager.values = manager.values.Add(service) 88 // info 89 internal := service.Internal() 90 functions := make(FnInfos, 0, len(service.Functions())) 91 for _, fn := range service.Functions() { 92 functions = append(functions, FnInfo{ 93 Name: fn.Name(), 94 Readonly: fn.Readonly(), 95 Internal: internal || fn.Internal(), 96 }) 97 } 98 sort.Sort(functions) 99 manager.infos = append(manager.infos, EndpointInfo{ 100 Id: manager.id, 101 Name: service.Name(), 102 Version: manager.version, 103 Internal: internal, 104 Functions: functions, 105 Document: service.Document(), 106 }) 107 sort.Sort(manager.infos) 108 return 109 } 110 111 func (manager *Manager) Info() (infos EndpointInfos) { 112 infos = manager.infos 113 return 114 } 115 116 func (manager *Manager) Get(_ context.Context, name []byte, options ...EndpointGetOption) (endpoint Endpoint, has bool) { 117 if len(options) > 0 { 118 opt := EndpointGetOptions{ 119 id: nil, 120 requestVersions: nil, 121 } 122 for _, option := range options { 123 option(&opt) 124 } 125 if len(opt.id) > 0 { 126 if manager.id != string(opt.id) { 127 return 128 } 129 } 130 if len(opt.requestVersions) > 0 { 131 if !opt.requestVersions.Accept(name, manager.version) { 132 return 133 } 134 } 135 } 136 endpoint, has = manager.values.Find(name) 137 return 138 } 139 140 func (manager *Manager) RequestAsync(ctx context.Context, name []byte, fn []byte, param any, options ...RequestOption) (future futures.Future, err error) { 141 // valid params 142 if len(name) == 0 { 143 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil")) 144 return 145 } 146 if len(fn) == 0 { 147 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil")) 148 return 149 } 150 // request 151 req := NewRequest(ctx, name, fn, param, options...) 152 // get endpoint 153 var endpointGetOptions []EndpointGetOption 154 if endpointId := req.Header().EndpointId(); len(endpointId) > 0 { 155 endpointGetOptions = make([]EndpointGetOption, 0, 1) 156 endpointGetOptions = append(endpointGetOptions, EndpointId(endpointId)) 157 } 158 if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 { 159 if endpointGetOptions == nil { 160 endpointGetOptions = make([]EndpointGetOption, 0, 1) 161 } 162 endpointGetOptions = append(endpointGetOptions, EndpointVersions(acceptedVersions)) 163 } 164 endpoint, found := manager.Get(req, name, endpointGetOptions...) 165 if !found { 166 err = errors.NotFound("fns: endpoint was not found"). 167 WithMeta("endpoint", bytex.ToString(name)). 168 WithMeta("fn", bytex.ToString(fn)) 169 return 170 } 171 // get fn 172 function, hasFunction := endpoint.Functions().Find(fn) 173 if !hasFunction { 174 err = errors.NotFound("fns: endpoint was not found"). 175 WithMeta("endpoint", bytex.ToString(name)). 176 WithMeta("fn", bytex.ToString(fn)) 177 return 178 } 179 // log 180 fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn))) 181 // components 182 service, ok := endpoint.(Service) 183 if ok { 184 components := service.Components() 185 if len(components) > 0 { 186 WithComponents(req, name, components) 187 } 188 } 189 // tracing 190 trace, hasTrace := tracings.Load(req) 191 if hasTrace { 192 trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local") 193 } 194 // promise 195 var promise futures.Promise 196 promise, future = futures.New() 197 // dispatch 198 dispatched := manager.worker.Dispatch(req, FnTask{ 199 Fn: function, 200 Promise: promise, 201 }) 202 if !dispatched { 203 // release promise 204 futures.ReleaseUnused(promise) 205 future = nil 206 // tracing 207 if hasTrace { 208 trace.Finish("succeed", "false", "cause", "***TOO MANY REQUEST***") 209 } 210 err = errors.TooMayRequest("fns: too may request, try again later."). 211 WithMeta("endpoint", bytex.ToString(name)). 212 WithMeta("fn", bytex.ToString(fn)) 213 } 214 return 215 } 216 217 func (manager *Manager) Request(ctx context.Context, name []byte, fn []byte, param interface{}, options ...RequestOption) (response Response, err error) { 218 // valid params 219 if len(name) == 0 { 220 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil")) 221 return 222 } 223 if len(fn) == 0 { 224 err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil")) 225 return 226 } 227 // request 228 req := AcquireRequest(ctx, name, fn, param, options...) 229 defer ReleaseRequest(req) 230 // get endpoint 231 var endpointGetOptions []EndpointGetOption 232 if endpointId := req.Header().EndpointId(); len(endpointId) > 0 { 233 endpointGetOptions = make([]EndpointGetOption, 0, 1) 234 endpointGetOptions = append(endpointGetOptions, EndpointId(endpointId)) 235 } 236 if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 { 237 if endpointGetOptions == nil { 238 endpointGetOptions = make([]EndpointGetOption, 0, 1) 239 } 240 endpointGetOptions = append(endpointGetOptions, EndpointVersions(acceptedVersions)) 241 } 242 endpoint, found := manager.Get(req, name, endpointGetOptions...) 243 if !found { 244 err = errors.NotFound("fns: endpoint was not found"). 245 WithMeta("endpoint", bytex.ToString(name)). 246 WithMeta("fn", bytex.ToString(fn)) 247 return 248 } 249 // get fn 250 function, hasFunction := endpoint.Functions().Find(fn) 251 if !hasFunction { 252 err = errors.NotFound("fns: endpoint was not found"). 253 WithMeta("endpoint", bytex.ToString(name)). 254 WithMeta("fn", bytex.ToString(fn)) 255 return 256 } 257 // log 258 fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn))) 259 // components 260 service, ok := endpoint.(Service) 261 if ok { 262 components := service.Components() 263 if len(components) > 0 { 264 WithComponents(req, name, components) 265 } 266 } 267 // tracing 268 trace, hasTrace := tracings.Load(req) 269 if hasTrace { 270 trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local") 271 trace.Waited() 272 } 273 // handle 274 result, handleErr := function.Handle(req) 275 if handleErr != nil { 276 codeErr := errors.Wrap(handleErr).WithMeta("endpoint", bytex.ToString(name)).WithMeta("fn", bytex.ToString(fn)) 277 if hasTrace { 278 trace.Finish("succeed", "false", "cause", codeErr.Name()) 279 } 280 err = codeErr 281 return 282 } 283 if hasTrace { 284 trace.Finish("succeed", "true") 285 } 286 response = NewResponse(result) 287 return 288 } 289 290 func (manager *Manager) Listen(ctx context.Context) (err error) { 291 errs := errors.MakeErrors() 292 for _, endpoint := range manager.values { 293 ln, ok := endpoint.(Listenable) 294 if !ok { 295 continue 296 } 297 errCh := make(chan error, 1) 298 name := ln.Name() 299 lnCtx := context.WithValue(ctx, "listener", name) 300 fLog.With(lnCtx, manager.log.With("service", name)) 301 if components := ln.Components(); len(components) > 0 { 302 WithComponents(lnCtx, bytex.FromString(name), components) 303 } 304 go func(ctx context.Context, ln Listenable, errCh chan error) { 305 lnErr := ln.Listen(ctx) 306 if lnErr != nil { 307 errCh <- lnErr 308 } 309 }(lnCtx, ln, errCh) 310 select { 311 case lnErr := <-errCh: 312 errs.Append(lnErr) 313 break 314 case <-time.After(5 * time.Second): 315 break 316 } 317 close(errCh) 318 if manager.log.DebugEnabled() { 319 manager.log.Debug().With("service", endpoint.Name()).Message("fns: service is listening...") 320 } 321 } 322 if len(errs) > 0 { 323 err = errs.Error() 324 } 325 return 326 } 327 328 func (manager *Manager) Shutdown(ctx context.Context) { 329 wg := new(sync.WaitGroup) 330 ch := make(chan struct{}, 1) 331 for _, endpoint := range manager.values { 332 wg.Add(1) 333 go func(ctx context.Context, endpoint Endpoint, wg *sync.WaitGroup) { 334 endpoint.Shutdown(ctx) 335 wg.Done() 336 }(ctx, endpoint, wg) 337 } 338 go func(ch chan struct{}, wg *sync.WaitGroup) { 339 wg.Wait() 340 ch <- struct{}{} 341 close(ch) 342 }(ch, wg) 343 select { 344 case <-ctx.Done(): 345 break 346 case <-ch: 347 break 348 } 349 }