vitess.io/vitess@v0.16.2/go/vt/topo/k8stopo/election.go (about) 1 /* 2 Copyright 2020 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 k8stopo 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 const electionsPath = "elections" 28 29 // NewLeaderParticipation is part of the topo.Server interface 30 func (s *Server) NewLeaderParticipation(name, id string) (topo.LeaderParticipation, error) { 31 return &kubernetesLeaderParticipation{ 32 s: s, 33 name: name, 34 id: id, 35 stop: make(chan struct{}), 36 done: make(chan struct{}), 37 }, nil 38 } 39 40 // kubernetesLeaderParticipation implements topo.LeaderParticipation. 41 // 42 // We use a directory (in global election path, with the name) with 43 // ephemeral files in it, that contains the id. The oldest revision 44 // wins the election. 45 type kubernetesLeaderParticipation struct { 46 // s is our parent kubernetes topo Server 47 s *Server 48 49 // name is the name of this LeaderParticipation 50 name string 51 52 // id is the process's current id. 53 id string 54 55 // stop is a channel closed when Stop is called. 56 stop chan struct{} 57 58 // done is a channel closed when we're done processing the Stop 59 done chan struct{} 60 } 61 62 func (mp *kubernetesLeaderParticipation) getElectionPath() string { 63 return path.Join(mp.s.root, electionsPath, mp.name) 64 } 65 66 // WaitForLeadership is part of the topo.LeaderParticipation interface. 67 func (mp *kubernetesLeaderParticipation) WaitForLeadership() (context.Context, error) { 68 // If Stop was already called, mp.done is closed, so we are interrupted. 69 select { 70 case <-mp.done: 71 return nil, topo.NewError(topo.Interrupted, "Leadership") 72 default: 73 } 74 75 electionPath := mp.getElectionPath() 76 var ld topo.LockDescriptor 77 78 // We use a cancelable context here. If stop is closed, 79 // we just cancel that context. 80 lockCtx, lockCancel := context.WithCancel(context.Background()) 81 go func() { 82 <-mp.stop 83 if ld != nil { 84 if err := ld.Unlock(context.Background()); err != nil { 85 log.Errorf("failed to unlock electionPath %v: %v", electionPath, err) 86 } 87 } 88 lockCancel() 89 close(mp.done) 90 }() 91 92 // Try to get the primaryship, by getting a lock. 93 var err error 94 ld, err = mp.s.lock(lockCtx, electionPath, mp.id, true) 95 if err != nil { 96 // It can be that we were interrupted. 97 return nil, err 98 } 99 100 // We got the lock. Return the lockContext. If Stop() is called, 101 // it will cancel the lockCtx, and cancel the returned context. 102 return lockCtx, nil 103 } 104 105 // Stop is part of the topo.LeaderParticipation interface 106 func (mp *kubernetesLeaderParticipation) Stop() { 107 close(mp.stop) 108 <-mp.done 109 } 110 111 // GetCurrentLeaderID is part of the topo.LeaderParticipation interface 112 func (mp *kubernetesLeaderParticipation) GetCurrentLeaderID(ctx context.Context) (string, error) { 113 id, _, err := mp.s.Get(ctx, mp.getElectionPath()) 114 if err != nil { 115 // NoNode means nobody is the primary 116 if topo.IsErrType(err, topo.NoNode) { 117 return "", nil 118 } 119 return "", err 120 } 121 return string(id), nil 122 } 123 124 // WaitForNewLeader is part of the topo.LeaderParticipation interface 125 func (mp *kubernetesLeaderParticipation) WaitForNewLeader(context.Context) (<-chan string, error) { 126 // Kubernetes doesn't seem to provide a primitive that watches a prefix 127 // or directory, so this likely can never be implemented. 128 return nil, topo.NewError(topo.NoImplementation, "wait for leader not supported in K8s topo") 129 }