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  }