github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/delay.go (about) 1 package state 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 // Delay is used to mark certain locks as unacquirable. When a lock is 9 // forcefully released (failing health check, destroyed session, etc.), it is 10 // subject to the LockDelay imposed by the session. This prevents another 11 // session from acquiring the lock for some period of time as a protection 12 // against split-brains. This is inspired by the lock-delay in Chubby. Because 13 // this relies on wall-time, we cannot assume all peers perceive time as flowing 14 // uniformly. This means KVSLock MUST ignore lockDelay, since the lockDelay may 15 // have expired on the leader, but not on the follower. Rejecting the lock could 16 // result in inconsistencies in the FSMs due to the rate time progresses. Instead, 17 // only the opinion of the leader is respected, and the Raft log is never 18 // questioned. 19 type Delay struct { 20 // delay has the set of active delay expiration times, organized by key. 21 delay map[string]time.Time 22 23 // lock protects the delay map. 24 lock sync.RWMutex 25 } 26 27 // NewDelay returns a new delay manager. 28 func NewDelay() *Delay { 29 return &Delay{delay: make(map[string]time.Time)} 30 } 31 32 // GetExpiration returns the expiration time of a key lock delay. This must be 33 // checked on the leader node, and not in KVSLock due to the variability of 34 // clocks. 35 func (d *Delay) GetExpiration(key string) time.Time { 36 d.lock.RLock() 37 expires := d.delay[key] 38 d.lock.RUnlock() 39 return expires 40 } 41 42 // SetExpiration sets the expiration time for the lock delay to the given 43 // delay from the given now time. 44 func (d *Delay) SetExpiration(key string, now time.Time, delay time.Duration) { 45 d.lock.Lock() 46 defer d.lock.Unlock() 47 48 d.delay[key] = now.Add(delay) 49 time.AfterFunc(delay, func() { 50 d.lock.Lock() 51 delete(d.delay, key) 52 d.lock.Unlock() 53 }) 54 }