Agentic Architecture Pattern: Compensation

Written by

in


    """
Pattern 190: Compensation Pattern
Purpose: Undo actions when transaction fails
"""

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum


class TransactionStatus(Enum):
    """Transaction status."""
    PENDING = "pending"
    COMMITTED = "committed"
    COMPENSATING = "compensating"
    COMPENSATED = "compensated"
    FAILED = "failed"


@dataclass
class Action:
    """Action with compensation."""
    name: str
    execute: Callable
    compensate: Callable
    executed: bool = False
    compensated: bool = False


@dataclass
class Transaction:
    """Transaction record."""
    id: str
    actions: List[Action]
    status: TransactionStatus = TransactionStatus.PENDING
    timestamp: datetime = field(default_factory=datetime.now)
    error: Optional[str] = None


class CompensationPattern:
    """
    Compensation Pattern - Undo actions when transaction fails.
    
    Features:
    - Transaction management
    - Automatic compensation
    - Rollback on failure
    - Compensation history
    - Saga pattern support
    """
    
    def __init__(self):
        self.transactions: Dict[str, Transaction] = {}
        self.stats = {
            "total_transactions": 0,
            "committed": 0,
            "compensated": 0,
            "failed": 0
        }
    
    def execute_transaction(self, transaction: Transaction) -> bool:
        """Execute transaction with compensation on failure."""
        self.stats["total_transactions"] += 1
        transaction.status = TransactionStatus.PENDING
        executed_actions = []
        
        try:
            # Execute all actions
            for action in transaction.actions:
                print(f"  Executing: {action.name}")
                action.execute()
                action.executed = True
                executed_actions.append(action)
            
            # All succeeded - commit
            transaction.status = TransactionStatus.COMMITTED
            self.stats["committed"] += 1
            self.transactions[transaction.id] = transaction
            print(f"✅ Transaction {transaction.id} committed")
            return True
            
        except Exception as e:
            # Failure - compensate executed actions in reverse
            print(f"❌ Transaction {transaction.id} failed: {e}")
            transaction.error = str(e)
            transaction.status = TransactionStatus.COMPENSATING
            
            print(f"⚠️  Compensating {len(executed_actions)} actions...")
            for action in reversed(executed_actions):
                try:
                    print(f"  Compensating: {action.name}")
                    action.compensate()
                    action.compensated = True
                except Exception as comp_error:
                    print(f"  ⚠️  Compensation failed for {action.name}: {comp_error}")
            
            transaction.status = TransactionStatus.COMPENSATED
            self.stats["compensated"] += 1
            self.transactions[transaction.id] = transaction
            return False
    
    def get_stats(self) -> Dict[str, Any]:
        """Get transaction statistics."""
        success_rate = (
            self.stats["committed"] / self.stats["total_transactions"] * 100
            if self.stats["total_transactions"] > 0 else 0
        )
        
        return {
            **self.stats,
            "success_rate": f"{success_rate:.1f}%"
        }


if __name__ == "__main__":
    print("=== Compensation Pattern Demo ===\n")
    
    pattern = CompensationPattern()
    
    # Define actions with compensation
    db_data = {"balance": 1000}
    
    def debit_account():
        print(f"    Debiting account (balance: {db_data['balance']})")
        db_data['balance'] -= 100
    
    def compensate_debit():
        print(f"    Reversing debit (balance: {db_data['balance']})")
        db_data['balance'] += 100
    
    def send_payment():
        print(f"    Sending payment")
        # Simulate payment
        pass
    
    def compensate_payment():
        print(f"    Reversing payment")
        pass
    
    def send_notification():
        print(f"    Sending notification")
        raise Exception("Notification service unavailable")  # Simulate failure
    
    def compensate_notification():
        print(f"    No notification to reverse")
        pass
    
    # Create transaction
    transaction = Transaction(
        id="TXN-001",
        actions=[
            Action("debit_account", debit_account, compensate_debit),
            Action("send_payment", send_payment, compensate_payment),
            Action("send_notification", send_notification, compensate_notification)
        ]
    )
    
    print("1. Executing transaction with compensation:\n")
    print(f"Initial balance: ${db_data['balance']}")
    success = pattern.execute_transaction(transaction)
    print(f"\nFinal balance: ${db_data['balance']}")
    print(f"Success: {success}\n")
    
    # Successful transaction
    print("\n2. Successful transaction:\n")
    db_data['balance'] = 1000
    
    transaction2 = Transaction(
        id="TXN-002",
        actions=[
            Action("debit_account", debit_account, compensate_debit),
            Action("send_payment", send_payment, compensate_payment)
        ]
    )
    
    print(f"Initial balance: ${db_data['balance']}")
    success = pattern.execute_transaction(transaction2)
    print(f"\nFinal balance: ${db_data['balance']}")
    print(f"Success: {success}\n")
    
    print("\n=== Statistics ===")
    stats = pattern.get_stats()
    print(f"Total transactions: {stats['total_transactions']}")
    print(f"Committed: {stats['committed']}")
    print(f"Compensated: {stats['compensated']}")
    print(f"Success rate: {stats['success_rate']}")

    

Agentic Architecture Pattern: Compensation

Pattern 190: 190 Compensation – Line-by-Line Explanation

Overview

Pattern 190: Compensation Pattern Purpose: Undo actions when transaction fails


Code Structure Analysis

File Information

  • Pattern Number: 190
  • Pattern Name: 190 Compensation
  • File Path: patterns\error_handling\190_compensation.py
  • Total Lines: 188

Detailed Code Explanation

Module Docstring

"""
Pattern 190: Compensation Pattern
Purpose: Undo actions when transaction fails
"""

Explanation: This module docstring describes the purpose and key features of the 190 Compensation pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Callable: Type hints for better code documentation – from dataclasses import dataclass, field: Data class decorators for clean data structures – from datetime import datetime: Date and time utilities – from enum import Enum: Enumeration support for fixed sets of values


Enumerations

TransactionStatus (Line 12)

class TransactionStatus(Enum):
    PENDING
    COMMITTED
    COMPENSATING
    COMPENSATED
    FAILED

Purpose: Transaction status.

Values: – PENDING: Initial state, not yet processed – COMMITTED: Represents committed – COMPENSATING: Represents compensating – COMPENSATED: Represents compensated – FAILED: Execution failed


Classes

TransactionStatus (Line 12)

Description: Transaction status.

Action (Line 22)

Description: Action with compensation.

Transaction (Line 32)

Description: Transaction record.

CompensationPattern (Line 41)

Description: Compensation Pattern – Undo actions when transaction fails.

Features: – Transaction management – Automatic compensation – Rollback on failure – Compensation history – Saga pattern support

Methods:

__init__ (Line 53)
def __init__(self):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference

execute_transaction (Line 62)
def execute_transaction(self, transaction):
    """Execute transaction with compensation on failure."""

Purpose: Execute transaction with compensation on failure.

Parameters: – self: Instance reference – transaction: Parameter for transaction

get_stats (Line 103)
def get_stats(self):
    """Get transaction statistics."""

Purpose: Get transaction statistics.

Parameters: – self: Instance reference


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = TransactionStatus(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *