go.etcd.io/etcd@v3.3.27+incompatible/proxy/grpcproxy/watch_broadcast.go (about) 1 // Copyright 2016 The etcd Authors 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 grpcproxy 16 17 import ( 18 "context" 19 "sync" 20 21 "github.com/coreos/etcd/clientv3" 22 pb "github.com/coreos/etcd/etcdserver/etcdserverpb" 23 ) 24 25 // watchBroadcast broadcasts a server watcher to many client watchers. 26 type watchBroadcast struct { 27 // cancel stops the underlying etcd server watcher and closes ch. 28 cancel context.CancelFunc 29 donec chan struct{} 30 31 // mu protects rev and receivers. 32 mu sync.RWMutex 33 // nextrev is the minimum expected next revision of the watcher on ch. 34 nextrev int64 35 // receivers contains all the client-side watchers to serve. 36 receivers map[*watcher]struct{} 37 // responses counts the number of responses 38 responses int 39 } 40 41 func newWatchBroadcast(wp *watchProxy, w *watcher, update func(*watchBroadcast)) *watchBroadcast { 42 cctx, cancel := context.WithCancel(wp.ctx) 43 wb := &watchBroadcast{ 44 cancel: cancel, 45 nextrev: w.nextrev, 46 receivers: make(map[*watcher]struct{}), 47 donec: make(chan struct{}), 48 } 49 wb.add(w) 50 go func() { 51 defer close(wb.donec) 52 53 opts := []clientv3.OpOption{ 54 clientv3.WithRange(w.wr.end), 55 clientv3.WithProgressNotify(), 56 clientv3.WithRev(wb.nextrev), 57 clientv3.WithPrevKV(), 58 clientv3.WithCreatedNotify(), 59 } 60 61 cctx = withClientAuthToken(cctx, w.wps.stream.Context()) 62 63 wch := wp.cw.Watch(cctx, w.wr.key, opts...) 64 65 for wr := range wch { 66 wb.bcast(wr) 67 update(wb) 68 } 69 }() 70 return wb 71 } 72 73 func (wb *watchBroadcast) bcast(wr clientv3.WatchResponse) { 74 wb.mu.Lock() 75 defer wb.mu.Unlock() 76 // watchers start on the given revision, if any; ignore header rev on create 77 if wb.responses > 0 || wb.nextrev == 0 { 78 wb.nextrev = wr.Header.Revision + 1 79 } 80 wb.responses++ 81 for r := range wb.receivers { 82 r.send(wr) 83 } 84 if len(wb.receivers) > 0 { 85 eventsCoalescing.Add(float64(len(wb.receivers) - 1)) 86 } 87 } 88 89 // add puts a watcher into receiving a broadcast if its revision at least 90 // meets the broadcast revision. Returns true if added. 91 func (wb *watchBroadcast) add(w *watcher) bool { 92 wb.mu.Lock() 93 defer wb.mu.Unlock() 94 if wb.nextrev > w.nextrev || (wb.nextrev == 0 && w.nextrev != 0) { 95 // wb is too far ahead, w will miss events 96 // or wb is being established with a current watcher 97 return false 98 } 99 if wb.responses == 0 { 100 // Newly created; create event will be sent by etcd. 101 wb.receivers[w] = struct{}{} 102 return true 103 } 104 // already sent by etcd; emulate create event 105 ok := w.post(&pb.WatchResponse{ 106 Header: &pb.ResponseHeader{ 107 // todo: fill in ClusterId 108 // todo: fill in MemberId: 109 Revision: w.nextrev, 110 // todo: fill in RaftTerm: 111 }, 112 WatchId: w.id, 113 Created: true, 114 }) 115 if !ok { 116 return false 117 } 118 wb.receivers[w] = struct{}{} 119 watchersCoalescing.Inc() 120 121 return true 122 } 123 func (wb *watchBroadcast) delete(w *watcher) { 124 wb.mu.Lock() 125 defer wb.mu.Unlock() 126 if _, ok := wb.receivers[w]; !ok { 127 panic("deleting missing watcher from broadcast") 128 } 129 delete(wb.receivers, w) 130 if len(wb.receivers) > 0 { 131 // do not dec the only left watcher for coalescing. 132 watchersCoalescing.Dec() 133 } 134 } 135 136 func (wb *watchBroadcast) size() int { 137 wb.mu.RLock() 138 defer wb.mu.RUnlock() 139 return len(wb.receivers) 140 } 141 142 func (wb *watchBroadcast) empty() bool { return wb.size() == 0 } 143 144 func (wb *watchBroadcast) stop() { 145 if !wb.empty() { 146 // do not dec the only left watcher for coalescing. 147 watchersCoalescing.Sub(float64(wb.size() - 1)) 148 } 149 150 wb.cancel() 151 <-wb.donec 152 }