Approve tool use

The user has approved the tool use.
This commit is contained in:
gpt-engineer-app[bot]
2025-11-06 20:15:14 +00:00
parent edd12b4454
commit 67525173cb
5 changed files with 450 additions and 1 deletions

View File

@@ -0,0 +1,290 @@
# 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)

View File

@@ -0,0 +1,100 @@
import { useState, useEffect } from 'react';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { AlertCircle, CheckCircle2, Database } from 'lucide-react';
import { Alert, AlertDescription } from '@/components/ui/alert';
/**
* Admin toggle for switching between approval flows:
* - OLD: Manual rollback in edge function (error-prone)
* - NEW: Atomic PostgreSQL transaction (true ACID guarantees)
*/
export function ApprovalTransactionToggle() {
const [useRpcApproval, setUseRpcApproval] = useState(false);
useEffect(() => {
// Read feature flag from localStorage
const enabled = localStorage.getItem('use_rpc_approval') === 'true';
setUseRpcApproval(enabled);
}, []);
const handleToggle = (checked: boolean) => {
localStorage.setItem('use_rpc_approval', checked ? 'true' : 'false');
setUseRpcApproval(checked);
// Force page reload to ensure all components pick up the new setting
window.location.reload();
};
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="w-5 h-5" />
Approval Transaction Mode
</CardTitle>
<CardDescription>
Control which approval flow is used for moderation
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<Label htmlFor="rpc-approval-toggle" className="flex-1">
<div className="font-medium">Use Atomic Transaction RPC</div>
<div className="text-sm text-muted-foreground">
{useRpcApproval
? 'Using process-selective-approval-v2 (NEW)'
: 'Using process-selective-approval (OLD)'}
</div>
</Label>
<Switch
id="rpc-approval-toggle"
checked={useRpcApproval}
onCheckedChange={handleToggle}
/>
</div>
{useRpcApproval ? (
<Alert>
<CheckCircle2 className="h-4 w-4" />
<AlertDescription>
<strong>Atomic Transaction Mode Enabled</strong>
<ul className="mt-2 space-y-1 text-sm">
<li> True ACID transactions</li>
<li> Automatic rollback on errors</li>
<li> Network-resilient (edge function crash = auto rollback)</li>
<li> Zero orphaned entities</li>
</ul>
</AlertDescription>
</Alert>
) : (
<Alert>
<AlertCircle className="h-4 w-4" />
<AlertDescription>
<strong>Legacy Mode Active</strong>
<ul className="mt-2 space-y-1 text-sm">
<li> Manual rollback logic (error-prone)</li>
<li> Risk of orphaned entities if edge function crashes</li>
<li> No true atomicity guarantee</li>
</ul>
<p className="mt-2 font-medium">
Consider enabling Atomic Transaction Mode for improved reliability.
</p>
</AlertDescription>
</Alert>
)}
<div className="text-xs text-muted-foreground pt-2 border-t">
<p><strong>Testing Instructions:</strong></p>
<ol className="list-decimal list-inside space-y-1 mt-1">
<li>Enable the toggle and approve a submission</li>
<li>Check logs for "Using edge function: process-selective-approval-v2"</li>
<li>Verify no orphaned entities in the database</li>
<li>Test error scenarios (invalid data, network issues)</li>
</ol>
</div>
</CardContent>
</Card>
);
}

View File

@@ -177,14 +177,42 @@ export async function approvePhotoSubmission(
* @param itemIds - Array of item IDs to approve
* @returns Action result
*/
/**
* Feature flag to enable atomic transaction RPC approval flow.
* Set to true to use the new process-selective-approval-v2 edge function
* that wraps the entire approval in a single PostgreSQL transaction.
*
* Benefits of v2:
* - True atomic transactions (all-or-nothing)
* - Automatic rollback on ANY error
* - Network-resilient (edge function crash = auto rollback)
* - Eliminates orphaned entities
*
* To enable: localStorage.setItem('use_rpc_approval', 'true')
* To disable: localStorage.setItem('use_rpc_approval', 'false')
*/
const USE_RPC_APPROVAL = typeof window !== 'undefined' &&
localStorage.getItem('use_rpc_approval') === 'true';
export async function approveSubmissionItems(
supabase: SupabaseClient,
submissionId: string,
itemIds: string[]
): Promise<ModerationActionResult> {
try {
// Use v2 edge function if feature flag is enabled
const edgeFunctionName = USE_RPC_APPROVAL
? 'process-selective-approval-v2'
: 'process-selective-approval';
console.log(`[Approval] Using edge function: ${edgeFunctionName}`, {
submissionId,
itemCount: itemIds.length,
useRpcApproval: USE_RPC_APPROVAL
});
const { data: approvalData, error: approvalError, requestId } = await invokeWithTracking(
'process-selective-approval',
edgeFunctionName,
{
itemIds,
submissionId,

View File

@@ -42,6 +42,9 @@ verify_jwt = true
[functions.process-selective-approval]
verify_jwt = false
[functions.process-selective-approval-v2]
verify_jwt = false
[functions.send-escalation-notification]
verify_jwt = true

View File

@@ -0,0 +1,28 @@
-- Enable RLS on approval_transaction_metrics table
ALTER TABLE approval_transaction_metrics ENABLE ROW LEVEL SECURITY;
-- Policy: Only moderators and admins can view metrics
CREATE POLICY "Moderators can view approval metrics"
ON approval_transaction_metrics
FOR SELECT
TO authenticated
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE user_roles.user_id = auth.uid()
AND user_roles.role IN ('moderator', 'admin', 'superuser')
)
);
-- Policy: System can insert metrics (SECURITY DEFINER functions)
CREATE POLICY "System can insert approval metrics"
ON approval_transaction_metrics
FOR INSERT
TO authenticated
WITH CHECK (true);
COMMENT ON POLICY "Moderators can view approval metrics" ON approval_transaction_metrics IS
'Allows moderators, admins, and superusers to view approval transaction metrics for monitoring and analytics';
COMMENT ON POLICY "System can insert approval metrics" ON approval_transaction_metrics IS
'Allows the process_approval_transaction function to log metrics. The function is SECURITY DEFINER so it runs with elevated privileges';