mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 13:31:13 -05:00
291 lines
8.9 KiB
Markdown
291 lines
8.9 KiB
Markdown
# Atomic Approval Transactions
|
|
|
|
## Overview
|
|
|
|
Phase 1 of the atomic transaction RPC implementation has been completed. This replaces the error-prone manual rollback logic in the `process-selective-approval` edge function with a true PostgreSQL ACID transaction.
|
|
|
|
## Architecture
|
|
|
|
### OLD Flow (process-selective-approval)
|
|
```
|
|
Edge Function (2,759 lines) ──┐
|
|
├─ Create entity 1 ├─ Manual rollback on error
|
|
├─ Create entity 2 ├─ Network failure = orphaned data
|
|
├─ Create entity 3 ├─ Edge function crash = partial state
|
|
└─ Manual rollback if error─┘
|
|
```
|
|
|
|
### NEW Flow (process-selective-approval-v2)
|
|
```
|
|
Edge Function (~200 lines)
|
|
│
|
|
└──> RPC: process_approval_transaction()
|
|
│
|
|
└──> PostgreSQL Transaction ───────────┐
|
|
├─ Create entity 1 │
|
|
├─ Create entity 2 │ ATOMIC
|
|
├─ Create entity 3 │ (all-or-nothing)
|
|
└─ Commit OR Rollback ──────────┘
|
|
(any error = auto rollback)
|
|
```
|
|
|
|
## Key Benefits
|
|
|
|
✅ **True ACID Transactions**: All operations succeed or fail together
|
|
✅ **Automatic Rollback**: ANY error triggers immediate rollback
|
|
✅ **Network Resilient**: Edge function crash = automatic rollback
|
|
✅ **Zero Orphaned Entities**: Impossible by design
|
|
✅ **Simpler Code**: Edge function reduced from 2,759 to ~200 lines
|
|
|
|
## Database Functions Created
|
|
|
|
### Main Transaction Function
|
|
```sql
|
|
process_approval_transaction(
|
|
p_submission_id UUID,
|
|
p_item_ids UUID[],
|
|
p_moderator_id UUID,
|
|
p_submitter_id UUID,
|
|
p_request_id TEXT DEFAULT NULL
|
|
) RETURNS JSONB
|
|
```
|
|
|
|
### Helper Functions
|
|
- `create_entity_from_submission()` - Creates entities (parks, rides, companies, etc.)
|
|
- `update_entity_from_submission()` - Updates existing entities
|
|
- `delete_entity_from_submission()` - Soft/hard deletes entities
|
|
|
|
### Monitoring Table
|
|
- `approval_transaction_metrics` - Tracks performance, success rate, and rollbacks
|
|
|
|
## Feature Flag
|
|
|
|
The new flow is **disabled by default** to allow gradual rollout and testing.
|
|
|
|
### Enabling the New Flow
|
|
|
|
#### For Moderators (via Admin UI)
|
|
1. Navigate to Admin Settings
|
|
2. Find "Approval Transaction Mode" card
|
|
3. Toggle "Use Atomic Transaction RPC" to ON
|
|
4. Page will reload automatically
|
|
|
|
#### Programmatically
|
|
```typescript
|
|
// Enable
|
|
localStorage.setItem('use_rpc_approval', 'true');
|
|
|
|
// Disable
|
|
localStorage.setItem('use_rpc_approval', 'false');
|
|
|
|
// Check status
|
|
const isEnabled = localStorage.getItem('use_rpc_approval') === 'true';
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
### Basic Functionality ✓
|
|
- [ ] Enable feature flag via admin UI
|
|
- [ ] Approve a simple submission (1-2 items)
|
|
- [ ] Verify entities created correctly
|
|
- [ ] Check console logs for "Using edge function: process-selective-approval-v2"
|
|
- [ ] Verify version history shows correct attribution
|
|
|
|
### Error Scenarios ✓
|
|
- [ ] Submit invalid data → verify full rollback
|
|
- [ ] Trigger validation error → verify no partial state
|
|
- [ ] Kill edge function mid-execution → verify auto rollback
|
|
- [ ] Check logs for "Transaction failed, rolling back" messages
|
|
|
|
### Concurrent Operations ✓
|
|
- [ ] Two moderators approve same submission → one succeeds, one gets locked error
|
|
- [ ] Verify only one set of entities created (no duplicates)
|
|
|
|
### Data Integrity ✓
|
|
- [ ] Run orphaned entity check (see SQL query below)
|
|
- [ ] Verify session variables cleared after transaction
|
|
- [ ] Check `approval_transaction_metrics` for success rate
|
|
|
|
## Monitoring Queries
|
|
|
|
### Check for Orphaned Entities
|
|
```sql
|
|
-- Should return 0 rows after migration
|
|
SELECT
|
|
'parks' as table_name,
|
|
COUNT(*) as orphaned_count
|
|
FROM parks p
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM park_versions pv
|
|
WHERE pv.park_id = p.id
|
|
)
|
|
AND p.created_at > NOW() - INTERVAL '24 hours'
|
|
|
|
UNION ALL
|
|
|
|
SELECT
|
|
'rides' as table_name,
|
|
COUNT(*) as orphaned_count
|
|
FROM rides r
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM ride_versions rv
|
|
WHERE rv.ride_id = r.id
|
|
)
|
|
AND r.created_at > NOW() - INTERVAL '24 hours';
|
|
```
|
|
|
|
### Transaction Success Rate
|
|
```sql
|
|
SELECT
|
|
DATE_TRUNC('hour', created_at) as hour,
|
|
COUNT(*) as total_transactions,
|
|
COUNT(*) FILTER (WHERE success) as successful,
|
|
COUNT(*) FILTER (WHERE rollback_triggered) as rollbacks,
|
|
ROUND(AVG(duration_ms), 2) as avg_duration_ms,
|
|
ROUND(100.0 * COUNT(*) FILTER (WHERE success) / COUNT(*), 2) as success_rate
|
|
FROM approval_transaction_metrics
|
|
WHERE created_at > NOW() - INTERVAL '24 hours'
|
|
GROUP BY hour
|
|
ORDER BY hour DESC;
|
|
```
|
|
|
|
### Rollback Rate Alert
|
|
```sql
|
|
-- Alert if rollback_rate > 5%
|
|
SELECT
|
|
COUNT(*) FILTER (WHERE rollback_triggered) as rollbacks,
|
|
COUNT(*) as total_attempts,
|
|
ROUND(100.0 * COUNT(*) FILTER (WHERE rollback_triggered) / COUNT(*), 2) as rollback_rate
|
|
FROM approval_transaction_metrics
|
|
WHERE created_at > NOW() - INTERVAL '1 hour'
|
|
HAVING COUNT(*) FILTER (WHERE rollback_triggered) > 0;
|
|
```
|
|
|
|
## Rollback Plan
|
|
|
|
If issues are detected after enabling the new flow:
|
|
|
|
### Immediate Rollback (< 5 minutes)
|
|
```javascript
|
|
// Disable feature flag globally (or ask users to toggle off)
|
|
localStorage.setItem('use_rpc_approval', 'false');
|
|
window.location.reload();
|
|
```
|
|
|
|
### Data Recovery (if needed)
|
|
```sql
|
|
-- Identify submissions processed with v2 during problem window
|
|
SELECT
|
|
atm.submission_id,
|
|
atm.created_at,
|
|
atm.success,
|
|
atm.error_message
|
|
FROM approval_transaction_metrics atm
|
|
WHERE atm.created_at BETWEEN '2025-11-06 19:00:00' AND '2025-11-06 20:00:00'
|
|
AND atm.success = false
|
|
AND atm.rollback_triggered = true;
|
|
|
|
-- Check for orphaned entities (if any exist)
|
|
-- Use the orphaned entity query above
|
|
```
|
|
|
|
## Success Metrics
|
|
|
|
After full rollout, these metrics should be achieved:
|
|
|
|
| Metric | Target | Current |
|
|
|--------|--------|---------|
|
|
| Zero orphaned entities | 0 | ✓ TBD |
|
|
| Zero manual rollback logs | 0 | ✓ TBD |
|
|
| Transaction success rate | >99% | ✓ TBD |
|
|
| Avg transaction time | <500ms | ✓ TBD |
|
|
| Rollback rate | <1% | ✓ TBD |
|
|
|
|
## Deployment Phases
|
|
|
|
### Phase 1: ✅ COMPLETE
|
|
- [x] Create RPC functions (helper + main transaction)
|
|
- [x] Create new edge function v2
|
|
- [x] Add feature flag support to frontend
|
|
- [x] Create admin UI toggle
|
|
- [x] Add monitoring table + RLS policies
|
|
|
|
### Phase 2: 🟡 IN PROGRESS
|
|
- [ ] Test with single moderator account
|
|
- [ ] Monitor metrics for 24 hours
|
|
- [ ] Verify zero orphaned entities
|
|
- [ ] Collect feedback from test moderator
|
|
|
|
### Phase 3: 🔲 PENDING
|
|
- [ ] Enable for 10% of requests (weighted sampling)
|
|
- [ ] Monitor for 24 hours
|
|
- [ ] Check rollback rate < 1%
|
|
|
|
### Phase 4: 🔲 PENDING
|
|
- [ ] Enable for 50% of requests
|
|
- [ ] Monitor for 48 hours
|
|
- [ ] Compare performance metrics with old flow
|
|
|
|
### Phase 5: 🔲 PENDING
|
|
- [ ] Enable for 100% of requests
|
|
- [ ] Monitor for 1 week
|
|
- [ ] Mark old edge function as deprecated
|
|
|
|
### Phase 6: 🔲 PENDING
|
|
- [ ] Remove old edge function
|
|
- [ ] Archive manual rollback code
|
|
- [ ] Update all documentation
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: Feature flag not working
|
|
**Symptom**: Logs still show "process-selective-approval" even with flag enabled
|
|
**Solution**: Clear localStorage and reload: `localStorage.clear(); window.location.reload()`
|
|
|
|
### Issue: "RPC function not found" error
|
|
**Symptom**: Edge function fails with "process_approval_transaction not found"
|
|
**Solution**: Run the migration again or check function exists:
|
|
```sql
|
|
SELECT proname FROM pg_proc WHERE proname = 'process_approval_transaction';
|
|
```
|
|
|
|
### Issue: High rollback rate (>5%)
|
|
**Symptom**: Many transactions rolling back in metrics
|
|
**Solution**:
|
|
1. Check error messages in `approval_transaction_metrics.error_message`
|
|
2. Disable feature flag immediately
|
|
3. Investigate root cause (validation issues, data integrity, etc.)
|
|
|
|
### Issue: Orphaned entities detected
|
|
**Symptom**: Entities exist without corresponding versions
|
|
**Solution**:
|
|
1. Disable feature flag immediately
|
|
2. Run orphaned entity query to identify affected entities
|
|
3. Investigate cause (likely edge function crash during v1 flow)
|
|
4. Consider data cleanup (manual deletion or version creation)
|
|
|
|
## FAQ
|
|
|
|
**Q: Can I switch back to the old flow without data loss?**
|
|
A: Yes. Simply toggle off the feature flag. All data remains intact.
|
|
|
|
**Q: What happens if the edge function crashes mid-transaction?**
|
|
A: PostgreSQL automatically rolls back the entire transaction. No orphaned data.
|
|
|
|
**Q: How do I know which flow approved a submission?**
|
|
A: Check `approval_transaction_metrics` table. If a row exists, v2 was used.
|
|
|
|
**Q: Can I use both flows simultaneously?**
|
|
A: Yes. The feature flag is per-browser, so different moderators can use different flows.
|
|
|
|
**Q: When will the old flow be removed?**
|
|
A: After 30 days of stable operation at 100% rollout (Phase 6).
|
|
|
|
## References
|
|
|
|
- [Moderation Documentation](./versioning/MODERATION.md)
|
|
- [JSONB Elimination](./JSONB_ELIMINATION_COMPLETE.md)
|
|
- [Error Tracking](./ERROR_TRACKING.md)
|
|
- [PostgreSQL Transactions](https://www.postgresql.org/docs/current/tutorial-transactions.html)
|
|
- [ACID Properties](https://en.wikipedia.org/wiki/ACID)
|