Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Toggle main menu visibility
Loading...
Searching...
No Matches
Channel Topologies and Redundancy Patterns
Channel Topology Patterns
This document describes common redundancy topologies and how they map to
vital_channel configurations. Each pattern supports different failure
scenarios and recovery characteristics.
Pattern 1: Single-System 2oo2 (A ↔ B → C)
Your Question: A and B on same unit, communicate with C
Architecture:
Online (A) Standby (B)
| |
+─────────────────+ Channel 0: Shared Memory
| | (bidirectional heartbeat)
+─────────────────+ Channel 1: FIFO
| | (bidirectional fallback)
v v
[Vital Channel: 2oo2 Voting]
|
v
Non-Vital Service (C)
(Diagnostics, Logging, Telemetry)
Use Case:
Railway ERTMS single board with redundant cores or processes on same CPU.
App Manager coordinates between Online (A) and Standby (B), sends diagnostics
to Non-Vital Service (C).
Characteristics:
- Scope: Single CPU/board
- Latency: <1ms (no network jitter)
- Failure Tolerance: 0 (if both A and B fail = entire system offline)
- Recovery: None (hardware failure) / Automatic (process restart)
- Suitable for: High-speed systems (>100Hz control loops)
Channel Configuration:
c
// Vital Channels (A ↔ B)
Channel 0: sapi_channel (2oo2 voting)
├─ Transport 0: Shared Memory Queue
│ ├─ Latency: <1µs
│ ├─ Reliability: FIFO queue (atomic)
│ ├─ Use: Fast heartbeat & state sync
│ └─ Timeout: 100ms
│
└─ Transport 1: FIFO (Named Pipe)
├─ Latency: 1-5ms
├─ Reliability: Ordered pipe
├─ Use: Fallback (if SHM unavailable)
└─ Timeout: 500ms
// Non-Vital Channels (A,B → C)
Channel 1: Shared Memory (non-blocking)
├─ Type: Unidirectional (A,B → C)
├─ Blocking: NO (C drops data if not ready)
├─ Use: Diagnostics, telemetry, logs
└─ Timeout: 0 (fire-and-forget)
App Manager Flow:
1. READ: Receive vital input from A/B (blocking, 100ms timeout)
vital_channel_recv(&vital, &cmd, timeout=100ms)
2. PROCESS: Compute state
execute_command(&cmd)
3. SEND: Broadcast vital output to A/B (atomic 2oo2)
vital_channel_send(&vital, &state)
4. SEND: Diagnostics to C (non-blocking)
ipc_send(&diag_channel, &telemetry, timeout=0)
Failure Scenarios:
| Scenario | A Status | B Status | Result |
|-------—|-------—|-------—|-----—|
| Normal | OK | OK | ✓ Voting passes, data output |
| A crashes | TIMEOUT | OK | ✗ Insufficient data, try again next cycle |
| B crashes | OK | TIMEOUT | ✗ Insufficient data, try again next cycle |
| Both crash | TIMEOUT | TIMEOUT | ✗ VOTING_TIMEOUT, trigger SAFE_STATE |
| A ≠ B | OK (A) | OK (B) | ✗ VOTING_DISAGREED, trigger SAFE_STATE |
Pattern 2: Distributed 2oo2 (A ↔ B Across Network)
For remote standby on different host
Architecture:
Host 1 (Online A) [Network] Host 2 (Standby B)
| |
+──────────TCP:5000──────────+
| (reliable, ordered) |
+──────────UDP:5001──────────+
| (fast, best-effort) |
| |
[Vital Channel: 2oo2] [Vital Channel: 2oo2]
| |
v v
Online: Make decision Standby: Verify data
Send to external Report health
Use Case:
Regional redundancy where Online is in main datacenter, Standby in backup
facility or different geographic location.
Characteristics:
- Scope: Network (LAN/WAN)
- Latency: 10-100ms (network dependent)
- Failure Tolerance: 0 (both fail = offline, but unlikely if geographically separated)
- Recovery: Network reconnection (seconds to minutes)
- Suitable for: Geographic redundancy, multi-site HA
Channel Configuration:
c
// Primary transport: TCP/IP (ordered, reliable)
Channel 0: TCP/IP
├─ Endpoint: standby.example.com:5000
├─ Latency: 30-50ms (LAN) or 100-500ms (WAN)
├─ Reliability: 100% delivery (connection-aware)
├─ Timeout: 1000ms (allow for network jitter)
└─ Use: Critical state sync, commands
// Fallback transport: UDP (fast, best-effort)
Channel 1: UDP
├─ Endpoint: standby.example.com:5001
├─ Latency: <10ms (same network)
├─ Reliability: Best-effort (~99% on LAN)
├─ Timeout: 500ms (quick detect)
└─ Use: Quick heartbeat, fast path
Handling Network Failures:
If TCP succeeds, UDP times out:
✓ Use TCP result (already reliable)
If TCP times out, UDP succeeds:
✓ Use UDP result (better than nothing)
If both timeout:
✗ VOTING_TIMEOUT, trigger SAFE_STATE
If both succeed but disagree:
✗ VOTING_DISAGREED, trigger SAFE_STATE
(indicates network corruption or Byzantine failure)
Pattern 3: Hot Standby with Mirroring
For automatic failover without data loss
Architecture:
Online (A) Standby/Mirror (B)
│ │
├──[Channel 0: SHM]────────┤ Real-time sync
│ │ (command echo)
├──[Channel 1: FIFO]───────┤ Heartbeat
│ │ (backup link)
├──[Channel 2: Voting]─────┤ State verification
│ │ (both publish result)
v v
Input Input
Process ←→ Process
Decision Decision
Output (to external systems, A only unless failover)
↓
[Channel 3] ──→ External Device
Failover on A crash:
B detects 3 consecutive timeouts
→ B takes over sending output
→ No interruption to external system
Use Case:
Zero-downtime redundancy where standby must take over immediately when
online fails. Both processes maintain identical state (mirror).
Characteristics:
- Scope: Can be same system or distributed
- Latency: <1ms (same) or 50-100ms (distributed)
- Failure Tolerance: 0 during operation, but automatic failover (<100ms)
- Recovery Time: <100ms (automatic, no manual intervention)
- Suitable for: Critical systems needing zero-downtime failover
Channel Configuration:
c
// Real-time synchronization (command echo)
Channel 0: Shared Memory Queue (same system) or TCP (distributed)
├─ A → B: "Execute command X"
├─ B → A: "Ack, executing"
├─ Latency: <1µs (SHM) or 20-50ms (TCP)
├─ Timeout: 50ms (fail fast on crash)
└─ Use: Command propagation & echo verification
// Health heartbeat
Channel 1: FIFO (same system) or UDP (distributed)
├─ A → B: "I'm alive (seq 42)"
├─ B → A: "Ack (seq 42)"
├─ Latency: 5-10ms
├─ Timeout: 200ms (allow for network jitter)
└─ Use: Presence detection
// State voting (mutual verification)
Channel 2: Shared Memory Queue (2oo2 voting)
├─ A publishes: "State = X"
├─ B publishes: "State = X" (verified match)
├─ Voting layer: Compare A vs B
├─ Mismatch: SAFE_STATE (corruption detected)
└─ Timeout: 100ms (state update cycle)
// Output channel (A primary, B takes over on failover)
Channel 3: to External Device
├─ A sends normally (while online)
├─ B sends only after failover detected
├─ Arbitration: Only one sender at a time
└─ Use: Safety-critical output to external system
Failover State Machine:
Normal Operation:
Online (A) Standby (B)
├─ Receive input ├─ Receive input
├─ Compute state ├─ Verify A's state
├─ Send output ├─ Monitor A alive
└─ Verify B echo └─ Ready to takeover
Failover Trigger (B detects A is down):
Condition: 3 consecutive timeouts on Channel 0/1
Action: B state becomes ONLINE
B starts sending output
A→B links become B→A (role swap)
Failover Complete:
Online (B) Standby (A)
├─ Receive input ├─ Monitor B alive
├─ Compute state ├─ Wait for re-sync
├─ Send output └─ Ready to recover
└─ Verify A online?
Recovery (A restarts):
Action: A detects B is now online
A requests full state sync from B
A verifies state matches B
A resumes as standby
Optionally: failback to A (depends on policy)
Advantages:
- ✓ Automatic failover (<100ms)
- ✓ No manual intervention
- ✓ Both systems always in sync (mirror)
- ✓ No data loss
- ✓ Voting ensures consistency
Challenges:
- ✗ 2x processing overhead (both compute)
- ✗ Complex state synchronization
- ✗ Split-brain risk if A/B network partition
(both think they're online, both send output)
→ Solution: Use arbitration (quorum, UUID-based tie-breaking)
Pattern 4: Triple Redundancy (2oo3)
For highest reliability (tolerate 1 fault)
Architecture:
Online (A) Standby 1 (B) Standby 2 (C)
| | |
+──────TCP────+ |
| | |
+──────TCP────────────────────+
| | |
+──────UDP────+──────UDP──────+
| | |
v v v
[Vital Channel: 2oo3]
Voting: 2 or 3 channels agree → RESULT
0-1 channels available → TIMEOUT
No majority → DISAGREED → SAFE_STATE
Voting Decision Table:
| Ch0 | Ch1 | Ch2 | Decision | Action |
|--—|--—|--—|-------—|-----—|
| A | A | A | AGREED | ✓ Use A |
| A | A | B | AGREED | ✓ Use A (2 agree) |
| A | B | B | AGREED | ✓ Use B (2 agree) |
| A | B | C | DISAGREED | ✗ SAFE_STATE |
| TO | A | A | AGREED | ✓ Use A (ignore timeout) |
| TO | TO | A | INSUFFICIENT | ✗ TIMEOUT |
| TO | TO | TO | TIMEOUT | ✗ SAFE_STATE |
Use Case:
Mission-critical systems (aircraft, medical devices, nuclear plants)
where losing 1 component must not cause failure.
Characteristics:
- Scope: Network (distributed systems)
- Latency: 50-150ms
- Failure Tolerance: 1 (if 1 fails, other 2 still decide)
- Recovery: Automatic (no manual intervention)
- Suitable for: SIL 4 / ASIL D systems, zero-downtime requirements
Channel Configuration:
c
// 3 channels using different transports or endpoints
Channel 0: TCP to Standby 1
├─ Timeout: 1000ms
└─ Use: Primary communication
Channel 1: TCP to Standby 2
├─ Timeout: 1000ms
└─ Use: Backup communication
Channel 2: UDP multicast to both standbies
├─ Timeout: 500ms
└─ Use: Fast path, best-effort
// Voting configuration
sapi_channel_config_t
cfg = {
.voting_strategy =
SAPI_VOTING_2OO3
,
.channel_count = 3,
.channel_timeout_ms = 1000,
.log_disagreements = true,
};
Advantages:
- ✓ Tolerates 1 faulty channel
- ✓ Automatic recovery (no manual intervention)
- ✓ Voted result trusted (majority)
- ✓ Highest reliability available
- ✓ Suitable for SIL 4 / ASIL D
Challenges:
- ✗ 3x communication overhead
- ✗ Complex voting logic
- ✗ Requires 3 independent systems
- ✗ Higher cost
Summary Comparison
| Topology | Channels | Transport | Latency | Tolerance | Failover | Best For |
|-------—|-------—|--------—|------—|--------—|-------—|-------—|
| 2oo2 Local | 2 | SHM+FIFO | <1ms | 0 | None | High-speed same-board |
| 2oo2 Dist. | 2 | TCP+UDP | 50-100ms | 0 | None | Regional active-standby |
| Hot Standby | 3 | SHM+FIFO+... | <1ms | 0 ops, <100ms failover | Auto | Zero-downtime HA |
| 2oo3 | 3 | TCP×3 | 50-150ms | 1 | Auto | Mission-critical SIL 4 |
| NMR | N | Mixed | Varies | M-quorum | Auto | Distributed consensus |
How to Choose a Topology
Question 1: What's the failure tolerance requirement?
- Need to tolerate 0 faults (2oo2) → Must have perfect communication
- Need to tolerate 1 fault (2oo3) → Use triple redundancy
- Need to tolerate N faults → Use NMR with appropriate quorum
Question 2: Are the redundant units on the same system?
- YES (same CPU/board) → Use shared memory (fastest)
- NO (different systems) → Use TCP/IP (reliable) or UDP (fast)
Question 3: Do you need automatic failover?
- YES (hot standby) → Use mirroring pattern + heartbeat
- NO (cold standby) → Use simple 2oo2 or 2oo3
Question 4: What's the acceptable latency?
- <1ms → Shared memory (local only)
- 1-10ms → FIFO or local UDP
- 10-100ms → TCP/IP (LAN)
- 100-1000ms → TCP/IP (WAN) or async patterns
Decision Tree:
Tolerance = 1 fault?
NO → 2oo2 topology
Same system?
YES → Shared Memory + FIFO
NO → TCP + UDP
YES → 2oo3 topology
Need failover?
YES → Hot standby + mirror
NO → 2oo3 simple voting
*/
Generated by
1.18.0