AgentSkillsCN

acc-saga-pattern-knowledge

为 Saga 模式提供知识库。为 Saga 编排、编舞以及分布式事务的审计提供各类模式、反模式以及专属于 PHP 的最佳实践指南。

SKILL.md
--- frontmatter
name: acc-saga-pattern-knowledge
description: Saga Pattern knowledge base. Provides patterns, antipatterns, and PHP-specific guidelines for saga orchestration, choreography, and distributed transaction audits.

Saga Pattern Knowledge Base

Quick reference for Saga pattern and PHP implementation guidelines for distributed transactions.

Core Principles

Saga Pattern Overview

code
┌─────────────────────────────────────────────────────────────────────────┐
│                         SAGA PATTERN                                     │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   CHOREOGRAPHY (Event-driven)                                            │
│   ┌─────────┐    event    ┌─────────┐    event    ┌─────────┐           │
│   │ Service │───────────▶ │ Service │───────────▶ │ Service │           │
│   │    A    │             │    B    │             │    C    │           │
│   └─────────┘             └─────────┘             └─────────┘           │
│        │                       │                       │                │
│        └───────── compensate ──┴─── compensate ────────┘                │
│                                                                          │
│   ORCHESTRATION (Central coordinator)                                    │
│                    ┌───────────────┐                                    │
│                    │  Saga         │                                    │
│                    │  Orchestrator │                                    │
│                    └───────┬───────┘                                    │
│              ┌─────────────┼─────────────┐                              │
│              ▼             ▼             ▼                              │
│        ┌─────────┐   ┌─────────┐   ┌─────────┐                          │
│        │ Service │   │ Service │   │ Service │                          │
│        │    A    │   │    B    │   │    C    │                          │
│        └─────────┘   └─────────┘   └─────────┘                          │
│                                                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Saga = Sequence of local transactions + compensating actions           │
│                                                                          │
│   T1 → T2 → T3 → ... → Tn                                               │
│    ↓    ↓    ↓          ↓                                               │
│   C1   C2   C3        Cn                                                │
│                                                                          │
│   If Ti fails: execute Ci-1, Ci-2, ..., C1 (reverse order)              │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Key Concepts

ConceptDescription
SagaSequence of local transactions with compensations
StepSingle local transaction within saga
Compensating ActionUndoes the effect of a previous step
OrchestratorCentral coordinator managing saga execution
ChoreographyDecentralized, event-driven saga coordination
Saga StateCurrent execution status (enum)
IdempotencySteps can be retried safely
Semantic LockPrevents concurrent saga conflicts

Choreography vs Orchestration

AspectChoreographyOrchestration
CoordinationDecentralized (events)Centralized (orchestrator)
CouplingLooseTighter to orchestrator
VisibilityDistributed (hard to trace)Centralized (easy to monitor)
ComplexitySimpler servicesSimpler overall flow
TestingHarder (distributed)Easier (centralized logic)
Best forSimple sagas (2-3 steps)Complex sagas (4+ steps)

Quick Checklists

Saga Design Checklist

  • Each step is a local transaction
  • Every step has compensating action
  • Compensations are idempotent
  • Forward actions are idempotent
  • Saga state is persisted
  • Failure handling defined for each step
  • Timeout handling for long-running steps

Orchestrator Checklist

  • State machine for saga lifecycle
  • Persistent saga state storage
  • Step execution tracking
  • Compensation ordering (reverse)
  • Retry logic with limits
  • Dead letter for failed sagas
  • Correlation ID propagation

Compensation Checklist

  • Semantic undo (not rollback)
  • Handles partial completion
  • Idempotent (can run multiple times)
  • Doesn't fail silently
  • Logs compensation actions
  • Eventual consistency acceptable

PHP 8.5 Saga Patterns

Saga State Enum

php
<?php

declare(strict_types=1);

namespace Domain\Shared\Saga;

enum SagaState: string
{
    case Pending = 'pending';
    case Running = 'running';
    case Compensating = 'compensating';
    case Completed = 'completed';
    case Failed = 'failed';
    case CompensationFailed = 'compensation_failed';

    public function canTransitionTo(self $next): bool
    {
        return match ($this) {
            self::Pending => $next === self::Running,
            self::Running => in_array($next, [self::Completed, self::Compensating], true),
            self::Compensating => in_array($next, [self::Failed, self::CompensationFailed], true),
            self::Completed, self::Failed, self::CompensationFailed => false,
        };
    }

    public function isTerminal(): bool
    {
        return in_array($this, [self::Completed, self::Failed, self::CompensationFailed], true);
    }
}

Saga Step Interface

php
<?php

declare(strict_types=1);

namespace Domain\Shared\Saga;

interface SagaStepInterface
{
    public function name(): string;

    public function execute(SagaContext $context): StepResult;

    public function compensate(SagaContext $context): StepResult;

    public function isIdempotent(): bool;
}

Saga Context

php
<?php

declare(strict_types=1);

namespace Domain\Shared\Saga;

final class SagaContext
{
    /** @var array<string, mixed> */
    private array $data = [];

    public function __construct(
        public readonly string $sagaId,
        public readonly string $correlationId,
        public readonly \DateTimeImmutable $startedAt
    ) {}

    public function set(string $key, mixed $value): void
    {
        $this->data[$key] = $value;
    }

    public function get(string $key, mixed $default = null): mixed
    {
        return $this->data[$key] ?? $default;
    }

    public function has(string $key): bool
    {
        return array_key_exists($key, $this->data);
    }

    public function all(): array
    {
        return $this->data;
    }
}

Saga Orchestrator

php
<?php

declare(strict_types=1);

namespace Application\Shared\Saga;

use Domain\Shared\Saga\SagaContext;
use Domain\Shared\Saga\SagaState;
use Domain\Shared\Saga\SagaStepInterface;
use Domain\Shared\Saga\StepResult;

final class SagaOrchestrator
{
    /** @var array<SagaStepInterface> */
    private array $steps = [];

    /** @var array<string> */
    private array $completedSteps = [];

    private SagaState $state = SagaState::Pending;

    public function __construct(
        private readonly SagaContext $context,
        private readonly SagaPersistenceInterface $persistence
    ) {}

    public function addStep(SagaStepInterface $step): self
    {
        $this->steps[] = $step;
        return $this;
    }

    public function execute(): SagaResult
    {
        $this->state = SagaState::Running;
        $this->persistence->save($this->context->sagaId, $this->state, []);

        foreach ($this->steps as $step) {
            $result = $step->execute($this->context);

            if ($result->isFailure()) {
                return $this->compensate($step->name(), $result->error());
            }

            $this->completedSteps[] = $step->name();
            $this->persistence->save(
                $this->context->sagaId,
                $this->state,
                $this->completedSteps
            );
        }

        $this->state = SagaState::Completed;
        $this->persistence->save($this->context->sagaId, $this->state, $this->completedSteps);

        return SagaResult::completed($this->context);
    }

    private function compensate(string $failedStep, string $error): SagaResult
    {
        $this->state = SagaState::Compensating;
        $this->persistence->save($this->context->sagaId, $this->state, $this->completedSteps);

        $stepsToCompensate = array_reverse($this->completedSteps);

        foreach ($stepsToCompensate as $stepName) {
            $step = $this->findStep($stepName);
            $result = $step->compensate($this->context);

            if ($result->isFailure()) {
                $this->state = SagaState::CompensationFailed;
                $this->persistence->save($this->context->sagaId, $this->state, $this->completedSteps);

                return SagaResult::compensationFailed($this->context, $error, $result->error());
            }
        }

        $this->state = SagaState::Failed;
        $this->persistence->save($this->context->sagaId, $this->state, $this->completedSteps);

        return SagaResult::failed($this->context, $error);
    }

    private function findStep(string $name): SagaStepInterface
    {
        foreach ($this->steps as $step) {
            if ($step->name() === $name) {
                return $step;
            }
        }
        throw new \RuntimeException("Step not found: {$name}");
    }
}

Common Violations Quick Reference

ViolationWhere to LookSeverity
Missing compensationSaga step without compensate()Critical
Non-idempotent stepsRetry causes duplicate effectsCritical
No saga state persistenceState lost on crashCritical
Synchronous distributed txTwo-phase commit attemptCritical
Forward-only sagaNo compensation at allWarning
Missing correlation IDCan't trace saga executionWarning
No timeout handlingSaga hangs foreverWarning
Compensation order wrongNot reversedWarning

Detection Patterns

bash
# Find saga implementations
Glob: **/Saga/**/*.php
Glob: **/*Saga.php
Grep: "SagaStep|SagaOrchestrator|Saga.*Interface" --glob "**/*.php"

# Check for saga state management
Grep: "SagaState|saga_state|enum.*Saga" --glob "**/*.php"

# Find compensating actions
Grep: "compensate|compensation|rollback.*step" --glob "**/Saga/**/*.php"

# Detect missing compensations
Grep: "implements.*SagaStep" --glob "**/*.php"
# Then check each for compensate() method

# Find choreography events
Grep: "SagaEvent|SagaCompleted|SagaFailed" --glob "**/Event/**/*.php"

# Check for saga persistence
Grep: "SagaPersistence|SagaRepository|saga.*save" --glob "**/*.php"

# Find potential issues
Grep: "Transaction.*begin.*Transaction" --glob "**/*.php"  # Distributed tx attempt

Example: Order Saga

code
Order Saga: Reserve Inventory → Charge Payment → Ship Order

Step 1: Reserve Inventory
  - Action: Decrement stock
  - Compensation: Release stock (increment)

Step 2: Charge Payment
  - Action: Charge credit card
  - Compensation: Refund charge

Step 3: Ship Order
  - Action: Create shipment
  - Compensation: Cancel shipment

If Step 2 fails:
  1. Compensate Step 1 (release inventory)
  2. Mark saga as Failed

References

For detailed information, load these reference files:

  • references/saga-patterns.md — Choreography and orchestration patterns
  • references/compensation.md — Compensating transaction strategies
  • references/antipatterns.md — Common violations with detection patterns
  • references/php-specific.md — PHP 8.5 specific implementations

Assets

  • assets/report-template.md — Structured audit report template