github.com/weaviate/weaviate@v1.24.6/usecases/cluster/ideal_node_list.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package cluster 13 14 import ( 15 "fmt" 16 "sort" 17 "strings" 18 "sync" 19 "time" 20 21 "github.com/sirupsen/logrus" 22 enterrors "github.com/weaviate/weaviate/entities/errors" 23 ) 24 25 type IdealClusterState struct { 26 memberNames []string 27 currentState MemberLister 28 lock sync.Mutex 29 } 30 31 func NewIdealClusterState(s MemberLister, logger logrus.FieldLogger) *IdealClusterState { 32 ics := &IdealClusterState{currentState: s} 33 enterrors.GoWrapper(func() { ics.startPolling() }, logger) 34 return ics 35 } 36 37 // Validate returns an error if the actual state does not match the assumed 38 // ideal state, e.g. because a node has died, or left unexpectedly. 39 func (ics *IdealClusterState) Validate() error { 40 ics.lock.Lock() 41 defer ics.lock.Unlock() 42 43 actual := map[string]struct{}{} 44 for _, name := range ics.currentState.AllNames() { 45 actual[name] = struct{}{} 46 } 47 48 var missing []string 49 for _, name := range ics.memberNames { 50 if _, ok := actual[name]; !ok { 51 missing = append(missing, name) 52 } 53 } 54 55 if len(missing) > 0 { 56 return fmt.Errorf("node(s) %s unhealthy or unavailable", 57 strings.Join(missing, ", ")) 58 } 59 60 return nil 61 } 62 63 func (ics *IdealClusterState) Members() []string { 64 ics.lock.Lock() 65 defer ics.lock.Unlock() 66 67 return ics.memberNames 68 } 69 70 func (ics *IdealClusterState) startPolling() { 71 t := time.NewTicker(1 * time.Second) 72 for { 73 <-t.C 74 current := ics.currentState.AllNames() 75 ics.extendList(current) 76 } 77 } 78 79 func (ics *IdealClusterState) extendList(current []string) { 80 ics.lock.Lock() 81 defer ics.lock.Unlock() 82 83 var unknown []string 84 known := map[string]struct{}{} 85 for _, name := range ics.memberNames { 86 known[name] = struct{}{} 87 } 88 89 for _, name := range current { 90 if _, ok := known[name]; !ok { 91 unknown = append(unknown, name) 92 } 93 } 94 95 ics.memberNames = append(ics.memberNames, unknown...) 96 sort.Strings(ics.memberNames) 97 }