github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/nodes.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  	"github.com/aacfactory/avro"
    22  	"github.com/aacfactory/errors"
    23  	"github.com/aacfactory/fns/commons/versions"
    24  	"github.com/aacfactory/fns/services"
    25  	"github.com/aacfactory/fns/services/documents"
    26  	"github.com/valyala/fasthttp"
    27  	"slices"
    28  	"sort"
    29  	"strings"
    30  )
    31  
    32  func NewService(name string, internal bool, functions services.FnInfos, document documents.Endpoint) (service Service, err error) {
    33  	service = Service{
    34  		Name:        name,
    35  		Internal:    internal,
    36  		Functions:   functions,
    37  		DocumentRaw: nil,
    38  	}
    39  	if document.Defined() {
    40  		p, encodeErr := avro.Marshal(document)
    41  		if encodeErr != nil {
    42  			err = errors.Warning("fns: new endpoint info failed").WithCause(encodeErr)
    43  			return
    44  		}
    45  		cp := make([]byte, 0)
    46  		cp = fasthttp.AppendDeflateBytesLevel(cp, p, fasthttp.CompressBestCompression)
    47  		service.DocumentRaw = cp
    48  	}
    49  	return
    50  }
    51  
    52  type Service struct {
    53  	Name        string           `json:"name"`
    54  	Internal    bool             `json:"internal"`
    55  	Functions   services.FnInfos `json:"functions"`
    56  	DocumentRaw []byte           `json:"document"`
    57  }
    58  
    59  func (service Service) Document() (document documents.Endpoint, err error) {
    60  	if len(service.DocumentRaw) == 0 {
    61  		return
    62  	}
    63  	p := make([]byte, 0)
    64  	p, err = fasthttp.AppendInflateBytes(p, service.DocumentRaw)
    65  	if err != nil {
    66  		err = errors.Warning("fns: service get document failed").WithCause(err)
    67  		return
    68  	}
    69  	document = documents.Endpoint{}
    70  	decodeErr := avro.Unmarshal(p, &document)
    71  	if decodeErr != nil {
    72  		err = errors.Warning("fns: service get document failed").WithCause(decodeErr)
    73  		return
    74  	}
    75  	return
    76  }
    77  
    78  type Node struct {
    79  	Id       string           `json:"id"`
    80  	Version  versions.Version `json:"version"`
    81  	Address  string           `json:"address"`
    82  	Services []Service        `json:"services"`
    83  }
    84  
    85  const (
    86  	Add    = NodeEventKind(1)
    87  	Remove = NodeEventKind(2)
    88  )
    89  
    90  type NodeEventKind int
    91  
    92  func (kind NodeEventKind) String() string {
    93  	switch kind {
    94  	case Add:
    95  		return "add"
    96  	case Remove:
    97  		return "evict"
    98  	default:
    99  		return "unknown"
   100  	}
   101  }
   102  
   103  type NodeEvent struct {
   104  	Kind NodeEventKind
   105  	Node Node
   106  }
   107  
   108  type Nodes []Node
   109  
   110  func (nodes Nodes) Len() int {
   111  	return len(nodes)
   112  }
   113  
   114  func (nodes Nodes) Less(i, j int) bool {
   115  	return nodes[i].Id < nodes[j].Id
   116  }
   117  
   118  func (nodes Nodes) Swap(i, j int) {
   119  	nodes[i], nodes[j] = nodes[j], nodes[i]
   120  	return
   121  }
   122  
   123  func (nodes Nodes) Add(node Node) Nodes {
   124  	n := append(nodes, node)
   125  	sort.Sort(n)
   126  	return n
   127  }
   128  
   129  func (nodes Nodes) Remove(node Node) Nodes {
   130  	idx, found := slices.BinarySearchFunc(nodes, node, func(x Node, j Node) int {
   131  		return strings.Compare(x.Id, j.Id)
   132  	})
   133  	if found {
   134  		return append(nodes[:idx], nodes[idx+1:]...)
   135  	}
   136  	return nodes
   137  }
   138  
   139  func (nodes Nodes) Difference(olds Nodes) (events []NodeEvent) {
   140  	events = make([]NodeEvent, 0, 1)
   141  	// remove
   142  	for _, old := range olds {
   143  		_, found := slices.BinarySearchFunc(nodes, old, func(x Node, j Node) int {
   144  			return strings.Compare(x.Id, j.Id)
   145  		})
   146  		if !found {
   147  			events = append(events, NodeEvent{
   148  				Kind: Remove,
   149  				Node: old,
   150  			})
   151  		}
   152  	}
   153  	// add
   154  	for _, node := range nodes {
   155  		_, found := slices.BinarySearchFunc(olds, node, func(x Node, j Node) int {
   156  			return strings.Compare(x.Id, j.Id)
   157  		})
   158  		if !found {
   159  			events = append(events, NodeEvent{
   160  				Kind: Add,
   161  				Node: node,
   162  			})
   163  		}
   164  	}
   165  	return
   166  }
   167  
   168  func MapEndpointInfosToNodes(infos services.EndpointInfos) (nodes Nodes) {
   169  	nodes = make(Nodes, 0, 1)
   170  	for _, info := range infos {
   171  		service, serviceErr := NewService(info.Name, info.Internal, info.Functions, info.Document)
   172  		if serviceErr != nil {
   173  			continue
   174  		}
   175  		exist := false
   176  		for i, node := range nodes {
   177  			if node.Id == info.Id {
   178  				node.Services = append(node.Services, service)
   179  				nodes[i] = node
   180  				exist = true
   181  				break
   182  			}
   183  		}
   184  		if exist {
   185  			continue
   186  		}
   187  		nodes = nodes.Add(Node{
   188  			Id:       info.Id,
   189  			Version:  info.Version,
   190  			Address:  info.Address,
   191  			Services: []Service{service},
   192  		})
   193  	}
   194  	sort.Sort(nodes)
   195  	return
   196  }