Skip to content

Operational Risk Modeling, Blast Radius Reduction & Failure Domain Architecture: Best Practices

Objective: Establish comprehensive risk modeling frameworks that identify failure domains, model blast radius, and design containment strategies across clusters, databases, data pipelines, and ML systems. When you need to reduce risk, when you want to contain failures, when you need failure domain architecture—this guide provides the complete framework.

Introduction

Operational risk modeling is not optional—it's fundamental to building reliable, safe systems. Without explicit risk modeling and blast radius containment, failures cascade, systems collapse, and incidents become disasters. This guide establishes patterns for risk modeling, blast radius reduction, and failure domain architecture.

What This Guide Covers: - Formal risk modeling frameworks - Failure domain diagrams (network partitions, GPU failures, storage node failures) - Service blast radius maps - Best practices for limiting cascading failures - Sandboxing geospatial jobs, ML inference, ingestion pipelines - Governance: risk committees, incident simulation cycles - Cluster segmentation (RKE2 + Rancher) - Postgres/Patroni replica dependency modeling - FDW isolation boundaries - ETL failure domain partitioning - ML model-serving fault isolation - GPU job firewalling - DR boundary planning

Prerequisites: - Understanding of distributed systems and failure modes - Familiarity with risk modeling and failure analysis - Experience with incident response and disaster recovery

Related Documents: This document integrates with: - Operational Resilience and Incident Response - Incident response patterns - Chaos Engineering, Fault Injection, and Reliability Validation - Chaos testing for risk validation - Patroni PostgreSQL HA - Database HA patterns - Performance Monitoring - Performance risk monitoring - Holistic Capacity Planning, Scaling Economics, and Workload Modeling - Capacity risk modeling

The Philosophy of Risk Modeling

Risk Principles

Principle 1: Explicit Risk Modeling - Identify all failure domains - Model blast radius - Quantify risk exposure

Principle 2: Containment First - Design for failure isolation - Prevent cascading failures - Minimize blast radius

Principle 3: Continuous Validation - Test failure scenarios - Validate containment - Improve continuously

Risk Modeling Framework

Risk Assessment Model

Risk Model:

# Risk assessment model
class RiskAssessment:
    def assess_risk(self, component: Component) -> RiskScore:
        """Assess operational risk"""
        # Calculate failure probability
        failure_probability = self.calculate_failure_probability(component)

        # Calculate impact
        impact = self.calculate_impact(component)

        # Calculate blast radius
        blast_radius = self.calculate_blast_radius(component)

        # Calculate risk score
        risk_score = failure_probability * impact * blast_radius

        return RiskScore(
            component=component,
            failure_probability=failure_probability,
            impact=impact,
            blast_radius=blast_radius,
            risk_score=risk_score
        )

Failure Domain Identification

Domain Classification:

# Failure domain classification
failure_domains:
  network:
    - "network-partition"
    - "dns-failure"
    - "load-balancer-failure"

  compute:
    - "node-failure"
    - "pod-failure"
    - "gpu-failure"

  storage:
    - "disk-failure"
    - "storage-node-failure"
    - "object-store-failure"

  database:
    - "primary-failure"
    - "replica-failure"
    - "wal-failure"

  application:
    - "service-failure"
    - "dependency-failure"
    - "configuration-error"

Failure Domain Diagrams

Network Partition Failure

Diagram:

graph TB
    subgraph RegionA["Region A"]
        AppA["Application"]
        DBA["Database"]
        CacheA["Cache"]
    end

    subgraph RegionB["Region B"]
        AppB["Application"]
        DBB["Database"]
        CacheB["Cache"]
    end

    Network["Network Partition"]

    AppA --> DBA
    AppB --> DBB
    Network -.->|"Partition"| AppA
    Network -.->|"Partition"| AppB

    style Network fill:#ffebee
    style RegionA fill:#e1f5ff
    style RegionB fill:#fff4e1

GPU Failure Domain

Diagram:

graph TB
    subgraph GPUNode["GPU Node"]
        GPU1["GPU 1"]
        GPU2["GPU 2"]
        GPU3["GPU 3"]
        GPU4["GPU 4"]
    end

    subgraph Workloads["Workloads"]
        ML1["ML Inference 1"]
        ML2["ML Inference 2"]
        ML3["ML Inference 3"]
        ML4["ML Inference 4"]
    end

    Failure["GPU Failure"]

    ML1 --> GPU1
    ML2 --> GPU2
    ML3 --> GPU3
    ML4 --> GPU4
    Failure -.->|"Affects"| GPU2

    style Failure fill:#ffebee
    style GPUNode fill:#e1f5ff
    style Workloads fill:#fff4e1

Storage Node Failure

Diagram:

graph TB
    subgraph Storage["Storage Cluster"]
        Node1["Node 1<br/>100 TiB"]
        Node2["Node 2<br/>100 TiB"]
        Node3["Node 3<br/>100 TiB"]
    end

    subgraph Data["Data Partitions"]
        P1["Partition 1"]
        P2["Partition 2"]
        P3["Partition 3"]
    end

    Failure["Node Failure"]

    P1 --> Node1
    P2 --> Node2
    P3 --> Node3
    Failure -.->|"Affects"| Node2

    style Failure fill:#ffebee
    style Storage fill:#e1f5ff
    style Data fill:#fff4e1

Service Blast Radius Maps

Blast Radius Calculation

Calculation Model:

# Blast radius calculation
class BlastRadiusCalculator:
    def calculate_blast_radius(self, service: Service) -> BlastRadius:
        """Calculate service blast radius"""
        # Get dependencies
        dependencies = self.get_dependencies(service)

        # Get dependents
        dependents = self.get_dependents(service)

        # Calculate affected services
        affected_services = dependencies + dependents

        # Calculate affected users
        affected_users = self.calculate_affected_users(service)

        # Calculate data impact
        data_impact = self.calculate_data_impact(service)

        return BlastRadius(
            service=service,
            dependencies=len(dependencies),
            dependents=len(dependents),
            affected_services=len(affected_services),
            affected_users=affected_users,
            data_impact=data_impact
        )

Blast Radius Map

Visualization:

graph TB
    subgraph Core["Core Services"]
        UserAPI["User API"]
        OrderAPI["Order API"]
        PaymentAPI["Payment API"]
    end

    subgraph Dependencies["Dependencies"]
        UserDB["User DB"]
        OrderDB["Order DB"]
        PaymentDB["Payment DB"]
        Redis["Redis"]
    end

    subgraph Dependents["Dependents"]
        Frontend["Frontend"]
        Analytics["Analytics"]
    end

    Failure["User API Failure"]

    UserAPI --> UserDB
    UserAPI --> Redis
    OrderAPI --> OrderDB
    PaymentAPI --> PaymentDB
    Frontend --> UserAPI
    Analytics --> UserAPI
    Failure -.->|"Blast Radius"| UserAPI
    Failure -.->|"Affects"| Frontend
    Failure -.->|"Affects"| Analytics

    style Failure fill:#ffebee
    style Core fill:#e1f5ff
    style Dependencies fill:#fff4e1
    style Dependents fill:#e8f5e9

Cascading Failure Prevention

Circuit Breaker Patterns

Pattern: Prevent cascading failures.

Example:

# Circuit breaker for blast radius reduction
class CircuitBreaker:
    def __init__(self, failure_threshold: int = 5):
        self.failure_threshold = failure_threshold
        self.failure_count = 0
        self.state = "closed"

    def call(self, func):
        """Call function with circuit breaker"""
        if self.state == "open":
            raise CircuitBreakerOpenError()

        try:
            result = func()
            self.failure_count = 0
            return result
        except Exception as e:
            self.failure_count += 1
            if self.failure_count >= self.failure_threshold:
                self.state = "open"
            raise e

Bulkhead Pattern

Pattern: Isolate failure domains.

Example:

# Bulkhead pattern
bulkhead:
  isolation:
    - name: "user-domain"
      resources:
        cpu: "100 cores"
        memory: "200 GiB"
      failure_domain: "isolated"

    - name: "order-domain"
      resources:
        cpu: "100 cores"
        memory: "200 GiB"
      failure_domain: "isolated"

Cluster Segmentation

RKE2 Cluster Segmentation

Segmentation Strategy:

# RKE2 cluster segmentation
cluster_segmentation:
  strategy: "namespace-isolation"
  segments:
    - name: "critical-services"
      namespace: "critical"
      isolation:
        network: "network-policy"
        storage: "storage-class"
        compute: "node-selector"

    - name: "batch-jobs"
      namespace: "batch"
      isolation:
        network: "network-policy"
        compute: "dedicated-nodes"

    - name: "ml-inference"
      namespace: "ml"
      isolation:
        network: "network-policy"
        compute: "gpu-nodes"
        gpu: "dedicated"

Rancher Project Isolation

Project Isolation:

# Rancher project isolation
rancher_isolation:
  projects:
    - name: "production"
      clusters: ["prod-cluster"]
      isolation:
        network: "project-network-policy"
        resource_quota: "project-quota"

    - name: "development"
      clusters: ["dev-cluster"]
      isolation:
        network: "project-network-policy"
        resource_quota: "project-quota"

Postgres/Patroni Dependency Modeling

Replica Dependency Model

Dependency Graph:

graph TB
    Primary["Primary<br/>Postgres"]
    Replica1["Replica 1<br/>Read"]
    Replica2["Replica 2<br/>Read"]
    Replica3["Replica 3<br/>Standby"]

    App1["Application 1"]
    App2["Application 2"]
    App3["Application 3"]

    Primary -->|"WAL Replication"| Replica1
    Primary -->|"WAL Replication"| Replica2
    Primary -->|"WAL Replication"| Replica3

    App1 --> Replica1
    App2 --> Replica2
    App3 --> Primary

    style Primary fill:#ffebee
    style Replica1 fill:#e1f5ff
    style Replica2 fill:#e1f5ff
    style Replica3 fill:#fff4e1

Dependency Configuration:

# Postgres dependency model
postgres_dependencies:
  primary:
    name: "postgres-primary"
    replicas:
      - name: "postgres-replica-1"
        role: "read"
        lag_threshold: "10 seconds"
      - name: "postgres-replica-2"
        role: "read"
        lag_threshold: "10 seconds"
      - name: "postgres-replica-3"
        role: "standby"
        lag_threshold: "1 minute"
  failover:
    automatic: true
    rto: "2 minutes"

FDW Isolation Boundaries

FDW Isolation Strategy

Isolation Pattern:

-- FDW isolation boundaries
CREATE SERVER isolated_fdw
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
    host 'remote-host',
    port '5432',
    isolation_level 'strict',
    connection_limit '10',
    connect_timeout '5'
);

-- Isolation policy
CREATE POLICY fdw_isolation_policy
ON FOREIGN TABLE remote_table
USING (
    current_user = 'isolated_user'
    AND pg_isolation_test_session()
);

ETL Failure Domain Partitioning

ETL Isolation

Partitioning Strategy:

# ETL failure domain partitioning
etl_partitioning:
  strategy: "pipeline-isolation"
  partitions:
    - name: "user-ingestion"
      isolation:
        compute: "dedicated-workers"
        storage: "dedicated-buckets"
        network: "isolated-vpc"

    - name: "order-processing"
      isolation:
        compute: "dedicated-workers"
        storage: "dedicated-buckets"
        network: "isolated-vpc"

ML Model-Serving Fault Isolation

Inference Isolation

Isolation Pattern:

# ML inference fault isolation
ml_inference_isolation:
  strategy: "model-per-pod"
  isolation:
    compute: "dedicated-gpu"
    network: "service-mesh-isolation"
    storage: "read-only-model-storage"
  fault_domains:
    - name: "model-a"
      isolation: "pod"
      blast_radius: "single-model"
    - name: "model-b"
      isolation: "pod"
      blast_radius: "single-model"

GPU Job Firewalling

GPU Isolation

Firewall Configuration:

# GPU job firewalling
gpu_firewall:
  strategy: "gpu-per-job"
  isolation:
    gpu: "dedicated"
    memory: "isolated"
    network: "job-network-policy"
  firewalls:
    - name: "ml-training"
      gpu_allocation: "dedicated"
      memory_limit: "32Gi"
      network_policy: "training-isolation"

    - name: "ml-inference"
      gpu_allocation: "dedicated"
      memory_limit: "16Gi"
      network_policy: "inference-isolation"

DR Boundary Planning

DR Boundaries

Boundary Definition:

# DR boundary planning
dr_boundaries:
  regions:
    - name: "us-east-1"
      boundary: "region"
      isolation: "complete"
      failover_target: "us-west-2"

    - name: "us-west-2"
      boundary: "region"
      isolation: "complete"
      failover_target: "us-east-1"

  clusters:
    - name: "prod-cluster"
      boundary: "cluster"
      isolation: "network"
      failover_target: "dr-cluster"

Risk Governance

Risk Committee

Committee Structure:

# Risk committee
risk_committee:
  members:
    - role: "architecture-lead"
      responsibility: "technical-risk"
    - role: "security-lead"
      responsibility: "security-risk"
    - role: "operations-lead"
      responsibility: "operational-risk"
  meetings:
    frequency: "monthly"
    agenda:
      - "risk-assessment-review"
      - "blast-radius-analysis"
      - "failure-domain-validation"

Incident Simulation Cycles

Simulation Framework:

# Incident simulation
incident_simulation:
  frequency: "quarterly"
  scenarios:
    - name: "database-primary-failure"
      blast_radius: "high"
      simulation: "chaos-engineering"

    - name: "network-partition"
      blast_radius: "medium"
      simulation: "network-chaos"

    - name: "storage-node-failure"
      blast_radius: "high"
      simulation: "storage-chaos"

Architecture Fitness Functions

Blast Radius Minimization Fitness Function

Definition:

# Blast radius minimization fitness function
class BlastRadiusMinimizationFitnessFunction:
    def evaluate(self, system: System) -> float:
        """Evaluate blast radius minimization"""
        # Calculate average blast radius
        avg_blast_radius = self.calculate_avg_blast_radius(system)

        # Calculate target blast radius
        target_blast_radius = system.target_blast_radius

        # Calculate fitness
        if avg_blast_radius <= target_blast_radius:
            fitness = 1.0
        else:
            fitness = target_blast_radius / avg_blast_radius

        return fitness

Failure Domain Isolation Fitness Function

Definition:

# Failure domain isolation fitness function
class FailureDomainIsolationFitnessFunction:
    def evaluate(self, system: System) -> float:
        """Evaluate failure domain isolation"""
        # Calculate isolation score
        isolation_score = 0.0

        for domain in system.failure_domains:
            # Check network isolation
            network_isolation = self.check_network_isolation(domain)

            # Check compute isolation
            compute_isolation = self.check_compute_isolation(domain)

            # Check storage isolation
            storage_isolation = self.check_storage_isolation(domain)

            # Calculate domain isolation
            domain_isolation = (network_isolation * 0.4) + \
                              (compute_isolation * 0.3) + \
                              (storage_isolation * 0.3)

            isolation_score += domain_isolation

        # Average isolation
        avg_isolation = isolation_score / len(system.failure_domains)

        return avg_isolation

Cross-Document Architecture

graph TB
    subgraph Risk["Risk Modeling<br/>(This Document)"]
        Modeling["Risk Modeling"]
        BlastRadius["Blast Radius"]
        Isolation["Failure Isolation"]
    end

    subgraph Resilience["Operational Resilience"]
        Incident["Incident Response"]
    end

    subgraph Chaos["Chaos Engineering"]
        Testing["Chaos Testing"]
    end

    subgraph Patroni["Patroni HA"]
        HA["High Availability"]
    end

    Modeling --> Incident
    BlastRadius --> Testing
    Isolation --> HA

    style Risk fill:#ffebee
    style Resilience fill:#e1f5ff
    style Chaos fill:#fff4e1
    style Patroni fill:#e8f5e9

Checklists

Risk Modeling Checklist

  • Risk assessment completed
  • Failure domains identified
  • Blast radius mapped
  • Cascading failure prevention implemented
  • Cluster segmentation configured
  • Dependency modeling complete
  • FDW isolation boundaries defined
  • ETL partitioning active
  • ML inference isolation configured
  • GPU job firewalling enabled
  • DR boundaries planned
  • Risk governance established
  • Incident simulation scheduled

Anti-Patterns

Risk Modeling Anti-Patterns

Single Shared FDW Hub:

-- Bad: Single shared FDW
CREATE SERVER shared_fdw
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'single-hub');
-- All services depend on one hub

-- Good: Isolated FDWs
CREATE SERVER user_fdw
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'user-hub');
-- Isolated per domain

Global WAL Choke Points:

# Bad: Single WAL stream
postgres:
  wal_streams: 1
  # All replicas depend on one stream

# Good: Distributed WAL
postgres:
  wal_streams: 3
  # Multiple streams for redundancy

See Also


This guide establishes comprehensive risk modeling and blast radius reduction patterns. Start with risk assessment, extend to failure domain isolation, and continuously validate containment strategies.