Skip to content

Commit

Permalink
Fixed a problem when throttling calls
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianWilhelm committed Dec 13, 2023
1 parent 698dee1 commit 46a4d29
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 18 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## Version 0.6 (2023-04-10)
## Version 0.6.1 (2023-12-10)

- Fixed a deadlock problem in `utils.throttle`

## Version 0.6 (2023-12-04)

- Migrate to Pydantic v2
- Require mininum Python version of 3.10
Expand Down
2 changes: 1 addition & 1 deletion src/pytanis/helpdesk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self, config: Config | None = None):

self._get_throttled = self._get
self._post_throttled = self._post
self.set_throttling(2, 1) # we are nice by default
self.set_throttling(1, 1) # we are nice by default

def set_throttling(self, calls: int, seconds: int):
"""Throttle the number of calls per seconds to the Pretalx API"""
Expand Down
1 change: 1 addition & 0 deletions src/pytanis/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Col(PretalxCol):
address_as = 'Address as'
track_prefs = 'Track Preferences'
committee_contact = 'Committee Contact'
committee_member = 'Committee Member'
all_proposals = 'All Proposals'

pretalx_activated = 'Pretalx activated'
Expand Down
33 changes: 17 additions & 16 deletions src/pytanis/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Additional utilities"""

import functools
import threading
import time
from collections.abc import Callable
from math import fabs
from typing import Any, TypeVar

import pandas as pd
Expand Down Expand Up @@ -66,24 +66,25 @@ def throttle(calls: int, seconds: int = 1) -> Callable[[Callable[..., RT]], Call
def decorator(func: Callable[..., RT]) -> Callable[..., RT]:
# keeps track of the last calls
last_calls: list[float] = []
lock = threading.Lock()

@functools.wraps(func)
def wrapper(*args, **kwargs) -> RT:
curr_time = time.time()
if last_calls:
# remove calls from last_calls list older than interval in seconds
idx_old_calls = [i for i, t in enumerate(last_calls) if t < curr_time - seconds]
if idx_old_calls:
del last_calls[: idx_old_calls[-1]]
if len(last_calls) >= calls:
idx = len(last_calls) - calls
delta = fabs(1 - curr_time + last_calls[idx])
logger = get_logger()
logger.debug('stalling call', func=func.__name__, secs=delta)
time.sleep(delta)
resp = func(*args, **kwargs)
last_calls.append(time.time())
return resp
nonlocal last_calls
with lock:
curr_time = time.time()
# Remove old calls
last_calls = [call for call in last_calls if call > curr_time - seconds]

if len(last_calls) >= calls:
sleep_time = last_calls[0] + seconds - curr_time
logger = get_logger()
logger.debug('stalling call', func=func.__name__, secs=sleep_time)
time.sleep(sleep_time)

resp = func(*args, **kwargs)
last_calls.append(time.time())
return resp

return wrapper

Expand Down

0 comments on commit 46a4d29

Please sign in to comment.