Skip to main content

Durability QoS Policy

The Durability policy controls whether samples are available to late-joining readers.

Values

ValueDescriptionPersistence
VolatileSamples only for connected readersNone
TransientLocalWriter caches samples for late joinersIn-memory
PersistentSamples persisted to diskDisk

Volatile (Default)

Samples are only delivered to readers that are connected when the sample is written.

use hdds::prelude::*;

let qos = DataWriterQos::default()
.durability(Durability::Volatile);

Use cases:

  • Real-time sensor data
  • Video/audio streaming
  • Data where history doesn't matter

Transient Local

Writers cache samples in memory. Late-joining readers receive cached samples.

use hdds::prelude::*;

let qos = DataWriterQos::default()
.durability(Durability::TransientLocal)
.history(History::KeepLast { depth: 100 });

let writer = publisher.create_writer_with_qos(&topic, qos)?;

// Write some samples
writer.write(&sample1)?;
writer.write(&sample2)?;

// Later, a new reader joins and receives sample1, sample2

Characteristics:

  • Samples persist as long as writer exists
  • Cache size controlled by History policy
  • Memory usage = history depth × sample size
  • Lost when writer terminates

Use cases:

  • Configuration/state topics
  • Last-known-good values
  • Initialization data

Persistent

Samples are persisted to disk. Survives application restarts.

use hdds::prelude::*;

let qos = DataWriterQos::default()
.durability(Durability::Persistent)
.durability_service(DurabilityService {
history_depth: 1000,
max_samples: 10_000,
max_instances: 100,
max_samples_per_instance: 100,
service_cleanup_delay_us: 0,
});

Characteristics:

  • Samples survive writer/application restart
  • Requires DurabilityService configuration
  • Higher write latency (disk I/O)
  • Disk usage = configured limits

Use cases:

  • Event logs
  • Audit trails
  • Mission-critical state

Compatibility Rules

Durability has an ordered compatibility: writer must offer at least what reader requests.

WriterReaderMatch?
PersistentPersistent✅ Yes
PersistentTransientLocal✅ Yes
PersistentVolatile✅ Yes
TransientLocalTransientLocal✅ Yes
TransientLocalVolatile✅ Yes
TransientLocalPersistent❌ No
VolatileVolatile✅ Yes
VolatileTransientLocal❌ No
VolatilePersistent❌ No

Rule: Writer.durability >= Reader.durability

DurabilityService Configuration

For TransientLocal and Persistent, configure the cache:

let service = DurabilityService {
// Cleanup delay after all readers acknowledge
service_cleanup_delay_us: 0,

// History depth for late-joiners
history_depth: 100,

// Maximum samples in cache
max_samples: 10_000,

// Maximum instances (for keyed topics)
max_instances: 100,

// Samples per instance
max_samples_per_instance: 100,
};

let qos = DataWriterQos::default()
.durability(Durability::TransientLocal)
.durability_service(service);

Validation:

  • All max_* fields must be > 0
  • max_samples >= max_samples_per_instance × max_instances

Late Joiner Behavior

Memory Considerations

Transient Local Memory Usage

Memory = history_depth × avg_sample_size × max_instances

Example:

  • History depth: 100
  • Sample size: 1 KB
  • Instances: 10
  • Memory: 1 MB

Controlling Cache Size

// Limit cache to 10 MB
let qos = DataWriterQos::default()
.durability(Durability::TransientLocal)
.resource_limits(ResourceLimits {
max_samples: 10_000,
max_instances: 1,
max_samples_per_instance: 10_000,
max_quota_bytes: 10 * 1024 * 1024, // 10 MB
});

Examples

Configuration Topic

// Writer publishes configuration, late joiners get latest
let writer_qos = DataWriterQos::default()
.durability(Durability::TransientLocal)
.reliability(Reliability::Reliable {
max_blocking_time: Duration::from_secs(1),
})
.history(History::KeepLast { depth: 1 });

// Reader expects to receive current config on join
let reader_qos = DataReaderQos::default()
.durability(Durability::TransientLocal)
.reliability(Reliability::Reliable);

Event Log

// Persistent event log survives restarts
let writer_qos = DataWriterQos::default()
.durability(Durability::Persistent)
.reliability(Reliability::Reliable {
max_blocking_time: Duration::from_secs(5),
})
.history(History::KeepAll)
.durability_service(DurabilityService {
history_depth: u32::MAX,
max_samples: 1_000_000,
max_instances: 1,
max_samples_per_instance: 1_000_000,
service_cleanup_delay_us: 0,
});

Volatile Streaming

// Real-time video stream, no caching needed
let writer_qos = DataWriterQos::default()
.durability(Durability::Volatile)
.reliability(Reliability::BestEffort)
.history(History::KeepLast { depth: 1 });

Interaction with History

Durability and History work together:

DurabilityHistoryCached Samples
VolatileAny0 (no cache)
TransientLocalKeepLast(N)Last N samples
TransientLocalKeepAllAll samples (bounded by ResourceLimits)
PersistentKeepLast(N)Last N per instance on disk
PersistentKeepAllAll samples on disk

Next Steps