Skip to content

Architectural Decisions

Deshraj Yadav edited this page Jul 7, 2017 · 1 revision

This is a collection of records for architecturally significant decisions.

URL Patterns

We follow a very basic yet strong convention for URL so that our rest APIs are properly namespaced. First of all, we rely heavily on HTTP verbs to perform CURD actions.

For example, to perform CURD operation on Challenge Host Model, following will be the URL patterns.

  • GET /hosts/challenge_host_team - Retrieves a list of challenge host teams

  • POST /hosts/challenge_host_team - Creates a new challenge host team

  • GET /hosts/challenge_host_team/<challenge_host_team_id> - Retrieves a specific challenge host team

  • PUT /hosts/challenge_host_team/<challenge_host_team_id> - Updates a specific challenge host team

  • PATCH /hosts/challenge_host_team/<challenge_host_team_id> - Partially updates a specific challenge host team

  • DELETE /hosts/challenge_host_team/<challenge_host_team_id> - Deletes a specific challenge host team

Also, we have namespaced the URL patterns on an app basis, so URLs for Challenge Host Model which is in hosts app will be

/hosts/challenge_host_team

This way one can easily identify where a particular API is located.

We use underscore _ in URL patterns.

Processing submission messages asynchronously

When a submission message is made, a REST API is called which saves the data related to submission in the database. A submission involves the processing and evaluation of input_file. This file is used to evaluate the submission and the decide the status of the submission whether it is FINISHED or FAILED.

One way to process the submission was to evaluate it as soon as it is made and hence blocking the request of the participant. Blocking the request here means to send the response to the participant only when the submission has been and its output is known. This would have worked fine if the number of the submission made is very low, but this is not the case.

Hence we decided to process and evaluate submission message in an asynchronous manner. To process the message in this way, we need to change our architecture a bit and add a Message Framework, along with a worker so that it can process the message.

Out of all the awesome messaging framework available, we chose RabbitMQ, because of its transactional nature and reliability. Also, RabbitMQ is easily horizontally scalable, which means we can easily handle the heavy load by simply adding more nodes to the cluster.

For the worker, we went ahead with a normal python worker, which simply runs a process and loads all the required data in its memory. As soon as the worker starts, it listens on a RabbitMQ queue named submission_task_queue for new submission messages.

Submission Worker

Submission worker is responsible for processing submission messages. It listens on a queue named submission_task_queue and on receiving a message for a submission it processes and evaluates the submission.

One of the major design changes that we decided to implement in submission worker was to load all the data related to challenge in the memory of the worker instead of fetching it every time whenever a submission message is there for any challenge. So the worker, when starting, fetches the list of active challenges from the database and then loads it into memory by maintaining a map EVALUATION_SCRIPTS on challenge id. This was actually a major performance improvement.

Another major design that we incorporated here was dynamically importing the challenge module and loading it in the map instead of invoking a new python process every time a submission message arrives. So now whenever a new message for a submission is received, we already have its corresponding challenge module being loaded in a map EVALUATION_SCRIPTS, and we just need to call

EVALUATION_SCRIPTS[challenge_id].evaluate(*params)

This was again a major performance improvement, wherein we saved us from the task of invoking and managing Python processes to evaluate submission messages. Also invoking a new python process every time for a new submission would have been really slow

Leaderboard