1
0
mirror of https://github.com/rancher/steve.git synced 2025-06-23 21:47:30 +00:00
steve/pkg/debounce/refresher.go
Michael Bolot 0e9fde750f Attempting to fix flaky tests
Some tests which relied on timeouts were a bit flaky in CI. This PR
refactors a few of them to work on a more reliable method of receiving
from a channel and raises the timeout of another test.
2024-04-03 15:32:23 -05:00

56 lines
1.6 KiB
Go

package debounce
import (
"context"
"sync"
"time"
"github.com/sirupsen/logrus"
)
// Refreshable represents an object which can be refreshed. This should be protected by a mutex for concurrent operation.
type Refreshable interface {
Refresh() error
}
// DebounceableRefresher is used to debounce multiple attempts to refresh a refreshable type.
type DebounceableRefresher struct {
sync.Mutex
// Refreshable is any type that can be refreshed. The refresh method should by protected by a mutex internally.
Refreshable Refreshable
current context.CancelFunc
onCancel func()
}
// RefreshAfter requests a refresh after a certain time has passed. Subsequent calls to this method will
// delay the requested refresh by the new duration. Note that this is a total override of the previous calls - calling
// RefreshAfter(time.Second * 2) and then immediately calling RefreshAfter(time.Microsecond * 1) will run a refresh
// in one microsecond
func (d *DebounceableRefresher) RefreshAfter(duration time.Duration) {
d.Lock()
defer d.Unlock()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
if d.current != nil {
d.current()
}
d.current = cancel
go func() {
timer := time.NewTimer(duration)
defer timer.Stop()
select {
case <-ctx.Done():
// this indicates that the context was cancelled.
if d.onCancel != nil {
d.onCancel()
}
case <-timer.C:
// note this can cause multiple refreshes to happen concurrently
err := d.Refreshable.Refresh()
if err != nil {
logrus.Errorf("failed to refresh with error: %v", err)
}
}
}()
}