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 }