From f3e5c35c081355f34c7d53947ebfb55300fe8162 Mon Sep 17 00:00:00 2001 From: Maskym Vavilov Date: Mon, 8 Apr 2024 12:22:41 +0100 Subject: [PATCH] GH-35 randomize validation reconcilation --- internal/common/helper.go | 21 +++++++++++ internal/common/helper_test.go | 41 +++++++++++++++++++++ internal/controller/dnsrecord_controller.go | 12 ++++-- 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 internal/common/helper.go create mode 100644 internal/common/helper_test.go diff --git a/internal/common/helper.go b/internal/common/helper.go new file mode 100644 index 0000000..1214349 --- /dev/null +++ b/internal/common/helper.go @@ -0,0 +1,21 @@ +package common + +import ( + "time" + + "k8s.io/apimachinery/pkg/util/rand" +) + +// RandomizeDuration randomizes duration for a given variance. +// variance is expected to be of a format 0.1 for 10%, 0.5 for 50% and so on +func RandomizeDuration(variance float64, duration time.Duration) time.Duration { + // we won't go smaller than a second - using milliseconds to have a relatively big number to randomize + millisecond := float64(duration.Milliseconds()) + + upperLimit := millisecond * (1.0 + variance) + lowerLimit := millisecond * (1.0 - variance) + + return time.Millisecond * time.Duration(rand.Int63nRange( + int64(lowerLimit), + int64(upperLimit))) +} diff --git a/internal/common/helper_test.go b/internal/common/helper_test.go new file mode 100644 index 0000000..f3bb466 --- /dev/null +++ b/internal/common/helper_test.go @@ -0,0 +1,41 @@ +package common + +import ( + "testing" + "time" +) + +func TestRandomizeDuration(t *testing.T) { + testIterations := 100 + + tests := []struct { + name string + variance float64 + duration time.Duration + }{ + { + name: "returns valid duration in range", + variance: 0.5, + duration: time.Second * 5, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + i := 0 + for i < testIterations { + if got := RandomizeDuration(tt.variance, tt.duration); !isValidVariance(tt.duration, got, tt.variance) { + t.Errorf("RandomizeDuration() invalid randomization; got = %v", got.String()) + } + i++ + } + }) + } +} + +func isValidVariance(duration, randomizedDuration time.Duration, variance float64) bool { + upperLimit := float64(duration.Milliseconds()) + float64(duration.Milliseconds())*variance + lowerLimmit := float64(duration.Milliseconds()) - float64(duration.Milliseconds())*variance + + return float64(randomizedDuration.Milliseconds()) > lowerLimmit && + float64(randomizedDuration.Milliseconds()) < upperLimit +} diff --git a/internal/controller/dnsrecord_controller.go b/internal/controller/dnsrecord_controller.go index 48b6098..f614821 100644 --- a/internal/controller/dnsrecord_controller.go +++ b/internal/controller/dnsrecord_controller.go @@ -37,17 +37,19 @@ import ( externaldnsprovider "sigs.k8s.io/external-dns/provider" "github.com/kuadrant/dns-operator/api/v1alpha1" + "github.com/kuadrant/dns-operator/internal/common" externaldnsplan "github.com/kuadrant/dns-operator/internal/external-dns/plan" "github.com/kuadrant/dns-operator/internal/provider" ) const ( - DNSRecordFinalizer = "kuadrant.io/dns-record" + DNSRecordFinalizer = "kuadrant.io/dns-record" + validationRequeueVariance = 0.5 ) var ( defaultRequeueTime time.Duration - validationRequeueTime = time.Second * 5 + validationRequeueTime = time.Millisecond * 5000 noRequeueDuration = time.Duration(0) validFor time.Duration reconcileStart = metav1.Time{} @@ -69,6 +71,10 @@ func (r *DNSRecordReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( logger := log.FromContext(ctx) reconcileStart = metav1.Now() + + // randomize validation reconcile delay + validationRequeueTime = common.RandomizeDuration(validationRequeueVariance, validationRequeueTime) + previous := &v1alpha1.DNSRecord{} err := r.Client.Get(ctx, client.ObjectKey{Namespace: req.Namespace, Name: req.Name}, previous) if err != nil { @@ -138,7 +144,7 @@ func (r *DNSRecordReconciler) updateStatus(ctx context.Context, previous, curren if apierrors.IsConflict(updateError) { return ctrl.Result{Requeue: true}, nil } - return ctrl.Result{}, updateError + return ctrl.Result{RequeueAfter: requeueAfter}, updateError } return ctrl.Result{RequeueAfter: requeueAfter}, nil }