-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OCPVE-627: fix: concurrent apply / status checks -> LVMCluster #391
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package internal | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
const DefaultMultiErrorSeparator = ";" | ||
|
||
// NewMultiError creates a MultiError that uses the default separator for each error. | ||
func NewMultiError(errs []error) error { | ||
return &MultiError{Errors: errs, Separator: DefaultMultiErrorSeparator} | ||
} | ||
|
||
// MultiError is an error that aggregates multiple errors together and uses | ||
// a separator to aggregate them when called with Error. | ||
type MultiError struct { | ||
Errors []error | ||
Separator string | ||
} | ||
|
||
func (m *MultiError) Error() string { | ||
errs := make([]string, len(m.Errors)) | ||
for i, err := range m.Errors { | ||
errs[i] = err.Error() | ||
} | ||
return strings.Join(errs, m.Separator) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,8 +25,8 @@ import ( | |
"github.com/go-logr/logr" | ||
configv1 "github.com/openshift/api/config/v1" | ||
secv1client "github.com/openshift/client-go/security/clientset/versioned/typed/security/v1" | ||
|
||
lvmv1alpha1 "github.com/openshift/lvm-operator/api/v1alpha1" | ||
"github.com/openshift/lvm-operator/controllers/internal" | ||
|
||
topolvmv1 "github.com/topolvm/topolvm/api/v1" | ||
|
||
|
@@ -165,7 +165,7 @@ func (r *LVMClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) | |
// errors returned by this will be updated in the reconcileSucceeded condition of the LVMCluster | ||
func (r *LVMClusterReconciler) reconcile(ctx context.Context, instance *lvmv1alpha1.LVMCluster) (ctrl.Result, error) { | ||
|
||
//The resource was deleted | ||
// The resource was deleted | ||
if !instance.DeletionTimestamp.IsZero() { | ||
// Check for existing LogicalVolumes | ||
lvsExist, err := r.logicalVolumesExist(ctx, instance) | ||
|
@@ -197,7 +197,7 @@ func (r *LVMClusterReconciler) reconcile(ctx context.Context, instance *lvmv1alp | |
r.Log.Info("successfully added finalizer") | ||
} | ||
|
||
resourceCreationList := []resourceManager{ | ||
resources := []resourceManager{ | ||
&csiDriver{}, | ||
&topolvmController{r.TopoLVMLeaderElectionPassthrough}, | ||
&openshiftSccs{}, | ||
|
@@ -208,16 +208,30 @@ func (r *LVMClusterReconciler) reconcile(ctx context.Context, instance *lvmv1alp | |
&topolvmVolumeSnapshotClass{}, | ||
} | ||
|
||
// handle create/update | ||
for _, unit := range resourceCreationList { | ||
err := unit.ensureCreated(r, ctx, instance) | ||
if err != nil { | ||
r.Log.Error(err, "failed to reconcile", "resource", unit.getName()) | ||
return ctrl.Result{}, err | ||
resourceSyncStart := time.Now() | ||
results := make(chan error, len(resources)) | ||
create := func(i int) { | ||
results <- resources[i].ensureCreated(r, ctx, instance) | ||
} | ||
|
||
for i := range resources { | ||
go create(i) | ||
} | ||
|
||
var errs []error | ||
for i := 0; i < len(resources); i++ { | ||
if err := <-results; err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if something goes wrong and we will stuck there forever? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you look at the code path there should not be a way to get stuck here. This will always return exactly the amount of times that a resource is attempted to be created. This means that there will always be the correct amount of results, no matter the amount of failures in the goroutines. |
||
errs = append(errs, err) | ||
} | ||
} | ||
|
||
r.Log.Info("successfully reconciled LVMCluster") | ||
resourceSyncElapsedTime := time.Since(resourceSyncStart) | ||
if len(errs) > 0 { | ||
return ctrl.Result{}, fmt.Errorf("failed to reconcile resources managed by LVMCluster within %v: %w", | ||
resourceSyncElapsedTime, internal.NewMultiError(errs)) | ||
} | ||
|
||
r.Log.Info("successfully reconciled LVMCluster", "resourceSyncElapsedTime", resourceSyncElapsedTime) | ||
|
||
return ctrl.Result{}, nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about using https://pkg.go.dev/golang.org/x/sync/errgroup ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errgroup cancel the context on the first error. We dont want the creation of other resources to abort in case of an error on one of the resources.