github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/endpoints.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/fns/commons/bytex"
    23  	"github.com/aacfactory/fns/commons/signatures"
    24  	"github.com/aacfactory/fns/commons/versions"
    25  	"github.com/aacfactory/fns/commons/window"
    26  	"github.com/aacfactory/fns/context"
    27  	"github.com/aacfactory/fns/services"
    28  	"github.com/aacfactory/fns/services/documents"
    29  	"github.com/aacfactory/fns/transports"
    30  	"github.com/aacfactory/logs"
    31  	"math/rand"
    32  	"sort"
    33  	"sync"
    34  	"sync/atomic"
    35  	"time"
    36  )
    37  
    38  func NewEndpoint(log logs.Logger, address string, id string, version versions.Version, name string, internal bool, document documents.Endpoint, client transports.Client, signature signatures.Signature) (endpoint *Endpoint) {
    39  	endpoint = &Endpoint{
    40  		log: log.With("endpoint", name),
    41  		info: services.EndpointInfo{
    42  			Id:        id,
    43  			Version:   version,
    44  			Address:   address,
    45  			Name:      name,
    46  			Internal:  internal,
    47  			Functions: nil,
    48  			Document:  document,
    49  		},
    50  		running:   atomic.Bool{},
    51  		functions: make(services.Fns, 0, 1),
    52  		client:    client,
    53  		signature: signature,
    54  		errs:      window.NewTimes(10 * time.Second),
    55  	}
    56  	endpoint.running.Store(true)
    57  	return
    58  }
    59  
    60  type Endpoint struct {
    61  	log       logs.Logger
    62  	info      services.EndpointInfo
    63  	running   atomic.Bool
    64  	functions services.Fns
    65  	client    transports.Client
    66  	signature signatures.Signature
    67  	errs      *window.Times
    68  }
    69  
    70  func (endpoint *Endpoint) Running() bool {
    71  	return endpoint.running.Load()
    72  }
    73  
    74  func (endpoint *Endpoint) Id() string {
    75  	return endpoint.info.Id
    76  }
    77  
    78  func (endpoint *Endpoint) Address() string {
    79  	return endpoint.info.Address
    80  }
    81  
    82  func (endpoint *Endpoint) Name() string {
    83  	return endpoint.info.Name
    84  }
    85  
    86  func (endpoint *Endpoint) Internal() bool {
    87  	return endpoint.info.Internal
    88  }
    89  
    90  func (endpoint *Endpoint) Document() documents.Endpoint {
    91  	return endpoint.info.Document
    92  }
    93  
    94  func (endpoint *Endpoint) Functions() services.Fns {
    95  	return endpoint.functions
    96  }
    97  
    98  func (endpoint *Endpoint) Shutdown(_ context.Context) {
    99  	endpoint.running.Store(false)
   100  	endpoint.client.Close()
   101  }
   102  
   103  func (endpoint *Endpoint) IsHealth() bool {
   104  	return endpoint.errs.Value() < 5
   105  }
   106  
   107  func (endpoint *Endpoint) AddFn(name string, internal bool, readonly bool) {
   108  	fn := &Fn{
   109  		log:          endpoint.log.With("fn", name),
   110  		endpointName: endpoint.info.Name,
   111  		address:      endpoint.info.Address,
   112  		name:         name,
   113  		internal:     internal,
   114  		readonly:     readonly,
   115  		path:         bytex.FromString(fmt.Sprintf("/%s/%s", endpoint.info.Name, name)),
   116  		signature:    endpoint.signature,
   117  		errs:         endpoint.errs,
   118  		health:       atomic.Bool{},
   119  		client:       endpoint.client,
   120  	}
   121  	fn.health.Store(true)
   122  	endpoint.functions = endpoint.functions.Add(fn)
   123  	endpoint.info.Functions = append(endpoint.info.Functions, services.FnInfo{
   124  		Name:     name,
   125  		Readonly: readonly,
   126  		Internal: internal,
   127  	})
   128  	sort.Sort(endpoint.info.Functions)
   129  }
   130  
   131  func (endpoint *Endpoint) Info() services.EndpointInfo {
   132  	return endpoint.info
   133  }
   134  
   135  type VersionEndpoints struct {
   136  	version versions.Version
   137  	values  []*Endpoint
   138  	length  uint64
   139  	pos     uint64
   140  }
   141  
   142  func (endpoints *VersionEndpoints) Add(ep *Endpoint) {
   143  	endpoints.values = append(endpoints.values, ep)
   144  	endpoints.length++
   145  	return
   146  }
   147  
   148  func (endpoints *VersionEndpoints) Remove(id []byte) (ok bool) {
   149  	if endpoints.length == 0 {
   150  		return
   151  	}
   152  	idStr := bytex.ToString(id)
   153  	pos := -1
   154  	for i, value := range endpoints.values {
   155  		if value.Id() == idStr {
   156  			pos = i
   157  			break
   158  		}
   159  	}
   160  	if pos == -1 {
   161  		return
   162  	}
   163  	endpoints.values = append(endpoints.values[:pos], endpoints.values[pos+1:]...)
   164  	endpoints.length--
   165  	ok = true
   166  	return
   167  }
   168  
   169  func (endpoints *VersionEndpoints) Get(id []byte) (ep *Endpoint) {
   170  	if endpoints.length == 0 {
   171  		return
   172  	}
   173  	idStr := bytex.ToString(id)
   174  	for _, value := range endpoints.values {
   175  		if value.Id() == idStr {
   176  			ep = value
   177  			return
   178  		}
   179  	}
   180  	return
   181  }
   182  
   183  func (endpoints *VersionEndpoints) Next() (ep *Endpoint) {
   184  	if endpoints.length == 0 {
   185  		return
   186  	}
   187  	for i := uint64(0); i < endpoints.length; i++ {
   188  		pos := atomic.AddUint64(&endpoints.pos, 1) % endpoints.length
   189  		target := endpoints.values[pos]
   190  		if target.Running() && target.IsHealth() {
   191  			ep = target
   192  			return
   193  		}
   194  	}
   195  	return
   196  }
   197  
   198  type SortedVersionEndpoints []*VersionEndpoints
   199  
   200  func (list SortedVersionEndpoints) Len() int {
   201  	return len(list)
   202  }
   203  
   204  func (list SortedVersionEndpoints) Less(i, j int) bool {
   205  	return list[i].version.LessThan(list[j].version)
   206  }
   207  
   208  func (list SortedVersionEndpoints) Swap(i, j int) {
   209  	list[i], list[j] = list[j], list[i]
   210  	return
   211  }
   212  
   213  func (list SortedVersionEndpoints) Get(version versions.Version) *VersionEndpoints {
   214  	for _, vps := range list {
   215  		if vps.version.Equals(version) {
   216  			return vps
   217  		}
   218  	}
   219  	return nil
   220  }
   221  
   222  func (list SortedVersionEndpoints) Add(ep *Endpoint) SortedVersionEndpoints {
   223  	vps := list.Get(ep.info.Version)
   224  	if vps == nil {
   225  		vps = &VersionEndpoints{
   226  			version: ep.info.Version,
   227  			values:  make([]*Endpoint, 0),
   228  			length:  0,
   229  			pos:     0,
   230  		}
   231  		vps.Add(ep)
   232  		newList := append(list, vps)
   233  		sort.Sort(newList)
   234  		return newList
   235  	} else {
   236  		vps.Add(ep)
   237  		return list
   238  	}
   239  }
   240  
   241  type Endpoints struct {
   242  	values SortedVersionEndpoints
   243  	length uint64
   244  	lock   sync.RWMutex
   245  }
   246  
   247  func (endpoints *Endpoints) Add(ep *Endpoint) {
   248  	endpoints.lock.Lock()
   249  	defer endpoints.lock.Unlock()
   250  	endpoints.values = endpoints.values.Add(ep)
   251  	endpoints.length++
   252  	return
   253  }
   254  
   255  func (endpoints *Endpoints) Remove(id []byte) {
   256  	endpoints.lock.Lock()
   257  	defer endpoints.lock.Unlock()
   258  	evict := -1
   259  	for i, value := range endpoints.values {
   260  		if value.Remove(id) {
   261  			if value.length == 0 {
   262  				evict = i
   263  			}
   264  			break
   265  		}
   266  	}
   267  	if evict == -1 {
   268  		return
   269  	}
   270  	endpoints.values = append(endpoints.values[:evict], endpoints.values[evict+1:]...)
   271  	endpoints.length--
   272  }
   273  
   274  func (endpoints *Endpoints) MaxOne() (ep *Endpoint) {
   275  	endpoints.lock.RLock()
   276  	defer endpoints.lock.RUnlock()
   277  	if endpoints.length == 0 {
   278  		return
   279  	}
   280  	ep = endpoints.values[endpoints.length-1].Next()
   281  	return
   282  }
   283  
   284  func (endpoints *Endpoints) Get(id []byte) *Endpoint {
   285  	endpoints.lock.RLock()
   286  	defer endpoints.lock.RUnlock()
   287  	if endpoints.length == 0 {
   288  		return nil
   289  	}
   290  	for _, value := range endpoints.values {
   291  		target := value.Get(id)
   292  		if target == nil {
   293  			continue
   294  		}
   295  		if target.Running() && target.IsHealth() {
   296  			return target
   297  		}
   298  		return nil
   299  	}
   300  	return nil
   301  }
   302  
   303  func (endpoints *Endpoints) Range(interval versions.Interval) *Endpoint {
   304  	endpoints.lock.RLock()
   305  	defer endpoints.lock.RUnlock()
   306  	if endpoints.length == 0 {
   307  		return nil
   308  	}
   309  	targets := make([]*Endpoint, 0, 1)
   310  	for _, value := range endpoints.values {
   311  		if interval.Accept(value.version) {
   312  			for _, endpoint := range value.values {
   313  				if endpoint.Running() && endpoint.IsHealth() {
   314  					targets = append(targets, endpoint)
   315  				}
   316  			}
   317  		}
   318  	}
   319  	pos := rand.Intn(len(targets))
   320  	return targets[pos]
   321  }
   322  
   323  func (endpoints *Endpoints) Infos() (v services.EndpointInfos) {
   324  	endpoints.lock.RLock()
   325  	defer endpoints.lock.RUnlock()
   326  	if endpoints.length == 0 {
   327  		return
   328  	}
   329  	for _, value := range endpoints.values {
   330  		for _, ep := range value.values {
   331  			v = append(v, ep.info)
   332  		}
   333  	}
   334  	return
   335  }