Skip to content

Commit 0b991b4

Browse files
Joibeleduardodbr
andauthored
fix: workflow controller to detect stale workflows (cherry-pick #15090 for 3.6) (#15263)
Signed-off-by: Eduardo Rodrigues <eduardodbr@hotmail.com> Signed-off-by: Alan Clucas <alan@clucas.org> Co-authored-by: Eduardo Rodrigues <eduardodbr@hotmail.com>
1 parent ec96c1b commit 0b991b4

3 files changed

Lines changed: 68 additions & 0 deletions

File tree

workflow/common/common.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const (
5252
// AnnotationKeyArtifactGCStrategy is listed as an annotation on the Artifact GC Pod to identify
5353
// the strategy whose artifacts are being deleted
5454
AnnotationKeyArtifactGCStrategy = workflow.WorkflowFullName + "/artifact-gc-strategy"
55+
56+
// AnnotationKeyLastSeenVersion stores the last seen version of the workflow when it was last successfully processed by the controller
57+
AnnotationKeyLastSeenVersion = workflow.WorkflowFullName + "/last-seen-version"
58+
5559
// AnnotationKeyPodGCStrategy is listed as an annotation on the Pod
5660
// the strategy for the pod, in case the pod is orphaned from its workflow
5761
AnnotationKeyPodGCStrategy = workflow.WorkflowFullName + "/pod-gc-strategy"

workflow/controller/controller.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ type recentCompletions struct {
7979
mutex gosync.RWMutex
8080
}
8181

82+
type lastSeenVersions struct {
83+
versions map[string]string
84+
mutex gosync.RWMutex
85+
}
86+
8287
// WorkflowController is the controller for workflow resources
8388
type WorkflowController struct {
8489
// namespace of the workflow controller
@@ -148,6 +153,8 @@ type WorkflowController struct {
148153
executorPlugins map[string]map[string]*spec.Plugin // namespace -> name -> plugin
149154

150155
recentCompletions recentCompletions
156+
157+
lastSeenVersions lastSeenVersions // key: workflow UID, value: resource version
151158
}
152159

153160
const (
@@ -201,6 +208,10 @@ func NewWorkflowController(ctx context.Context, restConfig *rest.Config, kubecli
201208
eventRecorderManager: events.NewEventRecorderManager(kubeclientset),
202209
progressPatchTickDuration: env.LookupEnvDurationOr(common.EnvVarProgressPatchTickDuration, 1*time.Minute),
203210
progressFileTickDuration: env.LookupEnvDurationOr(common.EnvVarProgressFileTickDuration, 3*time.Second),
211+
lastSeenVersions: lastSeenVersions{
212+
versions: make(map[string]string),
213+
mutex: gosync.RWMutex{},
214+
},
204215
}
205216

206217
if executorPlugins {
@@ -687,6 +698,12 @@ func (wfc *WorkflowController) processNextItem(ctx context.Context) bool {
687698
return true
688699
}
689700

701+
if wfc.isOutdated(un) {
702+
log.WithField("key", key).Debug("Skipping outdated workflow event")
703+
wfc.wfQueue.AddRateLimited(key)
704+
return true
705+
}
706+
690707
if !reconciliationNeeded(un) {
691708
log.WithFields(log.Fields{"key": key}).Debug("Won't process Workflow since it's completed")
692709
return true
@@ -903,6 +920,7 @@ func (wfc *WorkflowController) addWorkflowInformerHandlers(ctx context.Context)
903920
if !needed {
904921
key, _ := cache.MetaNamespaceKeyFunc(un)
905922
wfc.recordCompletedWorkflow(key)
923+
wfc.deleteLastSeenVersionKey(wfc.getLastSeenVersionKey(un))
906924
}
907925
return needed
908926
},
@@ -960,6 +978,7 @@ func (wfc *WorkflowController) addWorkflowInformerHandlers(ctx context.Context)
960978
// no need to add to the queue - this workflow is done
961979
wfc.throttler.Remove(key)
962980
}
981+
wfc.deleteLastSeenVersionKey(wfc.getLastSeenVersionKey(obj.(*unstructured.Unstructured)))
963982
},
964983
},
965984
},
@@ -1300,3 +1319,25 @@ func (wfc *WorkflowController) IsLeader() bool {
13001319
// the wfc.wfInformer is nil if it is not the leader
13011320
return wfc.wfInformer != nil
13021321
}
1322+
1323+
func (wfc *WorkflowController) isOutdated(wf metav1.Object) bool {
1324+
wfc.lastSeenVersions.mutex.RLock()
1325+
defer wfc.lastSeenVersions.mutex.RUnlock()
1326+
lastSeenRV, ok := wfc.lastSeenVersions.versions[wfc.getLastSeenVersionKey(wf)]
1327+
// always process if not seen before
1328+
if !ok || lastSeenRV == "" {
1329+
return false
1330+
}
1331+
annotations := wf.GetAnnotations()[common.AnnotationKeyLastSeenVersion]
1332+
return annotations != lastSeenRV
1333+
}
1334+
1335+
func (wfc *WorkflowController) getLastSeenVersionKey(wf metav1.Object) string {
1336+
return string(wf.GetUID())
1337+
}
1338+
1339+
func (wfc *WorkflowController) deleteLastSeenVersionKey(key string) {
1340+
wfc.lastSeenVersions.mutex.Lock()
1341+
defer wfc.lastSeenVersions.mutex.Unlock()
1342+
delete(wfc.lastSeenVersions.versions, key)
1343+
}

workflow/controller/operator.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) {
758758
woc.log.WithError(err).Warn("error updating taskset")
759759
}
760760

761+
oldRV := woc.wf.ResourceVersion
762+
woc.updateLastSeenVersionAnnotation(oldRV)
761763
wf, err := wfClient.Update(ctx, woc.wf, metav1.UpdateOptions{})
762764
if err != nil {
763765
woc.log.Warnf("Error updating workflow: %v %s", err, apierr.ReasonForError(err))
@@ -780,6 +782,7 @@ func (woc *wfOperationCtx) persistUpdates(ctx context.Context) {
780782
woc.controller.hydrator.HydrateWithNodes(woc.wf, nodes)
781783
}
782784

785+
woc.updateLastSeenVersion(oldRV)
783786
// The workflow returned from wfClient.Update doesn't have a TypeMeta associated
784787
// with it, so copy from the original workflow.
785788
woc.wf.TypeMeta = woc.orig.TypeMeta
@@ -861,9 +864,13 @@ func (woc *wfOperationCtx) writeBackToInformer() error {
861864
func (woc *wfOperationCtx) persistWorkflowSizeLimitErr(ctx context.Context, wfClient v1alpha1.WorkflowInterface, err error) {
862865
woc.wf = woc.orig.DeepCopy()
863866
woc.markWorkflowError(ctx, err)
867+
oldRV := woc.wf.ResourceVersion
868+
woc.updateLastSeenVersionAnnotation(oldRV)
864869
_, err = wfClient.Update(ctx, woc.wf, metav1.UpdateOptions{})
865870
if err != nil {
866871
woc.log.Warnf("Error updating workflow with size error: %v", err)
872+
} else {
873+
woc.updateLastSeenVersion(oldRV)
867874
}
868875
}
869876

@@ -4291,3 +4298,19 @@ func getChildNodeIdsRetried(node *wfv1.NodeStatus, nodes wfv1.Nodes) []string {
42914298
}
42924299
return childrenIds
42934300
}
4301+
4302+
func (woc *wfOperationCtx) updateLastSeenVersionAnnotation(value string) {
4303+
if woc.wf.GetAnnotations() == nil {
4304+
woc.wf.SetAnnotations(make(map[string]string))
4305+
}
4306+
woc.wf.GetAnnotations()[common.AnnotationKeyLastSeenVersion] = value
4307+
}
4308+
4309+
func (woc *wfOperationCtx) updateLastSeenVersion(value string) {
4310+
woc.controller.lastSeenVersions.mutex.Lock()
4311+
defer woc.controller.lastSeenVersions.mutex.Unlock()
4312+
if woc.controller.lastSeenVersions.versions == nil {
4313+
woc.controller.lastSeenVersions.versions = make(map[string]string)
4314+
}
4315+
woc.controller.lastSeenVersions.versions[woc.controller.getLastSeenVersionKey(woc.wf)] = value
4316+
}

0 commit comments

Comments
 (0)