github.com/altipla-consulting/ravendb-go-client@v0.1.3/node_selector.go (about)

     1  package ravendb
     2  
     3  import (
     4  	"time"
     5  )
     6  
     7  // NodeSelector describes node selector
     8  type NodeSelector struct {
     9  	updateFastestNodeTimer *time.Timer
    10  	state                  *NodeSelectorState
    11  }
    12  
    13  // NewNodeSelector creates a new NodeSelector
    14  func NewNodeSelector(t *Topology) *NodeSelector {
    15  	state := NewNodeSelectorState(t)
    16  	return &NodeSelector{
    17  		state: state,
    18  	}
    19  }
    20  
    21  func (s *NodeSelector) getTopology() *Topology {
    22  	return s.state.topology
    23  }
    24  
    25  func (s *NodeSelector) onFailedRequest(nodeIndex int) {
    26  	state := s.state
    27  	if nodeIndex < 0 || nodeIndex >= len(state.failures) {
    28  		return // probably already changed
    29  	}
    30  
    31  	state.failures[nodeIndex].incrementAndGet()
    32  }
    33  
    34  func (s *NodeSelector) onUpdateTopology(topology *Topology, forceUpdate bool) bool {
    35  	if topology == nil {
    36  		return false
    37  	}
    38  
    39  	stateEtag := s.state.topology.Etag
    40  	topologyEtag := topology.Etag
    41  
    42  	if stateEtag >= topologyEtag && !forceUpdate {
    43  		return false
    44  	}
    45  
    46  	s.state = NewNodeSelectorState(topology)
    47  
    48  	return true
    49  }
    50  
    51  func (s *NodeSelector) getPreferredNode() (*CurrentIndexAndNode, error) {
    52  	state := s.state
    53  	stateFailures := state.failures
    54  	serverNodes := state.nodes
    55  	n := min(len(serverNodes), len(stateFailures))
    56  	for i := 0; i < n; i++ {
    57  		if stateFailures[i].get() == 0 && serverNodes[i].URL != "" {
    58  			return NewCurrentIndexAndNode(i, serverNodes[i]), nil
    59  		}
    60  	}
    61  	return s.unlikelyEveryoneFaultedChoice(state)
    62  }
    63  
    64  func (s *NodeSelector) unlikelyEveryoneFaultedChoice(state *NodeSelectorState) (*CurrentIndexAndNode, error) {
    65  	// if there are all marked as failed, we'll chose the first
    66  	// one so the user will get an error (or recover :-) );
    67  	if len(state.nodes) == 0 {
    68  		return nil, newAllTopologyNodesDownError("There are no nodes in the topology at all")
    69  	}
    70  
    71  	return NewCurrentIndexAndNode(0, state.nodes[0]), nil
    72  }
    73  
    74  func (s *NodeSelector) getNodeBySessionID(sessionId int) (*CurrentIndexAndNode, error) {
    75  	state := s.state
    76  	index := sessionId % len(state.topology.Nodes)
    77  
    78  	for i := index; i < len(state.failures); i++ {
    79  		if state.failures[i].get() == 0 && state.nodes[i].ServerRole == ServerNodeRoleMember {
    80  			return NewCurrentIndexAndNode(i, state.nodes[i]), nil
    81  		}
    82  	}
    83  
    84  	for i := 0; i < index; i++ {
    85  		if state.failures[i].get() == 0 && state.nodes[i].ServerRole == ServerNodeRoleMember {
    86  			return NewCurrentIndexAndNode(i, state.nodes[i]), nil
    87  		}
    88  	}
    89  
    90  	return s.getPreferredNode()
    91  }
    92  
    93  func (s *NodeSelector) getFastestNode() (*CurrentIndexAndNode, error) {
    94  	state := s.state
    95  	if state.failures[state.fastest].get() == 0 && state.nodes[state.fastest].ServerRole == ServerNodeRoleMember {
    96  		return NewCurrentIndexAndNode(state.fastest, state.nodes[state.fastest]), nil
    97  	}
    98  
    99  	// if the fastest node has failures, we'll immediately schedule
   100  	// another run of finding who the fastest node is, in the meantime
   101  	// we'll just use the server preferred node or failover as usual
   102  
   103  	s.switchToSpeedTestPhase()
   104  	return s.getPreferredNode()
   105  }
   106  
   107  func (s *NodeSelector) restoreNodeIndex(nodeIndex int) {
   108  	state := s.state
   109  	if len(state.failures) < nodeIndex {
   110  		return // the state was changed and we no longer have it?
   111  	}
   112  
   113  	state.failures[nodeIndex].set(0)
   114  }
   115  
   116  /*
   117  TODO: not used, remove?
   118  func nodeSelectorThrowEmptyTopology() error {
   119  	return newIllegalStateError("Empty database topology, this shouldn't happen.")
   120  }
   121  */
   122  
   123  func (s *NodeSelector) switchToSpeedTestPhase() {
   124  	state := s.state
   125  
   126  	if !state.speedTestMode.compareAndSet(0, 1) {
   127  		return
   128  	}
   129  
   130  	for i := 0; i < len(state.fastestRecords); i++ {
   131  		state.fastestRecords[i] = 0
   132  	}
   133  
   134  	state.speedTestMode.incrementAndGet()
   135  }
   136  
   137  func (s *NodeSelector) inSpeedTestPhase() bool {
   138  	return s.state.speedTestMode.get() > 1
   139  }
   140  
   141  func (s *NodeSelector) recordFastest(index int, node *ServerNode) {
   142  	state := s.state
   143  	stateFastest := state.fastestRecords
   144  
   145  	// the following two checks are to verify that things didn't move
   146  	// while we were computing the fastest node, we verify that the index
   147  	// of the fastest node and the identity of the node didn't change during
   148  	// our check
   149  	if index < 0 || index >= len(stateFastest) {
   150  		return
   151  	}
   152  
   153  	if node != state.nodes[index] {
   154  		return
   155  	}
   156  
   157  	stateFastest[index]++
   158  	if stateFastest[index] >= 10 {
   159  		s.selectFastest(state, index)
   160  	}
   161  
   162  	if state.speedTestMode.incrementAndGet() <= len(state.nodes)*10 {
   163  		return
   164  	}
   165  
   166  	//too many concurrent speed tests are happening
   167  	maxIndex := s.findMaxIndex(state)
   168  	s.selectFastest(state, maxIndex)
   169  }
   170  
   171  func (s *NodeSelector) findMaxIndex(state *NodeSelectorState) int {
   172  	stateFastest := state.fastestRecords
   173  	maxIndex := 0
   174  	maxValue := 0
   175  
   176  	for i := 0; i < len(stateFastest); i++ {
   177  		if maxValue >= stateFastest[i] {
   178  			continue
   179  		}
   180  
   181  		maxIndex = i
   182  		maxValue = stateFastest[i]
   183  	}
   184  
   185  	return maxIndex
   186  }
   187  
   188  func (s *NodeSelector) selectFastest(state *NodeSelectorState, index int) {
   189  	state.fastest = index
   190  	state.speedTestMode.set(0)
   191  
   192  	if s.updateFastestNodeTimer != nil {
   193  		s.updateFastestNodeTimer.Reset(time.Minute)
   194  	} else {
   195  		f := func() {
   196  			s.updateFastestNodeTimer = nil
   197  			s.switchToSpeedTestPhase()
   198  		}
   199  		s.updateFastestNodeTimer = time.AfterFunc(time.Minute, f)
   200  	}
   201  }
   202  
   203  func (s *NodeSelector) scheduleSpeedTest() {
   204  	s.switchToSpeedTestPhase()
   205  }
   206  
   207  func (s *NodeSelector) Close() {
   208  	if s.updateFastestNodeTimer != nil {
   209  		s.updateFastestNodeTimer.Stop()
   210  		s.updateFastestNodeTimer = nil
   211  	}
   212  }
   213  
   214  type NodeSelectorState struct {
   215  	topology       *Topology
   216  	nodes          []*ServerNode
   217  	failures       []atomicInteger
   218  	fastestRecords []int
   219  	fastest        int
   220  	speedTestMode  atomicInteger
   221  }
   222  
   223  func NewNodeSelectorState(topology *Topology) *NodeSelectorState {
   224  	nodes := topology.Nodes
   225  	res := &NodeSelectorState{
   226  		topology: topology,
   227  		nodes:    nodes,
   228  	}
   229  	failures := make([]atomicInteger, len(nodes))
   230  	res.failures = failures
   231  	res.fastestRecords = make([]int, len(nodes))
   232  	return res
   233  }