Durability QoS Policy
The Durability policy controls whether samples are available to late-joining readers.
Values
| Value | Description | Persistence |
|---|---|---|
Volatile | Samples only for connected readers | None |
TransientLocal | Writer caches samples for late joiners | In-memory |
Persistent | Samples persisted to disk | Disk |
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.
| Writer | Reader | Match? |
|---|---|---|
| Persistent | Persistent | ✅ Yes |
| Persistent | TransientLocal | ✅ Yes |
| Persistent | Volatile | ✅ Yes |
| TransientLocal | TransientLocal | ✅ Yes |
| TransientLocal | Volatile | ✅ Yes |
| TransientLocal | Persistent | ❌ No |
| Volatile | Volatile | ✅ Yes |
| Volatile | TransientLocal | ❌ No |
| Volatile | Persistent | ❌ 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:
| Durability | History | Cached Samples |
|---|---|---|
| Volatile | Any | 0 (no cache) |
| TransientLocal | KeepLast(N) | Last N samples |
| TransientLocal | KeepAll | All samples (bounded by ResourceLimits) |
| Persistent | KeepLast(N) | Last N per instance on disk |
| Persistent | KeepAll | All samples on disk |
Next Steps
- History - Sample buffering configuration
- Reliability - Delivery guarantees