github.com/uber/kraken@v0.1.4/lib/healthcheck/state.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package healthcheck
    15  
    16  import (
    17  	"sync"
    18  
    19  	"github.com/uber/kraken/utils/stringset"
    20  )
    21  
    22  // state tracks the health status of a set of hosts. In particular, it tracks
    23  // consecutive passes or fails which cause hosts to transition between healthy
    24  // and unhealthy.
    25  //
    26  // state is thread-safe.
    27  type state struct {
    28  	sync.Mutex
    29  	config  FilterConfig
    30  	all     stringset.Set
    31  	healthy stringset.Set
    32  	trend   map[string]int
    33  }
    34  
    35  func newState(config FilterConfig) *state {
    36  	return &state{
    37  		config:  config,
    38  		all:     stringset.New(),
    39  		healthy: stringset.New(),
    40  		trend:   make(map[string]int),
    41  	}
    42  }
    43  
    44  // sync sets the current state to addrs. New entries are initialized as healthy,
    45  // while existing entries not found in addrs are removed from s.
    46  func (s *state) sync(addrs stringset.Set) {
    47  	s.Lock()
    48  	defer s.Unlock()
    49  
    50  	for addr := range addrs {
    51  		if !s.all.Has(addr) {
    52  			s.all.Add(addr)
    53  			s.healthy.Add(addr)
    54  		}
    55  	}
    56  
    57  	for addr := range s.healthy {
    58  		if !addrs.Has(addr) {
    59  			s.healthy.Remove(addr)
    60  			delete(s.trend, addr)
    61  		}
    62  	}
    63  }
    64  
    65  // failed marks addr as failed.
    66  func (s *state) failed(addr string) {
    67  	s.Lock()
    68  	defer s.Unlock()
    69  
    70  	s.trend[addr] = max(min(s.trend[addr]-1, -1), -s.config.Fails)
    71  	if s.trend[addr] == -s.config.Fails {
    72  		s.healthy.Remove(addr)
    73  	}
    74  }
    75  
    76  // passed marks addr as passed.
    77  func (s *state) passed(addr string) {
    78  	s.Lock()
    79  	defer s.Unlock()
    80  
    81  	s.trend[addr] = min(max(s.trend[addr]+1, 1), s.config.Passes)
    82  	if s.trend[addr] == s.config.Passes {
    83  		s.healthy.Add(addr)
    84  	}
    85  }
    86  
    87  // getHealthy returns the current healthy hosts.
    88  func (s *state) getHealthy() stringset.Set {
    89  	s.Lock()
    90  	defer s.Unlock()
    91  
    92  	return s.healthy.Copy()
    93  }
    94  
    95  func max(a, b int) int {
    96  	if a > b {
    97  		return a
    98  	}
    99  	return b
   100  }
   101  
   102  func min(a, b int) int {
   103  	if a < b {
   104  		return a
   105  	}
   106  	return b
   107  }