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  })