Skip to content

Commit 2fe8e47

Browse files
Merge pull request #440 from awgreene/429-to-release-4.12
[release-4.12] OCPBUGS-6260: Catalog, fatal error: concurrent map read and map write
2 parents ff8edef + 52d14ce commit 2fe8e47

File tree

3 files changed

+106
-18
lines changed

3 files changed

+106
-18
lines changed

staging/operator-lifecycle-manager/pkg/metrics/metrics.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package metrics
22

33
import (
4+
"sync"
45
"time"
56

67
"github.com/prometheus/client_golang/prometheus"
@@ -199,12 +200,37 @@ var (
199200
},
200201
)
201202

202-
// subscriptionSyncCounters keeps a record of the Prometheus counters emitted by
203-
// Subscription objects. The key of a record is the Subscription name, while the value
204-
// is struct containing label values used in the counter
205-
subscriptionSyncCounters = make(map[string]subscriptionSyncLabelValues)
203+
subscriptionSyncCounters = newSubscriptionSyncCounter()
206204
)
207205

206+
// subscriptionSyncCounter keeps a record of the Prometheus counters emitted by
207+
// Subscription objects. The key of a record is the Subscription name, while the value
208+
// is struct containing label values used in the counter. Read and Write access are
209+
// protected by mutex.
210+
type subscriptionSyncCounter struct {
211+
counters map[string]subscriptionSyncLabelValues
212+
countersLock sync.RWMutex
213+
}
214+
215+
func newSubscriptionSyncCounter() subscriptionSyncCounter {
216+
return subscriptionSyncCounter{
217+
counters: make(map[string]subscriptionSyncLabelValues),
218+
}
219+
}
220+
221+
func (s *subscriptionSyncCounter) setValues(key string, val subscriptionSyncLabelValues) {
222+
s.countersLock.Lock()
223+
defer s.countersLock.Unlock()
224+
s.counters[key] = val
225+
}
226+
227+
func (s *subscriptionSyncCounter) readValues(key string) (subscriptionSyncLabelValues, bool) {
228+
s.countersLock.RLock()
229+
defer s.countersLock.RUnlock()
230+
val, ok := s.counters[key]
231+
return val, ok
232+
}
233+
208234
type subscriptionSyncLabelValues struct {
209235
installedCSV string
210236
pkg string
@@ -280,14 +306,15 @@ func EmitSubMetric(sub *operatorsv1alpha1.Subscription) {
280306
if sub.Spec == nil {
281307
return
282308
}
309+
283310
SubscriptionSyncCount.WithLabelValues(sub.GetName(), sub.Status.InstalledCSV, sub.Spec.Channel, sub.Spec.Package, string(sub.Spec.InstallPlanApproval)).Inc()
284-
if _, present := subscriptionSyncCounters[sub.GetName()]; !present {
285-
subscriptionSyncCounters[sub.GetName()] = subscriptionSyncLabelValues{
311+
if _, present := subscriptionSyncCounters.readValues(sub.GetName()); !present {
312+
subscriptionSyncCounters.setValues(sub.GetName(), subscriptionSyncLabelValues{
286313
installedCSV: sub.Status.InstalledCSV,
287314
pkg: sub.Spec.Package,
288315
channel: sub.Spec.Channel,
289316
approvalStrategy: string(sub.Spec.InstallPlanApproval),
290-
}
317+
})
291318
}
292319
}
293320

@@ -302,7 +329,7 @@ func UpdateSubsSyncCounterStorage(sub *operatorsv1alpha1.Subscription) {
302329
if sub.Spec == nil {
303330
return
304331
}
305-
counterValues := subscriptionSyncCounters[sub.GetName()]
332+
counterValues, _ := subscriptionSyncCounters.readValues(sub.GetName())
306333
approvalStrategy := string(sub.Spec.InstallPlanApproval)
307334

308335
if sub.Spec.Channel != counterValues.channel ||
@@ -317,7 +344,7 @@ func UpdateSubsSyncCounterStorage(sub *operatorsv1alpha1.Subscription) {
317344
counterValues.channel = sub.Spec.Channel
318345
counterValues.approvalStrategy = approvalStrategy
319346

320-
subscriptionSyncCounters[sub.GetName()] = counterValues
347+
subscriptionSyncCounters.setValues(sub.GetName(), counterValues)
321348
}
322349
}
323350

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package metrics_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
9+
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
10+
"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
11+
)
12+
13+
func TestUpdateSubsSyncCounterStorageThreadSafety(t *testing.T) {
14+
for i := 0; i < 1000; i++ {
15+
go func(ii int) {
16+
sub := &operatorsv1alpha1.Subscription{
17+
ObjectMeta: metav1.ObjectMeta{
18+
Namespace: "foo",
19+
Name: "foo",
20+
},
21+
Spec: &operatorsv1alpha1.SubscriptionSpec{
22+
Channel: "foo",
23+
Package: "foo",
24+
InstallPlanApproval: "automatic",
25+
},
26+
Status: operatorsv1alpha1.SubscriptionStatus{
27+
InstalledCSV: "foo",
28+
},
29+
}
30+
sub.Spec.Channel = fmt.Sprintf("bar-%v", ii)
31+
metrics.UpdateSubsSyncCounterStorage(sub)
32+
}(i)
33+
}
34+
}

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/metrics/metrics.go

Lines changed: 36 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)