github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/scheduler_test.go (about) 1 // Copyright 2016 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 kvserver 12 13 import ( 14 "bytes" 15 "context" 16 "fmt" 17 "sort" 18 "testing" 19 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/testutils" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 "github.com/cockroachdb/cockroach/pkg/util/stop" 24 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 25 "github.com/cockroachdb/errors" 26 ) 27 28 func TestRangeIDChunk(t *testing.T) { 29 defer leaktest.AfterTest(t)() 30 31 var c rangeIDChunk 32 if c.Len() != 0 { 33 t.Fatalf("expected empty chunk, but found %d", c.Len()) 34 } 35 if c.WriteCap() != rangeIDChunkSize { 36 t.Fatalf("expected %d, but found %d", rangeIDChunkSize, c.WriteCap()) 37 } 38 if _, ok := c.PopFront(); ok { 39 t.Fatalf("successfully popped from empty chunk") 40 } 41 42 for i := 1; i <= rangeIDChunkSize; i++ { 43 if !c.PushBack(roachpb.RangeID(i)) { 44 t.Fatalf("%d: failed to push", i) 45 } 46 if e := i; e != c.Len() { 47 t.Fatalf("expected %d, but found %d", e, c.Len()) 48 } 49 if e := rangeIDChunkSize - i; e != c.WriteCap() { 50 t.Fatalf("expected %d, but found %d", e, c.WriteCap()) 51 } 52 } 53 if c.PushBack(0) { 54 t.Fatalf("successfully pushed to full chunk") 55 } 56 57 for i := 1; i <= rangeIDChunkSize; i++ { 58 id, ok := c.PopFront() 59 if !ok { 60 t.Fatalf("%d: failed to pop", i) 61 } 62 if roachpb.RangeID(i) != id { 63 t.Fatalf("expected %d, but found %d", i, id) 64 } 65 if e := rangeIDChunkSize - i; e != c.Len() { 66 t.Fatalf("expected %d, but found %d", e, c.Len()) 67 } 68 if c.WriteCap() != 0 { 69 t.Fatalf("expected full chunk, but found %d", c.WriteCap()) 70 } 71 } 72 if c.Len() != 0 { 73 t.Fatalf("expected empty chunk, but found %d", c.Len()) 74 } 75 if c.WriteCap() != 0 { 76 t.Fatalf("expected full chunk, but found %d", c.WriteCap()) 77 } 78 if _, ok := c.PopFront(); ok { 79 t.Fatalf("successfully popped from empty chunk") 80 } 81 } 82 83 func TestRangeIDQueue(t *testing.T) { 84 defer leaktest.AfterTest(t)() 85 86 var q rangeIDQueue 87 if q.Len() != 0 { 88 t.Fatalf("expected empty queue, but found %d", q.Len()) 89 } 90 if _, ok := q.PopFront(); ok { 91 t.Fatalf("successfully popped from empty queue") 92 } 93 94 const count = 3 * rangeIDChunkSize 95 for i := 1; i <= count; i++ { 96 q.PushBack(roachpb.RangeID(i)) 97 if e := i; e != q.Len() { 98 t.Fatalf("expected %d, but found %d", e, q.Len()) 99 } 100 } 101 102 for i := 1; i <= count; i++ { 103 id, ok := q.PopFront() 104 if !ok { 105 t.Fatalf("%d: failed to pop", i) 106 } 107 if roachpb.RangeID(i) != id { 108 t.Fatalf("expected %d, but found %d", i, id) 109 } 110 if e := count - i; e != q.Len() { 111 t.Fatalf("expected %d, but found %d", e, q.Len()) 112 } 113 } 114 if q.Len() != 0 { 115 t.Fatalf("expected empty queue, but found %d", q.Len()) 116 } 117 if _, ok := q.PopFront(); ok { 118 t.Fatalf("successfully popped from empty queue") 119 } 120 } 121 122 type testProcessor struct { 123 mu struct { 124 syncutil.Mutex 125 raftReady map[roachpb.RangeID]int 126 raftRequest map[roachpb.RangeID]int 127 raftTick map[roachpb.RangeID]int 128 } 129 } 130 131 func newTestProcessor() *testProcessor { 132 p := &testProcessor{} 133 p.mu.raftReady = make(map[roachpb.RangeID]int) 134 p.mu.raftRequest = make(map[roachpb.RangeID]int) 135 p.mu.raftTick = make(map[roachpb.RangeID]int) 136 return p 137 } 138 139 func (p *testProcessor) processReady(_ context.Context, rangeID roachpb.RangeID) { 140 p.mu.Lock() 141 p.mu.raftReady[rangeID]++ 142 p.mu.Unlock() 143 } 144 145 func (p *testProcessor) processRequestQueue(_ context.Context, rangeID roachpb.RangeID) bool { 146 p.mu.Lock() 147 p.mu.raftRequest[rangeID]++ 148 p.mu.Unlock() 149 return false 150 } 151 152 func (p *testProcessor) processTick(_ context.Context, rangeID roachpb.RangeID) bool { 153 p.mu.Lock() 154 p.mu.raftTick[rangeID]++ 155 p.mu.Unlock() 156 return false 157 } 158 159 func (p *testProcessor) countsLocked(m map[roachpb.RangeID]int) string { 160 var ids roachpb.RangeIDSlice 161 for id := range m { 162 ids = append(ids, id) 163 } 164 sort.Sort(ids) 165 var buf bytes.Buffer 166 fmt.Fprintf(&buf, "[") 167 for i, id := range ids { 168 if i > 0 { 169 fmt.Fprintf(&buf, ",") 170 } 171 fmt.Fprintf(&buf, "%d:%d", id, m[id]) 172 } 173 fmt.Fprintf(&buf, "]") 174 return buf.String() 175 } 176 177 func (p *testProcessor) String() string { 178 p.mu.Lock() 179 defer p.mu.Unlock() 180 return fmt.Sprintf("ready=%s request=%s tick=%s", 181 p.countsLocked(p.mu.raftReady), 182 p.countsLocked(p.mu.raftRequest), 183 p.countsLocked(p.mu.raftTick)) 184 } 185 186 // Verify that enqueuing more ranges than the number of workers correctly 187 // processes all of the ranges. This exercises a code path that was buggy 188 // during development. 189 func TestSchedulerLoop(t *testing.T) { 190 defer leaktest.AfterTest(t)() 191 192 p := newTestProcessor() 193 s := newRaftScheduler(nil, p, 1) 194 stopper := stop.NewStopper() 195 ctx := context.Background() 196 defer stopper.Stop(ctx) 197 s.Start(ctx, stopper) 198 s.EnqueueRaftTick(1, 2, 3) 199 200 testutils.SucceedsSoon(t, func() error { 201 const expected = "ready=[] request=[] tick=[1:1,2:1,3:1]" 202 if s := p.String(); expected != s { 203 return errors.Errorf("expected %s, but got %s", expected, s) 204 } 205 return nil 206 }) 207 } 208 209 // Verify that when we enqueue the same range multiple times for the same 210 // reason, it is only processed once. 211 func TestSchedulerBuffering(t *testing.T) { 212 defer leaktest.AfterTest(t)() 213 214 p := newTestProcessor() 215 s := newRaftScheduler(nil, p, 1) 216 stopper := stop.NewStopper() 217 ctx := context.Background() 218 defer stopper.Stop(ctx) 219 s.Start(ctx, stopper) 220 221 testCases := []struct { 222 state raftScheduleState 223 expected string 224 }{ 225 {stateRaftReady, "ready=[1:1] request=[] tick=[]"}, 226 {stateRaftRequest, "ready=[1:1] request=[1:1] tick=[]"}, 227 {stateRaftTick, "ready=[1:1] request=[1:1] tick=[1:1]"}, 228 {stateRaftReady | stateRaftRequest | stateRaftTick, "ready=[1:2] request=[1:2] tick=[1:2]"}, 229 } 230 231 for _, c := range testCases { 232 s.signal(s.enqueueN(c.state, 1, 1, 1, 1, 1)) 233 234 testutils.SucceedsSoon(t, func() error { 235 if s := p.String(); c.expected != s { 236 return errors.Errorf("expected %s, but got %s", c.expected, s) 237 } 238 return nil 239 }) 240 } 241 }