Make repository deletes much faster by adding custom deletion code and have additional tests to verify the deletion code paths

This commit is contained in:
Joseph Schorr 2014-11-10 23:05:20 -05:00
parent e7cbda86f7
commit eddcc02ea6
3 changed files with 80 additions and 3 deletions

View file

@ -245,6 +245,24 @@ class Repository(BaseModel):
(('namespace_user', 'name'), True),
)
def delete_instance(self, recursive=False, delete_nullable=False):
# Note: peewee generates extra nested deletion statements here that are slow and unnecessary.
# Therefore, we define our own deletion order here and use the dependency system to verify it.
ordered_dependencies = [RepositoryAuthorizedEmail, RepositoryTag, Image, LogEntry,
RepositoryBuild, RepositoryBuildTrigger, RepositoryNotification,
RepositoryPermission, AccessToken]
for query, fk in self.dependencies(search_nullable=True):
model = fk.model_class
if not model in ordered_dependencies:
raise Exception('Missing repository deletion dependency: %s', model)
for model in ordered_dependencies:
model.delete().where(model.repository == self).execute()
# Delete the repository itself.
super(Repository, self).delete_instance(recursive=False, delete_nullable=False)
class Role(BaseModel):
name = CharField(index=True, unique=True)
@ -441,7 +459,6 @@ class LogEntry(BaseModel):
performer = QuayUserField(allows_robots=True, index=True, null=True,
related_name='performer')
repository = ForeignKeyField(Repository, index=True, null=True)
access_token = ForeignKeyField(AccessToken, null=True)
datetime = DateTimeField(default=datetime.now, index=True)
ip = CharField(null=True)
metadata_json = TextField(default='{}')

View file

@ -1774,7 +1774,7 @@ def purge_repository(namespace_name, repository_name):
# Delete the rest of the repository metadata
fetched = _get_repository(namespace_name, repository_name)
fetched.delete_instance(recursive=True)
fetched.delete_instance(recursive=True, delete_nullable=True)
def get_private_repo_count(username):
@ -2004,7 +2004,7 @@ def log_action(kind_name, user_or_organization_name, performer=None,
kind = LogEntryKind.get(LogEntryKind.name == kind_name)
account = User.get(User.username == user_or_organization_name)
LogEntry.create(kind=kind, account=account, performer=performer,
repository=repository, access_token=access_token, ip=ip,
repository=repository, ip=ip,
metadata_json=json.dumps(metadata), datetime=timestamp)

View file

@ -1140,6 +1140,8 @@ class TestChangeRepoVisibility(ApiTestCase):
class TestDeleteRepository(ApiTestCase):
SIMPLE_REPO = ADMIN_ACCESS_USER + '/simple'
COMPLEX_REPO = ADMIN_ACCESS_USER + '/complex'
def test_deleterepo(self):
self.login(ADMIN_ACCESS_USER)
@ -1151,6 +1153,64 @@ class TestDeleteRepository(ApiTestCase):
params=dict(repository=self.SIMPLE_REPO),
expected_code=404)
def test_deleterepo2(self):
self.login(ADMIN_ACCESS_USER)
self.deleteResponse(Repository,
params=dict(repository=self.COMPLEX_REPO))
# Verify the repo was deleted.
self.getResponse(Repository,
params=dict(repository=self.COMPLEX_REPO),
expected_code=404)
def test_populate_and_delete_repo(self):
self.login(ADMIN_ACCESS_USER)
# Make sure the repository has come images and tags.
self.assertTrue(len(list(model.get_repository_images(ADMIN_ACCESS_USER, 'complex'))) > 0)
self.assertTrue(len(list(model.list_repository_tags(ADMIN_ACCESS_USER, 'complex'))) > 0)
# Add some data for the repository, in addition to is already existing images and tags.
repository = model.get_repository(ADMIN_ACCESS_USER, 'complex')
# Create some access tokens.
access_token = model.create_access_token(repository, 'read')
model.create_access_token(repository, 'write')
delegate_token = model.create_delegate_token(ADMIN_ACCESS_USER, 'complex', 'sometoken', 'read')
model.create_delegate_token(ADMIN_ACCESS_USER, 'complex', 'sometoken', 'write')
# Create some repository builds.
model.create_repository_build(repository, access_token, {}, 'someid', 'foobar')
model.create_repository_build(repository, delegate_token, {}, 'someid2', 'foobar2')
# Create some notifications.
model.create_repo_notification(repository, 'repo_push', 'hipchat', {})
model.create_repo_notification(repository, 'build_queued', 'slack', {})
# Create some logs.
model.log_action('push_repo', ADMIN_ACCESS_USER, repository=repository)
model.log_action('push_repo', ADMIN_ACCESS_USER, repository=repository)
# Create some build triggers.
user = model.get_user(ADMIN_ACCESS_USER)
model.create_build_trigger(repository, 'github', 'sometoken', user)
model.create_build_trigger(repository, 'github', 'anothertoken', user)
# Create some email authorizations.
model.create_email_authorization_for_repo(ADMIN_ACCESS_USER, 'complex', 'a@b.com')
model.create_email_authorization_for_repo(ADMIN_ACCESS_USER, 'complex', 'b@c.com')
# Delete the repository.
self.deleteResponse(Repository,
params=dict(repository=self.COMPLEX_REPO))
# Verify the repo was deleted.
self.getResponse(Repository,
params=dict(repository=self.COMPLEX_REPO),
expected_code=404)
class TestGetRepository(ApiTestCase):
PUBLIC_REPO = PUBLIC_USER + '/publicrepo'