github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/rangefeed/registry_test.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 rangefeed
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"testing"
    17  
    18  	_ "github.com/cockroachdb/cockroach/pkg/keys" // hook up pretty printer
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/storage"
    21  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    24  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  var (
    29  	keyA, keyB = roachpb.Key("a"), roachpb.Key("b")
    30  	keyC, keyD = roachpb.Key("c"), roachpb.Key("d")
    31  	keyX, keyY = roachpb.Key("x"), roachpb.Key("y")
    32  
    33  	spAB = roachpb.Span{Key: keyA, EndKey: keyB}
    34  	spBC = roachpb.Span{Key: keyB, EndKey: keyC}
    35  	spCD = roachpb.Span{Key: keyC, EndKey: keyD}
    36  	spAC = roachpb.Span{Key: keyA, EndKey: keyC}
    37  	spXY = roachpb.Span{Key: keyX, EndKey: keyY}
    38  )
    39  
    40  type testStream struct {
    41  	ctx     context.Context
    42  	ctxDone func()
    43  	mu      struct {
    44  		syncutil.Mutex
    45  		sendErr error
    46  		events  []*roachpb.RangeFeedEvent
    47  	}
    48  }
    49  
    50  func newTestStream() *testStream {
    51  	ctx, done := context.WithCancel(context.Background())
    52  	return &testStream{ctx: ctx, ctxDone: done}
    53  }
    54  
    55  func (s *testStream) Context() context.Context {
    56  	return s.ctx
    57  }
    58  
    59  func (s *testStream) Cancel() {
    60  	s.ctxDone()
    61  }
    62  
    63  func (s *testStream) Send(e *roachpb.RangeFeedEvent) error {
    64  	s.mu.Lock()
    65  	defer s.mu.Unlock()
    66  	if s.mu.sendErr != nil {
    67  		return s.mu.sendErr
    68  	}
    69  	s.mu.events = append(s.mu.events, e)
    70  	return nil
    71  }
    72  
    73  func (s *testStream) SetSendErr(err error) {
    74  	s.mu.Lock()
    75  	defer s.mu.Unlock()
    76  	s.mu.sendErr = err
    77  }
    78  
    79  func (s *testStream) Events() []*roachpb.RangeFeedEvent {
    80  	s.mu.Lock()
    81  	defer s.mu.Unlock()
    82  	es := s.mu.events
    83  	s.mu.events = nil
    84  	return es
    85  }
    86  
    87  func (s *testStream) BlockSend() func() {
    88  	s.mu.Lock()
    89  	return s.mu.Unlock
    90  }
    91  
    92  type testRegistration struct {
    93  	registration
    94  	stream *testStream
    95  	errC   <-chan *roachpb.Error
    96  }
    97  
    98  func newTestRegistration(
    99  	span roachpb.Span, ts hlc.Timestamp, catchup storage.SimpleIterator, withDiff bool,
   100  ) *testRegistration {
   101  	s := newTestStream()
   102  	errC := make(chan *roachpb.Error, 1)
   103  	return &testRegistration{
   104  		registration: newRegistration(
   105  			span,
   106  			ts,
   107  			catchup,
   108  			withDiff,
   109  			5,
   110  			NewMetrics(),
   111  			s,
   112  			errC,
   113  		),
   114  		stream: s,
   115  		errC:   errC,
   116  	}
   117  }
   118  
   119  func (r *testRegistration) Events() []*roachpb.RangeFeedEvent {
   120  	return r.stream.Events()
   121  }
   122  
   123  func (r *testRegistration) Err() *roachpb.Error {
   124  	select {
   125  	case pErr := <-r.errC:
   126  		return pErr
   127  	default:
   128  		return nil
   129  	}
   130  }
   131  
   132  func TestRegistrationBasic(t *testing.T) {
   133  	defer leaktest.AfterTest(t)()
   134  
   135  	val := roachpb.Value{RawBytes: []byte("val"), Timestamp: hlc.Timestamp{WallTime: 1}}
   136  	ev1, ev2 := new(roachpb.RangeFeedEvent), new(roachpb.RangeFeedEvent)
   137  	ev1.MustSetValue(&roachpb.RangeFeedValue{Key: keyA, Value: val})
   138  	ev2.MustSetValue(&roachpb.RangeFeedValue{Key: keyB, Value: val})
   139  
   140  	// Registration with no catchup scan specified.
   141  	noCatchupReg := newTestRegistration(spAB, hlc.Timestamp{}, nil, false)
   142  	noCatchupReg.publish(ev1)
   143  	noCatchupReg.publish(ev2)
   144  	require.Equal(t, len(noCatchupReg.buf), 2)
   145  	go noCatchupReg.runOutputLoop(context.Background())
   146  	require.NoError(t, noCatchupReg.waitForCaughtUp())
   147  	require.Equal(t, []*roachpb.RangeFeedEvent{ev1, ev2}, noCatchupReg.stream.Events())
   148  	noCatchupReg.disconnect(nil)
   149  	<-noCatchupReg.errC
   150  
   151  	// Registration with catchup scan.
   152  	catchupReg := newTestRegistration(spBC, hlc.Timestamp{WallTime: 1}, newTestIterator([]storage.MVCCKeyValue{
   153  		makeKV("b", "val1", 10),
   154  		makeInline("ba", "val2"),
   155  		makeKV("bc", "val3", 11),
   156  		makeKV("bd", "val4", 9),
   157  	}), false)
   158  	catchupReg.publish(ev1)
   159  	catchupReg.publish(ev2)
   160  	require.Equal(t, len(catchupReg.buf), 2)
   161  	go catchupReg.runOutputLoop(context.Background())
   162  	require.NoError(t, catchupReg.waitForCaughtUp())
   163  	events := catchupReg.stream.Events()
   164  	require.Equal(t, 6, len(events))
   165  	require.Equal(t, []*roachpb.RangeFeedEvent{ev1, ev2}, events[4:])
   166  	catchupReg.disconnect(nil)
   167  	<-catchupReg.errC
   168  
   169  	// EXIT CONDITIONS
   170  	// External Disconnect.
   171  	disconnectReg := newTestRegistration(spAB, hlc.Timestamp{}, nil, false)
   172  	disconnectReg.publish(ev1)
   173  	disconnectReg.publish(ev2)
   174  	go disconnectReg.runOutputLoop(context.Background())
   175  	require.NoError(t, disconnectReg.waitForCaughtUp())
   176  	discErr := roachpb.NewError(fmt.Errorf("disconnection error"))
   177  	disconnectReg.disconnect(discErr)
   178  	err := <-disconnectReg.errC
   179  	require.Equal(t, discErr, err)
   180  
   181  	// Overflow.
   182  	overflowReg := newTestRegistration(spAB, hlc.Timestamp{}, nil, false)
   183  	for i := 0; i < cap(overflowReg.buf)+3; i++ {
   184  		overflowReg.publish(ev1)
   185  	}
   186  	go overflowReg.runOutputLoop(context.Background())
   187  	err = <-overflowReg.errC
   188  	require.Equal(t, newErrBufferCapacityExceeded(), err)
   189  	require.Equal(t, cap(overflowReg.buf), len(overflowReg.Events()))
   190  
   191  	// Stream Error.
   192  	streamErrReg := newTestRegistration(spAB, hlc.Timestamp{}, nil, false)
   193  	streamErr := fmt.Errorf("stream error")
   194  	streamErrReg.stream.SetSendErr(streamErr)
   195  	go streamErrReg.runOutputLoop(context.Background())
   196  	streamErrReg.publish(ev1)
   197  	err = <-streamErrReg.errC
   198  	require.Equal(t, streamErr.Error(), err.GoError().Error())
   199  
   200  	// Stream Context Canceled.
   201  	streamCancelReg := newTestRegistration(spAB, hlc.Timestamp{}, nil, false)
   202  	streamCancelReg.stream.Cancel()
   203  	go streamCancelReg.runOutputLoop(context.Background())
   204  	require.NoError(t, streamCancelReg.waitForCaughtUp())
   205  	err = <-streamCancelReg.errC
   206  	require.Equal(t, streamCancelReg.stream.Context().Err().Error(), err.GoError().Error())
   207  }
   208  
   209  func TestRegistrationCatchUpScan(t *testing.T) {
   210  	defer leaktest.AfterTest(t)()
   211  
   212  	// Run a catch-up scan for a registration over a test
   213  	// iterator with the following keys.
   214  	txn1, txn2 := uuid.MakeV4(), uuid.MakeV4()
   215  	iter := newTestIterator([]storage.MVCCKeyValue{
   216  		makeKV("a", "valA1", 10),
   217  		makeInline("b", "valB1"),
   218  		makeIntent("c", txn1, "txnKeyC", 15),
   219  		makeProvisionalKV("c", "txnKeyC", 15),
   220  		makeKV("c", "valC2", 11),
   221  		makeKV("c", "valC1", 9),
   222  		makeIntent("d", txn2, "txnKeyD", 21),
   223  		makeProvisionalKV("d", "txnKeyD", 21),
   224  		makeKV("d", "valD5", 20),
   225  		makeKV("d", "valD4", 19),
   226  		makeKV("d", "valD3", 16),
   227  		makeKV("d", "valD2", 3),
   228  		makeKV("d", "valD1", 1),
   229  		makeKV("e", "valE3", 6),
   230  		makeKV("e", "valE2", 5),
   231  		makeKV("e", "valE1", 4),
   232  		makeKV("f", "valF3", 7),
   233  		makeKV("f", "valF2", 6),
   234  		makeKV("f", "valF1", 5),
   235  		makeInline("g", "valG1"),
   236  		makeKV("h", "valH1", 15),
   237  		makeKV("m", "valM1", 1),
   238  		makeIntent("n", txn1, "txnKeyN", 12),
   239  		makeProvisionalKV("n", "txnKeyN", 12),
   240  		makeIntent("r", txn1, "txnKeyR", 19),
   241  		makeProvisionalKV("r", "txnKeyR", 19),
   242  		makeKV("r", "valR1", 4),
   243  		makeIntent("w", txn1, "txnKeyW", 3),
   244  		makeProvisionalKV("w", "txnKeyW", 3),
   245  		makeInline("x", "valX1"),
   246  		makeIntent("z", txn2, "txnKeyZ", 21),
   247  		makeProvisionalKV("z", "txnKeyZ", 21),
   248  		makeKV("z", "valZ1", 4),
   249  	})
   250  	r := newTestRegistration(roachpb.Span{
   251  		Key:    roachpb.Key("d"),
   252  		EndKey: roachpb.Key("w"),
   253  	}, hlc.Timestamp{WallTime: 4}, iter, true /* withDiff */)
   254  
   255  	require.Zero(t, r.metrics.RangeFeedCatchupScanNanos.Count())
   256  	require.NoError(t, r.runCatchupScan())
   257  	require.True(t, iter.closed)
   258  	require.NotZero(t, r.metrics.RangeFeedCatchupScanNanos.Count())
   259  
   260  	// Compare the events sent on the registration's Stream to the expected events.
   261  	expEvents := []*roachpb.RangeFeedEvent{
   262  		rangeFeedValueWithPrev(
   263  			roachpb.Key("d"),
   264  			roachpb.Value{RawBytes: []byte("valD3"), Timestamp: hlc.Timestamp{WallTime: 16}},
   265  			roachpb.Value{RawBytes: []byte("valD2")},
   266  		),
   267  		rangeFeedValueWithPrev(
   268  			roachpb.Key("d"),
   269  			roachpb.Value{RawBytes: []byte("valD4"), Timestamp: hlc.Timestamp{WallTime: 19}},
   270  			roachpb.Value{RawBytes: []byte("valD3")},
   271  		),
   272  		rangeFeedValueWithPrev(
   273  			roachpb.Key("d"),
   274  			roachpb.Value{RawBytes: []byte("valD5"), Timestamp: hlc.Timestamp{WallTime: 20}},
   275  			roachpb.Value{RawBytes: []byte("valD4")},
   276  		),
   277  		rangeFeedValueWithPrev(
   278  			roachpb.Key("e"),
   279  			roachpb.Value{RawBytes: []byte("valE2"), Timestamp: hlc.Timestamp{WallTime: 5}},
   280  			roachpb.Value{RawBytes: []byte("valE1")},
   281  		),
   282  		rangeFeedValueWithPrev(
   283  			roachpb.Key("e"),
   284  			roachpb.Value{RawBytes: []byte("valE3"), Timestamp: hlc.Timestamp{WallTime: 6}},
   285  			roachpb.Value{RawBytes: []byte("valE2")},
   286  		),
   287  		rangeFeedValue(
   288  			roachpb.Key("f"),
   289  			roachpb.Value{RawBytes: []byte("valF1"), Timestamp: hlc.Timestamp{WallTime: 5}},
   290  		),
   291  		rangeFeedValueWithPrev(
   292  			roachpb.Key("f"),
   293  			roachpb.Value{RawBytes: []byte("valF2"), Timestamp: hlc.Timestamp{WallTime: 6}},
   294  			roachpb.Value{RawBytes: []byte("valF1")},
   295  		),
   296  		rangeFeedValueWithPrev(
   297  			roachpb.Key("f"),
   298  			roachpb.Value{RawBytes: []byte("valF3"), Timestamp: hlc.Timestamp{WallTime: 7}},
   299  			roachpb.Value{RawBytes: []byte("valF2")},
   300  		),
   301  		rangeFeedValue(
   302  			roachpb.Key("g"),
   303  			roachpb.Value{RawBytes: []byte("valG1"), Timestamp: hlc.Timestamp{WallTime: 0}},
   304  		),
   305  		rangeFeedValue(
   306  			roachpb.Key("h"),
   307  			roachpb.Value{RawBytes: []byte("valH1"), Timestamp: hlc.Timestamp{WallTime: 15}},
   308  		),
   309  	}
   310  	require.Equal(t, expEvents, r.Events())
   311  }
   312  
   313  func TestRegistryBasic(t *testing.T) {
   314  	defer leaktest.AfterTest(t)()
   315  
   316  	val := roachpb.Value{RawBytes: []byte("val"), Timestamp: hlc.Timestamp{WallTime: 1}}
   317  	ev1, ev2 := new(roachpb.RangeFeedEvent), new(roachpb.RangeFeedEvent)
   318  	ev3, ev4 := new(roachpb.RangeFeedEvent), new(roachpb.RangeFeedEvent)
   319  	ev1.MustSetValue(&roachpb.RangeFeedValue{Key: keyA, Value: val, PrevValue: val})
   320  	ev2.MustSetValue(&roachpb.RangeFeedValue{Key: keyB, Value: val, PrevValue: val})
   321  	ev3.MustSetValue(&roachpb.RangeFeedValue{Key: keyC, Value: val, PrevValue: val})
   322  	ev4.MustSetValue(&roachpb.RangeFeedValue{Key: keyD, Value: val, PrevValue: val})
   323  	err1 := roachpb.NewErrorf("error1")
   324  	noPrev := func(ev *roachpb.RangeFeedEvent) *roachpb.RangeFeedEvent {
   325  		ev = ev.ShallowCopy()
   326  		ev.GetValue().(*roachpb.RangeFeedValue).PrevValue = roachpb.Value{}
   327  		return ev
   328  	}
   329  
   330  	reg := makeRegistry()
   331  	require.Equal(t, 0, reg.Len())
   332  	require.NotPanics(t, func() { reg.PublishToOverlapping(spAB, ev1) })
   333  	require.NotPanics(t, func() { reg.Disconnect(spAB) })
   334  	require.NotPanics(t, func() { reg.DisconnectWithErr(spAB, err1) })
   335  
   336  	rAB := newTestRegistration(spAB, hlc.Timestamp{}, nil, false /* withDiff */)
   337  	rBC := newTestRegistration(spBC, hlc.Timestamp{}, nil, true /* withDiff */)
   338  	rCD := newTestRegistration(spCD, hlc.Timestamp{}, nil, true /* withDiff */)
   339  	rAC := newTestRegistration(spAC, hlc.Timestamp{}, nil, false /* withDiff */)
   340  	go rAB.runOutputLoop(context.Background())
   341  	go rBC.runOutputLoop(context.Background())
   342  	go rCD.runOutputLoop(context.Background())
   343  	go rAC.runOutputLoop(context.Background())
   344  	defer rAB.disconnect(nil)
   345  	defer rBC.disconnect(nil)
   346  	defer rCD.disconnect(nil)
   347  	defer rAC.disconnect(nil)
   348  
   349  	// Register 4 registrations.
   350  	reg.Register(&rAB.registration)
   351  	require.Equal(t, 1, reg.Len())
   352  	reg.Register(&rBC.registration)
   353  	require.Equal(t, 2, reg.Len())
   354  	reg.Register(&rCD.registration)
   355  	require.Equal(t, 3, reg.Len())
   356  	reg.Register(&rAC.registration)
   357  	require.Equal(t, 4, reg.Len())
   358  
   359  	// Publish to different spans.
   360  	reg.PublishToOverlapping(spAB, ev1)
   361  	reg.PublishToOverlapping(spBC, ev2)
   362  	reg.PublishToOverlapping(spCD, ev3)
   363  	reg.PublishToOverlapping(spAC, ev4)
   364  	require.NoError(t, reg.waitForCaughtUp(all))
   365  	require.Equal(t, []*roachpb.RangeFeedEvent{noPrev(ev1), noPrev(ev4)}, rAB.Events())
   366  	require.Equal(t, []*roachpb.RangeFeedEvent{ev2, ev4}, rBC.Events())
   367  	require.Equal(t, []*roachpb.RangeFeedEvent{ev3}, rCD.Events())
   368  	require.Equal(t, []*roachpb.RangeFeedEvent{noPrev(ev1), noPrev(ev2), noPrev(ev4)}, rAC.Events())
   369  	require.Nil(t, rAB.Err())
   370  	require.Nil(t, rBC.Err())
   371  	require.Nil(t, rCD.Err())
   372  	require.Nil(t, rAC.Err())
   373  
   374  	// Check the registry's operation filter.
   375  	f := reg.NewFilter()
   376  	// Testing NeedVal.
   377  	require.True(t, f.NeedVal(spAB))
   378  	require.True(t, f.NeedVal(spBC))
   379  	require.True(t, f.NeedVal(spCD))
   380  	require.True(t, f.NeedVal(spAC))
   381  	require.False(t, f.NeedVal(spXY))
   382  	require.True(t, f.NeedVal(roachpb.Span{Key: keyA}))
   383  	require.True(t, f.NeedVal(roachpb.Span{Key: keyB}))
   384  	require.True(t, f.NeedVal(roachpb.Span{Key: keyC}))
   385  	require.False(t, f.NeedVal(roachpb.Span{Key: keyX}))
   386  	// Testing NeedPrevVal.
   387  	require.False(t, f.NeedPrevVal(spAB))
   388  	require.True(t, f.NeedPrevVal(spBC))
   389  	require.True(t, f.NeedPrevVal(spCD))
   390  	require.True(t, f.NeedPrevVal(spAC))
   391  	require.False(t, f.NeedPrevVal(spXY))
   392  	require.False(t, f.NeedPrevVal(roachpb.Span{Key: keyA}))
   393  	require.True(t, f.NeedPrevVal(roachpb.Span{Key: keyB}))
   394  	require.True(t, f.NeedPrevVal(roachpb.Span{Key: keyC}))
   395  	require.False(t, f.NeedPrevVal(roachpb.Span{Key: keyX}))
   396  
   397  	// Disconnect span that overlaps with rCD.
   398  	reg.DisconnectWithErr(spCD, err1)
   399  	require.Equal(t, 3, reg.Len())
   400  	require.Equal(t, err1.GoError(), rCD.Err().GoError())
   401  
   402  	// Can still publish to rAB.
   403  	reg.PublishToOverlapping(spAB, ev4)
   404  	reg.PublishToOverlapping(spBC, ev3)
   405  	reg.PublishToOverlapping(spCD, ev2)
   406  	reg.PublishToOverlapping(spAC, ev1)
   407  	require.NoError(t, reg.waitForCaughtUp(all))
   408  	require.Equal(t, []*roachpb.RangeFeedEvent{noPrev(ev4), noPrev(ev1)}, rAB.Events())
   409  
   410  	// Disconnect from rAB without error.
   411  	reg.Disconnect(spAB)
   412  	require.Nil(t, rAC.Err())
   413  	require.Nil(t, rAB.Err())
   414  	require.Equal(t, 1, reg.Len())
   415  
   416  	// Check the registry's operation filter again.
   417  	f = reg.NewFilter()
   418  	// Testing NeedVal.
   419  	require.False(t, f.NeedVal(spAB))
   420  	require.True(t, f.NeedVal(spBC))
   421  	require.False(t, f.NeedVal(spCD))
   422  	require.True(t, f.NeedVal(spAC))
   423  	require.False(t, f.NeedVal(spXY))
   424  	require.False(t, f.NeedVal(roachpb.Span{Key: keyA}))
   425  	require.True(t, f.NeedVal(roachpb.Span{Key: keyB}))
   426  	require.False(t, f.NeedVal(roachpb.Span{Key: keyC}))
   427  	require.False(t, f.NeedVal(roachpb.Span{Key: keyX}))
   428  	// Testing NeedPrevVal.
   429  	require.False(t, f.NeedPrevVal(spAB))
   430  	require.True(t, f.NeedPrevVal(spBC))
   431  	require.False(t, f.NeedPrevVal(spCD))
   432  	require.True(t, f.NeedPrevVal(spAC))
   433  	require.False(t, f.NeedPrevVal(spXY))
   434  	require.False(t, f.NeedPrevVal(roachpb.Span{Key: keyA}))
   435  	require.True(t, f.NeedPrevVal(roachpb.Span{Key: keyB}))
   436  	require.False(t, f.NeedPrevVal(roachpb.Span{Key: keyC}))
   437  	require.False(t, f.NeedPrevVal(roachpb.Span{Key: keyX}))
   438  
   439  	// Unregister the rBC registration.
   440  	reg.Unregister(&rBC.registration)
   441  	require.Equal(t, 0, reg.Len())
   442  }
   443  
   444  func TestRegistryPublishAssertsPopulatedInformation(t *testing.T) {
   445  	defer leaktest.AfterTest(t)()
   446  	reg := makeRegistry()
   447  
   448  	rNoDiff := newTestRegistration(spAB, hlc.Timestamp{}, nil, false /* withDiff */)
   449  	go rNoDiff.runOutputLoop(context.Background())
   450  	reg.Register(&rNoDiff.registration)
   451  
   452  	rWithDiff := newTestRegistration(spCD, hlc.Timestamp{}, nil, true /* withDiff */)
   453  	go rWithDiff.runOutputLoop(context.Background())
   454  	reg.Register(&rWithDiff.registration)
   455  
   456  	key := roachpb.Key("a")
   457  	val := roachpb.Value{RawBytes: []byte("val"), Timestamp: hlc.Timestamp{WallTime: 1}}
   458  	noVal := roachpb.Value{Timestamp: hlc.Timestamp{WallTime: 1}}
   459  	ev := new(roachpb.RangeFeedEvent)
   460  
   461  	// Both registrations require RangeFeedValue events to have a Key.
   462  	ev.MustSetValue(&roachpb.RangeFeedValue{
   463  		Key:       nil,
   464  		Value:     val,
   465  		PrevValue: val,
   466  	})
   467  	require.Panics(t, func() { reg.PublishToOverlapping(spAB, ev) })
   468  	require.Panics(t, func() { reg.PublishToOverlapping(spCD, ev) })
   469  	require.NoError(t, reg.waitForCaughtUp(all))
   470  
   471  	// Both registrations require RangeFeedValue events to have a Value.
   472  	ev.MustSetValue(&roachpb.RangeFeedValue{
   473  		Key:       key,
   474  		Value:     noVal,
   475  		PrevValue: val,
   476  	})
   477  	require.Panics(t, func() { reg.PublishToOverlapping(spAB, ev) })
   478  	require.Panics(t, func() { reg.PublishToOverlapping(spCD, ev) })
   479  	require.NoError(t, reg.waitForCaughtUp(all))
   480  
   481  	// Neither registrations require RangeFeedValue events to have a PrevValue.
   482  	// Even when they are requested, the previous value can always be nil.
   483  	ev.MustSetValue(&roachpb.RangeFeedValue{
   484  		Key:       key,
   485  		Value:     val,
   486  		PrevValue: roachpb.Value{},
   487  	})
   488  	require.NotPanics(t, func() { reg.PublishToOverlapping(spAB, ev) })
   489  	require.NotPanics(t, func() { reg.PublishToOverlapping(spCD, ev) })
   490  	require.NoError(t, reg.waitForCaughtUp(all))
   491  
   492  	rNoDiff.disconnect(nil)
   493  	rWithDiff.disconnect(nil)
   494  }
   495  
   496  func TestRegistryPublishBeneathStartTimestamp(t *testing.T) {
   497  	defer leaktest.AfterTest(t)()
   498  	reg := makeRegistry()
   499  
   500  	r := newTestRegistration(spAB, hlc.Timestamp{WallTime: 10}, nil, false)
   501  	go r.runOutputLoop(context.Background())
   502  	reg.Register(&r.registration)
   503  
   504  	// Publish a value with a timestamp beneath the registration's start
   505  	// timestamp. Should be ignored.
   506  	ev := new(roachpb.RangeFeedEvent)
   507  	ev.MustSetValue(&roachpb.RangeFeedValue{
   508  		Value: roachpb.Value{Timestamp: hlc.Timestamp{WallTime: 5}},
   509  	})
   510  	reg.PublishToOverlapping(spAB, ev)
   511  	require.NoError(t, reg.waitForCaughtUp(all))
   512  	require.Nil(t, r.Events())
   513  
   514  	// Publish a value with a timestamp equal to the registration's start
   515  	// timestamp. Should be ignored.
   516  	ev.MustSetValue(&roachpb.RangeFeedValue{
   517  		Value: roachpb.Value{Timestamp: hlc.Timestamp{WallTime: 10}},
   518  	})
   519  	reg.PublishToOverlapping(spAB, ev)
   520  	require.NoError(t, reg.waitForCaughtUp(all))
   521  	require.Nil(t, r.Events())
   522  
   523  	// Publish a checkpoint with a timestamp beneath the registration's. Should
   524  	// be delivered.
   525  	ev.MustSetValue(&roachpb.RangeFeedCheckpoint{
   526  		Span: spAB, ResolvedTS: hlc.Timestamp{WallTime: 5},
   527  	})
   528  	reg.PublishToOverlapping(spAB, ev)
   529  	require.NoError(t, reg.waitForCaughtUp(all))
   530  	require.Equal(t, []*roachpb.RangeFeedEvent{ev}, r.Events())
   531  
   532  	r.disconnect(nil)
   533  	<-r.errC
   534  }
   535  
   536  func TestRegistrationString(t *testing.T) {
   537  	testCases := []struct {
   538  		r   registration
   539  		exp string
   540  	}{
   541  		{
   542  			r: registration{
   543  				span: roachpb.Span{Key: roachpb.Key("a")},
   544  			},
   545  			exp: `[a @ 0,0+]`,
   546  		},
   547  		{
   548  			r: registration{span: roachpb.Span{
   549  				Key: roachpb.Key("a"), EndKey: roachpb.Key("c")},
   550  			},
   551  			exp: `[{a-c} @ 0,0+]`,
   552  		},
   553  		{
   554  			r: registration{
   555  				span:             roachpb.Span{Key: roachpb.Key("d")},
   556  				catchupTimestamp: hlc.Timestamp{WallTime: 10, Logical: 1},
   557  			},
   558  			exp: `[d @ 0.000000010,1+]`,
   559  		},
   560  		{
   561  			r: registration{span: roachpb.Span{
   562  				Key: roachpb.Key("d"), EndKey: roachpb.Key("z")},
   563  				catchupTimestamp: hlc.Timestamp{WallTime: 40, Logical: 9},
   564  			},
   565  			exp: `[{d-z} @ 0.000000040,9+]`,
   566  		},
   567  	}
   568  	for _, tc := range testCases {
   569  		require.Equal(t, tc.exp, tc.r.String())
   570  	}
   571  }