Skip to main content

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 the Operation queue (QueueJob, processed one at a time, a single InProgress at 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.deleteSubscription handler (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:

  1. No Otoroshi cleanup (the deleteAll calls don't remove the keys on the Otoroshi side โ†’ orphan keys).
  2. 100% SYNC, no queue โ†’ it's the worst timeout candidate on a large tenant, and it doesn't even use DeletionService.

Summaryโ€‹

ObjectEntity itselfSubs cascadeOtoroshiRest 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.