Skip to content

Commit f087558

Browse files
Merge pull request #1982 from benluddy/continuous-registry-resync
Bug 1920526: Fix zero-delay resyncs for certain registry update policies.
2 parents 93c7978 + e5a160a commit f087558

File tree

3 files changed

+43
-19
lines changed

3 files changed

+43
-19
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ func (o *Operator) syncRegistryServer(logger *logrus.Entry, in *v1alpha1.Catalog
640640
// requeue the catalog sync based on the polling interval, for accurate syncs of catalogs with polling enabled
641641
if out.Spec.UpdateStrategy != nil {
642642
logger.Debugf("requeuing registry server sync based on polling interval %s", out.Spec.UpdateStrategy.Interval.Duration.String())
643-
resyncPeriod := reconciler.SyncRegistryUpdateInterval(out)
643+
resyncPeriod := reconciler.SyncRegistryUpdateInterval(out, time.Now())
644644
o.catsrcQueueSet.RequeueAfter(out.GetNamespace(), out.GetName(), queueinformer.ResyncWithJitter(resyncPeriod, 0.1)())
645645
return
646646
}

pkg/controller/registry/reconciler/grpc_polling.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
// SyncRegistryUpdateInterval returns a duration to use when requeuing the catalog source for reconciliation.
1313
// This ensures that the catalog is being synced on the correct time interval based on its spec.
1414
// Note: this function assumes the catalog has an update strategy set.
15-
func SyncRegistryUpdateInterval(source *v1alpha1.CatalogSource) time.Duration {
15+
func SyncRegistryUpdateInterval(source *v1alpha1.CatalogSource, now time.Time) time.Duration {
1616
pollingInterval := source.Spec.UpdateStrategy.Interval.Duration
1717
latestPoll := source.Status.LatestImageRegistryPoll
1818
creationTimestamp := source.CreationTimestamp.Time
@@ -22,19 +22,21 @@ func SyncRegistryUpdateInterval(source *v1alpha1.CatalogSource) time.Duration {
2222
return pollingInterval
2323
}
2424
// Resync based on the delta between the default sync and the actual last poll if the interval is greater than the default
25-
return defaultOr(latestPoll, pollingInterval, creationTimestamp)
25+
return defaultOr(latestPoll, pollingInterval, creationTimestamp, now)
2626
}
2727

28-
// defaultOr returns either the default resync period or the modulus of the polling interval and the default.
28+
// defaultOr returns either the default resync period or the time remaining until the next poll is due, whichever is smaller.
2929
// For example, if the polling interval is 40 minutes, OLM will sync after ~30 minutes and see that the next sync
30-
// for this catalog should be sooner than the default (15 minutes) and return 10 minutes (40 % 15).
31-
func defaultOr(latestPoll *metav1.Time, pollingInterval time.Duration, creationTimestamp time.Time) time.Duration {
30+
// for this catalog should be in 10 minutes, sooner than the default 15 minutes, and return 10 minutes.
31+
func defaultOr(latestPoll *metav1.Time, pollingInterval time.Duration, creationTimestamp time.Time, now time.Time) time.Duration {
3232
if latestPoll.IsZero() {
3333
latestPoll = &metav1.Time{Time: creationTimestamp}
3434
}
35+
36+
remaining := latestPoll.Add(pollingInterval).Sub(now)
3537
// sync ahead of the default interval in the case where now + default sync is after the last sync plus the interval
36-
if time.Now().Add(queueinformer.DefaultResyncPeriod).After(latestPoll.Add(pollingInterval)) {
37-
return (pollingInterval % queueinformer.DefaultResyncPeriod).Round(time.Minute)
38+
if remaining < queueinformer.DefaultResyncPeriod {
39+
return remaining
3840
}
3941
// return the default sync period otherwise: the next sync cycle will check again
4042
return queueinformer.DefaultResyncPeriod

pkg/controller/registry/reconciler/grpc_polling_test.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
)
1212

1313
func TestSyncRegistryUpdateInterval(t *testing.T) {
14+
now := time.Date(2021, time.January, 29, 14, 47, 0, 0, time.UTC)
1415
tests := []struct {
1516
name string
1617
source *v1alpha1.CatalogSource
@@ -46,12 +47,32 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
4647
},
4748
expected: 15 * time.Minute,
4849
},
50+
{
51+
name: "PollingIntervalMultipleOfDefaultResyncPeriod",
52+
source: &v1alpha1.CatalogSource{
53+
Spec: v1alpha1.CatalogSourceSpec{
54+
UpdateStrategy: &v1alpha1.UpdateStrategy{
55+
RegistryPoll: &v1alpha1.RegistryPoll{
56+
Interval: &metav1.Duration{
57+
Duration: 2 * queueinformer.DefaultResyncPeriod,
58+
},
59+
},
60+
},
61+
},
62+
Status: v1alpha1.CatalogSourceStatus{
63+
LatestImageRegistryPoll: &metav1.Time{
64+
Time: now.Add(1*time.Second - 2*queueinformer.DefaultResyncPeriod),
65+
},
66+
},
67+
},
68+
expected: 1 * time.Second,
69+
},
4970
{
5071
name: "PollingInterval10Minutes/AlreadyUpdated",
5172
source: &v1alpha1.CatalogSource{
5273
Status: v1alpha1.CatalogSourceStatus{
5374
LatestImageRegistryPoll: &metav1.Time{
54-
time.Now().Add(-(5 * time.Minute)),
75+
Time: now.Add(-(5 * time.Minute)),
5576
},
5677
},
5778
Spec: v1alpha1.CatalogSourceSpec{
@@ -71,7 +92,7 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
7192
source: &v1alpha1.CatalogSource{
7293
ObjectMeta: metav1.ObjectMeta{
7394
CreationTimestamp: metav1.Time{
74-
Time: time.Now().Add(-(35 * time.Minute)),
95+
Time: now.Add(-(35 * time.Minute)),
7596
},
7697
},
7798
Spec: v1alpha1.CatalogSourceSpec{
@@ -84,14 +105,14 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
84105
},
85106
},
86107
},
87-
expected: 10 * time.Minute,
108+
expected: 5 * time.Minute,
88109
},
89110
{
90111
name: "PollingInterval40Minutes/AlreadyUpdated30MinutesAgo",
91112
source: &v1alpha1.CatalogSource{
92113
Status: v1alpha1.CatalogSourceStatus{
93114
LatestImageRegistryPoll: &metav1.Time{
94-
time.Now().Add(-(30 * time.Minute)),
115+
Time: now.Add(-(30 * time.Minute)),
95116
},
96117
},
97118
Spec: v1alpha1.CatalogSourceSpec{
@@ -111,7 +132,7 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
111132
source: &v1alpha1.CatalogSource{
112133
ObjectMeta: metav1.ObjectMeta{
113134
CreationTimestamp: metav1.Time{
114-
Time: time.Now().Add(-(15 * time.Minute)),
135+
Time: now.Add(-(15 * time.Minute)),
115136
},
116137
},
117138
Spec: v1alpha1.CatalogSourceSpec{
@@ -131,7 +152,7 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
131152
source: &v1alpha1.CatalogSource{
132153
Status: v1alpha1.CatalogSourceStatus{
133154
LatestImageRegistryPoll: &metav1.Time{
134-
time.Now().Add(-(15 * time.Minute)),
155+
Time: now.Add(-(15 * time.Minute)),
135156
},
136157
},
137158
Spec: v1alpha1.CatalogSourceSpec{
@@ -149,10 +170,11 @@ func TestSyncRegistryUpdateInterval(t *testing.T) {
149170
}
150171

151172
for _, tt := range tests {
152-
t.Logf("name %s", tt.name)
153-
d := SyncRegistryUpdateInterval(tt.source)
154-
if d != tt.expected {
155-
t.Fatalf("unexpected registry sync interval for %s: expected %#v got %#v", tt.name, tt.expected, d)
156-
}
173+
t.Run(tt.name, func(t *testing.T) {
174+
d := SyncRegistryUpdateInterval(tt.source, now)
175+
if d != tt.expected {
176+
t.Errorf("unexpected registry sync interval for %s: expected %s got %s", tt.name, tt.expected, d)
177+
}
178+
})
157179
}
158180
}

0 commit comments

Comments
 (0)