Skip to main content

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 from client to the reporting controller. The Controller verifies permissions and call the producer logic.
  • Step 2
    Producer validates business rules if necessary, generates UUID (virtual id) if it is not provided in request, builds a message for RabbitMq and sends it to the exchange with x-consistent-hash type. After message is sent, the controller returns HTTP response to the client 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 from RabbitMq. 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.

Detailed scheme of interactions between RabbitMq and API

API properties

API has the following properties for connection to RabbitMq service:

NameEnvironment variable nameDefault value
rp.amqp.hostRP_AMQP_HOSTrabbitmq
rp.amqp.portRP_AMQP_PORT5672
rp.amqp.userRP_AMQP_USERrabbitmq
rp.amqp.passRP_AMQP_PASSrabbitmq
rp.amqp.addressesRP_AMQP_ADDRESSESamqp://rabbitmq:rabbitmq@rabbitmq:5672
reporting.queues.countREPORTING_QUEUES_COUNT10

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.'