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  }