Asynchronous reporting
Overview
Asynchronous reporting is set up using the AMQP 0-9-1 protocol with RabbitMq as the message broker. The main idea is to respond to the client immediately after the server receives a request. This way, the client isn't blocked and doesn't have to wait for the server to process the request. Additionally, it acts as a requests load balancer, storing it in queues until the backend is free to process them.
Scheme of interactions between RabbitMq and API
Difference between ID and UUID
ID
is a numerical identifier for an entity, automatically generated by the database at the moment of saving.
UUID
is a string virtual identifier for an entity. UUID
can be generated on the client side and provided with a request. If it is not provided,
it is generated automatically when the API
accepts the request.
Each entity has both ID
and UUID
. ID
is used to perform the CRUD operations on an entity that is already saved in db.
UUID
is used to build the child-parent relationships between entities on the client side during reporting.
In case of synchronous reporting, any response from API
is returned after the request is handled and the entity is saved in the database.
In case of asynchronous reporting, any response from API
is returned before the request is handled and the entity is saved in the database and after the request is published to the queue.
The responses in both modes look the same:
{
"id": "cd64d5eb-fea1-4e7e-8a5a-69998ac5620f"
}
The id
property in the response is actually an UUID
. This is for backward compatibility.
Therefore, when you have this UUID
and want to update or delete the entity, you need to first retrieve the physical ID
.
It can be done via API
:
Asynchronous reporting scheme
- Step 1
API
receives HTTP request fromclient
to the reporting controller. TheController
verifies permissions and call theproducer
logic. - Step 2
Producer
validates business rules if necessary, generates UUID (virtual id) if it is not provided in request, builds a message forRabbitMq
and sends it to the exchange with x-consistent-hash type. After message is sent, thecontroller
returns HTTP response to theclient
with UUID. At the moment, the physical entity in database may not be created yet! - Step 3
Consumer
starts processing the message as soon as it is received fromRabbitMq
. After a successful processing, the entity will be stored in a database and obtain a physical id. In case of an exception, it is logged and the entity is not saved.
Enable asynchronous reporting in agents
Async reporting is supported only by agents since version 5.0.0.
To enable it you should set rp.reporting.async=true
in reportportal.properties
.
By default (if property rp.reporting.async
is not specified) agents work in a synchronous mode.
rp.endpoint=https://example.com
rp.api.key=xxx
rp.launch=launch-name
rp.project=project-name
rp.reporting.async=true
(*) Listed above is an example for Java-based client. For another platforms please see corresponding documentation.
Asynchronous API
Async controllers have /api/v2
prefix.
Requests and responses have no differences with sync ones but there are some specific distinctions in the behavior that is described in
reporting guide.
- Start launch
- Start root(suite) item
- Start child(container) item
- Start child(step) item
- Finish child item
- Finish parent(container) item
- Save single log without attachment
- Batch save logs
- Save launch log
- Finish root(suite) item
- Finish launch
Detailed scheme of interactions between RabbitMq and API
API properties
API
has the following properties for connection to RabbitMq service:
Name | Environment variable name | Default value |
---|---|---|
rp.amqp.host | RP_AMQP_HOST | rabbitmq |
rp.amqp.port | RP_AMQP_PORT | 5672 |
rp.amqp.user | RP_AMQP_USER | rabbitmq |
rp.amqp.pass | RP_AMQP_PASS | rabbitmq |
rp.amqp.addresses | RP_AMQP_ADDRESSES | amqp://rabbitmq:rabbitmq@rabbitmq:5672 |
reporting.queues.count | REPORTING_QUEUES_COUNT | 10 |
rp.amqp.host
- Hostname of RabbitMq service.
rp.amqp.port
- Port of RabbitMq service.
rp.amqp.user
- Username to connect to RabbitMq service.
rp.amqp.pass
- User password to connect to RabbitMq service.
rp.amqp.addresses
- Full address to connect to RabbitMq service.
reporting.queues.count
- Number of queues to be processed by this service-api.
Exchanges and queues for reporting
The API
creates two exchanges: e.reporting
and e.reporting.retry
. The e.reporting
exchange is linked to queues that handle messages from requests, while the e.reporting.retry
exchange is linked to retry queue and manages rejected from the main reporting queues.
The number of queues in these exchanges depends on the REPORTING_QUEUES_COUNT
env variable. The e.reporting
exchange has N
queues named q.reporting.id.0
to q.reporting.id.N
. The e.reporting.retry
exchange has 1 queue named q.retry.reporting.ttl
.
If a message from e.reporting.retry
is consumed and throws an exception more than 20 times, it will be moved to a separate queue named q.parkingLot.reporting
, where it will be stored for 7 days for manual error analysis.
The retry message will be stored in q.retry.reporting.ttl
with progressive TTL. It means that ttl will be increase each time. The whole ttl of the retry message is about 2 hours.
Scheme
All requests (items, logs) related to the same launch will be stored in the same RabbitMQ queue.
This is achieved by using an exchange that maps messages to queues using the Consistent Hashing
algorithm.
Messages in the queue don't have a strict order but they are stored mostly in the same order as they arrive from client
.
This ensures a minimal amount of exceptions (causing the sending of such messages to the retry queue) caused by cases when a child is handled before its own parent.
Consuming scheme:
(!)
All not managed exceptions will be moved to the q.parkingLot.reporting
for manual analysis.
Possible exceptions that may be thrown and lead to moving the message to the retry queue:
- On start launch/test item:
- Entity not found. Parent entity not found.
- On finish launch/test item:
- Entity not found. Entity that has to be finished not found in database or parent entity not found (for test items).
- On log creation:
- Entity not found. Trying to create log for not existing launch/test item
Finishing launch
If the order is not broken, launch finish request will be handled when there are no more child item requests in the queue.
(!)
It is a main difference in reporting mechanism between ReportPortal version 4 and 5.
If the launch finish request is not the last in the queue, the launch will be finished anyway.
However, all subsequent requests related to that launch will be handled as they reach the consumer, and the launch statistics will be updated accordingly.
This means it is possible to report items under an already finished launch.
Events associated with the launch finish will be processed as soon as the launch finish is handled.
Items processed after the launch finish will not be included in post-launch handling processes such as 'Auto Analysis' and 'Quality Gates.'