Repository: readthedocs/readthedocs.org

Issue:
Clean deletion of a Version
When a Version is deleted, we are triggering `clear_artifacts` _and_ `symlink_project` but the later is triggered _before_ the object deletion; so, I think if the task is ran before it's effectively deleted the symlink won't be properly synced:

https://github.com/rtfd/readthedocs.org/blob/5472906a067adc60f42556108993255393408410/readthedocs/builds/models.py#L181-L187

To fix this we could chain some Celery task in this order:

1. trigger `clear_artifacts`
1. delete the Version object
1. trigger `symlink_projects`

Take into account that the first and last have to be executed in a broadcast call but the second one should be executed just once since it affects the database.

> This method `Version.delete` is not used in the codebase at all, so I'm not sure if this is a priority. I found that all the Versions are activated/deactivated but not deleted.

Ref: https://github.com/rtfd/readthedocs.org/pull/3649#issuecomment-370915276


Discussion:
We can use Celery task chaining to accomplish this probably.
> This method Version.delete is not used in the codebase at all, so I'm not sure if this is a priority. I found that all the Versions are activated/deactivated but not deleted.

Actually, we delete the versions when syncing the versions

https://github.com/rtfd/readthedocs.org/blob/4fbe0afde6bd0fc9fc14baee79dd361d68f714cb/readthedocs/restapi/utils.py#L134-L154

https://github.com/rtfd/readthedocs.org/blob/4fbe0afde6bd0fc9fc14baee79dd361d68f714cb/readthedocs/restapi/views/model_views.py#L196-L196

I'm not sure about chaining the tasks. What I understand, is that we access to the versions from the `symlink_project` task, which is in a race condition with the deletion of the current version.

So at least that `clear_artifacts` is in a race condition with `symlink_projects`, we can just change the call order and that would be enough.
> which is in a race condition with the deletion of the current version.

What would the race condition here be? If we remove a version and try to symlink, does the symlink stay?
`symlink_project` access to all versions and a version is being deleted at the same time. So, probably the symlink would stay

Resolving patch:
diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py
--- a/readthedocs/builds/models.py
+++ b/readthedocs/builds/models.py
@@ -213,10 +213,18 @@ def save(self, *args, **kwargs):  # pylint: disable=arguments-differ
     def delete(self, *args, **kwargs):  # pylint: disable=arguments-differ
         from readthedocs.projects import tasks
         log.info('Removing files for version %s', self.slug)
-        broadcast(type='app', task=tasks.clear_artifacts, args=[self.get_artifact_paths()])
         broadcast(
-            type='app', task=tasks.symlink_project, args=[self.project.pk])
+            type='app',
+            task=tasks.clear_artifacts,
+            args=[self.get_artifact_paths()],
+        )
+        project_pk = self.project.pk
         super(Version, self).delete(*args, **kwargs)
+        broadcast(
+            type='app',
+            task=tasks.symlink_project,
+            args=[project_pk],
+        )
 
     @property
     def identifier_friendly(self):


Test patch: