Skip to content

Best-effort delivery with transient-local durability

Rule 3 · applies to publishers and subscribers · Back to all rules

Breaks a guarantee. Late joiners may not receive the stored history you expect, because best-effort never retransmits the retained samples.

If you set Reliability = BEST_EFFORT together with Durability = TRANSIENT_LOCAL or stronger
Breaks a guarantee
  • Settings involved: Reliability and Durability
  • What QoS Guard checks: [DURABL ≥ TRAN_LOCAL] ∧ [RELIAB = BEST_EFFORT]

Example

A latched map topic uses TRANSIENT_LOCAL but best-effort. A node that starts late can miss the map instead of receiving the retained copy.

How to fix it

Use RELIABLE together with TRANSIENT_LOCAL so late joiners actually receive the retained samples.

Why this rule is flagged

What the DDS specification says

The DDS specification does not settle this case on its own, so the rule rests on the engine's implementation and direct measurement.


What the engine source code shows

TRANSIENT_LOCAL late-join replay is gated on the reliable delivery path in both Fast DDS and Cyclone DDS. Under best-effort reliability, historical data is not delivered to late joiners.

Fast DDS implementation evidence

// [TRANSIENT_LOCAL late-joiner logic resides within if (is_reliable)]
// → When best-effort, it returns false, preventing the later-joiner logic from executing → Transient behaviour does not occur
bool is_reliable = rp->is_reliable();
if (is_reliable)
{
    SequenceNumber_t min_seq = get_seq_num_min();
    SequenceNumber_t last_seq = get_seq_num_max();
    RTPSMessageGroup group(mp_RTPSParticipant, this, rp->message_sender());
    // History not empty
    if (min_seq != SequenceNumber_t::unknown())
    {
        (void)last_seq;
        assert(last_seq != SequenceNumber_t::unknown());
        assert(min_seq <= last_seq);
        try {
            // Late-joiner
            if (TRANSIENT_LOCAL <= rp->durability_kind() && TRANSIENT_LOCAL <= m_att.durabilityKind)

Cyclone DDS implementation evidence

// Providing historical data to late joiners is only performed when it is more efficient than best-effort processing.
/* Store available data into the late joining reader when it is reliable (we don't do
   historical data for best-effort data over the wire, so also not locally). */
if (rd->xqos->reliability.kind > DDS_RELIABILITY_BEST_EFFORT && rd->xqos->durability.kind > DDS_DURABILITY_VOLATILE)
    ddsi_deliver_historical_data (wr, rd);
Item Value
Dataset Download CSV
Fixed QoS setting None
Tested variable RELIAB.kind, DURABL.kind, late_join_ms
Tested values RELIAB ∈ {BEST_EFFORT, RELIABLE}, DURABL ∈ {VOLATILE, TRANSIENT_LOCAL}, late_join_ms ∈ {50, 150, 350, 550}
Rule-relevant case RELIAB = BEST_EFFORT, DURABL = TRANSIENT_LOCAL
Tested engines / versions Fast DDS 2.14.6 (Jazzy), Cyclone DDS 0.10.5
Network setting RTT = 1 ms, loss ∈ {0%, 20%}, PP = 50 ms, message size = 1024 B

Rule 3 Cyclone DDS heatmap

Rule 3 Fast DDS heatmap

Late-join recovery was observed only for RELIAB = RELIABLE with DURABL = TRANSIENT_LOCAL in the tested loopback measurements.


What the measurements show

The tested late-join measurements for this rule are shown with the source trace above, so there is no separate result to repeat here.