github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/resource_check_rate_limiter_test.go (about) 1 package db_test 2 3 import ( 4 "context" 5 "time" 6 7 "code.cloudfoundry.org/clock/fakeclock" 8 "github.com/pf-qiu/concourse/v6/atc" 9 "github.com/pf-qiu/concourse/v6/atc/db" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 "golang.org/x/time/rate" 13 ) 14 15 var _ = Describe("ResourceCheckRateLimiter", func() { 16 var ( 17 checkInterval time.Duration 18 checksPerSecond int 19 refreshInterval time.Duration 20 fakeClock *fakeclock.FakeClock 21 22 checkableCount int 23 24 ctx context.Context 25 26 limiter *db.ResourceCheckRateLimiter 27 ) 28 29 BeforeEach(func() { 30 checkInterval = time.Minute 31 checksPerSecond = 0 32 refreshInterval = 5 * time.Minute 33 fakeClock = fakeclock.NewFakeClock(time.Now()) 34 35 checkableCount = 0 36 37 ctx = context.Background() 38 }) 39 40 JustBeforeEach(func() { 41 limiter = db.NewResourceCheckRateLimiter( 42 rate.Limit(checksPerSecond), 43 checkInterval, 44 dbConn, 45 refreshInterval, 46 fakeClock, 47 ) 48 }) 49 50 wait := func(limiter *db.ResourceCheckRateLimiter) <-chan error { 51 errs := make(chan error) 52 go func() { 53 errs <- limiter.Wait(ctx) 54 }() 55 return errs 56 } 57 58 createCheckable := func() { 59 config, err := resourceConfigFactory.FindOrCreateResourceConfig( 60 defaultWorkerResourceType.Type, 61 atc.Source{"some": "source", "count": checkableCount}, 62 atc.VersionedResourceTypes{}, 63 ) 64 Expect(err).ToNot(HaveOccurred()) 65 66 _, err = config.FindOrCreateScope(nil) 67 Expect(err).ToNot(HaveOccurred()) 68 69 checkableCount++ 70 } 71 72 Context("with no static limit provided", func() { 73 BeforeEach(func() { 74 checksPerSecond = 0 75 }) 76 77 It("rate limits while adjusting to the amount of checkables", func() { 78 By("immediately returning with 0 checkables") 79 Expect(<-wait(limiter)).To(Succeed()) 80 Expect(limiter.Limit()).To(Equal(rate.Inf)) 81 82 By("creating one checkable") 83 createCheckable() 84 85 By("continuing to return immediately, as the refresh interval has not elapsed") 86 Expect(<-wait(limiter)).To(Succeed()) 87 Expect(limiter.Limit()).To(Equal(rate.Inf)) 88 89 By("waiting for the refresh interval") 90 fakeClock.Increment(refreshInterval) 91 92 By("adjusting the limit but returning immediately for the first time") 93 Expect(<-wait(limiter)).To(Succeed()) 94 Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds()))) 95 96 done := wait(limiter) 97 select { 98 case <-done: 99 Fail("should not have returned yet") 100 case <-time.After(100 * time.Millisecond): 101 } 102 103 By("unblocking after the rate limit elapses") 104 fakeClock.Increment(checkInterval / time.Duration(checkableCount)) 105 Expect(<-done).To(Succeed()) 106 107 By("creating more checkables") 108 for i := 0; i < 10; i++ { 109 createCheckable() 110 } 111 112 By("waiting for the refresh interval") 113 fakeClock.Increment(refreshInterval) 114 115 By("adjusting the limit but returning immediately for the first time") 116 Expect(<-wait(limiter)).To(Succeed()) 117 Expect(limiter.Limit()).To(Equal(rate.Limit(float64(checkableCount) / checkInterval.Seconds()))) 118 119 done = wait(limiter) 120 select { 121 case <-done: 122 Fail("should not have returned yet") 123 case <-time.After(100 * time.Millisecond): 124 } 125 126 By("unblocking after the the new rate limit elapses") 127 fakeClock.Increment(checkInterval / time.Duration(checkableCount)) 128 Expect(<-done).To(Succeed()) 129 }) 130 }) 131 132 Context("when a static checks per second value is provided", func() { 133 BeforeEach(func() { 134 checksPerSecond = 42 135 }) 136 137 It("respects the value rather than determining it dynamically", func() { 138 Expect(limiter.Limit()).To(Equal(rate.Limit(checksPerSecond))) 139 }) 140 }) 141 142 Context("when a negative static checks per second value is provided", func() { 143 BeforeEach(func() { 144 checksPerSecond = -1 145 }) 146 147 It("results in an infinite rate limit that ignores checkable count", func() { 148 Expect(<-wait(limiter)).To(Succeed()) 149 Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf))) 150 151 By("creating a few (ignored) checkables") 152 for i := 0; i < 10; i++ { 153 createCheckable() 154 } 155 156 By("waiting for the (ignored) refresh interval") 157 fakeClock.Increment(refreshInterval) 158 159 By("still returning immediately and retaining the infinite rate") 160 Expect(<-wait(limiter)).To(Succeed()) 161 Expect(limiter.Limit()).To(Equal(rate.Limit(rate.Inf))) 162 }) 163 }) 164 })