Skip to content

Commit

Permalink
ProxyService can send different contests to different RWSs.
Browse files Browse the repository at this point in the history
  • Loading branch information
vytisb committed Feb 20, 2024
1 parent 12b4259 commit b413b18
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 12 deletions.
2 changes: 2 additions & 0 deletions cms/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# Copyright © 2010-2012 Matteo Boscariol <[email protected]>
# Copyright © 2013 Luca Wehrstedt <[email protected]>
# Copyright © 2014 Fabian Gundlach <[email protected]>
# Copyright © 2014 Vytis Banaitis <[email protected]>
# Copyright © 2016 Myungwoo Chun <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -152,6 +153,7 @@ def __init__(self):
# ProxyService.
self.rankings = ["http://usern4me:passw0rd@localhost:8890/"]
self.https_certfile = None
self.ranking_contests = []

# PrintingService
self.max_print_length = 10_000_000 # 10 MB
Expand Down
70 changes: 58 additions & 12 deletions cms/service/ProxyService.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,29 @@ def safe_url(url):
return parts._replace(netloc=netloc).geturl()


def get_ranking_contests(index):
"""Get the set of contest ids to send to the RWS specified by index.
index (int): The index of the RWS as specified in the config.
return ({int}|None): The set of contest ids, or None if the RWS should
handle all active contests.
"""
if index >= len(config.ranking_contests):
return None
contests = config.ranking_contests[index]
if isinstance(contests, (list, tuple, set)):
return set(contests)
else:
return {contests}


class ProxyOperation(QueueItem):
def __init__(self, type_, data):
def __init__(self, type_, data, contest_id=None):
self.type_ = type_
self.data = data
self.contest_id = contest_id

def __str__(self):
return "sending data of type %s to ranking" % (
Expand Down Expand Up @@ -169,7 +188,7 @@ class ProxyExecutor(Executor):
# before trying again.
FAILURE_WAIT = 60.0

def __init__(self, ranking):
def __init__(self, ranking, contests):
"""Create a proxy for the ranking at the given URL.
ranking (bytes): a complete URL (containing protocol, username,
Expand All @@ -181,6 +200,24 @@ def __init__(self, ranking):

self._ranking = ranking
self._visible_ranking = safe_url(ranking)
self.contests = contests

def can_handle_contest(self, contest_id):
"""Determine whether data about contest_id should be sent to this RWS.
contest_id (int): id of the contest
return (bool): True if this ranking handles contest_id.
"""
if self.contests is None:
return True
return contest_id in self.contests

def enqueue(self, item, priority=None, timestamp=None):
if item.contest_id is not None and \
not self.can_handle_contest(item.contest_id):
return False
return super().enqueue(item, priority, timestamp)

def execute(self, entries):
"""Consume (i.e. send) the data put in the queue, forever.
Expand Down Expand Up @@ -270,8 +307,9 @@ def __init__(self, shard):

# Create one executor for each ranking.
self.rankings = list()
for ranking in config.rankings:
self.add_executor(ProxyExecutor(ranking))
for i, ranking in enumerate(config.rankings):
contests = get_ranking_contests(i)
self.add_executor(ProxyExecutor(ranking, contests))

# Enqueue the dispatch of some initial data to rankings. Needs
# to be done before the sweeper is started, as otherwise RWS
Expand Down Expand Up @@ -368,10 +406,14 @@ def initialize(self):
}

self.enqueue(ProxyOperation(ProxyExecutor.CONTEST_TYPE,
{contest_id: contest_data}))
self.enqueue(ProxyOperation(ProxyExecutor.TEAM_TYPE, teams))
self.enqueue(ProxyOperation(ProxyExecutor.USER_TYPE, users))
self.enqueue(ProxyOperation(ProxyExecutor.TASK_TYPE, tasks))
{contest_id: contest_data},
contest.id))
self.enqueue(ProxyOperation(ProxyExecutor.TEAM_TYPE, teams,
contest.id))
self.enqueue(ProxyOperation(ProxyExecutor.USER_TYPE, users,
contest.id))
self.enqueue(ProxyOperation(ProxyExecutor.TASK_TYPE, tasks,
contest.id))

def operations_for_score(self, submission):
"""Send the score for the given submission to all rankings.
Expand Down Expand Up @@ -405,9 +447,11 @@ def operations_for_score(self, submission):

return [
ProxyOperation(ProxyExecutor.SUBMISSION_TYPE,
{submission_id: submission_data}),
{submission_id: submission_data},
submission.task.contest_id),
ProxyOperation(ProxyExecutor.SUBCHANGE_TYPE,
{subchange_id: subchange_data})]
{subchange_id: subchange_data},
submission.task.contest_id)]

def operations_for_token(self, submission):
"""Send the token for the given submission to all rankings.
Expand All @@ -434,9 +478,11 @@ def operations_for_token(self, submission):

return [
ProxyOperation(ProxyExecutor.SUBMISSION_TYPE,
{submission_id: submission_data}),
{submission_id: submission_data},
submission.task.contest_id),
ProxyOperation(ProxyExecutor.SUBCHANGE_TYPE,
{subchange_id: subchange_data})]
{subchange_id: subchange_data},
submission.task.contest_id)]

@rpc_method
def reinitialize(self):
Expand Down
5 changes: 5 additions & 0 deletions config/cms.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@
"rankings": ["http://usern4me:passw0rd@localhost:8890/"],
"https_certfile": null,

"_help": "For each RWS specify which contests should be sent to it.",
"_help": "Each entry can be an int or a list or set of ints.",
"_help": "If not specified, all contests will be sent to the RWS.",
"ranking_contests": [],



"_section": "PrintingService",
Expand Down

0 comments on commit b413b18

Please sign in to comment.