Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions server/matchmaker/matchmaker_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ def initialize(self):
def num_players(self) -> int:
return sum(len(search.players) for search in self._queue.keys())

@property
def num_new_queued_players(self) -> int:
""" Get the number of players who have joined the queue and not yet had a failed matching attempt in the last pop.

Returns:
int: Number of players who have joined the queue since last matching attempt.
"""
return sum(len(search.players) for search in self._queue.keys() if search.failed_matching_attempts == 0)

async def queue_pop_timer(self) -> None:
""" Periodically tries to match all Searches in the queue. The amount
of time until next queue 'pop' is determined by the number of players
Expand Down
10 changes: 7 additions & 3 deletions server/matchmaker/pop_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ async def next_pop(self):
num_players = self.queue.num_players
metrics.matchmaker_players.labels(self.queue.name).set(num_players)

num_new_players = self.queue.num_new_queued_players
metrics.matchmaker_new_players.labels(self.queue.name).set(num_new_players)

self._last_queue_pop = time()
self.next_queue_pop = self._last_queue_pop + self.time_until_next_pop(
num_players, time_remaining
num_new_players, time_remaining
)

def time_until_next_pop(self, num_queued: int, time_queued: float) -> float:
Expand All @@ -82,8 +85,9 @@ def time_until_next_pop(self, num_queued: int, time_queued: float) -> float:

players_per_match = self.queue.team_size * 2
desired_players = config.QUEUE_POP_DESIRED_MATCHES * players_per_match
# Obtained by solving $ NUM_PLAYERS = rate * time $ for time.
next_pop_time = desired_players * total_times / total_players
player_addition_rate = total_players / total_times
# Obtained by solving $ desired_players = player_addition_rate * time $ for time.
next_pop_time = desired_players / player_addition_rate
if next_pop_time > config.QUEUE_POP_TIME_MAX:
self._logger.info(
"Required time (%.2fs) for %s is larger than max pop time (%ds). "
Expand Down
4 changes: 4 additions & 0 deletions server/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class MatchLaunch:
"server_matchmaker_queue_players", "Players in the queue at pop time", ["queue"]
)

matchmaker_new_players = Gauge(
"server_matchmaker_queue_new_players", "Number of newly added players to the queue at pop time", ["queue"]
)

matchmaker_queue_pop = Gauge(
"server_matchmaker_queue_pop_timer_seconds",
"Queue pop timer duration in seconds",
Expand Down
13 changes: 13 additions & 0 deletions tests/unit_tests/test_pop_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,16 @@ def test_queue_pop_time_moving_average_size(queue_factory):

# 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


def test_queue_pop_time_moving_average_size_stability(queue_factory):
# Test perfect number of players joining has a stable pop time
team_size = 2
desired_players = team_size * config.QUEUE_POP_DESIRED_MATCHES
t1 = PopTimer(queue_factory(team_size=2))
initial_time_to_pop = (config.QUEUE_POP_TIME_MAX + config.QUEUE_POP_TIME_MIN) / 2 # Arbitrary time between min and max
next_time_to_pop = initial_time_to_pop
for _ in range(100):
next_time_to_pop = t1.time_until_next_pop(desired_players, next_time_to_pop)

assert t1.time_until_next_pop(desired_players, next_time_to_pop) == next_time_to_pop # Perfect size should not change the next time
Loading