mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #8459 from heyitsanthony/mvcc-cancel-close
mvcc: only remove watch cancel after cancel completes
This commit is contained in:
commit
7cf8eb8dce
@ -144,7 +144,6 @@ func (s *watchableStore) watch(key, end []byte, startRev int64, id WatchID, ch c
|
|||||||
func (s *watchableStore) cancelWatcher(wa *watcher) {
|
func (s *watchableStore) cancelWatcher(wa *watcher) {
|
||||||
for {
|
for {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|
||||||
if s.unsynced.delete(wa) {
|
if s.unsynced.delete(wa) {
|
||||||
slowWatcherGauge.Dec()
|
slowWatcherGauge.Dec()
|
||||||
break
|
break
|
||||||
@ -152,6 +151,9 @@ func (s *watchableStore) cancelWatcher(wa *watcher) {
|
|||||||
break
|
break
|
||||||
} else if wa.compacted {
|
} else if wa.compacted {
|
||||||
break
|
break
|
||||||
|
} else if wa.ch == nil {
|
||||||
|
// already canceled (e.g., cancel/close race)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if !wa.victim {
|
if !wa.victim {
|
||||||
@ -177,6 +179,7 @@ func (s *watchableStore) cancelWatcher(wa *watcher) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watcherGauge.Dec()
|
watcherGauge.Dec()
|
||||||
|
wa.ch = nil
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,7 +428,6 @@ func (s *watchableStore) notify(rev int64, evs []mvccpb.Event) {
|
|||||||
if eb.revs != 1 {
|
if eb.revs != 1 {
|
||||||
plog.Panicf("unexpected multiple revisions in notification")
|
plog.Panicf("unexpected multiple revisions in notification")
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.send(WatchResponse{WatchID: w.id, Events: eb.evs, Revision: rev}) {
|
if w.send(WatchResponse{WatchID: w.id, Events: eb.evs, Revision: rev}) {
|
||||||
pendingEventsGauge.Add(float64(len(eb.evs)))
|
pendingEventsGauge.Add(float64(len(eb.evs)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -539,3 +539,49 @@ func TestWatchVictims(t *testing.T) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStressWatchCancelClose tests closing a watch stream while
|
||||||
|
// canceling its watches.
|
||||||
|
func TestStressWatchCancelClose(t *testing.T) {
|
||||||
|
b, tmpPath := backend.NewDefaultTmpBackend()
|
||||||
|
s := newWatchableStore(b, &lease.FakeLessor{}, nil)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
s.store.Close()
|
||||||
|
os.Remove(tmpPath)
|
||||||
|
}()
|
||||||
|
|
||||||
|
testKey, testValue := []byte("foo"), []byte("bar")
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
readyc := make(chan struct{})
|
||||||
|
wg.Add(100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
w := s.NewWatchStream()
|
||||||
|
ids := make([]WatchID, 10)
|
||||||
|
for i := range ids {
|
||||||
|
ids[i] = w.Watch(testKey, nil, 0)
|
||||||
|
}
|
||||||
|
<-readyc
|
||||||
|
wg.Add(1 + len(ids)/2)
|
||||||
|
for i := range ids[:len(ids)/2] {
|
||||||
|
go func(n int) {
|
||||||
|
defer wg.Done()
|
||||||
|
w.Cancel(ids[n])
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
w.Close()
|
||||||
|
}()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
close(readyc)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
s.Put(testKey, testValue, lease.NoLease)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
@ -129,16 +129,25 @@ func (ws *watchStream) Chan() <-chan WatchResponse {
|
|||||||
func (ws *watchStream) Cancel(id WatchID) error {
|
func (ws *watchStream) Cancel(id WatchID) error {
|
||||||
ws.mu.Lock()
|
ws.mu.Lock()
|
||||||
cancel, ok := ws.cancels[id]
|
cancel, ok := ws.cancels[id]
|
||||||
|
w := ws.watchers[id]
|
||||||
ok = ok && !ws.closed
|
ok = ok && !ws.closed
|
||||||
if ok {
|
|
||||||
delete(ws.cancels, id)
|
|
||||||
delete(ws.watchers, id)
|
|
||||||
}
|
|
||||||
ws.mu.Unlock()
|
ws.mu.Unlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrWatcherNotExist
|
return ErrWatcherNotExist
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
|
ws.mu.Lock()
|
||||||
|
// The watch isn't removed until cancel so that if Close() is called,
|
||||||
|
// it will wait for the cancel. Otherwise, Close() could close the
|
||||||
|
// watch channel while the store is still posting events.
|
||||||
|
if ww := ws.watchers[id]; ww == w {
|
||||||
|
delete(ws.cancels, id)
|
||||||
|
delete(ws.watchers, id)
|
||||||
|
}
|
||||||
|
ws.mu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user