go.etcd.io/etcd@v3.3.27+incompatible/proxy/grpcproxy/watcher.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  	"time"
    19  
    20  	"github.com/coreos/etcd/clientv3"
    21  	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
    22  	"github.com/coreos/etcd/mvcc"
    23  	"github.com/coreos/etcd/mvcc/mvccpb"
    24  )
    25  
    26  type watchRange struct {
    27  	key, end string
    28  }
    29  
    30  func (wr *watchRange) valid() bool {
    31  	return len(wr.end) == 0 || wr.end > wr.key || (wr.end[0] == 0 && len(wr.end) == 1)
    32  }
    33  
    34  type watcher struct {
    35  	// user configuration
    36  
    37  	wr       watchRange
    38  	filters  []mvcc.FilterFunc
    39  	progress bool
    40  	prevKV   bool
    41  
    42  	// id is the id returned to the client on its watch stream.
    43  	id int64
    44  	// nextrev is the minimum expected next event revision.
    45  	nextrev int64
    46  	// lastHeader has the last header sent over the stream.
    47  	lastHeader pb.ResponseHeader
    48  
    49  	// wps is the parent.
    50  	wps *watchProxyStream
    51  }
    52  
    53  // send filters out repeated events by discarding revisions older
    54  // than the last one sent over the watch channel.
    55  func (w *watcher) send(wr clientv3.WatchResponse) {
    56  	if wr.IsProgressNotify() && !w.progress {
    57  		return
    58  	}
    59  	if w.nextrev > wr.Header.Revision && len(wr.Events) > 0 {
    60  		return
    61  	}
    62  	if w.nextrev == 0 {
    63  		// current watch; expect updates following this revision
    64  		w.nextrev = wr.Header.Revision + 1
    65  	}
    66  
    67  	events := make([]*mvccpb.Event, 0, len(wr.Events))
    68  
    69  	var lastRev int64
    70  	for i := range wr.Events {
    71  		ev := (*mvccpb.Event)(wr.Events[i])
    72  		if ev.Kv.ModRevision < w.nextrev {
    73  			continue
    74  		} else {
    75  			// We cannot update w.rev here.
    76  			// txn can have multiple events with the same rev.
    77  			// If w.nextrev updates here, it would skip events in the same txn.
    78  			lastRev = ev.Kv.ModRevision
    79  		}
    80  
    81  		filtered := false
    82  		for _, filter := range w.filters {
    83  			if filter(*ev) {
    84  				filtered = true
    85  				break
    86  			}
    87  		}
    88  		if filtered {
    89  			continue
    90  		}
    91  
    92  		if !w.prevKV {
    93  			evCopy := *ev
    94  			evCopy.PrevKv = nil
    95  			ev = &evCopy
    96  		}
    97  		events = append(events, ev)
    98  	}
    99  
   100  	if lastRev >= w.nextrev {
   101  		w.nextrev = lastRev + 1
   102  	}
   103  
   104  	// all events are filtered out?
   105  	if !wr.IsProgressNotify() && !wr.Created && len(events) == 0 && wr.CompactRevision == 0 {
   106  		return
   107  	}
   108  
   109  	w.lastHeader = wr.Header
   110  	w.post(&pb.WatchResponse{
   111  		Header:          &wr.Header,
   112  		Created:         wr.Created,
   113  		CompactRevision: wr.CompactRevision,
   114  		Canceled:        wr.Canceled,
   115  		WatchId:         w.id,
   116  		Events:          events,
   117  	})
   118  }
   119  
   120  // post puts a watch response on the watcher's proxy stream channel
   121  func (w *watcher) post(wr *pb.WatchResponse) bool {
   122  	select {
   123  	case w.wps.watchCh <- wr:
   124  	case <-time.After(50 * time.Millisecond):
   125  		w.wps.cancel()
   126  		return false
   127  	}
   128  	return true
   129  }