<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gary Hsu</title>
    <description>The latest articles on DEV Community by Gary Hsu (@hchsu).</description>
    <link>https://dev.to/hchsu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3866778%2F833e029e-c544-49fd-bcee-08a342fa96ac.png</url>
      <title>DEV Community: Gary Hsu</title>
      <link>https://dev.to/hchsu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hchsu"/>
    <language>en</language>
    <item>
      <title>&lt;InSource&gt; argo-workflows #14615</title>
      <dc:creator>Gary Hsu</dc:creator>
      <pubDate>Sun, 19 Jul 2026 13:13:59 +0000</pubDate>
      <link>https://dev.to/hchsu/argo-workflows-14615-3koo</link>
      <guid>https://dev.to/hchsu/argo-workflows-14615-3koo</guid>
      <description>&lt;p&gt;解析 &lt;a href="https://github.com/argoproj/argo-workflows/issues/14615#issuecomment-4894277872" rel="noopener noreferrer"&gt;#14615&lt;/a&gt; 這個 issue: &lt;code&gt;Workflow&lt;/code&gt; 用 &lt;code&gt;spec.workflowTemplateRef&lt;/code&gt; 指向一個還不存在或 &lt;code&gt;informer cache&lt;/code&gt; 還沒同步到的 WorkflowTemplate 時，controller 直接呈現最終 Error 的狀態。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xyz-init-workflow   Error    2m3s   workflowtemplates.argoproj.io &lt;span class="s2"&gt;"xyz-workflow-template"&lt;/span&gt; not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User 在 issue 中有提到的細節內容:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is because the &lt;code&gt;workflow&lt;/code&gt; is deployed before the &lt;code&gt;workflowtemplate&lt;/code&gt;. But even if we deploy it in the correct order, ArgoWF actually takes some time to process the workflowtemplate and know about it. &lt;br&gt;
So it still failed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而且 User 直覺的表示是「retryStrategy 邏輯壞了」，因為配置的 &lt;code&gt;retryStrategy&lt;/code&gt; 完全沒有觸發:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Naturally, the &lt;code&gt;retryStrategy&lt;/code&gt; should make the workflow retry in such scenarios. But it doesn't do retries.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  name: xyz-init-workflow
spec:
  workflowTemplateRef:
    name: xyz-workflow-template
  retryStrategy:
    limit: 3
    retryPolicy: "Always"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;深挖一番整個呼叫鏈後發現，真正的問題不在 retryStrategy 邏輯壞了，而是根本沒有走到 retry，workflow &lt;code&gt;operate()&lt;/code&gt;就已經判定整個 workflow 是有錯誤並直接報錯。&lt;/p&gt;




&lt;h2&gt;
  
  
  Call Chain Analysis
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;operate&lt;span class="o"&gt;()&lt;/span&gt;                                    operator.go:194   ← reconcile 入口
  │  defer persistUpdates&lt;span class="o"&gt;()&lt;/span&gt;                  operator.go:199   ← 不管成敗，最後一定把 status 寫回 k8s
  │
  ├─ setExecWorkflow&lt;span class="o"&gt;(&lt;/span&gt;ctx&lt;span class="o"&gt;)&lt;/span&gt;                    operator.go:217   ← ★ 模板解析在這裡，極早期
  │    └─ setStoredWfSpec&lt;span class="o"&gt;(&lt;/span&gt;ctx&lt;span class="o"&gt;)&lt;/span&gt;               operator.go:4343
  │         └─ fetchWorkflowSpec&lt;span class="o"&gt;(&lt;/span&gt;ctx&lt;span class="o"&gt;)&lt;/span&gt;        operator.go:4454 → 4299
  │              └─ Lister&lt;span class="o"&gt;()&lt;/span&gt;.Get&lt;span class="o"&gt;(&lt;/span&gt;name&lt;span class="o"&gt;)&lt;/span&gt;       operator.go:4316  ← ★★ 錯誤源頭：NotFound
  │
  │   〔若 err〕→ markWorkflowError           operator.go:4345  ← ★★★ 無條件打成 Error（終態）
  │   〔若 err〕→ &lt;span class="k"&gt;return&lt;/span&gt;（early &lt;span class="nb"&gt;exit&lt;/span&gt;）         operator.go:220
  │
  └─（以下完全到不了）
     executeTemplate&lt;span class="o"&gt;()&lt;/span&gt; → processNodeRetries&lt;span class="o"&gt;()&lt;/span&gt; ← retryStrategy 在這層，永遠走不到
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;詳細的 Call Chain&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  1. 錯誤源頭 — &lt;code&gt;fetchWorkflowSpec&lt;/code&gt;，operator.go:4316
&lt;/h3&gt;

&lt;p&gt;我們先找到錯誤 &lt;code&gt;workflowtemplates.argoproj.io "xyz" not found&lt;/code&gt; 的源頭:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:4314-4320&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CountWorkflowTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;specHolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wftmplInformer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lister&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
   &lt;span class="n"&gt;WorkflowTemplates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkflowTemplateRef&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// :4316&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;          &lt;span class="c"&gt;// :4319 ← 原封不動往上拋，沒有任何分類&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Lister()&lt;/code&gt; 是 client-go 的 informer cache lister（不是直接打 API server）。cache 裡找不到該 name 時回 &lt;code&gt;*apierrors.StatusError{Reason: NotFound}&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;這一段也反映 User 遇到的真實情境，一個為 WFT 還沒有部署; 另一個則為 WFT 部署了但 informer cache 卻還沒有同步。&lt;/li&gt;
&lt;li&gt;:4318 &lt;code&gt;if err != nil { return nil, err }&lt;/code&gt; 完全沒做 transient 分類，導致 &lt;code&gt;NotFound&lt;/code&gt;跟&lt;code&gt;template 內容錯誤&lt;/code&gt;一視同仁往上拋。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. setStoredWfSpec，operator.go:4454-4456
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:4453-4457&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StoredWorkflowSpec&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wftHolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchWorkflowSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;// :4454&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;                                  &lt;span class="c"&gt;// :4456 ← 一樣原樣上拋&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這段是一個 Workflow 第一次被系統接手處理的檢查過程。&lt;br&gt;
這個防呆機制為: 如果連任務的定義（Spec）都找不到，系統就不會浪費資源去建立執行環境（execWf），而是直接報錯結束。&lt;br&gt;
所以 &lt;code&gt;NotFound&lt;/code&gt; 發生在生命週期的最開頭，而 &lt;code&gt;woc.execWf&lt;/code&gt; 此刻還是 nil。&lt;/p&gt;
&lt;h3&gt;
  
  
  3. 致命位置 — setExecWorkflow，operator.go:4343-4346
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:4333-4348（workflowTemplateRef 分支）&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkflowTemplateRef&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="c"&gt;// Strict/Secure mode 檢查&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setStoredWfSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c"&gt;// :4343&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markWorkflowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// :4345 ★★★ 無條件&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;                         &lt;span class="c"&gt;// :4346&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execWf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;wfv1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StoredWorkflowSpec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeepCopy&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="c"&gt;// :4348 ← 永遠到不了&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;4345 無條件 &lt;code&gt;markWorkflowError&lt;/code&gt; —— 不判斷是不是 transient、不判斷是不是 NotFound。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:2800-2802&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;wfOperationCtx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;markWorkflowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markWorkflowPhase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wfv1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkflowError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c"&gt;// Phase = "Error"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;最終導致 WorkflowError 是最終狀態：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// workflow_phase.go:15-22&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;WorkflowPhase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Completed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;WorkflowSucceeded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WorkflowFailed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WorkflowError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="c"&gt;// Error 算 Completed&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;一旦進 Error，controller 後續只會收尾，不會再 re-operate。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  4. 收尾 — early return + defer 落地，operator.go:218-220 / 199
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:217-221&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setExecWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unable to set ExecWorkflow"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// :219&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;                                                            &lt;span class="c"&gt;// :220 ← 整個 operate 提早結束&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:198-200&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persistUpdates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;// :199 ← 即使 :220 提早 return，defer 仍把 Error 寫進 k8s&lt;/span&gt;
&lt;span class="p"&gt;}()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;在這邊就宣判死刑了，status.message = &lt;code&gt;workflowtemplates.argoproj.io "xyz" not found&lt;/code&gt;。&lt;/p&gt;

&lt;h3&gt;
  
  
  User 的疑惑: 為什麼 retryStrategy 完全不參與?
&lt;/h3&gt;

&lt;p&gt;operator.go:387-407 要 &lt;code&gt;woc.execWf&lt;/code&gt; 已建好才會跑到。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// operator.go:387-407&lt;/span&gt;
&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executeTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execWf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// :387&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;// ★ 這條路徑「有」判 transient：transient 就不打 Error，留待 requeue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errorsutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsTransientErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Completed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markWorkflowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;// :399&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;retryStrategy&lt;/code&gt; 的實際重試在 &lt;code&gt;executeTemplate()&lt;/code&gt; → &lt;code&gt;processNodeRetries()&lt;/code&gt;（operator.go:972），只對已開始執行的 node 失敗才會產生效益。&lt;/p&gt;

&lt;p&gt;WFT 解析失敗發生則是發生在 Node 執行之前，連 &lt;code&gt;execWf&lt;/code&gt; 都還沒有建起來，所以根本走不到 &lt;code&gt;retryStrategy&lt;/code&gt;，這也說明了為什麼跟 User 其他有落差的原因。&lt;/p&gt;




&lt;h2&gt;
  
  
  From Root Cause to Solve
&lt;/h2&gt;

&lt;p&gt;從 Source Code 的分析可以知道整個 issue 的來龍去脈，並可以總結出:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;原因不是「retryStrategy 壞了」，而是兩個層級錯位 + 一條無條件失敗的路徑。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;層級錯位&lt;/strong&gt;: WFT 解析發生在 workflow 啟動最前面、早於任何 node 執行&lt;br&gt;
&lt;strong&gt;失敗路徑&lt;/strong&gt;: NotFound 在這條路徑上已經被無條件 markWorkflowError 直接宣判死刑。&lt;/p&gt;
&lt;h3&gt;
  
  
  How to Solve?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;setStoredWfSpec operator.go:4345&lt;/code&gt; 中無條件 &lt;code&gt;markWorkflowError&lt;/code&gt;的這段是我第一步著手想要處理的部分。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原本的行為是&lt;/strong&gt;：workflow 指到一個還沒就緒的 WorkflowTemplate，controller 二話不說直接把 workflow 打成 Error，但這個選擇太一番兩瞪眼。&lt;br&gt;
&lt;strong&gt;我的修法是&lt;/strong&gt;: 改成「先等一下（grace period 內標 Pending、每 10 秒重試），等模板出現就繼續跑；真的等太久才放棄打成 Error」，給判斷過程一個緩衝。&lt;/p&gt;
&lt;h4&gt;
  
  
  問題一: 這個新行為要套給誰？
&lt;/h4&gt;

&lt;p&gt;兩個選項: opt-in（要使用者自己打開）&amp;amp; default-on（直接幫所有人開）&lt;br&gt;
&lt;strong&gt;選項一:&lt;/strong&gt; opt-in（要使用者自己打開）&lt;br&gt;
在 CRD（Workflow 的 YAML 規格）裡加一個新欄位，像是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="c1"&gt;# 使用者要自己寫這行，新行為才生效&lt;/span&gt;
  &lt;span class="na"&gt;retryOnTemplateNotFound&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;這會導致沒有寫的人會維持秒死的舊行為，只有知道這個選項且有設定的人，才能有緩衝的時間。&lt;br&gt;
這個選項成本會很大，雖然只是在 CRD 加一個新欄位，但連帶會生成:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;跑 &lt;code&gt;make codegen&lt;/code&gt;（重新生成一堆 Go 型別）&lt;/li&gt;
&lt;li&gt;更新 swagger（API 文件定義）&lt;/li&gt;
&lt;li&gt;更新使用者 docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;這會一瞬間牽動很多檔案異動的大 PR，也會導致比較難 Review。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;選項二:&lt;/strong&gt; default-on（直接幫所有人開)&lt;br&gt;
不加任何欄位，所有 workflow 一律套用新行為，使用者什麼都不用改。&lt;/p&gt;

&lt;p&gt;我最終選擇走 default-on 直接幫所有人開啟，既沒有真實風險、又能省下 CRD/codegen 那一大坨工程量，讓 PR 保持最小。&lt;/p&gt;
&lt;h4&gt;
  
  
  問題二: 這個"等一下(grace period)" 該從什麼時候開始算？那要等多久？
&lt;/h4&gt;

&lt;p&gt;新的問題又來了，我們的重試總不能無限等待下去，需要設定一個等待的上限，而要計算上限需要知道我們該從什麼時候開始算。&lt;br&gt;
直覺上會想:「當這次把它標成 Pending，那就從這次標 Pending 的時間開始算。」這樣會出問題。&lt;br&gt;
因為 controller 是每 10 秒重跑一輪的，而且每一輪都會重新把它標成 Pending，造成每10秒都重新計算，最終變成永不放棄！&lt;/p&gt;

&lt;p&gt;那該選什麼好勒？ &lt;code&gt;CreationTimestamp&lt;/code&gt; 是 workflow 被建立那一刻的時間戳，K8s 已經幫我們記好的，而且建立後永遠不會變。&lt;br&gt;
這個固定不動的錨點，讓我們有一個明確的重試上限，一個一定會到期的上限，所以我們最終就以 &lt;code&gt;CreationTimestamp&lt;/code&gt;為我們的起算時間。&lt;/p&gt;


&lt;h3&gt;
  
  
  Action: In Detail
&lt;/h3&gt;

&lt;p&gt;這次的修正我們的核心概念是: 把 &lt;code&gt;setExecWorkflow&lt;/code&gt;（operator.go:4343-4346）目前&lt;strong&gt;無條件&lt;/strong&gt; &lt;code&gt;markWorkflowError&lt;/code&gt; 改成:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;只對 WorkflowTemplate NotFound 做有上限的重試&lt;/li&gt;
&lt;li&gt;並在 grace period 內標 &lt;code&gt;Pending&lt;/code&gt; 且 requeue 等 WorkflowTemplate 就緒，逾時才 fallback 回 &lt;code&gt;Error&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  深入細節
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setStoredWfSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Tolerate a not-yet-available WorkflowTemplate (applied together via GitOps, or informer cache&lt;/span&gt;
    &lt;span class="c"&gt;// lag): within a grace period measured from creation, keep the workflow Pending and requeue so it&lt;/span&gt;
    &lt;span class="c"&gt;// starts once the template appears; after that, fail so a genuinely missing template still fails fast.&lt;/span&gt;
    &lt;span class="n"&gt;gracePeriod&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetWorkflowTemplateNotFoundGracePeriod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;apierr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsNotFound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreationTimestamp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;gracePeriod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markWorkflowPhase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wfv1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkflowPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Waiting for referenced WorkflowTemplate to become available: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requeueAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetRequeueTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrWorkflowTemplateNotReady&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;修正程式碼&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;要點1: 只在&lt;code&gt;setExecWorkflow&lt;/code&gt; 判斷「找不到 WorkflowTemplate」，不要動全域規則&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;controller 有一個全域的「哪些錯誤算暫時性、可以重試」清單。其實最偷懶的改法是把「NotFound（找不到）」加進那個全域清單，但我們沒有走這個選項。&lt;br&gt;
雖然可能一行就搞定，但因為這是全域的，所有呼叫 &lt;code&gt;IsTransientErr&lt;/code&gt; 的地方從此都會把「NotFound」當成可重試，還包括那些"東西真的被刪了、本該永久失敗"的路徑，害它們變成無限重試。&lt;br&gt;
所以我們只在 &lt;code&gt;err := woc.setStoredWfSpec(ctx)&lt;/code&gt; 用 &lt;code&gt;apierr.IsNotFound&lt;/code&gt; 來判斷這個錯是不是 NotFound。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要點 2：等待要有期限，而且期限要從「Workflow 建立時間」算起&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;除了使用&lt;code&gt;CreationTimestamp&lt;/code&gt; 作為 workflow 被建立那一刻的時間戳外，也另外新增一個設定值 &lt;code&gt;workflowTemplateNotFoundGracePeriod&lt;/code&gt; 。&lt;br&gt;
預設為 30 seconds ，也可以調整為 0 second，即可完全恢復成舊的秒死的行為。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要點 3：用一個「特製的錯誤」當暗號，告訴 Operate 需要再等等&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;這邊設定一個新"暗號" &lt;code&gt;ErrWorkflowTemplateNotReady&lt;/code&gt;，當沒有發現 &lt;code&gt;WorkflowTemplate&lt;/code&gt;時，起到兩個作用:&lt;br&gt;
  a. &lt;strong&gt;「非空」的 error&lt;/strong&gt;: 讓上層 operate 提早收工，不要繼續往下跑（因為此時範本還沒解析好，硬跑會拿到不完整的資料）。&lt;br&gt;
  b. &lt;strong&gt;上層用 errors.Is 認出這個特定暗號&lt;/strong&gt;: 當出現這個預期的錯誤時，operate 會安靜的 return，不印錯誤 log，否則寬限期內每次重試都會噴一條誤導人的 ERROR，看起來像壞了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要點 4：markWorkflowPhase — 改 Workflow 的「狀態階段」&lt;/strong&gt;&lt;br&gt;
operator.go:2627 中核心內容:&lt;br&gt;
&lt;code&gt;woc.wf.Status.Phase = phase(L2643&lt;/code&gt;) 把這個 Workflow 標成 &lt;code&gt;Pending&lt;/code&gt; / &lt;code&gt;Running&lt;/code&gt; / &lt;code&gt;Error&lt;/code&gt; / &lt;code&gt;Succeeded&lt;/code&gt; 等。順帶做的事:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;它只改記憶體裡的 &lt;code&gt;woc.wf&lt;/code&gt; 物件並把 woc.updated = true(L2642)&lt;/li&gt;
&lt;li&gt;真正寫回 etcd 是 operate() 結尾的 &lt;code&gt;defer persistUpdates&lt;/code&gt; 統一落地。所以我們只是在流程中「標 Pending」,不會馬上打 API server。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而 &lt;code&gt;markWorkflowError&lt;/code&gt; 就是 &lt;code&gt;markWorkflowPhase&lt;/code&gt; 的一個薄包裝&lt;br&gt;
operator.go:2817,整個函式只有一行:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;woc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;wfOperationCtx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;markWorkflowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;woc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;markWorkflowPhase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wfv1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkflowError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;「逾時回 markWorkflowError」在講什麼:fix 加了 grace 判斷後，grace 期內走 markWorkflowPhase(Pending)(暫時等);&lt;br&gt;
grace 過了,就 fallback 回原本的 markWorkflowError,讓它照舊落終態 Error。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;要點 5：requeueAfter: N 秒後再排我&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;把這個 workflow 丟回待處理 &lt;code&gt;wfQueue&lt;/code&gt;，但不是馬上重跑，而是 N 秒後再排我一次。&lt;br&gt;
因為 template 剛套用時，controller 內部的快取(informer)可能還沒同步到，馬上重試也是白跑，不如等一下再檢查。&lt;/p&gt;

&lt;p&gt;controller 有三種 requeue 機制:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wfQueue.Add(key)&lt;/code&gt;: 立即入列&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wfQueue.AddAfter(key, d)&lt;/code&gt;: 固定延遲 d&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wfQueue.AddRateLimited(key)&lt;/code&gt;: limiter 決定的間隔&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那要等多久? 我們直接使用 &lt;code&gt;GetRequeueTime()&lt;/code&gt;，直接用系統統一設定的參數（統一 10 秒，可以用環境變數調整）。 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;為什麼非得主動 requeue:&lt;br&gt;
WFT 的 informer 沒有事件 handler,別人建立 WorkflowTemplate 不會自動叫醒在等的 Workflow。所以只能靠自己排 requeueAfter 定期回來看。這也是為什麼要標 Pending(未完成才吃得到 requeue)而不是 Error(終態,永不再看)。&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;經過 Reviewer 審閱後，他回覆了:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't think we should fix this. Even if we fix it for workflowTemplateRef we've still got a problem if your workflow references WFT A which needs B and B hasn't appeared yet.&lt;/p&gt;

&lt;p&gt;The only good fix here is to apply WorkflowTemplates before workflows, and I don't want the added complication and surprises at runtime which this fix would entail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;很顯然 Reviewer 覺得最正確的做法反而不是修改目前的流程，而是要 User 保證部署的順序 —— 先把所有 WorkflowTemplate 都建好，再去跑用到它們的 Workflow，而不是叫 controller 在執行時自己「等一下」。&lt;/p&gt;

&lt;p&gt;確實，在解這題的時候沒有想到 WorkflowTemplate 自己又依賴另一個 WorkflowTemplate（A 需要 B）這種用法，在這種情況下，又會回到原本的位置，很顯然沒有解掉完整的問題。&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
