Make namespace deletion asynchronous

Instead of deleting a namespace synchronously as before, we now mark the namespace for deletion, disable it, and rename it. A worker then comes along and deletes the namespace in the background. This results in a *significantly* better user experience, as the namespace deletion operation now "completes" in under a second, where before it could take 10s of minutes at the worse.

Fixes https://jira.coreos.com/browse/QUAY-838
This commit is contained in:
Joseph Schorr 2018-02-23 16:45:16 -05:00
parent d9015a1863
commit 8bc55a5676
21 changed files with 244 additions and 129 deletions

View file

@ -6,7 +6,7 @@ from flask import request
import features
from app import billing as stripe, avatar, all_queues, authentication
from app import billing as stripe, avatar, all_queues, authentication, namespace_gc_queue
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
related_user_resource, internal_only, require_user_admin, log_action,
show_if, path_param, require_scope, require_fresh_login)
@ -217,7 +217,7 @@ class Organization(ApiResource):
except model.InvalidOrganizationException:
raise NotFound()
model.user.delete_user(org, all_queues)
model.user.mark_namespace_for_deletion(org, all_queues, namespace_gc_queue)
return '', 204
raise Unauthorized()

View file

@ -412,7 +412,7 @@ class SuperUserManagement(ApiResource):
if superusers.is_superuser(username):
raise InvalidRequest('Cannot delete a superuser')
pre_oci_model.delete_user(username)
pre_oci_model.mark_user_for_deletion(username)
return '', 204
raise Unauthorized()
@ -540,7 +540,7 @@ class SuperUserOrganizationManagement(ApiResource):
def delete(self, name):
""" Deletes the specified organization. """
if SuperUserPermission().can():
pre_oci_model.delete_organization(name)
pre_oci_model.mark_organization_for_deletion(name)
return '', 204
raise Unauthorized()

View file

@ -353,7 +353,7 @@ class SuperuserDataInterface(object):
"""
@abstractmethod
def delete_user(self, username):
def mark_user_for_deletion(self, username):
"""
Returns None
"""
@ -383,7 +383,7 @@ class SuperuserDataInterface(object):
"""
@abstractmethod
def delete_organization(self, name):
def mark_organization_for_deletion(self, name):
"""
Returns None
"""

View file

@ -2,7 +2,7 @@ import features
from flask import request
from app import all_queues, userfiles
from app import all_queues, userfiles, namespace_gc_queue
from auth.permissions import ReadRepositoryPermission, ModifyRepositoryPermission, AdministerRepositoryPermission
from data import model, database
from endpoints.api.build import get_job_config, _get_build_status
@ -141,9 +141,9 @@ class PreOCIModel(SuperuserDataInterface):
return Organization(org.username, org.email)
def delete_organization(self, name):
def mark_organization_for_deletion(self, name):
org = model.organization.get_organization(name)
model.user.delete_user(org, all_queues)
model.user.mark_namespace_for_deletion(org, all_queues, namespace_gc_queue, force=True)
def take_ownership(self, namespace, authed_user):
entity = model.user.get_user_or_org(namespace)
@ -172,9 +172,9 @@ class PreOCIModel(SuperuserDataInterface):
user = model.user.get_nonrobot_user(username)
model.user.change_password(user, password)
def delete_user(self, username):
def mark_user_for_deletion(self, username):
user = model.user.get_nonrobot_user(username)
model.user.delete_user(user, all_queues, force=True)
model.user.mark_namespace_for_deletion(user, all_queues, namespace_gc_queue, force=True)
def create_reset_password_email_code(self, email):
code = model.user.create_reset_password_email_code(email)

View file

@ -12,7 +12,7 @@ from peewee import IntegrityError
import features
from app import (app, billing as stripe, authentication, avatar, user_analytics, all_queues,
oauth_login)
oauth_login, namespace_gc_queue)
from auth import scopes
from auth.auth_context import get_authenticated_user
@ -485,7 +485,7 @@ class User(ApiResource):
if app.config['AUTHENTICATION_TYPE'] != 'Database':
abort(404)
model.user.delete_user(get_authenticated_user(), all_queues)
model.user.mark_namespace_for_deletion(get_authenticated_user(), all_queues, namespace_gc_queue)
return '', 204