8.6 KiB
Reorg Handling Specification
Overview
This document specifies how the indexer handles blockchain reorganizations (reorgs), where the canonical chain changes and previously indexed blocks become invalid. Reorg handling ensures data consistency and maintains accurate blockchain state.
Reorg Detection
Detection Methods
1. Block Hash Mismatch
- Compare stored block hash with RPC node block hash
- If mismatch detected, reorg has occurred
2. Parent Hash Validation
- Verify each block's parent_hash matches previous block's hash
- Chain break indicates reorg point
3. Block Height Comparison
- Monitor chain head block number
- Sudden decrease indicates potential reorg
4. Chain Head Monitoring
- Poll
eth_blockNumberperiodically - Compare with last indexed block number
- Detect rollback scenarios
Detection Strategy
Real-time Monitoring:
- Check block hash after each new block ingestion
- Compare with RPC node's block hash for same block number
- Immediate detection for recent blocks
Periodic Validation:
- Validate last N blocks (e.g., 100 blocks) every M seconds
- Detect deep reorgs that may have been missed
- Background validation job
Checkpoint Validation:
- Validate checkpoint blocks (every 1000 blocks)
- Ensure checkpoint blocks still exist in canonical chain
- Detect deep reorgs quickly
Reorg Handling Flow
flowchart TB
Detect[Detect Reorg]
Identify[Identify Reorg Point<br/>Find Common Ancestor]
Mark[Mark Blocks as Orphaned]
Delete[Delete Orphaned Data]
Reindex[Re-index New Chain]
Verify[Verify Data Consistency]
Detect --> Identify
Identify --> Mark
Mark --> Delete
Delete --> Reindex
Reindex --> Verify
Verify --> Done[Complete]
Verify -->|Inconsistency| Delete
Step 1: Identify Reorg Point
Goal: Find the common ancestor block (last block that's still valid).
Algorithm:
- Start from current chain head
- Compare block hash with stored hash at each block number
- When hash matches, that's the common ancestor
- All blocks after common ancestor need reorg handling
Optimization:
- Binary search to find reorg point quickly
- Start from recent blocks and work backwards
- Cache last N block hashes for faster comparison
Step 2: Mark Blocks as Orphaned
Strategy: Mark blocks as orphaned before deletion (for audit trail).
Database Update:
UPDATE blocks
SET orphaned = true, orphaned_at = NOW()
WHERE chain_id = ?
AND block_number > ?
AND orphaned = false;
Benefits:
- Audit trail of reorgs
- Ability to query orphaned blocks
- Easier debugging
Step 3: Delete Orphaned Data
Cascade Deletion Order:
- Token transfers (depends on transactions)
- Logs (depends on transactions)
- Traces (depends on transactions)
- Internal transactions (depends on transactions)
- Transactions (depends on blocks)
- Blocks (orphaned blocks)
Implementation:
- Use database transactions for atomicity
- Cascade deletes via foreign keys (if enabled)
- Or explicit deletion in correct order
Performance Considerations:
- Batch deletions for large reorgs
- Index on
block_numberfor efficient deletion - Consider soft deletes (mark as deleted) vs hard deletes
Step 4: Re-index New Chain
Process:
- Fetch new blocks from RPC node (from reorg point onward)
- Process blocks through normal indexing pipeline
- Verify each block before marking as indexed
Optimization:
- Parallel processing for multiple blocks (if safe)
- Use existing indexer workers
- Priority queue: reorg blocks before new blocks
Step 5: Verify Data Consistency
Validation Checks:
- Block hashes match RPC node
- Transaction counts match
- Parent hashes form valid chain
- No gaps in block numbers
Metrics:
- Reorg depth (number of blocks affected)
- Reorg duration (time to handle)
- Data consistency verification
Data Consistency Guarantees
Transaction Isolation
Strategy: Use database transactions to ensure atomic reorg handling.
Implementation:
BEGIN;
-- Mark blocks as orphaned
-- Delete orphaned data
-- Insert new blocks
-- Verify consistency
COMMIT;
Rollback: If any step fails, rollback entire operation.
Idempotency
Requirement: Reorg handling must be idempotent (safe to retry).
Mechanisms:
- Check block hash before processing
- Skip blocks already correctly indexed
- Use unique constraints to prevent duplicates
Finality Considerations
Reorg Depth Limits:
- Only handle reorgs within finality window
- For PoW: Typically 12-100 blocks deep
- For PoS: Typically 1-2 epochs (32-64 blocks)
- For BFT: Typically immediate finality
Configuration:
- Configurable reorg depth limit per chain
- Skip reorgs beyond finality window (log for investigation)
Performance Optimization
Handling Frequent Reorgs
Problem: Some chains have frequent small reorgs (1-2 blocks).
Solution:
- Batch reorg handling (wait for stability)
- Detect reorgs but delay handling for short period
- Only handle if reorg persists
Configuration:
- Reorg confirmation period: 30 seconds
- Maximum reorg depth to handle immediately: 5 blocks
- Deeper reorgs: Manual investigation
Handling Deep Reorgs
Problem: Deep reorgs require deleting and re-indexing many blocks.
Optimization Strategies:
- Parallel Processing: Process new chain blocks in parallel
- Batch Operations: Batch database operations
- Incremental Updates: Only update changed data
- Selective Deletion: Only delete affected data (not entire blocks if possible)
Index Maintenance
During Reorg:
- Pause index updates temporarily
- Resume after reorg handling complete
- Rebuild affected indexes if necessary
Monitoring and Alerting
Metrics
Reorg Metrics:
- Reorg count (per chain, per time period)
- Reorg depth distribution
- Reorg handling duration (p50, p95, p99)
- Data consistency check results
Alerting Rules:
- Reorg depth > 10 blocks: Warning (investigate)
- Reorg depth > 100 blocks: Critical (potential chain issue)
- Reorg handling duration > 5 minutes: Warning
- Data consistency check failure: Critical
Logging
Log Events:
- Reorg detection (block number, depth)
- Reorg point identification (common ancestor)
- Blocks orphaned (count, block numbers)
- Re-indexing progress
- Data consistency verification results
Log Levels:
- INFO: Normal reorgs (< 5 blocks)
- WARN: Unusual reorgs (5-10 blocks)
- ERROR: Deep reorgs (> 10 blocks) or failures
Edge Cases
Multiple Reorgs in Quick Succession
Scenario: Chain reorgs, then reorgs again before first reorg is handled.
Handling:
- Cancel in-progress reorg handling
- Start new reorg handling from latest state
- Ensure idempotency
Reorg During Backfill
Scenario: Historical block indexing encounters a reorg.
Handling:
- Pause backfill
- Handle reorg
- Resume backfill from reorg point
Concurrent Reorg Handling
Prevention:
- Use database locks to prevent concurrent reorg handling
- Single reorg handler per chain
- Queue reorg events if handler is busy
Recovery Procedures
Manual Reorg Handling
When to Use:
- Automatic handling fails
- Deep reorgs beyond normal limits
- Data corruption detected
Procedure:
- Identify reorg point manually
- Verify with RPC node
- Mark blocks as orphaned
- Delete orphaned data
- Trigger re-indexing
- Verify data consistency
Data Recovery
Backup Strategy:
- Regular database backups
- Point-in-time recovery capability
- Ability to restore to pre-reorg state
Recovery Steps:
- Restore database to point before reorg
- Re-run indexer from that point
- Let automatic reorg handling process naturally
Testing Strategy
Unit Tests
- Reorg detection logic
- Common ancestor identification
- Orphan marking
- Data deletion logic
Integration Tests
- Simulate reorgs (testnet with known reorgs)
- Verify data consistency after reorg
- Test concurrent reorg handling
- Test deep reorgs
Load Tests
- Simulate frequent reorgs
- Measure performance impact
- Test reorg handling during high load
Configuration
Reorg Handling Configuration
reorg:
detection:
check_interval_seconds: 10
validation_depth: 100
checkpoint_interval: 1000
handling:
max_depth: 100
confirmation_period_seconds: 30
batch_size: 1000
parallel_processing: true
finality:
chain_138:
type: "poa" # or "pos", "pow", "bft"
depth_limit: 50
finality_blocks: 12
References
- Indexer Architecture: See
indexer-architecture.md - Data Models: See
data-models.md - Database Schema: See
../database/postgres-schema.md