Cascade Deletion and Restoration
Date: October 2025
Status: Production-ready
Branch: fix/soft-deletion-behavior
Overview
This document describes the configuration-driven cascade system for soft deletion and restoration in the Rhesis backend. The cascade system automatically propagates soft delete and restore operations to related child entities based on a centralized configuration, eliminating the need for manual cascade logic in every CRUD function.
Motivation
In a relational database with complex parent-child relationships, soft deleting or restoring a parent entity often requires the same operation on its children. For example:
- Deleting a TestRun should also soft delete all associated TestResults
- Restoring a TestRun should also restore all associated TestResults
The Problem: Manual Cascade Logic
Before the cascade system, each CRUD function needed manual cascade logic:
Problems:
- Repetitive code across multiple functions
- Error-prone (easy to miss child relationships)
- Hard to maintain (adding new relationships requires code changes everywhere)
- Not scalable (every new parent-child relationship needs new manual code)
The Solution: Configuration-Driven Cascades
With the cascade system, the same operation is now automatic:
Benefits:
- Single source of truth for cascade relationships
- Automatic cascade for both delete and restore
- No code changes needed in CRUD functions
- Easy to add new cascade relationships
- Consistent behavior across all entities
Architecture
The cascade system consists of three main components:
1. Cascade Configuration (config/cascade_config.py)
Centralized registry that defines all parent-child relationships:
2. Cascade Service (services/cascade.py)
Generic service that reads the configuration and performs bulk cascade operations:
3. Automatic Integration (utils/crud_utils.py)
The generic delete_item() and restore_item() functions automatically call the cascade service:
How to Add New Cascade Relationships
Adding a new cascade relationship is a 5-line change in config/cascade_config.py:
Step 1: Identify the Relationship
Determine:
- Parent model: The entity being deleted/restored
- Child model: The dependent entity
- Foreign key: The column in the child table that references the parent
Step 2: Add to Configuration
Add a new entry or append to an existing entry in CASCADE_RELATIONSHIPS:
Step 3: Test the Cascade
That’s it! No code changes needed. The cascade will work automatically. Test it:
Advanced Configuration
Disable Delete or Restore for Specific Relationships
You can selectively disable cascade operations:
Complex Multi-Level Cascades
The system automatically handles multi-level cascades:
When you delete a Project:
- All
TestSetrecords are soft deleted - All
Testrecords in those test sets are soft deleted - All
TestResultrecords for those tests are soft deleted
Note: Each level is handled independently, not recursively. The parent cascade handles its immediate children only.
Performance Considerations
Bulk UPDATE Operations
The cascade service uses efficient bulk UPDATE queries instead of loading objects:
Benefits:
- Fast: Single UPDATE query regardless of how many children
- Memory-efficient: No objects loaded into memory
- Transaction-safe: All updates happen in one transaction
Organization Filtering
The cascade service automatically applies organization filters when available:
This ensures:
- Security: Only cascade to entities in the same organization
- Performance: Smaller result sets to process
- Data integrity: Cross-org cascades are prevented
Transaction Management
All cascade operations are fully transactional:
Guarantees:
- Atomic: Either all entities are deleted/restored, or none are
- Consistent: Database remains in valid state even on errors
- Isolated: Other transactions don’t see partial cascades
- Durable: Committed cascades are permanent
Testing Cascade Relationships
Unit Tests
Test cascade relationships in isolation:
Integration Tests
Test end-to-end cascade behavior:
Test Data Factories
Use fixtures to create parent-child test data:
Best Practices
1. Document Relationships
Always add a description to cascade relationships:
2. Consider Data Preservation
Some relationships should NOT cascade:
3. Test Cascade Behavior
Always add tests when adding new cascade relationships:
4. Monitor Performance
For entities with many children (>1000), monitor cascade performance:
5. Keep Cascade Chains Short
Avoid deep cascade chains (>3 levels) as they can be hard to reason about:
Common Patterns
Pattern 1: Simple One-to-Many
The most common pattern:
Pattern 2: Multiple Children
A parent with multiple child types:
Pattern 3: Conditional Cascade
Different behavior for delete vs. restore:
Troubleshooting
Issue: Cascade Not Working
Symptom: Children are not being soft deleted when parent is deleted.
Solutions:
- Check that the relationship is configured in
CASCADE_RELATIONSHIPS - Verify the
foreign_keyname matches the actual column name - Ensure
cascade_delete=True(default) - Check logs for any errors during cascade
Issue: Performance Degradation
Symptom: Cascade operations are slow.
Solutions:
- Add an index on the foreign key column
- Verify bulk UPDATE is being used (not loading objects)
- Check for unnecessary filters or joins
- Consider batching for very large child sets (>10,000)
Issue: Orphaned Records
Symptom: Children remain after parent is permanently deleted.
Solutions:
- Soft delete cascade should happen before hard delete
- Check that hard delete operations go through proper channels
- Use database foreign key constraints with
ON DELETEclauses as backup
Migration Guide
Migrating Existing Manual Cascades
If you have existing manual cascade logic, follow these steps:
Step 1: Add Configuration
Add the relationship to CASCADE_RELATIONSHIPS:
Step 2: Remove Manual Logic
Replace manual cascade code:
Step 3: Update Tests
Update tests to use the new pattern:
Related Documentation
- Soft Deletion Architecture - Base soft deletion implementation
- Database Models - Model definitions and relationships
- API Structure - How cascade integrates with REST APIs
- Development Workflow - Testing and deployment
Summary
The cascade system provides a scalable, maintainable, and efficient way to handle parent-child relationships in soft deletion and restoration:
- Configuration-driven: 5 lines to add new relationships
- Automatic: No code changes in CRUD functions
- Efficient: Bulk UPDATE operations
- Transaction-safe: Full ACID guarantees
- Testable: Clear patterns for unit and integration tests
When adding new cascade relationships, simply update config/cascade_config.py and add tests. The system handles the rest automatically.