Deletion Flows
An overview of the deletion flows for each base object (subscription, api, plan, team, user, tenant):
what gets deleted in cascade, and whether it runs synchronously or asynchronously (through the Operation queue).
Sources: DeletionService, QueueJob, TenantController.
Legendโ
- ๐ข
SYNC= executed in the HTTP request thread (the front waits for it) - ๐ต
ASYNC= deferred through theOperationqueue (QueueJob, processed one at a time, a singleInProgressat any moment) logical=_deleted = true/physical= row actually removed
1. Subscription โ deleteSubscriptions (the base building block)โ
deleteSubscriptions(subs, api, tenant) ๐ข SYNC (all inline)
โโ Phase 0 promote orphan children (parent re-election) DB
โโ Phase 1 enabled = false DB
โโ Phase 2 Otoroshi: run (surviving parents) / runForDeletion (HTTP, //)
โโ Phase 3b delete pending notifs (action.subscription) DB physical
โโ Phase 3 save "ApiKeyDeletionInformationV2" notif DB
โ โโ if paid plan โ enqueue Operation ThirdPartySubscription โโโโโ
โโ Phase 4 delete subscriptions DB physical โ
โผ
๐ต ASYNC QueueJob.deleteThirdPartySubscription โ Stripe cancellation
โ ๏ธ Subscription deletion (Otoroshi + DB) is fully SYNC. Only the payment cancellation is queued. The
QueueJob.deleteSubscriptionhandler (Operation Subscription/Delete) still exists but is dead code (nothing enqueues it anymore).
2. API โ deleteApiByQueueโ
deleteApiByQueue(apiId) ๐ข SYNC
โโ find subs(api) โ deleteSubscriptions(...) ๐ข (see ยง1)
โโ deleteApis([api])
โโ api โ deleteLogically DB logical
โโ (paid plans) โ enqueue Operation ThirdPartyProduct โโโโโโโโโโ
โโ enqueue Operation Api/Delete โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โผ โผ
๐ต ASYNC QueueJob.deleteApi: ๐ต deleteThirdPartyProduct
posts, issues, doc pages, usage plans (physical), (Stripe)
api notifs, demands + step_validators
โ api delete (physical)
3. Usage plan โ deleteUsagePlanByQueueโ
deleteUsagePlanByQueue(planId, apiId) ๐ข SYNC
โโ find subs(api, plan) โ deleteSubscriptions(...) ๐ข (see ยง1)
โโ api.possibleUsagePlans -= plan (save api) DB
โโ plan โ deleteByIdLogically DB logical
โโ (paid plan) โ enqueue Operation ThirdPartyProduct โโโโโโโโโโโ
โโ enqueue Operation UsagePlan/Delete โโโโโโโโโโโโโโโโโโโโโโโโ โ
โผ โผ
๐ต ASYNC QueueJob.deleteUsagePlan: ๐ต deleteThirdPartyProduct
plan doc pages, demands + step_validators, (Stripe)
notifs (action.plan)
โ plan delete (physical)
4. Team โ deleteTeamByQueueโ
deleteTeamByQueue(teamId) ๐ข SYNC
โโ apis = apis owned by the team
โโ subs = subs of the team OR on its apis
โโ for each owned api: deleteSubscriptions(...) ๐ข (see ยง1) [sequential mapAsync(1)]
โโ "consumer" subs (team subscribed to external apis): deleteSubscriptions(...) ๐ข
โโ deleteApis(apis) โ N ร (api logical + enqueue Api/Delete) ๐ต (see ยง2)
โโ deleteTeam โ team logical + enqueue Operation Team/Delete โโโ
โผ
๐ต ASYNC QueueJob.deleteTeam: team notifs โ team delete (physical)
๐ต ASYNC QueueJob.deleteApi : (one per api, see ยง2)
5. User โ deleteUserByQueue (current tenant) / deleteCompleteUserByQueue (all tenants)โ
deleteUserByQueue(userId, tenant) ๐ข SYNC
โโ user's personalTeam (in the tenant)
โโ deleteTeamByQueue(personalTeam) ๐ข (see ยง4, + enqueues)
โโ if the user has a personal team ONLY in this tenant:
โ deleteUser โ user logical + enqueue Operation User/Delete โโโ
โ else: keep the user (exists in another tenant) โ
โโ deleteUserFromAllTeams (SQL: remove user from teams.users) โ
โโ deleteUserNotifications (SQL: notifs + demands + validators) โ
โโ deleteChat (SQL: messages) โ
โโ delete userSessions (SQL) โผ
๐ต ASYNC QueueJob.deleteUser: TeamInvitation notifs, messages
โ user delete (physical)
๐ต ASYNC QueueJob.deleteTeam: personal team (see ยง4)
deleteCompleteUserByQueue: same but over ALL personal teams (all tenants),
deleteUser always called, SQL cleanups without tenant filter.
6. Tenant โ deleteTenant (TenantController) โ ๏ธ special caseโ
deleteTenant(id) ๐ข SYNC (no queue)
โโ apiRepo.deleteAll() (all tenant apis) DB physical
โโ apiSubscriptionRepo.deleteAll() (all subs) DB physical
โโ apiDocumentationPageRepo.deleteAll() DB physical
โโ notificationRepo.deleteAll() DB physical
โโ teamRepo.deleteAll() DB physical
โโ tenant.copy(deleted = true) (ITSELF: logical) DB logical
โโ users.lastTenant = null DB
โ ๏ธ Two notable singularities:
- No Otoroshi cleanup (the
deleteAllcalls don't remove the keys on the Otoroshi side โ orphan keys).- 100% SYNC, no queue โ it's the worst timeout candidate on a large tenant, and it doesn't even use
DeletionService.
Summaryโ
| Object | Entity itself | Subs cascade | Otoroshi | Rest of the cleanup |
|---|---|---|---|---|
| Subscription | ๐ข physical | โ | ๐ข SYNC | ๐ต payment |
| API | ๐ข logical โ ๐ต physical | ๐ข SYNC | ๐ข SYNC | ๐ต (posts/issues/docs/plans/demands) |
| Plan | ๐ข logical โ ๐ต physical | ๐ข SYNC | ๐ข SYNC | ๐ต (docs/demands/notifs) |
| Team | ๐ข logical โ ๐ต physical | ๐ข SYNC | ๐ข SYNC | ๐ต (notifs) + APIs cascade |
| User | ๐ข logical โ ๐ต physical | ๐ข SYNC (via team) | ๐ข SYNC | ๐ข SQL (teams/notifs/chat/sessions) + ๐ต |
| Tenant | ๐ข logical | ๐ข deleteAll | โ none | ๐ข deleteAll (all SYNC, no queue) |
The general pattern: the entity is logically deleted right away (SYNC) so it disappears from the front, and an Operation finalizes the physical deletion + peripheral cleanup in the background (ASYNC).
Subscriptions and Otoroshi are the exception (fully SYNC) โ that's where the timeout risk lies โ and the tenant is entirely outside this model.