Using QoS Policies

Quality of Service (QoS) policies control the behavior of DDS entities. This guide explains how to use and configure QoS policies in AstuteDDS.

What are QoS Policies?

QoS policies define non-functional properties like:

  • Reliability: Guaranteed vs. best-effort delivery
  • Durability: Whether late joiners get historical data
  • History: How many samples to keep
  • Deadline: Maximum time between samples
  • Liveliness: How to detect inactive writers
  • Ownership: Single vs. multiple writers

QoS Policy Categories

Data Delivery Policies

Control how data is delivered from writers to readers.

Reliability

Determines delivery guarantees:

#include <astutedds/dcps/qos.hpp>

// RELIABLE: Guaranteed delivery with retransmission
auto reliable_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .build();

// BEST_EFFORT: No delivery guarantees, lower latency
auto besteffort_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::BEST_EFFORT_RELIABILITY_QOS)
    .build();

When to use RELIABLE: - Commands and control messages - Financial transactions - Configuration updates - Any data that cannot be lost

When to use BEST_EFFORT: - High-frequency sensor data - Video/audio streams - Real-time telemetry - Data where latest value matters most

Durability

Controls late joiner behavior:

// VOLATILE: No historical data
auto volatile_qos = astutedds::dcps::DataWriterQosBuilder()
    .durability(astutedds::dcps::DurabilityQosPolicyKind::VOLATILE_DURABILITY_QOS)
    .build();

// TRANSIENT_LOCAL: Late joiners get last samples
auto transient_qos = astutedds::dcps::DataWriterQosBuilder()
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
    .build();

// TRANSIENT: Survives process restart
auto transient_persistent_qos = astutedds::dcps::DataWriterQosBuilder()
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_DURABILITY_QOS)
    .build();

// PERSISTENT: Stored in database
auto persistent_qos = astutedds::dcps::DataWriterQosBuilder()
    .durability(astutedds::dcps::DurabilityQosPolicyKind::PERSISTENT_DURABILITY_QOS)
    .build();

Durability levels:

Level Late Joiners Process Restart Database
VOLATILE ❌ No data ❌ Lost ❌ No
TRANSIENT_LOCAL ✅ Last samples ❌ Lost ❌ No
TRANSIENT ✅ Last samples ✅ Survives ⚠️ Filesystem
PERSISTENT ✅ All samples ✅ Survives ✅ Database

History

Controls sample retention:

// KEEP_LAST: Keep only last N samples per instance
auto keep_last_qos = astutedds::dcps::DataWriterQosBuilder()
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS, 10)
    .build();

// KEEP_ALL: Keep all samples (up to resource limits)
auto keep_all_qos = astutedds::dcps::DataWriterQosBuilder()
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_ALL_HISTORY_QOS)
    .build();

Trade-offs: - KEEP_LAST(n): Bounded memory, may lose old samples - KEEP_ALL: Unbounded memory (constrained by resource limits)

Data Timing Policies

Control temporal aspects of data delivery.

Deadline

Maximum time between sample updates:

using namespace std::chrono_literals;

// Expect updates at least every 1 second
auto deadline_qos = astutedds::dcps::DataWriterQosBuilder()
    .deadline(1s)
    .build();

// Reader expects same deadline
auto reader_qos = astutedds::dcps::DataReaderQosBuilder()
    .deadline(1s)
    .build();

Use cases: - Periodic sensor data (must arrive every X ms) - Heartbeat monitoring - Time-critical state updates

Callback on missed deadline:

reader->set_listener(
    [](const astutedds::dcps::RequestedDeadlineMissedStatus& status) {
        std::cout << "Deadline missed! " << status.total_count << " times\n";
    }
);

Liveliness

Detects inactive writers:

// AUTOMATIC: DDS manages liveliness
auto auto_liveliness = astutedds::dcps::DataWriterQosBuilder()
    .liveliness(
        astutedds::dcps::LivelinessQosPolicyKind::AUTOMATIC_LIVELINESS_QOS,
        3s  // lease duration
    )
    .build();

// MANUAL_BY_PARTICIPANT: Application asserts participant liveliness
auto manual_participant = astutedds::dcps::DataWriterQosBuilder()
    .liveliness(
        astutedds::dcps::LivelinessQosPolicyKind::MANUAL_BY_PARTICIPANT_LIVELINESS_QOS,
        5s
    )
    .build();

// MANUAL_BY_TOPIC: Application asserts per-writer liveliness
auto manual_topic = astutedds::dcps::DataWriterQosBuilder()
    .liveliness(
        astutedds::dcps::LivelinessQosPolicyKind::MANUAL_BY_TOPIC_LIVELINESS_QOS,
        5s
    )
    .build();

Asserting liveliness manually:

// For MANUAL_BY_PARTICIPANT
participant->assert_liveliness();

// For MANUAL_BY_TOPIC
writer->assert_liveliness();

Data Availability Policies

Ownership

Controls multiple writers to the same instance:

// SHARED: All writers can update (default)
auto shared_ownership = astutedds::dcps::DataWriterQosBuilder()
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::SHARED_OWNERSHIP_QOS)
    .build();

// EXCLUSIVE: Highest strength writer wins
auto exclusive_ownership = astutedds::dcps::DataWriterQosBuilder()
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS)
    .ownership_strength(100)  // Higher = higher priority
    .build();

EXCLUSIVE ownership example:

// Primary writer (high strength)
auto primary_writer_qos = astutedds::dcps::DataWriterQosBuilder()
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS)
    .ownership_strength(200)
    .build();

// Backup writer (low strength)
auto backup_writer_qos = astutedds::dcps::DataWriterQosBuilder()
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS)
    .ownership_strength(100)
    .build();

// Reader only sees samples from primary (strength 200)
// If primary fails, reader switches to backup

Resource Management Policies

Resource Limits

Control memory usage:

auto resource_limits = astutedds::dcps::DataWriterQosBuilder()
    .resource_limits(
        1000,   // max_samples
        100,    // max_instances
        10      // max_samples_per_instance
    )
    .build();

Parameters: - max_samples: Total samples across all instances - max_instances: Maximum number of different keys - max_samples_per_instance: Samples per key

User Data Policies

Attach metadata to entities:

// Attach user data to participant
std::vector<uint8_t> participant_data = {'A', 'p', 'p', '1'};
auto participant_qos = astutedds::dcps::DomainParticipantQosBuilder()
    .user_data(participant_data)
    .build();

// Attach group data to publisher
std::vector<uint8_t> group_data = {'S', 'e', 'n', 's', 'o', 'r', 's'};
auto publisher_qos = astutedds::dcps::PublisherQosBuilder()
    .group_data(group_data)
    .build();

// Attach topic data
std::vector<uint8_t> topic_data = {'v', '1', '.', '0'};
auto topic_qos = astutedds::dcps::TopicQosBuilder()
    .topic_data(topic_data)
    .build();

QoS Policy Compatibility

For communication to occur, QoS policies must be compatible:

RxO (Requested vs. Offered) Policies

Some policies follow "Requested vs. Offered" semantics:

Policy Rule Example
Reliability Requested ≤ Offered Reader RELIABLE ← Writer RELIABLE ✅
Reader RELIABLE ← Writer BEST_EFFORT ❌
Durability Requested ≤ Offered Reader TRANSIENT_LOCAL ← Writer TRANSIENT_LOCAL ✅
Reader TRANSIENT_LOCAL ← Writer VOLATILE ❌
Deadline Offered ≤ Requested Writer 1s deadline → Reader 2s deadline ✅
Writer 2s deadline → Reader 1s deadline ❌
Liveliness Offered ≤ Requested Writer 1s liveliness → Reader 2s lease ✅

Example: Compatible QoS

// Writer offers RELIABLE, TRANSIENT_LOCAL
auto writer_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
    .build();

// Reader requests RELIABLE, TRANSIENT_LOCAL - Compatible! ✅
auto reader_qos = astutedds::dcps::DataReaderQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
    .build();

Example: Incompatible QoS

// Writer offers BEST_EFFORT
auto writer_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::BEST_EFFORT_RELIABILITY_QOS)
    .build();

// Reader requests RELIABLE - Incompatible! ❌
auto reader_qos = astutedds::dcps::DataReaderQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .build();

// Result: Endpoints won't match, no communication

Common QoS Patterns

Pattern 1: Real-Time Sensor Data

High-frequency data where latest value matters:

auto sensor_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::BEST_EFFORT_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::VOLATILE_DURABILITY_QOS)
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS, 1)
    .build();

Pattern 2: Command and Control

Critical messages that must be delivered:

auto command_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_ALL_HISTORY_QOS)
    .build();

Pattern 3: State Synchronization

Current state for late joiners:

auto state_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS, 1)
    .build();

Pattern 4: Event Stream

Sequence of events that must all be received:

auto event_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .durability(astutedds::dcps::DurabilityQosPolicyKind::VOLATILE_DURABILITY_QOS)
    .history(astutedds::dcps::HistoryQosPolicyKind::KEEP_ALL_HISTORY_QOS)
    .build();

Pattern 5: Failover System

High-availability with exclusive ownership:

// Primary system
auto primary_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS)
    .ownership_strength(200)
    .liveliness(
        astutedds::dcps::LivelinessQosPolicyKind::MANUAL_BY_TOPIC_LIVELINESS_QOS,
        1s
    )
    .build();

// Backup system
auto backup_qos = astutedds::dcps::DataWriterQosBuilder()
    .reliability(astutedds::dcps::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
    .ownership(astutedds::dcps::OwnershipQosPolicyKind::EXCLUSIVE_OWNERSHIP_QOS)
    .ownership_strength(100)  // Lower strength
    .liveliness(
        astutedds::dcps::LivelinessQosPolicyKind::MANUAL_BY_TOPIC_LIVELINESS_QOS,
        1s
    )
    .build();

Complete Example

Here's a complete example showing QoS configuration:

#include <astutedds/dcps/domain_participant.hpp>
#include <astutedds/dcps/qos.hpp>
#include "SensorData_TypeSupport.hpp"

int main() {
    using namespace astutedds::dcps;

    // Create participant
    auto participant = DomainParticipantFactory::create_participant(0);

    // Configure topic QoS
    auto topic_qos = TopicQosBuilder()
        .reliability(ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
        .durability(DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
        .build();

    // Create topic
    auto topic = participant->create_topic<SensorData>(
        "SensorTopic",
        topic_qos
    );

    // Configure writer QoS
    auto writer_qos = DataWriterQosBuilder()
        .reliability(ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
        .durability(DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
        .history(HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS, 10)
        .deadline(std::chrono::seconds(1))
        .resource_limits(1000, 100, 10)
        .build();

    // Create writer
    auto publisher = participant->create_publisher();
    auto writer = publisher->create_datawriter(topic, writer_qos);

    // Configure reader QoS (must be compatible)
    auto reader_qos = DataReaderQosBuilder()
        .reliability(ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS)
        .durability(DurabilityQosPolicyKind::TRANSIENT_LOCAL_DURABILITY_QOS)
        .history(HistoryQosPolicyKind::KEEP_LAST_HISTORY_QOS, 20)
        .deadline(std::chrono::seconds(2))  // More lenient than writer
        .build();

    // Create reader
    auto subscriber = participant->create_subscriber();
    auto reader = subscriber->create_datareader(topic, reader_qos);

    // Publish data
    SensorData data;
    data.temperature = 25.5;
    data.humidity = 60.0;
    writer->write(data);

    return 0;
}

QoS Best Practices

  1. Start with defaults: Use DATAWRITER_QOS_DEFAULT initially
  2. Match carefully: Ensure writer/reader QoS policies are compatible
  3. Test combinations: Verify QoS behavior in your specific scenario
  4. Monitor resource usage: KEEP_ALL can consume unbounded memory
  5. Use RELIABLE sparingly: Only for data that must not be lost
  6. Set appropriate limits: Configure resource limits to prevent OOM
  7. Consider late joiners: Use TRANSIENT_LOCAL for state data
  8. Profile for performance: BEST_EFFORT has lower latency

Next Steps

References