Skip to content

Commit

Permalink
Limit deadline split between attempts by a factor (#286)
Browse files Browse the repository at this point in the history
* Limit deadline split between attempts by a factor

* Add to CHANGELOG.md

* Fix

* Fix review comments

* Add more docs
  • Loading branch information
Pliner authored Jan 8, 2025
1 parent 4129ed2 commit bb69b16
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [Do not retry low timeout response](https://github.com/anna-money/aio-request/pull/276)
* Refactoring around request enrichers and deprecation of setup_v2. Related PRs: [#277](https://github.com/anna-money/aio-request/pull/277), [#282](https://github.com/anna-money/aio-request/pull/282), [#285](https://github.com/anna-money/aio-request/pull/285)
* [Deadline provider for sequential strategy](https://github.com/anna-money/aio-request/pull/284)
* [Limit deadline split between attempts by a factor](https://github.com/anna-money/aio-request/pull/286)


## v0.1.34 (2024-11-05)
Expand Down
20 changes: 13 additions & 7 deletions aio_request/deadline_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
DeadlineProvider = Callable[[Deadline, int, int], Deadline]


def split_deadline_between_attempts() -> DeadlineProvider:
def split_deadline_between_attempts(*, attempts_count_to_split: int | None = None) -> DeadlineProvider:
"""
Split deadline between attempts.
Expand All @@ -16,17 +16,23 @@ def split_deadline_between_attempts() -> DeadlineProvider:
2. 1 sec -> 1 sec -> 8 sec. Two attempts have spent 1 seconds each,
the last one has received the remaining 8 seconds due to redistribution.
If attempts_count_to_split is not None, then the deadline will be split between the first attempts_count_to_split.
"""

if attempts_count_to_split is not None and attempts_count_to_split < 2:
raise ValueError("attempts_count_to_split should be greater or equal to 2")

def __provider(deadline: Deadline, attempt: int, attempts_count: int) -> Deadline:
if deadline.expired:
return deadline

attempts_left = attempts_count - attempt
if attempts_left == 0:
raise ValueError("no attempts left")

return deadline / attempts_left
if attempts_count_to_split is None:
effective_attempts_left = attempts_count - attempt
else:
effective_attempts_left = min(attempts_count_to_split, attempts_count) - attempt
if effective_attempts_left <= 1:
return deadline
return deadline / effective_attempts_left

return __provider

Expand Down
40 changes: 38 additions & 2 deletions tests/test_deadline_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,29 @@ async def test_split_deadline_between_attempt():
attempt_deadline = provider(deadline, 0, 3)
assert 0.3 <= attempt_deadline.timeout <= 0.34

await asyncio.sleep(attempt_deadline.timeout)
await asyncio.sleep(0.33)

attempt_deadline = provider(deadline, 1, 3)
assert 0.3 <= attempt_deadline.timeout <= 0.34
await asyncio.sleep(attempt_deadline.timeout)
await asyncio.sleep(0.33)

attempt_deadline = provider(deadline, 2, 3)
assert 0.3 <= attempt_deadline.timeout <= 0.34


async def test_split_deadline_between_attempt_with_split_factor():
provider = aio_request.split_deadline_between_attempts(attempts_count_to_split=2)
deadline = aio_request.Deadline.from_timeout(1)

attempt_deadline = provider(deadline, 0, 3)
assert 0.45 <= attempt_deadline.timeout <= 0.5

await asyncio.sleep(0.33)

attempt_deadline = provider(deadline, 1, 3)
assert 0.6 <= attempt_deadline.timeout <= 0.67

await asyncio.sleep(0.33)

attempt_deadline = provider(deadline, 2, 3)
assert 0.3 <= attempt_deadline.timeout <= 0.34
Expand All @@ -35,3 +53,21 @@ async def test_split_deadline_between_attempts_fast_attempt_failure():

attempt_deadline = provider(deadline, 2, 3)
assert 0.75 <= attempt_deadline.timeout <= 0.8


async def test_split_deadline_between_attempts_fast_attempt_failure_with_split_factor():
provider = aio_request.split_deadline_between_attempts(attempts_count_to_split=2)
deadline = aio_request.Deadline.from_timeout(1)

attempt_deadline = provider(deadline, 0, 3)
assert 0.45 <= attempt_deadline.timeout <= 0.5

await asyncio.sleep(0.1) # fast attempt failure

attempt_deadline = provider(deadline, 1, 3)
assert 0.85 <= attempt_deadline.timeout <= 0.9

await asyncio.sleep(0.1) # fast attempt failure

attempt_deadline = provider(deadline, 2, 3)
assert 0.75 <= attempt_deadline.timeout <= 0.8

0 comments on commit bb69b16

Please sign in to comment.