github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/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 clusters
    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/signatures"
    26  	"github.com/aacfactory/fns/commons/versions"
    27  	"github.com/aacfactory/fns/context"
    28  	fLog "github.com/aacfactory/fns/logs"
    29  	"github.com/aacfactory/fns/runtime"
    30  	"github.com/aacfactory/fns/services"
    31  	"github.com/aacfactory/fns/services/tracings"
    32  	"github.com/aacfactory/fns/transports"
    33  	"github.com/aacfactory/logs"
    34  	"github.com/aacfactory/workers"
    35  	"reflect"
    36  	"sort"
    37  	"sync"
    38  	"time"
    39  )
    40  
    41  func NewManager(id string, version versions.Version, address string, cluster Cluster, local services.EndpointsManager, worker workers.Workers, log logs.Logger, dialer transports.Dialer, signature signatures.Signature) ClusterEndpointsManager {
    42  	v := &Manager{
    43  		id:        id,
    44  		version:   version,
    45  		address:   address,
    46  		log:       log.With("cluster", "endpoints"),
    47  		cluster:   cluster,
    48  		local:     local,
    49  		worker:    worker,
    50  		dialer:    dialer,
    51  		signature: signature,
    52  		registration: &Registration{
    53  			values: sync.Map{},
    54  		},
    55  	}
    56  	return v
    57  }
    58  
    59  type ClusterEndpointsManager interface {
    60  	services.EndpointsManager
    61  	FnAddress(ctx context.Context, endpoint []byte, fnName []byte, options ...services.EndpointGetOption) (address string, internal bool, has bool)
    62  }
    63  
    64  type Manager struct {
    65  	id           string
    66  	version      versions.Version
    67  	address      string
    68  	log          logs.Logger
    69  	cluster      Cluster
    70  	local        services.EndpointsManager
    71  	worker       workers.Workers
    72  	dialer       transports.Dialer
    73  	signature    signatures.Signature
    74  	registration *Registration
    75  }
    76  
    77  func (manager *Manager) Add(service services.Service) (err error) {
    78  	err = manager.local.Add(service)
    79  	if err != nil {
    80  		return
    81  	}
    82  	functions := make(services.FnInfos, 0, len(service.Functions()))
    83  	for _, fn := range service.Functions() {
    84  		functions = append(functions, services.FnInfo{
    85  			Name:     fn.Name(),
    86  			Readonly: fn.Readonly(),
    87  			Internal: service.Internal() || fn.Internal(),
    88  		})
    89  	}
    90  	sort.Sort(functions)
    91  	info, infoErr := NewService(service.Name(), service.Internal(), functions, service.Document())
    92  	if infoErr != nil {
    93  		err = errors.Warning("fns: create cluster service info failed").WithCause(infoErr).WithMeta("endpoint", service.Name())
    94  		return
    95  	}
    96  	manager.cluster.AddService(info)
    97  	return
    98  }
    99  
   100  func (manager *Manager) Info() (infos services.EndpointInfos) {
   101  	infos = manager.registration.Infos()
   102  	if infos == nil {
   103  		infos = make(services.EndpointInfos, 0)
   104  	}
   105  	local := manager.local.Info()
   106  	infos = append(infos, local...)
   107  	sort.Sort(infos)
   108  	return
   109  }
   110  
   111  func (manager *Manager) FnAddress(ctx context.Context, endpoint []byte, fnName []byte, options ...services.EndpointGetOption) (address string, internal bool, has bool) {
   112  	local, inLocal := manager.local.Get(ctx, endpoint, options...)
   113  	if inLocal {
   114  		fnNameString := bytex.ToString(fnName)
   115  		for _, fn := range local.Functions() {
   116  			if fn.Name() == fnNameString {
   117  				address = manager.address
   118  				internal = fn.Internal()
   119  				has = true
   120  				return
   121  			}
   122  		}
   123  	}
   124  
   125  	if len(options) == 0 {
   126  		matched := manager.registration.MaxOne(endpoint)
   127  		if matched == nil || reflect.ValueOf(matched).IsNil() {
   128  			return
   129  		}
   130  		if fn, hasFn := matched.Functions().Find(fnName); hasFn {
   131  			address = matched.Address()
   132  			internal = fn.Internal()
   133  			has = true
   134  		}
   135  		return
   136  	}
   137  
   138  	opt := services.EndpointGetOptions{}
   139  	for _, option := range options {
   140  		option(&opt)
   141  	}
   142  
   143  	interval, hasVersion := opt.Versions().Get(endpoint)
   144  	if hasVersion {
   145  		matched := manager.registration.Range(endpoint, interval)
   146  		if matched == nil || reflect.ValueOf(matched).IsNil() {
   147  			return
   148  		}
   149  		if fn, hasFn := matched.Functions().Find(fnName); hasFn {
   150  			address = matched.Address()
   151  			internal = fn.Internal()
   152  			has = true
   153  		}
   154  		return
   155  	}
   156  	if endpointId := opt.Id(); len(endpointId) > 0 {
   157  		matched := manager.registration.Get(endpoint, endpointId)
   158  		if matched == nil || reflect.ValueOf(matched).IsNil() {
   159  			return
   160  		}
   161  		if fn, hasFn := matched.Functions().Find(fnName); hasFn {
   162  			address = matched.Address()
   163  			internal = fn.Internal()
   164  			has = true
   165  		}
   166  		return
   167  	}
   168  	return
   169  }
   170  
   171  func (manager *Manager) Get(ctx context.Context, name []byte, options ...services.EndpointGetOption) (endpoint services.Endpoint, has bool) {
   172  	// local
   173  	local, inLocal := manager.local.Get(ctx, name, options...)
   174  	if inLocal {
   175  		endpoint = local
   176  		has = true
   177  		return
   178  	}
   179  	// max one
   180  	if len(options) == 0 {
   181  		endpoint = manager.registration.MaxOne(name)
   182  		has = !reflect.ValueOf(endpoint).IsNil()
   183  		return
   184  	}
   185  
   186  	opt := services.EndpointGetOptions{}
   187  	for _, option := range options {
   188  		option(&opt)
   189  	}
   190  	// get by id
   191  	if eid := opt.Id(); len(eid) > 0 {
   192  		endpoint = manager.registration.Get(name, eid)
   193  		has = !reflect.ValueOf(endpoint).IsNil()
   194  		return
   195  	}
   196  	// get by intervals
   197  	if intervals := opt.Versions(); len(intervals) > 0 {
   198  		interval, matched := intervals.Get(name)
   199  		if !matched {
   200  			return
   201  		}
   202  		endpoint = manager.registration.Range(name, interval)
   203  		has = !reflect.ValueOf(endpoint).IsNil()
   204  		return
   205  	}
   206  	return
   207  }
   208  
   209  func (manager *Manager) RequestAsync(ctx context.Context, name []byte, fn []byte, param any, options ...services.RequestOption) (future futures.Future, err error) {
   210  	// valid params
   211  	if len(name) == 0 {
   212  		err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil"))
   213  		return
   214  	}
   215  	if len(fn) == 0 {
   216  		err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil"))
   217  		return
   218  	}
   219  	// request
   220  	req := services.NewRequest(ctx, name, fn, param, options...)
   221  	// get endpoint
   222  	var endpointGetOptions []services.EndpointGetOption
   223  	if endpointId := req.Header().EndpointId(); len(endpointId) > 0 {
   224  		endpointGetOptions = make([]services.EndpointGetOption, 0, 1)
   225  		endpointGetOptions = append(endpointGetOptions, services.EndpointId(endpointId))
   226  	}
   227  	if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 {
   228  		if endpointGetOptions == nil {
   229  			endpointGetOptions = make([]services.EndpointGetOption, 0, 1)
   230  		}
   231  		endpointGetOptions = append(endpointGetOptions, services.EndpointVersions(acceptedVersions))
   232  	}
   233  	endpoint, found := manager.Get(req, name, endpointGetOptions...)
   234  	if !found {
   235  		err = errors.NotFound("fns: endpoint was not found").
   236  			WithMeta("endpoint", bytex.ToString(name)).
   237  			WithMeta("fn", bytex.ToString(fn))
   238  		return
   239  	}
   240  	// get fn
   241  	function, hasFunction := endpoint.Functions().Find(fn)
   242  	if !hasFunction {
   243  		err = errors.NotFound("fns: endpoint was not found").
   244  			WithMeta("endpoint", bytex.ToString(name)).
   245  			WithMeta("fn", bytex.ToString(fn))
   246  		return
   247  	}
   248  	// log
   249  	fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn)))
   250  	// components
   251  	service, ok := endpoint.(services.Service)
   252  	if ok {
   253  		components := service.Components()
   254  		if len(components) > 0 {
   255  			services.WithComponents(req, name, components)
   256  		}
   257  	}
   258  	// tracing
   259  	trace, hasTrace := tracings.Load(req)
   260  	if hasTrace {
   261  		trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local")
   262  	}
   263  	// promise
   264  	var promise futures.Promise
   265  	promise, future = futures.New()
   266  	// dispatch
   267  	dispatched := manager.worker.Dispatch(req, services.FnTask{
   268  		Fn:      function,
   269  		Promise: promise,
   270  	})
   271  	if !dispatched {
   272  		// release promise
   273  		futures.ReleaseUnused(promise)
   274  		future = nil
   275  		// tracing
   276  		if hasTrace {
   277  			trace.Finish("succeed", "false", "cause", "***TOO MANY REQUEST***")
   278  		}
   279  		err = errors.TooMayRequest("fns: too may request, try again later.").
   280  			WithMeta("endpoint", bytex.ToString(name)).
   281  			WithMeta("fn", bytex.ToString(fn))
   282  	}
   283  	return
   284  }
   285  
   286  func (manager *Manager) Request(ctx context.Context, name []byte, fn []byte, param interface{}, options ...services.RequestOption) (response services.Response, err error) {
   287  	// valid params
   288  	if len(name) == 0 {
   289  		err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("name is nil"))
   290  		return
   291  	}
   292  	if len(fn) == 0 {
   293  		err = errors.Warning("fns: endpoints handle request failed").WithCause(fmt.Errorf("fn is nil"))
   294  		return
   295  	}
   296  
   297  	// request
   298  	req := services.AcquireRequest(ctx, name, fn, param, options...)
   299  	defer services.ReleaseRequest(req)
   300  	// get endpoint
   301  	var endpointGetOptions []services.EndpointGetOption
   302  	if endpointId := req.Header().EndpointId(); len(endpointId) > 0 {
   303  		endpointGetOptions = make([]services.EndpointGetOption, 0, 1)
   304  		endpointGetOptions = append(endpointGetOptions, services.EndpointId(endpointId))
   305  	}
   306  	if acceptedVersions := req.Header().AcceptedVersions(); len(acceptedVersions) > 0 {
   307  		if endpointGetOptions == nil {
   308  			endpointGetOptions = make([]services.EndpointGetOption, 0, 1)
   309  		}
   310  		endpointGetOptions = append(endpointGetOptions, services.EndpointVersions(acceptedVersions))
   311  	}
   312  	endpoint, found := manager.Get(req, name, endpointGetOptions...)
   313  	if !found {
   314  		err = errors.NotFound("fns: endpoint was not found").
   315  			WithMeta("endpoint", bytex.ToString(name)).
   316  			WithMeta("fn", bytex.ToString(fn))
   317  		return
   318  	}
   319  	// get fn
   320  	function, hasFunction := endpoint.Functions().Find(fn)
   321  	if !hasFunction {
   322  		err = errors.NotFound("fns: endpoint was not found").
   323  			WithMeta("endpoint", bytex.ToString(name)).
   324  			WithMeta("fn", bytex.ToString(fn))
   325  		return
   326  	}
   327  	// log
   328  	fLog.With(req, manager.log.With("service", bytex.ToString(name)).With("fn", bytex.ToString(fn)))
   329  	// components
   330  	service, ok := endpoint.(services.Service)
   331  	if ok {
   332  		components := service.Components()
   333  		if len(components) > 0 {
   334  			services.WithComponents(req, name, components)
   335  		}
   336  	}
   337  	// tracing
   338  	trace, hasTrace := tracings.Load(req)
   339  	if hasTrace {
   340  		trace.Begin(req.Header().ProcessId(), name, fn, "scope", "local")
   341  		trace.Waited()
   342  	}
   343  	// handle
   344  	result, handleErr := function.Handle(req)
   345  	if handleErr != nil {
   346  		codeErr := errors.Wrap(handleErr).WithMeta("endpoint", bytex.ToString(name)).WithMeta("fn", bytex.ToString(fn))
   347  		if hasTrace {
   348  			trace.Finish("succeed", "false", "cause", codeErr.Name())
   349  		}
   350  		err = codeErr
   351  		return
   352  	}
   353  	if hasTrace {
   354  		trace.Finish("succeed", "true")
   355  	}
   356  	response = services.NewResponse(result)
   357  	return
   358  }
   359  
   360  func (manager *Manager) Listen(ctx context.Context) (err error) {
   361  	// watching
   362  	manager.watching()
   363  	// cluster.join
   364  	err = manager.cluster.Join(ctx)
   365  	if err != nil {
   366  		if manager.log.WarnEnabled() {
   367  			manager.log.Warn().With("cluster", "join").Cause(err).Message("fns: cluster join failed")
   368  		}
   369  		return
   370  	}
   371  	// local.listen
   372  	err = manager.local.Listen(ctx)
   373  	if err != nil {
   374  		_ = manager.cluster.Leave(ctx)
   375  		return
   376  	}
   377  	return
   378  }
   379  
   380  func (manager *Manager) Shutdown(ctx context.Context) {
   381  	leaveErr := manager.cluster.Leave(ctx)
   382  	if leaveErr != nil {
   383  		if manager.log.WarnEnabled() {
   384  			manager.log.Warn().With("cluster", "leave").Cause(leaveErr).Message("fns: cluster leave failed")
   385  		}
   386  	}
   387  	manager.local.Shutdown(ctx)
   388  	return
   389  }
   390  
   391  func (manager *Manager) watching() {
   392  	go func(eps *Manager) {
   393  		for {
   394  			event, ok := <-eps.cluster.NodeEvents()
   395  			if !ok {
   396  				break
   397  			}
   398  			if eps.log.DebugEnabled() {
   399  				eps.log.Debug().
   400  					With("event", event.Kind.String()).
   401  					Message(fmt.Sprintf(
   402  						"fns: get node(id:%s addr:%s ver:%s, services:%d) event(%s)",
   403  						event.Node.Id, event.Node.Address, event.Node.Version.String(), len(event.Node.Services), event.Kind.String()))
   404  			}
   405  			switch event.Kind {
   406  			case Add:
   407  				endpoints := make([]*Endpoint, 0, 1)
   408  				client, clientErr := eps.dialer.Dial(bytex.FromString(event.Node.Address))
   409  				if eps.log.DebugEnabled() {
   410  					succeed := "succeed"
   411  					var cause error
   412  					if clientErr != nil {
   413  						succeed = "failed"
   414  						cause = errors.Warning(fmt.Sprintf("fns: dial %s failed", event.Node.Address)).WithMeta("address", event.Node.Address).WithCause(clientErr)
   415  					}
   416  					eps.log.Debug().
   417  						With("cluster", "registrations").
   418  						Cause(cause).
   419  						Message(fmt.Sprintf("fns: dial %s %s", event.Node.Address, succeed))
   420  				}
   421  				if clientErr != nil {
   422  					if eps.log.WarnEnabled() {
   423  						eps.log.Warn().
   424  							With("cluster", "registrations").
   425  							Cause(errors.Warning(fmt.Sprintf("fns: dial %s failed", event.Node.Address)).WithMeta("address", event.Node.Address).WithCause(clientErr)).
   426  							Message(fmt.Sprintf("fns: dial %s failed", event.Node.Address))
   427  					}
   428  					break
   429  				}
   430  				// check health
   431  				active := false
   432  				for i := 0; i < 10; i++ {
   433  					ctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)
   434  					if runtime.CheckHealth(ctx, client) {
   435  						active = true
   436  						cancel()
   437  						break
   438  					}
   439  					cancel()
   440  					if eps.log.DebugEnabled() {
   441  						eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: %s is not health", event.Node.Address))
   442  					}
   443  					time.Sleep(1 * time.Second)
   444  				}
   445  
   446  				if eps.log.DebugEnabled() {
   447  					eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: health of %s is %v", event.Node.Address, active))
   448  				}
   449  				if !active {
   450  					break
   451  				}
   452  				// get document
   453  				for _, endpoint := range event.Node.Services {
   454  					document, documentErr := endpoint.Document()
   455  					if documentErr != nil {
   456  						if eps.log.WarnEnabled() {
   457  							eps.log.Warn().
   458  								With("cluster", "registrations").
   459  								Cause(errors.Warning("fns: get endpoint document failed").WithMeta("address", event.Node.Address).WithCause(documentErr)).
   460  								Message(fmt.Sprintf("fns: dial %s failed", event.Node.Address))
   461  						}
   462  						continue
   463  					}
   464  					ep := NewEndpoint(manager.log, event.Node.Address, event.Node.Id, event.Node.Version, endpoint.Name, endpoint.Internal, document, client, eps.signature)
   465  					for _, fnInfo := range endpoint.Functions {
   466  						ep.AddFn(fnInfo.Name, fnInfo.Internal, fnInfo.Readonly)
   467  					}
   468  					endpoints = append(endpoints, ep)
   469  				}
   470  				for _, endpoint := range endpoints {
   471  					eps.registration.Add(endpoint)
   472  				}
   473  				if eps.log.DebugEnabled() {
   474  					eps.log.Debug().With("cluster", "registrations").Message(fmt.Sprintf("fns: %s added", event.Node.Address))
   475  				}
   476  				break
   477  			case Remove:
   478  				for _, endpoint := range event.Node.Services {
   479  					eps.registration.Remove(endpoint.Name, event.Node.Id)
   480  				}
   481  				break
   482  			default:
   483  				break
   484  			}
   485  		}
   486  	}(manager)
   487  	return
   488  }