

# REL04-BP04 讓變異操作冪等
<a name="rel_prevent_interaction_failure_idempotent"></a>

 冪等服務確保每個請求只處理一次，因此發出多個相同請求的效果，與發出單一請求的效果相同。如此一來，用戶端可更輕鬆地重試，而不用擔心多次錯誤地處理請求。為此，用戶端可以使用冪等性字符發出 API 請求，每次重複請求時，都會使用這個權杖。冪等服務 API 會使用字符傳回回應，而該回應與第一次完成請求時傳回的回應相同，即使系統的基礎狀態已改變也一樣。

 在分散式系統中，相對容易的做法是最多執行一次動作 (用戶端只發出一個請求) 或至少執行一次動作 (持續發出請求，直到用戶端確認成功)。但很難保證動作*精準地執行一次*，使得發出多個相同的請求與發出單一請求效果相同。透過在 API 中使用冪等性字符，服務可以收到一次或多次變異請求，而不需建立重複的記錄或產生副作用。

 **預期成果：**您擁有一致、完整記錄且廣泛採用的方法，以確保所有元件和服務之間的冪等性。

 **常見的反模式：**
+  即使不需要，您仍一視同仁地套用冪等性。
+  您導入過於複雜的邏輯來實作冪等性。
+  您使用時間戳記做為冪等性的索引鍵。這樣可能會因為時鐘誤差或多個用戶端使用相同的時間戳記來套用變更，而造成不準確。
+  您為了冪等性而儲存整個承載。採用這個方法時，您會為每個請求儲存完整的資料承載，並在每個新請求中覆寫它。這可能會導致效能降低並影響可擴展性。
+  您在服務之間產生不一致的索引鍵。若索引鍵不一致，服務可能無法辨識重複的請求，進而導致意外的結果。

 **建立此最佳實務的優勢：**
+  可擴展性更大：系統可以處理重試和重複的請求，而不需執行額外的邏輯或複雜的狀態管理。
+  加強可靠性：冪等性可協助服務以一致的方式處理多個相同的請求，進而降低意外副作用或重複記錄的風險。這在經常發生網路故障和重試的分散式系統中尤其重要。
+  改善資料一致性：由於相同的請求會產生相同的回應，因此冪等性有助於在分散式系統之間保持資料一致性。這對於維護交易和操作的完整性相當重要。
+  錯誤處理：冪等性字符讓錯誤處理更簡單。如果用戶端因發生問題而未收到回應，它可以使用相同的冪等性字符安全地重新傳送請求。
+  操作透明度：冪等性可實現更有效的監控與記錄。服務可以透過冪等性字符來記錄請求，這樣更容易追蹤問題並進行偵錯。
+  簡化的 API 合約：它可以簡化用戶端和伺服器端系統之間的合約，並減少對資料處理錯誤的擔憂。

 **未建立此最佳實務時的曝險等級：**中 

## 實作指引
<a name="implementation-guidance"></a>

 在分散式系統中，最多執行一次動作 (用戶端只發出一個請求) 或至少執行一次動作 (用戶端持續發出請求直到確認成功) 是相對容易的做法。不過，實作*精確一次*的行為並不容易。若要達成此目的，您的用戶端應針對每個請求產生並提供冪等性字符。

 透過使用冪等性字符，服務就能區分新請求和重複的請求。當服務收到具有冪等性字符的請求時，它會檢查字符是否已使用。如果已使用字符，服務就會擷取並傳回預存回應。如果字符是新的，則服務會處理請求、一併儲存回應與字符，然後傳回回應。此機制會讓所有回應變成冪等，進而提高分散式系統的可靠性和一致性。

 冪等性也是事件驅動架構的重要行為。這些架構通常由訊息佇列支援，例如 Amazon SQS、Amazon MQ、Amazon Kinesis Streams 或 Amazon Managed Streaming for Apache Kafka (MSK)。在某些情況下，只發布一次的訊息可能會意外傳遞超過一次。當發布者在訊息中產生並包含冪等性字符時，它會要求處理收到的任何重複訊息時，不會對相同訊息產生重複的動作。取用者應追蹤收到的每個字符，並忽略包含重複字符的訊息。

 服務與取用者也應將收到的冪等性字符傳遞給其呼叫的任何下游服務。處理鏈中的每個下游服務都同樣負責確保實作冪等性，以免發生處理訊息超過一次的副作用。

### 實作步驟
<a name="implementation-steps"></a>

1.  **識別冪等操作** 

    確定哪些操作需要冪等性。這些通常包括 POST、PUT 和 DELETE HTTP 方法，以及資料庫插入、更新或刪除操作。不會改變狀態的操作 (例如唯讀查詢) 通常不需要冪等性，除非有副作用。

1.  **使用唯一識別碼** 

    在傳送者傳送的每個冪等操作請求中包含唯一的字符，無論是直接在請求中，還是做為其中繼資料的一部分 (例如 HTTP 標頭)。這可讓接收者辨識和處理重複的請求或操作。常用於字符的識別碼包括[通用唯一識別碼 (UUID)](https://datatracker.ietf.org/doc/html/rfc9562) 和 [K-Sortable Unique Identifiers (KSUID)](https://github.com/segmentio/ksuid)。

1.  **追蹤和管理狀態** 

    維護工作負載中每個操作或請求的狀態。這可以透過在資料庫、快取或其他持久性存放區中儲存冪等性字符及對應的狀態 (如待處理、已完成或失敗) 來達成。此狀態資訊可讓工作負載識別和處理重複的請求或操作。

    如有需要，可使用適當的並行控制機制來維持一致性和單元性，例如鎖定、交易或積極並行控制。包括記錄冪等性字符和執行所有與處理請求相關聯的改變操作程序。這樣做有助於防止競爭條件，並確認冪等操作正確執行。

    定期從資料儲存區移除舊的冪等性字符，以管理儲存空間和效能。如果您的儲存系統提供支援，請考慮針對資料使用過期時間戳記 (通常稱為存留時間或 TTL 值)。重複使用冪等性字符的可能性會隨著時間而降低。

    通常用於儲存冪等性字符和相關狀態的常見 AWS 儲存選項包括：
   +  **Amazon DynamoDB**：DynamoDB 是一項 NoSQL 資料庫服務，可提供低延遲效能和高可用性，因此非常適合儲存冪等性相關資料。DynamoDB 的索引鍵值和文件資料模型具備高效率儲存和擷取冪等性字符與相關聯狀態資訊的能力。如果您的應用程式在插入時設定 TTL 值，則 DynamoDB 也會自動使冪等性字符過期。
   +  **Amazon ElastiCache**：ElastiCache 可以儲存高輸送量、低延遲且成本低廉的冪等性字符。如果您的應用程式在插入時設定了 TTL 值，則 ElastiCache (Redis) 和 ElastiCache (Memcached) 也會自動讓冪等性字符過期。
   +  **Amazon Relational Database Service (RDS)**：您可以使用 Amazon RDS 來存放冪等性字符和相關的狀態資訊，特別是您的應用程式已將關聯式資料庫用於其他用途時。
   +  **Amazon Simple Storage Service (S3)**：Amazon S3 是高度可擴展且耐用的物件儲存服務，可用於存放冪等性字符和相關的中繼資料。S3 的版本控制功能在維護冪等操作的狀態方面特別實用。選擇儲存服務時，通常取決於以下因素：與冪等性相關的資料量、所需的效能特性、所須耐用性和可用性，以及冪等性機制與整體工作負載架構整合的方式。

1.  **實作冪等操作** 

    將您的 API 和工作負載元件設計為冪等。將冪等性檢查納入工作負載元件中。在您處理請求或執行操作之前，請先檢查唯一識別碼是否已處理。若已處理，則傳回先前的結果，而非再次執行操作。例如，若用戶端傳送建立使用者的請求，則檢查具有相同唯一識別碼的使用者是否已存在。如果使用者已存在，則應傳回現有的使用者資訊，而非建立新使用者。為此，用戶端可以使用冪等性字符發出 API 請求，每次重複請求時，都會使用這個權杖。

    建立全方位的測試套件，以確認請求的冪等性。這些套件應涵蓋各種不同的案例，例如成功請求、失敗請求和重複請求。

    如果您的工作負載利用 AWS Lambda 函數，請考慮 Powertools for AWS Lambda。Powertools for AWS Lambda 是一套開發人員工具組，當您搭配使用 AWS Lambda 函數時，可協助實作無伺服器最佳實務，並加快開發人員速度。它特別提供公用程式將 Lambda 函數轉換為可安全重試的冪等操作。

1.  **清楚傳達冪等性** 

    記錄您的 API 和工作負載元件，以清楚傳達操作的冪等性質。這樣有助於用戶端了解預期的行為，以及如何與您的工作負載進行可靠的互動。

1.  **監控和稽核** 

    實作監控和稽核機制，以偵測任何與回應冪等性相關的問題，例如非預期的回應變化，或過多的重複請求處理。這可協助您偵測和調查工作負載中的任何問題或意外行為。

## 資源
<a name="resources"></a>

 **相關的最佳實務：**
+  [REL05-BP03 控制和限制重試呼叫](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_mitigate_interaction_failure_limit_retries.html) 
+  [REL06-BP01 監控工作負載的所有元件 (產生)](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_monitor_aws_resources_monitor_resources.html) 
+  [REL06-BP03 傳送通知 (即時處理和警示)](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_monitor_aws_resources_notification_monitor.html) 
+  [REL08-BP02 將功能測試整合為部署的一部分](https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_tracking_change_management_functional_testing.html) 

 **相關文件：**
+  [Amazon 建置者資料中心：透過冪等 API 安全地進行重試](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) 
+  [Amazon 建置者資料中心：分散式系統的挑戰](https://aws.amazon.com/builders-library/challenges-with-distributed-systems/) 
+  [Amazon 建置者資料中心：可靠性、持續工作以及咖啡時刻](https://aws.amazon.com/builders-library/reliability-and-constant-work/) 
+  [Amazon Elastic Container Service：確保冪等性](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/ECS_Idempotency.html) 
+  [如何讓 Lambda 函數冪等？](https://repost.aws/knowledge-center/lambda-function-idempotent) 
+  [確保 Amazon EC2 API 請求中的冪等性](https://docs.aws.amazon.com/ec2/latest/devguide/ec2-api-idempotency.html) 

 **相關影片：**
+  [使用事件驅動型架構建置分散式應用程式 - AWS 線上技術講座](https://www.youtube.com/watch?v=gA2-eqDVSng&t=1668s) 
+  [AWS re:Invent 2023 - 使用事件驅動型架構建置新一代應用程式](https://www.youtube.com/watch?v=KXR17uwLEC8) 
+  [AWS re:Invent 2023 - 鬆耦合系統的進階整合模式和權衡](https://www.youtube.com/watch?v=FGKGdUiZKto) 
+  [AWS re:Invent 2023 - 使用 Amazon EventBridge 的進階事件驅動型模式](https://www.youtube.com/watch?v=6X4lSPkn4ps) 
+  [AWS re:Invent 2018 - 閉門造車與開放思維：如何取得大小型系統的控制權 (ARC337) (包括鬆耦合、持續工作、靜態穩定性)](https://youtu.be/O8xLxNje30M) 
+  [AWS re:Invent 2019 - 移至事件驅動型架構 (SVS308)](https://youtu.be/h46IquqjF3E) 

 **相關工具：**
+  [AWS Lambda Powertools 的冪等性 (Java)](https://docs.powertools.aws.dev/lambda/java/utilities/idempotency/) 
+  [AWS Lambda Powertools 的冪等性 (Python)](https://docs.powertools.aws.dev/lambda/python/latest/utilities/idempotency/) 
+  [AWS Lambda Powertools GitHub 頁面](https://github.com/aws-powertools/) 