Skip to main content

Ownership QoS Policy

The Ownership policy controls whether multiple DataWriters can update the same data instance simultaneously. Combined with Ownership Strength, it enables primary/backup failover patterns.

Purpose

Ownership provides writer arbitration:

  • SHARED (default): All writers can publish to the same instance; readers receive from all of them
  • EXCLUSIVE: Only the writer with the highest Ownership Strength can publish to a given instance; lower-strength writers are ignored

Ownership Kinds

KindBehaviorUse Case
ownership_shared()All writers deliver (default)Normal publish/subscribe
ownership_exclusive()Only highest-strength writer winsPrimary/backup, failover

Configuration

Shared Ownership (Default)

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("shared_app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Multiple writers can all publish to the same topic
let writer_a = participant
.topic::<SensorData>("sensors/temperature")?
.writer()
.qos(QoS::reliable().ownership_shared())
.build()?;

let writer_b = participant
.topic::<SensorData>("sensors/temperature")?
.writer()
.qos(QoS::reliable().ownership_shared())
.build()?;

// Reader receives data from BOTH writers
let reader = participant
.topic::<SensorData>("sensors/temperature")?
.reader()
.qos(QoS::reliable().ownership_shared())
.build()?;

Exclusive Ownership

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("failover_app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Primary writer (high strength)
let primary = participant
.topic::<ControlData>("control/commands")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(200))
.build()?;

// Backup writer (low strength) - only publishes if primary fails
let backup = participant
.topic::<ControlData>("control/commands")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(100))
.build()?;

// Reader only receives from the highest-strength active writer
let reader = participant
.topic::<ControlData>("control/commands")?
.reader()
.qos(QoS::reliable().ownership_exclusive())
.build()?;
#include <hdds.h>

/* Primary writer with EXCLUSIVE ownership, strength 200 */
struct HddsQoS* qos_primary = hdds_qos_reliable();
hdds_qos_set_ownership_exclusive(qos_primary, 200);

struct HddsDataWriter* primary = hdds_writer_create_with_qos(
participant, "control/commands", qos_primary);
hdds_qos_destroy(qos_primary);

/* Backup writer with strength 100 */
struct HddsQoS* qos_backup = hdds_qos_reliable();
hdds_qos_set_ownership_exclusive(qos_backup, 100);

struct HddsDataWriter* backup = hdds_writer_create_with_qos(
participant, "control/commands", qos_backup);
hdds_qos_destroy(qos_backup);

/* Reader with EXCLUSIVE ownership */
struct HddsQoS* qos_reader = hdds_qos_reliable();
hdds_qos_set_ownership_exclusive(qos_reader, 0);

struct HddsDataReader* reader = hdds_reader_create_with_qos(
participant, "control/commands", qos_reader);
hdds_qos_destroy(qos_reader);

Default Value

Default is SHARED with strength 0:

let qos = QoS::reliable();
// ownership = SHARED, ownership_strength = 0

Fluent Builder Methods

MethodDescription
.ownership_shared()Set shared ownership (default)
.ownership_exclusive()Set exclusive ownership
.ownership_strength(value)Set strength value (higher wins)
.ownership_strength_high()Set high strength (value: 100)
.ownership_strength_low()Set low strength (value: -100)

How Exclusive Ownership Works

Writer A (strength=100)  ----------> \
\
> Reader only sees Writer B
/
Writer B (strength=200) ----------> /

If Writer B crashes --> Reader sees Writer A (failover!)

Arbitration Rules

  1. The first writer to publish an instance becomes the owner
  2. A writer with higher or equal strength takes over ownership
  3. A writer with lower strength is rejected
  4. When the current owner is no longer alive, the next-highest-strength writer takes over
Writer A StrengthWriter B StrengthOwner
100200Writer B
200100Writer A
100100First to publish

Compatibility Rules

Writer and Reader ownership kinds must match exactly:

WriterReaderMatch?
SHAREDSHAREDYes
EXCLUSIVEEXCLUSIVEYes
SHAREDEXCLUSIVENo
EXCLUSIVESHAREDNo
Incompatibility

A SHARED writer cannot match an EXCLUSIVE reader, and vice versa. Both sides must agree on the ownership mode.

Use Cases

Primary/Backup Failover

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("failover_system")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Primary node: strength 200
let primary = participant
.topic::<StateUpdate>("system/state")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(200))
.build()?;

// Hot standby: strength 100 (takes over if primary fails)
let standby = participant
.topic::<StateUpdate>("system/state")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(100))
.build()?;

Redundant Sensors

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("redundant_sensors")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Primary sensor (higher quality, higher strength)
let primary_sensor = participant
.topic::<SensorData>("sensors/position")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength_high())
.build()?;

// Backup sensor (lower quality, lower strength)
let backup_sensor = participant
.topic::<SensorData>("sensors/position")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength_low())
.build()?;

Multi-Robot Coordination

use hdds::{Participant, QoS, TransportMode};

let participant = Participant::builder("robot_coordinator")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Leader robot (highest priority for shared resource control)
let leader = participant
.topic::<ResourceControl>("shared/resource")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(300))
.build()?;

// Follower robots (lower priority)
let follower = participant
.topic::<ResourceControl>("shared/resource")?
.writer()
.qos(QoS::reliable().ownership_exclusive().ownership_strength(100))
.build()?;

Interaction with Other Policies

Ownership + Liveliness

Exclusive ownership relies on liveliness to detect when the current owner has failed:

ScenarioBehavior
Owner aliveOnly owner's data delivered
Owner liveliness lostNext-highest-strength writer takes over
Owner comes back (higher strength)Regains ownership
info

Combine Exclusive Ownership with Liveliness monitoring for robust failover. Without liveliness, the middleware may not detect owner failure promptly.

Ownership + Reliability

OwnershipReliabilityBehavior
EXCLUSIVEreliable()Guaranteed delivery from the owner only
EXCLUSIVEbest_effort()Best-effort from owner, backup may take over
SHAREDreliable()Guaranteed delivery from all writers

Ownership + Durability

With transient_local() and EXCLUSIVE ownership, late joiners receive only samples from the current owner, not from lower-strength writers.

Common Pitfalls

  1. Mismatched kinds: SHARED writer + EXCLUSIVE reader (or vice versa) results in no match. Both must use the same kind.

  2. Forgetting strength on EXCLUSIVE: With EXCLUSIVE ownership, always set ownership_strength(). The default strength is 0, which may lead to unexpected arbitration with negative-strength writers.

  3. No liveliness monitoring: Without liveliness, failover may be slow because the middleware relies on other mechanisms to detect writer loss.

  4. Strength ties: When two writers have equal strength, the first to publish wins. This can lead to non-deterministic behavior. Use distinct strength values.

  5. Per-instance arbitration: Ownership is per data instance (key), not per topic. Different instances can have different owners.

Performance Notes

  • Ownership checking adds minimal overhead (atomic integer comparison)
  • EXCLUSIVE mode reduces reader-side traffic (only one writer's data delivered)
  • Strength-based arbitration is O(1) per sample
  • The OwnershipArbiter uses lock-free atomics for thread safety

Next Steps