|
4 | 4 |
|
5 | 5 | import redis |
6 | 6 | import structlog |
| 7 | +from django.apps import apps |
7 | 8 | from django.conf import settings |
| 9 | +from django.contrib.auth.models import User |
8 | 10 | from django.core.mail import EmailMultiAlternatives |
9 | 11 |
|
| 12 | +from readthedocs.builds.utils import memcache_lock |
| 13 | +from readthedocs.core.history import set_change_reason |
10 | 14 | from readthedocs.worker import app |
11 | 15 |
|
12 | 16 |
|
@@ -71,3 +75,38 @@ def cleanup_pidbox_keys(): |
71 | 75 | client.delete(key) |
72 | 76 |
|
73 | 77 | log.info("Redis pidbox objects.", memory=total_memory, keys=len(keys)) |
| 78 | + |
| 79 | + |
| 80 | +@app.task(queue="web", bind=True) |
| 81 | +def delete_object(self, model_name: str, pk: int, user_id: int | None = None): |
| 82 | + """ |
| 83 | + Delete an object from the database asynchronously. |
| 84 | +
|
| 85 | + This is useful for deleting large objects that may take time |
| 86 | + to delete, without timing out the request. |
| 87 | +
|
| 88 | + :param model_name: The model name in the format 'app_label.ModelName'. |
| 89 | + :param pk: The primary key of the object to delete. |
| 90 | + :param user_id: The ID of the user performing the deletion. |
| 91 | + Just for logging purposes. |
| 92 | + """ |
| 93 | + task_log = log.bind(model_name=model_name, object_pk=pk, user_id=user_id) |
| 94 | + lock_id = f"{self.name}-{model_name}-{pk}-lock" |
| 95 | + lock_expire = 60 * 60 * 2 # 2 hours |
| 96 | + with memcache_lock( |
| 97 | + lock_id=lock_id, lock_expire=lock_expire, app_identifier=self.app.oid |
| 98 | + ) as acquired: |
| 99 | + if not acquired: |
| 100 | + task_log.info("Object is already being deleted.") |
| 101 | + return |
| 102 | + |
| 103 | + user = User.objects.filter(pk=user_id).first() if user_id else None |
| 104 | + Model = apps.get_model(model_name) |
| 105 | + obj = Model.objects.filter(pk=pk).first() |
| 106 | + if obj: |
| 107 | + task_log.info("Deleting object.") |
| 108 | + set_change_reason(obj, reason="Object deleted asynchronously", user=user) |
| 109 | + obj.delete() |
| 110 | + task_log.info("Object deleted.") |
| 111 | + else: |
| 112 | + task_log.info("Object does not exist.") |
0 commit comments