github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/closedts/transport/transport_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 transport_test 12 13 import ( 14 "context" 15 "fmt" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts/ctpb" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts/transport" 21 transporttestutils "github.com/cockroachdb/cockroach/pkg/kv/kvserver/closedts/transport/testutils" 22 "github.com/cockroachdb/cockroach/pkg/roachpb" 23 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 24 "github.com/cockroachdb/cockroach/pkg/testutils" 25 "github.com/cockroachdb/cockroach/pkg/util/hlc" 26 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 27 "github.com/cockroachdb/cockroach/pkg/util/stop" 28 "github.com/cockroachdb/errors" 29 "github.com/kr/pretty" 30 ) 31 32 // NewTestContainer sets up an environment suitable for black box testing the 33 // transport subsystem. The returned test container contains most notably a 34 // Clients and Server set up to communicate to each other via a Dialer (which 35 // keeps a transcript that can be verified). 36 func NewTestContainer() *TestContainer { 37 stopper := stop.NewStopper() 38 39 st := cluster.MakeTestingClusterSettings() 40 p := &TestProducer{} 41 sink := newTestNotifyee(stopper) 42 refreshed := &RefreshTracker{} 43 s := transport.NewServer(stopper, p, refreshed.Add) 44 dialer := transporttestutils.NewChanDialer(stopper, s) 45 c := transport.NewClients(transport.Config{ 46 NodeID: roachpb.NodeID(12345), 47 Settings: st, 48 Stopper: stopper, 49 Dialer: dialer, 50 Sink: sink, 51 }) 52 return &TestContainer{ 53 Settings: st, 54 Stopper: stopper, 55 Producer: p, 56 Notifyee: sink, 57 Refreshed: refreshed, 58 Server: s, 59 Dialer: dialer, 60 Clients: c, 61 } 62 } 63 64 func assertNumSubscribers(t *testing.T, p *TestProducer, exp int) { 65 testutils.SucceedsSoon(t, func() error { 66 n := p.numSubscriptions() 67 if n > exp { 68 t.Fatalf("expected a single subscription, got %d", n) 69 } 70 if n < exp { 71 return errors.New("waiting for subscription") 72 } 73 return nil 74 }) 75 } 76 77 func TestTransportConnectOnRequest(t *testing.T) { 78 defer leaktest.AfterTest(t)() 79 80 container := NewTestContainer() 81 defer container.Stopper.Stop(context.Background()) 82 83 const ( 84 nodeID = 1 85 rangeID = 13 86 ) 87 88 // Requesting an update for a Range implies a connection attempt. 89 container.Clients.Request(nodeID, rangeID) 90 91 // Find the connection (via its subscription to receive new Entries). 92 assertNumSubscribers(t, container.Producer, 1) 93 94 // Verify that the client soon asks the server for an update for this range. 95 testutils.SucceedsSoon(t, func() error { 96 act := container.Refreshed.Get() 97 exp := []roachpb.RangeID{rangeID} 98 99 if diff := pretty.Diff(act, exp); len(diff) != 0 { 100 // We have to kick the tires a little bit. The client can only send 101 // the request as the reaction to an Entry. 102 container.Producer.sendAll(ctpb.Entry{}) 103 return errors.Errorf("diff(act, exp): %s", strings.Join(diff, "\n")) 104 } 105 return nil 106 }) 107 } 108 109 func TestTransportClientReceivesEntries(t *testing.T) { 110 defer leaktest.AfterTest(t)() 111 112 container := NewTestContainer() 113 defer container.Stopper.Stop(context.Background()) 114 115 const nodeID = 7 116 117 // Manual reconnections don't spawn new clients. 118 container.Clients.EnsureClient(nodeID) 119 container.Clients.EnsureClient(nodeID) 120 container.Clients.EnsureClient(nodeID) 121 assertNumSubscribers(t, container.Producer, 1) 122 123 // But connecting to other nodes does (only once). 124 for i := 0; i < 7; i++ { 125 container.Clients.EnsureClient(nodeID + 1) 126 container.Clients.EnsureClient(nodeID + 2) 127 container.Clients.Request(nodeID+3, roachpb.RangeID(7)) 128 } 129 assertNumSubscribers(t, container.Producer, 4) 130 131 // Our initial client doesn't do anything except say "hello" via 132 // a Reaction. 133 testutils.SucceedsSoon(t, func() error { 134 expectedTranscript := []interface{}{ 135 &ctpb.Reaction{}, 136 } 137 return checkTranscript(t, container.Dialer.Transcript(nodeID), expectedTranscript) 138 }) 139 140 // Now the producer (to which the server should maintain a subscription for this client, and 141 // notifications from which it should relay) emits an Entry. 142 e1 := ctpb.Entry{ClosedTimestamp: hlc.Timestamp{WallTime: 1e9}, Epoch: 12, MLAI: map[roachpb.RangeID]ctpb.LAI{12: 7}} 143 container.Producer.sendAll(e1) 144 145 // The client should see this entry soon thereafter. it responds with an empty 146 // Reaction (since we haven't Request()ed anything). 147 testutils.SucceedsSoon(t, func() error { 148 expectedTranscript := []interface{}{ 149 &ctpb.Reaction{}, 150 &e1, 151 &ctpb.Reaction{}, 152 } 153 return checkTranscript(t, container.Dialer.Transcript(nodeID), expectedTranscript) 154 }) 155 156 // And again, but only after Request() is called (which should be reflected in the transcript). 157 const rangeID = 7 158 container.Clients.Request(nodeID, rangeID) 159 e2 := ctpb.Entry{ClosedTimestamp: hlc.Timestamp{WallTime: 2e9}, Epoch: 13, MLAI: map[roachpb.RangeID]ctpb.LAI{13: 8}} 160 container.Producer.sendAll(e2) 161 testutils.SucceedsSoon(t, func() error { 162 expectedTranscript := []interface{}{ 163 &ctpb.Reaction{}, 164 &e1, 165 &ctpb.Reaction{}, 166 &e2, 167 &ctpb.Reaction{Requested: []roachpb.RangeID{rangeID}}, 168 } 169 return checkTranscript(t, container.Dialer.Transcript(nodeID), expectedTranscript) 170 }) 171 172 } 173 174 func checkTranscript(t *testing.T, actI, expI []interface{}) error { 175 t.Helper() 176 var act, exp []string 177 for _, i := range actI { 178 act = append(act, strings.TrimSpace(fmt.Sprintf("%v", i))) 179 } 180 for _, i := range expI { 181 exp = append(exp, strings.TrimSpace(fmt.Sprintf("%v", i))) 182 } 183 184 diffErr := errors.Errorf("actual:\n%s\nexpected:\n%s", strings.Join(act, "\n"), strings.Join(exp, "\n")) 185 if len(act) > len(exp) { 186 t.Fatal(errors.Wrap(diffErr, "actual transcript longer than expected")) 187 } 188 if len(act) < len(exp) { 189 return errors.Wrap(diffErr, "waiting for more") 190 } 191 if diff := pretty.Diff(actI, expI); len(diff) != 0 { 192 t.Fatal(errors.Wrapf(diffErr, "diff:\n%v\n", strings.Join(diff, "\n"))) 193 } 194 return nil 195 }