diff --git a/changedetectionio/api/Watch.py b/changedetectionio/api/Watch.py index b0130537e..2b11ad46b 100644 --- a/changedetectionio/api/Watch.py +++ b/changedetectionio/api/Watch.py @@ -68,13 +68,17 @@ class Watch(Resource): import time from copy import deepcopy watch = None - for _ in range(20): + # Retry up to 20 times if dict is being modified + # With sleep(0), this is fast: ~200µs best case, ~20ms worst case under heavy load + for attempt in range(20): try: watch = deepcopy(self.datastore.data['watching'].get(uuid)) break except RuntimeError: - # Incase dict changed, try again - time.sleep(0.01) + # Dict changed during deepcopy, retry after yielding to scheduler + # sleep(0) releases GIL and yields - no fixed delay, just lets other threads run + if attempt < 19: # Don't yield on last attempt + time.sleep(0) # Yield to scheduler (microseconds, not milliseconds) if not watch: abort(404, message='No watch exists with the UUID of {}'.format(uuid)) diff --git a/changedetectionio/store.py b/changedetectionio/store.py index bed01c500..a3e6039cd 100644 --- a/changedetectionio/store.py +++ b/changedetectionio/store.py @@ -348,7 +348,8 @@ class ChangeDetectionStore: r = requests.request(method="GET", url=url, # So we know to return the JSON instead of the human-friendly "help" page - headers={'App-Guid': self.__data['app_guid']}) + headers={'App-Guid': self.__data['app_guid']}, + timeout=5.0) # 5 second timeout to prevent blocking res = r.json() # List of permissible attributes we accept from the wild internet