|
1 | 1 | # Execution Service
|
2 | 2 |
|
| 3 | +The Execution Service provides backend functionality for running and validating code executions or submissions within a coding platform. It enables users to execute code against test cases and receive feedback on the correctness of their solutions. |
| 4 | + |
| 5 | +The Execution Service incorporates a code execution mechanism designed to run user-submitted solutions within an isolated, sandboxed environment. This approach enhances security by preventing arbitrary code from interacting with the host system directly and allows for performance monitoring |
| 6 | + |
| 7 | +### Technology Stack |
| 8 | + |
| 9 | +- Golang (Go): Statically typed, compiled language with low latency. Fast and efficient processing is ideal for high-read, high-write environments like in Execution Service, when many users run tests or submit tests. |
| 10 | +- Rest Server: chi router was utilized which supports CORS, logging and timeout via middlewares. It is stateless, which reduces coupling and enhances scalability and reliability, simplicity and flexibility. For example, clients may make requests to different server instances when scaled. |
| 11 | +- Firebase Firestore: NoSQL Document database that is designed for automatic horizontal scaling and schema-less design that allows for flexibility as number of tests increases or more users run tests. |
| 12 | +- Docker: used to containerize the Execution Service to simplify deployment. Additionally used to provide a sandboxed execution environment for user-submitted code, ensuring security by limiting code access to the host system and managing dependencies independently. |
| 13 | + |
| 14 | +### Execution Process |
| 15 | + |
| 16 | +For execution of user code (running of test cases without submission), only visible (public) and custom test cases are executed. |
| 17 | + |
| 18 | + |
| 19 | + |
| 20 | +### Submission Process |
| 21 | + |
| 22 | +For submission of user code, both visible (public) and hidden testcases are executed, before calling the history-service API to submit the submission data, code and test results. |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +### Design Decisions |
| 27 | + |
| 28 | +1. **Docker Containerisation** |
| 29 | + a. Upon receiving a code execution request, the service dynamically creates a Docker container with a controlled environment tailored to Python |
| 30 | + b. The Docker container is set up with only the minimal permissions and resources needed to execute the code, restricting the execution environment to reduce risk |
| 31 | + c. This containerized environment is automatically destroyed after execution, ensuring no residual data or state remains between executions |
| 32 | + |
| 33 | +2. **Security and Isolation** |
| 34 | + a. Containers provide isolation from the host system, limiting any interaction between user code and the underlying infrastructure |
| 35 | + b. Only essential files and libraries required for code execution are included, reducing potential attack surfaces within each container. The sandboxed, container-based execution system provides a secure and efficient way to run user code submissions. |
| 36 | + |
| 37 | +The sandboxed, container-based execution system provides a secure and efficient way to run user code submissions. |
| 38 | + |
| 39 | +### Communication between Execution and History Service |
| 40 | + |
| 41 | +The communication between the Execution service and the History service is implemented through a RabbitMQ Message Queue. RabbitMQ is ideal for message queues in microservices due to its reliability, flexible routing, and scalability. It ensures messages aren’t lost through durable queues and supports complex routing to handle diverse messaging needs. |
| 42 | + |
| 43 | +Asynchronous communication was chosen as a user’s submission history did not need to be updated immediately. Instead of waiting for a response, the Execution Service can put the message in a queue and continue processing other requests. |
| 44 | + |
| 45 | + |
| 46 | + |
| 47 | +A message queue allows services to communicate without depending on each other's availability. The Execution Service can send a message to the queue, and the History Service can process it when it’s ready. This decoupling promotes loose coupling and reduces dependencies between services, which helps maintain a robust and adaptable system. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## Setup |
| 52 | + |
| 53 | +### Prerequisites |
| 54 | + |
| 55 | +Ensure you have Go installed on your machine. |
| 56 | + |
3 | 57 | ### Installation
|
4 | 58 |
|
5 | 59 | 1. Install dependencies:
|
@@ -61,10 +115,10 @@ The server will be available at http://localhost:8083.
|
61 | 115 |
|
62 | 116 | ## API Endpoints
|
63 | 117 |
|
64 |
| -- `POST /tests/populate` |
65 |
| -- `GET /tests/{questionDocRefId}/` |
66 |
| -- `POST /tests/{questionDocRefId}/execute` |
67 |
| -- `POST /tests/{questionDocRefId}/submit` |
| 118 | +- `POST: /tests/populate`: Deletes and repopulates all tests in Firebase |
| 119 | +- `GET: /{questionDocRefId}`: Reads the public testcases for the question, identified by the question reference ID |
| 120 | +- `POST: /{questionDocRefId}/execute`: Executes the public testcases for the question, identified by the question reference ID |
| 121 | +- `POST: /{questionDocRefId}/submit`: Executes the public and hidden testcases for the question, identified by the question reference ID, and submits the code submission to History Service |
68 | 122 |
|
69 | 123 | ## Managing Firebase
|
70 | 124 |
|
|
0 commit comments