vitess.io/vitess@v0.16.2/go/vt/topo/memorytopo/election.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package memorytopo 18 19 import ( 20 "context" 21 "path" 22 23 "vitess.io/vitess/go/vt/log" 24 "vitess.io/vitess/go/vt/topo" 25 ) 26 27 // NewLeaderParticipation is part of the topo.Server interface 28 func (c *Conn) NewLeaderParticipation(name, id string) (topo.LeaderParticipation, error) { 29 if c.closed { 30 return nil, ErrConnectionClosed 31 } 32 33 c.factory.mu.Lock() 34 defer c.factory.mu.Unlock() 35 36 // Make sure the global path exists. 37 electionPath := path.Join(electionsPath, name) 38 if n := c.factory.getOrCreatePath(c.cell, electionPath); n == nil { 39 return nil, topo.NewError(topo.NoNode, electionPath) 40 } 41 42 return &cLeaderParticipation{ 43 c: c, 44 name: name, 45 id: id, 46 stop: make(chan struct{}), 47 done: make(chan struct{}), 48 }, nil 49 } 50 51 // cLeaderParticipation implements topo.LeaderParticipation. 52 // 53 // We use a directory (in global election path, with the name) with 54 // ephemeral files in it, that contains the id. The oldest revision 55 // wins the election. 56 type cLeaderParticipation struct { 57 // c is our memorytopo connection 58 c *Conn 59 60 // name is the name of this LeaderParticipation 61 name string 62 63 // id is the process's current id. 64 id string 65 66 // stop is a channel closed when Stop is called. 67 stop chan struct{} 68 69 // done is a channel closed when we're done processing the Stop 70 done chan struct{} 71 } 72 73 // WaitForLeadership is part of the topo.LeaderParticipation interface. 74 func (mp *cLeaderParticipation) WaitForLeadership() (context.Context, error) { 75 if mp.c.closed { 76 return nil, ErrConnectionClosed 77 } 78 79 // If Stop was already called, mp.done is closed, so we are interrupted. 80 select { 81 case <-mp.done: 82 return nil, topo.NewError(topo.Interrupted, "Leadership") 83 default: 84 } 85 86 electionPath := path.Join(electionsPath, mp.name) 87 88 // We use a cancelable context here. If stop is closed, 89 // we just cancel that context. 90 lockCtx, lockCancel := context.WithCancel(context.Background()) 91 92 // Try to get the primaryship, by getting a lock. 93 ld, err := mp.c.Lock(lockCtx, electionPath, mp.id) 94 if err != nil { 95 lockCancel() 96 close(mp.done) 97 // It can be that we were interrupted. 98 return nil, err 99 } 100 101 go func() { 102 <-mp.stop 103 if err := ld.Unlock(context.Background()); err != nil { 104 log.Errorf("failed to unlock LockDescriptor %v: %v", electionPath, err) 105 } 106 lockCancel() 107 close(mp.done) 108 }() 109 110 // We got the lock. Return the lockContext. If Stop() is called, 111 // it will cancel the lockCtx, and cancel the returned context. 112 return lockCtx, nil 113 } 114 115 // Stop is part of the topo.LeaderParticipation interface 116 func (mp *cLeaderParticipation) Stop() { 117 close(mp.stop) 118 <-mp.done 119 } 120 121 // GetCurrentLeaderID is part of the topo.LeaderParticipation interface 122 func (mp *cLeaderParticipation) GetCurrentLeaderID(ctx context.Context) (string, error) { 123 if mp.c.closed { 124 return "", ErrConnectionClosed 125 } 126 127 electionPath := path.Join(electionsPath, mp.name) 128 129 mp.c.factory.mu.Lock() 130 defer mp.c.factory.mu.Unlock() 131 132 n := mp.c.factory.nodeByPath(mp.c.cell, electionPath) 133 if n == nil { 134 return "", nil 135 } 136 137 return n.lockContents, nil 138 } 139 140 // WaitForNewLeader is part of the topo.LeaderParticipation interface 141 func (mp *cLeaderParticipation) WaitForNewLeader(ctx context.Context) (<-chan string, error) { 142 if mp.c.closed { 143 return nil, ErrConnectionClosed 144 } 145 146 mp.c.factory.mu.Lock() 147 defer mp.c.factory.mu.Unlock() 148 149 electionPath := path.Join(electionsPath, mp.name) 150 n := mp.c.factory.nodeByPath(mp.c.cell, electionPath) 151 if n == nil { 152 return nil, topo.NewError(topo.NoNode, electionPath) 153 } 154 155 notifications := make(chan string, 8) 156 watchIndex := nextWatchIndex 157 nextWatchIndex++ 158 n.watches[watchIndex] = watch{lock: notifications} 159 160 if n.lock != nil { 161 notifications <- n.lockContents 162 } 163 164 go func() { 165 defer close(notifications) 166 167 select { 168 case <-mp.stop: 169 case <-ctx.Done(): 170 } 171 172 mp.c.factory.mu.Lock() 173 defer mp.c.factory.mu.Unlock() 174 175 n := mp.c.factory.nodeByPath(mp.c.cell, electionPath) 176 if n == nil { 177 return 178 } 179 delete(n.watches, watchIndex) 180 }() 181 182 return notifications, nil 183 }