0Pricing
Serverless AWS Lambda Development · レッスン

レジリエントなサーバーレスシステムの構築

サーキットブレーカー、リトライ、冪等性などのパターンを各関数に取り入れ、高可用性と耐障害性を備えたサーバーレスアーキテクチャを設計します。

「レジリエントなサーバーレスシステムの構築」はCoddyKit上の無料Serverless AWS Lambda Developmentレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはServerless AWS Lambda Development学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Serverless AWS Lambda Developmentコースには全4レッスンが含まれています。

このレッスンの一部はまだ翻訳されておらず、英語で表示されています。

Building Robust Serverless Systems

Welcome! In this lesson, we'll dive into designing highly resilient and fault-tolerant serverless applications. Even though AWS manages much of the infrastructure, your functions still need to handle failures gracefully.

We'll explore key architectural patterns to ensure your applications remain stable and performant, even when things go wrong.

The Reality of Distributed Systems

In a serverless world, your functions often interact with many other services: databases, APIs, message queues. These interactions happen over a network, and networks can be unreliable.

  • Transient Failures: Brief network glitches or service slowdowns.
  • Downstream Service Issues: A service your Lambda calls might be temporarily unavailable.
  • Unexpected Data: Malformed input can cause your function to crash.

Designing for these "failures" is crucial for a stable system.

Lambda's Built-in Retry Logic

For certain invocation types, AWS Lambda automatically retries your function if it fails. This is a powerful built-in resilience mechanism for asynchronous invocations.

For example, if an SQS queue triggers your Lambda and your function errors, Lambda (or SQS) will retry the invocation a few times. This helps overcome transient issues without any code changes.

However, retries aren't a silver bullet; they can lead to duplicate processing if not handled carefully.

Making Operations Idempotent

When retries happen, your function might execute the same operation multiple times. This is where idempotency comes in.

An idempotent operation is one that can be applied multiple times without changing the result beyond the initial application.

  • Example: Setting a value (x = 5) is idempotent.
  • Non-Example: Incrementing a value (x++) is NOT idempotent, as each retry would change the value.

For resilient systems, many operations should strive to be idempotent.

Keys to Idempotent Functions

To make your Lambda functions idempotent, you often need to track the state of a request. This typically involves:

  1. Generating a unique Idempotency Key for each request (e.g., from request ID, event source ID).
  2. Checking if this key has already been processed before performing the core logic.
  3. Storing the result or status of the operation associated with the key.

This ensures that even if a function is retried, the core side-effect only occurs once.

import hashlib
import json

# Imagine a database or cache for storing processed requests
# For simplicity, using a global dict here. A real app uses persistent storage.
processed_requests = {}

def is_idempotent(event_payload):
    # Create a unique key from the event payload
    # For a real app, use a proper hashing/unique ID strategy
    event_hash = hashlib.md5(json.dumps(event_payload, sort_keys=True).encode('utf-8')).hexdigest()

    if event_hash in processed_requests:
        print(f"Request with hash {event_hash} already processed.")
        return True
    
    processed_requests[event_hash] = "processing" # Mark as processing
    return False

def lambda_handler(event, context):
    if is_idempotent(event):
        return {
            'statusCode': 200,
            'body': json.dumps('Request already processed or is being processed.')
        }

    # Simulate actual work (e.g., writing to a database)
    print(f"Processing new request: {event}")
    
    # In a real scenario, update processed_requests[event_hash] = "completed"
    # after successful processing and store the result in persistent storage.
    
    return {
        'statusCode': 200,
        'body': json.dumps('Request processed successfully!')
    }

Preventing Cascading Failures

The Circuit Breaker pattern is a powerful way to prevent a failing service from causing cascading failures throughout your application.

Imagine a call to an external API that starts failing. Continuously retrying it will just waste resources and slow down your function. A circuit breaker detects this and "opens" the circuit, stopping calls to the failing service temporarily.

This gives the failing service time to recover and prevents your application from getting bogged down.

How a Circuit Breaker Works

A circuit breaker typically has three states:

  • Closed: Operations proceed as normal. If failures exceed a threshold, it transitions to Open.
  • Open: All calls to the protected service immediately fail (or return a fallback). After a timeout, it transitions to Half-Open.
  • Half-Open: A limited number of test calls are allowed through. If these succeed, it transitions back to Closed. If they fail, it returns to Open.

This intelligent behavior allows for self-healing.

Implementing a Simple Circuit Breaker

Implementing a full circuit breaker involves managing state (failures, success counts, last failure time). For serverless, this state might be stored in a shared cache (like ElastiCache) or a database.

While complex to implement from scratch in a simple Lambda, understanding the logic is key. Libraries exist for various languages to help, or you can leverage AWS services like Step Functions to orchestrate retry logic with delays.

import time

class CircuitBreaker:
    def __init__(self, failure_threshold=3, reset_timeout=5):
        self.state = "CLOSED"
        self.failure_count = 0
        self.last_failure_time = 0
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout # seconds

    def call(self, func, *args, **kwargs):
        if self.state == "OPEN":
            if time.time() - self.last_failure_time > self.reset_timeout:
                self.state = "HALF-OPEN"
                # In a real app, log this state change
            else:
                raise Exception("Circuit is open, service unavailable.")
        
        try:
            result = func(*args, **kwargs)
            if self.state == "HALF-OPEN":
                self.state = "CLOSED"
                self.failure_count = 0
                # In a real app, log this state change
            return result
        except Exception as e:
            self.failure_count += 1
            self.last_failure_time = time.time()
            if self.failure_count >= self.failure_threshold:
                self.state = "OPEN"
                # In a real app, log this state change
            raise e

Timeouts Prevent Hanging

Another crucial resilience pattern is using timeouts for external calls. If your Lambda function calls another service (e.g., a database, an HTTP API), that call could hang indefinitely if the service is unresponsive.

Configuring a timeout ensures your function doesn't wait forever, freeing up resources and allowing for retry logic to kick in faster. AWS Lambda itself has a configurable timeout, but you should also set timeouts within your code for specific external requests.

Resilient Design Challenge

Consider a Lambda function that processes incoming orders. If the function fails after successfully deducting payment but before updating the order status in a database, and then retries, what problem could arise if the payment deduction is NOT idempotent?

Summary: Building for Failure

We've covered essential patterns for building resilient serverless applications:

  • Retries: Lambda's built-in mechanism for transient errors.
  • Idempotency: Ensuring operations can be safely retried without unintended side-effects (e.g., duplicate charges).
  • Circuit Breakers: Preventing cascading failures by intelligently stopping calls to failing services.
  • Timeouts: Protecting against unresponsive external services.

By applying these principles, you can create serverless systems that gracefully handle inevitable failures.

よくある質問

「レジリエントなサーバーレスシステムの構築」レッスンは無料ですか?

はい。「レジリエントなサーバーレスシステムの構築」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Serverless AWS Lambda Developmentコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Serverless AWS Lambda Developmentコースには全4レッスンが含まれています。

「レジリエントなサーバーレスシステムの構築」で何を学びますか?

サーキットブレーカー、リトライ、冪等性などのパターンを各関数に取り入れ、高可用性と耐障害性を備えたサーバーレスアーキテクチャを設計します。 ブラウザで直接実行するハンズオンコードでServerless AWS Lambda Developmentを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Serverless AWS Lambda Developmentを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのServerless AWS Lambda Developmentは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「レジリエントなサーバーレスシステムの構築」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このServerless AWS Lambda Developmentレッスンでコードを書いて実行できますか?

はい。すべてのServerless AWS Lambda Developmentレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. カナリアリリースとBlue/Greenデプロイメント
  2. レジリエントなサーバーレスシステムの構築
  3. サーバーレスアーキテクチャパターン
  4. サーバーレスアーキテクチャのコスト最適化
← Serverless AWS Lambda Developmentに戻る