Repository: etcd-io/etcd
File: server/etcdserver/v3_server.go (Go)

Code before:
		span.AddEvent("Receive raft result")
		return x.(*apply2.Result), nil
	case <-cctx.Done():
		proposalsFailed.Inc()
		s.w.Trigger(id, nil) // GC wait
		return nil, s.parseProposeCtxErr(cctx.Err(), start)
	case <-s.done:
		return nil, errors.ErrStopped
	}
}

// Watchable returns a watchable interface attached to the etcdserver.
func (s *EtcdServer) Watchable() mvcc.WatchableKV { return s.KV() }

func (s *EtcdServer) linearizableReadLoop() {
	for {
		requestID := s.reqIDGen.Next()
		leaderChangedNotifier := s.leaderChanged.Receive()
		select {
		case <-leaderChangedNotifier:
			continue
		case <-s.readwaitc:
		case <-s.stopping:
			return
		}

		// as a single loop is can unlock multiple reads, it is not very useful
		// to propagate the trace from Txn or Range.
		_, trace := traceutil.EnsureTrace(context.Background(), s.Logger(), "linearizableReadLoop")

		nextnr := newNotifier()
		s.readMu.Lock()
		nr := s.readNotifier
		s.readNotifier = nextnr
		s.readMu.Unlock()

		confirmedIndex, err := s.requestCurrentIndex(leaderChangedNotifier, requestID)
		if isStopped(err) {
			return
		}
		if err != nil {
			nr.notify(err)
			continue
		}

		trace.Step("read index received")

		trace.AddField(traceutil.Field{Key: "readStateIndex", Value: confirmedIndex})

		appliedIndex := s.getAppliedIndex()
		trace.AddField(traceutil.Field{Key: "appliedIndex", Value: strconv.FormatUint(appliedIndex, 10)})

Review comment (performance):
I know it's a bit late, but you can also add a test to check whether we cover all protobuf fields:

```
func getRequestType(r *pb.InternalRaftRequest) string {
	e := reflect.TypeOf(r).Elem()
	for i := 0; i < e.NumField(); i++ {
		if e.Field(i).Tag.Get("protobuf") != "" {
			// test whether  e.Field(i).Name returns Unknown
		}
	}
``` 

otherwise we might not notice an endpoint went missing. Of course we can also add reflection directly here, but that likely is more expensive than the switch checks.

Code after:

	span := trace.SpanFromContext(ctx)
	span.AddEvent("Send raft proposal")
	err = s.r.Propose(cctx, data)
	if err != nil {
		proposalsFailed.Inc()
		s.w.Trigger(id, nil) // GC wait
		return nil, err
	}
	proposalsPending.Inc()
	defer proposalsPending.Dec()

	select {
	case x := <-ch:
		span.AddEvent("Receive raft result")
		return x.(*apply2.Result), nil
	case <-cctx.Done():
		proposalsFailed.Inc()
		s.w.Trigger(id, nil) // GC wait
		return nil, s.parseProposeCtxErr(cctx.Err(), start)
	case <-s.done:
		return nil, errors.ErrStopped
	}
}

func getRequestType(r *pb.InternalRaftRequest) string {
	switch {
	case r.Range != nil:
		return "Range"
	case r.Put != nil:
		return "Put"
	case r.DeleteRange != nil:
		return "DeleteRange"
	case r.Txn != nil:
		return "Txn"
	case r.Compaction != nil:
		return "Compaction"
	case r.LeaseGrant != nil:
		return "LeaseGrant"
	case r.LeaseRevoke != nil:
		return "LeaseRevoke"
	case r.LeaseCheckpoint != nil:
		return "LeaseCheckpoint"
	case r.Alarm != nil:
		return "Alarm"
	case r.Authenticate != nil:
		return "Authenticate"
	case r.AuthEnable != nil:
		return "AuthEnable"
	case r.AuthDisable != nil:
		return "AuthDisable"