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 }