github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/hk/housekeeper_test.go (about)

     1  // Package hk provides mechanism for registering cleanup
     2  // functions which are invoked at specified intervals.
     3  /*
     4   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     5   */
     6  package hk_test
     7  
     8  import (
     9  	"math/rand"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/NVIDIA/aistore/cmn/atomic"
    14  	"github.com/NVIDIA/aistore/hk"
    15  	. "github.com/onsi/ginkgo/v2"
    16  	. "github.com/onsi/gomega"
    17  )
    18  
    19  var _ = Describe("Housekeeper", func() {
    20  	It("should register the callback and fire it", func() {
    21  		fired := false
    22  		hk.Reg("foo", func() time.Duration {
    23  			fired = true
    24  			return time.Second
    25  		}, 0)
    26  		defer hk.Unreg("foo")
    27  		time.Sleep(20 * time.Millisecond)
    28  		Expect(fired).To(BeTrue()) // callback should be fired at the start
    29  		fired = false
    30  
    31  		time.Sleep(500 * time.Millisecond)
    32  		Expect(fired).To(BeFalse())
    33  
    34  		time.Sleep(600 * time.Millisecond)
    35  		Expect(fired).To(BeTrue())
    36  	})
    37  
    38  	It("should register the callback and fire it after initial interval", func() {
    39  		fired := false
    40  		hk.Reg("foo", func() time.Duration {
    41  			fired = true
    42  			return time.Second
    43  		}, time.Second)
    44  		defer hk.Unreg("foo")
    45  
    46  		time.Sleep(500 * time.Millisecond)
    47  		Expect(fired).To(BeFalse())
    48  
    49  		time.Sleep(600 * time.Millisecond)
    50  		Expect(fired).To(BeTrue())
    51  	})
    52  
    53  	It("should register multiple callbacks and fire it in correct order", func() {
    54  		fired := make([]bool, 2)
    55  		hk.Reg("foo", func() time.Duration {
    56  			fired[0] = true
    57  			return 2 * time.Second
    58  		}, 0)
    59  		defer hk.Unreg("foo")
    60  		hk.Reg("bar", func() time.Duration {
    61  			fired[1] = true
    62  			return time.Second + 500*time.Millisecond
    63  		}, 0)
    64  		defer hk.Unreg("bar")
    65  
    66  		time.Sleep(20 * time.Millisecond)
    67  		// "foo" and "bar" should fire at the start (no initial interval)
    68  		for idx := range len(fired) {
    69  			Expect(fired[idx]).To(BeTrue())
    70  			fired[idx] = false
    71  		}
    72  
    73  		time.Sleep(600 * time.Millisecond) // ~600ms
    74  
    75  		// "foo" nor "bar" should fire
    76  		Expect(fired[0] || fired[1]).To(BeFalse())
    77  
    78  		time.Sleep(time.Second) // ~1.6s
    79  
    80  		// "bar" should fire
    81  		Expect(fired[0]).To(BeFalse())
    82  		Expect(fired[1]).To(BeTrue())
    83  		fired[1] = false
    84  
    85  		time.Sleep(500 * time.Millisecond) // ~2.1s
    86  
    87  		// "foo" should fire
    88  		Expect(fired[0]).To(BeTrue())
    89  		Expect(fired[1]).To(BeFalse())
    90  
    91  		time.Sleep(time.Second) // ~3.1s
    92  
    93  		// "bar" should fire once again
    94  		Expect(fired[0] && fired[1]).To(BeTrue())
    95  	})
    96  
    97  	It("should unregister callback", func() {
    98  		fired := make([]bool, 2)
    99  		hk.Reg("bar", func() time.Duration {
   100  			fired[0] = true
   101  			return 400 * time.Millisecond
   102  		}, 400*time.Millisecond)
   103  		hk.Reg("foo", func() time.Duration {
   104  			fired[1] = true
   105  			return 200 * time.Millisecond
   106  		}, 200*time.Millisecond)
   107  
   108  		time.Sleep(500 * time.Millisecond)
   109  		Expect(fired[0] && fired[1]).To(BeTrue())
   110  
   111  		fired[0], fired[1] = false, false
   112  		hk.Unreg("foo")
   113  
   114  		time.Sleep(time.Second)
   115  		Expect(fired[1]).To(BeFalse())
   116  		Expect(fired[0]).To(BeTrue())
   117  
   118  		hk.Unreg("bar")
   119  	})
   120  
   121  	It("should unregister callback that returns UnregInterval", func() {
   122  		for range 3 {
   123  			fired := make([]bool, 2)
   124  			hk.Reg("bar", func() time.Duration {
   125  				fired[0] = true
   126  				return 400 * time.Millisecond
   127  			}, 400*time.Millisecond)
   128  			hk.Reg("foo", func() time.Duration {
   129  				fired[1] = true
   130  				return hk.UnregInterval
   131  			}, 100*time.Millisecond)
   132  
   133  			time.Sleep(500 * time.Millisecond)
   134  			Expect(fired[0] && fired[1]).To(BeTrue())
   135  
   136  			fired[0], fired[1] = false, false
   137  
   138  			time.Sleep(500 * time.Millisecond)
   139  			Expect(fired[1]).To(BeFalse()) // foo
   140  			Expect(fired[0]).To(BeTrue())  // bar
   141  
   142  			hk.Unreg("bar")
   143  		}
   144  	})
   145  
   146  	It("should register and unregister multiple callbacks", func() {
   147  		var fired bool
   148  		f := func(name string) {
   149  			Expect(fired).To(BeFalse())
   150  			hk.Reg(name, func() time.Duration {
   151  				fired = true
   152  				return 100 * time.Millisecond
   153  			}, 100*time.Millisecond)
   154  
   155  			time.Sleep(110 * time.Millisecond)
   156  			Expect(fired).To(BeTrue())
   157  
   158  			hk.Unreg(name)
   159  			fired = false
   160  		}
   161  
   162  		f("foo")
   163  		f("bar")
   164  		f("baz")
   165  
   166  		time.Sleep(time.Second)
   167  		Expect(fired).To(BeFalse())
   168  	})
   169  
   170  	It("should correctly call multiple callbacks", func() {
   171  		type action struct {
   172  			d       time.Duration
   173  			origIdx int
   174  		}
   175  		const (
   176  			actionCnt = 30
   177  		)
   178  		var (
   179  			counter atomic.Int32
   180  			durs    = make([]action, 0, actionCnt)
   181  			fired   = make([]int32, actionCnt)
   182  		)
   183  
   184  		for i := range actionCnt {
   185  			durs = append(durs, action{
   186  				d:       50*time.Millisecond + time.Duration(40*i)*time.Millisecond,
   187  				origIdx: i,
   188  			})
   189  			fired[i] = -1
   190  		}
   191  
   192  		rand.Shuffle(actionCnt, func(i, j int) {
   193  			durs[i], durs[j] = durs[j], durs[i]
   194  		})
   195  
   196  		for i := range actionCnt {
   197  			index := i
   198  			hk.Reg(strconv.Itoa(index), func() time.Duration {
   199  				if fired[index] == -1 {
   200  					fired[index] = counter.Inc() - 1
   201  				}
   202  				return durs[index].d
   203  			}, durs[index].d)
   204  		}
   205  
   206  		time.Sleep(100 * actionCnt * time.Millisecond)
   207  
   208  		for i := range actionCnt {
   209  			Expect(durs[i].origIdx).To(BeEquivalentTo(fired[i]))
   210  		}
   211  	})
   212  })