Frequently Asked Questions

The 30 questions developers ask most often when adopting Astute DDS, ordered by frequency. Answers describe Astute DDS behaviour only.

Contents

  1. What is Astute DDS?
  2. How do I publish and subscribe to a topic?
  3. What is the difference between read() and take()?
  4. Why don't late-joining subscribers receive earlier samples?
  5. What does the RELIABILITY QoS do?
  6. What does the DURABILITY QoS do?
  7. What is the difference between HISTORY and DURABILITY?
  8. How do I specify a domain ID?
  9. Why aren't my publisher and subscriber discovering each other?
  10. What IDL features are supported?
  11. How do I filter samples I published myself?
  12. What characters are allowed in topic names?
  13. How do I detect when a remote writer or reader goes away?
  14. How do I receive a callback when new data arrives?
  15. What does "Incompatible QoS" mean?
  16. How do I send samples larger than a UDP datagram?
  17. How do I configure which network interface is used?
  18. Can I use Astute DDS on a network without multicast?
  19. How do I tune for low latency?
  20. Why do I see latency spikes when publishing large samples?
  21. How do I bridge two DDS domains?
  22. What language bindings are available?
  23. Which platforms and OS versions are supported?
  24. How do I enable DDS Security?
  25. How do I persist data across restarts?
  26. How do I record and replay traffic?
  27. How do I evolve a data type without breaking subscribers?
  28. How do I use content-filtered topics?
  29. How do I configure QoS from an XML file?
  30. How do I debug interop or discovery issues?

1. What is Astute DDS?

Astute DDS is a C++20 implementation of the OMG Data Distribution Service (DDS) DCPS API, DDSI-RTPS 2.5 wire protocol, DDS-XTypes 1.3 type system, DDS-Security 1.1/1.2 plugins, and IDL 4.2. It is delivered as the static library libastutedds.a (plus C, Python and Rust bindings) and ships with the astutedds-inspect diagnostics GUI and an interoperable Shapes Demo.

2. How do I publish and subscribe to a topic?

Create a DomainParticipant, a Topic, then a DataWriter (publisher side) or DataReader (subscriber side):

using namespace astutedds::dcps;

auto dpf = DomainParticipantFactory::get_instance();
auto dp  = dpf->create_participant(0, PARTICIPANT_QOS_DEFAULT, nullptr, 0);
auto topic = dp->create_topic("Hello", "HelloMsg", TOPIC_QOS_DEFAULT, nullptr, 0);

// Publisher
auto pub    = dp->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr, 0);
auto writer = pub->create_datawriter(topic, DATAWRITER_QOS_DEFAULT, nullptr, 0);
writer->write(sample, HANDLE_NIL);

// Subscriber
auto sub    = dp->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr, 0);
auto reader = sub->create_datareader(topic, DATAREADER_QOS_DEFAULT, nullptr, 0);

See Getting Started › First Application.

3. What is the difference between read() and take()?

  • read() returns samples but leaves them in the reader cache, so later calls (or other readers in the same subscriber) can read them again.
  • take() returns samples and removes them from the cache.

Use read() when the topic represents shared state you want to inspect repeatedly; use take() when the topic represents events you want to consume exactly once.

4. Why don't late-joining subscribers receive earlier samples?

By default DURABILITY is VOLATILE, so samples written before a subscriber is matched are not delivered. To deliver historic samples to late joiners, configure the writer and matching readers with TRANSIENT_LOCAL durability (which also requires RELIABLE reliability):

DataWriterQos qos;
writer->get_qos(qos);
qos.durability().kind   = TRANSIENT_LOCAL_DURABILITY_QOS;
qos.reliability().kind  = RELIABLE_RELIABILITY_QOS;
writer->set_qos(qos);

For persistence across writer restarts use the Persistence Service or the TRANSIENT/PERSISTENT durability kinds.

5. What does the RELIABILITY QoS do?

  • BEST_EFFORT (default for many built-in topics): samples that are lost on the wire are not retransmitted. Lowest overhead.
  • RELIABLE: the writer tracks acknowledgements (ACKNACK / heartbeat) from each matched reader and retransmits missed sequence numbers until the reader catches up or the history is full.

Reliability is per-writer/per-reader and must be compatible: a BEST_EFFORT reader can match a RELIABLE writer, but not the reverse.

6. What does the DURABILITY QoS do?

DURABILITY controls whether samples are retained for delivery to readers that join after the sample was written:

Kind Available to late joiners?
VOLATILE No
TRANSIENT_LOCAL Yes, while the writer is alive
TRANSIENT Yes, across writer lifetime (via Persistence Service)
PERSISTENT Yes, across process restarts (on-disk)

7. What is the difference between HISTORY and DURABILITY?

HISTORY (KEEP_LAST(depth) or KEEP_ALL) controls how many samples the writer/reader cache holds at any instant. DURABILITY controls whether the writer offers samples to readers that match later. They work together but solve different problems: KEEP_ALL alone does not make samples available to late joiners.

8. How do I specify a domain ID?

Pass the domain ID as the first argument to create_participant:

auto dp = DomainParticipantFactory::get_instance()
              ->create_participant(42, PARTICIPANT_QOS_DEFAULT, nullptr, 0);

Participants on different domains do not communicate. The DDSI-RTPS port formula 7400 + 250 * domain_id determines the base UDP ports used for discovery.

9. Why aren't my publisher and subscriber discovering each other?

Common causes:

  1. Different domain IDs — both participants must use the same ID.
  2. Multicast blocked — SPDP uses UDP multicast on 239.255.0.1 by default. Check the host firewall and switch IGMP snooping.
  3. Wrong network interface — on multi-homed hosts set ASTUTEDDS_NETWORK_INTERFACE=<name> or configure it in the discovery JSON / XML profile (see JSON Discovery Config).
  4. Incompatible QoS — see question 15.
  5. Different transports — verify both peers use UDPv4 (or both UDPv6, TCP, SHM).

Enable trace logging with ASTUTEDDS_LOG_LEVEL=trace and inspect a packet capture with astutedds-inspect.

10. What IDL features are supported?

The bundled astutedds-idl compiler implements OMG IDL 4.2 including:

  • Primitive types (boolean, octet, integer types int8uint64, float/double/long double, char/wchar, string/wstring).
  • Structures, unions, enumerations, bitmasks, typedefs.
  • Bounded and unbounded sequences, arrays, maps.
  • @key, @id, @optional, @default, @range, @min, @max, @unit, @extensibility(FINAL|APPENDABLE|MUTABLE), @nested.
  • Constants and module namespaces.

Generated C++ uses XCDR2 by default with optional XCDR1 for interop.

11. How do I filter samples I published myself?

Two options:

  1. Ignore at participant level: call dp->ignore_publication(handle) passing the local writer's InstanceHandle_t.
  2. Filter in the reader callback using SampleInfo::publication_handle and comparing it to your writer's get_instance_handle().

A content-filtered topic can also exclude a self-published key field.

12. What characters are allowed in topic names?

Topic names may contain ASCII alphanumeric characters, _, and / as a hierarchy separator. Length is bounded by 256 characters. Avoid leading digits and reserved DDS built-in prefixes (DCPS, dds). Type names follow the same rule.

13. How do I detect when a remote writer or reader goes away?

Install a DataReaderListener and implement on_subscription_matched / on_liveliness_changed, or poll reader->get_subscription_matched_status(). The current_count_change field is negative when a peer un-matches.

Liveliness is governed by the LIVELINESS QoS — set kind = AUTOMATIC_LIVELINESS_QOS with a lease_duration to detect silent peers within a bounded time.

14. How do I receive a callback when new data arrives?

Attach a listener with the DATA_AVAILABLE_STATUS mask:

class MyListener : public DataReaderListener {
    void on_data_available(DataReader* reader) override {
        // call reader->take(...) here
    }
};
reader->set_listener(&listener, DATA_AVAILABLE_STATUS);

on_data_available fires for both new samples and instance-lifecycle changes; always inspect SampleInfo::valid_data before dereferencing the sample payload.

15. What does "Incompatible QoS" mean?

A writer and reader match only when every Request/Offered QoS is compatible:

Policy Requested ≤ Offered
RELIABILITY reader BEST_EFFORT ≤ writer RELIABLE
DURABILITY reader kind ≤ writer kind (in spec ordering)
DEADLINE reader period ≥ writer period
LATENCY_BUDGET reader duration ≥ writer duration
OWNERSHIP must be equal (SHARED or EXCLUSIVE)
PARTITION at least one partition name must overlap

Listen for on_offered_incompatible_qos / on_requested_incompatible_qos to find the exact policy ID at runtime.

16. How do I send samples larger than a UDP datagram?

Astute DDS automatically fragments samples larger than the configured fragment_size (default 1344 bytes for IPv4) and reassembles them on the reader using DDSI-RTPS DATA_FRAG submessages. The reliability protocol recovers missing fragments without re-sending the whole sample.

For very large samples (≥ 1 MB) increase the OS socket buffers:

sudo sysctl -w net.core.rmem_max=8388608
sudo sysctl -w net.core.wmem_max=8388608

and consider switching to the shared-memory transport for local readers.

17. How do I configure which network interface is used?

Three options, in order of precedence:

  1. Environment variable: ASTUTEDDS_NETWORK_INTERFACE=eth0.
  2. JSON discovery config — see JSON Discovery Config.
  3. XML QoS profile — see DDS-XML QoS Profiles.

If none is set Astute DDS selects the first non-loopback interface with a multicast-capable address.

18. Can I use Astute DDS on a network without multicast?

Yes. Two alternatives:

  • Unicast SPDP peer list: provide a list of peer host/port pairs in the discovery config; Astute DDS unicasts SPDP announcements to each peer.
  • TCP transport: switch the participant to the TCP RTPS transport for environments that block UDP entirely. See DDS Domain Router for crossing isolated networks.

19. How do I tune for low latency?

  • Use RELIABLE reliability with KEEP_LAST(1) history on both ends so retransmits are minimal.
  • Enable the shared-memory transport for co-located participants.
  • Pin Astute DDS threads with CPU affinity and use real-time scheduling (SCHED_FIFO) on the publisher and subscriber processes.
  • Disable Nagle by using UDP (the default).
  • Increase socket buffers (see question 16) to absorb bursts.
  • Set LATENCY_BUDGET = 0 to disable any batching delay.

20. Why do I see latency spikes when publishing large samples?

When samples exceed the OS default socket buffer (often 208 KB on Linux), the kernel drops packets and the reliable protocol must retransmit, causing spikes. Raising net.core.rmem_max / net.core.wmem_max to at least the sample size resolves this. Consider fragmenting with smaller fragment_size on lossy networks.

21. How do I bridge two DDS domains?

Use the Astute DDS Domain Router — a service that joins two or more domains and forwards configured topics between them, with optional key/content filtering and QoS translation. See DDS Domain Router.

22. What language bindings are available?

Language Status Notes
C++20 Stable Primary API — libastutedds.a
C Stable Flat C API for FFI, see include/astutedds/c/astutedds.h
Python Stable pybind11 bindings, CPython 3.10 – 3.13
Rust Beta astutedds crate (safe wrapper over the C API)

23. Which platforms and OS versions are supported?

Platform Tested versions
Linux Ubuntu 22.04 / 24.04, RHEL 9 / 10, AlmaLinux 9 / 10, Debian 12
Windows Windows 10, Windows 11, Windows Server 2022

The Linux Python wheels are tagged manylinux_2_28 (glibc ≥ 2.28) and run on any compatible distribution.

24. How do I enable DDS Security?

  1. Provision identity (X.509) and permissions (signed XML) artefacts.
  2. Configure the participant property QoS with the Auth, Access Control and Crypto plugin properties (CA cert, identity cert/key, permissions file, governance file).
  3. Link against Astute DDS built with -DASTUTEDDS_ENABLE_SECURITY=ON.

See DDS Security for end-to-end instructions and a governance/permissions template.

25. How do I persist data across restarts?

Run the Persistence Service alongside your domain. It subscribes to configured TRANSIENT / PERSISTENT topics, stores samples in a local database (SQLite by default), and re-publishes them to late-joining subscribers — including those that join after the original writer is gone. See Persistence Service.

26. How do I record and replay traffic?

Use the bundled astutedds-record tool to capture a domain into a .astdb file, and astutedds-replay to publish it back with adjustable time scaling. Recording is type-aware and uses XCDR2 internally. See Recording and Replay.

27. How do I evolve a data type without breaking subscribers?

Annotate the IDL type with @extensibility(MUTABLE) (or APPENDABLE) and tag each member with @id(N). XCDR2 mutable streams allow:

  • Adding new members to either end.
  • Removing optional members.
  • Reordering members.

Mismatched members are skipped via the X-Types assignability rules. @extensibility(FINAL) locks the layout and must be used when binary compatibility with legacy XCDR1 readers matters.

28. How do I use content-filtered topics?

Create a ContentFilteredTopic from a regular topic with a SQL-like filter expression and bind a DataReader to it:

std::vector<std::string> params{"42"};
auto cft = dp->create_contentfilteredtopic(
    "HighSeverity", topic, "severity > %0", params);
auto reader = sub->create_datareader(cft, DATAREADER_QOS_DEFAULT, nullptr, 0);

The filter is evaluated on the writer side when possible to save bandwidth.

29. How do I configure QoS from an XML file?

Use XmlQosLoader to read a DDS-XML profile and apply it when creating entities:

XmlQosLoader loader;
loader.load_file("qos_profiles.xml");
auto qos = loader.get_datawriter_qos("MyLib", "ReliableProfile");
auto writer = pub->create_datawriter(topic, qos, nullptr, 0);

See DDS-XML QoS Profiles for the schema and worked examples.

30. How do I debug interop or discovery issues?

  1. Run astutedds-inspect against the domain to view discovered participants, endpoints and matched status in real time.
  2. Capture traffic with tcpdump -i any -w dds.pcap 'udp port 7400 or portrange 7400-7500' and open it in Wireshark — the RTPS dissector decodes SPDP, SEDP and user data.
  3. Enable trace logs: ASTUTEDDS_LOG_LEVEL=trace ASTUTEDDS_LOG_FILE=/tmp/astutedds.log.
  4. Verify entity IDs and ports:
    • SPDP writer/reader: 00:01:00:c2 / 00:01:00:c7
    • SEDP publication: 00:00:03:c2 / 00:00:03:c7
    • SEDP subscription: 00:00:04:c2 / 00:00:04:c7
    • Base port: 7400 + 250 × domain_id.
  5. Confirm both peers advertise compatible vendor IDs and protocol version (DDSI-RTPS 2.5).