fix: comprehensive GDPR user removal with performance optimization#314
Conversation
Fix missing table deletions and RESTRICT constraint bugs in the GDPR user removal flow, and optimize performance by breaking the single long-running transaction into batched phases. Bug fixes: - Fix kilo_pass_issuance_items RESTRICT FK blocking credit_transactions deletion for Kilo Pass subscribers - Fix cloud_agent_webhook_triggers RESTRICT FK potentially blocking agent_environment_profiles cascade on user deletion - Add 13 previously missing table deletions (enrichment_data, source_embeddings, deployments, code_indexing_*, referral_codes, referral_code_usages, organization_memberships, organization_user_*, free_model_usage, microdollar_usage_metadata, api_request_log) Performance: - Split single transaction into 3 phases to reduce lock duration - Phase 1: batch-delete high-volume tables in chunks of 5000 rows outside a transaction (microdollar_usage_metadata, microdollar_usage, api_request_log) - Phase 2: small transaction for Kilo Pass issuance chain + credits - Phase 3: fast transaction for remaining tables + user row - Parallelize external service calls (Stripe, Customer.io, R2, v2 sessions) via Promise.allSettled - Batch v2 session Durable Object deletions in chunks of 10 concurrent requests
Code Review SummaryStatus: No Issues Found | Recommendation: Merge This PR refactors the GDPR user deletion flow into a well-structured 3-phase approach and adds concurrent external service deletion. Key observations:
Files Reviewed (4 files)
|
Code Review SummaryStatus: No Issues Found | Recommendation: Merge OverviewThis PR significantly improves the GDPR user deletion flow with three well-designed changes:
The three-phase approach (batch pre-delete → Kilo Pass chain → final transaction) is a sound architecture that balances data integrity with performance. The new tables covered ( The test coverage is thorough, with dedicated tests for each newly covered table and the Kilo Pass RESTRICT constraint scenario. Files Reviewed (4 files)
|
Summary
Bug Fixes
kilo_pass_issuance_itemsRESTRICT blocker (critical)The old code deleted
credit_transactionsbefore the user row, butkilo_pass_issuance_itemsholds aRESTRICTFK oncredit_transactions.id. The cascade chain (kilo_pass_subscriptions→kilo_pass_issuances→kilo_pass_issuance_items) only fires whenkilocode_usersis deleted — too late. This means GDPR deletion would fail for any Kilo Pass subscriber. Phase 2 now explicitly deletes the issuance chain before credit transactions.cloud_agent_webhook_triggersRESTRICT riskHas a
RESTRICTFK onagent_environment_profiles.id. When user deletion cascades to profiles, this could block the cascade. Now explicitly deleted before the user row.Missing Table Deletions
Previously orphaned after GDPR removal:
microdollar_usage_metadataenrichment_datasource_embeddingsdeploymentscode_indexing_search/code_indexing_manifestreferral_codes/referral_code_usagesorganization_membershipsorganization_user_limits/organization_user_usagefree_model_usageapi_request_logPerformance Optimization
Before: Single transaction deleting all tables sequentially — could hold locks for minutes on heavy users, causing timeouts.
After: Three-phase approach:
microdollar_usage_metadata,microdollar_usage,api_request_log) in chunks of 5,000 rows. Each batch holds locks for milliseconds.External service calls (Stripe, Customer.io, R2 blobs, v2 session DOs) now run concurrently via
Promise.allSettled(), and v2 session deletions are batched in chunks of 10 concurrent requests.Test Coverage
microdollar_usage_metadatacleanupenrichment_dataPII deletionfree_model_usagedeletion