From 6c4e78ec0c2de8528bf47ac587436f27512a931e Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Wed, 25 Feb 2015 17:49:46 -0500 Subject: [PATCH] Fix the deadlock in tags. --- data/model/legacy.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/data/model/legacy.py b/data/model/legacy.py index 85b533053..5188a9710 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -1580,25 +1580,16 @@ def list_repository_tags(namespace_name, repository_name, include_hidden=False): def _garbage_collect_tags(namespace_name, repository_name): - inner = (RepositoryTag - .select(RepositoryTag.id, - RepositoryTag.lifetime_end_ts, - RepositoryTag.repository) - .alias('rt')) - to_delete = (RepositoryTag - .select(inner.c.id) - .from_(inner) - .join(Repository, on=(inner.c.repository_id == Repository.id)) - .join(Namespace, on=(Repository.namespace_user == Namespace.id)) - .where(Repository.name == repository_name, Namespace.username == namespace_name, - ~(inner.c.lifetime_end_ts >> None), - (inner.c.lifetime_end_ts + Namespace.removed_tag_expiration_s) <= - int(time.time()))) + # We do this without using a join to prevent holding read locks on the repository table + repo = _get_repository(namespace_name, repository_name) + now = int(time.time()) - (RepositoryTag - .delete() - .where(RepositoryTag.id << to_delete) - .execute()) + (RepositoryTag + .delete() + .where(RepositoryTag.repository == repo, + ~(RepositoryTag.lifetime_end_ts >> None), + (RepositoryTag.lifetime_end_ts + repo.namespace_user.removed_tag_expiration_s) <= now) + .execute()) def garbage_collect_repository(namespace_name, repository_name): @@ -1798,20 +1789,23 @@ def create_or_update_tag(namespace_name, repository_name, tag_name, now_ts = int(time.time()) + created = RepositoryTag.create(repository=repo, image=image, name=tag_name, + lifetime_start_ts=now_ts) + try: # When we move a tag, we really end the timeline of the old one and create a new one query = _tag_alive(RepositoryTag .select() - .where(RepositoryTag.repository == repo, RepositoryTag.name == tag_name)) - tag = db_for_update(query).get() + .where(RepositoryTag.repository == repo, RepositoryTag.name == tag_name, + RepositoryTag.id != created.id)) + tag = query.get() tag.lifetime_end_ts = now_ts tag.save() except RepositoryTag.DoesNotExist: # No tag that needs to be ended pass - return RepositoryTag.create(repository=repo, image=image, name=tag_name, - lifetime_start_ts=now_ts) + return created def delete_tag(namespace_name, repository_name, tag_name):