github.com/containerd/Containerd@v1.4.13/gc/scheduler/scheduler_test.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package scheduler 18 19 import ( 20 "context" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/containerd/containerd/gc" 26 "gotest.tools/v3/assert" 27 ) 28 29 func TestPauseThreshold(t *testing.T) { 30 cfg := &config{ 31 // With 100μs, gc should run about every 5ms 32 PauseThreshold: 0.02, 33 } 34 tc := &testCollector{ 35 d: time.Microsecond * 100, 36 } 37 38 scheduler := newScheduler(tc, cfg) 39 40 ctx, cancel := context.WithCancel(context.Background()) 41 defer cancel() 42 43 go scheduler.run(ctx) 44 45 // Ensure every possible GC cycle runs 46 go func() { 47 tick := time.NewTicker(time.Microsecond * 100) 48 for { 49 select { 50 case <-tick.C: 51 tc.trigger(true) 52 case <-ctx.Done(): 53 return 54 } 55 } 56 }() 57 58 time.Sleep(time.Millisecond * 15) 59 60 if c := tc.runCount(); c > 4 { 61 t.Fatalf("unexpected gc run count %d, expected less than 5", c) 62 } 63 } 64 65 func TestDeletionThreshold(t *testing.T) { 66 cfg := &config{ 67 // Prevent GC from scheduling again before check 68 PauseThreshold: 0.001, 69 DeletionThreshold: 5, 70 } 71 tc := &testCollector{ 72 d: time.Second, 73 } 74 75 scheduler := newScheduler(tc, cfg) 76 77 ctx, cancel := context.WithCancel(context.Background()) 78 defer cancel() 79 80 go scheduler.run(ctx) 81 82 // Block until next GC finishes 83 gcWait := make(chan struct{}) 84 go func() { 85 scheduler.wait(ctx, false) 86 close(gcWait) 87 }() 88 89 // Increment deletion count 5, checking GC hasn't run in 90 // between each call 91 for i := 0; i < 5; i++ { 92 time.Sleep(time.Millisecond) 93 if c := tc.runCount(); c != 0 { 94 t.Fatalf("GC ran unexpectedly") 95 } 96 tc.trigger(true) 97 } 98 99 select { 100 case <-gcWait: 101 case <-time.After(time.Millisecond * 30): 102 t.Fatal("GC wait timed out") 103 } 104 105 if c := tc.runCount(); c != 1 { 106 t.Fatalf("unexpected gc run count %d, expected 1", c) 107 } 108 } 109 110 func TestTrigger(t *testing.T) { 111 var ( 112 cfg = &config{} 113 tc = &testCollector{ 114 d: time.Millisecond * 10, 115 } 116 ctx, cancel = context.WithCancel(context.Background()) 117 scheduler = newScheduler(tc, cfg) 118 stats gc.Stats 119 err error 120 ) 121 122 defer cancel() 123 go scheduler.run(ctx) 124 125 // Block until next GC finishes 126 gcWait := make(chan struct{}) 127 go func() { 128 stats, err = scheduler.ScheduleAndWait(ctx) 129 close(gcWait) 130 }() 131 132 select { 133 case <-gcWait: 134 case <-time.After(time.Millisecond * 10): 135 t.Fatal("GC wait timed out") 136 } 137 138 if err != nil { 139 t.Fatalf("GC failed: %#v", err) 140 } 141 142 assert.Equal(t, tc.d, stats.Elapsed()) 143 144 if c := tc.runCount(); c != 1 { 145 t.Fatalf("unexpected gc run count %d, expected 1", c) 146 } 147 } 148 149 func TestStartupDelay(t *testing.T) { 150 var ( 151 cfg = &config{ 152 // Prevent GC from scheduling again before check 153 PauseThreshold: 0.001, 154 StartupDelay: duration(time.Millisecond), 155 } 156 tc = &testCollector{ 157 d: time.Second, 158 } 159 ctx, cancel = context.WithCancel(context.Background()) 160 scheduler = newScheduler(tc, cfg) 161 ) 162 defer cancel() 163 go scheduler.run(ctx) 164 165 time.Sleep(time.Millisecond * 30) 166 167 if c := tc.runCount(); c != 1 { 168 t.Fatalf("unexpected gc run count %d, expected 1", c) 169 } 170 } 171 172 type testCollector struct { 173 d time.Duration 174 gc int 175 m sync.Mutex 176 177 callbacks []func(bool) 178 } 179 180 func (tc *testCollector) trigger(delete bool) { 181 for _, f := range tc.callbacks { 182 f(delete) 183 } 184 } 185 186 func (tc *testCollector) runCount() int { 187 tc.m.Lock() 188 c := tc.gc 189 tc.m.Unlock() 190 return c 191 } 192 193 func (tc *testCollector) RegisterMutationCallback(f func(bool)) { 194 tc.callbacks = append(tc.callbacks, f) 195 } 196 197 func (tc *testCollector) GarbageCollect(context.Context) (gc.Stats, error) { 198 tc.m.Lock() 199 tc.gc++ 200 tc.m.Unlock() 201 return gcStats{elapsed: tc.d}, nil 202 } 203 204 type gcStats struct { 205 elapsed time.Duration 206 } 207 208 // Elapsed returns the duration which elapsed during a collection 209 func (s gcStats) Elapsed() time.Duration { 210 return s.elapsed 211 }