Repository: rotki/rotki

Issue:
Unexpected percent finished on tax report
## Problem Definition

I ran a tax report for 1/1/2020 to 18/12/2020 and it seemed to work fine. I then tried running for 1/1/2016 to 31/12/2016 and I saw this screen. The report seems to have worked fine, but the percentage complete seems wrong.

![image](https://user-images.githubusercontent.com/5417918/102668101-18dd8e80-4159-11eb-84e2-409ef57b418f.png)

## Logs

### System Description

Mac OS

Operating system:  Mac
Rotki version: 1.10.1


Discussion:
Ah yes the super complete percentage :)

This is a bug that seems to be surfacing in some situations. Thanks for the issue. The steps you provided may help reproduce, pin down and fix!
I am seeing that one very often but seldom on the first "cold" where the progress seems to be reported ok.
I do see the problem however on subsequent runs.

Resolving patch:
diff --git a/rotkehlchen/accounting/accountant.py b/rotkehlchen/accounting/accountant.py
--- a/rotkehlchen/accounting/accountant.py
+++ b/rotkehlchen/accounting/accountant.py
@@ -64,9 +64,7 @@ def __init__(
 
         self.asset_movement_fees = FVal(0)
         self.last_gas_price = 0
-
-        self.started_processing_timestamp = Timestamp(-1)
-        self.currently_processing_timestamp = Timestamp(-1)
+        self.reset_processing_timestamps()
 
     def __del__(self) -> None:
         del self.events
@@ -100,6 +98,10 @@ def _customize(self, settings: DBSettings) -> None:
         if settings.account_for_assets_movements is not None:
             self.events.account_for_assets_movements = settings.account_for_assets_movements
 
+    def reset_processing_timestamps(self) -> None:
+        self.started_processing_timestamp = Timestamp(-1)
+        self.currently_processing_timestamp = Timestamp(-1)
+
     def get_fee_in_profit_currency(self, trade: Trade) -> Fee:
         """Get the profit_currency rate of the fee of the given trade
 
@@ -422,7 +424,7 @@ def _process_action(
         May raise:
         - PriceQueryUnsupportedAsset if from/to asset is missing from price oracles
         - NoPriceForGivenTimestamp if we can't find a price for the asset in the given
-        timestamp from cryptocompare
+        timestamp from the price oracle
         - RemoteError if there is a problem reaching the price oracle server
         or with reading the response returned by the server
         """
diff --git a/rotkehlchen/externalapis/coingecko.py b/rotkehlchen/externalapis/coingecko.py
--- a/rotkehlchen/externalapis/coingecko.py
+++ b/rotkehlchen/externalapis/coingecko.py
@@ -1,5 +1,6 @@
 import json
 import logging
+import gevent
 from typing import Any, Dict, List, NamedTuple, Optional, Union, overload
 from urllib.parse import urlencode
 
@@ -22,6 +23,7 @@
 log = RotkehlchenLogsAdapter(logger)
 
 PRICE_HISTORY_FILE_PREFIX = 'gecko_price_history_'
+COINGECKO_QUERY_RETRY_TIMES = 4
 
 
 class CoingeckoImageURLs(NamedTuple):
@@ -141,21 +143,50 @@ def _query(
         url = f'https://api.coingecko.com/api/v3/{module}/'
         if subpath:
             url += subpath
-        try:
-            response = self.session.get(f'{url}?{urlencode(options)}')
-        except requests.exceptions.RequestException as e:
-            raise RemoteError(f'Coingecko API request failed due to {str(e)}') from e
+
+        logger.debug(f'Querying coingecko: {url}?{urlencode(options)}')
+        tries = COINGECKO_QUERY_RETRY_TIMES
+        while tries >= 0:
+            try:
+                response = self.session.get(f'{url}?{urlencode(options)}')
+            except requests.exceptions.RequestException as e:
+                raise RemoteError(f'Coingecko API request failed due to {str(e)}') from e
+
+            if response.status_code == 429:
+                # Coingecko allows only 100 calls per minute. If you get 429 it means you
+                # exceeded this and are throttled until the next minute window
+                # backoff and retry 4 times =  2.5 + 3.33 + 5 + 10 = at most 20.8 secs
+                if tries >= 1:
+                    backoff_seconds = 10 / tries
+                    log.debug(
+                        f'Got rate limited by coingecko. '
+                        f'Backing off for {backoff_seconds}',
+                    )
+                    gevent.sleep(backoff_seconds)
+                    tries -= 1
+                    continue
+
+                # else
+                log.debug(
+                    f'Got rate limited by coingecko and did not manage to get a '
+                    f'request through even after {COINGECKO_QUERY_RETRY_TIMES} '
+                    f'incremental backoff retries',
+                )
+
+            break
 
         if response.status_code != 200:
-            raise RemoteError(
+            msg = (
                 f'Coingecko API request {response.url} failed with HTTP status '
-                f'code: {response.status_code}',
+                f'code: {response.status_code}'
             )
+            raise RemoteError(msg)
 
         try:
             decoded_json = rlk_jsonloads(response.text)
         except json.decoder.JSONDecodeError as e:
-            raise RemoteError(f'Invalid JSON in Kraken response. {e}') from e
+            msg = f'Invalid JSON in Coingecko response. {e}'
+            raise RemoteError(msg) from e
 
         return decoded_json
 
diff --git a/rotkehlchen/rotkehlchen.py b/rotkehlchen/rotkehlchen.py
--- a/rotkehlchen/rotkehlchen.py
+++ b/rotkehlchen/rotkehlchen.py
@@ -500,6 +500,7 @@ def process_history(
             start_ts: Timestamp,
             end_ts: Timestamp,
     ) -> Tuple[Dict[str, Any], str]:
+        self.accountant.reset_processing_timestamps()
         (
             error_or_empty,
             history,


Test patch: