- Golang
- PostgreSQL
- Redis
- Docker
- Docker-compose
- OpenAPI
- Swagger
The user wants to calculate arithmetic expressions. He enters the string 2 + 2 * 2 and wants the answer to be 6. But our addition and multiplication operations (also division and subtraction) take a “very, very” long time to complete. Therefore, the option in which the user makes an http request and receives the result as a response is impossible. Moreover: the calculation of each such operation in our “alternative reality” takes “giant” computing power. Accordingly, we must be able to perform each action separately and we can scale this system by adding computing power to our system in the form of new “machines”. Therefore, when a user sends an expression, he receives an expression identifier in response and can, at some periodicity, check with the server whether the expression has been counted? If the expression is finally evaluated, he will get the result. Remember that some parts of an arphimetic expression can be evaluated in parallel.
-
Arithmetic expression input form. The user enters an arithmetic expression and sends a POST http request with this expression to the back-end. Note: Requests must be idempotent. A unique identifier is added to requests. If a user sends a request with an identifier that has already been sent and accepted for processing, the response is 200. Possible response options:
- [200]. The expression was successfully accepted, parsed and accepted for processing
- [400]. The expression is invalid
- [500]. Something is wrong on the back-end. As a response, you need to return the id of the expression accepted for execution.
-
Page with a list of expressions in the form of a list with expressions. Each entry on the page contains a status, an expression, the date it was created, and the date the calculation was completed. The page receives data with a GET http request from the back-end
-
A page with a list of operations in the form of pairs: operation name + execution time (editable field). As already stated in the problem statement, our operations take “as if for a very long time.” The page receives data with a GET http request from the back-end. The user can configure the operation execution time and save the changes.
-
Page with a list of computing capabilities. The page receives data with a GET http request from the server in the form of pairs: the name of the computing resource + the operation performed on it.
Requirements:
- The orchestrator can be restarted without losing state. We store all expressions in the DBMS.
- The orchestrator must keep track of tasks that take too long to complete (the computer may also go offline) and make them available for computation again.
Consists of 2 elements:
- The server, which receives an arithmetic expression, translates it into a set of sequential tasks and ensures the order in which they are executed. From now on we will call it Orchestrator.
- A computer that can receive orchestrator task, execute it and return the result to the server. In what follows we will call it Agent.
Orchestrator Server that has the following endpoints:
- Adding an arithmetic expression evaluation.
- Getting a list of expressions with statuses.
- Getting the value of an expression by its identifier.
- Obtaining a list of available operations with their execution time.
- Receiving a task for execution.
- Receiving the result of data processing.
The Daemon Agent Receives an expression to be evaluated from the server, evaluates it and sends the result of the expression to the server. When starting, the daemon launches several goroutines, each of which acts as an independent computer. The number of goroutines is controlled by an environment variable.
To start project you need:
cd <"path/to/project/root/directory">
- Set passwords for redis and postgresql in the file
.env.example
- Rename
.env.example
to.env
docker-compose -f docker-compose.yml up -d
POST http://localhost:8080/register
{
"login": "klef99",
"password": "123abCC"
}
OK, if the registration is successful. Otherwise, the HTTP code is not 200.
POST http://localhost:8080/login
{
"login": "klef99",
"password": "123abCC"
}
JWT token if registration is successful. Otherwise, the HTTP code is not 200.
GET http://localhost:8080/getWorkersStatus
[
{
"workerName": "worker1",
"status": "OK",
"taskCount": "2"
}
]
The agent is considered unavailable if a minute has passed since the last heartbeat. If no agent is available, the expressions are not sent for calculation. taskCount - the number of operations currently being calculated. Since this is a method for internal use, it does not require a token.
The view is as follows: Authorization: Bearer <token>
POST http://localhost:8080/addExpression
In this request, the user can send his id as an idempotence key. If header:X-Request-Id is empty, the server-generated uuid is returned in the response body. Otherwise, the user's uuid is used. To create a uuid, you can use this site.
{
"expression": "2+2/1+2/1"
}
{
"expressionid": "603b53cb-2175-46bd-a15f-bfba1e1918fb",
"expression": "2+2/1+2/1",
"status": 0
}
GET http://localhost:8080/getExpressionByID?expressionId=<expressionid>
{
"expressionid": "6c992cda-5565-4123-a004-4bd645b5de63",
"expression": "((9*7)-(4/2)+(6*3)/(15-3)*(10+2))+(5-2)/(8*2)*(7/1)",
"status": 2,
"result": 80.3125
}
- 0 - The expression was added to the database.
- 1 - The expression was divided into elementary operations.
- 2 - The expression was calculated (result != null)
- -1 - The expression was invalidated during calculation.
GET http://localhost:8080/getExpressionsList
[
{
"expressionid": "edd8d169-7e60-41ea-8d3c-e8766718461a",
"expression": "(1+1))",
"status": -1,
"result": null
},
{
"expressionid": "d4be595a-f538-4132-a14b-efe7784d5aa5",
"expression": "((5*3)+(8/2)-(7*4)/(6-3)*(9+1)/(2*5)-(6/2)+(3*2)+(4-1)/(9*1)*(2+7)/(8-6)*(5/5))",
"status": 2,
"result": 14.166666666666666
},
{
"expressionid": "603b53cb-2175-46bd-a15f-bfba1e1918fb",
"expression": "2+2/1+2/1",
"status": 2,
"result": 6
}
]
POST http://localhost:8080/setOperationsTimeout
{
"*": 3,
"+": 5,
"-": 5,
"/": 10
}
The body can contain any number of operations (from 0 to 4). If there is no data about any operation in redis, then the default value is set for this operation (10 seconds). Timeout in seconds.
OK
GET http://localhost:8080/getOperationsTimeout
{
"*": 3,
"+": 5,
"-": 5,
"/": 10
}
For a more convenient introduction to the system, you should use postman Postman file. Also available OpenAPI configuration file. Swagger instance