skip to content

In ReportPortal, a second attempt of a test item arrives while a launch is running. What does `retryOf` link, and what happens to the earlier item's place in that launch?

level: seniorimportance: should knowfreq 40%

answer

  1. one attempt points at another
  2. the newer one keeps the tree slot
  3. the older loses its path and launch
  4. a flag is raised on item and launch

basics

~20 s

retryOf points a superseded attempt at the item that replaced it. The newer attempt becomes the main item, keeping the tree position and the statistics; the older one has its path and launch link cleared and stops counting.

solid answer

~50 s

A client starting a test item may send `retryOf` carrying the uuid of the item this execution supersedes. The server looks that item up and demotes it: its path and its launch link are set to null and its `retryOf` is pointed at the new item, which takes over as the **main** item — the one that keeps a place in the launch tree and contributes statistics. The demoted item's statistics and its issue are deleted so the launch totals do not count the same test twice. `hasRetries` is then raised on the surviving item and on the `Launch` itself, which is how a reader knows before opening anything that this launch contains repeated attempts. If no `retryOf` is sent, the server falls back to matching active items on `uniqueId` and parent and picking the latest start time as the main.

go deeper

for a junior

Know that ReportPortal links a repeated attempt to the one it replaces, and that only one of the two keeps a place in the launch tree while the other hangs off it.

for a middle

Explain both routes into the link - an explicit retryOf uuid on the start request, and the fallback that matches active items on uniqueId and parent - and say which of the two attempts ends up as the main item.

for a senior

Be ready to say why the launch totals do not double-count: the demoted attempt has its statistics and issue deleted along with its place in the tree, and a flag is raised on the survivor and on the launch itself.

for a principal

Own the consequence for every consumer downstream. A reader that walks the tree sees one attempt per test and a reader that follows the retry link sees them all, so an integration has to declare which population its numbers describe.

## The two routes into a retry link ReportPortal treats a repeated execution as **two rows that get linked**, not as one row that gets overwritten. Measured against `service-api` 6.0.0, the linking has two modes and the client's request decides which one runs. 1. **Explicit.** The start-item request carries `retryOf`, a string holding the uuid of the item this execution is a repeat of. The server resolves that uuid to a stored item and links the pair directly. If the uuid resolves to nothing, it logs and falls through to the other mode. If it resolves to an item that is *already* a retry, or one that has already left the tree, the server logs and does nothing — there is nothing sensible to demote. 2. **Implicit.** No `retryOf` is sent. The server instead looks for **active** items sharing the new item's `uniqueId` and the same parent, and takes the one with the latest start time as the winner. Active has a precise meaning here: an item that still has a path and whose own `retryOf` is null — in other words, one still in the launch tree in its own right. Both modes converge on the same outcome, so a client that never sends `retryOf` still gets retry handling, just inferred rather than declared. ## What being demoted actually costs an item One item comes out of the exchange as the **main** item and the others are demoted. Three separate things happen to a demoted item, and they are worth keeping distinct because they fail in different ways: | change | effect | |---|---| | its path is cleared | it is no longer part of the launch tree, so it stops appearing where readers browse | | its launch link is cleared | it no longer belongs to the launch for queries that walk from the launch down | | its `retryOf` is set | it now names the main item, which is the only route back to it | Alongside those, the demoted item's **statistics and its issue are deleted**. That is the part people miss. It is not merely hidden from a view: its contribution to the launch's counts is actively removed, which is exactly why a launch containing repeated attempts does not report inflated totals. The row itself survives in storage; its arithmetic does not. Two flags then go up. `hasRetries` is set on the surviving item, and `hasRetries` is set on the `Launch` — the launch-level flag is exposed on the launch resource, so a consumer can tell that a launch involved repetition without walking any items at all. ## Flattening, not chaining A test executed several times could in principle produce a chain, each attempt pointing at the one before it. ReportPortal does not do that. On every retry event the existing links are re-pointed so that **every** superseded attempt names the current main item directly. The structure stays one level deep: a main item, and a flat set of retries hanging off it. That matters for anything reading the data. Finding all attempts of a test is a single lookup on the main item, not a walk of unknown length, and there is no risk of a partially-updated chain leaving an attempt pointing at another attempt that has itself been demoted. ## Attempts that never finished Because the demoted attempt is a real row and not a fragment of the survivor, it can be left in an unfinished state — the client moved on to the next attempt and never sent a finish for the one it abandoned. ReportPortal sweeps these when the main item finishes: every item whose `retryOf` names it and which is still `IN_PROGRESS` is stamped with the main item's final status and end time. Without that sweep a launch could sit with attempts that never resolve. ## The reading a consumer has to get right The consequence for anything downstream is that **there is no single answer to "what did this launch run?"** — there are two, and they differ: - Walk the launch tree and you see main items only: one row per test, totals that count each test once. - Query by the retry link and you see every attempt, including the ones whose statistics were deleted. An integration that pulls results out of ReportPortal has to say which of those it means. Reconciling a tree-derived count against an attempt-derived count and calling the difference a defect is the usual mistake; the two are measuring different populations by design.

  • What does the server do when the client sends no `retryOf` at all?
    It falls back to matching. It looks for active items sharing the new item's `uniqueId` and parent, takes the one with the latest start time as the main, and demotes the rest. Active means an item still in the tree: one with a path and no `retryOf` of its own.
  • A retried item is still in progress when its main item finishes. What happens to it?
    It gets swept. Finishing the main item triggers a pass over every item whose `retryOf` names it and which is still `IN_PROGRESS`, stamping each with the main item's final status and end time so no attempt is left dangling in the launch.

saying these in an interview costs you the question

  • Thinks both attempts stay side by side in the launch tree
  • Assumes the earlier attempt still contributes to launch statistics
  • Believes retryOf must always be sent by the client
  • Expects a chain, each attempt pointing at the previous one