@@ -116,6 +116,7 @@ type Operator struct {
116
116
installPlanTimeout time.Duration
117
117
bundleUnpackTimeout time.Duration
118
118
clientFactory clients.Factory
119
+ muInstallPlan sync.Mutex
119
120
}
120
121
121
122
type CatalogSourceSyncFunc func (logger * logrus.Entry , in * v1alpha1.CatalogSource ) (out * v1alpha1.CatalogSource , continueSync bool , syncError error )
@@ -1167,6 +1168,26 @@ func (o *Operator) ensureInstallPlan(logger *logrus.Entry, namespace string, gen
1167
1168
return nil , err
1168
1169
}
1169
1170
1171
+ // There are multiple(2) worker threads process the namespaceQueue.
1172
+ // Both worker can work at the same time when 2 separate updates are made for the namespace.
1173
+ // The following sequence causes 2 installplans are created for a subscription
1174
+ // 1. worker 1 doesn't find the installplan
1175
+ // 2. worker 2 doesn't find the installplan
1176
+ // 3. both worker 1 and 2 create the installplan
1177
+ //
1178
+ // This lock prevents the step 2 in the sequence so that only one installplan is created for a subscription.
1179
+ // The sequence is like the following with this lock
1180
+ // 1. worker 1 locks
1181
+ // 2. worker 1 doesn't find the installplan
1182
+ // 3. worker 2 wait for unlock <--- difference
1183
+ // 4. worker 1 creates the installplan
1184
+ // 5. worker 1 unlocks
1185
+ // 6. worker 2 locks
1186
+ // 7. worker 2 finds the installplan <--- difference
1187
+ // 8. worker 2 unlocks
1188
+ o .muInstallPlan .Lock ()
1189
+ defer o .muInstallPlan .Unlock ()
1190
+
1170
1191
for _ , installPlan := range installPlans {
1171
1192
if installPlan .Spec .Generation == gen {
1172
1193
return reference .GetReference (installPlan )
0 commit comments