github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/closedts/transport/server.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package transport
    12  
    13  import (
    14  	"context"
    15  	"time"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts"
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts/ctpb"
    19  	"github.com/cockroachdb/cockroach/pkg/util/log"
    20  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    21  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    22  	"github.com/cockroachdb/errors"
    23  )
    24  
    25  // Server handles incoming closed timestamp update stream requests.
    26  type Server struct {
    27  	stopper *stop.Stopper
    28  	p       closedts.Producer
    29  	refresh closedts.RefreshFn
    30  }
    31  
    32  // NewServer sets up a Server which relays information from the given producer
    33  // to incoming clients.
    34  func NewServer(stopper *stop.Stopper, p closedts.Producer, refresh closedts.RefreshFn) *Server {
    35  	return &Server{
    36  		stopper: stopper,
    37  		p:       p,
    38  		refresh: refresh,
    39  	}
    40  }
    41  
    42  var _ ctpb.Server = (*Server)(nil)
    43  
    44  // Get handles incoming client connections.
    45  func (s *Server) Get(client ctpb.InboundClient) error {
    46  	// TODO(tschottdorf): the InboundClient API isn't great since it
    47  	// is blocking. How can we eagerly terminate these connections when
    48  	// the server shuts down? I think we need to inject a cancellation
    49  	// into the context, but grpc hands that to us.
    50  	// This problem has likely been solved somewhere in our codebase.
    51  	ctx := client.Context()
    52  	ch := make(chan ctpb.Entry, 10)
    53  
    54  	if log.V(1) {
    55  		log.Infof(ctx, "closed timestamp server serving new inbound client connection")
    56  	}
    57  
    58  	// TODO(tschottdorf): make this, say, 2*closedts.CloseFraction*closedts.TargetInterval.
    59  	const closedTimestampNoUpdateWarnThreshold = 10 * time.Second
    60  	t := timeutil.NewTimer()
    61  
    62  	// NB: We can't use Stopper.RunWorker because doing so would race with
    63  	// calling Stopper.Stop.
    64  	if err := s.stopper.RunAsyncTask(ctx, "closedts-subscription", func(ctx context.Context) {
    65  		s.p.Subscribe(ctx, ch)
    66  	}); err != nil {
    67  		return err
    68  	}
    69  	for {
    70  		reaction, err := client.Recv()
    71  		if err != nil {
    72  			return err
    73  		}
    74  
    75  		if len(reaction.Requested) != 0 {
    76  			s.refresh(reaction.Requested...)
    77  		}
    78  
    79  		t.Reset(closedTimestampNoUpdateWarnThreshold)
    80  		var entry ctpb.Entry
    81  		var ok bool
    82  		select {
    83  		case <-ctx.Done():
    84  			return ctx.Err()
    85  		case <-s.stopper.ShouldQuiesce():
    86  			return errors.New("node is draining")
    87  		case entry, ok = <-ch:
    88  			if !ok {
    89  				return errors.New("subscription dropped unexpectedly")
    90  			}
    91  		case <-t.C:
    92  			t.Read = true
    93  			// Send an empty entry to the client, which can use that to warn
    94  			// about the absence of heartbeats. We don't log here since it
    95  			// would log a message per incoming stream, which makes little
    96  			// sense. It's the producer's job to warn on this node.
    97  		}
    98  		if err := client.Send(&entry); err != nil {
    99  			return err
   100  		}
   101  	}
   102  }