Repository: learningequality/kolibri

Issue:
Delete channel or resources task fails when attempting to release an expired Redis lock

### Observed behavior
Both `Delete channel` and `Manage > Delete (all) resources` for that same channel are failing. 
Asset from `v0.14.4-weekly7` build on Windows 7.
The only content present was **KA (EN) > Math > Arithmetic**, and I was not able to delete neither the full channel, nor by selecting all the resources inside, as the task was reported as `Failed`. I suspect just the DB was deleted but not the resources.

### Expected behavior
Being able to delete channel/resources.

### User-facing consequences
Inability to manage channels and resources on the device.

### Errors and logs

Nothing jumped at me as relevant in the browser console, attaching the logs.

[logs.zip](https://github.com/learningequality/kolibri/files/5497727/logs.zip)


### Steps to reproduce
1. Download **KA (EN) > Math > Arithmetic**.
2. Try to delete the channel and/or all the resources.

### Context

Tell us about your environment, including:
 * Kolibri version: v0.14.4-weekly7
 * Operating system: Windows 7
 * Browser: Firefox




Discussion:
Seems to be an issue here:
```
  File "c:\python36\lib\site-packages\kolibri\core\tasks\worker.py", line 72, in handle_finished_future
    result = future.result()
  File "c:\python36\lib\concurrent\futures\_base.py", line 425, in result
    return self.__get_result()
  File "c:\python36\lib\concurrent\futures\_base.py", line 384, in __get_result
    raise self._exception
  File "c:\python36\lib\concurrent\futures\thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "c:\python36\lib\site-packages\kolibri\core\tasks\worker.py", line 217, in wrap
    return f(*args, **kwargs)
  File "c:\python36\lib\site-packages\kolibri\core\tasks\job.py", line 194, in y
    result = func(*args, **kwargs)
  File "c:\python36\lib\site-packages\kolibri\dist\django\core\management\__init__.py", line 131, in call_command
    return command.execute(*args, **defaults)
  File "c:\python36\lib\site-packages\kolibri\dist\django\core\management\base.py", line 330, in execute
    output = self.handle(*args, **options)
  File "c:\python36\lib\site-packages\kolibri\core\tasks\management\commands\base.py", line 119, in handle
    return self.handle_async(*args, **options)
  File "c:\python36\lib\site-packages\kolibri\core\content\management\commands\deletecontent.py", line 114, in handle_async
    channel, node_ids, exclude_node_ids, force_delete
  File "c:\python36\lib\site-packages\kolibri\core\content\management\commands\deletecontent.py", line 45, in delete_metadata
    channel.delete_content_tree_and_files()
  File "c:\python36\lib\site-packages\kolibri\core\utils\cache.py", line 70, in __exit__
    self.release()
  File "c:\python36\lib\site-packages\kolibri\core\utils\cache.py", line 64, in release
    self._lock.release()
  File "c:\python36\lib\site-packages\kolibri\dist\diskcache\recipes.py", line 165, in release
    assert is_owned, 'cannot release un-acquired lock'
AssertionError: cannot release un-acquired lock
```

This is called after the channel metadata has been removed from the DB, but before the content is cleared up, so it's consistent with the reports.
FYI, in a RPI, with kolibri-server and this setup for redis:
```
CACHE_BACKEND = redis
CACHE_REDIS_MAXMEMORY = 536870912
CACHE_REDIS_MAXMEMORY_POLICY = allkeys-lru
```
deleting the same channel @radinamatic  comments has worked, but... it took 8 minutes

Resolving patch:
diff --git a/kolibri/core/utils/cache.py b/kolibri/core/utils/cache.py
--- a/kolibri/core/utils/cache.py
+++ b/kolibri/core/utils/cache.py
@@ -61,7 +61,12 @@ def acquire(self):
         self._lock.acquire()
 
     def release(self):
-        self._lock.release()
+        try:
+            self._lock.release()
+        except AssertionError:
+            logger.warning(
+                "Got an AssertionError when releasing a lock! This is likely from the lock TTL expiring."
+            )
 
     def __enter__(self):
         self.acquire()


Test patch: