go.etcd.io/etcd@v3.3.27+incompatible/proxy/grpcproxy/watch_broadcasts.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  	"sync"
    19  )
    20  
    21  type watchBroadcasts struct {
    22  	wp *watchProxy
    23  
    24  	// mu protects bcasts and watchers from the coalesce loop.
    25  	mu       sync.Mutex
    26  	bcasts   map[*watchBroadcast]struct{}
    27  	watchers map[*watcher]*watchBroadcast
    28  
    29  	updatec chan *watchBroadcast
    30  	donec   chan struct{}
    31  }
    32  
    33  // maxCoalesceRecievers prevents a popular watchBroadcast from being coalseced.
    34  const maxCoalesceReceivers = 5
    35  
    36  func newWatchBroadcasts(wp *watchProxy) *watchBroadcasts {
    37  	wbs := &watchBroadcasts{
    38  		wp:       wp,
    39  		bcasts:   make(map[*watchBroadcast]struct{}),
    40  		watchers: make(map[*watcher]*watchBroadcast),
    41  		updatec:  make(chan *watchBroadcast, 1),
    42  		donec:    make(chan struct{}),
    43  	}
    44  	go func() {
    45  		defer close(wbs.donec)
    46  		for wb := range wbs.updatec {
    47  			wbs.coalesce(wb)
    48  		}
    49  	}()
    50  	return wbs
    51  }
    52  
    53  func (wbs *watchBroadcasts) coalesce(wb *watchBroadcast) {
    54  	if wb.size() >= maxCoalesceReceivers {
    55  		return
    56  	}
    57  	wbs.mu.Lock()
    58  	for wbswb := range wbs.bcasts {
    59  		if wbswb == wb {
    60  			continue
    61  		}
    62  		wb.mu.Lock()
    63  		wbswb.mu.Lock()
    64  		// 1. check if wbswb is behind wb so it won't skip any events in wb
    65  		// 2. ensure wbswb started; nextrev == 0 may mean wbswb is waiting
    66  		// for a current watcher and expects a create event from the server.
    67  		if wb.nextrev >= wbswb.nextrev && wbswb.responses > 0 {
    68  			for w := range wb.receivers {
    69  				wbswb.receivers[w] = struct{}{}
    70  				wbs.watchers[w] = wbswb
    71  			}
    72  			wb.receivers = nil
    73  		}
    74  		wbswb.mu.Unlock()
    75  		wb.mu.Unlock()
    76  		if wb.empty() {
    77  			delete(wbs.bcasts, wb)
    78  			wb.stop()
    79  			break
    80  		}
    81  	}
    82  	wbs.mu.Unlock()
    83  }
    84  
    85  func (wbs *watchBroadcasts) add(w *watcher) {
    86  	wbs.mu.Lock()
    87  	defer wbs.mu.Unlock()
    88  	// find fitting bcast
    89  	for wb := range wbs.bcasts {
    90  		if wb.add(w) {
    91  			wbs.watchers[w] = wb
    92  			return
    93  		}
    94  	}
    95  	// no fit; create a bcast
    96  	wb := newWatchBroadcast(wbs.wp, w, wbs.update)
    97  	wbs.watchers[w] = wb
    98  	wbs.bcasts[wb] = struct{}{}
    99  }
   100  
   101  // delete removes a watcher and returns the number of remaining watchers.
   102  func (wbs *watchBroadcasts) delete(w *watcher) int {
   103  	wbs.mu.Lock()
   104  	defer wbs.mu.Unlock()
   105  
   106  	wb, ok := wbs.watchers[w]
   107  	if !ok {
   108  		panic("deleting missing watcher from broadcasts")
   109  	}
   110  	delete(wbs.watchers, w)
   111  	wb.delete(w)
   112  	if wb.empty() {
   113  		delete(wbs.bcasts, wb)
   114  		wb.stop()
   115  	}
   116  	return len(wbs.bcasts)
   117  }
   118  
   119  func (wbs *watchBroadcasts) stop() {
   120  	wbs.mu.Lock()
   121  	for wb := range wbs.bcasts {
   122  		wb.stop()
   123  	}
   124  	wbs.bcasts = nil
   125  	close(wbs.updatec)
   126  	wbs.mu.Unlock()
   127  	<-wbs.donec
   128  }
   129  
   130  func (wbs *watchBroadcasts) update(wb *watchBroadcast) {
   131  	select {
   132  	case wbs.updatec <- wb:
   133  	default:
   134  	}
   135  }