Repository: rotki/rotki

Issue:
Unhandled exception at shutdown for premium users
## Problem Definition

This is not really something bad or a priority but it seems that sometimes something is not handled properly at shutdown (it does not always happen). It's not a user facing error since the app is already shutting down. The cleanup is just not happening properly

```
2020-12-10T10:54:41.181Z: Exiting the application
2020-12-10T10:54:43.600Z: Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 818, in gevent._greenlet.Greenlet.run
  File "/home/lefteris/w/rotkehlchen/rotkehlchen/rotkehlchen.py", line 362, in main_loop
    self.premium_sync_manager.maybe_upload_data_to_server()
  File "/home/lefteris/w/rotkehlchen/rotkehlchen/premium/sync.py", line 162, in maybe_upload_data_to_server
    b64_encoded_data, our_hash = self.data.compress_and_encrypt_db(self.password)
  File "/home/lefteris/w/rotkehlchen/rotkehlchen/data_handler.py", line 195, in compress_and_encrypt_db
    self.db.export_unencrypted(tempdb)
AttributeError: 'DataHandler' object has no attribute 'db'
2020-12-10T10:54:41Z <Greenlet at 0x7f2166ba6ef0: <bound method Rotkehlchen.main_loop of <rotkehlchen.rotkehlchen.Rotkehlchen object at 0x7f2166a71f10>>> failed with AttributeError
```

## Task

Figure out why and fix it


Discussion:
This should actually not happen again anymore. Since the organization of the background tasks and the task manager the premium sync is one task in there. And all of them would be killed before the user logs out so this should not be seen anymore.
> This should actually not happen again anymore. Since the organization of the background tasks and the task manager the premium sync is one task in there. And all of them would be killed before the user logs out so this should not be seen anymore.

Narrator: It happened again.

Resolving patch:
diff --git a/rotkehlchen/rotkehlchen.py b/rotkehlchen/rotkehlchen.py
--- a/rotkehlchen/rotkehlchen.py
+++ b/rotkehlchen/rotkehlchen.py
@@ -317,7 +317,7 @@ def unlock_user(
         self.user_is_logged_in = True
         log.debug('User unlocking complete')
 
-    def logout(self) -> None:
+    def _logout(self) -> None:
         if not self.user_is_logged_in:
             return
         user = self.data.username
@@ -350,6 +350,13 @@ def logout(self) -> None:
             user=user,
         )
 
+    def logout(self) -> None:
+        if self.task_manager is None:  # no user logged in?
+            return
+
+        with self.task_manager.schedule_lock:
+            self._logout()
+
     def set_premium_credentials(self, credentials: PremiumCredentials) -> None:
         """
         Sets the premium credentials for rotki
diff --git a/rotkehlchen/tasks/manager.py b/rotkehlchen/tasks/manager.py
--- a/rotkehlchen/tasks/manager.py
+++ b/rotkehlchen/tasks/manager.py
@@ -87,6 +87,7 @@ def __init__(
             self._maybe_schedule_exchange_history_query,
             self._maybe_schedule_ethereum_txreceipts,
         ]
+        self.schedule_lock = gevent.lock.Semaphore()
 
     def _prepare_cryptocompare_queries(self) -> None:
         """Prepare the queries to do to cryptocompare
@@ -278,7 +279,7 @@ def _maybe_schedule_exchange_history_query(self) -> None:
         )
         self.last_exchange_query_ts[exchange.location_id()] = now
 
-    def schedule(self) -> None:
+    def _schedule(self) -> None:
         """Schedules background tasks"""
         self.greenlet_manager.clear_finished()
         current_greenlets = len(self.greenlet_manager.greenlets) + len(self.api_task_greenlets)
@@ -298,3 +299,12 @@ def schedule(self) -> None:
 
         for callable_fn in callables:
             callable_fn()  # type: ignore
+
+    def schedule(self) -> None:
+        """Schedules background task while holding the scheduling lock
+
+        Used during logout to make sure no task is being scheduled at the same time
+        as logging out
+        """
+        with self.schedule_lock:
+            self._schedule()


Test patch: