github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/util/election/tracker.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     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  
    15  package election
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/golang/glog"
    24  )
    25  
    26  // MasterTracker tracks the current mastership state across multiple IDs.
    27  type MasterTracker struct {
    28  	mu          sync.RWMutex
    29  	masterFor   map[int64]bool
    30  	masterCount int
    31  	notify      func(id int64, isMaster bool)
    32  }
    33  
    34  // NewMasterTracker creates a new MasterTracker instance to track the mastership
    35  // status for the given set of ids.
    36  func NewMasterTracker(ids []int64, notify func(id int64, isMaster bool)) *MasterTracker {
    37  	mf := make(map[int64]bool)
    38  	for _, id := range ids {
    39  		mf[id] = false
    40  	}
    41  	return &MasterTracker{masterFor: mf, notify: notify}
    42  }
    43  
    44  // Set changes the tracked mastership status for the given id.  This method should
    45  // be called exactly once for each state transition.
    46  func (mt *MasterTracker) Set(id int64, val bool) {
    47  	mt.mu.Lock()
    48  	defer mt.mu.Unlock()
    49  	existing, ok := mt.masterFor[id]
    50  	if ok && val == existing {
    51  		glog.Warningf("toggle masterFor[%d] from %v to %v!", id, existing, val)
    52  	}
    53  	mt.masterFor[id] = val
    54  	if val && !existing {
    55  		mt.masterCount++
    56  	} else if !val && existing {
    57  		mt.masterCount--
    58  	}
    59  	if mt.notify != nil {
    60  		mt.notify(id, val)
    61  	}
    62  }
    63  
    64  // Count returns the number of IDs for which we are currently master.
    65  func (mt *MasterTracker) Count() int {
    66  	mt.mu.RLock()
    67  	defer mt.mu.RUnlock()
    68  	return mt.masterCount
    69  }
    70  
    71  // Held returns a (sorted) list of the IDs for which we are currently master.
    72  func (mt *MasterTracker) Held() []int64 {
    73  	mt.mu.RLock()
    74  	defer mt.mu.RUnlock()
    75  	ids := make([]int64, 0, mt.masterCount)
    76  	for id := range mt.masterFor {
    77  		if mt.masterFor[id] {
    78  			ids = append(ids, id)
    79  		}
    80  	}
    81  	sort.Sort(int64arr(ids))
    82  	return ids
    83  }
    84  
    85  // IDs returns a (sorted) list of the IDs that we are currently tracking.
    86  func (mt *MasterTracker) IDs() []int64 {
    87  	mt.mu.RLock()
    88  	defer mt.mu.RUnlock()
    89  	ids := make([]int64, 0, len(mt.masterFor))
    90  	for id := range mt.masterFor {
    91  		ids = append(ids, id)
    92  	}
    93  	sort.Sort(int64arr(ids))
    94  	return ids
    95  }
    96  
    97  // String returns a textual decription of the current mastership status.
    98  func (mt *MasterTracker) String() string {
    99  	return HeldInfo(mt.Held(), mt.IDs())
   100  }
   101  
   102  // HeldInfo produces a textual description of the set of held IDs, compared
   103  // to a complete set of IDs.
   104  func HeldInfo(held []int64, ids []int64) string {
   105  	result := ""
   106  	prefix := ""
   107  	for _, id := range ids {
   108  		idStr := fmt.Sprintf("%d", id)
   109  		show := strings.Repeat(".", len(idStr))
   110  		for _, h := range held {
   111  			if h == id {
   112  				show = idStr
   113  			}
   114  			if h >= id {
   115  				break
   116  			}
   117  		}
   118  		result += fmt.Sprintf("%s%s", prefix, show)
   119  		prefix = " "
   120  	}
   121  	return result
   122  }
   123  
   124  // Make int64 slice sortable:
   125  type int64arr []int64
   126  
   127  // Len returns length
   128  func (a int64arr) Len() int {
   129  	return len(a)
   130  }
   131  
   132  // Swap swaps
   133  func (a int64arr) Swap(i, j int) {
   134  	a[i], a[j] = a[j], a[i]
   135  }
   136  
   137  // Less compares
   138  func (a int64arr) Less(i, j int) bool {
   139  	return a[i] < a[j]
   140  }