Skip to content

Commit

Permalink
Issue/#970 Prevent queue pop timer from becoming negative (#1014)
Browse files Browse the repository at this point in the history
* Extract pop timer tests to their own file

* Add config value QUEUE_POP_TIME_MIN to set lower bound for pop timers
  • Loading branch information
Askaholic authored Apr 21, 2024
1 parent 78561f8 commit 61750e4
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 44 deletions.
2 changes: 2 additions & 0 deletions server/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ def __init__(self):
self.MAP_POOL_RATING_SELECTION = "mean"
# The maximum amount of time in seconds to wait between pops
self.QUEUE_POP_TIME_MAX = 90
# The minimum amount of time in seconds to wait between pops
self.QUEUE_POP_TIME_MIN = 15
# The number of possible matches we would like to have when the queue
# pops. The queue pop time will be adjusted based on the current rate of
# players queuing to try and hit this number.
Expand Down
8 changes: 8 additions & 0 deletions server/matchmaker/pop_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ def time_until_next_pop(self, num_queued: int, time_queued: float) -> float:
next_pop_time, self.queue.name, config.QUEUE_POP_TIME_MAX
)
return config.QUEUE_POP_TIME_MAX

if next_pop_time < config.QUEUE_POP_TIME_MIN:
self._logger.warning(
"Required time (%.2fs) for %s is lower than min pop time (%ds). ",
next_pop_time, self.queue.name, config.QUEUE_POP_TIME_MIN
)
return config.QUEUE_POP_TIME_MIN

return next_pop_time

def cancel(self):
Expand Down
45 changes: 1 addition & 44 deletions tests/unit_tests/test_matchmaker_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from hypothesis import strategies as st

from server.config import config
from server.matchmaker import CombinedSearch, MapPool, PopTimer, Search
from server.matchmaker import CombinedSearch, MapPool, Search
from server.players import PlayerState
from server.rating import RatingType

Expand Down Expand Up @@ -276,49 +276,6 @@ def test_combined_search_attributes(matchmaker_players):
assert search.failed_matching_attempts == 2


def test_queue_time_until_next_pop(queue_factory):
team_size = 2
t1 = PopTimer(queue_factory(team_size=team_size))
t2 = PopTimer(queue_factory(team_size=team_size))

desired_players = config.QUEUE_POP_DESIRED_MATCHES * team_size * 2

assert t1.time_until_next_pop(0, 0) == config.QUEUE_POP_TIME_MAX
# If the desired number of players is not reached within the maximum waiting
# time, then the next round must wait for the maximum allowed time as well.
a1 = t1.time_until_next_pop(
num_queued=desired_players - 1,
time_queued=config.QUEUE_POP_TIME_MAX
)
assert a1 == config.QUEUE_POP_TIME_MAX

# If there are more players than expected, the time should drop
a2 = t1.time_until_next_pop(
num_queued=desired_players * 2,
time_queued=config.QUEUE_POP_TIME_MAX
)
assert a2 < a1

# Make sure that queue moving averages are calculated independently
assert t2.time_until_next_pop(0, 0) == config.QUEUE_POP_TIME_MAX


def test_queue_pop_time_moving_average_size(queue_factory):
t1 = PopTimer(queue_factory())

for _ in range(100):
t1.time_until_next_pop(100, 1)

# The rate should be extremely high, meaning the pop time should be low
assert t1.time_until_next_pop(100, 1) < 1

for _ in range(config.QUEUE_POP_TIME_MOVING_AVG_SIZE):
t1.time_until_next_pop(0, 100)

# The rate should be extremely low, meaning the pop time should be high
assert t1.time_until_next_pop(0, 100) == config.QUEUE_POP_TIME_MAX


@given(rating=st.integers())
def test_queue_map_pools_empty(queue_factory, rating):
queue = queue_factory()
Expand Down
45 changes: 45 additions & 0 deletions tests/unit_tests/test_pop_timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from server.config import config
from server.matchmaker import PopTimer


def test_queue_time_until_next_pop(queue_factory):
team_size = 2
t1 = PopTimer(queue_factory(team_size=team_size))
t2 = PopTimer(queue_factory(team_size=team_size))

desired_players = config.QUEUE_POP_DESIRED_MATCHES * team_size * 2

assert t1.time_until_next_pop(0, 0) == config.QUEUE_POP_TIME_MAX
# If the desired number of players is not reached within the maximum waiting
# time, then the next round must wait for the maximum allowed time as well.
a1 = t1.time_until_next_pop(
num_queued=desired_players - 1,
time_queued=config.QUEUE_POP_TIME_MAX
)
assert a1 == config.QUEUE_POP_TIME_MAX

# If there are more players than expected, the time should drop
a2 = t1.time_until_next_pop(
num_queued=desired_players * 2,
time_queued=config.QUEUE_POP_TIME_MAX
)
assert a2 < a1

# Make sure that queue moving averages are calculated independently
assert t2.time_until_next_pop(0, 0) == config.QUEUE_POP_TIME_MAX


def test_queue_pop_time_moving_average_size(queue_factory):
t1 = PopTimer(queue_factory())

for _ in range(100):
t1.time_until_next_pop(100, 1)

# The rate should be extremely high, meaning the pop time should be low
assert t1.time_until_next_pop(100, 1) == config.QUEUE_POP_TIME_MIN

for _ in range(config.QUEUE_POP_TIME_MOVING_AVG_SIZE):
t1.time_until_next_pop(0, 100)

# The rate should be extremely low, meaning the pop time should be high
assert t1.time_until_next_pop(0, 100) == config.QUEUE_POP_TIME_MAX

0 comments on commit 61750e4

Please sign in to comment.