

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Lambda 耐用函數
<a name="durable-functions"></a>

Lambda 耐用函數可讓您建置彈性的多步驟應用程式和 AI 工作流程，可執行長達一年，同時在中斷的情況下保持可靠的進度。當持久性函數執行時，這個完整的生命週期稱為持久性執行，它使用檢查點來追蹤進度，並透過重新執行自動從失敗中復原，從頭開始重新執行，同時略過完成的工作。

在每個函數中，您會使用耐用的操作做為基本的建置區塊。步驟會使用內建重試和進度追蹤來執行商業邏輯，同時等待暫停執行而不會產生運算費用，因此非常適合長時間執行的程序human-in-the-loop工作流程或輪詢外部相依性。無論您是在處理訂單、協調微服務或協調代理式 AI 應用程式，當您使用熟悉的程式設計語言編寫程式碼時，耐用的函數會自動維持狀態並從失敗中復原。

## 主要優點
<a name="durable-functions-benefits"></a>

**自然撰寫彈性程式碼：**使用熟悉的程式設計建構，您可以撰寫自動處理失敗的程式碼。內建檢查點、透明重試和自動復原意味著您的商業邏輯保持乾淨且專注。

**只需為您使用的項目付費：**在等待操作期間，您的函數會暫停，而不會產生運算費用。對於等待數小時或數天的長時間執行工作流程，您只需支付實際處理時間，而非閒置等待。

**操作簡單：**使用 Lambda 的無伺服器模型，您可以自動擴展，包括scale-to-zero，而無需管理基礎設施。耐用的函數會自動處理狀態管理、重試邏輯和故障復原，從而降低營運開銷。

## 使用耐用函數的時機
<a name="durable-functions-use-cases"></a>

**短期協調：**透過自動轉返失敗，協調跨多項服務的付款、庫存和運送。透過驗證、付款授權、庫存分配和履行來處理訂單，並保證完成。

**安心處理付款：**建立彈性付款流程，透過失敗維持交易狀態，並自動處理重試。協調跨付款供應商的多步驟授權、詐騙檢查和和解，並跨步驟進行完整稽核。

**建置可靠的 AI 工作流程：**建立多步驟 AI 工作流程，以鏈結模型呼叫、整合人工意見回饋，並在失敗期間果斷地處理長時間執行的任務。停用後自動繼續，且只需支付作用中執行時間的費用。

**協調複雜的訂單履行：**使用內建彈性協調跨庫存、付款、運送和通知系統的訂單處理。自動處理部分故障、保留中斷時的順序狀態，並有效率地等待外部事件，而不會耗用運算資源。

**自動化多步驟業務工作流程：**為員工加入、貸款核准和跨越數天或數週的合規程序建立可靠的工作流程。維護跨人工核准、系統整合和排程任務的工作流程狀態，同時提供程序狀態和歷史記錄的完整可見性。

### 與 Step Functions 比較的耐久性函數
<a name="durable-functions-vs-step-functions"></a>

耐用的函數和 Step Functions 都提供具有自動狀態管理的工作流程協同運作。主要差異在於它們的執行位置，以及您如何定義工作流程：
+ **耐用函數：**在 Lambda 內執行、使用標準程式設計語言、在 Lambda 環境中管理
+ **Step Functions：**單機服務、圖形式 DSL 或視覺化設計工具，完全受管，無需維護

耐用的函數非常適合在 Lambda 中應用程式開發，其中工作流程與商業邏輯緊密結合。Step Functions 擅長跨您需要視覺化設計、原生整合超過 220 個服務以及零維護基礎設施 AWS 的服務進行工作流程協同運作。

如需詳細比較，請參閱[耐用函數或 Step Functions](durable-step-functions.md)。

## 運作方式
<a name="durable-functions-how-it-works"></a>

 在幕後，耐用的函數是一般的 Lambda 函數，使用檢查點/重播機制來追蹤進度，並透過使用者定義的暫停點支援長時間執行的操作，通常稱為耐用執行。函數從暫停或中斷恢復後，系統會執行重播。在重播期間，您的程式碼會從頭開始執行，但會略過已完成的檢查點，使用儲存的結果，而不是重新執行已完成的操作。此重播機制可確保一致性，同時啟用長時間執行的執行。

為了在您的應用程式中利用此checkpoint-and-replay機制，Lambda 提供耐用的執行 SDK。開發套件可消除管理檢查點和重播的複雜性，公開您在程式碼中使用的稱為耐久操作的簡單基本概念。開發套件適用於 JavaScript、TypeScript、Python 和 Java （預覽版），可與您現有的 Lambda 開發工作流程無縫整合。

使用 SDK，您可以包裝 Lambda 事件處理常式，然後提供 DurableContext 與您的事件並行。此內容可讓您存取耐用的操作，例如步驟和等待。您可以將函數邏輯寫入為一般循序程式碼，但不會直接呼叫 服務，而是將這些呼叫包裝為自動檢查點和重試的步驟。當您需要暫停執行時，您可以新增暫停函數的等待，而不會產生費用。開發套件可處理所有複雜的狀態管理和幕後重播，讓您的程式碼保持乾淨且可讀取。

 ![\[Filter for Amazon Inspector results related to Lambda functions\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/how_durable_works.png) 

## 後續步驟
<a name="durable-functions-next-steps"></a>
+ [開始使用耐用的 函數](durable-getting-started.md)
+ [探索耐用的執行 SDK](durable-execution-sdk.md)
+ [耐用函數或 Step Functions](durable-step-functions.md)
+ [監控和偵錯耐用函數](durable-monitoring.md)
+ [檢閱安全性和許可](durable-security.md)
+ [遵循最佳實務](durable-best-practices.md)

# 基本概念
<a name="durable-basic-concepts"></a>

Lambda 提供適用於 JavaScript、TypeScript 和 Python SDKs。這些 SDKs是建置耐用函數的基礎，提供您檢查點進度、處理重試和管理執行流程所需的基本概念。如需完整的 SDK 文件和範例，請參閱 GitHub 上的 [JavaScript/TypeScript SDK](https://github.com/aws/aws-durable-execution-sdk-js) 和 [Python SDK](https://github.com/aws/aws-durable-execution-sdk-python)。

## 持久性執行
<a name="durable-execution-concept"></a>

**持久性執行**代表 Lambda 持久性函數的完整生命週期，使用檢查點和重播機制來追蹤業務邏輯進度、暫停執行並從失敗中復原。當函數在暫停或中斷後恢復時，會重新播放先前完成的檢查點，且函數會繼續執行。

生命週期可能包含 Lambda 函數的多次調用以完成執行，特別是在暫停或故障復原之後。此方法可讓您的函數長時間執行 （長達一年），同時在中斷的情況下維持可靠的進度。

**重播的運作方式**  
Lambda 會在函數執行時保留所有耐久操作 （步驟、等待和其他操作） 的執行中日誌。當您的函數需要暫停或遇到中斷時，Lambda 會儲存此檢查點日誌並停止執行。恢復時，Lambda 會從頭再次叫用您的函數，並重播檢查點日誌，以儲存的值取代已完成的操作。這表示您的程式碼會再次執行，但先前完成的步驟不會重新執行。而是改用其儲存的結果。

此重播機制對於了解持久功能至關重要。您的程式碼在重播期間必須是確定性的，這表示它在相同的輸入下會產生相同的結果。避免在步驟外出現副作用 （例如產生隨機數字或取得目前時間） 的操作，因為這些操作可能會在重播期間產生不同的值，並導致非確定性的行為。

## DurableContext
<a name="durable-context-concept"></a>

**DurableContext** 是您耐用函數接收的內容物件。它提供持久性操作的方法，例如建立檢查點和管理執行流程的步驟和等待。

您的耐用函數會收到 `DurableContext`而非預設 Lambda 內容：

------
#### [ TypeScript ]

```
import {
  DurableContext,
  withDurableExecution,
} from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const result = await context.step(async () => {
      return "step completed";
    });
    return result;
  },
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import (
    DurableContext,
    durable_execution,
    durable_step,
)

@durable_step
def my_step(step_context, data):
    # Your business logic
    return result

@durable_execution
def handler(event, context: DurableContext):
    result = context.step(my_step(event["data"]))
    return result
```

------

適用於耐用函數的 Python SDK 使用同步方法，不支援 `await`。TypeScript SDK 使用 `async/await`。

## 步驟
<a name="steps-concept"></a>

**步驟**會使用內建重試和自動檢查點來執行商業邏輯。每個步驟都會儲存其結果，確保您的函數可以在中斷後從任何已完成的步驟恢復。

------
#### [ TypeScript ]

```
// Each step is automatically checkpointed
const order = await context.step(async () => processOrder(event));
const payment = await context.step(async () => processPayment(order));
const result = await context.step(async () => completeOrder(payment));
```

------
#### [ Python ]

```
# Each step is automatically checkpointed
order = context.step(lambda: process_order(event))
payment = context.step(lambda: process_payment(order))
result = context.step(lambda: complete_order(payment))
```

------

## 等待狀態
<a name="wait-states-concept"></a>

**等待狀態**是您的函數停止執行 （並停止充電） 的計劃暫停，直到繼續為止。使用它們來等待期間、外部回呼或特定條件。

------
#### [ TypeScript ]

```
// Wait for 1 hour without consuming resources
await context.wait({ seconds:3600 });

// Wait for external callback
const approval = await context.waitForCallback(
  async (callbackId) => sendApprovalRequest(callbackId)
);
```

------
#### [ Python ]

```
# Wait for 1 hour without consuming resources
context.wait(3600)

# Wait for external callback
approval = context.wait_for_callback(
    lambda callback_id: send_approval_request(callback_id)
)
```

------

當您的函數遇到等待或需要暫停時，Lambda 會儲存檢查點日誌並停止執行。恢復時，Lambda 會再次叫用您的函數，並重播檢查點日誌，以儲存的值取代已完成的操作。

對於更複雜的工作流程，耐用的 Lambda 函數也隨附進階操作，例如`parallel()`用於並行執行、`map()`用於處理陣列、`runInChildContext()`用於巢狀操作，以及`waitForCondition()`用於輪詢。如需何時使用每項操作的詳細範例和指引，請參閱[範例](durable-examples.md)。

## 叫用其他 函數
<a name="invoke-concept"></a>

**調用**允許耐用的函數呼叫其他 Lambda 函數，並等待其結果。呼叫函數會在調用函數執行時暫停，建立保留結果的檢查點。這可讓您建置模組化工作流程，讓專門的 函數處理特定任務。

使用 `context.invoke()`從您的耐用函數中呼叫其他函數。調用是檢查點的，因此如果您的函數在調用函數完成後中斷，它會繼續以儲存的結果繼續，而不會重新調用函數。

------
#### [ TypeScript ]

```
// Invoke another function and wait for result
const customerData = await context.invoke(
  'validate-customer',
  'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
  { customerId: event.customerId }
);

// Use the result in subsequent steps
const order = await context.step(async () => {
  return processOrder(customerData);
});
```

------
#### [ Python ]

```
# Invoke another function and wait for result
customer_data = context.invoke(
    'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
    {'customerId': event['customerId']},
    name='validate-customer'
)

# Use the result in subsequent steps
order = context.step(
    lambda: process_order(customer_data),
    name='process-order'
)
```

------

調用函數可以是耐用或標準 Lambda 函數。如果您叫用持久性函數，呼叫函數會等待完全持久性執行完成。此模式在微服務架構中很常見，其中每個函數都會處理特定網域，讓您從專業、可重複使用的函數編寫複雜的工作流程。

**注意**  
不支援跨帳戶調用。調用函數必須與呼叫函數位於相同的 AWS 帳戶中。

## 耐用的函數組態
<a name="durable-configuration-basic"></a>

耐用函數具有特定的組態設定，可控制執行行為和資料保留。這些設定與標準 Lambda 函數組態分開，並套用至整個耐久執行生命週期。

**DurableConfig** 物件會定義耐用函數的組態：

```
{
  "ExecutionTimeout": Integer,
  "RetentionPeriodInDays": Integer
}
```

### Execution timeout (執行逾時)
<a name="durable-execution-timeout"></a>

**執行逾時**可控制持久性執行從開始到完成的時間長度。這與 Lambda 函數逾時不同，它控制單一函數調用可以執行的時間長度。

當 Lambda 函數通過檢查點、等待和重播時，持久的執行可以跨越多個 Lambda 函數調用。執行逾時適用於持久性執行的總經過時間，不適用於個別函數叫用。

**了解差異**  
Lambda 函數逾時 （最長 15 分鐘） 會限制函數的每個個別調用。持久的執行逾時 （最長 1 年） 會限制從執行開始到完成、失敗或逾時的總時間。在此期間，您的函數可能會在處理步驟、等待並從失敗中復原時被叫用多次。

例如，如果您將持久的執行逾時設定為 24 小時，Lambda 函數逾時設定為 5 分鐘：
+ 每個函數調用必須在 5 分鐘內完成
+ 整個耐久執行最多可執行 24 小時
+ 在這 24 小時內，您的函數可以被叫用多次
+ 等待操作不會計入 Lambda 函數逾時，但會計入執行逾時

您可以使用 Lambda 主控台或 建立耐用函數時 AWS CLI，設定執行逾時 AWS SAM。在 Lambda 主控台中，選擇您的函數，然後選擇組態、持久性執行。以秒為單位設定執行逾時值 （預設值：86400 秒 / 24 小時，最小值：60 秒，最大值：31536000 秒 / 1 年）。

**注意**  
執行逾時和 Lambda 函數逾時的設定不同。Lambda 函數逾時控制每個個別調用可以執行的時間長度 （最長 15 分鐘）。執行逾時控制整個持久性執行的總經過時間 （最長 1 年）。

### 保留期間
<a name="durable-retention-period"></a>

**保留期間**控制 Lambda 在持久性執行完成後保留執行歷史記錄和檢查點資料的時間長度。此資料包含步驟結果、執行狀態和完整的檢查點日誌。

保留期間到期後，Lambda 會刪除執行歷史記錄和檢查點資料。您無法再擷取執行詳細資訊或重播執行。保留期會在執行達到結束狀態 (SUCCEEDED、FAILED、STOPPED 或 TIMED\$1OUT) 時開始。

您可以使用 Lambda 主控台 AWS CLI或 來設定建立耐用函數時的保留期間 AWS SAM。在 Lambda 主控台中，選擇您的函數，然後選擇組態、持久性執行。以天為單位設定保留期間值 （預設值：14 天，下限：1 天，上限：90 天）。

根據您的合規要求、除錯需求和成本考量，選擇保留期。較長的保留期可提供更多偵錯和稽核的時間，但會增加儲存成本。

## 另請參閱
<a name="durable-basic-concepts-see-also"></a>
+ [耐用函數或 Step Functions](durable-step-functions.md) – 比較耐用函數與 Step Functions，以了解每個方法最有效的時機。

# 建立 Lambda 耐用函數
<a name="durable-getting-started"></a>

若要開始使用 Lambda 耐用函數，請使用 Lambda 主控台建立耐用函數。在幾分鐘內，您可以建立和部署耐久的 函數，該函數使用步驟 並等待示範檢查點型執行。

執行教學課程時，您將學習基本的耐用函數概念，例如如何使用 `DurableContext` 物件、使用步驟建立檢查點，以及使用等待暫停執行。您也將了解當您的函數在等待後恢復時，重播的運作方式。

為了保持簡單，本教學課程說明如何使用 Python 或 Node.js 執行時間來建立函數。您可以使用這些轉譯語言，直接在主控台的內建程式碼編輯器中編輯函數程式碼。

Java （預覽版） 中的耐用函數目前只能透過容器映像部署。如需從容器映像建立持久性函數的詳細資訊，請參閱[持久性函數支援的執行時間](durable-supported-runtimes.md)，或使用[基礎設施即程式碼部署 Lambda 持久性函數](durable-getting-started-iac.md)。

**注意**  
耐用函數目前支援 Python 和 Node.js (JavaScript/TypeScript) 執行期和容器映像 (OCI)，例如 Java。如需支援執行時間版本和容器映像選項的完整清單，請參閱[耐用函數支援的執行時間](durable-supported-runtimes.md)。如需搭配 Lambda 使用容器映像的詳細資訊，請參閱[《Lambda 開發人員指南》中的建立 Lambda 容器映像](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)。

**提示**  
若要了解如何建置**無伺服器解決方案**，請參閱[無伺服器開發人員指南](https://docs.aws.amazon.com/serverless/latest/devguide/)。

## 先決條件
<a name="durable-getting-started-prerequisites"></a>

### 註冊 AWS 帳戶
<a name="sign-up-for-aws"></a>

如果您沒有 AWS 帳戶，請完成下列步驟來建立一個。

**註冊 AWS 帳戶**

1. 開啟 [https://portal.aws.amazon.com/billing/signup](https://portal.aws.amazon.com/billing/signup)。

1. 請遵循線上指示進行。

   部分註冊程序需接收來電或簡訊，並在電話鍵盤輸入驗證碼。

   當您註冊 時 AWS 帳戶，*AWS 帳戶根使用者*會建立 。根使用者有權存取該帳戶中的所有 AWS 服務 和資源。作為安全最佳實務，請將管理存取權指派給使用者，並且僅使用根使用者來執行[需要根使用者存取權的任務](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html#root-user-tasks)。

AWS 會在註冊程序完成後傳送確認電子郵件給您。您可以隨時登錄 [https://aws.amazon.com/](https://aws.amazon.com/) 並選擇**我的帳戶**，以檢視您目前的帳戶活動並管理帳戶。

### 建立具有管理存取權的使用者
<a name="create-an-admin"></a>

註冊 後 AWS 帳戶，請保護 AWS 帳戶根使用者、啟用 AWS IAM Identity Center和建立管理使用者，以免將根使用者用於日常任務。

**保護您的 AWS 帳戶根使用者**

1.  選擇**根使用者**並輸入 AWS 帳戶 您的電子郵件地址，以帳戶擁有者[AWS 管理主控台](https://console.aws.amazon.com/)身分登入 。在下一頁中，輸入您的密碼。

   如需使用根使用者登入的說明，請參閱 *AWS 登入 使用者指南*中的[以根使用者身分登入](https://docs.aws.amazon.com/signin/latest/userguide/console-sign-in-tutorials.html#introduction-to-root-user-sign-in-tutorial)。

1. 若要在您的根使用者帳戶上啟用多重要素驗證 (MFA)。

   如需說明，請參閱《*IAM 使用者指南*》中的[為您的 AWS 帳戶 根使用者 （主控台） 啟用虛擬 MFA 裝置](https://docs.aws.amazon.com/IAM/latest/UserGuide/enable-virt-mfa-for-root.html)。

**建立具有管理存取權的使用者**

1. 啟用 IAM Identity Center。

   如需指示，請參閱《AWS IAM Identity Center 使用者指南》**中的[啟用 AWS IAM Identity Center](https://docs.aws.amazon.com//singlesignon/latest/userguide/get-set-up-for-idc.html)。

1. 在 IAM Identity Center 中，將管理存取權授予使用者。

   如需使用 IAM Identity Center 目錄 做為身分來源的教學課程，請參閱*AWS IAM Identity Center 《 使用者指南*》中的[使用預設值設定使用者存取 IAM Identity Center 目錄](https://docs.aws.amazon.com//singlesignon/latest/userguide/quick-start-default-idc.html)。

**以具有管理存取權的使用者身分登入**
+ 若要使用您的 IAM Identity Center 使用者簽署，請使用建立 IAM Identity Center 使用者時傳送至您電子郵件地址的簽署 URL。

  如需使用 IAM Identity Center 使用者登入的說明，請參閱*AWS 登入 《 使用者指南*》中的[登入 AWS 存取入口網站](https://docs.aws.amazon.com/signin/latest/userguide/iam-id-center-sign-in-tutorial.html)。

**指派存取權給其他使用者**

1. 在 IAM Identity Center 中，建立一個許可集來遵循套用最低權限的最佳實務。

   如需指示，請參閱《AWS IAM Identity Center 使用者指南》**中的[建立許可集](https://docs.aws.amazon.com//singlesignon/latest/userguide/get-started-create-a-permission-set.html)。

1. 將使用者指派至群組，然後對該群組指派單一登入存取權。

   如需指示，請參閱《AWS IAM Identity Center 使用者指南》**中的[新增群組](https://docs.aws.amazon.com//singlesignon/latest/userguide/addgroups.html)。

## 使用主控台建立 Lambda 耐用函數
<a name="getting-started-create-durable-function"></a>

在此範例中，您的耐用函數會使用自動檢查點處理多個步驟的訂單。函數會取得包含訂單 ID 的 JSON 物件、驗證訂單、處理付款，以及確認訂單。每個步驟都會自動檢查點，因此如果函數中斷，則會從上次完成的步驟繼續執行。

您的函數也會示範等待操作，暫停執行一小段時間以模擬等待外部確認。

**使用主控台建立耐用的函數**

1. 開啟 Lambda 主控台中的 [函數頁面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 選擇**建立函數**。

1. 選取**從頭開始撰寫**。

1. 在**基本資訊**窗格中，為**函數名稱**輸入 `myDurableFunction`。

1. 針對**執行期**，選擇 **Node.js 24** 或 **Python 3.14。**

1. 選取**啟用持久性執行**。

Lambda 會使用包含檢查點操作 (`lambda:CheckpointDurableExecutions` 和 ) 許可的[執行角色](lambda-intro-execution-role.md)來建立您的耐用函數`lambda:GetDurableExecutionState`。

**注意**  
Lambda 執行時間包含持久性執行 SDK，因此您可以測試持久性函數，而無需封裝相依性。不過，我們建議您在部署套件中包含 開發套件以供生產使用。這可確保版本一致性，並避免可能影響函數的潛在執行時間更新。

使用主控台的內建程式碼編輯器來新增您的耐用函數程式碼。

------
#### [ Node.js ]

**若要在主控台中修改程式碼**

1. 選擇 **程式碼** 標籤。

   在主控台的內建程式碼編輯器中，您應該會看到 Lambda 建立的函數程式碼。如果您在程式碼編輯器中沒看到 **index.mjs** 標籤，請在檔案總管中選取 **index.mjs**，如下圖所示。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/durable-nodejs.png)

1. 將以下程式碼貼到 **index.mjs** 標籤中，替換 Lambda 建立的程式碼。

   ```
   import {
     DurableContext,
     withDurableExecution,
   } from "@aws/durable-execution-sdk-js";
   
   export const handler = withDurableExecution(
     async (event, context) => {
       const orderId = event.orderId;
       
       // Step 1: Validate order
       const validationResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Validating order ${orderId}`);
         return { orderId, status: "validated" };
       });
       
       // Step 2: Process payment
       const paymentResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Processing payment for order ${orderId}`);
         return { orderId, status: "paid", amount: 99.99 };
       });
       
       // Wait for 10 seconds to simulate external confirmation
       await context.wait({ seconds: 10 });
       
       // Step 3: Confirm order
       const confirmationResult = await context.step(async (stepContext) => {
         stepContext.logger.info(`Confirming order ${orderId}`);
         return { orderId, status: "confirmed" };
       });
           
       return {
         orderId: orderId,
         status: "completed",
         steps: [validationResult, paymentResult, confirmationResult]
       };
     }
   );
   ```

1. 在 **DEPLOY** 區段中，選擇**部署**以更新函數的程式碼：  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

**了解您的耐用函數程式碼**  
在進行下一個步驟之前，讓我們先查看函數程式碼，並了解關鍵的耐久函數概念。
+ `withDurableExecution` 包裝函式：

  您的耐用函數會包裝在 中`withDurableExecution`。此包裝函式提供 `DurableContext` 物件和管理檢查點操作，以啟用持久的執行。
+ `DurableContext` 物件：

  您的函數會收到 ，而不是標準 Lambda 內容`DurableContext`。此物件提供建立檢查點等耐久操作的方法`wait()`，例如 `step()`和 。
+ 步驟和檢查點：

  每個`context.step()`呼叫都會在執行前後建立檢查點。如果您的函數中斷，則會從上次完成的檢查點繼續執行。函數不會重新執行完成的步驟。它會改用其儲存的結果。
+ 等待操作：

  `context.wait()` 呼叫會暫停執行，而不會耗用運算資源。當等待完成時，Lambda 會再次叫用您的函數，並重播檢查點日誌，以儲存的值取代已完成的步驟。
+ 重播機制：

  當您的函數在等待或中斷後繼續時，Lambda 會從頭開始執行您的程式碼。不過，已完成的步驟不會重新執行。Lambda 會從檢查點日誌重播其結果。這就是您的程式碼必須具有決定性的原因。

------
#### [ Python ]

**若要在主控台中修改程式碼**

1. 選擇 **程式碼** 標籤。

   在主控台的內建程式碼編輯器中，您應該會看到 Lambda 建立的函數程式碼。如果您在程式碼編輯器中沒看到 **lambda\$1function.py**，請在檔案總管中選取 **lambda\$1function.py**，如下圖所示。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/durable-python.png)

1. 將以下程式碼貼到 **lambda\$1function.py** 標籤中，替換 Lambda 建立的程式碼。

   ```
   from aws_durable_execution_sdk_python import (
       DurableContext,
       durable_execution,
       durable_step,
   )
   from aws_durable_execution_sdk_python.config import Duration
   
   @durable_step
   def validate_order(step_context, order_id):
       step_context.logger.info(f"Validating order {order_id}")
       return {"orderId": order_id, "status": "validated"}
   
   @durable_step
   def process_payment(step_context, order_id):
       step_context.logger.info(f"Processing payment for order {order_id}")
       return {"orderId": order_id, "status": "paid", "amount": 99.99}
   
   @durable_step
   def confirm_order(step_context, order_id):
       step_context.logger.info(f"Confirming order {order_id}")
       return {"orderId": order_id, "status": "confirmed"}
   
   @durable_execution
   def lambda_handler(event, context: DurableContext):
       order_id = event['orderId']
       
       # Step 1: Validate order
       validation_result = context.step(validate_order(order_id))
       
       # Step 2: Process payment
       payment_result = context.step(process_payment(order_id))
       
       # Wait for 10 seconds to simulate external confirmation
       context.wait(Duration.from_seconds(10))
       
       # Step 3: Confirm order
       confirmation_result = context.step(confirm_order(order_id))
           
       return {
           "orderId": order_id,
           "status": "completed",
           "steps": [validation_result, payment_result, confirmation_result]
       }
   ```

1. 在 **DEPLOY** 區段中，選擇**部署**以更新函數的程式碼：  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/getting-started-tutorial/deploy-console.png)

**了解您的耐用函數程式碼**  
在進行下一個步驟之前，讓我們先查看函數程式碼，並了解關鍵的耐久函數概念。
+ `@durable_execution` 裝飾項目：

  您的處理常式函數以 裝飾`@durable_execution`。此裝飾項目透過提供 `DurableContext` 物件和管理檢查點操作來實現持久的執行。
+ `@durable_step` 裝飾項目：

  每個步驟函數都以 裝飾`@durable_step`。此裝飾項目會將函數標記為建立檢查點的耐久步驟。
+ `DurableContext` 物件：

  您的函數會收到 ，而不是標準 Lambda 內容`DurableContext`。此物件提供建立檢查點等耐久操作的方法`wait()`，例如 `step()`和 。
+ 步驟和檢查點：

  每個`context.step()`呼叫都會在執行前後建立檢查點。如果您的函數中斷，則會從上次完成的檢查點繼續執行。函數不會重新執行完成的步驟。它會改用其儲存的結果。
+ 等待操作：

  `context.wait()` 呼叫會暫停執行，而不會耗用運算資源。當等待完成時，Lambda 會再次叫用您的函數，並重播檢查點日誌，以儲存的值取代已完成的步驟。
+ Python SDK 是同步的：

  請注意，Python SDK 不使用 `await`。所有耐用的操作都是同步方法呼叫。

------

## 使用主控台程式碼編輯器叫用耐用函數
<a name="get-started-invoke-durable-manually"></a>

未指定明確版本 （或發佈） 時，主控台會使用`$LATEST`版本限定詞叫用耐久函數。不過，為了決定性執行程式碼，您必須一律使用指向穩定版本的合格 ARN。

**發佈函數的版本**

1. 選擇**版本**索引標籤。

1. 選擇 **Publish new version (發佈新版本)**。

1. 針對**版本描述**，輸入 **Initial version**（選用）。

1. 選擇**發布**。

1. Lambda 會建立函數的第 1 版。請注意，函數 ARN 現在`:1`會在結尾包含 ，表示這是第 1 版。

現在建立測試事件以傳送至您的 函數。事件是包含訂單 ID 的 JSON 格式文件。

**若要建立測試事件**

1. 在主控台程式碼編輯器的 **TEST EVENTS** 區段中，選擇**建立測試事件**。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/getting-started-tutorial/test-event.png)

1. **Event Name (事件名稱)** 輸入 **myTestEvent**。

1. 在**事件 JSON** 區段中，將預設 JSON 取代為下列項目：

   ```
   {
     "orderId": "order-12345"
   }
   ```

1. 選擇**儲存**。

**測試您的耐用函數並檢視執行**

在主控台程式碼編輯器的 **TEST EVENTS** 區段中，選擇測試事件旁邊的執行圖示：

![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/getting-started-tutorial/run-test-event.png)


您的耐用函數會開始執行。因為它包含 10 秒的等待時間，初始調用會快速完成，而且函數會在等待期間之後繼續。您可以在**持久性執行索引標籤中檢視執行**進度。

**檢視您的耐久函數執行**

1. 選擇**持久性執行**索引標籤。

1. 在清單中尋找您的執行。執行會顯示目前狀態 （執行中、成功或失敗）。

1. 選擇執行 ID 以檢視詳細資訊，包括：
   + 顯示每個步驟完成時間的執行時間表
   + 檢查點歷史記錄
   + 等待期間
   + 步驟結果

您也可以在 CloudWatch Logs 中檢視函數的日誌，以查看每個步驟的主控台輸出。

**若要在 CloudWatch Logs 中檢視函數的調用記錄**

1. 開啟 CloudWatch 主控台的 [日誌群組](https://console.aws.amazon.com/cloudwatch/home#logs:) 頁面。

1. 為函數 (`/aws/lambda/myDurableFunction`) 選擇日誌群組名稱。

1. 向下捲動並選擇要查看的函數調用**日誌串流**。  
![\[\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/log-stream.png)

   您應該會看到每次叫用函數的日誌項目，包括初始執行和等待後的重播。

**注意**  
當您從 `DurableContext`（例如 `context.logger`或 `stepContext.logger`) 使用記錄器時，日誌也會出現在 Lambda 主控台的耐久執行和步驟檢視中。這些日誌可能需要一些時間才能載入。

## 清除
<a name="gettingstarted-durable-cleanup"></a>

當您完成使用範例耐用函數時，請將其刪除。您還可以刪除存放函數日誌的日誌群組，以及主控台建立的[執行角色](lambda-intro-execution-role.md)。

**若要刪除 Lambda 函數**

1. 開啟 Lambda 主控台中的 [函數頁面](https://console.aws.amazon.com/lambda/home#/functions)。

1. 選擇您建立的函數。

1. 選擇 **Actions** (動作)、**Delete** (刪除)。

1. 在文字輸入欄位中輸入 **confirm**，然後選擇**刪除**。

**刪除日誌群組**

1. 開啟 CloudWatch 主控台的 [日誌群組](https://console.aws.amazon.com/cloudwatch/home#logs:) 頁面。

1. 選取函數的日誌群組 (`/aws/lambda/myDurableFunction`)。

1. 選擇 **動作** 、**刪除日誌群組** 。

1. 在 **刪除日誌群組** 對話方塊中，選擇 **刪除** 。

**刪除執行角色**

1. 開啟 AWS Identity and Access Management (IAM) 主控台的角色[頁面](https://console.aws.amazon.com/iam/home?#/roles)。

1. 選取函數的執行角色，(例如 `myDurableFunction-role-31exxmpl`)。

1. 選擇 **刪除**。

1. 在**刪除角色**對話方塊中輸入角色名稱，然後選擇**刪除**。

## 其他資源和後續步驟
<a name="durable-getting-started-more-resources"></a>

現在您已使用 主控台建立並測試簡單的耐用函數，請採取下列後續步驟：
+ 了解耐用函數的常見使用案例，包括分散式交易、訂單處理和人工審核工作流程。請參閱[範例](durable-examples.md)。
+ 了解如何使用 CloudWatch 指標和執行歷史記錄監控持久的函數執行。請參閱[監控和偵錯](durable-monitoring.md)。
+ 了解如何同步和非同步叫用耐久函數，以及管理長時間執行的執行。請參閱[叫用耐用函數](durable-invoking.md)。
+ 遵循撰寫確定性程式碼、管理檢查點大小和最佳化成本的最佳實務。請參閱[最佳實務](durable-best-practices.md)。
+ 了解如何在本機和雲端測試耐用的函數。請參閱[測試耐用函數](durable-testing.md)。
+ 比較耐用的函數與 Step Functions，以了解每個方法最有效的時機。請參閱[耐用函數或 Step Functions](durable-step-functions.md)。

# 使用 部署和叫用 Lambda 耐用函數 AWS CLI
<a name="durable-getting-started-cli"></a>

使用 AWS CLI 以使用命令建立和部署 Lambda 耐用函數。此方法可讓您直接控制部署程序的每個步驟。

## 先決條件
<a name="durable-cli-prerequisites"></a>
+ 安裝及設定 AWS CLI。如需說明，請參閱[安裝 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ 使用函數程式碼和耐用的執行 SDK 建立部署套件。
+ 建立具有檢查點許可的 IAM 執行角色。

## 建立執行角色
<a name="durable-cli-create-role"></a>

建立具有基本 Lambda 執行和檢查點操作許可的 IAM 角色。

**建立執行角色**

1. 建立允許 Lambda 擔任角色的信任政策文件。將此儲存為 `trust-policy.json`：

   ```
   {
     "Version": "2012-10-17",		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

1. 建立角色：

   ```
   aws iam create-role \
     --role-name durable-function-role \
     --assume-role-policy-document file://trust-policy.json
   ```

1. 連接檢查點操作和基本執行的耐久執行政策：

   ```
   aws iam attach-role-policy \
     --role-name durable-function-role \
     --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
   ```

`AWSLambdaBasicDurableExecutionRolePolicy` 受管政策包含檢查點操作 (`lambda:CheckpointDurableExecutions` 和 `lambda:GetDurableExecutionState`) 和基本 Lambda 執行所需的許可。

## 建立耐用的 函數
<a name="durable-cli-create-function"></a>

使用 `--durable-config` 參數建立您的耐用函數。

**建立耐用的函數**

1. 將具有相依性的函數程式碼封裝至 .zip 檔案：

   ```
   zip -r function.zip index.mjs node_modules/
   ```

1. 建立已啟用持久性執行的函數：

   ```
   aws lambda create-function \
     --function-name myDurableFunction \
     --runtime nodejs22.x \
     --role arn:aws:iam::123456789012:role/durable-function-role \
     --handler index.handler \
     --zip-file fileb://function.zip \
     --durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'
   ```

**注意**  
您只能在建立函數時啟用持久性執行。您無法在現有函數上啟用它。

**注意**  
目前 Java （預覽） 中的耐用函數只能透過容器映像建立。如需從容器映像建立持久性函數的詳細資訊，請參閱[持久性函數支援的執行時間](durable-supported-runtimes.md)。

## 發佈版本
<a name="durable-cli-publish-version"></a>

雖然可以使用`$LATEST`版本限定詞叫用耐久的函數，但您必須一律使用指向穩定版本的合格 ARN，以確保程式碼的確定性執行。

```
aws lambda publish-version \
  --function-name myDurableFunction \
  --description "Initial version"
```

命令會傳回版本 ARN。請注意 ARN 結尾的版本編號 （例如 `:1`)。

或者，建立指向 版本的別名：

```
aws lambda create-alias \
  --function-name myDurableFunction \
  --name prod \
  --function-version 1
```

## 叫用耐用的 函數
<a name="durable-cli-invoke"></a>

使用合格的 ARN （版本或別名） 叫用您的耐久函數。

**注意**  
**等冪叫用：**若要防止重試失敗叫用時重複執行，您可以提供執行名稱，確保at-most-once。如需詳細資訊，請參閱[等冪性](durable-execution-idempotency.md)。

**同步調用**  
對於在 15 分鐘內完成的執行，請使用同步調用：

```
aws lambda invoke \
  --function-name myDurableFunction:1 \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

或使用別名：

```
aws lambda invoke \
  --function-name myDurableFunction:prod \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

**非同步調用**  
對於長時間執行的執行，請使用非同步調用：

```
aws lambda invoke \
  --function-name myDurableFunction:prod \
  --invocation-type Event \
  --payload '{"orderId": "order-12345"}' \
  --cli-binary-format raw-in-base64-out \
  response.json
```

使用非同步調用時，Lambda 會立即傳回 。函數會繼續在背景執行。

**注意**  
您可以在 主控台中使用 `$LATEST`進行原型設計和測試。對於生產工作負載，請使用發佈的版本或別名。

## 管理持久的執行
<a name="durable-cli-manage-executions"></a>

使用下列命令來管理和監控耐用的函數執行。

**列出執行**  
列出耐用函數的所有執行：

```
aws lambda list-durable-executions-by-function \
  --function-name myDurableFunction
```

**取得執行詳細資訊**  
取得特定執行的詳細資訊：

```
aws lambda get-durable-execution \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

**取得執行歷史記錄**  
檢視執行的檢查點歷史記錄：

```
aws lambda get-durable-execution-history \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

**停止執行**  
停止執行中的耐久執行：

```
aws lambda stop-durable-execution \
  --durable-execution-arn arn:aws:lambda:us-east-1:123456789012:function:myDurableFunction:my-function-version/durable-execution/my-execution-name/my-execution-id
```

## 更新函數程式碼
<a name="durable-cli-update-function"></a>

更新您的耐用函數程式碼並發佈新版本：

**更新和發佈新版本**

1. 更新函數程式碼：

   ```
   aws lambda update-function-code \
     --function-name myDurableFunction \
     --zip-file fileb://function.zip
   ```

1. 等待更新完成：

   ```
   aws lambda wait function-updated \
     --function-name myDurableFunction
   ```

1. 發佈新版本：

   ```
   aws lambda publish-version \
     --function-name myDurableFunction \
     --description "Updated order processing logic"
   ```

1. 更新別名以指向新版本：

   ```
   aws lambda update-alias \
     --function-name myDurableFunction \
     --name prod \
     --function-version 2
   ```

**重要**  
執行中的執行會繼續使用其啟動的版本。新的調用使用更新的別名版本。

## 檢視函數日誌
<a name="durable-cli-view-logs"></a>

在 CloudWatch Logs 中檢視耐用函數的日誌：

```
aws logs tail /aws/lambda/myDurableFunction --follow
```

篩選特定執行的日誌：

```
aws logs filter-log-events \
  --log-group-name /aws/lambda/myDurableFunction \
  --filter-pattern "exec-abc123"
```

## 清除資源
<a name="durable-cli-cleanup"></a>

刪除您的耐用函數和相關聯的資源：

```
# Delete the function
aws lambda delete-function --function-name myDurableFunction

# Delete the IAM role policies
aws iam detach-role-policy \
  --role-name durable-function-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

aws iam detach-role-policy \
  --role-name durable-function-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy

# Delete the role
aws iam delete-role --role-name durable-function-role
```

## 後續步驟
<a name="durable-cli-next-steps"></a>

使用 部署您的耐用函數之後 AWS CLI：
+ 使用 `list-durable-executions`和 `get-durable-execution`命令監控執行
+ 檢視 AWS CloudTrail 資料事件中的檢查點操作
+ 針對執行失敗或長時間執行的執行設定 CloudWatch 警示
+ 使用 shell 指令碼或 CI/CD 管道自動化部署

如需 Lambda AWS CLI 命令的詳細資訊，請參閱 [AWS CLI 命令參考](https://docs.aws.amazon.com/cli/latest/reference/lambda/index.html)。

# 使用 Infrastructure as Code 部署 Lambda 耐用函數
<a name="durable-getting-started-iac"></a>

您可以使用 Infrastructure as Code (IaC) 工具部署 Lambda 耐用函數 AWS CloudFormation，例如 AWS CDK AWS Serverless Application Model， 或 Terraform。這些工具可讓您在程式碼中定義函數、執行角色和許可，讓部署可重複且受版本控制。

這三個工具都需要您：
+ 在函數上啟用持久性執行
+ 將檢查點許可授予執行角色
+ 發佈版本或建立別名 （持久性函數需要合格的 ARNs)

## ZIP 中的耐用函數
<a name="durable-iac-zip"></a>

### AWS CloudFormation
<a name="durable-iac-cloudformation"></a>

使用 在範本中 CloudFormation 定義您的耐用函數。下列範例會建立具有所需許可的耐用函數。

```
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda durable function example

Resources:
  DurableFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'		 	 	 
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy

  DurableFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: myDurableFunction
      Runtime: nodejs22.x
      Handler: index.handler
      Role: !GetAtt DurableFunctionRole.Arn
      Code:
        ZipFile: |
          // Your durable function code here
          export const handler = async (event, context) => {
            return { statusCode: 200 };
          };
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7

  DurableFunctionVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref DurableFunction
      Description: Initial version

  DurableFunctionAlias:
    Type: AWS::Lambda::Alias
    Properties:
      FunctionName: !Ref DurableFunction
      FunctionVersion: !GetAtt DurableFunctionVersion.Version
      Name: prod

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunctionAlias
```

**部署範本**

```
aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name my-durable-function-stack \
  --capabilities CAPABILITY_IAM
```

### AWS CDK
<a name="durable-iac-cdk"></a>

AWS CDK 可讓您使用程式設計語言定義基礎設施。下列範例示範如何使用 TypeScript 和 Python 建立耐用的函數。

------
#### [ TypeScript ]

```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DurableFunctionStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create the durable function
    const durableFunction = new lambda.Function(this, 'DurableFunction', {
      runtime: lambda.Runtime.NODEJS_22_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda'),
      functionName: 'myDurableFunction',
      durableConfig: { executionTimeout: Duration.hours(1), retentionPeriod: Duration.days(30) },
    });

    // Create version and alias
    const version = durableFunction.currentVersion;
    const alias = new lambda.Alias(this, 'ProdAlias', {
      aliasName: 'prod',
      version: version,
    });

    // Output the alias ARN
    new cdk.CfnOutput(this, 'FunctionAliasArn', {
      value: alias.functionArn,
      description: 'Use this ARN to invoke the durable function',
    });
  }
}
```

------
#### [ Python ]

```
from aws_cdk import (
    Stack,
    aws_lambda as lambda_,
    aws_iam as iam,
    CfnOutput,
)
from constructs import Construct

class DurableFunctionStack(Stack):
    def __init__(self, scope: Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        # Create the durable function
        durable_function = lambda_.Function(
            self, 'DurableFunction',
            runtime=lambda_.Runtime.NODEJS_22_X,
            handler='index.handler',
            code=lambda_.Code.from_asset('lambda'),
            function_name='myDurableFunction',
            durable_execution={execution_timeout: Duration.hours(1), retention_period: Duration.days(30)}
        )

        # Add durable execution managed policy for checkpoint permissions
        durable_function.role.add_managed_policy(
            iam.ManagedPolicy.from_aws_managed_policy_name('service-role/AWSLambdaBasicDurableExecutionRolePolicy')
        )

        # Create version and alias
        version = durable_function.current_version
        alias = lambda_.Alias(
            self, 'ProdAlias',
            alias_name='prod',
            version=version
        )

        # Output the alias ARN
        CfnOutput(
            self, 'FunctionAliasArn',
            value=alias.function_arn,
            description='Use this ARN to invoke the durable function'
        )
```

------

**部署 CDK 堆疊**

```
cdk deploy
```

### AWS Serverless Application Model
<a name="durable-iac-sam"></a>

AWS SAM 簡化無伺服器應用程式的 CloudFormation 範本。下列範本使用 建立耐久的 函數 AWS SAM。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda durable function with SAM

Resources:
  DurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myDurableFunction
      Runtime: nodejs22.x
      Handler: index.handler
      CodeUri: ./src
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
      AutoPublishAlias: prod

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunction.Alias
```

**部署 SAM 範本**

```
sam build
sam deploy --guided
```

### Terraform
<a name="durable-iac-terraform"></a>

Terraform 是支援 AWS 資源的熱門開放原始碼 IaC 工具。下列範例使用 AWS 提供者版本 6.25.0 或更新版本，使用 Terraform 建立耐久的函數。

```
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 6.25.0"
    }
  }
}

provider "aws" {
  region = "us-east-2"
}

# IAM Role for Lambda Function
resource "aws_iam_role" "lambda_role" {
  name = "durable-function-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

# Attach durable execution policy for checkpoint operations
resource "aws_iam_role_policy_attachment" "lambda_durable" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"
  role       = aws_iam_role.lambda_role.name
}

# Lambda Function with Durable Execution enabled
resource "aws_lambda_function" "durable_function" {
  filename      = "function.zip"
  function_name = "myDurableFunction"
  role          = aws_iam_role.lambda_role.arn
  handler       = "index.handler"
  runtime       = "nodejs22.x"
  timeout       = 30
  memory_size   = 512

  durable_config {
    execution_timeout = 900
    retention_period  = 7
  }
}

# Publish a version
resource "aws_lambda_alias" "prod" {
  name             = "prod"
  function_name    = aws_lambda_function.durable_function.function_name
  function_version = aws_lambda_function.durable_function.version
}

output "function_arn" {
  description = "ARN of the Lambda function"
  value       = aws_lambda_function.durable_function.arn
}

output "alias_arn" {
  description = "ARN of the function alias (use this for invocations)"
  value       = aws_lambda_alias.prod.arn
}
```

**使用 Terraform 部署**

```
terraform init
terraform plan
terraform apply
```

**注意**  
Lambda 耐久函數的 Terraform 支援需要 AWS 提供者版本 6.25.0 或更新版本。如果您使用的是舊版，請更新您的提供者版本。

## 來自 OCI 容器映像的耐用函數
<a name="durable-iac-oci"></a>

您也可以根據容器映像建立耐久函數。如需如何建置容器映像的指示，請參閱[耐用函數支援的執行時間](durable-supported-runtimes.md)。

### AWS CDK
<a name="durable-iac-oci-cdk"></a>

AWS CDK 可讓您使用程式設計語言定義基礎設施。下列範例示範如何從容器映像使用 TypeScript 建立耐用的函數。

```
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DurableFunctionStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create the durable function
    const durableFunction = new lambda.DockerImageFunction(this, 'DurableFunction', {
      code: lambda.DockerImageCode.fromImageAsset('./lambda', {
        platform: cdk.aws_ecr_assets.Platform.LINUX_AMD64,
      }),
      functionName: 'myDurableFunction',
      memorySize: 512,
      timeout: cdk.Duration.seconds(30),
      durableConfig: { executionTimeout: cdk.Duration.hours(1), retentionPeriod: cdk.Duration.days(30) },
    });

    // Create version and alias
    const version = durableFunction.currentVersion;
    const alias = new lambda.Alias(this, 'ProdAlias', {
      aliasName: 'prod',
      version: version,
    });

    // Output the alias ARN
    new cdk.CfnOutput(this, 'FunctionAliasArn', {
      value: alias.functionArn,
      description: 'Use this ARN to invoke the durable function',
    });
  }
}
```

**部署 CDK 堆疊**

```
cdk deploy
```

### AWS Serverless Application Model
<a name="durable-iac-oci-sam"></a>

AWS SAM 簡化無伺服器應用程式的 CloudFormation 範本。下列範本使用 建立耐久的 函數 AWS SAM。

```
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda durable function with SAM

Resources:
  DurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: myDurableFunction
      PackageType: Image
      ImageUri: ./src
      DurableConfig:
        ExecutionTimeout: 3600
        RetentionPeriodInDays: 7
      Policies:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy
      AutoPublishAlias: prod
    Metadata:
      DockerTag: latest
      DockerContext: ./src
      Dockerfile: Dockerfile

Outputs:
  FunctionArn:
    Description: Durable function ARN
    Value: !GetAtt DurableFunction.Arn
  AliasArn:
    Description: Function alias ARN (use this for invocations)
    Value: !Ref DurableFunction.Alias
```

**部署 SAM 範本**

```
sam build
sam deploy --guided
```

## 常見的組態模式
<a name="durable-iac-common-patterns"></a>

無論您使用哪種 IaC 工具，請遵循這些模式以取得耐用的函數：

**啟用持久性執行**  
在函數上設定 `DurableConfig` 屬性以啟用持久性執行。此屬性僅在建立函數時可用。您無法在現有函數上啟用持久性執行。

**授予檢查點許可**  
將 `AWSLambdaBasicDurableExecutionRolePolicy`受管政策連接至執行角色。此政策包含必要的 `lambda:CheckpointDurableExecutions`和 `lambda:GetDurableExecutionState`許可。

**使用合格的 ARNs**  
為您的函數建立版本或別名。耐用的函數需要合格的 ARNs（具有版本或別名） 才能叫用。在 `AutoPublishAlias`中使用 AWS SAM 或在 CloudFormation AWS CDK和 Terraform 中建立明確版本。

**套件相依性**  
在您的部署套件中包含耐用的執行 SDK。對於 Node.js，安裝 `@aws/durable-execution-sdk-js`。針對 Python，安裝 `aws-durable-execution-sdk-python`。

## 後續步驟
<a name="durable-iac-next-steps"></a>

部署您的耐用函數之後：
+ 使用合格的 ARN （版本或別名） 測試您的函數
+ 在持久性執行索引標籤下的 Lambda 主控台中監控執行進度
+ 檢視 AWS CloudTrail 資料事件中的檢查點操作
+ 檢閱 CloudWatch Logs 以取得函數輸出和重播行為

如需使用 IaC 工具部署 Lambda 函數的詳細資訊，請參閱：
+ [CloudFormation AWS::Lambda::Function 參考](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html)
+ [AWS CDK Lambda 模組文件](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html)
+ [AWS SAM 開發人員指南](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html)

# 耐用函數或 Step Functions
<a name="durable-step-functions"></a>

Lambda 耐用函數和 透過自動狀態管理和故障復原 AWS Step Functions 啟用可靠的工作流程協同運作。它們提供不同的開發人員偏好設定和架構模式。耐用函數已針對 Lambda 內的應用程式開發進行最佳化，而 Step Functions 則是針對跨 AWS 服務的工作流程協同運作而建置。

## 何時使用耐用函數
<a name="durable-sfn-when-durable"></a>

在以下情況下使用耐用的函數：
+ 您的團隊偏好標準程式設計語言和熟悉的開發工具
+ 您的應用程式邏輯主要位於 Lambda 函數內
+ 您想要精細控制程式碼中的執行狀態
+ 您正在建置以 Lambda 為中心的應用程式，在工作流程和商業邏輯之間緊密結合
+ 您想要快速反覆運算，而無需在程式碼和視覺化/JSON 設計工具之間切換

## 何時使用 Step Functions
<a name="durable-sfn-when-step"></a>

在下列情況下使用 Step Functions：
+ 您需要視覺化工作流程呈現才能跨團隊可見性
+ 您正在協調多個 AWS 服務，並希望在沒有自訂 SDK 程式碼的情況下進行原生整合
+ 您需要零維護基礎設施 （無需修補、執行時間更新）
+ 非技術利益相關者需要了解和驗證工作流程邏輯

## 決策架構
<a name="durable-sfn-decision-framework"></a>

使用下列問題來判斷哪些服務符合您的使用案例：
+ **您的主要重點是什麼？** Lambda 中的應用程式開發 → 耐用函數。跨 AWS → Step Functions 的工作流程協同運作。
+ **您偏好的程式設計模型是什麼？** 標準程式設計語言 → 耐用的函數。以圖形為基礎的 DSL 或視覺化設計工具 → Step Functions。
+ **涉及多少 AWS 服務？** 主要是 Lambda → 耐用函數。多項 AWS 服務 → Step Functions。
+ **您使用哪些開發工具？** Lambda 開發人員體驗、IDE 搭配 LLM 代理程式、程式設計語言特定單元測試架構 AWS SAM、 AWS CDK AWS Toolkit → 耐用函數。視覺化工作流程建置器， AWS CDK 以建立工作流程模型 → Step Functions。
+ **誰管理基礎設施？** 需要 Lambda 內的彈性 → 耐用函數。想要完全受管、零維護 → Step Functions。

## 功能比較
<a name="durable-sfn-comparison"></a>

下表比較 Step Functions 和 Lambda 耐用函數之間的主要功能：


| 功能 | AWS Step Functions | Lambda 耐用函數 | 
| --- | --- | --- | 
| 主要焦點 | 跨 的工作流程協同運作 AWS | Lambda 中的應用程式開發 | 
| 服務類型 | 獨立的專用工作流程服務 | 在 Lambda 中執行 | 
| 程式設計模型 | 以圖形為基礎的 Amazon States 語言 DSL 或 AWS CDK | 標準程式設計語言 (JavaScript/TypeScript、Python) | 
| 開發工具 | 主控台 / AWS Toolkit IDE 延伸中的視覺化建置器， AWS CDK | IDE 和 LLM 代理程式內的 Lambda DX、單元測試架構 AWS SAM、 AWS Toolkit IDE 延伸模組 | 
| 整合 | 220\$1 AWS 服務、16k APIs | Lambda 事件驅動的程式設計模型延伸 （事件來源） | 
| 管理 | 全受管、執行期無關、零維護 （無修補、執行期更新） | 在 Lambda 環境中受管 | 
| 最適合 | 業務流程和 IT 自動化、資料處理、AI 工作流程 | 分散式交易、具狀態應用程式邏輯、函數協同運作、資料處理、AI 工作流程 | 

## 混合架構
<a name="durable-sfn-hybrid"></a>

許多應用程式都受益於使用這兩種服務。常見的模式是對 Lambda 內的應用程式層級邏輯使用耐久的函數，而 Step Functions 則協調 Lambda 函數以外跨多個 AWS 服務的高階工作流程。

## 遷移考量事項
<a name="durable-sfn-migration"></a>

**啟動簡單、不斷發展的複雜：**從 Lambda 工作流程的耐久函數開始。當您需要多服務協同運作或視覺化工作流程設計時，請新增 Step Functions。

**現有的 Step Functions 使用者：**為已建立的跨服務工作流程保留 Step Functions。對於需要可靠性的新 Lambda 應用程式邏輯，請考慮使用耐用的函數。

## 相關資源
<a name="durable-sfn-related"></a>
+ [Lambda 耐用函數](durable-functions.md)
+ [使用 Step Functions 協調 Lambda 函數](with-step-functions.md)
+ [耐用函數入門](durable-getting-started.md)

# 範例和使用案例
<a name="durable-examples"></a>

Lambda 耐用函數可讓您使用步驟和等待等耐用操作來建置容錯的多步驟應用程式。透過自動檢查點和檢查點重播模型。 執行會在失敗後從頭開始重新啟動，但會略過已完成的檢查點，您的函數可以從失敗中復原並繼續執行，而不會遺失進度。

## 短期容錯程序
<a name="durable-examples-short-lived"></a>

使用耐用的 函數來建置通常在幾分鐘內完成的可靠操作。雖然這些程序比長時間執行的工作流程短，但它們仍然受益於分散式系統的自動檢查點和容錯能力。耐用的 函數有助於確保您的多步驟程序即使在個別服務呼叫失敗時也能順利完成，而不需要複雜的錯誤處理或狀態管理程式碼。

常見案例包括飯店預訂系統、餐廳預訂平台、共乘行程請求、事件票證購買和 SaaS 訂閱升級。這些案例有共同的特性：多個必須一起完成的服務呼叫、在暫時性故障時自動重試的需求，以及跨分散式系統維持一致狀態的需求。

### 跨微型服務的分散式交易
<a name="durable-examples-distributed-transactions"></a>

使用自動轉返失敗，協調跨多項服務的付款、庫存和運送。每個服務操作都會包裝在步驟中，確保交易可以在服務失敗時從任何時間點復原。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, amount, items } = event;
    
    // Reserve inventory across multiple warehouses
    const inventory = await context.step("reserve-inventory", async () => {
      return await inventoryService.reserve(items);
    });
    
    // Process payment
    const payment = await context.step("process-payment", async () => {
      return await paymentService.charge(amount);
    });
    
    // Create shipment
    const shipment = await context.step("create-shipment", async () => {
      return await shippingService.createShipment(orderId, inventory);
    });
    
    return { orderId, status: 'completed', shipment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    amount = event['amount']
    items = event['items']
    
    # Reserve inventory across multiple warehouses
    inventory = context.step(
        lambda _: inventory_service.reserve(items),
        name='reserve-inventory'
    )
    
    # Process payment
    payment = context.step(
        lambda _: payment_service.charge(amount),
        name='process-payment'
    )
    
    # Create shipment
    shipment = context.step(
        lambda _: shipping_service.create_shipment(order_id, inventory),
        name='create-shipment'
    )
    
    return {'orderId': order_id, 'status': 'completed', 'shipment': shipment}
```

------

如果任何步驟失敗，函數會自動從最後一個成功的檢查點重試。即使付款處理暫時失敗，庫存保留仍會持續存在。當函數重試時，它會略過已完成的庫存步驟，並直接進行付款處理。這可消除重複的保留，並確保分散式系統的狀態一致。

### 使用多個步驟進行訂單處理
<a name="durable-examples-order-processing"></a>

透過驗證、付款授權、庫存分配和自動重試和復原的履行來處理訂單。每個步驟都會經過檢查點，確保即使個別步驟失敗並重試，訂單仍會繼續。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, customerId, items } = event;
    
    // Validate order details
    const validation = await context.step("validate-order", async () => {
      const customer = await customerService.validate(customerId);
      const itemsValid = await inventoryService.validateItems(items);
      return { customer, itemsValid };
    });
    
    if (!validation.itemsValid) {
      return { orderId, status: 'rejected', reason: 'invalid_items' };
    }
    
    // Authorize payment
    const authorization = await context.step("authorize-payment", async () => {
      return await paymentService.authorize(
        validation.customer.paymentMethod,
        calculateTotal(items)
      );
    });
    
    // Allocate inventory
    const allocation = await context.step("allocate-inventory", async () => {
      return await inventoryService.allocate(items);
    });
    
    // Fulfill order
    const fulfillment = await context.step("fulfill-order", async () => {
      return await fulfillmentService.createShipment({
        orderId,
        items: allocation.allocatedItems,
        address: validation.customer.shippingAddress
      });
    });
    
    return {
      orderId,
      status: 'completed',
      trackingNumber: fulfillment.trackingNumber
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    customer_id = event['customerId']
    items = event['items']
    
    # Validate order details
    def validate_order(_):
        customer = customer_service.validate(customer_id)
        items_valid = inventory_service.validate_items(items)
        return {'customer': customer, 'itemsValid': items_valid}
    
    validation = context.step(validate_order, name='validate-order')
    
    if not validation['itemsValid']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'invalid_items'}
    
    # Authorize payment
    authorization = context.step(
        lambda _: payment_service.authorize(
            validation['customer']['paymentMethod'],
            calculate_total(items)
        ),
        name='authorize-payment'
    )
    
    # Allocate inventory
    allocation = context.step(
        lambda _: inventory_service.allocate(items),
        name='allocate-inventory'
    )
    
    # Fulfill order
    fulfillment = context.step(
        lambda _: fulfillment_service.create_shipment({
            'orderId': order_id,
            'items': allocation['allocatedItems'],
            'address': validation['customer']['shippingAddress']
        }),
        name='fulfill-order'
    )
    
    return {
        'orderId': order_id,
        'status': 'completed',
        'trackingNumber': fulfillment['trackingNumber']
    }
```

------

此模式可確保訂單永遠不會卡在中繼狀態。如果驗證失敗，則會在付款授權之前拒絕訂單。如果付款授權失敗，則不會配置庫存。每個步驟都以上一個步驟為基礎，並具有自動重試和復原。

**注意**  
條件式檢查`if (!validation.itemsValid)`位於步驟之外，並在重播期間重新執行。這是安全的，因為它是確定性的，它永遠會在相同的驗證物件下產生相同的結果。

## 長時間執行的程序
<a name="durable-examples-long-running"></a>

對跨越數小時、數天或數週的程序使用耐久的函數。等待操作會暫停執行，而不會產生運算費用，讓長時間執行的程序符合成本效益。在等待期間，您的函數會停止執行，Lambda 會回收執行環境。恢復時，Lambda 會再次叫用您的函數，並從最後一個檢查點重播。

此執行模型讓耐用的函數非常適合需要長時間暫停的程序，無論是等待人工決策、外部系統回應、排程處理時段還是以時間為基礎的延遲。您只需支付作用中的運算時間，而非等待。

常見案例包括文件核准程序、排程批次處理、多日加入程序、訂閱試用程序，以及延遲通知系統。這些案例有共同的特性：以小時或天為單位測量的延長等待期、在這些等待期間維持執行狀態的需求，以及支付閒置運算時間的成本敏感要求是禁止的。

### Human-in-the-loop核准
<a name="durable-examples-human-in-loop"></a>

暫停執行文件檢閱、核准或決策，同時維持執行狀態。函數會等待外部回呼而不耗用資源，在收到核准時自動繼續。

此模式對於需要人工判斷或外部驗證的程序至關重要。函數會在回呼點暫停，等待時不會產生運算費用。當有人透過 API 提交其決策時，Lambda 會再次調用您的函數，並從檢查點重播，繼續執行核准結果。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { documentId, reviewers } = event;
    
    // Step 1: Prepare document for review
    const prepared = await context.step("prepare-document", async () => {
      return await documentService.prepare(documentId);
    });
    
    // Step 2: Request approval with callback
    const approval = await context.waitForCallback(
      "approval-callback",
      async (callbackId) => {
        await notificationService.sendApprovalRequest({
          documentId,
          reviewers,
          callbackId,
          expiresIn: 86400
        });
      },
      {
        timeout: { seconds: 86400 }
      }
    );
    
    // Function resumes here when approval is received
    if (approval?.approved) {
      const finalized = await context.step("finalize-document", async () => {
        return await documentService.finalize(documentId, approval.comments);
      });
      
      return {
        status: 'approved',
        documentId,
        finalizedAt: finalized.timestamp
      };
    }
    
    // Handle rejection
    await context.step("archive-rejected", async () => {
      await documentService.archive(documentId, approval?.reason);
    });
    
    return {
      status: 'rejected',
      documentId,
      reason: approval?.reason
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, WaitConfig

@durable_execution
def lambda_handler(event, context: DurableContext):
    document_id = event['documentId']
    reviewers = event['reviewers']
    
    # Step 1: Prepare document for review
    prepared = context.step(
        lambda _: document_service.prepare(document_id),
        name='prepare-document'
    )
    
    # Step 2: Request approval with callback
    def send_approval_request(callback_id):
        notification_service.send_approval_request({
            'documentId': document_id,
            'reviewers': reviewers,
            'callbackId': callback_id,
            'expiresIn': 86400
        })
    
    approval = context.wait_for_callback(
        send_approval_request,
        name='approval-callback',
        config=WaitConfig(timeout=86400)
    )
    
    # Function resumes here when approval is received
    if approval and approval.get('approved'):
        finalized = context.step(
            lambda _: document_service.finalize(document_id, approval.get('comments')),
            name='finalize-document'
        )
        
        return {
            'status': 'approved',
            'documentId': document_id,
            'finalizedAt': finalized['timestamp']
        }
    
    # Handle rejection
    context.step(
        lambda _: document_service.archive(document_id, approval.get('reason') if approval else None),
        name='archive-rejected'
    )
    
    return {
        'status': 'rejected',
        'documentId': document_id,
        'reason': approval.get('reason') if approval else None
    }
```

------

收到回呼且您的函數繼續時，它會從頭開始重播。prepare-document 步驟會立即傳回其檢查點結果。waitForCallback 操作也會立即傳回預存核准結果，而不是再次等待。接著執行會繼續完成或封存步驟。

### 多階段資料管道
<a name="durable-examples-data-pipelines"></a>

透過擷取、轉換和載入階段處理大型資料集，並在階段之間使用檢查點。每個階段可能需要數小時才能完成，而檢查點可讓管道在中斷時從任何階段繼續。

此模式非常適合 ETL 工作流程、資料遷移或批次處理任務，而您需要在其中使用復原點分階段處理資料。如果階段失敗，管道會從上次完成的階段繼續，而不是從頭開始重新啟動。您也可以使用等待操作在階段之間暫停；遵守速率限制、等待下游系統就緒，或在離峰時間排程處理。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { datasetId, batchSize } = event;
    
    // Stage 1: Extract data from source
    const extracted = await context.step("extract-data", async () => {
      const records = await sourceDatabase.extractRecords(datasetId);
      return { recordCount: records.length, records };
    });
    
    // Wait 5 minutes to respect source system rate limits
    await context.wait({ seconds: 300 });
    
    // Stage 2: Transform data in batches
    const transformed = await context.step("transform-data", async () => {
      const batches = chunkArray(extracted.records, batchSize);
      const results = [];
      
      for (const batch of batches) {
        const transformed = await transformService.processBatch(batch);
        results.push(transformed);
      }
      
      return { batchCount: batches.length, results };
    });
    
    // Wait until off-peak hours (e.g., 2 AM)
    const now = new Date();
    const targetHour = 2;
    const msUntilTarget = calculateMsUntilHour(now, targetHour);
    await context.wait({ seconds: Math.floor(msUntilTarget / 1000) });
    
    // Stage 3: Load data to destination
    const loaded = await context.step("load-data", async () => {
      let loadedCount = 0;
      
      for (const result of transformed.results) {
        await destinationDatabase.loadBatch(result);
        loadedCount += result.length;
      }
      
      return { loadedCount };
    });
    
    // Stage 4: Verify and finalize
    const verified = await context.step("verify-pipeline", async () => {
      const verification = await destinationDatabase.verifyRecords(datasetId);
      await pipelineService.markComplete(datasetId, verification);
      return verification;
    });
    
    return {
      datasetId,
      recordsProcessed: extracted.recordCount,
      batchesProcessed: transformed.batchCount,
      recordsLoaded: loaded.loadedCount,
      verified: verified.success
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution
from datetime import datetime

@durable_execution
def lambda_handler(event, context: DurableContext):
    dataset_id = event['datasetId']
    batch_size = event['batchSize']
    
    # Stage 1: Extract data from source
    def extract_data(_):
        records = source_database.extract_records(dataset_id)
        return {'recordCount': len(records), 'records': records}
    
    extracted = context.step(extract_data, name='extract-data')
    
    # Wait 5 minutes to respect source system rate limits
    context.wait(300)
    
    # Stage 2: Transform data in batches
    def transform_data(_):
        batches = chunk_array(extracted['records'], batch_size)
        results = []
        
        for batch in batches:
            transformed = transform_service.process_batch(batch)
            results.append(transformed)
        
        return {'batchCount': len(batches), 'results': results}
    
    transformed = context.step(transform_data, name='transform-data')
    
    # Wait until off-peak hours (e.g., 2 AM)
    now = datetime.now()
    target_hour = 2
    ms_until_target = calculate_ms_until_hour(now, target_hour)
    context.wait(ms_until_target // 1000)
    
    # Stage 3: Load data to destination
    def load_data(_):
        loaded_count = 0
        
        for result in transformed['results']:
            destination_database.load_batch(result)
            loaded_count += len(result)
        
        return {'loadedCount': loaded_count}
    
    loaded = context.step(load_data, name='load-data')
    
    # Stage 4: Verify and finalize
    def verify_pipeline(_):
        verification = destination_database.verify_records(dataset_id)
        pipeline_service.mark_complete(dataset_id, verification)
        return verification
    
    verified = context.step(verify_pipeline, name='verify-pipeline')
    
    return {
        'datasetId': dataset_id,
        'recordsProcessed': extracted['recordCount'],
        'batchesProcessed': transformed['batchCount'],
        'recordsLoaded': loaded['loadedCount'],
        'verified': verified['success']
    }
```

------

每個階段都會包裝在步驟中，建立檢查點，允許管道在中斷時從任何階段繼續。擷取和轉換之間的 5 分鐘等待會遵守來源系統速率限制，而不會耗用運算資源，而等到上午 2 點才會在離峰時間排程昂貴的負載操作。

**注意**  
`new Date()` 呼叫和`calculateMsUntilHour()`函數位於步驟之外，並在重播期間重新執行。對於必須在重播之間保持一致的時間型操作，請計算步驟內的時間戳記，或僅將其用於等待持續時間 （檢查點）。

## 進階模式
<a name="durable-examples-advanced"></a>

使用持久性函數來建置複雜的多步驟應用程式，結合多個持久性操作、平行執行、陣列處理、條件式邏輯和輪詢。這些模式可讓您建置複雜的應用程式來協調許多任務，同時維持容錯能力和自動復原。

進階模式超過簡單的循序步驟。您可以與 同時執行操作`parallel()`、使用 處理陣列`map()`、使用 等待外部條件`waitForCondition()`，並結合這些基本概念來建置可靠的應用程式。每個耐用的操作都會建立自己的檢查點，因此您的應用程式可以在中斷時從任何時間點復原。

### 使用者加入程序
<a name="durable-examples-user-onboarding"></a>

引導使用者完成註冊、電子郵件驗證、設定檔設定，以及具有重試處理的初始組態。此範例結合了循序步驟、回呼和條件式邏輯，以建立完整的加入程序。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { userId, email } = event;
    
    // Step 1: Create user account
    const user = await context.step("create-account", async () => {
      return await userService.createAccount(userId, email);
    });
    
    // Step 2: Send verification email
    await context.step("send-verification", async () => {
      return await emailService.sendVerification(email);
    });
    
    // Step 3: Wait for email verification (up to 48 hours)
    const verified = await context.waitForCallback(
      "email-verification",
      async (callbackId) => {
        await notificationService.sendVerificationLink({
          email,
          callbackId,
          expiresIn: 172800
        });
      },
      {
        timeout: { seconds: 172800 }
      }
    );
    
    if (!verified) {
      await context.step("send-reminder", async () => {
        await emailService.sendReminder(email);
      });
      
      return {
        status: "verification_timeout",
        userId,
        message: "Email verification not completed within 48 hours"
      };
    }
    
    // Step 4: Initialize user profile in parallel
    const setupResults = await context.parallel("profile-setup", [
      async (ctx: DurableContext) => {
        return await ctx.step("create-preferences", async () => {
          return await preferencesService.createDefaults(userId);
        });
      },
      
      async (ctx: DurableContext) => {
        return await ctx.step("setup-notifications", async () => {
          return await notificationService.setupDefaults(userId);
        });
      },
      
      async (ctx: DurableContext) => {
        return await ctx.step("create-welcome-content", async () => {
          return await contentService.createWelcome(userId);
        });
      }
    ]);
    
    // Step 5: Send welcome email
    await context.step("send-welcome", async () => {
      const [preferences, notifications, content] = setupResults.getResults();
      return await emailService.sendWelcome({
        email,
        preferences,
        notifications,
        content
      });
    });
    
    return {
      status: "onboarding_complete",
      userId,
      completedAt: new Date().toISOString()
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, WaitConfig
from datetime import datetime

@durable_execution
def lambda_handler(event, context: DurableContext):
    user_id = event['userId']
    email = event['email']
    
    # Step 1: Create user account
    user = context.step(
        lambda _: user_service.create_account(user_id, email),
        name='create-account'
    )
    
    # Step 2: Send verification email
    context.step(
        lambda _: email_service.send_verification(email),
        name='send-verification'
    )
    
    # Step 3: Wait for email verification (up to 48 hours)
    def send_verification_link(callback_id):
        notification_service.send_verification_link({
            'email': email,
            'callbackId': callback_id,
            'expiresIn': 172800
        })
    
    verified = context.wait_for_callback(
        send_verification_link,
        name='email-verification',
        config=WaitConfig(timeout=172800)
    )
    
    if not verified:
        context.step(
            lambda _: email_service.send_reminder(email),
            name='send-reminder'
        )
        
        return {
            'status': 'verification_timeout',
            'userId': user_id,
            'message': 'Email verification not completed within 48 hours'
        }
    
    # Step 4: Initialize user profile in parallel
    def create_preferences(ctx: DurableContext):
        return ctx.step(
            lambda _: preferences_service.create_defaults(user_id),
            name='create-preferences'
        )
    
    def setup_notifications(ctx: DurableContext):
        return ctx.step(
            lambda _: notification_service.setup_defaults(user_id),
            name='setup-notifications'
        )
    
    def create_welcome_content(ctx: DurableContext):
        return ctx.step(
            lambda _: content_service.create_welcome(user_id),
            name='create-welcome-content'
        )
    
    setup_results = context.parallel(
        [create_preferences, setup_notifications, create_welcome_content],
        name='profile-setup'
    )
    
    # Step 5: Send welcome email
    def send_welcome(_):
        results = setup_results.get_results()
        preferences, notifications, content = results[0], results[1], results[2]
        return email_service.send_welcome({
            'email': email,
            'preferences': preferences,
            'notifications': notifications,
            'content': content
        })
    
    context.step(send_welcome, name='send-welcome')
    
    return {
        'status': 'onboarding_complete',
        'userId': user_id,
        'completedAt': datetime.now().isoformat()
    }
```

------

此程序將循序步驟與檢查點結合，以建立帳戶和傳送電子郵件，然後暫停最多 48 小時等待電子郵件驗證，而不會耗用資源。條件式邏輯會根據驗證是否完成或逾時來處理不同的路徑。設定檔設定任務會使用平行操作同時執行，以減少總執行時間，而且每個步驟都會在暫時性失敗時自動重試，以協助確保上線可靠地完成。

### 跨 函數的鏈結調用
<a name="durable-examples-chained-invocations"></a>

使用 從耐用函數內叫用其他 Lambda 函數`context.invoke()`。呼叫函數會在等待調用函數完成時暫停，建立保留結果的檢查點。如果呼叫函數在調用函數完成後中斷，它會以儲存的結果繼續呼叫函數。

當您有處理特定網域 （客戶驗證、付款處理、庫存管理） 且需要在工作流程中協調它們的特殊 函數時，請使用此模式。每個函數都會維護自己的邏輯，並可由多個協調器函數叫用，避免程式碼重複。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

// Main orchestrator function
export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { orderId, customerId } = event;
    
    // Step 1: Validate customer by invoking customer service function
    const customer = await context.invoke(
      "validate-customer",
      "arn:aws:lambda:us-east-1:123456789012:function:customer-service:1",
      { customerId }
    );
    
    if (!customer.isValid) {
      return { orderId, status: "rejected", reason: "invalid_customer" };
    }
    
    // Step 2: Check inventory by invoking inventory service function
    const inventory = await context.invoke(
      "check-inventory",
      "arn:aws:lambda:us-east-1:123456789012:function:inventory-service:1",
      { orderId, items: event.items }
    );
    
    if (!inventory.available) {
      return { orderId, status: "rejected", reason: "insufficient_inventory" };
    }
    
    // Step 3: Process payment by invoking payment service function
    const payment = await context.invoke(
      "process-payment",
      "arn:aws:lambda:us-east-1:123456789012:function:payment-service:1",
      {
        customerId,
        amount: inventory.totalAmount,
        paymentMethod: customer.paymentMethod
      }
    );
    
    // Step 4: Create shipment by invoking fulfillment service function
    const shipment = await context.invoke(
      "create-shipment",
      "arn:aws:lambda:us-east-1:123456789012:function:fulfillment-service:1",
      {
        orderId,
        items: inventory.allocatedItems,
        address: customer.shippingAddress
      }
    );
    
    return {
      orderId,
      status: "completed",
      trackingNumber: shipment.trackingNumber,
      estimatedDelivery: shipment.estimatedDelivery
    };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution

# Main orchestrator function
@durable_execution
def lambda_handler(event, context: DurableContext):
    order_id = event['orderId']
    customer_id = event['customerId']
    
    # Step 1: Validate customer by invoking customer service function
    customer = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:customer-service:1',
        {'customerId': customer_id},
        name='validate-customer'
    )
    
    if not customer['isValid']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'invalid_customer'}
    
    # Step 2: Check inventory by invoking inventory service function
    inventory = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:inventory-service:1',
        {'orderId': order_id, 'items': event['items']},
        name='check-inventory'
    )
    
    if not inventory['available']:
        return {'orderId': order_id, 'status': 'rejected', 'reason': 'insufficient_inventory'}
    
    # Step 3: Process payment by invoking payment service function
    payment = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:payment-service:1',
        {
            'customerId': customer_id,
            'amount': inventory['totalAmount'],
            'paymentMethod': customer['paymentMethod']
        },
        name='process-payment'
    )
    
    # Step 4: Create shipment by invoking fulfillment service function
    shipment = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:fulfillment-service:1',
        {
            'orderId': order_id,
            'items': inventory['allocatedItems'],
            'address': customer['shippingAddress']
        },
        name='create-shipment'
    )
    
    return {
        'orderId': order_id,
        'status': 'completed',
        'trackingNumber': shipment['trackingNumber'],
        'estimatedDelivery': shipment['estimatedDelivery']
    }
```

------

每次調用都會在協調器函數中建立檢查點。如果協調器在客戶驗證完成後中斷，它會使用儲存的客戶資料從該檢查點繼續，略過驗證調用。這可防止對下游服務的重複呼叫，並確保中斷之間的執行一致。

調用函數可以是耐用的函數或標準 Lambda 函數。如果您叫用耐用的 函數，它可以擁有自己的多步驟工作流程，其中包含等待和檢查點。協調器只會等待完整耐久執行完成，並收到最終結果。

**注意**  
不支援跨帳戶調用。所有調用函數必須與呼叫函數位於相同的 AWS 帳戶中。

### 使用檢查點進行批次處理
<a name="durable-examples-batch-processing"></a>

處理數百萬筆記錄，並在失敗後從最後一個成功的檢查點自動復原。此範例示範 函數如何將`map()`操作與區塊化和速率限制結合，以處理大規模的資料處理。

------
#### [ TypeScript ]

```
import { DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js";

interface Batch {
  batchIndex: number;
  recordIds: string[];
}

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    const { datasetId, batchSize = 1000 } = event;
    
    // Step 1: Get all record IDs to process
    const recordIds = await context.step("fetch-record-ids", async () => {
      return await dataService.getRecordIds(datasetId);
    });
    
    // Step 2: Split into batches
    const batches: Batch[] = [];
    for (let i = 0; i < recordIds.length; i += batchSize) {
      batches.push({
        batchIndex: Math.floor(i / batchSize),
        recordIds: recordIds.slice(i, i + batchSize)
      });
    }
    
    // Step 3: Process batches with controlled concurrency
    const batchResults = await context.map(
      "process-batches",
      batches,
      async (ctx: DurableContext, batch: Batch, index: number) => {
        const processed = await ctx.step(`batch-${batch.batchIndex}`, async () => {
          const results = [];
          for (const recordId of batch.recordIds) {
            const result = await recordService.process(recordId);
            results.push(result);
          }
          return results;
        });
        
        const validated = await ctx.step(`validate-${batch.batchIndex}`, async () => {
          return await validationService.validateBatch(processed);
        });
        
        return {
          batchIndex: batch.batchIndex,
          recordCount: batch.recordIds.length,
          successCount: validated.successCount,
          failureCount: validated.failureCount
        };
      },
      {
        maxConcurrency: 5
      }
    );
    
    // Step 4: Aggregate results
    const summary = await context.step("aggregate-results", async () => {
      const results = batchResults.getResults();
      const totalSuccess = results.reduce((sum, r) => sum + r.successCount, 0);
      const totalFailure = results.reduce((sum, r) => sum + r.failureCount, 0);
      
      return {
        datasetId,
        totalRecords: recordIds.length,
        batchesProcessed: batches.length,
        successCount: totalSuccess,
        failureCount: totalFailure,
        completedAt: new Date().toISOString()
      };
    });
    
    return summary;
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import DurableContext, durable_execution, MapConfig
from datetime import datetime
from typing import List, Dict

@durable_execution
def lambda_handler(event, context: DurableContext):
    dataset_id = event['datasetId']
    batch_size = event.get('batchSize', 1000)
    
    # Step 1: Get all record IDs to process
    record_ids = context.step(
        lambda _: data_service.get_record_ids(dataset_id),
        name='fetch-record-ids'
    )
    
    # Step 2: Split into batches
    batches = []
    for i in range(0, len(record_ids), batch_size):
        batches.append({
            'batchIndex': i // batch_size,
            'recordIds': record_ids[i:i + batch_size]
        })
    
    # Step 3: Process batches with controlled concurrency
    def process_batch(ctx: DurableContext, batch: Dict, index: int):
        batch_index = batch['batchIndex']
        
        def process_records(_):
            results = []
            for record_id in batch['recordIds']:
                result = record_service.process(record_id)
                results.append(result)
            return results
        
        processed = ctx.step(process_records, name=f'batch-{batch_index}')
        
        validated = ctx.step(
            lambda _: validation_service.validate_batch(processed),
            name=f'validate-{batch_index}'
        )
        
        return {
            'batchIndex': batch_index,
            'recordCount': len(batch['recordIds']),
            'successCount': validated['successCount'],
            'failureCount': validated['failureCount']
        }
    
    batch_results = context.map(
        process_batch,
        batches,
        name='process-batches',
        config=MapConfig(max_concurrency=5)
    )
    
    # Step 4: Aggregate results
    def aggregate_results(_):
        results = batch_results.get_results()
        total_success = sum(r['successCount'] for r in results)
        total_failure = sum(r['failureCount'] for r in results)
        
        return {
            'datasetId': dataset_id,
            'totalRecords': len(record_ids),
            'batchesProcessed': len(batches),
            'successCount': total_success,
            'failureCount': total_failure,
            'completedAt': datetime.now().isoformat()
        }
    
    summary = context.step(aggregate_results, name='aggregate-results')
    
    return summary
```

------

記錄會分割為可管理的批次，以避免記憶體或下游服務過載，然後多個批次程序同時`maxConcurrency`控制平行處理。每個批次都有自己的檢查點，因此失敗只會重試失敗的批次，而不是重新處理所有記錄。此模式非常適合處理可能需要數小時的 ETL 任務、資料遷移或大量操作。

## 後續步驟
<a name="durable-examples-next-steps"></a>
+ 探索[基本概念](durable-basic-concepts.md)以了解 DurableContext、步驟和等待
+ 檢閱撰寫確定性程式碼和最佳化效能的[最佳實務](durable-best-practices.md) 
+ 了解如何在本機和雲端[測試耐用的函數](durable-testing.md) 
+ 比較耐用的函數與 Step Functions，以了解每個方法最有效的時機。請參閱[耐用函數或 Step Functions](durable-step-functions.md)。

# Lambda 耐用函數的安全性和許可
<a name="durable-security"></a>

Lambda 耐用函數需要特定的 IAM 許可才能管理檢查點操作。僅授予函數所需的許可，以遵循最低權限原則。

## 執行角色許可
<a name="durable-execution-role"></a>

您耐用函數的執行角色需要建立檢查點和擷取執行狀態的許可。下列政策顯示最低必要許可：

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:CheckpointDurableExecution",
                "lambda:GetDurableExecutionState"
            ],
            "Resource": "arn:aws:lambda:region:account-id:function:function-name:*"
        }
    ]
}
```

當您使用主控台建立耐用的函數時，Lambda 會自動將這些許可新增至執行角色。如果您使用 AWS CLI 或 建立函數 AWS CloudFormation，請將這些許可新增至執行角色。

**最低權限原則**  
將 `Resource`元素範圍限定為特定函數 ARNs，而不是使用萬用字元。這只會將執行角色限制為需要它們的函數的檢查點操作。

**範例：多個函數的範圍許可**

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:CheckpointDurableExecution",
                "lambda:GetDurableExecutionState"
            ],
            "Resource": [
                "arn:aws:lambda:us-east-1:123456789012:function:orderProcessor:*",
                "arn:aws:lambda:us-east-1:123456789012:function:paymentHandler:*"
            ]
        }
    ]
}
```

或者，您可以使用 AWS 受管政策`AWSLambdaBasicDurableExecutionRolePolicy`，其中包含必要的持久執行許可，以及 Amazon CloudWatch Logs 的基本 Lambda 執行許可。

## 狀態加密
<a name="durable-state-encryption"></a>

Lambda 耐用函數使用 AWS 擁有的金鑰自動啟用靜態加密，無需付費。每個函數執行都會維持其他執行無法存取的隔離狀態。不支援客戶受管金鑰 (CMK)。

檢查點資料包括：
+ 步驟結果和傳回值
+ 執行進度和時間軸
+ 等待狀態資訊

當 Lambda 讀取或寫入檢查點資料時，所有資料都會使用 TLS 在傳輸中加密。

### 使用自訂序列化程式和還原序列化程式進行自訂加密
<a name="durable-custom-encryption"></a>

對於關鍵安全需求，您可以使用自訂序列化程式和使用耐用 SDK 的還原序列化程式 (SerDer) 來實作自己的加密和解密機制。此方法可讓您完全控制用來保護檢查點資料的加密金鑰和演算法。

**重要**  
當您使用自訂加密時，您會失去 Lambda 主控台和 API 回應中操作結果的可見性。檢查點資料會在執行歷史記錄中顯示加密，且無法在未解密的情況下進行檢查。

函數的執行角色對自訂 SerDer 實作中使用的 AWS KMS 金鑰的需求`kms:Encrypt`和`kms:Decrypt`許可。

## CloudTrail 日誌記錄
<a name="durable-cloudtrail-logging"></a>

Lambda 會將檢查點操作記錄為其中的資料事件 AWS CloudTrail。您可以使用 CloudTrail 來稽核檢查點的建立時間、追蹤執行狀態變更，以及監控對持久執行資料的存取。

檢查點操作會顯示在具有下列事件名稱的 CloudTrail 日誌中：
+ `CheckpointDurableExecution` - 步驟完成並建立檢查點時記錄
+ `GetDurableExecutionState` - 當 Lambda 在重播期間擷取執行狀態時記錄

若要啟用耐用函數的資料事件記錄，請設定 CloudTrail 追蹤來記錄 Lambda 資料事件。如需詳細資訊，請參閱《CloudTrail 使用者指南》中的[記錄資料事件](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html)。

**範例：檢查點操作的 CloudTrail 日誌項目**

```
{
    "eventVersion": "1.08",
    "eventTime": "2024-11-16T10:30:45Z",
    "eventName": "CheckpointDurableExecution",
    "eventSource": "lambda.amazonaws.com",
    "requestParameters": {
        "functionName": "myDurableFunction",
        "executionId": "exec-abc123",
        "stepId": "step-1"
    },
    "responseElements": null,
    "eventType": "AwsApiCall"
}
```

## 跨帳戶考量事項
<a name="durable-cross-account-access"></a>

如果您跨 AWS 帳戶叫用耐久的函數，呼叫帳戶需要 `lambda:InvokeFunction` 許可，但檢查點操作一律使用函數帳戶中的執行角色。呼叫帳戶無法直接存取檢查點資料或執行狀態。

此隔離可確保檢查點資料在函數的帳戶內保持安全，即使從外部帳戶調用也一樣。

## 繼承的 Lambda 安全功能
<a name="durable-inherited-security"></a>

耐用的函數繼承 Lambda 的所有安全、控管和合規功能，包括 VPC 連線、環境變數加密、無效字母佇列、預留並行、函數 URLs、程式碼簽署和合規認證 (SOC、PCI DSS、HIPAA 等）。

如需 Lambda 安全性功能的詳細資訊，請參閱《Lambda 開發人員指南》中的 [中的安全性 AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/lambda-security.html)。持久功能的唯一額外安全考量是本指南中記錄的檢查點許可。

# 耐用的執行 SDK
<a name="durable-execution-sdk"></a>

耐用執行 SDK 是建置耐用函數的基礎。它提供檢查點進度、處理重試和管理執行流程所需的基本概念。SDK 可抽象化檢查點管理和重播的複雜性，讓您撰寫可自動容錯的循序程式碼。

開發套件適用於 JavaScript、TypeScript、Python 和 Java （預覽版）。如需完整的 API 文件和範例，請參閱 GitHub 上的 [JavaScript/TypeScript SDK](https://github.com/aws/aws-durable-execution-sdk-js)、[Python SDK](https://github.com/aws/aws-durable-execution-sdk-python) 和 [Java SDK](https://github.com/aws/aws-durable-execution-sdk-java)。

## DurableContext
<a name="durable-sdk-context"></a>

開發套件為您的函數提供公開所有耐久操作的`DurableContext`物件。此內容會取代標準 Lambda 內容，並提供建立檢查點、管理執行流程以及與外部系統協調的方法。

若要使用 SDK，請使用耐用的執行包裝函式來包裝 Lambda 處理常式：

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Your function receives DurableContext instead of Lambda context
    // Use context.step(), context.wait(), etc.
    return result;
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event: dict, context: DurableContext):
    # Your function receives DurableContext
    # Use context.step(), context.wait(), etc.
    return result
```

------
#### [ Java (Preview) ]

```
import software.amazon.lambda.durable.DurableContext;
import software.amazon.lambda.durable.DurableHandler;

public class Handler extends DurableHandler<Object, String> {
    @Override
    public String handleRequest(Object input, DurableContext context) {
        // Your function receives DurableContext
        // Use context.step(), context.wait(), etc.
        return result;
    }
}
```

------

包裝函式會攔截函數叫用、載入任何現有的檢查點日誌，並提供`DurableContext`管理重播和檢查點的 。

## 開發套件的功能
<a name="durable-sdk-what-it-does"></a>

開發套件會處理三個關鍵責任，以實現持久的執行：

**檢查點管理：**軟體開發套件會在函數執行耐久操作時自動建立檢查點。每個檢查點都會記錄操作類型、輸入和結果。函數完成步驟時，SDK 會保留檢查點再繼續。這可確保函數可以在中斷時從任何已完成的操作恢復。

**重播協調：**當函數在暫停或中斷後恢復時，軟體開發套件會執行重播。它會從頭開始執行程式碼，但會略過已完成的操作，使用儲存的檢查點結果，而不是重新執行它們。SDK 可確保重播是確定性的，因為提供相同的輸入和檢查點日誌，您的函數會產生相同的結果。

**狀態隔離：**SDK 會與您的業務邏輯分開維護執行狀態。每個持久性執行都有自己的檢查點日誌，其他執行無法存取。SDK 會加密靜態檢查點資料，並確保狀態在重播之間保持一致。

## 檢查點的運作方式
<a name="durable-sdk-how-checkpointing-works"></a>

當您呼叫耐用操作時，軟體開發套件會遵循以下順序：

1. **檢查現有檢查點：**軟體開發套件會檢查此操作是否已在先前的調用中完成。如果檢查點存在，軟體開發套件會傳回儲存的結果，而無需重新執行操作。

1. **執行操作：**如果沒有檢查點，則 SDK 會執行您的操作程式碼。對於步驟，這表示呼叫您的 函數。對於等待，這表示排程恢復。

1. **建立檢查點：**操作完成後，開發套件會序列化結果並建立檢查點。檢查點包含操作類型、名稱、輸入、結果和時間戳記。

1. **持久性檢查點：** SDK 會呼叫 Lambda 檢查點 API 來持久性檢查點。這可確保檢查點在繼續執行之前是耐用的。

1. **傳回結果：**軟體開發套件會將操作結果傳回至您的程式碼，繼續下一個操作。

此序列可確保一旦操作完成，就會安全地存放其結果。如果您的函數在任何時間點中斷，開發套件可以重新執行到最後完成的檢查點。

## 重播行為
<a name="durable-sdk-replay-behavior"></a>

當您的函數在暫停或中斷後繼續時，軟體開發套件會執行重播：

1. **載入檢查點日誌：** SDK 會從 Lambda 擷取此執行的檢查點日誌。

1. **從頭開始執行：**軟體開發套件會從頭調用您的處理常式函數，而不是從暫停的位置調用。

1. **略過已完成的持久性操作：**當程式碼呼叫持久性操作時，軟體開發套件會根據檢查點日誌檢查每個操作。對於已完成的耐久操作，軟體開發套件會傳回預存結果，而不執行操作程式碼。
**注意**  
如果子內容的結果大於檢查點大小上限 (256 KB)，則在重播期間會再次執行內容的程式碼。這可讓您從在內容中執行的耐久操作建構大型結果，這些操作將從檢查點日誌中查詢。因此，只能在內容本身中執行確定性程式碼。使用具有大型結果的子內容時，最佳實務是在步驟內執行長時間執行或非確定性工作，並且只執行將結果合併到內容本身的短期執行任務。

1. **在中斷點繼續：**當開發套件在沒有檢查點的情況下達到 操作時，它會正常執行，並在耐久操作完成時建立新的檢查點。

此重播機制需要您的程式碼具有決定性。假設輸入和檢查點日誌相同，您的函數必須進行相同序列的耐久操作呼叫。SDK 透過驗證操作名稱和類型在重播期間符合檢查點日誌來強制執行此操作。

## 可用的耐用操作
<a name="durable-sdk-operations"></a>

`DurableContext` 提供不同協調模式的操作。每個耐用的操作都會自動建立檢查點，確保您的函數可以隨時恢復。

### 步驟
<a name="durable-sdk-op-step"></a>

使用自動檢查點和重試來執行商業邏輯。對呼叫外部服務、執行計算或執行任何應檢查點邏輯的操作使用步驟。軟體開發套件會在步驟前後建立檢查點，存放結果以進行重播。

------
#### [ TypeScript ]

```
const result = await context.step('process-payment', async () => {
  return await paymentService.charge(amount);
});
```

------
#### [ Python ]

```
result = context.step(
    lambda _: payment_service.charge(amount),
    name='process-payment'
)
```

------
#### [ Java (Preview) ]

```
var result = context.step("process-payment", Payment.class, 
    () -> paymentService.charge(amount)
);
```

------

步驟支援可設定的重試策略、執行語意 (at-most-once或at-least-once和自訂序列化。

### 等待
<a name="durable-sdk-op-wait"></a>

在指定的持續時間內暫停執行，而不耗用運算資源。SDK 會建立檢查點、終止函數叫用，以及排程恢復。當等待完成時，Lambda 會再次叫用您的函數，而 SDK 會在繼續之前重播至等待點。

------
#### [ TypeScript ]

```
// Wait 1 hour without charges
await context.wait({ seconds: 3600 });
```

------
#### [ Python ]

```
# Wait 1 hour without charges
context.wait(3600)
```

------
#### [ Java (Preview) ]

```
// Wait 1 hour without charges
context.wait(Duration.ofHours(1));
```

------

### 回呼
<a name="durable-sdk-op-callback"></a>

回呼可讓函數暫停並等待外部系統提供輸入。當您建立回呼時，開發套件會產生唯一的回呼 ID 並建立檢查點。然後，您的函數會暫停 （終止調用），而不會產生運算費用。外部系統使用 `SendDurableExecutionCallbackSuccess`或 `SendDurableExecutionCallbackFailure` Lambda APIs提交回呼結果。提交回呼時，Lambda 會再次叫用您的函數，開發套件會重播至回呼點，而函數會繼續回呼結果。

開發套件提供兩種使用回呼的方法：

**createCallback：**建立回呼，並同時傳回 promise 和回呼 ID。您可以將回呼 ID 傳送至外部系統，該系統會使用 Lambda API 提交結果。

------
#### [ TypeScript ]

```
const [promise, callbackId] = await context.createCallback('approval', {
  timeout: { hours: 24 }
});

await sendApprovalRequest(callbackId, requestData);
const approval = await promise;
```

------
#### [ Python ]

```
callback = context.create_callback(
    name='approval',
    config=CallbackConfig(timeout_seconds=86400)
)

context.step(
    lambda _: send_approval_request(callback.callback_id),
    name='send_request'
)

approval = callback.result()
```

------
#### [ Java (Preview) ]

```
var config = CallbackConfig.builder(Duration.ofHours(24)).timeout()

var callback = context.createCallback("approval", String.class, config);

context.step("send-request", String.class, () -> {
    notificationService.sendApprovalRequest(callback.callbackId(), requestData);
    return "request-sent";
});

// Blocks until the callback finishes or times out
String approval = callback.get();
```

------

**waitForCallback：**將回呼建立和提交結合在一個操作中，簡化回呼處理。開發套件會建立回呼、使用回呼 ID 執行您的提交者函數，並等待結果。

------
#### [ TypeScript ]

```
const result = await context.waitForCallback(
  'external-api',
  async (callbackId, ctx) => {
    await submitToExternalAPI(callbackId, requestData);
  },
  { timeout: { minutes: 30 } }
);
```

------
#### [ Python ]

```
result = context.wait_for_callback(
    lambda callback_id: submit_to_external_api(callback_id, request_data),
    name='external-api',
    config=WaitForCallbackConfig(timeout_seconds=1800)
)
```

------
#### [ Java (Preview) ]

waitForCallback 仍在開發 Java。

------

設定逾時以防止函數無限期等待。如果回呼逾時，開發套件會擲回 `CallbackError`，而您的函數可以處理逾時案例。針對長時間執行的回呼使用活動訊號逾時，以偵測外部系統何時停止回應。

將回呼用於human-in-the-loop工作流程、外部系統整合、Webhook 回應或任何執行必須針對外部輸入暫停的情況。

### 平行執行
<a name="durable-sdk-op-parallel"></a>

與選用並行控制同時執行多個操作。SDK 會管理平行執行、為每個操作建立檢查點，以及根據您的完成政策處理失敗。

------
#### [ TypeScript ]

```
const results = await context.parallel([
  async (ctx) => ctx.step('task1', async () => processTask1()),
  async (ctx) => ctx.step('task2', async () => processTask2()),
  async (ctx) => ctx.step('task3', async () => processTask3())
]);
```

------
#### [ Python ]

```
results = context.parallel(
    lambda ctx: ctx.step(lambda _: process_task1(), name='task1'),
    lambda ctx: ctx.step(lambda _: process_task2(), name='task2'),
    lambda ctx: ctx.step(lambda _: process_task3(), name='task3')
)
```

------
#### [ Java (Preview) ]

平行處理仍在開發 Java。

------

使用 `parallel` 可同時執行獨立操作。

### Map
<a name="durable-sdk-op-map"></a>

使用選用並行控制，同時對陣列中的每個項目執行 操作。SDK 會管理並行執行、為每個操作建立檢查點，以及根據您的完成政策處理失敗。

------
#### [ TypeScript ]

```
const results = await context.map(itemArray, async (ctx, item, index) =>
  ctx.step('task', async () => processItem(item, index))
);
```

------
#### [ Python ]

```
results = context.map(
    item_array,
    lambda ctx, item, index: ctx.step(
        lambda _: process_item(item, index),
        name='task'
    )
)
```

------
#### [ Java (Preview) ]

地圖仍在開發 Java。

------

使用 `map` 處理具有並行控制的陣列。

### 子內容
<a name="durable-sdk-op-child-context"></a>

建立用於分組操作的隔離執行內容。子內容有自己的檢查點日誌，可包含多個步驟、等待和其他操作。SDK 會將整個子內容視為單一單位，以供重試和復原。

使用子內容來組織複雜的工作流程、實作子工作流程，或隔離應同時重試的操作。

------
#### [ TypeScript ]

```
const result = await context.runInChildContext(
  'batch-processing',
  async (childCtx) => {
    return await processBatch(childCtx, items);
  }
);
```

------
#### [ Python ]

```
result = context.run_in_child_context(
    lambda child_ctx: process_batch(child_ctx, items),
    name='batch-processing'
)
```

------
#### [ Java (Preview) ]

```
var result = context.runInChildContext(
    "batch-processing", 
    String.class, 
    childCtx -> process_batch(childCtx, items)
);
```

------

重播機制要求以決定性順序進行持久性操作。使用多個子內容，您可以同時執行多個工作串流，而且決定性會分別套用在每個內容中。這可讓您建置高效能函數，以有效率地利用多個 CPU 核心。

例如，假設我們啟動兩個子內容 A 和 B。在初始調用時，內容中的步驟會依此順序執行，而 'A' 步驟會與 'B' 步驟同時執行：A1, B1, B2, A2, A3。重新執行時，從檢查點日誌擷取結果時，時間會快得多，而步驟的發生順序也不同：B1, A1, A2, B2, A3。由於「A」步驟的順序正確 (A1、A2, A3)，且「B」步驟的順序正確 (B1、B2)，因此正確滿足確定性的需求。

### 條件式等待
<a name="durable-sdk-op-wait-condition"></a>

輪詢在嘗試之間具有自動檢查點的條件。SDK 會執行您的檢查函數、建立具有結果的檢查點、根據您的策略等待，並重複直到滿足條件為止。

------
#### [ TypeScript ]

```
const result = await context.waitForCondition(
  async (state, ctx) => {
    const status = await checkJobStatus(state.jobId);
    return { ...state, status };
  },
  {
    initialState: { jobId: 'job-123', status: 'pending' },
    waitStrategy: (state) => 
      state.status === 'completed' 
        ? { shouldContinue: false }
        : { shouldContinue: true, delay: { seconds: 30 } }
  }
);
```

------
#### [ Python ]

```
result = context.wait_for_condition(
    lambda state, ctx: check_job_status(state['jobId']),
    config=WaitForConditionConfig(
        initial_state={'jobId': 'job-123', 'status': 'pending'},
        wait_strategy=lambda state, attempt: 
            {'should_continue': False} if state['status'] == 'completed'
            else {'should_continue': True, 'delay': 30}
    )
)
```

------
#### [ Java (Preview) ]

waitForCondition 仍在開發 Java。

------

使用 `waitForCondition`輪詢外部系統、等待資源準備就緒，或使用退避實作重試。

### 函數調用
<a name="durable-sdk-op-invoke"></a>

叫用另一個 Lambda 函數並等待其結果。SDK 會建立檢查點、叫用目標函數，並在叫用完成時繼續您的函數。這可啟用函數合成和工作流程分解。

------
#### [ TypeScript ]

```
const result = await context.invoke(
  'invoke-processor',
  'arn:aws:lambda:us-east-1:123456789012:function:processor:1',
  { data: inputData }
);
```

------
#### [ Python ]

```
result = context.invoke(
    'arn:aws:lambda:us-east-1:123456789012:function:processor:1',
    {'data': input_data},
    name='invoke-processor'
)
```

------
#### [ Java (Preview) ]

```
var result = context.invoke(
    "invoke-processor", 
    "arn:aws:lambda:us-east-1:123456789012:function:processor:1",
    inputData,
    Result.class, 
    InvokeConfig.builder().build()
);
```

------

## 如何計量耐久性操作
<a name="durable-operations-checkpoint-consumption"></a>

您透過 呼叫的每個耐用操作都會`DurableContext`建立檢查點，以追蹤執行進度並存放狀態資料。這些操作會根據其用量產生費用，而檢查點可能包含對您的資料寫入和保留成本有所貢獻的資料。儲存的資料包括調用事件資料、步驟傳回的承載，以及完成回呼時傳遞的資料。了解耐用操作的計量方式可協助您預估執行成本並最佳化工作流程。如需定價的詳細資訊，請參閱 [Lambda 定價頁面](https://aws.amazon.com/lambda/pricing/)。

承載大小是指持久性操作持續的序列化資料大小。資料是以位元組為單位測量，大小可能會因 操作使用的序列化程式而有所不同。操作的承載可能是成功完成的結果本身，如果操作失敗，可能是序列化錯誤物件。

### 基本操作
<a name="durable-operations-basic"></a>

基本操作是耐用函數的基本建置區塊：


| 作業 | 檢查點計時 | 操作數量 | 資料持續存在 | 
| --- | --- | --- | --- | 
| 執行 | 已開始 | 1 | 輸入承載大小 | 
| 執行 | 已完成 Succeeded/Failed/Stopped) | 0 | 輸出承載大小 | 
| Step (步驟) | Retry/Succeeded/Failed | 1 \$1 N 次重試 | 每次嘗試傳回的承載大小 | 
| 等候 | 已開始 | 1 | N/A | 
| WaitForCondition | 每次輪詢嘗試 | 1 \$1 N 輪詢 | 每次輪詢嘗試傳回的承載大小 | 
| 調用層級重試 | 已開始 | 1 | 錯誤物件的承載 | 

### 回呼操作
<a name="durable-operations-callbacks"></a>

回呼操作可讓您的函數暫停並等待外部系統提供輸入。這些操作會在建立回呼和完成時建立檢查點：


| 作業 | 檢查點計時 | 操作數量 | 資料持續存在 | 
| --- | --- | --- | --- | 
| CreateCallback | 已開始 | 1 | N/A | 
| 透過 API 呼叫完成回呼 | 已完成 | 0 | 回呼承載 | 
| WaitForCallback | 已開始 | 3 \$1 N 次重試 （內容 \$1 回呼 \$1 步驟） | 提交者步驟嘗試傳回的承載，加上兩個回呼承載副本 | 

### 複合操作
<a name="durable-operations-compound"></a>

複合操作結合多個耐久操作來處理複雜的協調模式，例如平行執行、陣列處理和巢狀內容：


| 作業 | 檢查點計時 | 操作數量 | 資料持續存在 | 
| --- | --- | --- | --- | 
| 平行 | 已開始 | 1 \$1 N 個分支 (1 個父內容 \$1 N 個子內容） | 每個分支最多兩個傳回承載大小的副本，以及每個分支的狀態 | 
| Map | 已開始 | 1 \$1 N 個分支 (1 個父內容 \$1 N 個子內容） | 每次反覆運算傳回的承載大小最多兩個副本，加上每次反覆運算的狀態 | 
| Promise 協助程式 | 已完成 | 1 | 從 promise 傳回的承載大小 | 
| RunInChildContext | 成功/失敗 | 1 | 從子內容傳回的承載大小 | 

對於內容，例如來自 `runInChildContext`或由複合操作內部使用，小於 256 KB 的結果會直接檢查點。不會儲存較大的結果，而是透過重新處理內容的操作，在重播期間重建結果。

# 支援耐用函數的執行時間
<a name="durable-supported-runtimes"></a>

耐用的函數可用於選取的受管執行期和 OCI 容器映像，以提高執行期版本的靈活性。您可以直接在主控台中使用受管執行時間，或透過infrastructure-as-code以程式設計方式建立 Node.js 和 Python 的耐用函數。Java （預覽版） 中的耐用函數目前只能透過容器映像部署。

## Lambda 受管執行時間
<a name="durable-managed-runtimes"></a>

當您在 Lambda 主控台中或使用 AWS CLI 搭配 `--durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'` 參數建立函數時，下列受管執行期支援耐久函數。如需 Lambda 執行時間的完整資訊，請參閱 [Lambda 執行時間](lambda-runtimes.md)。


| Language | 執行時期 | 
| --- | --- | 
| Node.js | nodejs22.x | 
| Node.js | nodejs24.x | 
| Python | python3.13 | 
| Python | python3.14 | 

**注意**  
Lambda 執行時間包括用於測試和開發的耐久執行 SDK。不過，我們建議您在部署套件中包含 開發套件以供生產使用。這可確保版本一致性，並避免可能影響函數行為的潛在執行時間更新。

### Node.js
<a name="durable-runtime-nodejs"></a>

在 Node.js 專案中安裝 SDK：

```
npm install @aws/durable-execution-sdk-js
```

SDK 支援 JavaScript 和 TypeScript。對於 TypeScript 專案，開發套件包含類型定義。

### Python
<a name="durable-runtime-python"></a>

在 Python 專案中安裝 SDK：

```
pip install aws-durable-execution-sdk-python
```

Python SDK 使用同步方法，不需要 `async/await`。

### Java （預覽版）
<a name="durable-runtime-java"></a>

將相依性新增至 `pom.xml`：

```
<dependency>
    <groupId>software.amazon.lambda.durable</groupId>
    <artifactId>aws-durable-execution-sdk-java</artifactId>
    <version>VERSION</version>
</dependency>
```

在 Java 專案中安裝 SDK：

```
mvn install
```

Java 開發套件的預覽版本可供使用。waitForCondition、 waitForCallback、平行和映射操作仍在開發中。

## 容器映像
<a name="durable-container-images"></a>

您可以搭配容器映像使用耐用的函數，以支援其他執行時間版本或自訂執行時間組態。容器映像可讓您使用不可做為受管執行時間的執行時間版本，或自訂執行時間環境。

若要使用容器映像建立耐用的函數：

1. 根據 Lambda 基礎映像建立 Dockerfile

1. 在容器中安裝耐用的執行 SDK

1. 建置容器映像並將其推送至 Amazon Elastic Container Registry

1. 從已啟用持久性執行的容器映像建立 Lambda 函數

### 容器範例
<a name="durable-container-python"></a>

建立 Dockerfile：

------
#### [ Python ]

建立適用於 Python 3.11 的 Dockerfile：

```
FROM public.ecr.aws/lambda/python:3.11

# Copy requirements file
COPY requirements.txt ${LAMBDA_TASK_ROOT}/

# Install dependencies including durable SDK
RUN pip install -r requirements.txt

# Copy function code
COPY lambda_function.py ${LAMBDA_TASK_ROOT}/

# Set the handler
CMD [ "lambda_function.handler" ]
```

建立`requirements.txt`檔案：

```
aws-durable-execution-sdk-python
```

------
#### [ Java (Preview) ]

為 Java 25 建立 Dockerfile：

```
FROM --platform=linux/amd64 public.ecr.aws/lambda/java:25

# Install Maven
RUN dnf install -y maven

WORKDIR /var/task

# Copy Maven configuration and source code
COPY pom.xml .
COPY src ./src

# Build
RUN mvn clean package -DskipTests

# Move JAR to lib directory
RUN mv target/*.jar lib/

# Set the handler
CMD ["src.path.to.lambdaFunction::handler"]
```

------

建置並推送映像：

```
# Build the image
docker build -t my-durable-function .

# Tag for ECR
docker tag my-durable-function:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest

# Push to ECR
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest
```

建立已啟用持久性執行的函數：

```
aws lambda create-function \
  --function-name myDurableFunction \
  --package-type Image \
  --code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/my-durable-function:latest \
  --role arn:aws:iam::123456789012:role/lambda-execution-role \
  --durable-config '{"ExecutionTimeout": 3600, "RetentionPeriodInDays": 7}'
```

如需搭配 Lambda 使用容器映像的詳細資訊，請參閱[《Lambda 開發人員指南》中的建立 Lambda 容器映像](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)。

## 執行時期考量
<a name="durable-runtime-considerations"></a>

**SDK 版本管理：**在您的部署套件或容器映像中包含耐久的執行 SDK。這可確保函數使用特定的 SDK 版本，並且不受執行時間更新的影響。在 `package.json`或 中釘選 SDK 版本`requirements.txt`，以控制升級時間。

**執行期更新：** AWS 更新受管執行期，以包含安全性修補程式和錯誤修正。這些更新可能包含新的 SDK 版本。為了避免意外行為，請在部署套件中包含 開發套件，並在部署至生產環境之前進行徹底測試。

**容器映像大小：**容器映像的最大未壓縮大小為 10 GB。耐用的執行 SDK 為您的映像新增最小大小。使用多階段建置並移除不必要的相依性，以最佳化您的容器。

**冷啟動效能：**容器映像的冷啟動時間可能比受管執行時間更長。耐用的執行 SDK 對冷啟動效能的影響最小。如果冷啟動延遲對您的應用程式至關重要，請使用佈建並行。

# 叫用耐用的 Lambda 函數
<a name="durable-invoking"></a>

耐用的 Lambda 函數支援與標準 Lambda 函數相同的調用方法。您可以同步、非同步或透過事件來源映射叫用耐久函數。叫用程序與標準 函數相同，但耐用的 函數為長時間執行和自動狀態管理提供額外的功能。

## 調用方法
<a name="durable-invoking-methods"></a>

**同步叫用：**叫用耐久的 函數並等待回應。同步調用受到 Lambda 限制為 15 分鐘 （或更少，取決於設定的函數和執行逾時）。當您需要立即結果或與預期回應APIs 和服務整合時，請使用同步調用。您可以使用等待操作進行有效率的運算，而不會中斷發起人 — 調用會等待整個耐久執行完成。對於等冪性執行啟動，請使用[等冪性](durable-execution-idempotency.md)中所述的執行名稱參數。

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**非同步調用：**將事件排入佇列以進行處理，而無需等待回應。Lambda 會將事件置於佇列中並立即傳回。非同步調用支援長達 1 年的執行持續時間。將非同步叫用用於fire-and-forget的情況，或在背景中可能發生處理時。對於等冪性執行開始，請使用[等冪性](durable-execution-idempotency.md)中所述的執行名稱參數。

```
aws lambda invoke \
  --function-name my-durable-function:1 \
  --invocation-type Event \
  --cli-binary-format raw-in-base64-out \
  --payload '{"orderId": "12345"}' \
  response.json
```

**事件來源映射：**將 Lambda 設定為在 Amazon SQS、Kinesis 或 DynamoDB 等串流或佇列型服務提供記錄時，自動叫用您的耐久函數。事件來源映射會輪詢事件來源，並使用批次記錄叫用函數。如需搭配耐久函數使用事件來源映射的詳細資訊，包括執行持續時間限制，請參閱[搭配持久函數的事件來源映射](durable-invoking-esm.md)。

如需每個調用方法的完整詳細資訊，請參閱[同步調用](invocation-sync.md)和非[同步調用](invocation-async.md)。

**注意**  
耐用的函數支援無效字母佇列 (DLQs) 來處理錯誤，但不支援 Lambda 目的地。設定 DLQ 以從失敗的調用中擷取記錄。

## 合格 ARNs需求
<a name="durable-invoking-qualified-arns"></a>

耐用的函數需要合格的識別符才能叫用。您必須使用版本編號、別名或 叫用耐久函數`$LATEST`。您可以搭配版本/別名尾碼使用完整的合格 ARN 或函數名稱。您無法使用不合格的識別符 （沒有版本或別名尾碼）。

**有效的調用：**

```
# Using full ARN with version number
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1

# Using full ARN with alias
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:prod

# Using full ARN with $LATEST
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:$LATEST

# Using function name with version number
my-durable-function:1

# Using function name with alias
my-durable-function:prod
```

**無效的叫用：**

```
# Unqualified ARN (not allowed)
arn:aws:lambda:us-east-1:123456789012:function:my-durable-function

# Unqualified function name (not allowed)
my-durable-function
```

此要求可確保持久的執行在整個生命週期中保持一致。當持久性執行開始時，它會固定到特定的函數版本。如果您的函數暫停並在數小時或數天後恢復，Lambda 會叫用開始執行的相同版本，以確保整個工作流程的程式碼一致性。

**最佳實務**  
將編號版本或別名用於生產耐久函數，而非 `$LATEST`。編號版本不可變，並支援確定性重播。或者，別名提供穩定的參考，您可以更新以指向新版本，而無需變更調用程式碼。當您更新別名時，新的執行會使用新版本，而進行中的執行會繼續其原始版本。您可以使用 `$LATEST`進行原型設計，或縮短開發期間的部署時間，了解如果基礎程式碼在執行期間變更，執行可能無法正確重播 （甚至失敗）。

## 了解執行生命週期
<a name="durable-invoking-execution-lifecycle"></a>

當您叫用耐久的函數時，Lambda 會建立持久的執行，可以跨越多個函數叫用：

1. **初始調用：**您的調用請求會建立新的耐久執行。Lambda 會指派唯一的執行 ID 並開始處理。

1. **執行和檢查點：**當函數執行持久性操作時，開發套件會建立檢查點來追蹤進度。

1. **暫停 （如果需要）：**如果您的函數使用持久的等待，例如 `wait`或 `waitForCallback`，或自動步驟重試，Lambda 會暫停執行，並在運算時間內停止收費。

1. **恢復：**當需要繼續時 （包括重試後），Lambda 會再次叫用您的函數。SDK 會重播檢查點日誌，並從執行暫停的位置繼續。

1. **完成：**當您的函數傳回最終結果或擲出未處理的錯誤時，持久性執行就會完成。

對於同步調用，發起人會等待整個耐久執行完成，包括任何等待操作。如果執行超過呼叫逾時 (15 分鐘或更短），則呼叫會逾時。對於非同步調用，Lambda 會立即傳回 ，且執行會獨立繼續。使用耐久APIs 來追蹤執行狀態並擷取最終結果。

## 從應用程式碼叫用
<a name="durable-invoking-with-sdk"></a>

使用 AWS SDKs從您的應用程式程式碼叫用耐久的函數。叫用程序與標準函數相同：

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';

const client = new LambdaClient({});

// Synchronous invocation
const response = await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  Payload: JSON.stringify({ orderId: '12345' })
}));

const result = JSON.parse(Buffer.from(response.Payload!).toString());

// Asynchronous invocation
await client.send(new InvokeCommand({
  FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
  InvocationType: 'Event',
  Payload: JSON.stringify({ orderId: '12345' })
}));
```

------
#### [ Python ]

```
import boto3
import json

client = boto3.client('lambda')

# Synchronous invocation
response = client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    Payload=json.dumps({'orderId': '12345'})
)

result = json.loads(response['Payload'].read())

# Asynchronous invocation
client.invoke(
    FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType='Event',
    Payload=json.dumps({'orderId': '12345'})
)
```

------

## 鏈結調用
<a name="durable-invoking-chained"></a>

耐用的 函數可以使用 的 `invoke`操作叫用其他耐用和非耐用的 函數`DurableContext`。這會建立鏈結叫用，呼叫函數會等待 （暫停） 叫用函數完成：

------
#### [ TypeScript ]

```
export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Invoke another durable function and wait for result
    const result = await context.invoke(
      'process-order',
      'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
      { orderId: event.orderId }
    );
    
    return { statusCode: 200, body: JSON.stringify(result) };
  }
);
```

------
#### [ Python ]

```
@durable_execution
def handler(event, context: DurableContext):
    # Invoke another durable function and wait for result
    result = context.invoke(
        'arn:aws:lambda:us-east-1:123456789012:function:order-processor:1',
        {'orderId': event['orderId']},
        name='process-order'
    )
    
    return {'statusCode': 200, 'body': json.dumps(result)}
```

------

鏈結調用會在呼叫函數中建立檢查點。如果呼叫函數中斷，它會使用調用函數的結果從檢查點繼續，而無需重新調用函數。

**注意**  
不支援跨帳戶鏈結調用。調用函數必須與呼叫函數位於相同的 AWS 帳戶中。

# 具有耐用函數的事件來源映射
<a name="durable-invoking-esm"></a>

耐用函數適用於所有 Lambda 事件來源映射。設定耐用函數的事件來源映射，與設定標準函數的方式相同。事件來源映射會自動輪詢事件來源，例如 Amazon SQS、Kinesis 和 DynamoDB Streams，並使用批次記錄叫用函數。

事件來源映射適用於使用複雜多步驟工作流程處理串流或佇列的耐久函數。例如，您可以建立耐久的 函數，透過重試、外部 API 呼叫和人工核准來處理 Amazon SQS 訊息。

## 事件來源映射如何叫用耐久函數
<a name="durable-esm-invocation-behavior"></a>

事件來源映射會同步叫用持久性函數，等待完全持久性執行完成，然後再處理下一個批次或將記錄標記為已處理。如果總耐久執行時間超過 15 分鐘，則執行會逾時並失敗。事件來源映射會收到逾時例外狀況，並根據其重試組態進行處理。

## 15 分鐘執行限制
<a name="durable-esm-duration-limit"></a>

當事件來源映射調用持久性函數時，總持久性執行持續時間不能超過 15 分鐘。此限制適用於從開始到完成的整個持久性執行，而不只是個別函數叫用。

此 15 分鐘限制與 Lambda 函數逾時分開 （最長 15 分鐘）。函數逾時控制每個個別調用可以執行的時間長度，而持久的執行逾時控制從執行開始到完成的總經過時間。

**範例案例：**
+ **有效：**耐用的函數會處理具有三個步驟的 Amazon SQS 訊息，每個步驟需要 2 分鐘，然後等待 5 分鐘，再完成最後一個步驟。總執行時間：11 分鐘。這是因為總計少於 15 分鐘。
+ **無效：**耐用函數會處理 Amazon SQS 訊息、在 2 分鐘內完成初始處理，然後等待 20 分鐘進行外部回呼，然後再完成。總執行時間：22 分鐘。這超過 15 分鐘的限制，將會失敗。
+ **無效：**耐用函數會處理 Kinesis 記錄，其中多個等待操作在步驟之間總計為 30 分鐘。即使每個個別調用都快速完成，總執行時間仍超過 15 分鐘。

**重要**  
使用事件來源映射時，將您的持久執行逾時設定為 15 分鐘或更少，否則建立事件來源映射將會失敗。如果您的工作流程需要較長的執行時間，請使用如下所述的中介函數模式。

## 設定事件來源映射
<a name="durable-esm-configuration"></a>

使用 Lambda 主控台 AWS CLI或 AWS SDKs 設定耐用函數的事件來源映射。所有標準事件來源映射屬性都適用於耐用函數：

```
aws lambda create-event-source-mapping \
  --function-name arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1 \
  --event-source-arn arn:aws:sqs:us-east-1:123456789012:my-queue \
  --batch-size 10 \
  --maximum-batching-window-in-seconds 5
```

為耐用函數設定事件來源映射時，請記得使用合格的 ARN （具有版本編號或別名）。

## 使用事件來源映射處理錯誤
<a name="durable-esm-error-handling"></a>

事件來源映射提供內建錯誤處理，可搭配耐用的 函數運作：
+ **重試行為：**如果初始調用失敗，事件來源映射會根據其重試組態重試。根據您的需求設定重試嘗試次數上限和重試間隔。
+ **無效字母佇列：**設定無效字母佇列以擷取所有重試後失敗的記錄。這可防止訊息遺失，並啟用失敗記錄的手動檢查。
+ **部分批次失敗：**對於 Amazon SQS 和 Kinesis，請使用部分批次失敗報告個別處理記錄，並且只重試失敗的記錄。
+ **偏差錯誤：**對於 Kinesis 和 DynamoDB 串流，啟用偏差錯誤以分割失敗的批次並隔離有問題的記錄。

**注意**  
耐用的函數支援無效字母佇列 (DLQs) 來處理錯誤，但不支援 Lambda 目的地。設定 DLQ 以從失敗的調用中擷取記錄。

如需事件來源映射錯誤處理的完整資訊，請參閱[事件來源映射](invocation-eventsourcemapping.md)。

## 將中介函數用於長時間執行的工作流程
<a name="durable-esm-intermediary-function"></a>

如果您的工作流程需要超過 15 分鐘才能完成，請在事件來源映射和耐用函數之間使用中介標準 Lambda 函數。中介函數會從事件來源映射接收事件，並以非同步方式叫用耐久函數，移除 15 分鐘的執行限制。

此模式會將事件來源映射的同步調用模型與耐久函數長時間執行的執行模型分離。事件來源映射會叫用中介函數，該函數會在啟動耐久執行後快速傳回。然後，耐用函數會視需要獨立執行 （最長 1 年）。

### Architecture
<a name="durable-esm-intermediary-architecture"></a>

中介函數模式使用三個元件：

1. **事件來源映射：**輪詢事件來源 (Amazon SQS、Kinesis、DynamoDB Streams)，並使用批次記錄同步叫用中介函數。

1. **中介函數：**從事件來源映射接收事件的標準 Lambda 函數，會視需要驗證和轉換資料，並以非同步方式叫用持久函數。此函數會快速完成 （通常不到 1 秒），並將控制項傳回至事件來源映射。

1. **耐用函數：**使用可長時間執行的複雜多步驟邏輯來處理事件。非同步叫用，因此不受 15 分鐘限制。

### 實作
<a name="durable-esm-intermediary-implementation"></a>

中介函數會從事件來源映射接收整個事件，並以非同步方式叫用耐久函數。使用執行名稱參數來確保等冪執行開始，防止事件來源映射重試時重複處理：

------
#### [ TypeScript ]

```
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
import { SQSEvent } from 'aws-lambda';
import { createHash } from 'crypto';

const lambda = new LambdaClient({});

export const handler = async (event: SQSEvent) => {
  // Invoke durable function asynchronously with execution name
  await lambda.send(new InvokeCommand({
    FunctionName: 'arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
    InvocationType: 'Event',
    Payload: JSON.stringify({
      executionName: event.Name,
      event: event
    })
  }));
  
  return { statusCode: 200 };
};
```

------
#### [ Python ]

```
import boto3
import json
import hashlib

lambda_client = boto3.client('lambda')

def handler(event, context):  
    # Invoke durable function asynchronously with execution name
    lambda_client.invoke(
        FunctionName='arn:aws:lambda:us-east-1:123456789012:function:my-durable-function:1',
        InvocationType='Event',
        Payload=json.dumps({
            'executionName': execution_name,
            'event': event["name"]
        })
    )
    
    return {'statusCode': 200}
```

------

對於中介函數本身的冪等性，如果事件來源映射重試中介函數，請使用[適用於 的 Powertools AWS Lambda](https://docs.aws.amazon.com//powertools/) 來防止重複叫用持久函數。

耐用函數會接收具有執行名稱的承載，並使用長時間執行的邏輯處理所有記錄：

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (payload: any, context: DurableContext) => {
    const sqsEvent = payload.event;
    
    // Process each record with complex, multi-step logic
    const results = await context.map(
      sqsEvent.Records,
      async (ctx, record) => {
        const validated = await ctx.step('validate', async () => {
          return validateOrder(JSON.parse(record.body));
        });
        
        // Wait for external approval (could take hours or days)
        const approval = await ctx.waitForCallback(
          'approval',
          async (callbackId) => {
            await requestApproval(callbackId, validated);
          },
          { timeout: { hours: 48 } }
        );
        
        // Complete processing
        return await ctx.step('complete', async () => {
          return completeOrder(validated, approval);
        });
      }
    );
    
    return { statusCode: 200, processed: results.getResults().length };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
from aws_durable_execution_sdk_python.config import Duration, WaitForCallbackConfig
from collections.abc import Sequence
import json

def validate_order(order_data: dict) -> dict:
    """Validate order data - always passes."""
    return order_data

def request_approval(callback_id: str, validated_order: dict) -> None:
    """Request approval for the order - always passes."""
    pass

def complete_order(validated_order: dict, approval_result: str) -> dict:
    """Complete the order processing - always passes."""
    return validated_order

@durable_execution
def lambda_handler(payload, context: DurableContext):
    sqs_event = payload['event']

    def process_record(
        ctx: DurableContext, 
        record: dict, 
        index: int, 
        items: Sequence[dict]
    ) -> dict:
        validated = ctx.step(
            lambda _: validate_order(json.loads(record['body'])),
            name=f'validate-{index}'
        )

        approval = ctx.wait_for_callback(
            submitter=lambda callback_id, wait_ctx: request_approval(callback_id, validated),
            name=f'approval-{index}',
            config=WaitForCallbackConfig(timeout=Duration.from_seconds(172800))
        )

        return ctx.step(
            lambda _: complete_order(validated, approval),
            name=f'complete-{index}'
        )

    results = context.map(
        inputs=sqs_event['Records'],
        func=process_record,
        name='process-records'
    )

    return {
        'statusCode': 200, 
        'started': results.started_count,
        'completed': results.success_count,
        'failed': results.failure_count,
        'total': results.total_count
    }
```

------

### 關鍵考量
<a name="durable-esm-intermediary-tradeoffs"></a>

此模式會取消事件來源映射與持久性執行的耦合，以移除 15 分鐘執行限制。中介函數會在啟動耐久執行後立即傳回，允許事件來源映射繼續處理。然後，耐用函數會視需要獨立執行。

中介函數會在叫用持久性函數時成功，而不是在持久性執行完成時成功。如果稍後持久性執行失敗，則事件來源映射不會重試，因為它已成功處理批次。在耐用函數中實作錯誤處理，並為失敗的執行設定無效字母佇列。

使用執行名稱參數來確保等冪執行開始。如果事件來源映射重試中介函數，則耐久函數不會啟動重複的執行，因為執行名稱已存在。

## 支援的事件來源
<a name="durable-esm-supported-sources"></a>

耐用函數支援使用事件來源映射的所有 Lambda 事件來源：
+ Amazon SQS 佇列 （標準和 FIFO)
+ Kinesis 串流
+ DynamoDB Streams
+ Amazon Managed Streaming for Apache Kafka (Amazon MSK)
+ 自我管理的 Apache Kafka
+ Amazon MQ (ActiveMQ 和 RabbitMQ)
+ Amazon DocumentDB 變更串流

呼叫耐用函數時，所有事件來源類型都受限於 15 分鐘的耐久執行限制。

# 重試 Lambda 耐用函數
<a name="durable-execution-sdk-retries"></a>

耐用的 函數提供自動重試功能，讓您的應用程式能夠適應暫時性故障。開發套件處理兩個層級的重試：商業邏輯故障的步驟重試，以及基礎設施故障的後端重試。

## 步驟重試
<a name="durable-step-retries"></a>

當步驟中發生未攔截的例外狀況時，軟體開發套件會根據設定的重試策略自動重試步驟。步驟重試是檢查點操作，可讓 SDK 暫停執行並在稍後繼續，而不會遺失進度。

### 步驟重試行為
<a name="durable-step-retry-behavior"></a>

下表說明 SDK 在步驟中如何處理例外狀況：


| 案例 | 發生的情況 | 計量影響 | 
| --- | --- | --- | 
| 步驟中剩餘重試嘗試的例外狀況 | SDK 會建立重試的檢查點，並暫停函數。在下次調用時，步驟會重試已設定的退避延遲。 | 1 操作 \$1 錯誤承載大小 | 
| 步驟中的例外狀況，沒有剩餘的重試嘗試 | 步驟失敗並擲回例外狀況。如果您的處理常式程式碼未擷取此例外狀況，則整個執行會失敗。 | 1 操作 \$1 錯誤承載大小 | 

當步驟需要重試時，開發套件會檢查重試狀態，並在沒有其他工作執行時結束 Lambda 調用。這可讓 SDK 實作退避延遲，而不會耗用運算資源。函數會在退避期間後自動恢復。

### 設定步驟重試策略
<a name="durable-step-retry-configuration"></a>

設定重試策略以控制步驟處理失敗的方式。您可以指定重試的嘗試次數上限、退避間隔和條件。

**最大嘗試次數的指數退避：**

------
#### [ TypeScript ]

```
const result = await context.step('call-api', async () => {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 5) {
      return { shouldRetry: false };
    }
    // Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    const delay = Math.min(2 * Math.pow(2, attemptCount - 1), 300);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 5:
        return {'should_retry': False}
    # Exponential backoff: 2s, 4s, 8s, 16s, 32s (capped at 300s)
    delay = min(2 * (2 ** (attempt_count - 1)), 300)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_external_api(),
    name='call-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**固定間隔退避：**

------
#### [ TypeScript ]

```
const orders = await context.step('query-orders', async () => {
  return await queryDatabase(event.userId);
}, {
  retryStrategy: (error, attemptCount) => {
    if (attemptCount >= 3) {
      return { shouldRetry: false };
    }
    return { shouldRetry: true, delay: { seconds: 5 } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    if attempt_count >= 3:
        return {'should_retry': False}
    return {'should_retry': True, 'delay': 5}

orders = context.step(
    lambda _: query_database(event['userId']),
    name='query-orders',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**條件式重試 （僅重試特定錯誤）：**

------
#### [ TypeScript ]

```
const result = await context.step('call-rate-limited-api', async () => {
  const response = await fetch('https://api.example.com/data');
  
  if (response.status === 429) throw new Error('RATE_LIMIT');
  if (response.status === 504) throw new Error('TIMEOUT');
  if (!response.ok) throw new Error(`API_ERROR_${response.status}`);
  
  return await response.json();
}, {
  retryStrategy: (error, attemptCount) => {
    // Only retry rate limits and timeouts
    const isRetryable = error.message === 'RATE_LIMIT' || error.message === 'TIMEOUT';
    
    if (!isRetryable || attemptCount >= 3) {
      return { shouldRetry: false };
    }
    
    // Exponential backoff: 1s, 2s, 4s (capped at 30s)
    const delay = Math.min(Math.pow(2, attemptCount - 1), 30);
    return { shouldRetry: true, delay: { seconds: delay } };
  }
});
```

------
#### [ Python ]

```
def retry_strategy(error, attempt_count):
    # Only retry rate limits and timeouts
    is_retryable = str(error) in ['RATE_LIMIT', 'TIMEOUT']
    
    if not is_retryable or attempt_count >= 3:
        return {'should_retry': False}
    
    # Exponential backoff: 1s, 2s, 4s (capped at 30s)
    delay = min(2 ** (attempt_count - 1), 30)
    return {'should_retry': True, 'delay': delay}

result = context.step(
    lambda _: call_rate_limited_api(),
    name='call-rate-limited-api',
    config=StepConfig(retry_strategy=retry_strategy)
)
```

------

**停用重試：**

------
#### [ TypeScript ]

```
const isDuplicate = await context.step('check-duplicate', async () => {
  return await checkIfOrderExists(event.orderId);
}, {
  retryStrategy: () => ({ shouldRetry: false })
});
```

------
#### [ Python ]

```
is_duplicate = context.step(
    lambda _: check_if_order_exists(event['orderId']),
    name='check-duplicate',
    config=StepConfig(
        retry_strategy=lambda error, attempt: {'should_retry': False}
    )
)
```

------

當重試策略傳回 時`shouldRetry: false`，步驟會立即失敗，而不會重試。將此用於不應重試的操作，例如等冪等性檢查或具有無法安全重複之副作用的操作。

## 步驟外部的例外狀況
<a name="durable-handler-exceptions"></a>

當您的處理常式程式碼中發生未攔截的例外狀況，但在任何步驟之外，軟體開發套件會將執行標記為失敗。這可確保正確擷取和報告應用程式邏輯中的錯誤。


| 案例 | 發生的情況 | 計量影響 | 
| --- | --- | --- | 
| 任何步驟外處理常式程式碼的例外狀況 | SDK 會將執行標記為 FAILED，並傳回錯誤。系統不會自動重試例外狀況。 | 錯誤承載大小 | 

若要啟用易出錯程式碼的自動重試，請使用重試策略將其包裝在步驟中。步驟提供可設定退避的自動重試，而步驟外的程式碼會立即失敗。

## 後端重試
<a name="durable-backend-retries"></a>

當 Lambda 遇到基礎設施故障、執行時間錯誤，或開發套件無法與持久的執行服務通訊時，就會發生後端重試。Lambda 會自動重試這些失敗，以協助您的耐用函數從暫時性基礎設施問題中復原。

### 後端重試案例
<a name="durable-backend-retry-scenarios"></a>

當函數遇到下列情況時，Lambda 會自動重試函數：
+ **內部服務錯誤** - 當 Lambda 或持久性執行服務傳回 5xx 錯誤時，表示暫時服務問題。
+ **限流** - 當函數因並行限制或服務配額而限流時。
+ **逾時** - 當 SDK 在逾時期間內無法達到持久的執行服務時。
+ **沙盒初始化失敗** - 當 Lambda 無法初始化執行環境時。
+ **執行時間錯誤** - 當 Lambda 執行時間遇到函數程式碼以外的錯誤，例如out-of-memory錯誤或程序當機。
+ **無效的檢查點字符錯誤** - 當檢查點字符不再有效時，通常是由於服務端狀態變更。

下表說明 SDK 如何處理這些案例：


| 案例 | 發生的情況 | 計量影響 | 
| --- | --- | --- | 
| 耐用處理常式外的執行時間錯誤 (OOM、逾時、當機） | Lambda 會自動重試調用。開發套件會從最後一個檢查點重播，略過完成的步驟。 | 錯誤承載大小 \$1 每次重試 1 個操作 | 
| 呼叫 / GetDurableExecutionState APIs時發生服務錯誤 (5xx) CheckpointDurableExecution 或逾時 | Lambda 會自動重試調用。開發套件會從最後一個檢查點重播。 | 錯誤承載大小 \$1 每次重試 1 個操作 | 
| 呼叫 / GetDurableExecutionState APIs 時調節 (429) CheckpointDurableExecution 或無效的檢查點字符 | Lambda 會自動重試具有指數退避的調用。開發套件會從最後一個檢查點重播。 | 錯誤承載大小 \$1 每次重試 1 個操作 | 
| 當 / GetDurableExecutionState APIs時，用戶端錯誤 CheckpointDurableExecution (4xx，429 和無效的字符除外） | SDK 會將執行標記為 FAILED。不會自動重試，因為錯誤表示永久問題。 | 錯誤承載大小 | 

後端重試會使用指數退避並繼續，直到函數成功或達到執行逾時為止。在重播期間，軟體開發套件會略過已完成的檢查點，並繼續從上次成功的操作執行，確保您的函數不會重新執行已完成的工作。

## 重試最佳實務
<a name="durable-retry-best-practices"></a>

設定重試策略時，請遵循下列最佳實務：
+ **設定明確的重試策略** - 不要依賴生產環境中的預設重試行為。為您的使用案例使用適當的最大嘗試次數和退避間隔來設定明確的重試策略。
+ **使用條件式重試** - `shouldRetry` 實作邏輯僅重試暫時性錯誤 （速率限制、逾時），並在永久錯誤時快速失敗 （驗證失敗，找不到）。
+ **設定適當的最大嘗試次數** - 在彈性和執行時間之間取得平衡。太多重試可能會延遲故障偵測，而太少會導致不必要的故障。
+ **使用指數退避** - 指數退避可減少下游服務的負載，並提高從暫時性故障中復原的可能性。
+ **在步驟中包裝容易出錯的程式碼** - 無法自動重試步驟外部的程式碼。使用重試策略在步驟中包裝外部 API 呼叫、資料庫查詢和其他容易出錯的操作。
+ **監控重試指標** - 在 Amazon CloudWatch 中追蹤步驟重試操作和執行失敗，以識別模式並最佳化重試策略。

# 冪等性
<a name="durable-execution-idempotency"></a>

耐用的 函數提供內建的冪等性，以便從執行名稱開始執行。當您提供執行名稱時，Lambda 會使用它來防止重複的執行，並啟用呼叫請求的安全重試。根據預設at-least-once執行語意 - 在重播期間，軟體開發套件會傳回檢查點結果，而不會重新執行已完成的步驟，但您的商業邏輯必須等冪，才能在完成之前處理潛在的重試。

**注意**  
Lambda 事件來源映射 (ESM) 不支援啟動時的冪等性。因此，每次叫用 （包括重試） 都會啟動新的耐久執行。若要使用事件來源映射確保等冪性執行，請在函數程式碼中實作等冪性邏輯，例如使用 [Powertools for AWS Lambda](https://docs.aws.amazon.com//powertools/)或使用一般 Lambda 函數做為代理 （分派器），以使用等冪性金鑰 （執行名稱參數） 叫用持久性函數。

## 執行名稱
<a name="durable-idempotency-execution-names"></a>

您可以在叫用耐用函數時提供執行名稱。執行名稱做為等冪性金鑰，可讓您安全地重試調用請求，而無需建立重複的執行。如果您未提供名稱，Lambda 會自動產生唯一的執行 ID。

執行名稱在您的帳戶和區域中必須是唯一的。當您使用已存在的執行名稱叫用函數時，Lambda 行為取決於現有執行的狀態以及承載是否相符。

## 冪等行為
<a name="durable-idempotency-behavior"></a>

下表說明 Lambda 如何處理調用請求，取決於您是否提供執行名稱、現有的執行狀態，以及承載是否相符：


| 案例 | 提供的名稱？ | 現有執行狀態 | 承載是否相同？ | Behavior (行為) | 
| --- | --- | --- | --- | --- | 
| 1 | 否 | N/A | N/A | 新的執行已開始：Lambda 會產生唯一的執行 ID，並啟動新的執行 | 
| 2 | 是 | 從未存在或保留過期 | N/A | 新的執行已開始：Lambda 使用提供的名稱啟動新的執行 | 
| 3 | 是 | 執行中 | 是 | 等冪啟動：Lambda 會傳回現有的執行資訊，而不會啟動重複的執行資訊。對於同步調用，這可做為重新連接至執行中的執行 | 
| 4 | 是 | 執行中 | 否 | 錯誤：Lambda 傳回DurableExecutionAlreadyExists錯誤，因為具有此名稱的執行已使用不同的承載執行 | 
| 5 | 是 | 關閉 （成功、失敗、停止或逾時） | 是 | 等冪啟動：Lambda 會傳回現有的執行資訊，而不啟動新的執行。傳回已關閉的執行結果 | 
| 6 | 是 | 關閉 （成功、失敗、停止或逾時） | 否 | 錯誤：Lambda 傳回DurableExecutionAlreadyExists錯誤，因為具有此名稱的執行已在不同的承載下完成 | 

**注意**  
案例 3 和 5 示範等冪行為，其中 Lambda 透過傳回現有的執行資訊而非建立重複項目，安全地處理重複的調用請求。

## 步驟冪等性
<a name="durable-idempotency-steps"></a>

根據預設at-least-once執行語意。當您的函數在等待、回呼或失敗後重播時，軟體開發套件會根據檢查點日誌檢查每個步驟。對於已完成的步驟，開發套件會傳回檢查點結果，而不會重新執行步驟邏輯。不過，如果步驟失敗或函數在步驟完成之前中斷，則該步驟可能會執行多次。

您在步驟中包裝的業務邏輯必須等冪，才能處理潛在的重試。使用等冪性金鑰，以確保付款或資料庫寫入等操作只會執行一次，即使步驟重試也一樣。

**範例：在步驟中使用等冪索引鍵**

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency key once
    const idempotencyKey = await context.step('generate-key', async () => {
      return randomUUID();
    });
    
    // Use idempotency key in payment API to prevent duplicate charges
    const payment = await context.step('process-payment', async () => {
      return paymentAPI.charge({
        amount: event.amount,
        idempotencyKey: idempotencyKey
      });
    });
    
    return { statusCode: 200, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency key once
    idempotency_key = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-key'
    )
    
    # Use idempotency key in payment API to prevent duplicate charges
    payment = context.step(
        lambda _: payment_api.charge(
            amount=event['amount'],
            idempotency_key=idempotency_key
        ),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'payment': payment}
```

------

您可以將執行模式設定為 ，將步驟設定為at-most-once的執行語意`AT_MOST_ONCE_PER_RETRY`。這可確保每次重試嘗試時，步驟最多執行一次，但如果函數在步驟完成之前中斷，則可能完全無法執行。

SDK 透過驗證該步驟名稱和順序在重播期間符合檢查點日誌，強制執行確定性重播。如果您的程式碼嘗試以不同的順序或以不同的名稱執行步驟，開發套件會擲回 `NonDeterministicExecutionError`。

**重播如何搭配已完成的步驟運作：**

1. 第一次叫用：函數執行步驟 A、建立檢查點，然後等待

1. 第二次調用 （等待後）：函數從頭開始重播，步驟 A 會立即傳回檢查點結果，而無需重新執行，然後繼續步驟 B

1. 第三次叫用 （在另一個等待之後）：函數從頭開始重播，步驟 A 和 B 會立即傳回檢查點結果，然後繼續步驟 C

此重播機制可確保已完成的步驟不會重新執行，但您的商業邏輯仍必須等冪才能在完成之前處理重試。

# 測試 Lambda 耐用函數
<a name="durable-testing"></a>

AWS 為耐用函數提供專用測試 SDKs，可讓您在本機和雲端執行和檢查執行。為您的語言安裝測試 SDK：

------
#### [ TypeScript ]

```
npm install --save-dev @aws/aws-durable-execution-sdk-js-testing
```

如需完整的文件和範例，請參閱 GitHub 上的 [TypeScript 測試 SDK](https://github.com/aws/aws-durable-execution-sdk-js/tree/development/packages/aws-durable-execution-sdk-js-testing)。

------
#### [ Python ]

```
pip install aws-durable-execution-sdk-python-testing
```

如需完整的文件和範例，請參閱 GitHub 上的 [Python 測試 SDK](https://github.com/aws/aws-durable-execution-sdk-python-testing)。

------

測試 SDK 提供兩種測試模式：快速單元測試的本機測試，以及針對已部署函數的整合測試的雲端測試。

## 本機測試
<a name="durable-local-testing"></a>

本機測試會在開發環境中執行您的耐久函數，而不需要部署的資源。測試執行器會直接執行函數程式碼，並擷取所有要檢查的操作。

針對單元測試、測試驅動的開發和 CI/CD 管道使用本機測試。測試會在本機執行，無需網路延遲或額外費用。

**測試範例：**

------
#### [ TypeScript ]

```
import { withDurableExecution } from '@aws/aws-durable-execution-sdk-js';
import { DurableFunctionTestRunner } from '@aws/aws-durable-execution-sdk-js-testing';

const handler = withDurableExecution(async (event, context) => {
  const result = await context.step('calculate', async () => {
    return event.a + event.b;
  });
  return result;
});

test('addition works correctly', async () => {
  const runner = new DurableFunctionTestRunner({ handler });
  const result = await runner.run({ a: 5, b: 3 });
  
  expect(result.status).toBe('SUCCEEDED');
  expect(result.result).toBe(8);
  
  const step = result.getStep('calculate');
  expect(step.result).toBe(8);
});
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
from aws_durable_execution_sdk_python_testing import DurableFunctionTestRunner
from aws_durable_execution_sdk_python.execution import InvocationStatus

@durable_execution
def handler(event: dict, context: DurableContext) -> int:
    result = context.step(lambda _: event["a"] + event["b"], name="calculate")
    return result

def test_addition():
    runner = DurableFunctionTestRunner(handler=handler)
    with runner:
        result = runner.run(input={"a": 5, "b": 3}, timeout=10)
    
    assert result.status is InvocationStatus.SUCCEEDED
    assert result.result == 8
    
    step = result.get_step("calculate")
    assert step.result == 8
```

------

測試執行器會擷取執行狀態，包括最終結果、個別步驟結果、等待操作、回呼和任何錯誤。您可以依名稱檢查操作，或逐一查看所有操作，以驗證執行行為。

### 執行存放區
<a name="durable-execution-stores"></a>

測試 SDK 使用執行存放區來保留測試執行資料。根據預設，測試會使用快速且不需要清除的記憶體內存放區。若要偵錯或分析執行歷史記錄，您可以使用將執行儲存為 JSON 檔案的檔案系統存放區。

**記憶體內存放區 （預設）：**

記憶體內存放區會在測試執行期間將執行資料保留在記憶體中。當測試完成時，資料會遺失，因此非常適合標準單元測試和 CI/CD 管道，您不需要在測試完成後檢查執行。

**檔案系統存放區：**

檔案系統存放區會將執行資料保留為 JSON 檔案至磁碟。每個執行都會儲存在個別的檔案中，以便在測試完成後輕鬆檢查執行歷史記錄。在偵錯複雜測試失敗或分析執行模式時，請使用 檔案系統存放區。

使用環境變數設定存放區：

```
# Use filesystem store
export AWS_DEX_STORE_TYPE=filesystem
export AWS_DEX_STORE_PATH=./test-executions

# Run tests
pytest tests/
```

執行檔案會以已淨化的名稱存放，並包含完整的執行狀態，包括操作、檢查點和結果。如果儲存目錄不存在，檔案系統存放區會自動建立儲存目錄。

## 雲端測試
<a name="durable-cloud-testing"></a>

雲端測試會叫用 中部署的耐用函數 AWS ，並使用 Lambda API 擷取其執行歷史記錄。使用雲端測試，透過真正的 AWS 服務和組態來驗證類似生產環境中的行為。

雲端測試需要已部署的函數和 AWS 登入資料，具有叫用函數和讀取執行歷史記錄的許可：

```
{
    "Version": "2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction",
                "lambda:GetDurableExecution",
                "lambda:GetDurableExecutionHistory"
            ],
            "Resource": "arn:aws:lambda:region:account-id:function:function-name"
        }
    ]
}
```

**雲端測試範例：**

------
#### [ TypeScript ]

```
import { DurableFunctionCloudTestRunner } from '@aws/aws-durable-execution-sdk-js-testing';

test('deployed function processes orders', async () => {
  const runner = new DurableFunctionCloudTestRunner({
    functionName: 'order-processor',
    region: 'us-east-1'
  });
  
  const result = await runner.run({ orderId: 'order-123' });
  
  expect(result.status).toBe('SUCCEEDED');
  expect(result.result.status).toBe('completed');
});
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python_testing import (
    DurableFunctionCloudTestRunner,
    DurableFunctionCloudTestRunnerConfig
)

def test_deployed_function():
    config = DurableFunctionCloudTestRunnerConfig(
        function_name="order-processor",
        region="us-east-1"
    )
    runner = DurableFunctionCloudTestRunner(config=config)
    
    result = runner.run(input={"orderId": "order-123"})
    
    assert result.status is InvocationStatus.SUCCEEDED
    assert result.result["status"] == "completed"
```

------

雲端測試會叫用實際部署的函數，並從中擷取執行歷史記錄 AWS。這可讓您驗證與其他 AWS 服務的整合、驗證效能特性，以及使用類似生產的資料和組態進行測試。

## 測試項目
<a name="durable-testing-patterns"></a>

透過驗證執行結果、操作行為和錯誤處理來測試耐用的函數。專注於商業邏輯正確性，而不是實作詳細資訊。

**驗證執行結果：**檢查函數是否傳回指定輸入的預期值。測試成功執行和錯誤案例，以確保函數適當地處理無效的輸入。

**檢查操作執行：**確認步驟、等待和回呼如預期般執行。檢查步驟結果，以確保中繼操作產生正確的值。驗證等待操作是否設定適當的逾時，以及回呼是否使用正確的設定建立。

**測試錯誤處理：**當指定無效的輸入時，使用描述性錯誤訊息來驗證函數是否正確失敗。模擬暫時性失敗並確認操作重試正確，以測試重試行為。檢查永久故障是否不會觸發不必要的重試。

**驗證工作流程：**對於多步驟工作流程，驗證操作以正確的順序執行。測試條件式分支，以確保不同的執行路徑正常運作。驗證平行操作同時執行並產生預期結果。

SDK 文件儲存庫包含廣泛的測試模式範例，包括多步驟工作流程、錯誤案例、逾時處理和輪詢模式。

## 測試策略
<a name="durable-testing-strategy"></a>

在開發期間和 CI/CD 管道中使用單元測試的本機測試。本機測試會快速執行，不需要 AWS 登入資料，並提供程式碼變更的立即意見回饋。撰寫本機測試，以驗證商業邏輯、錯誤處理和操作行為。

在部署到生產環境之前，使用雲端測試進行整合測試。雲端測試會驗證具有真實 AWS 服務和組態的行為、驗證效能特性，以及測試end-to-end工作流程。在預備環境中執行雲端測試，以便在整合問題到達生產環境之前加以發現。

在本機測試中模擬外部相依性，以隔離函數邏輯並保持快速測試。使用雲端測試來驗證與資料庫、APIs和其他 服務等外部 AWS 服務的實際整合。

撰寫重點測試來驗證一個特定行為。使用描述性測試名稱來說明正在測試的內容。將相關測試分組在一起，並針對常見的設定程式碼使用測試固定裝置。保持簡單的測試，並避免難以理解的複雜測試邏輯。

## 偵錯失敗
<a name="durable-testing-debugging"></a>

當測試失敗時，請檢查執行結果以了解發生錯誤。檢查執行狀態，以查看函數是否成功、失敗或逾時。讀取錯誤訊息以了解失敗原因。

檢查個別操作結果，找出行為偏離預期的位置。檢查步驟結果以查看產生了哪些值。驗證操作排序，以確認依預期順序執行的操作。計數操作以確保建立正確數量的步驟、等待和回呼。

常見問題包括可在重播時產生不同結果的非確定性程式碼、透過重播期間中斷的全域變數共用狀態，以及因條件式邏輯錯誤而缺少操作。使用標準除錯器和記錄來逐步執行函數程式碼和追蹤執行流程。

對於雲端測試，請檢查 CloudWatch Logs 中的執行歷史記錄，以查看詳細的操作日誌。使用追蹤來追蹤跨 服務的執行流程，並識別瓶頸。

# 監控耐用的 函數
<a name="durable-monitoring"></a>

您可以使用 CloudWatch 指標、CloudWatch Logs 和追蹤來監控耐用的函數。由於耐用的函數可以長時間執行並跨越多個函數叫用，因此監控它們需要了解其獨特的執行模式，包括檢查點、狀態轉換和重播行為。

## CloudWatch 指標
<a name="durable-monitoring-metrics"></a>

Lambda 會自動將指標發佈至 CloudWatch，無需額外費用。耐用函數提供超出標準 Lambda 指標的額外指標，協助您監控長時間執行的工作流程、狀態管理和資源使用率。

### 耐用的執行指標
<a name="durable-monitoring-execution-metrics"></a>

Lambda 會針對持久性執行發出下列指標：


| 指標 | Description | 
| --- | --- | 
| ApproximateRunningDurableExecutions | 處於 RUNNING 狀態的耐久執行數量 | 
| ApproximateRunningDurableExecutionsUtilization | 目前使用中您帳戶的最大執行中耐久執行配額的百分比 | 
| DurableExecutionDuration | 持久執行保持為 RUNNING 狀態的經過時間，以毫秒為單位 | 
| DurableExecutionStarted | 啟動的持久性執行數量 | 
| DurableExecutionStopped | 使用 StopDurableExecution API 停止的耐久執行數目 | 
| DurableExecutionSucceeded | 成功完成的持久性執行數量 | 
| DurableExecutionFailed | 因失敗而完成的耐久執行數量 | 
| DurableExecutionTimedOut | 超過其設定執行逾時的持久執行數量 | 
| DurableExecutionOperations | 在持久性執行中執行的累計操作數 （上限：3，000) | 
| DurableExecutionStorageWrittenBytes | 持久執行持續的累積資料量，以位元組為單位 （上限：100 MB) | 

### CloudWatch 指標
<a name="durable-monitoring-standard-metrics"></a>

Lambda 會發出耐用函數的標準調用、效能和並行指標。由於耐久的執行可能會在通過檢查點和重播時跨越多個函數叫用，因此這些指標的行為與標準函數不同：
+ **調用：**計算每個函數調用，包括重播。單一耐久執行可以產生多個調用資料點。
+ **持續時間：**分別測量每個函數叫用。`DurableExecutionDuration` 用於單一持久性執行所花費的總時間。
+ **錯誤：**追蹤函數叫用失敗。`DurableExecutionFailed` 用於執行層級失敗。

如需標準 Lambda 指標的完整清單，請參閱 [Lambda 函數的指標類型](https://docs.aws.amazon.com//lambda/latest/dg/monitoring-metrics-types.html)。

### 建立 CloudWatch 警示
<a name="durable-monitoring-alarms"></a>

建立 CloudWatch 警示，以在指標超過閾值時通知您。常見警示包括：
+ `ApproximateRunningDurableExecutionsUtilization` 超過配額的 80%
+ `DurableExecutionFailed` 增加超過閾值
+ `DurableExecutionTimedOut` 表示執行逾時
+ `DurableExecutionStorageWrittenBytes` 接近儲存限制

如需詳細資訊，[請參閱使用 CloudWatch 警示。](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html)

## EventBridge 事件
<a name="durable-monitoring-eventbridge"></a>

Lambda 會將持久的執行狀態變更事件發佈至 EventBridge。您可以使用這些事件來觸發工作流程、傳送通知，或追蹤耐久函數的執行生命週期變更。

### 持久性執行狀態變更事件
<a name="durable-eventbridge-status-changes"></a>

每當持久性執行變更狀態時，Lambda 都會向 EventBridge 發出事件。這些事件具有下列特性：
+ **來源**：`aws.lambda`
+ **詳細資訊類型：** `Durable Execution Status Change`

狀態變更事件會針對下列執行狀態發佈：
+ `RUNNING` - 執行已開始
+ `SUCCEEDED` - 執行成功完成
+ `STOPPED` - 使用 StopDurableExecution API 停止執行
+ `FAILED` - 執行失敗並發生錯誤
+ `TIMED_OUT` - 執行超過設定的逾時

下列範例顯示持久的執行狀態變更事件：

```
{
  "version": "0",
  "id": "d019b03c-a8a3-9d58-85de-241e96206538",
  "detail-type": "Durable Execution Status Change",
  "source": "aws.lambda",
  "account": "123456789012",
  "time": "2025-11-20T13:08:22Z",
  "region": "us-east-1",
  "resources": [],
  "detail": {
    "durableExecutionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:$LATEST/durable-execution/090c4189-b18b-4296-9d0c-cfd01dc3a122/9f7d84c9-ea3d-3ffc-b3e5-5ec51c34ffc9",
    "durableExecutionName": "order-123",
    "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:my-function:2",
    "status": "RUNNING",
    "startTimestamp": "2025-11-20T13:08:22.345Z"
  }
}
```

對於終端機狀態 (`SUCCEEDED`、`STOPPED`、`FAILED`、`TIMED_OUT`)，事件包含指出執行完成時間`endTimestamp`的欄位。

### 建立 EventBridge 規則
<a name="durable-eventbridge-rules"></a>

建立規則，將持久的執行狀態變更事件路由到 Amazon Simple Notification Service、Amazon Simple Queue Service 或其他 Lambda 函數等目標。

下列範例會建立符合所有持久執行狀態變更的規則：

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"]
}
```

下列範例會建立僅符合失敗執行的規則：

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"],
  "detail": {
    "status": ["FAILED"]
  }
}
```

下列範例會建立符合特定函數狀態變更的規則：

```
{
  "source": ["aws.lambda"],
  "detail-type": ["Durable Execution Status Change"],
  "detail": {
    "functionArn": [{
      "prefix": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
    }]
  }
}
```

如需建立規則的詳細資訊，請參閱《[EventBridge 使用者指南》中的 Amazon EventBridge 教學](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-tutorial.html)課程。 EventBridge 

## AWS X-Ray 追蹤
<a name="durable-monitoring-xray"></a>

您可以在耐用的函數上啟用 X-Ray 追蹤。Lambda 會將 X-Ray 追蹤標頭傳遞至持久性執行，讓您追蹤整個工作流程的請求。

若要使用 Lambda 主控台啟用 X-Ray； 追蹤，請選擇您的函數，然後選擇組態、監控和操作工具，然後在 X-Ray 下開啟主動追蹤。

若要使用 啟用 X-Ray 追蹤 AWS CLI：

```
aws lambda update-function-configuration \
    --function-name my-durable-function \
    --tracing-config Mode=Active
```

若要使用 啟用 AWS X-Ray 追蹤 AWS SAM：

```
Resources:
  MyDurableFunction:
    Type: AWS::Serverless::Function
    Properties:
      Tracing: Active
      DurableConfig:
        ExecutionTimeout: 3600
```

如需 X-Ray 的詳細資訊，[請參閱 AWS X-Ray 開發人員指南。](https://docs.aws.amazon.com//xray/latest/devguide/aws-xray.html)

# Lambda 耐用函數的最佳實務
<a name="durable-best-practices"></a>

耐用函數使用重播型執行模型，需要與傳統 Lambda 函數不同的模式。遵循這些最佳實務來建置可靠且符合成本效益的工作流程。

## 寫入確定性程式碼
<a name="durable-determinism"></a>

在重播期間，您的函數會從頭開始執行，並且必須遵循與原始執行相同的執行路徑。持久性操作之外的程式碼必須具有決定性，在相同的輸入下產生相同的結果。

**以步驟包裝非確定性操作：**
+ 隨機數產生和 UUIDs
+ 目前時間或時間戳記
+ 外部 API 呼叫和資料庫查詢
+ 檔案系統操作

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';
import { randomUUID } from 'crypto';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate transaction ID inside a step
    const transactionId = await context.step('generate-transaction-id', async () => {
      return randomUUID();
    });
    
    // Use the same ID throughout execution, even during replay
    const payment = await context.step('process-payment', async () => {
      return processPayment(event.amount, transactionId);
    });
    
    return { statusCode: 200, transactionId, payment };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate transaction ID inside a step
    transaction_id = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-transaction-id'
    )
    
    # Use the same ID throughout execution, even during replay
    payment = context.step(
        lambda _: process_payment(event['amount'], transaction_id),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'transactionId': transaction_id, 'payment': payment}
```

------

**Important (重要)**  
請勿使用全域變數或關閉在步驟之間共用狀態。透過傳回值傳遞資料。重播期間全域狀態中斷，因為步驟會傳回快取的結果，但全域變數會重設。

**避免關閉變動：**在關閉中擷取的變數可能會在重播期間遺失變動。步驟會傳回快取的結果，但步驟外的變數更新不會重播。

------
#### [ TypeScript ]

```
// ❌ WRONG: Mutations lost on replay
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    await context.step(async () => {
      total += item.price; // ⚠️ Mutation lost on replay!
      return saveItem(item);
    });
  }
  
  return { total }; // Inconsistent value!
});

// ✅ CORRECT: Accumulate with return values
export const handler = withDurableExecution(async (event, context) => {
  let total = 0;
  
  for (const item of items) {
    total = await context.step(async () => {
      const newTotal = total + item.price;
      await saveItem(item);
      return newTotal; // Return updated value
    });
  }
  
  return { total }; // Consistent!
});

// ✅ EVEN BETTER: Use map for parallel processing
export const handler = withDurableExecution(async (event, context) => {
  const results = await context.map(
    items,
    async (ctx, item) => {
      await ctx.step(async () => saveItem(item));
      return item.price;
    }
  );
  
  const total = results.getResults().reduce((sum, price) => sum + price, 0);
  return { total };
});
```

------
#### [ Python ]

```
# ❌ WRONG: Mutations lost on replay
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        context.step(
            lambda _: save_item_and_mutate(item, total),  # ⚠️ Mutation lost on replay!
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Inconsistent value!

# ✅ CORRECT: Accumulate with return values
@durable_execution
def handler(event, context: DurableContext):
    total = 0
    
    for item in items:
        total = context.step(
            lambda _: save_item_and_return_total(item, total),
            name=f'save-item-{item["id"]}'
        )
    
    return {'total': total}  # Consistent!

# ✅ EVEN BETTER: Use map for parallel processing
@durable_execution
def handler(event, context: DurableContext):
    def process_item(ctx, item):
        ctx.step(lambda _: save_item(item))
        return item['price']
    
    results = context.map(items, process_item)
    total = sum(results.get_results())
    
    return {'total': total}
```

------

## 等冪性的設計
<a name="durable-idempotency"></a>

由於重試或重播，操作可能會執行多次。非等冪性操作會導致重複的副作用，例如向客戶收費兩次或傳送多封電子郵件。

**使用等冪符記：在步驟中**產生符記，並將其包含在外部 API 呼叫中，以防止重複操作。

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Generate idempotency token once
    const idempotencyToken = await context.step('generate-idempotency-token', async () => {
      return crypto.randomUUID();
    });
    
    // Use token to prevent duplicate charges
    const charge = await context.step('charge-payment', async () => {
      return paymentService.charge({
        amount: event.amount,
        cardToken: event.cardToken,
        idempotencyKey: idempotencyToken
      });
    });
    
    return { statusCode: 200, charge };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext
import uuid

@durable_execution
def handler(event, context: DurableContext):
    # Generate idempotency token once
    idempotency_token = context.step(
        lambda _: str(uuid.uuid4()),
        name='generate-idempotency-token'
    )
    
    # Use token to prevent duplicate charges
    def charge_payment(_):
        return payment_service.charge(
            amount=event['amount'],
            card_token=event['cardToken'],
            idempotency_key=idempotency_token
        )
    
    charge = context.step(charge_payment, name='charge-payment')
    
    return {'statusCode': 200, 'charge': charge}
```

------

**使用at-most-once語意：**對於絕不能重複的關鍵操作 （金融交易、庫存扣除），請設定at-most-once的執行模式。

------
#### [ TypeScript ]

```
// Critical operation that must not duplicate
await context.step('deduct-inventory', async () => {
  return inventoryService.deduct(event.productId, event.quantity);
}, {
  executionMode: 'AT_MOST_ONCE_PER_RETRY'
});
```

------
#### [ Python ]

```
# Critical operation that must not duplicate
context.step(
    lambda _: inventory_service.deduct(event['productId'], event['quantity']),
    name='deduct-inventory',
    config=StepConfig(execution_mode='AT_MOST_ONCE_PER_RETRY')
)
```

------

**資料庫冪等性：**使用check-before-write模式、條件更新或 upsert 操作，以防止重複記錄。

## 有效率地管理狀態
<a name="durable-state-management"></a>

每個檢查點都會將狀態儲存為持久性儲存。大型狀態物件會增加成本、緩慢檢查點和影響效能。僅儲存必要的工作流程協調資料。

**保持最小狀態：**
+ 存放 IDs和參考，而非完整物件
+ 視需要在步驟內擷取詳細資訊
+ 針對大型資料使用 Amazon S3 或 DynamoDB，以 狀態傳遞參考
+ 避免在步驟之間傳遞大型承載

------
#### [ TypeScript ]

```
import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(
  async (event: any, context: DurableContext) => {
    // Store only the order ID, not the full order object
    const orderId = event.orderId;
    
    // Fetch data within each step as needed
    await context.step('validate-order', async () => {
      const order = await orderService.getOrder(orderId);
      return validateOrder(order);
    });
    
    await context.step('process-payment', async () => {
      const order = await orderService.getOrder(orderId);
      return processPayment(order);
    });
    
    return { statusCode: 200, orderId };
  }
);
```

------
#### [ Python ]

```
from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event, context: DurableContext):
    # Store only the order ID, not the full order object
    order_id = event['orderId']
    
    # Fetch data within each step as needed
    context.step(
        lambda _: validate_order(order_service.get_order(order_id)),
        name='validate-order'
    )
    
    context.step(
        lambda _: process_payment(order_service.get_order(order_id)),
        name='process-payment'
    )
    
    return {'statusCode': 200, 'orderId': order_id}
```

------

## 設計有效的步驟
<a name="durable-step-design"></a>

步驟是耐用函數中的基本工作單位。精心設計的步驟可讓工作流程更易於理解、偵錯和維護。

**步驟設計原則：**
+ **使用描述性名稱** - 類似 的名稱，`validate-order`而不是`step1`讓日誌和錯誤更容易理解
+ 將**名稱保持靜態** - 請勿將動態名稱與時間戳記或隨機值搭配使用。步驟名稱必須具有決定性才能重播
+ **平衡精細程度** - 將複雜的操作分解為重點步驟，但避免增加檢查點負荷的過小步驟
+ **群組相關操作** - 應該一起成功或失敗的操作屬於同一步驟

## 有效率地使用等待操作
<a name="durable-wait-operations"></a>

等待操作會暫停執行，而不會耗用資源或產生成本。使用它們，而不是讓 Lambda 保持執行狀態。

以**時間為基礎的等待：**使用 `context.wait()` 處理延遲，而非 `setTimeout`或 `sleep`。

**外部回呼：**等待外部系統`context.waitForCallback()`時使用 。一律設定逾時以防止無限期等待。

**輪詢：**使用 `context.waitForCondition()`搭配指數退避來輪詢外部服務，而不會讓它們過多。

------
#### [ TypeScript ]

```
// Wait 24 hours without cost
await context.wait({ seconds: 86400 });

// Wait for external callback with timeout
const result = await context.waitForCallback(
  'external-job',
  async (callbackId) => {
    await externalService.submitJob({
      data: event.data,
      webhookUrl: `https://api.example.com/callbacks/${callbackId}`
    });
  },
  { timeout: { seconds: 3600 } }
);
```

------
#### [ Python ]

```
# Wait 24 hours without cost
context.wait(86400)

# Wait for external callback with timeout
result = context.wait_for_callback(
    lambda callback_id: external_service.submit_job(
        data=event['data'],
        webhook_url=f'https://api.example.com/callbacks/{callback_id}'
    ),
    name='external-job',
    config=WaitForCallbackConfig(timeout_seconds=3600)
)
```

------

## 其他考量
<a name="durable-additional-considerations"></a>

**錯誤處理：**重試暫時性故障，例如網路逾時和速率限制。請勿重試永久失敗，例如無效的輸入或身分驗證錯誤。使用適當的最大嘗試次數和退避率來設定重試策略。如需詳細範例，請參閱[錯誤處理和重試](durable-execution-sdk-retries.md)。

**效能：**透過儲存參考而非完整承載，將檢查點大小降至最低。使用 `context.parallel()`和 `context.map()` 同時執行獨立操作。批次相關操作，以減少檢查點額外負荷。

**版本控制：**使用版本編號或別名叫用函數，將執行釘選到特定的程式碼版本。確保新的程式碼版本可以處理較舊版本的狀態。請勿重新命名步驟或以中斷重播的方式變更其行為。

**序列化：**將 JSON 相容類型用於操作輸入和結果。將日期轉換為 ISO 字串，並將自訂物件轉換為純物件，然後再將其傳遞至持久的操作。

**監控：**啟用具有執行 IDs結構化記錄。針對錯誤率和執行持續時間設定 CloudWatch 警示。使用追蹤來識別瓶頸。如需詳細指引，請參閱[監控和偵錯](durable-monitoring.md)。

**測試：**測試快樂路徑、錯誤處理和重播行為。回呼和等待的測試逾時案例。使用本機測試來縮短反覆運算時間。如需詳細指引，請參閱[測試耐用函數](durable-testing.md)。

**要避免的常見錯誤：**不要巢狀`context.step()`化呼叫，請改用子內容。在步驟中包裝非確定性操作。一律設定回呼的逾時。平衡步驟精細程度與檢查點額外負荷。儲存參考，而不是處於 狀態的大型物件。

## 其他資源
<a name="durable-additional-resources"></a>
+ [Python SDK 文件](https://github.com/aws/aws-durable-execution-sdk-python/tree/main/docs) - 完整的 API 參考、測試模式和進階範例
+ [TypeScript SDK 文件](https://github.com/aws/aws-durable-execution-sdk-js/tree/main/docs) - 完整的 API 參考、測試模式和進階範例