Skip to content

Commit

Permalink
Checker: Make safety margin for scheduling configurable
Browse files Browse the repository at this point in the history
As a workaround for services with a high variance in check runtimes.

Closes: #79

Co-authored-by: Simon Ruderich <simon@ruderich.org>
  • Loading branch information
F30 and rudis committed Apr 13, 2024
1 parent 98383a0 commit 1b45bd8
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 2 deletions.
15 changes: 15 additions & 0 deletions src/ctf_gameserver/checker/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ def get_service_attributes(db_conn, service_slug, prohibit_changes=False):
}


def get_service_margin(db_conn, service_slug, prohibit_changes=False):
"""
Returns the configured safety margin of a service for a given slug.
"""

with transaction_cursor(db_conn, prohibit_changes) as cursor:
cursor.execute('SELECT margin FROM scoring_service WHERE slug = %s', (service_slug,))
result = cursor.fetchone()

if result is None:
raise DBDataError('Service has not been configured')

return result[0]


def get_current_tick(db_conn, prohibit_changes=False):
"""
Reads the current tick and the "cancel_checks" field from the game database.
Expand Down
3 changes: 2 additions & 1 deletion src/ctf_gameserver/checker/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def main():
try:
service_id = database.get_service_attributes(db_conn, args.service,
prohibit_changes=True)['id']
database.get_service_margin(db_conn, args.service, prohibit_changes=True)
except DBDataError as e:
logging.warning('Invalid database state: %s', e)
service_id = 1337 # Use dummy value for subsequent grant checks
Expand Down Expand Up @@ -392,7 +393,7 @@ def update_launch_params(self, tick):
total_tasks = database.get_task_count(self.db_conn, self.service['id'])
local_tasks = math.ceil(total_tasks / self.checker_count)

margin_seconds = self.tick_duration.total_seconds() / 6
margin_seconds = database.get_service_margin(self.db_conn, self.service['slug'])
launch_timeframe = max(self.tick_duration.total_seconds() - check_duration - margin_seconds, 0)

intervals_per_timeframe = math.floor(launch_timeframe / self.interval) + 1
Expand Down
4 changes: 4 additions & 0 deletions src/ctf_gameserver/web/scoring/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class Service(models.Model):

name = models.CharField(max_length=30, unique=True)
slug = models.SlugField(max_length=30, unique=True, help_text=_('Simplified name for use in paths'))
margin = models.PositiveIntegerField(default=30,
help_text=_('Safety margin (in seconds) for checker scheduling, '
'gets added to the automatically determined check '
'duration'))

def __str__(self): # pylint: disable=invalid-str-returned
return self.name
Expand Down
2 changes: 1 addition & 1 deletion tests/checker/test_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,4 @@ def test_update_launch_params(self, check_duration_mock):
self.master_loop.interval = 10
self.master_loop.tick_duration = datetime.timedelta(seconds=90)
self.master_loop.update_launch_params(10)
self.assertEqual(self.master_loop.tasks_per_launch, 7)
self.assertEqual(self.master_loop.tasks_per_launch, 9)

0 comments on commit 1b45bd8

Please sign in to comment.