RTI Connext

Core Libraries and Utilities

User’s Manual

Part 3 — Advanced Concepts

Chapters 10-21

Version 5.0

© 2012 Real-Time Innovations, Inc.

All rights reserved.

Printed in U.S.A. First printing.

August 2012.

Trademarks

Real-Time Innovations, RTI, DataBus, and Connext are trademarks or registered trademarks of Real-Time Innovations, Inc. All other trademarks used in this document are the property of their respective owners.

Copy and Use Restrictions

No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form (including electronic, mechanical, photocopy, and facsimile) without the prior written permission of Real- Time Innovations, Inc. The software described in this document is furnished under and subject to the RTI software license agreement. The software may be used or copied only under the terms of the license agreement.

Third-Party Copyright Notices

Note: In this section, "the Software" refers to third-party software, portions of which are used in Connext; "the Software" does not refer to Connext.

This product implements the DCPS layer of the Data Distribution Service (DDS) specification version 1.2 and the DDS Interoperability Wire Protocol specification version 2.1, both of which are owned by the Object Management, Inc. Copyright 1997-2007 Object Management Group, Inc. The publication of these specifications can be found at the Catalog of OMG Data Distribution Service (DDS) Specifications. This documentation uses material from the OMG specification for the Data Distribution Service, section 7. Reprinted with permission. Object Management, Inc. © OMG. 2005.

Portions of this product were developed using ANTLR (www.ANTLR.org). This product includes software developed by the University of California, Berkeley and its contributors.

Portions of this product were developed using AspectJ, which is distributed per the CPL license. AspectJ source code may be obtained from Eclipse. This product includes software developed by the University of California, Berkeley and its contributors.

Portions of this product were developed using MD5 from Aladdin Enterprises.

Portions of this product include software derived from Fnmatch, (c) 1989, 1993, 1994 The Regents of the University of California. All rights reserved. The Regents and contributors provide this software "as is" without warranty.

Portions of this product were developed using EXPAT from Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 2001, 2002 Expat maintainers. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

Technical Support

Real-Time Innovations, Inc.

232 E. Java Drive

Sunnyvale, CA 94089

Phone:

(408) 990-7444

Email:

support@rti.com

Website:

https://support.rti.com/

Contents, Part 3

10 Reliable Communications

10-1

10.1 Sending Data Reliably

10-1

10.1.1

Best-effort Delivery Model

10-1

10.1.2

Reliable Delivery Model

10-2

10.2 Overview of the Reliable Protocol

10-3

10.3 Using QosPolicies to Tune the Reliable Protocol

10-6

10.3.1

Enabling Reliability

10-7

 

10.3.1.1

Blocking until the Send Queue Has Space Available

10-7

10.3.2 Tuning Queue Sizes and Other Resource Limits

10-7

 

10.3.2.1

Understanding the Send Queue and Setting its Size

10-8

 

10.3.2.2

Understanding the Receive Queue and Setting Its Size

10-10

10.3.3 Controlling Queue Depth with the History QosPolicy

10-13

10.3.4 Controlling Heartbeats and Retries with DataWriterProtocol QosPolicy

10-13

 

10.3.4.1

How Often Heartbeats are Resent (heartbeat_period)

10-13

 

10.3.4.2 How Often Piggyback Heartbeats are Sent

 

 

 

(heartbeats_per_max_samples)

10-14

 

10.3.4.3

Controlling Packet Size for Resent Samples

 

 

 

(max_bytes_per_nack_response)

10-15

 

10.3.4.4 Controlling How Many Times Heartbeats are Resent

 

 

 

(max_heartbeat_retries)

10-16

 

10.3.4.5

Treating Non-Progressing Readers as Inactive Readers

 

 

 

(inactivate_nonprogressing_readers)

10-17

 

10.3.4.6 Coping with Redundant Requests for Missing Samples

 

 

 

(max_nack_response_delay)

10-17

 

10.3.4.7

Disabling Positive Acknowledgements

 

 

 

(disable_postive_acks_min_sample_keep_duration)

10-18

10.3.5 Avoiding Message Storms with DataReaderProtocol QosPolicy

10-19

10.3.6 Resending Samples to Late-Joiners with the Durability QosPolicy

10-19

10.3.7

Use Cases

10-19

 

10.3.7.1

Importance of Relative Thread Priorities

10-19

 

10.3.7.2

Aperiodic Use Case: One-at-a-Time

10-20

 

10.3.7.3

Aperiodic, Bursty

10-23

 

10.3.7.4

Periodic

10-26

11 Collaborative DataWriters

11-1

11.1 Collaborative DataWriters Use Cases

11-2

iii

 

11.2 Sample Combination (Synchronization) Process in a DataReader

11-3

 

11.3

Configuring Collaborative DataWriters

11-3

 

 

11.3.1

Assocating Virtual GUIDs with Data Samples

11-3

 

 

11.3.2

Assocating Virtual Sequence Numbers with Data Samples

11-3

 

 

11.3.3 Specifying which DataWriters will Deliver Samples to the DataReader

 

 

 

 

from a Logical Data Source

11-3

 

 

11.3.4 Specifying How Long to Wait for a Missing Sample

11-4

 

11.4 Collaborative DataWriters and Persistence Service

11-4

12

Mechanisms for Achieving Information Durability and

 

 

Persistence

 

12-1

 

12.1

Introduction

 

12-1

 

 

12.1.1 Scenario 1. DataReader Joins after DataWriter Restarts (Durable Writer History)

12-2

 

 

12.1.2

Scenario 2: DataReader Restarts While DataWriter Stays Up (Durable Reader State) 12-2

 

 

12.1.3 Scenario 3. DataReader Joins after DataWriter Leaves Domain (Durable Data)

12-3

 

12.2 Durability and Persistence Based on Virtual GUIDs

12-4

 

12.3

Durable Writer History

12-5

 

 

12.3.1 Durable Writer History Use Case

12-5

 

 

12.3.2 How To Configure Durable Writer History

12-6

 

12.4

Durable Reader State

12-8

 

 

12.4.1 Durable Reader State With Protocol Acknowledgment

12-8

 

 

 

12.4.1.1

Bandwidth Utilization

12-9

 

 

12.4.2

Durable Reader State with Application Acknowledgment

12-10

 

 

 

12.4.2.1

Bandwidth Utilization

12-10

 

 

12.4.3 Durable Reader State Use Case

12-10

 

 

12.4.4 How To Configure a DataReader for Durable Reader State

12-11

 

12.5

Data Durability

 

12-12

 

 

12.5.1

RTI Persistence Service

12-12

13

Guaranteed Delivery of Data

13-1

 

13.1

Introduction

 

13-1

 

 

13.1.1 Identifying the Required Consumers of Information

13-2

 

 

13.1.2 Ensuring Consumer Applications Process the Data Successfully

13-3

 

 

13.1.3 Ensuring Information is Available to Late-Joining Applications

13-4

 

13.2

Scenarios

 

13-5

 

 

13.2.1 Scenario 1: Guaranteed Delivery to a-priori known subscribers

13-5

 

 

13.2.2 Scenario 2: Surviving a Writer Restart when Delivering Samples to

 

 

 

 

a priori Known Subscribers

13-7

 

 

13.2.3 Scenario 3: Delivery Guaranteed by Persistence Service (Store and Forward) to

 

 

 

 

a priori Known Subscribers

13-7

 

 

 

13.2.3.1

Variation: Using Redundant Persistence Services

13-9

 

 

 

13.2.3.2

Variation: Using Load-Balanced Persistent Services

13-10

14

Discovery

 

14-1

 

14.1

What is Discovery?

14-1

 

 

14.1.1

Simple Participant Discovery

14-2

 

 

14.1.2

Simple Endpoint Discovery

14-2

iv

 

14.2

Configuring the Peers List Used in Discovery

14-3

 

 

14.2.1

Peer Descriptor Format

14-4

 

 

 

14.2.1.1

Locator Format

14-5

 

 

 

14.2.1.2

Address Format

14-6

 

 

14.2.2 NDDS_DISCOVERY_PEERS Environment Variable Format

14-6

 

 

14.2.3

NDDS_DISCOVERY_PEERS File Format

14-7

 

14.3

Discovery Implementation

14-8

 

 

14.3.1

Participant Discovery

14-8

 

 

 

14.3.1.1

Refresh Mechanism

14-9

 

 

 

14.3.1.2 Maintaining DataWriter Liveliness for kinds AUTOMATIC and

 

 

 

 

 

MANUAL_BY_PARTICIPANT

14-14

 

 

14.3.2

Endpoint Discovery

14-14

 

 

14.3.3

Discovery Traffic Summary

14-20

 

 

14.3.4

Discovery-Related QoS

14-20

 

14.4

Debugging Discovery

14-21

 

14.5

Ports Used for Discovery

14-23

 

 

14.5.1 Inbound Ports for Meta-Traffic

14-24

 

 

14.5.2 Inbound Ports for User Traffic

14-25

 

 

14.5.3 Automatic Selection of participant_id and Port Reservation

14-25

 

 

14.5.4 Tuning domain_id_gain and participant_id_gain

14-25

15

Transport Plugins

15-1

 

15.1

Builtin Transport Plugins

15-2

 

15.2

Extension Transport Plugins

15-2

 

15.3

The NDDSTransportSupport Class

15-3

 

15.4

Explicitly Creating Builtin Transport Plugin Instances

15-3

 

15.5

Setting Builtin Transport Properties of the Default Transport Instance

 

 

 

—get/set_builtin_transport_properties()

15-4

 

15.6

Setting Builtin Transport Properties with the PropertyQosPolicy

15-5

 

 

15.6.1 Notes Regarding Loopback and Shared Memory

15-17

 

 

15.6.2 Setting the Maximum Gather-Send Buffer Count for UDPv4 and UDPv6

15-17

 

 

15.6.3 Formatting Rules for IPv6 ‘Allow’ and ‘Deny’ Address Lists

15-18

 

15.7

Installing Additional Builtin Transport Plugins with register_transport()

15-18

 

 

15.7.1

Transport Lifecycles

15-19

 

 

15.7.2

Transport Aliases

15-19

 

 

15.7.3

Transport Network Addresses

15-20

 

15.8

Installing Additional Builtin Transport Plugins with PropertyQosPolicy

15-20

 

15.9

Other Transport Support Operations

15-21

 

 

15.9.1 Adding a Send Route

15-21

 

 

15.9.2 Adding a Receive Route

15-22

 

 

15.9.3 Looking Up a Transport Plugin

15-23

16

Built-In Topics

 

16-1

 

16.1

Listeners for Built-in Entities

16-1

 

16.2

Built-in DataReaders

16-2

 

 

16.2.1 LOCATOR_FILTER QoS Policy (DDS Extension)

16-7

v

16.3

Accessing the Built-in Subscriber

16-8

16.4

Restricting Communication—Ignoring Entities

16-8

 

16.4.1 Ignoring Specific Remote DomainParticipants

16-9

 

16.4.2

Ignoring Publications and Subscriptions

16-9

 

16.4.3

Ignoring Topics

16-10

17 Configuring QoS with XML

17-1

17.1

Example XML File

17-2

17.2

How to Load XML-Specified QoS Settings

17-2

 

17.2.1

Loading, Reloading and Unloading Profiles

17-3

17.3

How to Use XML-Specified QoS Settings

17-4

17.4

XML File Syntax

17-5

17.5

Using Environment Variables in XML

17-6

17.6

XML String Syntax

17-7

17.7

How the XML is Validated

17-7

 

17.7.1

Validation at Run-Time

17-7

 

17.7.2

XML File Validation During Editing

17-8

17.8

Configuring QoS with XML

17-8

 

17.8.1

QosPolicies

17-9

 

17.8.2

Sequences

17-9

 

17.8.3

Arrays

17-12

 

17.8.4

Enumeration Values

17-12

 

17.8.5

Time Values (Durations)

17-12

 

17.8.6

Transport Properties

17-13

 

17.8.7

Thread Settings

17-13

17.9

QoS Profiles

17-14

 

17.9.1

QoS Profiles with a Single QoS

17-15

 

17.9.2

QoS Profile Inheritance

17-15

 

17.9.3

Topic Filters

17-17

 

17.9.4

Overwriting Default QoS Values

17-19

 

17.9.5

Get Qos Profiles

17-20

17.10

QoS Libraries

17-20

 

17.10.1 Get Qos Profile Libraries

17-21

17.11

URL Groups

17-21

17.12

Configuring Logging Via XML

17-22

18 Multi-channel DataWriters

18-1

18.1

What is a Multi-channel DataWriter?

18-2

18.2

How to Configure a Multi-channel DataWriter

18-4

 

18.2.1 Limitations

18-5

18.3

Multi-channel Configuration on the Reader Side

18-5

18.4

Where Does the Filtering Occur?

18-6

 

18.4.1 Filtering at the DataWriter

18-6

 

18.4.2 Filtering at the DataReader

18-7

 

18.4.3 Filtering on the Network Hardware

18-7

18.5

Fault Tolerance and Redundancy

18-7

vi

18.6

Reliability with Multi-Channel DataWriters

18-8

 

18.6.1

Reliable Delivery

18-8

 

18.6.2

Reliable Protocol Considerations

18-9

18.7

Performance Considerations

18-9

 

18.7.1

Network-Switch Filtering

18-9

 

18.7.2 DataWriter and DataReader Filtering

18-10

19 Connext Threading Model

19-1

19.1

Database Thread

19-1

19.2

Event Thread

19-2

19.3

Receive Threads

19-3

19.4

Exclusive Areas, Connext Threads and User Listeners

19-4

19.5

Controlling CPU Core Affinity for RTI Threads

19-5

20 Sample-Data Memory Management

20-1

20.1 Sample-Data Memory Management for DataWriters

20-1

20.1.1 Memory Management without Batching

20-2

20.1.2 Memory Management with Batching

20-2

20.1.3 Writer-Side Memory Management when Using Java

20-3

20.1.4 Writer-Side Memory Management when Working with Large Data

20-4

20.2 Sample-Data Memory Management for DataReaders

20-6

20.2.1 Memory Management for DataReaders Using Generated Type-Plugins

20-6

20.2.2 Reader-Side Memory Management when Using Java

20-7

20.2.3 Memory Management for DynamicData DataReaders

20-8

20.2.5 Memory Management for Fragmented Samples

20-10

20.2.6 Reader-Side Memory Management when Working with Large Data

20-10

21 Troubleshooting

21-1

21.1 What Version am I Running?

21-1

21.1.1 Finding Version Information in Revision Files

21-1

21.1.2 Finding Version Information Programmatically

21-1

21.2 Controlling Messages from Connext

21-2

21.2.1 Format of Logged Messages

21-4

21.2.1.1

Timestamps

21-4

21.2.1.2

Thread identification

21-5

21.2.1.3

Hierarchical Context

21-5

21.2.1.4

Explanation of Context Strings

21-5

21.2.2 Configuring Logging via XML

21-6

21.2.3 Customizing the Handling of Generated Log Messages

21-7

vii

Chapter 10 Reliable Communications

Connext uses best-effort delivery by default. The other type of delivery that Connext supports is called reliable. This chapter provides instructions on how to set up and use reliable communication.

This chapter includes the following sections:

Sending Data Reliably (Section 10.1)

Overview of the Reliable Protocol (Section 10.2)

Using QosPolicies to Tune the Reliable Protocol (Section 10.3)

10.1Sending Data Reliably

The DCPS reliability model recognizes that the optimal balance between time-determinism and data-delivery reliability varies widely among applications and can vary among different publications within the same application. For example, individual samples of signal data can often be dropped because their value disappears when the next sample is sent. However, each sample of command data must be received and it must be received in the order sent.

The QosPolicies provide a way to customize the determinism/reliability trade-off on a per Topic basis, or even on a per DataWriter/DataReader basis.

There are two delivery models:

Best-effort delivery mode “I’m not concerned about missed or unordered samples.”

Reliable delivery model “Make sure all samples get there, in order.”

10.1.1Best-effort Delivery Model

By default, Connext uses the best-effort delivery model: there is no effort spent ensuring in-order delivery or resending lost samples. Best-effort DataReaders ignore lost samples in favor of the latest sample. Your application is only notified if it does not receive a new sample within a certain time period (set in the DEADLINE QosPolicy (Section 6.5.5)).

The best-effort delivery model is best for time-critical information that is sent continuously. For instance, consider a DataWriter for the value of a sensor device (such as a the pressure inside a tank), and assume the DataWriter sends samples continuously. In this situation, a DataReader for this Topic is only interested in having the latest pressure reading available—older samples are obsolete.

10-1

10.1.2Reliable Delivery Model

Reliable delivery means the samples are guaranteed to arrive, in the order published.

The DataWriter maintains a send queue with space to hold the last X number of samples sent. Similarly, a DataReader maintains a receive queue with space for consecutive X expected samples.

The send and receive queues are used to temporarily cache samples until Connext is sure the samples have been delivered and are not needed anymore. Connext removes samples from a publication’s send queue after the sample has been acknowledged by all reliable subscriptions. When positive acknowledgements are disabled (see DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) and DATA_READER_PROTOCOL QosPolicy (DDS Extension) (Section 7.6.1)), samples are removed from the send queue after the corresponding keep- duration has elapsed (see Table 6.36, “DDS_RtpsReliableWriterProtocol_t,” on page 6-81).

If an out-of-order sample arrives, Connext speculatively caches it in the DataReader’s receive queue (provided there is space in the queue). Only consecutive samples are passed on to the

DataReader.

DataWriters can be set up to wait for available queue space when sending samples. This will cause the sending thread to block until there is space in the send queue. (Or, you can decide to sacrifice sending samples reliably so that the sending rate is not compromised.) If the DataWriter is set up to ignore the full queue and sends anyway, then older cached samples will be pushed out of the queue before all DataReaders have received them. In this case, the DataReader (or its Subscriber) is notified of the missing samples through its Listener and/or Conditions.

Connext automatically sends acknowledgments (ACKNACKs) as necessary to maintain reliable communications. The DataWriter may choose to block for a specified duration to wait for these acknowledgments (see Waiting for Acknowledgments in a DataWriter (Section 6.3.11)).

Connext establishes a virtual reliable channel between the matching DataWriter and all DataReaders. This mechanism isolates DataReaders from each other, allows the application to control memory usage, and provides mechanisms for the DataWriter to balance reliability and determinism. Moreover, the use of send and receive queues allows Connext to be implemented efficiently without introducing unnecessary delays in the stream.

Note that a successful return code (DDS_RETCODE_OK) from write() does not necessarily mean that all DataReaders have received the data. It only means that the sample has been added to the DataWriter’s queue. To see if all DataReaders have received the data, look at the RELIABLE_WRITER_CACHE_CHANGED Status (DDS Extension) (Section 6.3.6.7) to see if any samples are unacknowledged.

Suppose DataWriter A reliably publishes a Topic to which DataReaders B and C reliably subscribe. B has space in its queue, but C does not. Will DataWriter A be notified? Will DataReader C receive any error messages or callbacks? The exact behavior depends on the QoS settings:

If HISTORY_KEEP_ALL is specified for C, C will reject samples that cannot be put into the queue and request A to resend missing samples. The Listener is notified with the on_sample_rejected() callback (see SAMPLE_REJECTED Status (Section 7.3.7.8)). If A has a queue large enough, or A is no longer writing new samples, A won’t notice unless it checks the RELIABLE_WRITER_CACHE_CHANGED Status (DDS Extension) (Section 6.3.6.7).

If HISTORY_KEEP_LAST is specified for C, C will drop old samples and accept new ones. The Listener is notified with the on_sample_lost() callback (see SAMPLE_LOST Status (Section 7.3.7.7)). To A, it is as if all samples have been received by C (that is, they have all been acknowledged).

10-2

10.2Overview of the Reliable Protocol

An important advantage of Connext is that it can offer the reliability and other QoS guarantees mandated by DDS on top of a very wide variety of transports, including packet-based transports, unreliable networks, multicast-capable transports, bursty or high-latency transports, etc. Connext is also capable of maintaining liveliness and application-level QoS even in the presence of sporadic connectivity loss at the transport level, an important benefit in mobile networks. Connext accomplishes this by implementing a reliable protocol that sequences and acknowledges application-level messages and monitors the liveliness of the link. This is called the Real-Time Publish-Subscribe (RTPS) protocol; it is an open, international standard.1

In order to work in this wide range of environments, the reliable protocol defined by RTPS is highly configurable with a set of parameters that let the application fine-tune its behavior to trade-off latency, responsiveness, liveliness, throughput, and resource utilization. This section describes the most important features to the extent needed to understand how the configuration parameters affect its operation.

The most important features of the RTPS protocol are:

Support for both push and pull operating modes

Support for both positive and negative acknowledgments

Support for high data-rate DataWriters

Support for multicast DataReaders

Support for high-latency environments

In order to support these features, RTPS uses several types of messages: Data messages (DATA), acknowledgments (ACKNACKs), and heartbeats (HBs).

DATA messages contain snapshots of the value of data-objects and associate the snapshot with a sequence number that Connext uses to identify them within the DataWriter’s history. These snapshots are stored in the history as a direct result of the application calling write() on the DataWriter. Incremental sequence numbers are automatically assigned by the DataWriter each time write() is called. In Figure 10.1 through Figure 10.7, these messages are represented using the notation DATA(<value>, <sequenceNum>). For example, DATA(A,1) represents a message that communicates the value ‘A’ and associates the sequence number ‘1’ with this message. A DATA is used for both keyed and non-keyed data types.

HB messages announce to the DataReader that it should have received all snapshots up to the one tagged with a range of sequence numbers and can also request the DataReader to send an acknowledgement back. For example, HB(1-3) indicates to the DataReader that it should have received snapshots tagged with sequence numbers 1, 2, and 3 and asks the DataReader to confirm this.

ACKNACK messages communicate to the DataWriter that particular snapshots have been successfully stored in the DataReader’s history. ACKNACKs also tell the DataWriter which snapshots are missing on the DataReader side. The ACKNACK message includes a set of sequence numbers represented as a bit map. The sequence numbers indicate which ones the DataReader is missing. (The bit map contains the base sequence number that has not been received, followed by the number of bits in bit map and the optional bit map.

The maximum size of the bit map is 256.) All numbers up to (not including) those in the set are considered positively acknowledged. They are represented in Figure 10.1 through Figure 10.7 as ACKNACK(<first-missing>) or ACKNACK(<first-missing>-<last-

1. For a link to the RTPS specification, see the RTI website, www.rti.com.

10-3

missing>). For example, ACKNACK(4) indicates that the snapshots with sequence numbers 1, 2, and 3 have been successfully stored in the DataReader history, and that 4 has not been received.

It is important to note that Connext can bundle multiple of the above messages within a single network packet. This ‘submessage bundling’ provides for higher performance communications.

Figure 10.1 Basic RTPS Reliable Protocol

Assigned sequence number

History of send data values

Whether or not the sample has been delivered to the reader history

DataWriter DataReader

write

(A)

1

A

X

 

 

DATA (A,1);

 

 

cache

HB (1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(A, 1)

 

 

cache

 

 

 

 

 

 

 

 

(A, 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

check

(1)

 

 

 

 

 

 

ACKNACK

1

A

4

 

 

 

(2)

 

acked

 

 

 

 

 

 

 

 

 

 

 

 

(1)

 

 

 

 

 

 

 

time

time

 

 

 

 

 

 

 

 

 

 

 

 

 

Assigned sequence number

DataReader history

Whether or not the sample is available for the application to read/take

1 A 4

Figure 10.1 illustrates the basic behavior of the protocol when an application calls the write() operation on a DataWriter that is associated with a DataReader. As mentioned, the RTPS protocol can bundle multiple submessages into a single network packet. In Figure 10.1 this feature is used to piggyback a HB message to the DATA message. Note that before the message is sent, the data is given a sequence number (1 in this case) which is stored in the DataWriter’s send queue. As soon as the message is received by the DataReader, it places it into the DataReader’s receive queue. From the sequence number the DataReader can tell that it has not missed any messages and therefore it can make the data available immediately to the user (and call the DataReaderListener). This is indicated by the “” symbol. The reception of the HB(1) causes the DataReader to check that it has indeed received all updates up to and including the one with sequenceNumber=1. Since this is true, it replies with an ACKNACK(2) to positively acknowledge all messages up to (but not including) sequence number 2. The DataWriter notes that the update has been acknowledged, so it no longer needs to be retained in its send queue. This is indicated by the “” symbol.

Figure 10.2 illustrates the behavior of the protocol in the presence of lost messages. Assume that the message containing DATA(A,1) is dropped by the network. When the DataReader receives

10-4

Figure 10.2 RTPS Reliable Protocol in the Presence of Message Loss

DataWriter

write(S01)

1 A X

cache (A, 1)

D

 

AT

 

A (

 

A,

 

1);

 

HB (1)

DataReader

r

write(S02)

1

A

X

 

 

 

 

cache(B,2)

 

 

 

2

B

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

get(1)

write(S03)

DATA

 

 

(B,2);

 

 

HB (1-2)

 

K(1)

KNAC

 

AC

 

 

D

 

 

AT

 

 

A (

 

 

A,1)

 

1

A

X

cache(C,3)

 

 

 

2

B

X

 

 

 

 

 

3

C

X

 

 

 

 

 

 

 

 

 

 

 

 

 

1

A

4

 

 

 

 

 

2

B

4

 

 

 

 

acked(1-3)

3

C

4

 

 

 

 

 

 

 

 

 

DATA (C,3); HB (1-3)

 

K(4)

KNAC

AC

 

time

 

 

 

 

1

 

X

cache (B,2)

 

 

 

 

2

B

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

A

4

cache (A,1)

 

 

 

2

B

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

A

4

cache (C,3)

 

 

 

2

B

4

 

 

 

 

 

 

 

 

 

 

 

 

 

3

C

4

 

 

 

 

 

 

 

 

 

 

 

 

check(1-3)

 

 

 

time

See Figure 10.1 for

meaning of table columns.

 

the next message (DATA(B,2); HB(1-2)) the DataReader will notice that the data associated with sequence number 1 was never received. It realizes this because the heartbeat HB(1-2) tells the DataReader that it should have received all messages up to and including the one with sequence number 2. This realization has two consequences:

1.The data associated with sequence number 2 (B) is tagged with ‘X’ to indicate that it is not deliverable to the application (that is, it should not be made available to the application, because the application needs to receive the data associated with sample 1

(A) first).

2.An ACKNACK(1) is sent to the DataWriter to request that the data tagged with sequence number 1 be resent.

Reception of the ACKNACK(1) causes the DataWriter to resend DATA(A,1). Once the DataReader receives it, it can ‘commit’ both A and B such that the application can now access both (indicated

10-5

by the “”) and call the DataReaderListener. From there on, the protocol proceeds as before for the next data message (C) and so forth.

A subtle but important feature of the RTPS protocol is that ACKNACK messages are only sent as a direct response to HB messages. This allows the DataWriter to better control the overhead of these ‘administrative’ messages. For example, if the DataWriter knows that it is about to send a chain of DATA messages, it can bundle them all and include a single HB at the end, which minimizes ACKNACK traffic.

10.3Using QosPolicies to Tune the Reliable Protocol

Reliability is controlled by the QosPolicies in Table 10.1. To enable reliable delivery, read the following sections to learn how to change the QoS for the DataWriter and DataReader:

Enabling Reliability (Section 10.3.1)

Tuning Queue Sizes and Other Resource Limits (Section 10.3.2)

Controlling Heartbeats and Retries with DataWriterProtocol QosPolicy (Section 10.3.4)

Avoiding Message Storms with DataReaderProtocol QosPolicy (Section 10.3.5)

Resending Samples to Late-Joiners with the Durability QosPolicy (Section 10.3.6)

Then see this section to explore example use cases:

Use Cases (Section 10.3.7)

Table 10.1 QosPolicies for Reliable Communications

QosPolicy

Description

Related

Reference

Entitiesa

 

 

 

 

 

 

 

 

To establish reliable communication, this QoS must be

 

Section 10.3.1,

Reliability

set to DDS_RELIABLE_RELIABILITY_QOS for the

DW, DR

Section 6.5.19

 

DataWriter and its DataReaders.

 

 

 

 

 

 

 

 

 

This QoS determines the amount of resources each side

 

 

 

can use to manage instances and samples of instances.

 

 

 

Therefore it controls the size of the DataWriter’s send

 

Section 10.3.2,

ResourceLimits

queue and the DataReader’s receive queue. The send

DW, DR

Section 6.5.20

 

queue stores samples until they have been ACKed by

 

 

 

 

 

all DataReaders. The DataReader’s receive queue stores

 

 

 

samples for the user’s application to access.

 

 

 

 

 

 

History

This QoS affects how a DataWriter/DataReader behaves

DW, DR

Section 10.3.3,

 

when its send/receive queue fills up.

 

Section 6.5.10

DataWriterProtocol

This QoS configures DataWriter-specific protocol. The

DW

Section 10.3.4,

 

QoS can disable positive ACKs for its DataReaders.

 

Section 6.5.3

 

When a reliable DataReader receives a heartbeat from a

 

 

 

DataWriter and needs to return an ACKNACK, the

 

Section 10.3.5,

DataReaderProtocol

DataReader can choose to delay a while. This QoS sets

DR

Section 7.6.1

 

the minimum and maximum delay. It can also disable

 

 

 

 

 

positive ACKs for the DataReader.

 

 

 

 

 

 

10-6

Table 10.1 QosPolicies for Reliable Communications

QosPolicy

Description

Related

Reference

Entitiesa

 

 

 

 

 

 

 

 

This QoS determines additional amounts of resources

 

 

 

that the DataReader can use to manage samples

 

 

DataReaderResource-

(namely, the size of the DataReader’s internal queues,

DR

Section 10.3.2,

Limits

which cache samples until they are ordered for reliabil-

 

Section 7.6.2

 

ity and can be moved to the DataReader’s receive queue

 

 

 

for access by the user’s application).

 

 

 

 

 

 

Durability

This QoS affects whether late-joining DataReaders will

DW, DR

Section 10.3.6,

 

receive all previously-sent data or not.

 

Section 6.5.7

a.DW = DataWriter, DR = DataReader

10.3.1Enabling Reliability

You must modify the RELIABILITY QosPolicy (Section 6.5.19) of the DataWriter and each of its reliable DataReaders. Set the kind field to DDS_RELIABLE_RELIABILITY_QOS:

DataWriter

writer_qos.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

DataReader

reader_qos.reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

10.3.1.1Blocking until the Send Queue Has Space Available

The max_blocking_time property in the RELIABILITY QosPolicy (Section 6.5.19) indicates how long a DataWriter can be blocked during a write().

If max_blocking_time is non-zero and the reliability send queue is full, the write is blocked (the sample is not sent). If max_blocking_time has passed and the sample is still not sent, write() returns DDS_RETCODE_TIMEOUT and the sample is not sent.

If the number of unacknowledged samples in the reliability send queue drops below max_samples (set in the RESOURCE_LIMITS QosPolicy (Section 6.5.20)) before max_blocking_time, the sample is sent and write() returns DDS_RETCODE_OK.

If max_blocking_time is zero and the reliability send queue is full, write() returns DDS_RETCODE_TIMEOUT and the sample is not sent.

10.3.2Tuning Queue Sizes and Other Resource Limits

Set the HISTORY QosPolicy (Section 6.5.10) appropriately to accommodate however many samples should be saved in the DataWriter’s send queue or the DataReader’s receive queue. The defaults may suit your needs; if so, you do not have to modify this QosPolicy.

Set the DDS_RtpsReliableWriterProtocol_t in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) appropriately to accommodate the number of unacknowledged samples that can be in-flight at a time from a DataWriter.

For more information, see the following sections:

Understanding the Send Queue and Setting its Size (Section 10.3.2.1)

Understanding the Receive Queue and Setting Its Size (Section 10.3.2.2)

Note: The HistoryQosPolicy’s depth must be less than or equal to the ResourceLimitsQosPolicy’s max_samples_per_instance; max_samples_per_instance must be less than or equal to the ResourceLimitsQosPolicy’s max_samples (see RESOURCE_LIMITS QosPolicy (Section 6.5.20)), and max_samples_per_remote_writer (see

10-7

DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 7.6.2)) must be less than or equal to max_samples.

depth <= max_samples_per_instance <= max_samples

max_samples_per_remote_writer <= max_samples

Examples:

DataWriter

writer_qos.resource_limits.initial_instances = 10; writer_qos.resource_limits.initial_samples = 200; writer_qos.resource_limits.max_instances = 100; writer_qos.resource_limits.max_samples = 2000; writer_qos.resource_limits.max_samples_per_instance = 20; writer_qos.history.depth = 20;

DataReader

reader_qos.resource_limits.initial_instances = 10; reader_qos.resource_limits.initial_samples = 200; reader_qos.resource_limits.max_instances = 100; reader_qos.resource_limits.max_samples = 2000; reader_qos.resource_limits.max_samples_per_instance = 20; reader_qos.history.depth = 20; reader_qos.reader_resource_limits.max_samples_per_remote_writer = 20;

10.3.2.1Understanding the Send Queue and Setting its Size

A DataWriter’s send queue is used to store each sample it writes. A sample will be removed from the send queue after it has been acknowledged (through an ACKNACK) by all the reliable DataReaders. A DataReader can request that the DataWriter resend a missing sample (through an ACKNACK). If that sample is still available in the send queue, it will be resent. To elicit timely ACKNACKs, the DataWriter will regularly send heartbeats to its reliable DataReaders.

A DataWriter’s send queue size is determined by its RESOURCE_LIMITS QosPolicy (Section 6.5.20), specifically the max_samples field. The appropriate value depends on application parameters such as how fast the publication calls write().

A DataWriter has a "send window" that is the maximum number of unacknowledged samples allowed in the send queue at a time. The send window enables configuration of the number of samples queued for reliability to be done independently from the number of samples queued for history. This is of great benefit when the size of the history queue is much different than the size of the reliability queue. For example, you may want to resend a large history to late-joining DataReaders, so the send queue size is large. However, you do not want performance to suffer due to a large send queue; this can happen when the send rate is greater than the read rate, and the DataWriter has to resend many samples from its large historical send queue. If the send queue size was both the historical and reliability queue size, then both these goals could not be met. Now, with the send window, having a large history with good live reliability performance is possible.

The send window is determined by the DataWriterProtocolQosPolicy, specifically the fields min_send_window_size and max_send_window_size within the rtps_reliable_writer field of type DDS_RtpsReliableWriterProtocol_t. Other fields control a dynamic send window, where the send window size changes in response to network congestion to maximize the effective send rate. Like for max_samples, the appropriate values depend on application parameters.

Strict reliability: If a DataWriter does not receive ACKNACKs from one or more reliable DataReaders, it is possible for the reliability send queue—either its finite send window, or max_samples if its send window is infinite—to fill up. If you want to achieve strict reliability, the kind field in the HISTORY QosPolicy (Section 6.5.10) for both the DataReader and DataWriter must be set to KEEP_ALL, positive acknowledgments must be enabled for both the DataReader and DataWriter, and your publishing application should wait until space is available in the

10-8

reliability queue before writing any more samples. Connext provides two mechanisms to do this:

Allow the write() operation to block until there is space in the reliability queue again to store the sample. The maximum time this call blocks is determined by the max_blocking_time field in the RELIABILITY QosPolicy (Section 6.5.19) (also discussed in Section 10.3.1.1).

Use the DataWriter’s Listener to be notified when the reliability queue fills up or empties again.

When the HISTORY QosPolicy (Section 6.5.10) on the DataWriter is set to KEEP_LAST, strict reliability is not guaranteed. When there are depth number of samples in the queue (set in the HISTORY QosPolicy (Section 6.5.10), see Section 10.3.3) the oldest sample will be dropped from the queue when a new sample is written. Note that in such a reliable mode, when the send window is larger than max_samples, the DataWriter will never block, but strict reliability is no longer guaranteed.

If there is a request for the purged sample from any DataReaders, the DataWriter will send a heartbeat that no longer contains the sequence number of the dropped sample (it will not be able to send the sample).

Alternatively, a DataWriter with KEEP_LAST may block on write() when its send window is smaller than its send queue. The DataWriter will block when its send window is full. Only after the blocking time has elapsed, the DataWriter will purge a sample, and then strict reliability is no longer guaranteed.

The send queue size is set in the max_samples field of the RESOURCE_LIMITS QosPolicy (Section 6.5.20). The appropriate size for the send queue depends on application parameters (such as the send rate), channel parameters (such as end-to-end delay and probability of packet loss), and quality of service requirements (such as maximum acceptable probability of sample loss).

The DataReader’s receive queue size should generally be larger than the DataWriter’s send queue size. Receive queue size is discussed in Section 10.3.2.2.

A good rule of thumb, based on a simple model that assumes individual packet drops are not correlated and time-independent, is that the size of the reliability send queue, N, is as shown in Figure 10.3.

Figure 10.3 Calculating Minimum Send Queue Size for a Desired Level of Reliability

NRTlog ( 1 Q)

=2 -------------------------

log ( p)

Simple formula for determining the minimum size of the send queue required for strict reliability.

In the above equation, R is the rate of sending samples, T is the round-trip transmission time, p is the probability of a packet loss in a round trip, and Q is the required probability that a sample is eventually successfully delivered. Of course, network-transport dropouts must also be taken into account and may influence or dominate this calculation.

Table 10.2 gives the required size of the send queue for several common scenarios.

Table 10.2 Required Size of the Send Queue for Different Network Parameters

Qa

pb

Tc

 

Rd

Ne

 

 

 

 

 

 

99%

1%

0.001f sec

100 Hz

 

1

99%

1%

0.001 sec

2000 Hz

 

2

 

 

 

 

 

 

99%

5%

0.001 sec

100 Hz

 

1

 

 

 

 

 

 

99%

5%

0.001 sec

2000 Hz

 

4

 

 

 

 

 

 

99.99%

1%

0.001 sec

100 Hz

 

1

 

 

 

 

 

 

10-9

Table 10.2 Required Size of the Send Queue for Different Network Parameters

Qa

pb

 

Tc

 

Rd

Ne

 

 

 

 

 

 

 

99.99%

1%

0.001 sec

 

2000 Hz

 

6

 

 

 

 

 

 

 

99.99%

5%

0.001 sec

 

100 Hz

 

1

 

 

 

 

 

 

 

99.99%

5%

0.001 sec

 

2000 Hz

 

8

 

 

 

 

 

 

 

a."Q" is the desired level of reliability measured as the probability that any data update will eventually be delivered successfully. In other words, percentage of samples that will be successfully delivered.

b."p" is the probability that any single packet gets lost in the network.

c."T" is the round-trip transport delay in the network

d."R" is the rate at which the publisher is sending updates.

e."N" is the minimum required size of the send queue to accomplish the desired level of reliability "Q".

f.The typical round-trip delay for a dedicated 100 Mbit/second ethernet is about 0.001 seconds.

Note: Packet loss on a network frequently happens in bursts, and the packet loss events are correlated. This means that the probability of a packet being lost is much higher if the previous packet was lost because it indicates a congested network or busy receiver. For this situation, it may be better to use a queue size that can accommodate the longest period of network congestion, as illustrated in Figure 10.4.

Figure 10.4 Calculating Minimum Send Queue Size for Networks with Dropouts

N = RD( Q)

Send queue size as a function of send rate "R" and maximum dropout time D.

In the above equation R is the rate of sending samples, D(Q) is a time such that Q percent of the dropouts are of equal or lesser length, and Q is the required probability that a sample is eventually successfully delivered. The problem with the above formula is that it is hard to determine the value of D(Q) for different values of Q.

For example, if we want to ensure that 99.9% of the samples are eventually delivered successfully, and we know that the 99.9% of the network dropouts are shorter than 0.1 seconds, then we would use N = 0.1*R. So for a rate of 100Hz, we would use a send queue of N = 10; for a rate of 2000Hz, we would use N = 200.

10.3.2.2Understanding the Receive Queue and Setting Its Size

Samples are stored in the DataReader’s receive queue, which is accessible to the user’s application.

A sample is removed from the receive queue after it has been accessed by take(), as described in Accessing Data Samples with Read or Take (Section 7.4.3). Note that read() does not remove samples from the queue.

A DataReader's receive queue size is limited by its RESOURCE_LIMITS QosPolicy (Section 6.5.20), specifically the max_samples field. The storage of out-of-order samples for each DataWriter is also allocated from the DataReader’s receive queue; this sample resource is shared among all reliable DataWriters. That is, max_samples includes both ordered and out-of-order samples.

A DataReader can maintain reliable communications with multiple DataWriters (e.g., in the case of the OWNERSHIP_STRENGTH QosPolicy (Section 6.5.16) setting of SHARED). The maximum number of out-of-order samples from any one DataWriter that can occupy in the receive queue is set in the max_samples_per_remote_writer field of the DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 7.6.2); this value can be used to prevent a single DataWriter from using all the space in the receive queue. max_samples_per_remote_writer must be set to be <= max_samples.

10-10

The DataReader will cache samples that arrive out of order while waiting for missing samples to be resent. (Up to 256 samples can be resent; this limitation is imposed by the wire protocol.) If there is no room, the DataReader has to reject out-of-order samples and request them again later after the missing samples have arrived.

The appropriate size of the receive queue depends on application parameters, such as the DataWriter’s sending rate and the probability of a dropped sample. However, the receive queue size should generally be larger than the send queue size. Send queue size is discussed in Section 10.3.2.1.

Figure 10.5 and Figure 10.6 compare two hypothetical DataReaders, both interacting with the same DataWriter. The queue on the left represents an ordering cache, allocated from receive queue—samples are held here if they arrive out of order. The DataReader in Figure 10.5 on page 10-11 has a sufficiently large receive queue (max_samples) for the given send rate of the DataWriter and other operational parameters. In both cases, we assume that all samples are taken from the DataReader in the Listener callback. (See Accessing Data Samples with Read or Take (Section 7.4.3) for information on take() and related operations.)

In Figure 10.6 on page 10-12, max_samples is too small to cache out-of-order samples for the same operational parameters. In both cases, the DataReaders eventually receive all the samples in order. However, the DataReader with the larger max_samples will get the samples earlier and with fewer transactions. In particular, sample “4” is never resent for the DataReader with the larger queue size.

Figure 10.5 Effect of Receive-Queue Size on Performance: Large Queue Size

DataWriter DataReader

Send Sample “1”

Send Sample “2”

1

Sample

Send Sample “3” “2” lost. Send HeartBeat

max_samples is 4. This also limits the number of unordered samples that can be cached.

Sample 1 is taken

Note: no unordered samples cached

Send Sample “4”

Re-send Sample “2”

C

A

 

 

 

Send Sample “5”

 

 

 

 

 

(

 

 

 

 

 

 

3

 

 

 

 

 

 

 

 

1-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

)

 

 

 

 

 

 

 

 

 

Space reserved for missing sample “2”.

 

 

K

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Samples “3” and “4” are cached

 

 

 

 

 

 

 

3

4

 

 

 

 

 

 

while waiting for missing sample “2”.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

3

4

 

 

 

 

 

 

Samples 2-4 are taken

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

Sample 5 is taken

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10-11

Figure 10.6 Effect of Receive Queue Size on Performance: Small Queue Size

DataWriter DataReader

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Send Sample “1”

 

 

 

 

 

 

 

 

 

 

 

Send Sample “2”

 

 

 

 

 

 

 

 

 

 

 

Send Sample “3”

 

 

 

Sample

 

 

 

 

 

“2” lost

 

 

 

Send Heartbeat

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

H

 

 

 

 

 

 

 

 

 

 

 

 

 

B (

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

 

 

 

 

 

 

3

 

 

 

 

 

 

Send Sample “4”

 

 

 

 

)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(

 

 

 

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

C

 

A

 

 

 

 

 

Re-send Sample “2”

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Send Sample “5”

 

 

 

 

 

 

 

 

 

 

 

Send Heartbeat

 

 

 

 

 

 

 

 

 

 

 

 

 

H

 

 

 

 

 

 

 

 

 

 

 

 

 

B (

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

)

 

 

 

 

 

 

 

 

 

 

(

 

 

 

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

Re-send Sample “4”

 

 

N

 

 

 

 

 

 

CK

 

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

max_samples is 2. This also limits the

 

 

 

 

 

 

 

 

 

 

 

number of unordered samples that

 

 

 

 

 

 

 

 

 

 

 

can be cached.

1

 

 

 

 

 

 

 

 

 

 

Move sample 1 to receive queue.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Note: no unordered samples cached

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Space reserved for missing sample “2”.

 

 

3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3

 

 

 

 

 

Sample “4” must be dropped

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

 

because it does not fit in the queue.

 

 

 

 

 

 

 

 

 

Move samples 2 and 3 to receive queue.

 

3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Space reserved for missing sample “4”.

4

 

 

 

 

 

 

 

 

Move samples 4 and 5 to receive queue.

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

10-12

10.3.3Controlling Queue Depth with the History QosPolicy

If you want to achieve strict reliability, set the kind field in the HISTORY QosPolicy (Section 6.5.10) for both the DataReader and DataWriter to KEEP_ALL; in this case, the depth does not matter.

Or, for non-strict reliability, you can leave the kind set to KEEP_LAST (the default). This will provide non-strict reliability; some samples may not be delivered if the resource limit is reached.

The depth field in the HISTORY QosPolicy (Section 6.5.10) controls how many samples Connext will attempt to keep on the DataWriter’s send queue or the DataReader’s receive queue. For reliable communications, depth should be >= 1. The depth can be set to 1, but cannot be more than the max_samples_per_instance in RESOURCE_LIMITS QosPolicy (Section 6.5.20).

Example:

DataWriter

writer_qos.history.depth = <number of samples to keep in send queue>;

DataReader

reader_qos.history.depth = <number of samples to keep in receive queue>;

10.3.4Controlling Heartbeats and Retries with DataWriterProtocol QosPolicy

In the Connext reliability model, the DataWriter sends data samples and heartbeats to reliable DataReaders. A DataReader responds to a heartbeat by sending an ACKNACK, which tells the DataWriter what the DataReader has received so far.

In addition, the DataReader can request missing samples (by sending an ACKNACK) and the DataWriter will respond by resending the missing samples. This section describes some advanced timing parameters that control the behavior of this mechanism. Many applications do not need to change these settings. These parameters are contained in the

DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

The protocol described in Overview of the Reliable Protocol (Section 10.2) uses very simple rules such as piggybacking HB messages to each DATA message and responding immediately to ACKNACKs with the requested repair messages. While correct, this protocol would not be capable of accommodating optimum performance in more advanced use cases.

This section describes some of the parameters configurable by means of the rtps_reliable_writer structure in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) and how they affect the behavior of the RTPS protocol.

10.3.4.1How Often Heartbeats are Resent (heartbeat_period)

If a DataReader does not acknowledge a sample that has been sent, the DataWriter resends the heartbeat. These heartbeats are resent at the rate set in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3), specifically its heartbeat_period field.

For example, a heartbeat_period of 3 seconds means that if a DataReader does not receive the latest sample (for example, it gets dropped by the network), it might take up to 3 seconds before the DataReader realizes it is missing data. The application can lower this value when it is important that recovery from packet loss is very fast.

The basic approach of sending HB messages as a piggyback to DATA messages has the advantage of minimizing network traffic. However, there is a situation where this approach, by itself, may result in large latencies. Suppose there is a DataWriter that writes bursts of data, separated by relatively long periods of silence. Furthermore assume that the last message in one of the bursts is lost by the network. This is the case shown for message DATA(B, 2) in Figure 10.7. If HBs were only sent piggybacked to DATA messages, the DataReader would not realize it missed the ‘B’ DATA message with sequence number ‘2’ until the DataWriter wrote the next message. This may be a long time if data is written sporadically. To avoid this situation,

10-13

Connext can be configured so that HBs are sent periodically as long as there are samples that have not been acknowledged even if no data is being sent. The period at which these HBs are sent is configurable by setting the rtps_reliable_writer.heartbeat_period field in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

Note that a small value for the heartbeat_period will result in a small worst-case latency if the last message in a burst is lost. This comes at the expense of the higher overhead introduced by more frequent HB messages.

Also note that the heartbeat_period should not be less than the rtps_reliable_reader.heartbeat_suppression_duration in the DATA_READER_PROTOCOL QosPolicy (DDS Extension) (Section 7.6.1); otherwise those HBs will be lost.

Figure 10.7 Use of heartbeat_period

DataWriter

DataReader

write(A)

1 A X

cache (A, 1)

DATA (A,1)

cache (A,1) 1

A 4

write(B)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cache(B,2)

 

 

 

 

 

1

 

A

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DATA (B,2)

 

 

 

2

 

B

X

 

heartbeat peri

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

acked(1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

HB(1-2)

 

 

 

get(2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

K(2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

KNAC

 

 

 

 

 

 

 

 

 

 

 

AC

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A(

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2)

 

 

 

 

 

 

 

 

 

 

 

H

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

B(

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

A

4

 

 

 

 

 

 

 

 

 

 

K(3)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

KNAC

 

2

 

B

4

 

 

 

 

 

 

AC

 

 

 

 

 

 

 

 

 

acked(1-2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

time

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

r

check(1-2)

 

 

 

1

A

4

cache (B,2)

 

 

 

2

B

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

check(1-2)

 

 

 

 

 

 

 

 

 

time

See Figure 10.1 for

 

meaning of table

 

columns.

10.3.4.2How Often Piggyback Heartbeats are Sent (heartbeats_per_max_samples)

A DataWriter will automatically send heartbeats with new samples to request regular ACKNACKs from the DataReader. These are called “piggyback” heartbeats.

10-14

If batching is disabled1: one piggyback heartbeat will be sent every [max_samples2/ heartbeats_per_max_samples] number of samples.

If batching is enabled: one piggyback heartbeat will be sent every [max_batches3/ heartbeats_per_max_samples] number of samples.

Furthermore, one piggyback heartbeat will be sent per send window. If the above calculation is greater than the send window size, then the DataWriter will send a piggyback heartbeat for every [send window size] number of samples.

The heartbeats_per_max_samples field is part of the rtps_reliable_writer structure in the

DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3). If heartbeats_per_max_samples is set equal to max_samples, this means that a heartbeat will be sent with each sample. A value of 8 means that a heartbeat will be sent with every 'max_samples/ 8' samples. Say max_samples is set to 1024, then a heartbeat will be sent once every 128 samples. If you set this to zero, samples are sent without any piggyback heartbeat. The max_samples field is part of the RESOURCE_LIMITS QosPolicy (Section 6.5.20).

Figure 10.1 on page 10-4 and Figure 10.2 on page 10-5 seem to imply that a HB is sent as a piggyback to each DATA message. However, in situations where data is sent continuously at high rates, piggybacking a HB to each message may result in too much overhead; not so much on the HB itself, but on the ACKNACKs that would be sent back as replies by the DataReader.

There are two reasons to send a HB:

To request that a DataReader confirm the receipt of data via an ACKNACK, so that the

DataWriter can remove it from its send queue and therefore prevent the DataWriter’s history from filling up (which could cause the write() operation to temporarily block4).

To inform the DataReader of what data it should have received, so that the DataReader can send a request for missing data via an ACKNACK.

The DataWriter’s send queue can buffer many data-samples while it waits for ACKNACKs, and the DataReader’s receive queue can store out-of-order samples while it waits for missing ones. So it is possible to send HB messages much less frequently than DATA messages. The ratio of piggyback HB messages to DATA messages is controlled by the rtps_reliable_writer.heartbeats_per_max_samples field in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

A HB is used to get confirmation from DataReaders so that the DataWriter can remove acknowledged samples from the queue to make space for new samples. Therefore, if the queue size is large, or new samples are added slowly, HBs can be sent less frequently.

In Figure 10.8 on page 10-16, the DataWriter sets the heartbeats_per_max_samples to certain value so that a piggyback HB will be sent for every three samples. The DataWriter first writes sample A and B. The DataReader receives both. However, since no HB has been received, the DataReader won’t send back an ACKNACK. The DataWriter will still keep all the samples in its queue. When the DataWriter sends sample C, it will send a piggyback HB along with the sample. Once the DataReader receives the HB, it will send back an ACKNACK for samples up to sequence number 3, such that the DataWriter can remove all three samples from its queue. ,

10.3.4.3Controlling Packet Size for Resent Samples (max_bytes_per_nack_response)

A repair packet is the maximum amount of data that a DataWriter will resend at a time. For example, if the DataReader requests 20 samples, each 10K, and the max_bytes_per_nack_response is set to 100K, the DataWriter will only send the first 10 samples. The DataReader will have to ACKNACK again to receive the next 10 samples.

1.Batching is enabled with the BATCH QosPolicy (DDS Extension) (Section 6.5.2).

2.max_samples is set in the RESOURCE_LIMITS QosPolicy (Section 6.5.20)

3.max_batches is set in the DATA_WRITER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 6.5.4)

4.Note that data could also be removed from the DataWriter’s send queue if it is no longer relevant due to some other QoS such a HISTORY KEEP_LAST (Section 6.5.10) or LIFESPAN (Section 6.5.12).

10-15

Figure 10.8 Use of heartbeats_per_max_samples

DataWriter

DataReader

write(A)

1 A X cache (A, 1)

write(B)

DATA (A,1)

cache (A,1)

 

1

A

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cache(B,2)

 

 

1

A

X

 

 

 

 

 

 

 

 

 

2

B

X

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

write(C)

DATA(B,2)

cache (B,2)

 

1

A

4

 

 

 

 

 

 

 

2

B

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

A

X

 

 

 

2

B

X

 

 

 

3

C

X

 

 

 

 

 

 

1

A

4

 

 

 

2

B

4

 

 

 

3

C

4

 

 

 

 

 

 

cache(C,3)

D

 

AT

 

A(

 

C,3);H

 

B(1-3)

1 A 4

cache (C,3)

2 B 4 check(1-3) 3 C 4

acked(1-3)

 

 

 

 

4)

 

 

 

K(

 

 

AC

 

 

KN

 

 

 

AC

 

 

 

time

 

 

 

time

See Figure 10.1 for meaning of table columns.

A DataWriter may resend multiple missed samples in the same packet. The max_bytes_per_nack_response field in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) limits the size of this ‘repair’ packet.

10.3.4.4Controlling How Many Times Heartbeats are Resent (max_heartbeat_retries)

If a DataReader does not respond within max_heartbeat_retries number of heartbeats, it will be dropped by the DataWriter and the reliable DataWriter’s Listener will be called with a

RELIABLE_READER_ACTIVITY_CHANGED Status (DDS Extension) (Section 6.3.6.8).

If the dropped DataReader becomes available again (perhaps its network connection was down temporarily), it will be added back to the DataWriter the next time the DataWriter receives some message (ACKNACK) from the DataReader.

When a DataReader is ‘dropped’ by a DataWriter, the DataWriter will not wait for the DataReader to send an ACKNACK before any samples are removed. However, the DataWriter will still send data and HBs to this DataReader as normal.

10-16

The max_heartbeat_retries field is part of the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

10.3.4.5Treating Non-Progressing Readers as Inactive Readers (inactivate_nonprogressing_readers)

In addition to max_heartbeat_retries, if inactivate_nonprogressing_readers is set, then not only are non-responsive DataReaders considered inactive, but DataReaders sending non-progressing NACKs can also be considered inactive. A non-progressing NACK is one which requests the same oldest sample as the previously received NACK. In this case, the DataWriter will not consider a non-progressing NACK as coming from an active reader, and hence will inactivate the DataReader if no new NACKs are received before max_heartbeat_retries number of heartbeat periods has passed.

One example for which it could be useful to turn on inactivate_nonprogressing_readers is when a DataReader’s (keep-all) queue is full of untaken historical samples. Each subsequent heartbeat would trigger the same NACK, and nominally the DataReader would not be inactivated. A user not requiring strict-reliability could consider setting inactivate_nonprogressing_readers to allow the DataWriter to progress rather than being held up by this non-progressing DataReader.

10.3.4.6Coping with Redundant Requests for Missing Samples (max_nack_response_delay)

When a DataWriter receives a request for missing samples from a DataReader and responds by resending the requested samples, it will ignore additional requests for the same samples during the time period max_nack_response_delay.

The rtps_reliable_writer.max_nack_response_delay field is part of the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

If your send period is smaller than the round-trip delay of a message, this can cause unnecessary sample retransmissions due to redundant ACKNACKs. In this situation, an ACKNACK triggered by an out-of-order sample is not received before the next sample is sent. When a DataReader receives the next message, it will send another ACKNACK for the missing sample. As illustrated in Figure 10.9 on page 10-18, duplicate ACKNACK messages cause another resending of missing sample “2” and lead to wasted CPU usage on both the publication and the subscription sides.

While these redundant messages provide an extra cushion for the level of reliability desired, you can conserve the CPU and network bandwidth usage by limiting how often the same ACKNACK messages are sent; this is controlled by min_nack_response_delay.

Reliable subscriptions are prevented from resending an ACKNACK within min_nack_response_delay seconds from the last time an ACKNACK was sent for the same sample. Our testing shows that the default min_nack_response_delay of 0 seconds achieves an optimal balance for most applications on typical Ethernet LANs.

However, if your system has very slow computers and/or a slow network, you may want to consider increasing min_nack_response_delay. Sending an ACKNACK and resending a missing sample inherently takes a long time in this system. So you should allow a longer time for recovery of the lost sample before sending another ACKNACK. In this situation, you should increase min_nack_response_delay.

If your system consists of a fast network or computers, and the receive queue size is very small, then you should keep min_nack_response_delay very small (such as the default value of 0). If the queue size is small, recovering a missing sample is more important than conserving CPU and network bandwidth (new samples that are too far ahead of the missing sample are thrown away). A fast system can cope with a smaller min_nack_response_delay value, and the reliable sample stream can normalize more quickly.

10-17

Figure 10.9 Resending Missing Samples due to Duplicate ACKNACKs

DataWriter DataReader

Send Sample “1”

Send Sample “2”

1

Send Sample “3”

Send Sample “4”

Resend Sample “2” Send Sample “5”

Resend Sample “2”

 

 

 

 

 

 

 

 

)

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

(

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

N

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

)

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

(

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

N

 

 

 

 

 

 

 

 

K

 

 

 

 

 

 

 

 

C

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3

3 4

2 3 4

5

Space must be reserved for missing sample “2”.

Samples “3” and “4” are cached while waiting for missing sample “2”.

Sample “2” is dropped since it is older than the last sample that has been handed to the application.

10.3.4.7Disabling Positive Acknowledgements (disable_postive_acks_min_sample_keep_duration)

When ACKNACK storms are a primary concern in a system, an alternative to tuning heartbeat and ACKNACK response delays is to disable positive acknowledgments (ACKs) and rely just on NACKs to maintain reliability. Systems with non-strict reliability requirements can disable ACKs to reduce network traffic and directly solve the problem of ACK storms. ACKs can be disabled for the DataWriter and the DataReader; when disabled for the DataWriter, none of its DataReaders will send ACKs, whereas disabling it at the DataReader allows per-DataReader configuration.

Normally when ACKs are enabled, strict reliability is maintained by the DataWriter, guaranteeing that a sample stays in its send queue until all DataReaders have positively acknowledged it (aside from relevant DURABILITY, HISTORY, and LIFESPAN QoS policies). When ACKs are disabled, strict reliability is no longer guaranteed, but the DataWriter should still keep the sample for a sufficient duration for ACK-disabled DataReaders to have a chance to NACK it. Thus, a configurable “keep-duration” (disable_postive_acks_min_sample_keep_duration) applies for samples written for ACK- disabled DataReaders, where samples are kept in the queue for at least that keep-duration. After the keep-duration has elapsed for a sample, the sample is considered to be “acknowledged” by its ACK-disabled DataReaders.

The keep duration should be configured for the expected worst-case from when the sample is written to when a NACK for the sample could be received. If set too short, the sample may no longer be queued when a NACK requests it, which is the cost of not enforcing strict reliability.

If the peak send rate is known and writer resources are available, the writer queue can be sized so that writes will not block. For this case, the queue size must be greater than the send rate multiplied by the keep duration.

10-18

10.3.5Avoiding Message Storms with DataReaderProtocol QosPolicy

DataWriters send data samples and heartbeats to DataReaders. A DataReader responds to a heartbeat by sending an acknowledgement that tells the DataWriter what the DataReader has received so far and what it is missing. If there are many DataReaders, all sending ACKNACKs to the same DataWriter at the same time, a message storm can result. To prevent this, you can set a delay for each DataReader, so they don’t all send ACKNACKs at the same time. This delay is set in the DATA_READER_PROTOCOL QosPolicy (DDS Extension) (Section 7.6.1).

If you have several DataReaders per DataWriter, varying this delay for each one can avoid ACKNACK message storms to the DataWriter. If you are not concerned about message storms, you do not need to change this QosPolicy.

Example:

reader_qos.protocol.rtps_reliable_reader.min_heartbeat_response_delay.sec = 0; reader_qos.protocol.rtps_reliable_reader.min_heartbeat_response_delay.nanosec = 0; reader_qos.protocol.rtps_reliable_reader.max_heartbeat_response_delay.sec = 0; reader_qos.protocol.rtps_reliable_reader.max_heartbeat_response_delay.nanosec =

0.5 * 1000000000UL; // 0.5 sec

As the name suggests, the minimum and maximum response delay bounds the random wait time before the response. Setting both to zero will force immediate response, which may be necessary for the fastest recovery in case of lost samples.

10.3.6Resending Samples to Late-Joiners with the Durability QosPolicy

The DURABILITY QosPolicy (Section 6.5.7) is also somewhat related to Reliability. Connext requires a finite time to "discover" or match DataReaders to DataWriters. If an application attempts to send data before the DataReader and DataWriter "discover" one another, then the sample will not actually get sent. Whether or not samples are resent when the DataReader and DataWriter eventually "discover" one another depends on how the DURABILITY and HISTORY QoS are set. The default setting for the Durability QosPolicy is VOLATILE, which means that the DataWriter will not store samples for redelivery to late-joining DataReaders.

Connext also supports the TRANSIENT_LOCAL setting for the Durability, which means that the samples will be kept stored for redelivery to late-joining DataReaders, as long as the DataWriter is around and the RESOURCE_LIMITS QosPolicy (Section 6.5.20) allows. The samples are not stored beyond the lifecycle of the DataWriter.

See also: Waiting for Historical Data (Section 7.3.6).

10.3.7Use Cases

This section contains advanced material that discusses practical applications of the reliability related QoS.

10.3.7.1Importance of Relative Thread Priorities

For high throughput, the Connext Event thread’s priority must be sufficiently high on the sending application. Unlike an unreliable writer, a reliable writer relies on internal Connext threads: the Receive thread processes ACKNACKs from the DataReaders, and the Event thread schedules the events necessary to maintain reliable data flow.

When samples are sent to the same or another application on the same host, the Receive thread priority should be higher than the writing thread priority (priority of the thread calling write() on the DataWriter). This will allow the Receive thread to process the messages as they are sent by the writing thread. A sustained reliable flow requires the reader to be able to process the samples from the writer at a speed equal to or faster than the writer emits.

10-19

The default Event thread priority is low. This is adequate if your reliable transfer is not sustained; queued up events will eventually be processed when the writing thread yields the CPU. The Connext can automatically grow the event queue to store all pending events. But if the reliable communication is sustained, reliable events will continue to be scheduled, and the event queue will eventually reach its limit. The default Event thread priority is unsuitable for maintaining a fast and sustained reliable communication and should be increased through the participant_qos.event.thread.priority. This value maps directly to the OS thread priority, see EVENT QosPolicy (DDS Extension) (Section 8.5.5)).

The Event thread should also be increased to minimize the reliable latency. If events are processed at a higher priority, dropped packets will be resent sooner.

Now we consider some practical applications of the reliability related QoS:

Aperiodic Use Case: One-at-a-Time (Section 10.3.7.2)

Aperiodic, Bursty (Section 10.3.7.3)

Periodic (Section 10.3.7.4)

10.3.7.2Aperiodic Use Case: One-at-a-Time

Suppose you have aperiodically generated data that needs to be delivered reliably, with minimum latency, such as a series of commands (“Ready,” “Aim,” “Fire”). If a writing thread may block between each sample to guarantee reception of the just sent sample on the reader’s middleware end, a smaller queue will provide a smaller upper bound on the sample delivery time. Adequate writer QoS for this use case are presented in Figure 10.10.

Figure 10.10 QoS for an Aperiodic, One-at-a-time Reliable Writer

1 qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3qos->protocol.push_on_write = DDS_BOOLEAN_TRUE;

5//use these hard coded value unless you use a key

6qos->resource_limits.initial_samples = qos->resource_limits.max_samples = 1;

7qos->resource_limits.max_samples_per_instance =

8qos->resource_limits.max_samples;

9qos->resource_limits.initial_instances =

10qos->resource_limits.max_instances = 1;

12// want to piggyback HB w/ every sample.

13qos->protocol.rtps_reliable_writer.heartbeats_per_max_samples =

14qos->resource_limits.max_samples;

15

16qos->protocol.rtps_reliable_writer.high_watermark = 1;

17qos->protocol.rtps_reliable_writer.low_watermark = 0;

18qos->protocol.rtps_reliable_writer.min_nack_response_delay.sec = 0;

19qos->protocol.rtps_reliable_writer.min_nack_response_delay.nanosec = 0;

20//consider making non-zero for reliable multicast

21qos->protocol.rtps_reliable_writer.max_nack_response_delay.sec = 0;

22qos->protocol.rtps_reliable_writer.max_nack_response_delay.nanosec = 0;

24// should be faster than the send rate, but be mindful of OS resolution

25qos->protocol.rtps_reliable_writer.fast_heartbeat_period.sec = 0;

26qos->protocol.rtps_reliable_writer.fast_heartbeat_period.nanosec =

27alertReaderWithinThisMs * 1000000;

29qos->reliability.max_blocking_time = blockingTime;

30qos->protocol.rtps_reliable_writer.max_heartbeat_retries = 7;

32// essentially turn off slow HB period

33qos->protocol.rtps_reliable_writer.heartbeat_period.sec = 3600 * 24 * 7;

10-20

Line 1 (Figure 10.10): This is the default setting for a writer, shown here strictly for clarity.

Line 2 (Figure 10.10): Setting the History kind to KEEP_ALL guarantees that no sample is ever lost.

Line 3 (Figure 10.10): This is the default setting for a writer, shown here strictly for clarity. ‘Push’ mode reliability will yield lower latency than ‘pull’ mode reliability in normal situations where there is no sample loss. (See DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).) Furthermore, it does not matter that each packet sent in response to a command will be small, because our data sent with each command is likely to be small, so that maximizing throughput for this data is not a concern.

Line 5 - Line 10 (Figure 10.10): For this example, we assume a single writer is writing samples one at a time. If we are not using keys (see Section 2.2.2), there is no reason to use a queue with room for more than one sample, because we want to resolve a sample completely before moving on to the next. While this negatively impacts throughput, it minimizes memory usage. In this example, a written sample will remain in the queue until it is acknowledged by all active readers (only 1 for this example).

Line 12 - Line 14 (Figure 10.10): The fastest way for a writer to ensure that a reader is up-to-date is to force an acknowledgement with every sample. We do this by appending a Heartbeat with every sample. This is akin to a certified mail; the writer learns—as soon as the system will allow—whether a reader has received the letter, and can take corrective action if the reader has not. As with certified mail, this model has significant overhead compared to the unreliable case, trading off lower packet efficiency in favor of latency and fast recovery.

Line 16-Line 17 (Figure 10.10): Since the writer takes responsibility for pushing the samples out to the reader, a writer will go into a “heightened alert” mode as soon as the high water mark is reached (which is when any sample is written for this writer) and only come out of this mode when the low water mark is reached (when all samples have been acknowledged for this writer). Note that the selected high and low watermarks are actually the default values.

Line 18-Line 22 (Figure 10.10): When a reader requests a lost sample, we respond to the reader immediately in the interest of faster recovery. If the readers receive packets on unicast, there is no reason to wait, since the writer will eventually have to feed individual readers separately anyway. In case of multicast readers, it makes sense to consider further. If the writer delayed its response enough so that all or most of the readers have had a chance to NACK a sample, the writer may coalesce the requests and send just one packet to all the multicast readers. Suppose that all multicast readers do indeed NACK within approximately 100 sec. Setting the minimum and maximum delays at 100 sec will allow the writer to collect all these NACKs and send a single response over multicast. (See DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) for information on setting min_nack_response_delay and max_nack_response_delay.) Note that Connext relies on the OS to wait for this 100 sec. Unfortunately, not all operating systems can sleep for such a fine duration. On Windows systems, for example, the minimum achievable sleep time is somewhere between 1 to 20 milliseconds, depending on the version. On VxWorks systems, the minimum resolution of the wait time is based on the tick resolution, which is 1/system clock rate (thus, if the system clock rate is 100 Hz, the tick resolution is 10 millisecond). On such systems, the achievable minimum wait is actually far larger than the desired wait time. This could have an unintended consequence due to the delay caused by the OS; at a minimum, the time to repair a packet may be longer than you specified.

Line 24-Line 27 (Figure 10.10): If a reader drops a sample, the writer recovers by notifying the reader of what it has sent, so that the reader may request resending of the lost sample. Therefore, the recovery time depends primarily on how quickly the writer pings the reader that has fallen behind. If commands will not be generated faster than one every few seconds, it may be acceptable for the writer to ping the reader several hundred milliseconds after the sample is sent.

10-21

Suppose that the round-trip time of fairly small packets between the writer and the reader application is 50 microseconds, and that the reader does not delay response to a Heartbeat from the writer (see DATA_READER_PROTOCOL QosPolicy (DDS Extension) (Section 7.6.1) for how to change this). If a sample is dropped, the writer will ping the reader after a maximum of the OS delay resolution discussed above and alertReaderWithinThisMs (let’s say 10 ms for this example). The reader will request the missing sample immediately, and with the code set as above, the writer will feed the missing sample immediately. Neglecting the processing time on the writer or the reader

end, and assuming that this retry succeeds, the time to recover the sample from the original publication time is: alertReaderWithinThisMs + 50 sec + 25 sec.

If the OS is capable of micro-sleep, the recovery time can be within 100 sec, barely noticeable to a human operator. If the OS minimum wait resolution is much larger, the recovery time is dominated by the wait resolution of the OS. Since ergonomic studies suggest that delays in excess of a 0.25 seconds start hampering operations that require low latency data, even a 10 ms limitation seems to be acceptable.

What if two packets are dropped in a row? Then the recovery time would be

2 * alertReaderWithinThisMs + 2 * 50 sec + 25 sec. If alertReaderWithinThisMs is 100 ms, the recovery time now exceeds 200 ms, and can perhaps degrade user experience.

Line 29-Line 30 (Figure 10.10): What if another command (like another button press) is issued before the recovery? Since we must not drop this new sample, we block the writer until the recovery completes. If alertReaderWithinThisMs is 10 ms, and we assume no more than 7 consecutive drops, the longest time for recovery will be just above (alertReaderWithinThisMs * max_heartbeat_retries), or 70 ms.

So if we set blockingTime to about 80 ms, we will have given enough chance for recovery. Of course, in a dynamic system, a reader may drop out at any time, in which case max_heartbeat_retries will be exceeded, and the unresponsive reader will be dropped by the writer. In either case, the writer can continue writing. Inappropriate values will cause a writer to prematurely drop a temporarily unresponsive (but otherwise healthy) reader, or be stuck trying unsuccessfully to feed a crashed reader. In the unfortunate case where a reader becomes temporarily unresponsive for a duration exceeding (alertReaderWithinThisMs * max_heartbeat_retries), the writer may issue gaps to that reader when it becomes active again; the dropped samples are irrecoverable. So estimating the worst case unresponsive time of all potential readers is critical if sample drop is unacceptable.

Line 32-Line 33 (Figure 10.10): Since the command may not be issued for hours or even days on end, there is no reason to keep announcing the writer’s state to the readers.

Figure 10.11 shows how to set the QoS for the reader side, followed by a line-by-line explanation.

Figure 10.11 QoS for an Aperiodic, One-at-a-time Reliable Reader

1qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3

4// 1 is ok for normal use. 2 allows fast infinite loop

5qos->reader_resource_limits.max_samples_per_remote_writer = 2;

6qos->resource_limits.initial_samples = 2;

7qos->resource_limits.initial_instances = 1;

9qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.sec = 0;

10qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.nanosec = 0;

11qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.sec = 0;

12qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.nanosec = 0;

Line 1-Line 2 (Figure 10.11): Unlike a writer, the reader’s default reliability setting is best-effort, so reliability must be turned on. Since we don’t want to drop anything, we choose KEEP_ALL history.

10-22

Line 4-Line 6 (Figure 10.11): Since we enforce reliability on each sample, it would be sufficient to keep the queue size at 1, except in the following case: suppose that the reader takes some action in response to the command received, which in turn causes the writer to issue another command right away. Because Connext passes the user data up to the application even before acknowledging the sample to the writer (for minimum latency), the first sample is still pending for acknowledgement in the writer’s queue when the writer attempts to write the second sample, and will cause the writing thread to block until the reader completes processing the first sample and acknowledges it to the writer; all are as they should be. But if you want to run this infinite loop at full throttle, the reader should buffer one more sample. Let’s follow the packets flow under a normal circumstance:

1.The sender application writes sample 1 to the reader. The receiver application processes it and sends a user-level response 1 to the sender application, but has not yet ACK’d sample 1.

2.The sender application writes sample 2 to the receiving application in response to response 1. Because the reader’s queue is 2, it can accept sample 2 even though it may not yet have acknowledged sample 1. Otherwise, the reader may drop sample 2, and would have to recover it later.

3.At the same time, the receiver application acknowledges sample 1, and frees up one slot in the queue, so that it can accept sample 3, which it on its way.

The above steps can be repeated ad-infinitum in a continuous traffic.

Line 7 (Figure 10.11): Since we are not using keys, there is just one instance.

Line 9-Line 12 (Figure 10.11): We choose immediate response in the interest of fastest recovery. In high throughput, multicast scenario, delaying the response (with event thread priority set high of course) may decrease the likelihood of NACK storm causing a writer to drop some NACKs. This random delay reduces this chance by staggering the NACK response. But the minimum delay achievable once again depends on the OS.

10.3.7.3Aperiodic, Bursty

Suppose you have aperiodically generated bursts of data, as in the case of a new aircraft approaching an airport. The data may be the same or different, but if they are written by a single writer, the challenge to this writer is to feed all readers as quickly and efficiently as possible when this burst of hundreds or thousands of samples hits the system.

If you use an unreliable writer to push this burst of data, some of them may be dropped over an unreliable transport such as UDP.

If you try to shape the burst according to however much the slowest reader can process, the system throughput may suffer, and places an additional burden of queueing the samples on the sender application.

If you push the data reliably as fast they are generated, this may cost dearly in repair packets, especially to the slowest reader, which is already burdened with application chores.

Connext pull mode reliability offers an alternative in this case by letting each reader pace its own data stream. It works by notifying the reader what it is missing, then waiting for it to request only as much as it can handle. As in the aperiodic one-at-a-time case (Section 10.3.7.2), multicast is supported, but its performance depends on the resolution of the minimum delay supported by the OS. At the cost of greater latency, this model can deliver reliability while using far fewer packets than in the push mode. The writer QoS is given in Figure 10.12, with a line-by-line explanation below.

Line 1 (Figure 10.12): This is the default setting for a writer, shown here strictly for clarity.

Line 2 (Figure 10.12): Since we do not want any data lost, we want the History kind set to KEEP_ALL.

10-23

Figure 10.12 QoS for an Aperiodic, Bursty Writer

1 qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3qos->protocol.push_on_write = DDS_BOOLEAN_FALSE;

5//use these hard coded value until you use key

6qos->resource_limits.initial_instances =

7qos->resource_limits.max_instances = 1;

8qos->resource_limits.initial_samples = qos->resource_limits.max_samples

9= worstBurstInSample;

10qos->resource_limits.max_samples_per_instance =

11 qos->resource_limits.max_samples;

12

13// piggyback HB not used

14qos->protocol.rtps_reliable_writer.heartbeats_per_max_samples = 0;

16qos->protocol.rtps_reliable_writer.high_watermark = 1;

17qos->protocol.rtps_reliable_writer.low_watermark = 0;

19qos->protocol.rtps_reliable_writer.min_nack_response_delay.sec = 0;

20qos->protocol.rtps_reliable_writer.min_nack_response_delay.nanosec = 0;

21qos->protocol.rtps_reliable_writer.max_nack_response_delay.sec = 0;

22qos->protocol.rtps_reliable_writer.max_nack_response_delay.nanosec = 0;

23qos->reliability.max_blocking_time = blockingTime;

25// should be faster than the send rate, but be mindful of OS resolution

26qos->protocol.rtps_reliable_writer.fast_heartbeat_period.sec = 0;

27qos->protocol.rtps_reliable_writer.fast_heartbeat_period.nanosec =

28alertReaderWithinThisMs * 1000000;

29qos->protocol.rtps_reliable_writer.max_heartbeat_retries = 5;

31// essentially turn off slow HB period

32qos->protocol.rtps_reliable_writer.heartbeat_period.sec = 3600 * 24 * 7;

Line 3 (Figure 10.12): The default Connext reliable writer will push, but we want the reader to pull instead.

Line 5-Line 11 (Figure 10.12): We assume a single instance, in which case the maximum sample count will be the same as the maximum sample count per writer. In contrast to the one-at-a-time case discussed in Section 10.3.7.2, the writer’s queue is large; as big as the burst size in fact, but no more because this model tries to resolve a burst within a reasonable period, to be computed shortly. Of course, we could block the writing thread in the middle of the burst, but that might complicate the design of the sending application.

Line 13-Line 14 (Figure 10.12): By a ‘piggyback’ Heartbeat, we mean only a Heartbeat that is appended to data being pushed from the writer. Strictly speaking, the writer will also append a Heartbeat with each reply to a reader’s lost sample request, but we call that a ‘framing’ Heartbeat. Since data is pulled, heartbeats_per_max_samples is ignored.

Line 16-Line 17 (Figure 10.12): Similar to the previous aperiodic writer, this writer spends most of its time idle. But as the name suggests, even a single new sample implies more sample to follow in a burst. Putting the writer into a fast mode quickly will allow readers to be notified soon. Only when all samples have been delivered, the writer can rest.

Line 19- Line 23 (Figure 10.12): Similar to the one-at-a-time case, there is no reason to delay response with only one reader. In this case, we can estimate the time to resolve a burst with only a few parameters. Let’s say that the reader figures it can safely receive and process 20 samples at a time without being overwhelmed, and that the time it takes a writer to fetch these 20 samples and send a single packet containing these 20 samples, plus the time it takes a reader to receive and process these sample samples, and send another request back to the writer for the next 20 samples is 11 ms. Even on the same hardware, if the reader’s processing time can be reduced,

10-24

this time will decrease; other factors such as the traversal time through Connext and the transport are typically in microseconds range (depending on machines of course).

For example, let’s also say that the worst case burst is 1000 samples. The writing thread will of course not block because it is merely copying each of the 1000 samples to the Connext queue on the writer side; on a typical modern machine, the act of writing these 1000 samples will probably take no more than a few ms. But it would take at least 1000/20 = 50 resend packets for the reader to catch up to the writer, or 50 times 11 ms = 550 ms. Since the burst model deals with one burst at a time, we would expect that another burst would not come within this time, and that we are allowed to block for at least this period. Including a safety margin, it would appear that we can comfortably handle a burst of 1000 every second or so.

But what if there are multiple readers? The writer would then take more time to feed multiple readers, but with a fast transport, a few more readers may only increase the 11 ms to only 12 ms or so. Eventually, however, the number of readers will justify the use of multicast. Even in pull mode, Connext supports multicast by measuring how many multicast readers have requested sample repair. If the writer does not delay response to NACK, then repairs will be sent in unicast. But a suitable NACK delay allows the writer to collect potentially NACKs from multiple readers, and feed a single multicast packet. But as discussed in Section 10.3.7.2, by delaying reply to coalesce response, we may end up waiting much longer than desired. On a Windows system with 10 ms minimum sleep achievable, the delay would add at least 10 ms to the 11 ms delay, so that the time to push 1000 samples now increases to 50 times 21 ms = 1.05 seconds. It would appear that we will not be able to keep up with incoming burst if it came at roughly 1 second, although we put fewer packets on the wire by taking advantage of multicast.

Line 25-Line 28 (Figure 10.12): We now understand how the writer feeds the reader in response to the NACKs. But how does the reader realize that it is behind? The writer notifies the reader with a Heartbeat to kick-start the exchange. Therefore, the latency will be lower bound by the writer’s fast heartbeat period. If the application is not particularly sensitive to latency, the minimum wait time supported by the OS (10 ms on Windows systems, for example) might be a reasonable value.

Line 29 (Figure 10.12): With a fast heartbeat period of 50 ms, a writer will take 500 ms (50 ms times the default max_heartbeat_retries of 10) to write-off an unresponsive reader. If a reader crashes while we are writing a lot of samples per second, the writer queue may completely fill up before the writer has a chance to drop the crashed reader. Lowering max_heartbeat_retries will prevent that scenario.

Line 31-Line 32 (Figure 10.12): For an aperiodic writer, turning off slow periodic Heartbeats will remove unwanted traffic from the network.

10-25

Figure 10.13 shows example code for a corresponding aperiodic, bursty reader.

Figure 10.13 QoS for an Aperiodic, Bursty Reader

1qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3qos->resource_limits.initial_samples =

4qos->resource_limits.max_samples =

5qos->reader_resource_limits.max_samples_per_remote_writer = 32;

7//use these hard coded value until you use key

8qos->resource_limits.max_samples_per_instance =

9qos->resource_limits.max_samples;

10qos->resource_limits.initial_instances =

11qos->resource_limits.max_instances = 1;

13// the writer probably has more for the reader; ask right away

14qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.sec = 0;

15qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.nanosec = 0;

16qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.sec = 0;

17qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.nanosec = 0;

Line 1-Line 2 (Figure 10.13): Unlike a writer, the reader’s default reliability setting is best-effort, so reliability must be turned on. Since we don’t want to drop anything, we choose KEEP_ALL for the History QoS kind.

Line 3-Line 5 (Figure 10.13): Unlike the writer, the reader’s queue can be kept small, since the reader is free to send ACKs for as much as it wants anyway. In general, the larger the queue, the larger the packet needs to be, and the higher the throughput will be. When the reader NACKs for lost sample, it will only ask for this much.

Line 7-Line 11 (Figure 10.13): We do not use keys in this example.

Line 13-Line 17 (Figure 10.13): We respond immediately to catch up as soon as possible. When there are many readers, this may cause a NACK storm, as discussed in the reader code for one- at-a-time reliable reader.

10.3.7.4Periodic

In a periodic reliable model, we can use the writer and the reader queue to keep the data flowing at a smooth rate. The data flows from the sending application to the writer queue, then to the transport, then to the reader queue, and finally to the receiving application. Unless the sending application or any one of the receiving applications becomes unresponsive (including a crash) for a noticeable duration, this flow should continue uninterrupted.

The latency will be low in most cases, but will be several times higher for the recovered and many subsequent samples. In the event of a disruption (e.g., loss in transport, or one of the readers becoming temporarily unresponsive), the writer’s queue level will rise, and may even block in the worst case. If the writing thread must not block, the writer’s queue must be sized sufficiently large to deal with any fluctuation in the system. Figure 10.14 shows an example, with line-by-line analysis below.

Line 1 (Figure 10.14): This is the default setting for a writer, shown here strictly for clarity.

Line 2 (Figure 10.14): Since we do not want any data lost, we set the History kind to KEEP_ALL.

Line 3 (Figure 10.14): This is the default setting for a writer, shown here strictly for clarity. Pushing will yield lower latency than pulling.

Line 5-Line 7 (Figure 10.14): We do not use keys in this example, so there is only one instance.

Line 9-Line 11 (Figure 10.14): Though a simplistic model of queue, this is consistent with the idea that the queue size should be proportional to the data rate and the wort case jitter in communication.

10-26

Figure 10.14 QoS for a Periodic Reliable Writer

1 qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3qos->protocol.push_on_write = DDS_BOOLEAN_TRUE;

5//use these hard coded value until you use key

6qos->resource_limits.initial_instances =

7qos->resource_limits.max_instances = 1;

9int unresolvedSamplePerRemoteWriterMax =

10worstCaseApplicationDelayTimeInMs * dataRateInHz / 1000;

11qos->resource_limits.max_samples = unresolvedSamplePerRemoteWriterMax;

12qos->resource_limits.initial_samples = qos->resource_limits.max_samples/2;

13qos->resource_limits.max_samples_per_instance =

14

qos->resource_limits.max_samples;

15

 

16int piggybackEvery = 8;

17qos->protocol.rtps_reliable_writer.heartbeats_per_max_samples =

18qos->resource_limits.max_samples / piggybackEvery;

19

20qos->protocol.rtps_reliable_writer.high_watermark = piggybackEvery * 4;

21qos->protocol.rtps_reliable_writer.low_watermark = piggybackEvery * 2;

22qos->reliability.max_blocking_time = blockingTime;

23

24qos->protocol.rtps_reliable_writer.min_nack_response_delay.sec = 0;

25qos->protocol.rtps_reliable_writer.min_nack_response_delay.nanosec = 0;

27qos->protocol.rtps_reliable_writer.max_nack_response_delay.sec = 0;

28qos->protocol.rtps_reliable_writer.max_nack_response_delay.nanosec = 0;

30qos->protocol.rtps_reliable_writer.fast_heartbeat_period.sec = 0;

31qos->protocol.rtps_reliable_writer.fast_heartbeat_period.nanosec =

32alertReaderWithinThisMs * 1000000;

33qos->protocol.rtps_reliable_writer.max_heartbeat_retries = 7;

35// essentially turn off slow HB period

36qos->protocol.rtps_reliable_writer.heartbeat_period.sec = 3600 * 24 * 7;

Line 12 (Figure 10.14): Even though we have sized the queue according to the worst case, there is a possibility for saving some memory in the normal case. Here, we initially size the queue to be only half of the worst case, hoping that the worst case will not occur. When it does, Connext will keep increasing the queue size as necessary to accommodate new samples, until the maximum is reached. So when our optimistic initial queue size is breached, we will incur the penalty of dynamic memory allocation. Furthermore, you will wind up using more memory, as the initially allocated memory will be orphaned (note: does not mean a memory leak or dangling pointer); if the initial queue size is M_i and the maximal queue size is M_m, where M_m = M_i * 2^n, the memory wasted in the worst case will be (M_m - 1) * sizeof(sample) bytes. Note that the memory allocation can be avoided by setting the initial queue size equal to its max value.

Line 13-Line 14 (Figure 10.14): If there is only one instance, maximum samples per instance is the same as maximum samples allowed.

Line 16-Line 18 (Figure 10.14): Since we are pushing out the data at a potentially rapid rate, the piggyback heartbeat will be useful in letting the reader know about any missing samples. The piggybackEvery can be increased if the writer is writing at a fast rate, with the cost that more samples will need to queue up for possible resend. That is, you can consider the piggyback heartbeat to be taking over one of the roles of the periodic heartbeat in the case of a push. So sending fewer samples between piggyback heartbeats is akin to decreasing the fast heartbeat period seen in previous sections. Please note that we cannot express piggybackEvery directly as its own QoS, but indirectly through the maximum samples.

Line 20-Line 22 (Figure 10.14): If piggybackEvery was exactly identical to the fast heartbeat, there would be no need for fast heartbeat or the high watermark. But one of the important roles

10-27

for the fast heartbeat period is to allow a writer to abandon inactive readers before the queue fills. If the high watermark is set equal to the queue size, the writer would not doubt the status of an unresponsive reader until the queue completely fills—blocking on the next write (up to blockingTime). By lowering the high watermark, you can control how vigilant a writer is about checking the status of unresponsive readers. By scaling the high watermark to piggybackEvery, the writer is expressing confidence that an alive reader will respond promptly within the time it would take a writer to send 4 times piggybackEvery samples. If the reader does not delay the response too long, this would be a good assumption. Even if the writer estimated on the low side and does go into fast mode (suspecting that the reader has crashed) when a reader is temporarily unresponsive (e.g., when it is performing heavy computation for a few milliseconds), a response from the reader in question will resolve any doubt, and data delivery can continue uninterrupted. As the reader catches up to the writer and the queue level falls below the low watermark, the writer will pop out to the normal, relaxed mode.

Line 24-Line 28 (Figure 10.14): When a reader is behind (including a reader whose Durability QoS is non-VOLATILE and therefore needs to catch up to the writer as soon as it is created), how quickly the writer responds to the reader’s request will determine the catch-up rate. While a multicast writer (that is, a writer with multicast readers) may consider delaying for some time to take advantage of coalesced multicast packets. Keep in mind the OS delay resolution issue discussed in the previous section.

Line 30-Line 33 (Figure 10.14): The fast heartbeat mechanism allows a writer to detect a crashed reader and move along with the remaining readers when a reader does not respond to any of the max_heartbeat_retries number of heartbeats sent at the fast_heartbeat_period rate. So if you want a more cautious writer, decrease either numbers; conversely, increasing either number will result in a writer that is more reluctant to write-off an unresponsive reader.

Line 35-Line 36 (Figure 10.14): Since this a periodic model, a separate periodic heartbeat to notify the writer’s status would seem unwarranted; the piggyback heartbeat sent with samples takes over that role.

Figure 10.15 shows how to set the QoS for a matching reader, followed by a line-by-line explanation.

Figure 10.15 QoS for a Periodic Reliable Reader

1qos->reliability.kind = DDS_RELIABLE_RELIABILITY_QOS;

2qos->history.kind = DDS_KEEP_ALL_HISTORY_QOS;

3qos->resource_limits.initial_samples =

4qos->resource_limits.max_samples =

5qos->reader_resource_limits.max_samples_per_remote_writer =

6((2*piggybackEvery - 1) + dataRateInHz * delayInMs / 1000);

8//use these hard coded value until you use key

9qos->resource_limits.max_samples_per_instance =

10qos->resource_limits.max_samples;

11qos->resource_limits.initial_instances =

12qos->resource_limits.max_instances = 1;

14qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.sec = 0;

15qos->protocol.rtps_reliable_reader.min_heartbeat_response_delay.nanosec = 0;

16qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.sec = 0;

17qos->protocol.rtps_reliable_reader.max_heartbeat_response_delay.nanosec = 0;

Line 1-Line 2 (Figure 10.15): Unlike a writer, the reader’s default reliability setting is best-effort, so reliability must be turned on. Since we don’t want to drop anything, we choose KEEP_ALL for the History QoS.

Line 3-Line 6 (Figure 10.15) Unlike the writer, the reader queue is sized not according to the jitter of the reader, but rather how many samples you want to cache speculatively in case of a gap in sequence of samples that the reader must recover. Remember that a reader will stop giving a sequence of samples as soon as an unintended gap appears, because the definition of strict reliability includes in-order delivery. If the queue size were 1, the reader would have no choice

10-28

but to drop all subsequent samples received until the one being sought is recovered. Connext uses speculative caching, which minimizes the disruption caused by a few dropped samples. Even for the same duration of disruption, the demand on reader queue size is greater if the writer will send more rapidly. In sizing the reader queue, we consider 2 factors that comprise the lost sample recovery time:

How long it takes a reader to request a resend to the writer.

The piggyback heartbeat tells a reader about the writer’s state. If only samples between two piggybacked samples are dropped, the reader must cache piggybackEvery samples before asking the writer for resend. But if a piggybacked sample is also lost, the reader will not get around to asking the writer until the next piggybacked sample is received. Note that in this worst case calculation, we are ignoring stand-alone heartbeats (i.e., not piggybacked heartbeat from the writer). Of course, the reader may drop any number of heartbeats, including the stand-alone heartbeat; in this sense, there is no such thing as the absolute worst case—just reasonable worst case, where the probability of consecutive drops is acceptably low. For the majority of applications, even two consecutive drops is unlikely, in which case we need to cache at most (2*piggybackEvery - 1) samples before the reader will ask the writer to resend, assuming no delay (Line 14-Line 17).

How long it takes for the writer to respond to the request.

Even ignoring the flight time of the resend request through the transport, the writer takes a finite time to respond to the repair request--mostly if the writer delays reply for multi- cast readers. In case of immediate response, the processing time on the writer end, as well as the flight time of the messages to and from the writer do not matter unless very larger data rate; that is, it is the product term that matters. In case the delay for multicast is random (that is, the minimum and the maximum delay are not equal), one would have to use the maximum delay to be conservative.

Line 8-Line 12 (Figure 10.15): Since we are not using keys, there is just one instance.

Line 14-Line 17 (Figure 10.15): If we are not using multicast, or the number of readers being fed by the writer, there is no reason to delay.

10-29

Chapter 11 Collaborative DataWriters

The Collaborative DataWriters feature allows you to have multiple DataWriters publishing samples from a common logical data source. The DataReaders will combine the samples coming from these DataWriters in order to reconstruct the correct order in which they were produced at the source. This combination process for the DataReaders can be configured using the AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1). It requires the middleware to provide a way to uniquely identify every sample published in a domain independently of the actual DataWriter that published the sample.

In Connext, every modification (sample) to the global dataspace made by a DataWriter within a domain is identified by a pair (virtual GUID, sequence number).

The virtual GUID (Global Unique Identifier) is a 16-byte character identifier associated with the logical data source. DataWriters can be assigned a virtual GUID using virtual_guid in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

The virtual sequence number is a 64-bit integer that identifies changes within the logical data source.

Several DataWriters can be configured with the same virtual GUID. If each of these DataWriters publishes a sample with sequence number '0', the sample will only be received once by the DataReaders subscribing to the content published by the DataWriters (see Figure 11.1).

Figure 11.1 Global Dataspace Changes

11-1

11.1Collaborative DataWriters Use Cases

Ordered delivery of samples in high availability scenarios

One example of this is RTI Persistence Service1. When a late-joining DataReader configured with DURABILITY QosPolicy (Section 6.5.7) set to PERSISTENT or TRANSIENT joins a DDS domain, it will start receiving samples from multiple DataWriters. For example, if the original DataWriter is still alive, the newly created DataReader will receive samples from the original DataWriter and one or more RTI Persistence Service DataWriters (PRSTDataWriters).

Ordered delivery of samples in load-balanced scenarios

Multiple instances of the same application can work together to process and deliver samples. When the samples arrive through different data-paths out of order, the DataReader will be able to reconstruct the order at the source. An example of this is when multiple instances of RTI Persistence Service are used to persist the data. Persisting data to a database on disk can impact performance. By dividing the workload (e.g., samples larger than 10 are persisted by Persistence Service 1, samples smaller or equal to 10 are persisted by Persistence Service 2) across different instances of RTI Persistence Service using different databases the user can improve scalability and performance.

Ordered delivery of samples with Group Ordered Access

The Collaborative DataWriters feature can also be used to configure the sample ordering process when the Subscriber is configured with PRESENTATION QosPolicy (Section 6.4.6) access_scope set to GROUP. In this case, the Subscriber must deliver in order the samples published by a group of DataWriters that belong to the same Publisher and have access_scope set to GROUP.

Figure 11.2 Load-Balancing with Persistence Service

1. For more information on Persistence Service, see Part 6: RTI Persistence Service.

11-2

11.2Sample Combination (Synchronization) Process in a DataReader

A DataReader will deliver a sample (VGUIDn, VSNm) to the application only when if one of the following conditions is satisfied:

(GUIDn, SNm-1) has already been delivered to the application.

All the known DataWriters publishing VGUIDn have announced that they do not have (VGUIDn, VSNm-1).

None of the known DataWriters publishing VGUIDn have announced potential availability of (VGUIDn, VSNm-1) and a configurable timeout (max_data_availability_waiting_time) expires.

For additional details on how the reconstruction process works see the AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1).

11.3Configuring Collaborative DataWriters

11.3.1Assocating Virtual GUIDs with Data Samples

There are two ways to associate a virtual GUID with the samples published by a DataWriter.

Per DataWriter: Using virtual_guid in DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

Per Sample: By setting the writer_guid in the identity field of the WriteParams_t structure provided to the write_w_params operation (see Writing Data (Section 6.3.8)). Since the writer_guid can be set per sample, the same DataWriter can potentially write samples from independent logical data sources. One example of this is RTI Persistence Service where a single persistence service DataWriter can write samples on behalf of multiple original DataWriters.

11.3.2Assocating Virtual Sequence Numbers with Data Samples

You can associate a virtual sequence number with a sample published by a DataWriter by setting the sequence_number in the identity field of the WriteParams_t structure provided to the write_w_params operation (see Writing Data (Section 6.3.8)). Virtual sequence numbers for a given virtual GUID must be strictly monotonically increasing. If you try to write a sample with a sequence number less than or equal to the last sequence number, the write operation will fail.

11.3.3Specifying which DataWriters will Deliver Samples to the DataReader from a Logical Data Source

The required_matched_endpoint_groups field in the AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1) can be used to specify the set of DataWriter groups that are expected to provide samples for the same data source (virtual GUID). The quorum count in a group represents the number of DataWriters that must be discovered for that group before the DataReader is allowed to provide non-consecutive samples to the application.

A DataWriter becomes a member of an endpoint group by configuring the role_name in ENTITY_NAME QosPolicy (DDS Extension) (Section 6.5.9).

11-3

11.3.4Specifying How Long to Wait for a Missing Sample

A DataReader’s AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1) specifies how long to wait for a missing sample. For example, this is important when the first sample is received: how long do you wait to determine the lowest sequence number available in the system?

The max_data_availability_waiting_time defines how much time to wait before delivering a sample to the application without having received some of the previous samples.

The max_endpoint_availability_waiting_time defines how much time to wait to discover DataWriters providing samples for the same data source (virtual GUID).

11.4Collaborative DataWriters and Persistence Service

The DataWriters created by persistence service are automatically configured to do collaboration:

Every sample published by the Persistence Service DataWriter keeps its original identity.

Persistence Service associates the role name PERSISTENCE_SERVICE with all the DataWriters that it creates. You can overwrite that setting by changing the DataWriter QoS configuration in persistence service.

For more information, see Part 6: RTI Persistence Service.

11-4

Chapter 12 Mechanisms for Achieving Information Durability and Persistence

12.1Introduction

Connext offers the following mechanisms for achieving durability and persistence:

Durable Writer History This feature allows a DataWriter to persist its historical cache, perhaps locally, so that it can survive shutdowns, crashes and restarts. When an application restarts, each DataWriter that has been configured to have durable writer history automatically load all of the data in this cache from disk and can carry on sending data as if it had never stopped executing. To the rest of the system, it will appear as if the DataWriter had been temporarily disconnected from the network and then reappeared.

Durable Reader State This feature allows a DataReader to persist its state and remember which data it has already received. When an application restarts, each DataReader that has been configured to have durable reader state automatically loads its state from disk and can carry on receiving data as if it had never stopped executing. Data that had already been received by the DataReader before the restart will be suppressed so that it is not even sent over the network.

Data Durability This feature is a full implementation of the OMG DDS Persistence Profile. The DURABILITY QosPolicy (Section 6.5.7) allows an application to configure a DataWriter so that the information written by the DataWriter survives beyond the lifetime of the DataWriter. In this manner, a late-joining DataReader can subscribe to and receive the information even after the DataWriter application is no longer executing. To use this feature, you need Persistence Service, a separate application described in Chapter 26: Introduction to RTI Persistence Service.

These features can be configured separately or in combination. To use Durable Writer State and Durable Reader State, you need a relational database, which is not included with Connext. Supported databases are listed in the Release Notes. Persistence Service does not require a database when used in TRANSIENT mode (see Section 12.5.1) or in PERSISTENT mode with file-system storage (see Section 12.5.1 and Section 27.5).

To understand how these features interact we will examine the behavior of the system using the following scenarios:

Scenario 1. DataReader Joins after DataWriter Restarts (Durable Writer History) (Section 12.1.1)

Scenario 2: DataReader Restarts While DataWriter Stays Up (Durable Reader State) (Section 12.1.2)

12-1

Scenario 3. DataReader Joins after DataWriter Leaves Domain (Durable Data) (Section 12.1.3)

12.1.1Scenario 1. DataReader Joins after DataWriter Restarts (Durable Writer History)

In this scenario, a DomainParticipant joins the domain, creates a DataWriter and writes some data, then the DataWriter shuts down (gracefully or due to a fault). The DataWriter restarts and a DataReader joins the domain. Depending on whether the DataWriter is configured with durable history, the late-joining DataReader may or may not receive the data published already by the DataWriter before it restarted. This is illustrated in Figure 12.1. For more information, see Durable Writer History (Section 12.3)

Figure 12.1 Durable Writer History

DataWriter

 

DataWriter

 

 

 

a

 

 

 

a

 

 

 

 

 

 

 

b

 

 

b

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataReader

 

 

 

 

 

 

 

DataReader

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

b

Without Durable Writer History:

the late-joining DataReader will not receive data (a and b) that was published before the DataWriter’s restart.

With Durable Writer History:

the restarted DataWriter will recover its history and deliver its data to the late- joining DataReader

12.1.2Scenario 2: DataReader Restarts While DataWriter Stays Up (Durable Reader State)

In this scenario, two DomainParticipants join a domain; one creates a DataWriter and the other a DataReader on the same Topic. The DataWriter publishes some data ("a" and "b") that is received by the DataReader. After this, the DataReader shuts down (gracefully or due to a fault) and then restarts—all while the DataWriter remains present in the domain.

Depending on whether the DataReader is configured with Durable Reader State, the DataReader may or may not receive a duplicate copy of the data it received before it restarted. This is illustrated in Figure 12.2. For more information, see Durable Reader State (Section 12.4).

12-2

Figure 12.2 Durable Reader State

DataWriter DataReader

a

a

b

b

 

 

 

 

 

 

 

a b

Without Durable Reader State:

the DataReader will receive the data that was already received before the restart.

DataWriter DataReader

a a

b b

With Durable Reader State:

the DataReader remembers that it already received the data and does not request it again.

12.1.3Scenario 3. DataReader Joins after DataWriter Leaves Domain (Durable Data)

In this scenario, a DomainParticipant joins a domain, creates a DataWriter, publishes some data on a Topic and then shuts down (gracefully or due to a fault). Later, a DataReader joins the domain and subscribes to the data. Persistence Service is running.

Depending on whether Durable Data is enabled for the Topic, the DataReader may or may not receive the data previous published by the DataWriter. This is illustrated in Figure 12.3. For more information, see Data Durability (Section 12.5)

Figure 12.3 Durable Data

DataWriter

a

b

DataReader

Without Durable Data:

the late-joining DataReader will not receive data (a and b) that was published before the DataWriter quit.

DataWriter Persistence

Service

a a

b b

DataReader

a

b

With Durable Data:

Persistence Service remembers what data was published and delivers it to the late-joining DataReader.

This third scenario is similar to Scenario 1. DataReader Joins after DataWriter Restarts (Durable Writer History) (Section 12.1.1) except that in this case the DataWriter does not need to restart for the DataReader to get the data previously written by the DataWriter. This is because Persistence Service acts as an intermediary that stores the data so it can be given to late-joining DataReaders.

12-3

12.2Durability and Persistence Based on Virtual GUIDs

Every modification to the global dataspace made by a DataWriter is identified by a pair (virtual GUID, sequence number).

The virtual GUID (Global Unique Identifier) is a 16-byte character identifier associated with a DataWriter or DataReader; it is used to uniquely identify this entity in the global data space.

The sequence number is a 64-bit identifier that identifies changes published by a specific

DataWriter.

Several DataWriters can be configured with the same virtual GUID. If each of these DataWriters publishes a sample with sequence number '0', the sample will only be received once by the DataReaders subscribing to the content published by the DataWriters (see Figure 12.4).

Figure 12.4 Global Dataspace Changes

 

DataWriter

 

(vg: 1, sn: 0)

 

 

 

 

 

(vg: 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

(vg: 1, sn: 0)

DataReader

 

 

 

 

 

 

 

 

 

DataWriter

 

 

 

 

 

 

 

 

 

 

 

 

 

(vg: 1)

 

 

 

 

 

 

 

 

 

(vg: 1)

 

 

 

 

(vg: 2, sn: 0)

(vg: 2, sn: 0)

 

 

 

 

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

 

 

 

 

DataWriter

 

 

 

 

 

 

 

(vg: 2)

 

(vg: 2, sn: 0)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Additionally, Connext uses the virtual GUID to associate a persisted state (state in permanent storage) to the corresponding Entity.

For example, the history of a DataWriter will be persisted in a database table with a name generated from the virtual GUID of the DataWriter. If the DataWriter is restarted, it must have associated the same virtual GUID to restore its previous history.

Likewise, the state of a DataReader will be persisted in a database table whose name is generated from the DataReader virtual GUID (see Figure 12.5).

Figure 12.5 History/State Persistence Based on the Virtual GUID

DataWriter

 

DataReader

 

 

 

vg: 1

 

 

 

vg: 1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A DataWriter’s virtual GUID can be configured using the member virtual_guid in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3).

A DataReader’s virtual GUID can be configured using the member virtual_guid in the DATA_READER_PROTOCOL QosPolicy (DDS Extension) (Section 7.6.1).

12-4

The DDS_PublicationBuiltinTopicData and DDS_SubscriptionBuiltinTopicData structures include the virtual GUID associated with the discovered publication or subscription (see Built-in DataReaders (Section 16.2)).

12.3Durable Writer History

The DURABILITY QosPolicy (Section 6.5.7) controls whether or not, and how, published samples are stored by the DataWriter application for DataReaders that are found after the samples were initially written. The samples stored by the DataWriter constitute the DataWriter’s history.

Connext provides the capability to make the DataWriter history durable, by persisting its content in a relational database. This makes it possible for the history to be restored when the DataWriter restarts. See the Release Notes for the list of supported relational databases.

The association between the history stored in the database and the DataWriter is done using the virtual GUID.

12.3.1Durable Writer History Use Case

The following use case describes the durable writer history functionality:

1.A DataReader receives two samples with sequence number 1 and 2 published by a DataWriter with virtual GUID 1.

1, 2

 

DataWriter

1, 2

 

DataReader

1, 2

 

 

 

 

 

 

 

 

 

(vg: 1)

 

 

 

(vg: 1)

 

 

 

 

 

 

 

 

 

 

 

2.The process running the DataWriter is stopped and a new late-joining DataReader is created.

DataReader (vg: 1)

DataReader (vg: 2)

The new DataReader with virtual GUID 2 does not receive samples 1 and 2 because the original DataWriter has been destroyed. If the samples must be available to late-joining

DataReaders after the DataWriter deletion, you can use Persistence Service, described in Chapter 26: Introduction to RTI Persistence Service.

3. The DataWriter is restarted using the same virtual GUID.

DataWriter

DataReader

(vg: 1)

1, 2

(vg: 1)

DataReader

 

1, 2

(vg: 2)

 

 

 

 

 

 

12-5

After being restarted, the DataWriter restores its history. The late-joining DataReader will receive samples 1 and 2 because they were not received previously. The DataReader with virtual GUID 1 will not receive samples 1 and 2 because it already received them

4. The DataWriter publishes two new samples.

 

 

 

3, 4

DataReader

3, 4

 

 

 

 

 

 

 

DataWriter

 

 

(vg: 1)

 

 

 

 

(vg: 1)

 

3, 4

 

 

3, 4

 

 

 

 

 

 

 

 

 

 

 

 

DataReader

 

 

 

 

 

 

 

 

 

 

(vg: 2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The two new samples with sequence numbers 3 and 4 will be received by both DataRead- ers.

12.3.2How To Configure Durable Writer History

Connext allows a DataWriter’s history to be stored in a relational database that provides an ODBC driver.

For each DataWriter history that is configured to be durable, Connext will create a maximum of two tables:

The first table is used to store the samples associated with the writer history. The name of that table is WS<32 uuencoding of the writer virtual GUID>.

The second table is only created for keyed-topic and it is used to store the instances associated with the writer history. The name of the second table is WI<32 uuencoding of the writer virtual GUID>.

To configure durable writer history, use the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17) associated with DataWriters and DomainParticipants.

A ‘durable writer history’ property defined in the DomainParticipant will be applicable to all the DataWriters belonging to the DomainParticipant unless it is overwritten by the DataWriter. Table 12.1 lists the supported ‘durable writer history’ properties.

Table 12.1 Durable Writer History Properties

Property

Description

 

 

 

Required.

dds.data_writer.history.plugin_name

Must be set to "dds.data_writer.history.odbc_plugin.builtin" to enable

 

durable writer history in the DataWriter.

 

 

dds.data_writer.history.odbc_plugin.

Required.

The ODBC DSN (Data Source Name) associated with the database where

dsn

the writer history must be persisted.

 

 

 

 

Tells Connext which ODBC driver to load. If the property is not

dds.data_writer.history.odbc_plugin.

specified, Connext will try to use the standard ODBC driver manager

driver

library (UnixOdbc on UNIX/Linux systems, the Windows ODBC driver

 

manager on Windows systems).

 

 

dds.data_writer.history.odbc_plugin.

 

username

Configures the username/password used to connect to the database.

 

Default: No password or username

dds.data_writer.history.odbc_plugin.

password

 

 

 

 

When set to 1, Connext will create a single connection per DSN that will

dds.data_writer.history.odbc_plugin.

be shared across DataWriters within the same Publisher.

shared

A DataWriter can be configured to create its own database connection by

 

setting this property to 0 (the default).

 

 

12-6

Table 12.1 Durable Writer History Properties

Property

 

 

Description

 

 

 

 

 

 

dds.data_writer.history.odbc_plugin.

These properties configure the resource limits associated with the ODBC

instance_cache_max_size

writer history caches.

 

 

 

 

 

To minimize the number of accesses to the database, Connext uses two

dds.data_writer.history.odbc_plugin.

instance_cache_init_size

caches, one for samples and one for instances. The initial size and the

 

maximum size of these caches are configured using these properties.

 

dds.data_writer.history.odbc_plugin.

 

sample_cache_max_size

The resource limits, initial_instances, max_instances, initial_samples,

max_samples,

and

max_samples_per_instance

defined

in

 

 

 

RESOURCE_LIMITS QosPolicy (Section 6.5.20) are used to configure the

 

maximum number of samples and instances that can be stored in the

 

relational database.

 

 

 

 

 

Defaults:

 

 

 

 

 

 

instance_cache_max_size:

max_instances

in

 

RESOURCE_LIMITS QosPolicy (Section 6.5.20)

 

 

dds.data_writer.history.odbc_plugin.

instance_cache_init_size:

initial_instances

in

sample_cache_init_size

RESOURCE_LIMITS QosPolicy (Section 6.5.20)

 

 

 

sample_cache_max_size: 32

 

 

 

 

sample_cache_init_size: 32

 

 

 

 

Note: If the property in_memory_state (see below in this table) is 1,

 

then instance_cache_max_size is always equal to max_instances in

 

RESOURCE_LIMITS QosPolicy (Section 6.5.20)—it cannot be

 

changed.

 

 

 

 

 

 

 

 

This property indicates whether or not the persisted writer history must

 

be restored once the DataWriter is restarted.

 

 

dds.data_writer.history.odbc_plugin.

If this property is 0, the content of the database associated with the

DataWriter being restarted will be deleted.

 

 

 

restore

If it is 1, the DataWriter will restore its previous state from the database

 

 

content.

 

 

 

 

 

 

Default: 1

 

 

 

 

 

 

 

 

This property determines how much state will be kept in memory by the

 

ODBC writer history in order to avoid accessing the database.

 

 

If this property is 1, then the property

instance_cache_max_size (see

 

above in this table) is always equal to max_instances

in

 

RESOURCE_LIMITS QosPolicy (Section 6.5.20)—it cannot be changed.

 

In addition, the ODBC writer history will keep in memory a fixed state

dds.data_writer.history.odbc_plugin.

overhead of 24 bytes per sample. This mode provides the best ODBC

in_memory_state

writer history performance. However, the restore operation will be

 

slower and the maximum number of samples that the writer history can

 

manage is limited by the available physical memory.

 

 

 

If it is 0, all the state will be kept in the underlying database. In this

 

mode, the maximum number of samples in the writer history is not

 

limited by the physical memory available.

 

 

 

 

Default: 1

 

 

 

 

 

 

 

 

 

 

 

 

Note: Durable Writer History is not supported for Multi-channel DataWriters (see Chapter 18) or when Batching is enabled (see Section 6.5.2); an error is reported if this type of DataWriter tries to configure Durable Writer History.

See also: Durable Reader State (Section 12.4).

Example C++ Code

/* Get default QoS */

...

retcode = DDSPropertyQosPolicyHelper::add_property (writerQos.property, "dds.data_writer.history.plugin_name", "dds.data_writer.history.odbc_plugin.builtin",

12-7

DDS_BOOLEAN_FALSE);

if (retcode != DDS_RETCODE_OK) { /* Report error */

}

retcode = DDSPropertyQosPolicyHelper::add_property (writerQos.property, "dds.data_writer.history.odbc_plugin.dsn",

"<user DSN>", DDS_BOOLEAN_FALSE);

if (retcode != DDS_RETCODE_OK) { /* Report error */

}

retcode = DDSPropertyQosPolicyHelper::add_property (writerQos.property, "dds.data_writer.history.odbc_plugin.driver",

"<ODBC library>", DDS_BOOLEAN_FALSE);

if (retcode != DDS_RETCODE_OK) { /* Report error */

}

retcode = DDSPropertyQosPolicyHelper::add_property (writerQos.property, "dds.data_writer.history.odbc_plugin.shared", "<0|1>",

DDS_BOOLEAN_FALSE); if (retcode != DDS_RETCODE_OK) {

/* Report error */

}

/* Create Data Writer */

...

12.4Durable Reader State

Durable reader state allows a DataReader to locally store its state in disk and remember the data that has already been processed by the application1. When an application restarts, each DataReader configured to have durable reader state automatically reads its state from disk. Data that has already been processed by the application before the restart will not be provided to the application again.

Important: The DataReader does not persist the full contents of the data in its historical cache; it only persists an identification (e.g. sequence numbers) of the data the application has processed. This distinction is not meaningful if your application always uses the ‘take’ methods to access your data, since these methods remove the data from the cache at the same time they deliver it to your application. (See Read vs. Take (Section 7.4.3.1)) However, if your application uses the ‘read’ methods, leaving the data in the DataReader's cache after you've accessed it for the first time, those previously viewed samples will not be restored to the DataReader's cache in the event of a restart.

Connext requires a relational database to persist the state of a DataReader. This database is accessed using ODBC. See the Release Notes for the list of supported relational databases.

12.4.1Durable Reader State With Protocol Acknowledgment

For each DataReader configured to have durable state, Connext will create one database table with the following naming convention: RS<32 uuencoding of the reader virtual GUID>. This table will store the last sequence number processed from each virtual GUID. For DataReaders on

1.The circumstances under which a data sample is considered “processed by the application” are described in the sections that follow.

12-8

keyed topics requesting instance-ordering (see PRESENTATION QosPolicy (Section 6.4.6)), this state will be stored per instance per virtual GUID..

Criteria to consider a sample “processed by the application”

For the read/take methods that require calling return_loan(), a sample 's1' with sequence number 's1_seq_num' and virtual GUID ‘vg1’ is considered processed by the application when the DataReader’s return_loan() operation is called for sample 's1' or any other sample with the same virtual GUID and a sequence number greater than 's1_seq_num'. For example:

retcode = Foo_reader->take(data_seq, info_seq, DDS_LENGTH_UNLIMITED, DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE);

if (retcode == DDS_RETCODE_NO_DATA) { return;

}else if (retcode != DDS_RETCODE_OK) { /* report error */

return;

}

for (i = 0; i < data_seq.length(); ++i) { /* Operate with the data */

}

/* Return the loan */

retcode = Foo_reader->return_loan(data_seq, info_seq); if (retcode != DDS_RETCODE_OK) {

/* Report and error */

}

/* At this point the samples contained in data_seq will be considered as received. If the DataReader restarts, the samples will not be received again */

For the read/take methods that do not require calling return_loan(), a sample 's1' with sequence number 's1_seq_num' and virtual GUID ‘vg1’ will be considered processed after the application reads or takes the sample 's1' or any other sample with the same virtual GUID and with a sequence number greater than 's1_seq_num'. For example:

retcode = Foo_reader->take_next_sample(data,info);

/* At this point the sample contained in data will be considered as received. All the samples with a sequence number smaller than the sequence number associated with data will also be considered as received. If the DataReader restarts these sample will not be received again */

Important: If you access the samples in the DataReader cache out of order—for example via QueryCondition, specifying an instance state, or reading by instance when the PRESENTATION QoS is not set to INSTANCE_PRESENTATION_QOS—then the samples that have not yet been taken or read by the application may still be considered as ”processed by the application”.

12.4.1.1Bandwidth Utilization

To optimize network usage, if a DataReader configured with durable reader state is restarted and it discovers a DataWriter with a virtual GUID ‘vg’, the DataReader will ACK all the samples with a sequence number smaller than ‘sn’, where ‘sn’ is the first sequence number that has not been being processed by the application for ‘vg’.

Notice that the previous algorithm can significantly reduce the number of duplicates on the wire. However, it does not suppress them completely in the case of keyed DataReaders where the durable state is kept per (instance, virtual GUID). In this case, and assuming that the application has read samples out of order (e.g., by reading different instances), the ACK is sent for the

12-9

lowest sequence number processed across all instances and may cause samples already processed to flow on the network again. These redundant samples waste bandwidth, but they will be dropped by the DataReader and not be delivered to the application.

12.4.2Durable Reader State with Application Acknowledgment

This section assumes you are familiar with the concept of Application Acknowledgment as described in Section 6.3.12.

For each DataReader configured to be durable and that uses application acknowledgement (see Section 6.3.12), Connext will create one database table with the following naming convention:

RS<32 uuencoding of the reader virtual GUID>. This table will store the list of sequence number intervals that have been acknowledged for each virtual GUID. The size of the column that stores the sequence number intervals is limited to 32767 bytes. If this size is exceeded for a given virtual GUID, the operation that persists the DataReader state into the database will fail.

12.4.2.1Bandwidth Utilization

To optimize network usage, if a DataReader configured with durable reader state is restarted and it discovers a DataWriter with a virtual GUID ‘vg’, the DataReader will send an APP_ACK message with all the samples that were auto-acknowledged or explicitly acknowledged in previous executions.

Notice that this algorithm can significantly reduce the number of duplicates on the wire. However, it does not suppress them completely since the DataReader may send a NACK and receive some samples from the DataWriter before the DataWriter receives the APP_ACK message.

12.4.3Durable Reader State Use Case

The following use case describes the durable reader state functionality:

1.A DataReader receives two samples with sequence number 1 and 2 published by a DataWriter with virtual GUID 1. The application takes those samples.

1, 2

 

DataWriter

 

1, 2

 

DataReader

 

take 1, 2

 

 

(vg: 1)

 

 

 

 

(vg: 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2.After the application returns the loan on samples 1 and 2, the DataReader considers them as processed and it persists the state change.

 

DataWriter

 

 

DataReader

return loan 1, 2

 

(vg: 1)

 

 

(vg: 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(dw vg: 1,last sn: 2)

3. The process running the DataReader is stopped.

12-10

4. The DataReader is restarted.

DataWriter

 

DataReader

(vg: 1)

 

(vg: 1)

 

 

 

(dw vg: 1,last sn: 2)

Because all the samples with sequence number smaller or equal than 2 were considered as received, the reader will not ask for these samples to the DataWriter.

12.4.4How To Configure a DataReader for Durable Reader State

To configure a DataReader with durable reader state, use the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17) associated with DataReaders and DomainParticipants.

A property defined in the DomainParticipant will be applicable to all the DataReaders contained in the participant unless it is overwritten by the DataReaders. Table 12.2 lists the supported properties.

Table 12.2 Durable Reader State Properties

Property

Description

 

 

 

Required.

dds.data_reader.state.odbc.dsn

The ODBC DSN (Data Source Name) associated with the database where

 

the DataReader state must be persisted.

 

 

 

To enable durable reader state, this property must be set to 1.

dds.data_reader.state.

When set to 0, the reader state is not maintained and Connext does not

filter_redundant_samples

filter duplicate samples that may be coming from the same virtual writer.

 

Default: 1

 

 

 

This property indicates which ODBC driver to load. If the property is not

dds.data_reader.state.odbc.driver

specified, Connext will try to use the standard ODBC driver manager

library (UnixOdbc on UNIX/Linux systems, the Windows ODBC driver

 

 

manager on Windows systems).

 

 

dds.data_reader.state.odbc.username

These two properties configure the username and password used to

 

connect to the database.

dds.data_reader.state.odbc.password

Default: No password or username

 

 

 

This property indicates if the persisted DataReader state must be restored

 

or not once the DataReader is restarted.

dds.data_reader.state.restore

If this property is 0, the previous state will be deleted from the database.

If it is 1, the DataReader will restore its previous state from the database

 

 

content.

 

Default: 1

 

 

 

This property controls how often the reader state is stored into the

 

database. A value of N means store the state once every N samples.

dds.data_reader.state.

A high frequency will provide better performance. However, if the

reader is restarted it may receive some duplicate samples. These samples

checkpoint_frequency

will be filtered by Connext and they will not be propagated to the

 

 

application.

 

Default: 1

 

 

dds.data_reader.state.persistence_

This property indicates how many of the most recent historical samples

the persisted DataReader wants to receive upon start-up.

service.request_depth

Default: 0

 

 

 

12-11

Example (C++ code):

/* Get default QoS */

...

retcode = DDSPropertyQosPolicyHelper::add_property( readerQos.property, "dds.data_reader.state.odbc.dsn", "<user DSN>",

DDS_BOOLEAN_FALSE); if (retcode != DDS_RETCODE_OK) {

/* Report error */

}

retcode = DDSPropertyQosPolicyHelper::add_property(readerQos.property, "dds.data_reader.state.odbc.driver", "<ODBC library>", DDS_BOOLEAN_FALSE);

if (retcode != DDS_RETCODE_OK) { /* Report error */

}

retcode = DDSPropertyQosPolicyHelper::add_property(readerQos.property, "dds.data_reader.state.restore", "<0|1>", DDS_BOOLEAN_FALSE);

if (retcode != DDS_RETCODE_OK) { /* Report error */

}

/* Create Data Reader */

...

12.5Data Durability

The data durability feature is an implementation of the OMG DDS Persistence Profile. The DURABILITY QosPolicy (Section 6.5.7) allows an application to configure a DataWriter so that the information written by the DataWriter survives beyond the lifetime of the DataWriter.

Connext implements TRANSIENT and PERSISTENT durability using an external service called Persistence Service, available for purchase as a separate RTI product.

Persistence Service receives information from DataWriters configured with TRANSIENT or PERSISTENT durability and makes that information available to late-joining DataReaders—even if the original DataWriter is not running.

The samples published by a DataWriter can be made durable by setting the kind field of the DURABILITY QosPolicy (Section 6.5.7) to one of the following values:

DDS_TRANSIENT_DURABILITY_QOS: Connext will store previously published samples in memory using Persistence Service, which will send the stored data to newly discovered DataReaders.

DDS_PERSISTENT_DURABILITY_QOS: Connext will store previously published samples in permanent storage, like a disk, using Persistence Service, which will send the stored data to newly discovered DataReaders.

A DataReader can request TRANSIENT or PERSISTENT data by setting the kind field of the corresponding DURABILITY QosPolicy (Section 6.5.7). A DataReader requesting PERSISTENT data will not receive data from DataWriters or Persistence Service applications that are configured with TRANSIENT durability.

12-12

12.5.1RTI Persistence Service

Persistence Service is a Connext application that is configured to persist topic data. Persistence Service is included with Connext Messaging. For each one of the topics that must be persisted for a specific domain, the service will create a DataWriter (known as PRSTDataWriter) and a DataReader (known as PRSTDataReader). The samples received by the PRSTDataReaders will be published by the corresponding PRSTDataWriters to be available for late-joiners DataReaders.

For more information on Persistence Service, please see:

Chapter 26: Introduction to RTI Persistence Service

Chapter 27: Configuring Persistence Service

Chapter 28: Running RTI Persistence Service

Persistence Service can be configured to operate in PERSISTENT or TRANSIENT mode:

TRANSIENT mode The PRSTDataReaders and PRSTDataWriters will be created with TRANSIENT durability and Persistence Service will keep the received samples in memory. Samples published by a TRANSIENT DataWriter will survive the DataWriter lifecycle but will not survive the lifecycle of Persistence Service (unless you are running multiple copies).

PERSISTENT mode The PRSTDataWriters and PRSTDataReaders will be created with PERSISTENT durability and Persistence Service will store the received samples in files or in an external relational database. Samples published by a PERSISTENT DataWriter will survive the DataWriter lifecycle as well as any restarts of Persistence Service.

Peer-to-Peer Communication:

By default, a PERSISTENT/TRANSIENT DataReader will receive samples directly from the original DataWriter if it is still alive. In this scenario, the DataReader may also receive the same samples from Persistence Service. Duplicates will be discarded at the middleware level. This Peer- To-Peer communication pattern is illustrated inFigure 12.6. To use this peer-to-peer communication pattern, set the direct_communication field in the DURABILITY QosPolicy (Section 6.5.7) to TRUE. A PERSISTENT/TRANSIENT DataReader will receive information directly from PERSISTENT/TRANSIENT DataWriters.

Figure 12.6 Peer-to-Peer Communication

 

DataWriter

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

DataReader

 

(vg: 1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(vg: 1)

 

 

 

(vg: 1, sn: 0)

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

 

 

 

 

(vg: 1, sn: 0)

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

RTI Persistence

The application only

receives one sample.

Service

 

Relay Communication:

A PERSISTENT/TRANSIENT DataReader may also be configured to not receive samples from the original DataWriter. In this case the traffic is relayed by Persistence Service. This ‘relay communication’ pattern is illustrated in Figure 12.7. To use relay communication, set the direct_communication field in the DURABILITY QosPolicy (Section 6.5.7) to FALSE. A PERSISTENT/TRANSIENT DataReader will receive all the information from Persistence Service.

12-13

Figure 12.7 Relay Communication

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

 

 

 

DataReader

 

DataWriter

 

 

RTI Persistence

 

 

 

 

 

 

(vg: 1)

 

(vg: 1)

 

 

Service

 

 

 

 

 

 

 

(vg: 1, sn: 0)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(vg: 1, sn: 0)

12-14

Chapter 13 Guaranteed Delivery of Data

13.1Introduction

Some application scenarios need to ensure that the information produced by certain producers is delivered to all the intended consumers. This chapter describes the mechanisms available in Connext to guarantee the delivery of information from producers to consumers such that the delivery is robust to many kinds of failures in the infrastructure, deployment, and even the producing/consuming applications themselves.

Guaranteed information delivery is not the same as protocol-level reliability (described in Chapter 10: Reliable Communications) or information durability (described in Chapter 12: Mechanisms for Achieving Information Durability and Persistence). Guaranteed information delivery is an end-to-end application-level QoS, whereas the others are middleware-level QoS. There are significant differences between these two:

With protocol-level reliability alone, the producing application knows that the information is received by the protocol layer on the consuming side. However the producing application cannot be certain that the consuming application read that information or was able to successfully understand and process it. The information could arrive in the consumer’s protocol stack and be placed in the DataReader cache but the consuming application could either crash before it reads it from the cache, not read its cache, or read the cache using queries or conditions that prevent that particular data sample from being accessed. Furthermore, the consuming application could access the sample, but not be able to interpret its meaning or process it in the intended way.

With information durability alone, there is no way to specify or characterize the intended consumers of the information. Therefore the infrastructure has no way to know when the information has been consumed by all the intended recipients. The information may be persisted such that it is not lost and is available to future applications, but the infrastructure and producing applications have no way to know that all the intended consumers have joined the system, received the information, and processed it successfully.

The guaranteed data-delivery mechanism provided in Connext overcomes the limitations described above by providing the following features:

Required subscriptions. This feature provides a way to configure, identify and detect the applications that are intended to consume the information. See Required Subscriptions (Section 6.3.13).

Application-level acknowledgments. This feature provides the means ensure that the information was successfully processed by the application-layer in a consumer application. See Application Acknowledgment (Section 6.3.12).

13-1

Durable subscriptions. This feature leverages the RTI Persistence Service to persist samples intended for the required subscriptions such that they are delivered even if the originating application is not available. See Configuring Durable Subscriptions in Persistence Service (Section 27.9).

These features used in combination with the mechanisms provided for Information Durability and Persistence (see Chapter 12: Mechanisms for Achieving Information Durability and Persistence) enable the creation of applications where the information delivery is guaranteed despite application and infrastructure failures. Scenarios (Section 13.2) describes various guaranteed-delivery scenarios and how to configure the applications to achieve them.

When implementing an application that needs guaranteed data delivery, we have to consider three key aspects:

Key Aspects to Consider

 

Related Features and QoS

 

 

 

 

Required subscriptions

Identifying the required consumers of information

Durable subscriptions

EntityName QoS policy

 

 

Availability QoS policy

 

 

 

Application-level acknowledgment

Ensuring the intended consumer applications

• Acknowledgment by a quorum of required and

 

durable subscriptions

process the data successfully

 

• Reliability QoS policy (acknowledgment mode)

 

 

Availability QoS policy

 

 

 

 

Persistence Service

Ensuring information is available to late joining

Durable Subscriptions

applications

Durability QoS

 

Durable Writer History

 

 

 

13.1.1Identifying the Required Consumers of Information

The first step towards ensuring that information is processed by the intended consumers is the ability to specify and recognize those intended consumers. This is done using the required subscriptions feature (Required Subscriptions (Section 6.3.13)) configured via the ENTITY_NAME QosPolicy (DDS Extension) (Section 6.5.9) and AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1)).

Connext DDS DataReader entities (as well as DataWriter and DomainParticipant entities) can have a name and a role_name. These names are configured using the ENTITY_NAME QosPolicy (DDS Extension) (Section 6.5.9), which is propagated via DDS discovery and is available as part of the builtin-topic data for the Entity (see Chapter 16: Built-In Topics).

The DDS DomainParticipant, DataReader and DataWriter entities created by RTI-provided applications and services, specifically services such as RTI Persistence Service, automatically configure the ENTITY_NAME QoS policy according to their function. For example the

DataReaders created by RTI Persistence Service have their role_name set to “PERSISTENCE_SERVICE”.

Unless explicitly set by the user, the DomainParticipant, DataReader and DataWriter entities created by end-user applications have their name and role_name set to NULL. However applications may modify this using the ENTITY_NAME QosPolicy (DDS Extension) (Section 6.5.9).

Connext uses the role_name of DataReaders to identify the consumer’s logical function. For this reason Connext’s required subscriptions feature relies on the role_name to identify intended consumers of information. The use of the DataReader’s role_name instead of the name is intentional. From the point of view of the information producer, the important thing is not the

13-2

concrete DataReader (identified by its name, for example, “Logger123”) but rather its logical function in the system (identified by its role_name, for example “LoggingService”).

A DataWriter that needs to ensure its information is delivered to all the intended consumers uses the AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1) to configure the role names of the consumers that must receive the information.

The AVAILABILITY QoS Policy set on a DataWriter lets an application configure the required consumers of the data produced by the DataWriter. The required consumers are specified in the required_matched_endpoint_groups attribute within the AVAILABILITY QoS Policy. This attribute is a sequence of DDS EndpointGroup structures. Each EndpointGroup represents a required information consumer characterized by the consumer’s role_name and quorum. The role_name identifies a logical consumer; the quorum specifies the minimum number of consumers with that role_name that must acknowledge the sample before the DataWriter can consider it delivered to that required consumer.

For example, an application that wants to ensure data written by a DataWriter is delivered to at least two Logging Services and one Display Service would configure the DataWriter’s AVAILABILITY QoS Policy with a required_matched_endpoint_groups consisting of two elements. The first element would specify a required consumer with the role_name “LoggingService” and a quorum of 2. The second element would specify a required consumer with the role_name “DisplayService” and a quorum of 1. Furthermore, the application would set the logging service DataReader ENTITY_NAME policy to have a role_name of “LoggingService” and similarly the display service DataReader ENTITY_NAME policy to have the role_name of “DisplayService.”

A DataWriter that has been configured with an AVAILABILITY QoS policy will not remove samples from the DataWriter cache until they have been “delivered” to both the already- discovered DataReaders and the minimum number (quorum) of DataReaders specified for each role. In particular, samples will be retained by the DataWriter if the quorum of matched DataReaders with a particular role_name have not been discovered yet.

We used the word “delivered” in quotes above because the level of assurance a DataWriter has that a particular sample has been delivered depends on the setting of the RELIABILITY QosPolicy (Section 6.5.19). We discuss this next in Section 13.1.2.

13.1.2Ensuring Consumer Applications Process the Data Successfully

Section 13.1.1 described mechanisms by which an application could configure who the required consumers of information are. This section is about the criteria, mechanisms, and assurance provided by Connext to ensure consumers have the information delivered to them and process it in a successful manner.

RTI provides four levels of information delivery guarantee. You can set your desired level using the RELIABILITY QosPolicy (Section 6.5.19). The levels are:

Best-effort, relying only on the underlying transport The DataWriter considers the sample delivered/acknowledged as soon as it is given to the transport to send to the DataReader’s destination. Therefore, the only guarantee is the one provided by the underlying transport itself. Note that even if the underlying transport is reliable (e.g., shared memory or TCP) the reliability is limited to the transport-level buffers. There is no guarantee that the sample will arrive to the DataReader cache because after the transport delivers to the DataReader’s transport buffers, it is possible for the sample to be dropped because it exceeds a resource limit, fails to deserialize properly, the receiving application crashes, etc.

Reliable with protocol acknowledgment The DDS-RTPS reliability protocol used by Connext provides acknowledgment at the RTPS protocol level: a DataReader will acknowledge it has deserialized the sample correctly and stored it in the DataReader’s

13-3

cache. However, there is no guarantee the application actually processed the sample. The application might crash before processing the sample, or it might simply fail to read it from the cache.

Reliable with Application Acknowledgment (Auto) Application Acknowledgment in Auto mode causes Connext to send an additional application-level acknowledgment (above and beyond the RTPS protocol level acknowledgment) after the consuming application has read the sample from the DataReader cache and the application has subsequently called the DataReader’s return_loan() operation (see Section 7.4.2) for that sample. This mode guarantees that the application has fully read the sample all the way until it indicates it is done with it. However it does not provide a guarantee that the application was able to successfully interpret or process the sample. For example, the sample could be a command to execute a certain action and the application may read the sample and not understand the command or may not be able to execute the action.

Reliable with Application Acknowledgment (Explicit) Application Acknowledgment in Explicit mode causes Connext to send an application-level acknowledgment only after the consuming application has read the sample from the DataReader cache and subsequently called the DataReader’s acknowledge_sample() operation (see Section 7.4.4) for that sample. This mode guarantees that the application has fully read the sample and completed operating on it as indicated by explicitly calling acknowledge_sample(). In contrast with the Auto mode described above, the application can delay the acknowledgment of the sample beyond the time it holds onto the data buffers, allowing it to be process in a more flexible manner. Similar to the Auto mode, it does not provide a guarantee that the application was able to successfully interpret or process the sample. For example, the sample could be a command to execute a certain action and the application may read the sample and not understand the command or may not be able to execute the action. Applications that need guarantees that the data was successfully processed and interpreted should use a request-reply interaction, which is available as part of RTI Connext Messaging (see Part 4: Request-Reply Communication Pattern).

13.1.3Ensuring Information is Available to Late-Joining Applications

The third aspect of guaranteed data delivery addresses situations where the application needs to ensure that the information produced by a particular DataWriter is available to DataReaders that join the system after the data was produced. The need for data delivery may even extend beyond the lifetime of the producing application; that is, it may be required that the information is delivered to applications that join the system after the producing application has left the system.

Connext provides four mechanisms to handle these scenarios:

The DDS Durability QoS Policy. The DURABILITY QosPolicy (Section 6.5.7) specifies whether samples should be available to late joiners. The policy is set on the DataWriter and the DataReader and supports four kinds: VOLATILE, TRANSIENT_LOCAL, TRANSIENT, or PERSISTENT. If the DataWriter’s Durability QoS policy is set to VOLATILE kind, the DataWriter’s samples will not be made available to any late joiners. If the DataWriter’s policy kind is set to TRANSIENT_LOCAL, TRANSIENT, or PERSISTENT, the samples will be made available for late-joining DataReaders who also set their DURABILITY QoS policy kind to something other than VOLATILE.

Durable Writer History. A DataWriter configured with a DURABILITY QoS policy kind other than VOLATILE keeps its data in a local cache so that it is available when the late- joining application appears. The data is maintained in the DataWriter’s cache until it is considered to be no longer needed. The precise criteria depends on the configuration of additional QoS policies such as LIFESPAN QoS Policy (Section 6.5.12), HISTORY QosPolicy (Section 6.5.10), RESOURCE_LIMITS QosPolicy (Section 6.5.20), etc. For the purposes of guaranteeing information delivery it is important to note that the

13-4

DataWriter’s cache can be configured to be a memory cache or a durable (disk-based) cache. A memory cache will not survive an application restart. However, a durable (disk- based) cache can survive the restart of the producing application. The use a durable writer history, including the use of an external ODBC database as a cache is described in Durable Writer History (Section 12.3).

RTI Persistence Service. This service allows the information produced by a DataWriter to survive beyond the lifetime of the producing application. Persistence Service is an stand- alone application that runs on many supported platforms. This service complies with the Persistent Profile of the OMG DDS specification. The service uses DDS to subscribe to the DataWriters that specify a DURABILITY QosPolicy (Section 6.5.7) kind of TRANSIENT or PERSISTENT. Persistence Service receives the data from those DataWriters, stores the data in its internal caches, and makes the data available via DataWriters (which are automatically created by Persistence Service) to late-joining DataReaders that specify a Durability kind of TRANSIENT or PERSISTENT. Persistence Service can operate as a relay for the information from the original writer, preserving the source_timestamp of the data, as well as the original sample virtual writer GUID (see RTI Persistence Service (Section 12.5.1)). In addition, you can configure Persistence Service itself to use a memory- based cache or a durable (disk-based or database-based) cache. See Configuring Persistent Storage (Section 27.6). Configuration of redundant and load-balanced persistence services is also supported.

Durable Subscriptions. This is a Persistence Service configuration setting that allows configuration of the required subscriptions (Identifying the Required Consumers of Information (Section 13.1.1)) for the data stored by Persistence Service (Managing Data Instances (Working with Keyed Data Types) (Section 6.3.14)). Configuring required subscriptions for Persistence Service ensures that the service will store the samples until they have been delivered to the configured number (quorum) of DataReaders that have each of the specified roles.

13.2Scenarios

In each of the scenarios below, we assume both the DataWriter and DataReader are configured for strict reliability (RELIABLE ReliabilityQosPolicyKind and KEEP_ALL HistoryQosPolicyKind, see Section 10.3.3). As a result, when the DataWriter’s cache is full of unacknowledged samples, the write() operation will block until samples are acknowledged by all the intended consumers.

13.2.1Scenario 1: Guaranteed Delivery to a-priori known subscribers

A common use case is to guarantee delivery to a set of known subscribers. These subscribers may be already running and have been discovered, they may be temporarily non-responsive, or it could be that some of those subscribers are still not present in the system. See Figure 13.1 on page 13-6.

To guarantee delivery, the list of required subscribers should be configured using the AVAILABILITY QosPolicy (DDS Extension) (Section 6.5.1) on the DataWriters to specify the role_name and quorum for each required subscription. Similarly the ENTITY_NAME QosPolicy (DDS Extension) (Section 6.5.9) should be used on the DataReaders to specify their role_name. In

13-5

addition we use Application Acknowledgment (Section 6.3.12) to guarantee the sample was delivered and processed by the DataReader.

Figure 13.1 Guaranteed Delivery Scenario 1

The DataWriter and DataReader RELIABILITY QoS Policy can be configured for either AUTO or EXPLICIT application acknowledgment kind. As the DataWriter publishes the sample, it will await acknowledgment from the DataReader (through the protocol-level acknowledgment) and from the subscriber application (though the additional application-level acknowledgment). The DataWriter will only consider the sample acknowledged when it has been acknowledged by all discovered active DataReaders and also by the quorum of each required subscription.

In this specific scenario, DataReader #1 is configured for EXPLICIT application acknowledgment. After reading and processing the sample, the subscribing application calls acknowledge_sample() or acknowledge_all() (see Section 7.4.4). As a result, Connext will send an application-level acknowledgment to the DataWriter, which will in its turn confirm the acknowledgment.

If the sample was lost in transit, the reliability protocol will repair the sample. Since it has not been acknowledged, it remains available in the writer’s queue to be automatically resent by Connext. The sample will remain available until acknowledged by the application. If the subscribing application crashes while processing the sample and restarts, Connext will repair the unacknowledged sample. Samples which already been processed and acknowledged will not be resent.

In this scenario, DataReader #2 may be a late joiner. When it starts up, because it is configured with TRANSIENT_LOCAL Durability, the reliability protocol will re-send the samples previously sent by the writer. These samples were considered unacknowledged by the

13-6

DataWriter because they had not been confirmed yet by the required subscription (identified by its role_name: ‘logger’).

DataReader #2 does not explicitly acknowledge the samples it reads. It is configured to use AUTO application acknowledgment, which will automatically acknowledge samples that have been read or taken after the application calls the DataReader return_loan operation.

This configuration works well for situations where the DataReader may not be immediately available or may restart. However, this configuration does not provide any guarantee if the DataWriter restarts. When the DataWriter restarts, samples previously unacknowledged are lost and will no longer be available to any late joining DataReaders.

13.2.2Scenario 2: Surviving a Writer Restart when Delivering Samples to a priori Known Subscribers

Scenario 1 describes a use case where samples are delivered to a list of a priori known subscribers. In that scenario, Connext will deliver samples to the late-joining or restarting subscriber. However, if the producer is re-started the samples it had written will no longer be available to future subscribers.

To handle a situation where the producing application is restarted, we will use the Durable Writer History (Section 12.3) feature. See Figure 13.2 on page 13-8.

A DataWriter can be configured to maintain its data and state in durable storage. This configuration is done using the PROPERTY QoS policy as described in Section 12.3.2.. With this configuration the data samples written by the DataWriter and any necessary internal state is persisted by the DataWriter into durable storage As a result, when the DataWriter restarts, samples which had not been acknowledged by the set of required subscriptions will be resent and late-joining DataReaders specifying DURABILITY kind different from VOLATILE will receive the previously-written samples.

13.2.3Scenario 3: Delivery Guaranteed by Persistence Service (Store and Forward) to a priori Known Subscribers

Previous scenarios illustrated that using the DURABILITY, RELIABILITY, and AVAILABILITY QoS policies we can ensure that as long as the DataWriter is present in the system, samples written by a DataWriter will be delivered to the intended consumers. The use of the durable writer history in the previous scenario extended this guarantee even in the presence of a restart of the application writing the data.

This scenario addresses the situation where the originating application that produced the data is no longer available. For example, the network could have become partitioned, the application could have been terminated, it could have crashed and not have been restarted, etc.

In order to deliver data to applications that appear after the producing application is no longer available on the network it is necessary to have another service that stores those samples and delivers them. This is the purpose of the RTI Persistence Service.

The RTI Persistence Service can be configured to automatically discover DataWriters that specify a DURABILITY QoS with kind TRANSIENT or PERSISTENT and automatically create pairs (DataReader, DataWriter) that receive and store that information (see Chapter 26: Introduction to RTI Persistence Service). All the DataReaders created by the RTI Persistence Service have the ENTITY_QOS policy set with the role_name of “PERSISTENCE_SERVICE”. This allows an application to specify Persistence Service as one of the required subscriptions for its DataWriters.

In this third scenario, we take advantage of this capability to configure the DataWriter to have the RTI Persistence Service as a required subscription. See Figure 13.3 on page 13-8.

The RTI Persistence Service can also have its DataWriters configured with required subscriptions. This feature is known as Persistence Service “durable subscriptions”. DataReader #1 is pre configured in Persistence Service as a Durable Subscription. (Alternatively, DataReader #1 could

13-7

Figure 13.2 Guaranteed Delivery Scenario 2

Figure 13.3 Guaranteed Delivery Scenario 3

13-8

have registered itself dynamically as Durable Subscription using the DomainParticipant register_durable_subscription() operation).

We also configure the RELIBILITY QoS policy setting of the AcknowledgmentKind to APPLICATION_AUTO_ACKNOWLEDGMENT_MODE in order to ensure samples are stored in the Persistence Service and properly processed on the consuming application prior to them being removed from the DataWriter cache.

With this configuration in place the DataWriter will deliver samples to the DataReader and to the Persistence Service reliably and wait for the Application Acknowledgment from both. Delivery of samples to DataReader #1 and the Persistence Service occurs concurrently. The Persistence Service in turn takes responsibility to deliver the samples to the configured “logger” durable subscription. If the original publisher is no longer available, samples can still be delivered by the Persistence Service. to DataReader #1 and any other late-joining DataReaders.

When DataReader #1 acknowledges the sample through an application-acknowledgment message, both the original DataWriter and Persistence Service will receive the application- acknowledgment. RTI Connext takes advantage of this to reduce or eliminate delivery if duplicate samples, that is, the Persistence Service can notice that DataReader #1 has acknowledged a sample and refrain from separately sending the same sample to DataReader #1.

13.2.3.1Variation: Using Redundant Persistence Services

Using a single Persistence Service to guarantee delivery can still raise concerns about having the Persistence Service as a single point of failure. To provide a level of added redundancy, the publisher may be configured to await acknowledgment from a quorum of multiple persistence services (role_name remains PERSISTENCE). Using this configuration we can achieve higher levels of redundancy

Figure 13.4 Guaranteed Delivery Scenario 3 with Redundant Persistence Service

The RTI Persistence Services will automatically share information to keep each other synchronized. This includes both the data and also the information on the durable subscriptions. That is, when a Persistence Service discovers a durable subscription, information about durable subscriptions is automatically replicated and synchronized among persistence services (CITE: New section to be written in Persistence Service Chapter).

13-9

13.2.3.2Variation: Using Load-Balanced Persistent Services

The Persistence Service will store samples on behalf of many DataWriters and, depending on the configuration, it might write those samples to a database or to disk. For this reason the Persistence Service may become a bottleneck in systems with high durable sample throughput.

It is possible to run multiple instances of the Persistence Service in a manner where each is only responsible for the guaranteed delivery of certain subset of the durable data being published. These Persistence Service can also be run different computers and in this manner achieve much higher throughput. For example, depending on the hardware, using typical hard-drives a single a Persistence Service may be able to store only 30000 samples per second. By running 10 persistence services in 10 different computers we would be able to handle storing 10 times that system-wide, that is, 300000 samples per second.

The data to be persisted can be partitioned among the persistence services by specifying different Topics to be persisted by each Persistence Service. If a single Topic has more data that can be handled y a single Persistence Service it is also possible to specify a content-filter so that only the data within that Topic that matches the filter will be stored by the Persistence Service. For example assume the Topic being persisted has an member named “x” of type float. It is possible to configure two Persistence Services one with the filter “x>10”, and the other “x <=10”, such that each only stores a subject of the data published on the Topic. See also: Configuring Durable Subscriptions in Persistence Service (Section 27.9).

13-10

Chapter 14 Discovery

This chapter discusses how Connext objects on different nodes find out about each other using the default Simple Discovery Protocol (SDP). It describes the sequence of messages that are passed between Connext on the sending and receiving sides.

This chapter includes the following sections:

What is Discovery? (Section 14.1)

Configuring the Peers List Used in Discovery (Section 14.2)

Discovery Implementation (Section 14.3)

Debugging Discovery (Section 14.4)

Ports Used for Discovery (Section 14.5)

The discovery process occurs automatically, so you do not have to implement any special code. We recommend that all users read What is Discovery? (Section 14.1) and Configuring the Peers List Used in Discovery (Section 14.2). The remaining sections contain advanced material for those who have a particular need to understand what is happening ‘under the hood.’ This information can help you debug a system in which objects are not communicating.

You may also be interested in reading Chapter 15: Transport Plugins , as well as learning about these QosPolicies:

TRANSPORT_SELECTION QosPolicy (DDS Extension) (Section 6.5.22)

TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7)

TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23)

TRANSPORT_MULTICAST QosPolicy (DDS Extension) (Section 7.6.5)

14.1What is Discovery?

Discovery is the behind-the-scenes way in which Connext objects (DomainParticipants, DataWriters, and DataReaders) on different nodes find out about each other. Each DomainParticipant maintains a database of information about all the active DataReaders and DataWriters that are in the same domain. This database is what makes it possible for DataWriters and DataReaders to communicate. To create and refresh the database, each application follows a common discovery process.

This chapter describes the default discovery mechanism known as the Simple Discovery Protocol, which includes two phases: Simple Participant Discovery (Section 14.1.1) and Simple

14-1

Endpoint Discovery (Section 14.1.2). (Discovery can also be performed using the Enterprise Discovery Protocol—this requires a separately purchased package, RTI Enterprise Discovery Service.)

The goal of these two phases is to build, for each DomainParticipant, a complete picture of all the entities that belong to the remote participants that are in its peers list. The peers list is the list of nodes with which a participant may communicate. It starts out the same as the initial_peers list that you configure in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2). If the accept_unknown_peers flag in that same QosPolicy is TRUE, then other nodes may also be added as they are discovered; if it is FALSE, then the peers list will match the initial_peers list, plus any peers added using the DomainParticipant’s add_peer() operation.

14.1.1Simple Participant Discovery

This phase of the Simple Discovery Protocol is performed by the Simple Participant Discovery Protocol (SPDP).

During the Participant Discovery phase, DomainParticipants learn about each other. The DomainParticipant’s details are communicated to all other DomainParticipants in the same domain by sending participant declaration messages, also known as participant DATA submessages. The details include the DomainParticipant’s unique identifying key (GUID or Globally Unique ID described below), transport locators (addresses and port numbers), and QoS. These messages are sent on a periodic basis using best-effort communication.

Participant DATAs are sent periodically to maintain the liveliness of the DomainParticipant. They are also used to communicate changes in the DomainParticipant’s QoS. Only changes to QosPolicies that are part of the DomainParticipant’s built-in data (namely, the USER_DATA QosPolicy (Section 6.5.25)) need to be propagated.

When a DomainParticipant is deleted, a participant DATA (delete) submessage with the

DomainParticipant's identifying GUID is sent.

The GUID is a unique reference to an entity. It is composed of a GUID prefix and an Entity ID. By default, the GUID prefix is calculated from the IP address and the process ID. (For more on how the GUID is calculated, see Controlling How the GUID is Set (rtps_auto_id_kind) (Section 8.5.9.4).) The IP address and process ID are stored in the DomainParticipant’s WIRE_PROTOCOL QosPolicy (DDS Extension) (Section 8.5.9). The entityID is set by Connext (you may be able to change it in a future version).

Once a pair of remote participants have discovered each other, they can move on to the Endpoint Discovery phase, which is how DataWriters and DataReaders find each other.

14.1.2Simple Endpoint Discovery

This phase of the Simple Discovery Protocol is performed by the Simple Endpoint Discovery Protocol (SEDP).

During the Endpoint Discovery phase, Connext matches DataWriters and DataReaders. Information (GUID, QoS, etc.) about your application’s DataReaders and DataWriters is exchanged by sending publication/subscription declarations in DATA messages that we will refer to as publication DATAs and subscription DATAs. The Endpoint Discovery phase uses reliable communication.

As described in Section 14.3, these declaration or DATA messages are exchanged until each DomainParticipant has a complete database of information about the participants in its peers list and their entities. Then the discovery process is complete and the system switches to a steady state. During steady state, participant DATAs are still sent periodically to maintain the liveliness status of participants. They may also be sent to communicate QoS changes or the deletion of a

DomainParticipant.

14-2

When a remote DataWriter/DataReader is discovered, Connext determines if the local application has a matching DataReader/DataWriter. A ‘match’ between the local and remote entities occurs only if the DataReader and DataWriter have the same Topic, same data type, and compatible QosPolicies (which includes having the same partition name string, see Section 6.4.5). Furthermore, if the DomainParticipant has been set up to ignore certain DataWriters/DataReaders, those entities will not be considered during the matching process. See Section 16.4.2 for more on ignoring specific publications and subscriptions.

This ‘matching’ process occurs as soon as a remote entity is discovered, even if the entire database is not yet complete: that is, the application may still be discovering other remote entities.

A DataReader and DataWriter can only communicate with each other if each one’s application has hooked up its local entity with the matching remote entity. That is, both sides must agree to the connection.

Section 14.3 describes the details about the discovery process.

14.2Configuring the Peers List Used in Discovery

The Connext discovery process will try to contact all possible participants on each remote node in the ‘initial peers list,’ which comes from the initial_peers field of the DomainParticipant’s DISCOVERY QosPolicy.

The ‘initial peers list’ is just that: an initial list of peers to contact. Furthermore, the peers list merely contains potential peers—there is no requirement that there actually be Connext applications on the hosts in the list.

After startup, you can add to the ‘peers list’ with the add_peer() operation (see Adding and Removing Peers List Entries (Section 8.5.2.3)). The ‘peer list’ may also grow as peers are automatically discovered (if accept_unknown_peers is TRUE, see Controlling Acceptance of Unknown Peers (Section 8.5.2.6)).

When you call get_default_participant_qos() for a DomainParticipantFactory, the values used for the DiscoveryQosPolicy’s initial_peers and multicast_receive_addresses may come from the following:

A file named NDDS_DISCOVERY_PEERS, which is formatted as described in NDDS_DISCOVERY_PEERS File Format (Section 14.2.3). The file must be in the same directory as your application’s executable.

An environment variable named NDDS_DISCOVERY_PEERS, defined as a comma- separated list of peer descriptors (see NDDS_DISCOVERY_PEERS Environment Variable Format (Section 14.2.2)).

The value specified in the default XML QoS profile (see Overwriting Default QoS Values (Section 17.9.4)).

If NDDS_DISCOVERY_PEERS (file or environment variable) does not contain a multicast address, then multicast_receive_addresses is cleared and the RTI discovery process will not listen for discovery messages via multicast.

If NDDS_DISCOVERY_PEERS (file or environment variable) contains one or more multicast addresses, the addresses are stored in multicast_receive_addresses, starting at element 0. They will be stored in the order in which they appear in NDDS_DISCOVERY_PEERS.

Note: Setting initial_peers in the default XML QoS Profile does not modify the value of multicast_receive_address.

If both the file and environment variable are found, the file takes precedence and the environment variable will be ignored.1 The settings in the default XML QoS Profile take

14-3

precedence over the file and environment variable. In the absence of a file, environment variable, or default XML QoS profile values, Connext will use a default value. See the API Reference HTML documentation for details (in the section on the DISCOVERY QosPolicy).

If initial peers are specified in both the currently loaded QoS XML profile and in the NDDS_DISCOVERY_PEERS file, the values in the profile take precedence.

The file, environment variable, and default XML QoS Profile make it easy to reconfigure which nodes will take part in the discovery process—without recompiling your application.

The file, environment variable, and default XML QoS Profile are the possible sources for the default initial peers list. You can, of course, explicitly set the initial list by changing the values in the QoS provided to the DomainParticipantFactory's create_participant() operation, or by adding to the list after startup with the DomainParticipant’s add_peer() operation (see Section 8.5.2.3).

If you set NDDS_DISCOVERY_PEERS and You Want to Communicate over Shared Memory:

Suppose you want to communicate with other Connext applications on the same host and you are explicitly setting NDDS_DISCOVERY_PEERS (generally in order to use unicast discovery with applications on other hosts).

If the local host platform does not support the shared memory transport, then you can include the name of the local host in the NDDS_DISCOVERY_PEERS list. (To check if your platform supports shared memory, see the Platform Notes document.)

If the local host platform supports the shared memory transport, then you must do one of the following:

Include "shmem://" in the NDDS_DISCOVERY_PEERS list. This will cause shared memory to be used for discovery and data traffic for applications on the same host.

or:

Include the name of the local host in the NDDS_DISCOVERY_PEERS list, and disable the shared memory transport in the TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7) of the DomainParticipant. This will cause UDP loopback to be used for discovery and data traffic for applications on the same host.

14.2.1Peer Descriptor Format

A peer descriptor string specifies a range of participants at a given locator. Peer descriptor strings are used in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) initial_peers field (see Section 8.5.2.2) and the DomainParticipant’s add_peer() and remove_peer() operations (see Section 8.5.2.3).

The anatomy of a peer descriptor is illustrated in Figure 14.1 using a special "StarFabric" transport example.

A peer descriptor consists of:

[optional] A participant ID. If a simple integer is specified, it indicates the maximum participant ID to be contacted by the Connext discovery mechanism at the given locator. If that integer is enclosed in square brackets (e.g., [2]), then only that Participant ID will be used. You can also specify a range in the form of [a,b]: in this case only the Participant IDs in that specific range are contacted. If omitted, a default value of 4 is implied.

A locator, as described in Section 14.2.1.1.

These are separated by the '@' character. The separator may be omitted if a participant ID limit is not explicitly specified.

1. This is true even if the file is empty.

14-4

Figure 14.1 Peer Descriptor Address String

The "participant ID limit" only applies to unicast locators; it is ignored for multicast locators (and therefore should be omitted for multicast peer descriptors).

14.2.1.1Locator Format

A locator string specifies a transport and an address in string format. Locators are used to form peer descriptors. A locator is equivalent to a peer descriptor with the default participant ID limit

(4).

A locator consists of:

[optional] Transport name (alias or class). This identifies the set of transport plug-ins (transport aliases) that may be used to parse the address portion of the locator. Note that a transport class name is an implicit alias used to refer to all the transport plug-in instances of that class.

[optional] An address, as described in Section 14.2.1.2.

These are separated by the "://" string. The separator is specified if and only if a transport name is specified.

If a transport name is specified, the address may be omitted; in that case all the unicast addresses (across all transport plug-in instances) associated with the transport class are implied. Thus, a locator string may specify several addresses.

If an address is specified, the transport name and the separator string may be omitted; in that case all the available transport plug-ins for the Entity may be used to parse the address string.

The transport names for the built-in transport plug-ins are:

shmem - Shared Memory Transport

udpv4 - UDPv4 Transport

udpv6 - UDPv6 Transport

14-5

14.2.1.2Address Format

An address string specifies a transport-independent network address that qualifies a transport- dependent address string. Addresses are used to form locators. Addresses are also used in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) multicast_receive_addresses and the

DDS_TransportMulticastSettings_t::receive_address fields. An address is equivalent to a locator in which the transport name and separator are omitted.

An address consists of:

[optional] A network address in IPv4 or IPv6 string notation. If omitted, the network address of the transport is implied.

[optional] A transport address, which is a string that is passed to the transport for processing. The transport maps this string into

NDDS_Transport_Property_t::address_bit_count bits. If omitted, the network address is used as the fully qualified address.

These are separated by the '#' character. If a separator is specified, it must be followed by a non- empty string which is passed to the transport plug-in.

The bits resulting from the transport address string are prepended with the network address. The least significant NDDS_Transport_Property_t::address_bit_count bits of the network address are ignored.

If you omit the ‘#’ separator and the string is not a valid IPv4 or IPv6 address, it is treated as a transport address with an implicit network address (of the transport plug-in).

14.2.2NDDS_DISCOVERY_PEERS Environment Variable Format

You can set the default value for the initial peers list in an environment variable named NDDS_DISCOVERY_PEERS. Multiple peer descriptor entries must be separated by commas. Table 14.1 shows some examples. The examples use an implied maximum participant ID of 4 unless otherwise noted. (If you need instructions on how to set environment variables, see the Getting Started Guide).

Table 14.1 NDDS_DISCOVERY_PEERS Environment Variable Examples

NDDS_DISCOVERY_PEERS

Description of Host(s)

 

 

 

 

239.255.0.1

multicast

 

 

localhost

localhost

 

 

192.168.1.1

10.10.30.232 (IPv4)

 

 

FAA0::1

FAA0::0 (IPv6)

 

 

himalaya,gangotri

himalaya and gangotri

 

 

1@himalaya,1@gangotri

himalaya and gangotri (with a maximum participant ID of 1 on each

host)

 

 

 

FAA0::0localhost

FAA0::0localhost (could be a UDPv4 transport plug-in registered at

network address of FAA0::0) (IPv6)

 

 

 

udpv4://himalaya

himalaya accessed using the "udpv4" transport plug-ins) (IPv4)

 

 

udpv4://FAA0::0localhost

localhost using the "udpv4" transport plug-ins) registered at network

address FAA0::0

 

 

 

udpv4://

all unicast addresses accessed via the "udpv4" (UDPv4) transport

plug-ins)

 

 

 

0/0/R

0/0/R (StarFabric)

#0/0/R

 

 

 

starfabric://0/0/R

0/0/R (StarFabric) using the "starfabric" (StarFabric) transport plug-

starfabric://#0/0/R

ins

 

 

14-6

Table 14.1 NDDS_DISCOVERY_PEERS Environment Variable Examples

NDDS_DISCOVERY_PEERS

Description of Host(s)

 

 

 

 

starfabric://FBB0::0#0/0/R

0/0/R (StarFabric) using the "starfabric" (StarFabric) transport plug-

ins registered at network address FAA0::0

 

 

starfabric://

all unicast addresses accessed via the "starfabric" (StarFabric)

transport plug-ins

 

 

 

shmem://

all unicast addresses accessed via the "shmem" (shared memory)

transport plug-ins

 

 

 

shmem://FCC0::0

all unicast addresses accessed via the "shmem" (shared memory)

transport plug-ins registered at network address FCC0::0

 

 

 

14.2.3NDDS_DISCOVERY_PEERS File Format

You can set the default value for the initial peers list in a file named NDDS_DISCOVERY_PEERS. The file must be in the your application’s current working directory.

The file is optional. If it is found, it supersedes the values in any environment variable of the same name.

Entries in the file must contain a sequence of peer descriptors separated by whitespace or the comma (',') character. The file may also contain comments starting with a semicolon (';') character until the end of the line.

Example file contents:

;;NDDS_DISCOVERY_PEERS - Default Discovery Configuration File

;;Multicast builtin.udpv4://239.255.0.1 ; default discovery multicast addr

;;Unicast

localhost,192.168.1.1

; A comma can be used a separator

FAA0::1 FAA0::0#localhost ; Whitespace can be used as a separator

1@himalaya

; Max participant ID of 1 on 'himalaya'

1@gangotri

 

;; UDPv4

 

udpv4://himalaya

; 'himalaya' via 'udpv4' transport plugin(s)

udpv4://FAA0::0#localhost ; 'localhost' via 'updv4' transport plugin

 

;

registered at network address FAA0::0

;; Shared Memory

 

 

shmem://

; All 'shmem' transport plugin(s)

builtin.shmem://

; The builtin builtin 'shmem' transport plugin

shmem://FCC0::0

; Shared memory transport plugin registered

 

;

at network address FCC0::0

;; StarFabric

 

 

0/0/R

; StarFabric node 0/0/R

starfabric://0/0/R

; 0/0/R accessed via 'starfabric'

 

;

transport plugin(s)

starfabric://FBB0::0#0/0/R ; StarFabric transport plugin registered

 

;

at network address FBB0::0

starfabric://

; All 'starfabric' transport plugin(s)

14-7

14.3Discovery Implementation

Note: this section contains advanced material not required by most users.

Discovery is implemented using built-in DataWriters and DataReaders. These are the same class of entities your application uses to send/receive data. That is, they are also of type

DDSDataWriter/DDSDataReader. For each DomainParticipant, three built-in DataWriters and three built-in DataReaders are automatically created for discovery purposes. Figure 14.2 shows how these objects are used. (For more on built-in DataReaders and DataWriters, see Chapter 16: Built-In Topics).

Figure 14.2 Built-in Writers and Readers for Discovery

DomainParticipant

 

 

 

 

 

participant DATA

 

Advertises this

Builtin

 

 

 

 

 

 

 

 

Participant

participant

DataWriter

 

“DCPSParticipant” builtin topic

Discovery

 

 

 

 

 

 

 

Phase

Discovers other

Builtin

 

 

participant DATA

 

DataReader

 

 

 

 

 

 

participants

 

“DCPSParticipant” builtin topic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

publication DATA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Builtin

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Advertises this

 

 

 

 

DataWriter

 

“DCPSPublication” builtin topic

 

 

 

 

 

 

 

 

 

 

 

participant’s

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataWriters and

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

subscription DATA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataReaders

 

 

 

 

 

Builtin

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataWriter

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Endpoint

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

“DCPSSubscription” builtin topic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(Writer/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Reader)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Discovery

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

publication DATA

 

 

 

Builtin

 

 

 

 

 

 

 

Phase

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Discovers other

 

 

 

 

DataReader

 

“DCPSPublication” builtin topic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

participants’

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataWriters and

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataReaders

 

 

 

 

 

Builtin

 

 

 

 

 

subscription DATA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DataReader

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

“DCPSSubscription” builtin topic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Network

For each DomainParticipant, there are six objects automatically created for discovery purposes. The top two objects are used to send/receive participant DATA messages, which are used in the Participant Discovery phase to find remote DomainParticipants. This phase uses best-effort communications. Once the participants are aware of each other, they move on to the Endpoint Discovery Phase to learn about each other’s DataWriters and DataReaders. This phase uses reliable communications.

The implementation is split into two separate protocols:

Simple Participant Discovery Protocol (SPDP)

+Simple Endpoint Discovery Protocol (SEDP)

=Simple Discovery Protocol (SDP)

14.3.1Participant Discovery

When a DomainParticipant is created, a DataWriter and a DataReader are automatically created to exchange participant DATA messages in the network. These DataWriters and DataReaders are "special" because the DataWriter can send to a given list of destinations, regardless of whether there is a Connext application at the destination, and the DataReader can receive data from any

14-8

source, whether the source is previously known or not. In other words, these special readers and writers do not need to discover the remote entity and perform a match before they can communicate with each other.

When a DomainParticipant joins or leaves the network, it needs to notify its peer participants. The list of remote participants to use during discovery comes from the peer list described in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2). The remote participants are notified via participant DATA messages. In addition, if a participant’s QoS is modified in such a way that other participants need to know about the change (that is, changes to the USER_DATA QosPolicy (Section 6.5.25)), a new participant DATA will be sent immediately.

Participant DATAs are also used to maintain a participant’s liveliness status. These are sent at the rate set in the participant_liveliness_assert_period in the DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3).

Let’s examine what happens when a new remote participant is discovered. If the new remote participant is in the local participant's peer list, the local participant will add that remote participant into its database. If the new remote participant is not in the local application's peer list, it may still be added, if the accept_unknown_peers field in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) is set to TRUE.

Once a remote participant has been added to the Connext database, Connext keeps track of that remote participant’s participant_liveliness_lease_duration. If a participant DATA for that participant (identified by the GUID) is not received at least once within the participant_liveliness_lease_duration, the remote participant is considered stale, and the remote participant, together with all its entities, will be removed from the database of the local participant.

To keep from being purged by other participants, each participant needs to periodically send a participant DATA to refresh its liveliness. The rate at which the participant DATA is sent is controlled by the participant_liveliness_assert_period in the participant’s DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3). This exchange, which keeps Participant A from appearing ‘stale,’ is illustrated in Figure 14.3. Figure 14.4 shows what happens when Participant A terminates ungracefully and therefore needs to be seen as ‘stale.’

14.3.1.1Refresh Mechanism

To ensure that a late-joining participant does not need to wait until the next refresh of the remote participant DATA to discover the remote participant, there is a resend mechanism. If the received participant DATA is from a never-before-seen remote participant, and it is in the local participant's peers list, the application will resend its own participant DATA to all its peers. This resend can potentially be done multiple times, with a random sleep time in between. Figure 14.5 illustrates this scenario.

The number of retries and the random amount of sleep between them are controlled by each participant’s DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3) (see and in Figure 14.5).

Figure 14.6 provides a summary of the messages sent during the participant discovery phase.

14-9

Figure 14.3 Periodic ‘participant DATAs’

Node A

Participant created

Participant’s UserDataQosPolicy modified

Participant A’s DDS_DomainParticipantQos.discovery_config.

participant_liveliness_assert_period

Random time between min_initial_participant_announcement_period and

max_initial_participant_announcement_period (in A’s

DDS_DomainParticipantQos.discovery_config)

Participant destroyed

Node B

participant A DATA

participant A DATA (delete)

The DomainParticipant on Node A sends a ‘participant DATA’ to Node B, which is in Node A’s peers list. This occurs regardless of whether or not there is a Connext application on Node B.

The green short dashed lines are periodic participant DATAs. The time between these messages is controlled by the participant_liveliness_assert_period in the DiscoveryConfig QosPolicy.

In addition to the periodic participant DATAs, ‘initial repeat messages’ (shown in blue, with longer dashes) are sent from A to B. These messages are sent at a random time between min_initial_participant_announcement_period and max_initial_participant_announcement_period (in A’s DiscoveryConfig QosPolicy). The number of these initial repeat messages is set in initial_participant_announcements.

14-10

Figure 14.4 Ungraceful Termination of a Participant

Node A

Node B

 

 

Participant created

Participant created

participant A

DATA

 

 

 

New remote participant A

 

 

added to database

 

 

 

Participant ungracefully terminated

Participant A’s DDS_DomainParticipantQos.discovery_config. participant_liveliness_assert_period

Participant A’s DDS_DomainParticipantQos.discovery_config. participant_liveliness_lease_duration

Remote participant A considered ‘stale,’ removed from database

Participant A is removed from participant B’s database if it is not refreshed within the liveliness lease duration. Dashed lines are periodic participant DATA messages.

(Periodic resends of ‘participant B DATA’ from B to A are omitted from this diagram for simplicity. Initial repeat messages from A to B are also omitted from this diagram—these messages are sent at a random time between min_initial_participant_announcement_period and max_initial_participant_announcement_period, see Figure 14.3.)

14-11

Figure 14.5 Resending ‘participant DATA’ to a Late-Joiner

Node A

Node B

Participant created

 

participant A

 

DATA

 

 

 

 

 

 

participant B

 

 

 

 

 

DATA

New remote participant B

 

 

added to database

 

 

 

 

 

 

 

 

 

 

participant A

 

 

 

 

 

 

 

 

 

 

 

DATA

 

 

 

 

 

 

participant A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DATA

participant B already in

 

participant B

database, no action taken

 

DATA

 

 

 

 

 

 

Participant created

New remote participant A added to database

Resend participant DATA to all peers

participant A already in database, no action taken

wait random time (between min_initial_participant_announcement_period and max_initial_participant_announcement_period) for [initial_participant_announcements] = 2 (using values from A’s DiscoveryQosPolicy)

same as , but using participant B’s QoS

Participant A has Participant B in its peers list. Participant B does not have Participant A in its peers list, but [DiscoveryQosPolicy.accept_unknown_peers] is set to DDS_BOOLEAN_TRUE. Participant A joins the system after B has sent its initial announcement. After B discovers A, it waits for time , then resends its participant DATA.

(Initial repeat messages are omitted from this diagram for simplicity, see Figure 14.3.)

14-12

Figure 14.6 Participant Discovery Summary

Node A

Node B

Participant created

 

 

p

 

 

 

 

 

 

 

 

artic

 

 

 

 

 

 

 

 

ip

 

 

 

 

 

 

 

 

ant

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

AT

initial

 

 

 

 

 

A

 

 

r

 

 

 

 

 

 

 

 

e

 

 

 

 

 

p

a

rticip

p

e

a

t of

 

 

a

 

 

 

 

 

 

 

 

 

nt

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

 

A

 

Newly discovered participant B added to database

 

 

 

 

 

A

 

Participant created

 

 

t B DAT

 

 

pan

 

 

 

 

 

rtici

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

 

 

 

t of

 

 

 

 

 

 

 

 

 

 

pea

 

 

 

 

al re

 

 

 

 

 

initi

 

 

 

TA

 

 

 

 

 

 

nt B

DA

 

 

 

 

pa

 

 

 

 

rtici

 

 

 

 

p

 

pa

 

 

 

 

 

 

 

 

 

 

 

 

artici

 

 

 

 

 

 

 

pant A

 

 

Newly discovered

 

 

 

 

DA

 

 

 

 

 

 

TA

 

participant A added

 

 

 

 

 

 

 

to database

Participant B already in database, no action required

wait random time (between min_initial_participant_announcement_period and max_initial_participant_announcement_period) for [initial_participant_announcements] = 1 (using values from A’s DiscoveryQosPolicy)

Participants have discovered

 

 

 

 

 

each other

 

 

 

 

 

 

 

 

 

 

ATA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ant

B D

 

 

 

 

 

 

 

ticip

 

 

 

 

 

 

par

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

er

 

 

 

 

 

 

 

 

 

 

iodic

 

 

 

 

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

 

 

rticip

 

 

 

 

 

 

 

 

 

 

a

 

 

 

 

 

 

 

 

 

 

nt

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

TA

 

 

 

 

 

 

 

nt B

DA

 

 

 

 

 

 

 

cipa

 

 

 

 

 

 

 

 

arti

 

 

 

 

 

 

 

ic p

 

 

 

 

 

 

 

iod

 

 

 

 

 

 

 

 

 

per

 

 

 

 

 

 

 

 

 

 

same as , but using Participant B’s QoS

 

 

ic

 

 

TA

 

 

riod

 

 

 

 

 

pe

 

 

t B

DA

 

 

 

 

an

 

 

 

 

 

ip

 

 

 

 

 

rtic

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

er

 

 

 

 

 

 

 

iodic

 

 

 

 

 

 

p

 

 

 

 

 

 

 

ar

 

 

 

 

 

 

 

ti

c

 

 

 

 

 

 

 

ipant

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

A

 

Participants A and B both have each other in their peers lists. Participant A is created first.

14-13

14.3.1.2Maintaining DataWriter Liveliness for kinds AUTOMATIC and MANUAL_BY_PARTICIPANT

To maintain the liveliness of DataWriters that have a LIVELINESS QosPolicy (Section 6.5.13) kind field set to AUTOMATIC or MANUAL_BY_PARTICIPANT, Connext uses a built-in

DataWriter and DataReader pair, referred to as the inter-participant reader and inter-participant writer.

If the DomainParticipant has any DataWriters with Liveliness QosPolicy kind set to AUTOMATIC, the inter-participant writer will reliably broadcast an AUTOMATIC liveliness message at a period equal to the shortest lease_duration of these DataWriters. (The lease_duration is a field in the LIVELINESS QosPolicy (Section 6.5.13).) Figure 14.7 illustrates this scenario.

Figure 14.7 DataWriter with AUTOMATIC Liveliness

Node A

Node B

Participant created

DataWriter C created with liveliness kind = AUTOMATIC

DataWriter C’s DDS_DataWriterQos.liveliness.lease_duration

 

A

 

 

 

 

UT

 

 

 

 

OM

 

 

l

 

A

 

 

iv

TI

C

 

 

elin

 

 

 

 

ess

 

 

 

 

mess

 

 

 

 

a

 

 

 

 

ge

 

 

 

 

 

 

A liveliness message is sent automatically when a DataWriter with AUTOMATIC Liveliness kind is created, and then periodically, every DDS_DataWriterQos.liveliness.lease_duration.

If the DomainParticipant has any DataWriters with Liveliness QosPolicy kind set to MANUAL_BY_PARTICIPANT, Connext will periodically check to see if any of them have called write(), assert_liveliness(), dispose() or unregister(). The rate of this check is every X seconds, where X is the smallest lease_duration among all the DomainParticipant's

MANUAL_BY_PARTICIPANT DataWriters. (The lease_duration is a field in the LIVELINESS QosPolicy (Section 6.5.13).) If any of the MANUAL_BY_PARTICIPANT DataWriters have called any of those operations, the inter-participant writer will reliably broadcast a MANUAL liveliness message.

If a DomainParticipant's assert_liveliness() operation is called, and that DomainParticipant has any MANUAL_BY_PARTICIPANT DataWriters, the inter-participant writer will reliably broadcast a MANUAL liveliness message within the above-defined X time period. These MANUAL liveliness messages are used to update the liveliness of all the DomainParticipant's

MANUAL_BY_PARTICIPANT DataWriters, as well as the liveliness of the DomainParticipant itself. Figure 14.8 shows an example sequence.

The inter-participant reader receives data from remote inter-participant writers and asserts the liveliness of remote DomainParticipants endpoints accordingly.

If the DomainParticipant has no DataWriters with LIVELINESS QosPolicy (Section 6.5.13) kind set to AUTOMATIC or MANUAL_BY_PARTICIPANT, then no liveliness messages are ever sent from the inter-participant writer.

14.3.2Endpoint Discovery

14-14

Figure 14.8 DataWriter with MANUAL_BY_PARTICIPANT Liveliness

Node A

Node B

Participant created

DomainParticipant::assert_liveliness()

(no liveliness message is sent)

DataWriter C created with liveliness kind = MANUAL_BY_PARTICIPANT

DomainParticipant::assert_liveliness() (causes Liveliness message to be sent later)

M

A

 

 

N

 

Li

U

 

A

v

 

L

 

eli

 

 

ness m

 

 

ess

 

 

age

Calling assert_liveliness(), write(), dispose(), or

 

unregister_instance() on DataWriter C

(causes Liveliness message to be sent later)

 

 

 

DataWriter C’s

DDS_DataWriterQos.liveliness.lease_duration

M

A

 

 

 

N

 

 

Li

U

 

 

A

 

v

 

L

 

 

eli

 

 

 

ness m

 

 

 

ess

 

 

 

age

 

 

 

 

 

Once a MANUAL_BY_PARTICIPANT DataWriter is created, subsequent calls to assert_liveliness, write, dispose, or unregister_instance will trigger Liveliness messages, which update the liveliness status of all the participant’s DataWriters, and the participant itself.

As we saw in Figure 14.2 on page 14-8, reliable DataReaders and Datawriters are automatically created to exchange publication/subscription information for each DomainParticipant. We will refer to these as ‘discovery endpoint readers and writers.’ However, nothing is sent through the network using these entities until they have been ‘matched’ with their remote counterparts. This ‘matching’ is triggered by the Participant Discovery phase. The goal of the Endpoint Discovery phase is to add the remote endpoint to the local database, so that user-created endpoints (your application’s DataWriters/DataReaders) can communicate with each other.

When a new remote DomainParticipant is discovered and added to a participant’s database, Connext assumes that the remote DomainParticipant is implemented in the same way and therefore is creating the appropriate counterpart entities. Therefore, Connext will automatically add two remote discovery endpoint readers and two remote discovery endpoint writers for that remote DomainParticipant into the local database. Once that is done, there is now a match with the local discovery endpoint writers and readers, and publication DATAs and subscription DATAs can then be sent between the discovery endpoint readers/writers of the two

DomainParticipant.

When you create a DataWriter/DataReader for your user data, a publication/subscription DATA describing the newly created object is sent from the local discovery endpoint writer to the remote discovery endpoint readers of the remote DomainParticipants that are currently in the local database.

If your application changes any of the following QosPolicies for a local user-data DataWriter/ DataReader, a modified subscription/publication DATA is sent to propagate the QoS change to other DomainParticipants:

14-15

TOPIC_DATA QosPolicy (Section 5.2.1)

GROUP_DATA QosPolicy (Section 6.4.4)

USER_DATA QosPolicy (Section 6.5.25)

USER_DATA QosPolicy (Section 6.5.25)

OWNERSHIP_STRENGTH QosPolicy (Section 6.5.16)

PARTITION QosPolicy (Section 6.4.5)

TIME_BASED_FILTER QosPolicy (Section 7.6.4)

LIFESPAN QoS Policy (Section 6.5.12)

What the above QosPolicies have in common is that they are all changeable and part of the built- in data (see Chapter 16: Built-In Topics).

Similarly, if the application deletes any user-data writers/readers, the discovery endpoint writer/readers send delete publication/subscription DATAs. In addition to sending publication/ subscription DATAs, the discovery endpoint writer will check periodically to see if the remote discovery endpoint reader is up-to-date. (The rate for this check is the publication_writer.heartbeat_period or subscription_writer.heartbeat_period in the DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3). If the discovery endpoint writer has not been acknowledged by the remote discovery endpoint reader regarding receipt of the latest DATA, the discovery endpoint writer will send a special Heartbeat (HB) message with the Final bit set to 0 (F=0) to request acknowledgement from the remote discovery endpoint reader, as seen in Figure 14.9.

Discovery endpoint writers and readers have their HISTORY QosPolicy (Section 6.5.10) set to KEEP_LAST, and their DURABILITY QosPolicy (Section 6.5.7) set to TRANSIENT_LOCAL. Therefore, even if the remote DomainParticipant has not yet been discovered at the time the local user’s DataWriter/DataReader is created, the remote DomainParticipant will still be informed about the previously created DataWriter/DataReader. This is achieved by the HB and ACK/ NACK that are immediately sent by the built-in endpoint writer and built-in endpoint reader respectively when a new remote participant is discovered. Figure 14.10 and Figure 14.11 illustrate this sequence for HB and ACK/NACK triggers, respectively.

Endpoint discovery latency is determined by the following members of the DomainParticipant’s DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3):

publication_writer

subscription_writer

publication_reader

subscription_reader

When a remote entity record is added, removed, or changed in the database, matching is performed with all the local entities. Only after there is a successful match on both ends can an application’s user-created DataReaders and DataWriters communicate with each other.

For more information about reliable communication, see Chapter 10: Reliable Communications.

14-16

Figure 14.9 Endpoint Discovery Summary

Node A

Node B

 

DataWriter C created

Periodic HB not sent since A knows that B is up-to-date

Modify DataWriter C’s

UserData QoS

pu

 

 

 

 

b

 

 

 

 

licat

 

 

 

 

ion C

 

 

 

 

D

 

 

 

 

AT

 

 

 

A

 

 

 

Remote DataWriter C discovered,

perio

 

 

added to database

 

 

 

 

dic H

 

 

 

 

B (

 

 

 

 

F=0)

K

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AC

 

 

KN

 

 

 

AC

 

 

 

 

mo

 

 

 

difi

 

 

 

ed public

 

 

 

ation C

 

 

 

D

 

 

 

ATA

 

 

 

 

DataWriter C’s QoS modified,

perio

 

record in database modified

 

 

 

dic H

 

 

 

B (

 

 

 

F=0)

 

 

 

 

 

K

 

 

 

 

 

 

 

 

 

AC

KN

 

 

 

AC

 

 

 

 

DataWriter C deleted

 

del

 

 

 

 

 

 

 

ete

 

 

 

 

 

 

 

public

 

 

 

 

 

 

 

ati

 

 

 

 

 

 

 

on C

 

 

 

 

 

 

 

D

 

 

 

perio

 

 

 

ATA

 

 

 

dic H

 

 

 

 

 

 

 

B (

 

 

 

 

 

 

 

F=0)

 

 

 

 

 

 

K

 

 

 

 

 

 

AC

 

 

 

 

 

KN

 

 

 

 

 

 

AC

 

 

 

 

 

 

 

 

 

 

 

 

 

A’s DDS_DomainParticipantQos.discovery_config.

publication_writer.heartbeat_period

DataWriter C removed from database

wait random time between B’s [DDS_DomainParticipantQos. discovery_config. publication_reader.min_heartbeat_ response_delay] and [...max_heartbeat_response_delay]

Assume participants A and B have been discovered on both sides. A’s DiscoveryConfigQosPolicy.publication_writer.heartbeats_per_max_samples = 0, so no HB is piggybacked with the publication DATA. A HB with F=0 is a request for an ACK/NACK. The periodic and initial repeat participant DATAs are omitted from the diagram.

14-17

Figure 14.10 DataWriter Discovered by Late-Joiner, Triggered by HB

Node A

Create DataWriter C

Send HB to see if the discovery publication reader on Node B is up-to-date.

Public

 

 

 

 

 

 

 

 

 

 

 

Node B

 

 

e

nd

ation

C

D

 

 

 

 

 

 

 

 

 

point

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ATA se

nt to disc

 

 

 

parti

cip

 

 

readers

of

 

 

ov

 

 

 

 

a

 

 

 

 

 

 

disc

 

 

 

 

 

 

 

 

nts in

d

 

 

 

 

ov

 

 

 

 

 

 

 

 

 

 

 

at

ab

as

er

ed

re

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

ote

 

 

 

 

 

 

 

 

 

 

 

 

 

 

e

 

 

 

Node B hasn’t been discovered by

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Node A yet, so it doesn’t receive

 

 

 

 

 

 

 

 

 

 

ATA

 

 

 

 

the publication DATA for C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nt

B D

 

 

 

 

 

 

 

 

 

 

 

ipa

 

 

 

 

 

 

 

 

 

 

 

 

 

 

rtic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

 

 

 

 

Participant B created

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

H

 

 

 

 

B (

F=1)

 

 

 

 

 

 

 

 

 

 

Latest DATA from C has

 

 

K

 

not been received

 

 

 

 

AC

 

 

 

 

 

KN

 

 

 

AC

 

 

 

 

 

 

 

Public

 

 

 

 

 

 

 

 

d

isc

 

ation

C

D

 

 

 

 

of

 

overy

e

nd

 

A se

nt t

o

 

 

 

rem

ote

 

point r

e

 

 

 

 

 

 

 

 

ader

 

 

 

 

 

 

partici

pa

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nt B

 

 

 

 

perio

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

dic

 

 

 

 

 

 

 

Participant A’s

 

 

 

 

 

 

HB (

 

 

 

 

 

 

 

 

 

 

 

 

 

F=

 

 

 

 

 

 

 

 

 

 

 

 

 

0)

 

 

 

[DDS_DomainParticipant

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Qos.discovery_config.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

publication_writer.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

heartbeat_period)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Remote DataWriter C finally discovered, added to database

ACK

wait random time between A’s [DDS_DomainParticipantQos.discovery_config. publication_writer.min_nack_response_delay] and (...max_nack_response_delay]

wait random time between B’s [DDS_DomainParticipantQos.discovery_config. publication_reader.min_heartbeat_response_delay] and (...max_heartbeat_response_delay]

Writer C is created on Participant A before Participant A discovers Participant B. Assuming DiscoveryConfigQosPolicy.publication_writer.heartbeats_per_max_samples = 0, no HB is piggybacked with the publication DATA. Participant B has A in its peer list, but not vice versa. Accept_unknown_locators is true. On A, in response to receiving the new participant B DATA message, a participant A DATA message is sent to B. The discovery endpoint reader on A will also send an ACK/NACK to the discovery endpoint writer on B. (Initial repeat participant messages and periodic participant messages are omitted from this diagram for simplicity, see

Figure 14.3.)

14-18

Figure 14.11 DataWriter Discovered by Late-Joiner, Triggered by ACKNACK

Node A

Create DataWriter C

Public

 

 

e

nd

p

 

ation

C

D

 

 

p

 

oint re

ad

ATA

artici

pa

ers of

 

 

 

 

 

 

nts

 

 

 

Node B

 

sent

 

 

to disc

 

dis

ov

co

ery

 

ver

 

 

ed

 

 

remote

Participant A’s [DDS_DomainParticipant Qos.discovery_config. publication_writer. heartbeat_period)

perio

 

 

 

 

 

dic A

 

 

 

 

 

D

 

 

 

 

 

ATA

 

 

 

 

 

K

 

 

 

 

AC

 

 

KN

 

 

AC

 

 

 

 

C

 

 

 

 

 

D

 

 

 

 

 

AT

 

 

 

 

 

A + H

 

 

 

 

 

B (

 

 

 

 

 

F=0)

 

 

 

 

K

 

 

 

AC

 

KN

 

 

 

AC

 

 

 

 

 

Participant B created

A is discovered. ACKNACK sent immediately to discovery endpoint writer of the newly discovered remote participant

 

Remote DataWriter C discovered,

added to database

 

wait random time between A’s [DDS_DomainParticipantQos.discovery_config. publication_writer.min_nack_response_delay] and (...max_nack_response_delay]

wait random time between B’s [DDS_DomainParticipantQos.discovery_config. publication_reader.min_heartbeat_response_delay] and (...max_heartbeat_response_delay]

Writer C is created on Participant A before Participant A discovers Participant B. Assuming DiscoveryConfigQosPolicy.publication_writer.heartbeats_per_max_samples = 0, no HB is piggybacked with the publication DATA message. Participant A has B in its peer list, but not vice versa. Accept_unknown_locators is true. In response to receiving the new Participant A DATA message on node B, a participant B DATA message will be sent to A. The discovery endpoint writer on Node B will also send a HB to the discovery endpoint reader on Node A. These are omitted in the diagram for simplicity. (Initial repeat participant messages and periodic participant messages are omitted from this diagram, see

Figure 14.3.)

14-19

14.3.3Discovery Traffic Summary

Node A

Node B

 

Participant A created

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

artic

 

 

 

 

 

 

 

 

 

 

ip

 

 

 

 

 

 

 

 

 

 

ant

 

 

 

 

 

 

 

 

 

 

A

 

init

 

 

 

 

 

 

D

 

ial

 

 

 

 

 

AT

 

 

 

r

 

 

 

 

 

A

 

 

 

 

e

 

 

 

 

 

 

p

a

rtic

p

e

a

t of

 

 

 

ip

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ant

 

 

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

 

 

 

A

 

 

Newly discovered Participant B added to database

DataWriter C created

DataWriter C deleted

Participant A destroyed

 

 

 

 

 

 

 

nt

B DATA

 

 

 

 

Participant B created

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ipa

 

 

 

 

 

 

 

 

 

 

rtic

 

 

 

 

 

 

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

of

 

 

 

 

 

 

 

 

 

 

 

 

 

at

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pe

 

 

 

 

 

 

 

 

 

 

 

 

l re

 

 

 

 

 

 

 

 

 

 

 

 

 

itia

 

 

 

 

A

 

 

 

 

 

 

 

in

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

 

 

 

 

 

nt

B D

 

 

 

 

 

 

 

 

 

 

 

ipa

 

 

 

 

 

 

 

 

 

 

 

 

rtic

 

 

 

 

 

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

 

 

par

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ti

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ci

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pa

nt

A D

 

 

 

 

 

 

 

Newly discovered Participant A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AT

 

 

 

 

 

 

added to database

 

 

 

 

 

 

 

 

A

 

 

 

 

 

 

 

 

 

 

 

 

 

ATA

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ant

B D

 

 

 

 

 

 

 

 

ticip

 

 

 

 

 

 

 

 

 

 

 

 

 

par

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

public

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ation C

D

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ATA

 

 

 

 

 

 

Remote DataWriter C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

discovered, added to database

public

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ation C

D

 

 

 

 

 

 

 

Remote DataWriter C deleted,

 

 

 

 

 

 

 

ATA (

del

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ete)

 

 

 

removed from database

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

partici

 

 

 

 

 

 

 

 

 

 

 

 

 

Remote Participant A

 

pant

A

 

 

 

 

 

 

 

 

 

 

 

 

 

 

D

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ATA (d

 

 

 

 

 

removed from database

 

 

 

 

 

 

 

 

 

elet

 

 

 

 

 

 

 

 

 

 

 

 

 

 

e)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

)

 

 

Participant B destroyed

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

lete

 

 

 

 

 

 

 

ATA (de

 

 

 

 

 

 

 

nt

B D

 

 

 

 

 

 

 

 

 

ipa

 

 

 

 

 

 

 

 

 

 

 

 

 

rtic

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pa

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

wait random time (between min_initial_participant_announcement_period and max_initial_participant_announcement_period) for [initial_participant_announcements] = 1 (using values from A’s DiscoveryConfigQosPolicy)

same as , but using participant B’s QoS

This diagram shows both phases of the discovery process. Participant A is created first, followed by Participant B. Each has the other in its peers list. After they have discovered each other, a DataWriter is created on Participant A. Periodic participant DATAs, HBs and ACK/NACKs are omitted from this diagram.

14.3.4Discovery-Related QoS

Each DomainParticipant needs to be uniquely identified in the domain and specify which other DomainParticipants it is interested in communicating with. The WIRE_PROTOCOL QosPolicy (DDS Extension) (Section 8.5.9) uniquely identifies a DomainParticipant in the domain. The DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) specified the peer participants it is interested in communicating with.

There is a trade-off between the amount of traffic on the network for the purposes of discovery and the delay in reaching steady state when the DomainParticipant is first created.

For example, if the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2)’s participant_liveliness_assert_period and participant_liveliness_lease_duration fields are set

to small values, the discovery of stale remote DomainParticipants will occur faster, but more discovery traffic will be sent over the network. Setting the participant’s heartbeat_period1 to a

1.heartbeat_period is part of the DDS_RtpsReliableWriterProtocol_t structure used in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2)’s publication_writer and subscription_writer fields.

14-20

small value can cause late-joining DomainParticipants to discover remote user-data DataWriters and DataReaders at a faster rate, but Connext might send HBs to other nodes more often. This timing can be controlled by the following DomainParticipant QosPolicies:

DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) — specifies how other DomainPartici- pants in the network can communicate with this DomainParticipant, and which other DomainParticipants in the network this DomainParticipant is interested in communicating with. See also: Ports Used for Discovery (Section 14.5).

DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3) — specifies the QoS of the dis- covery readers and writers (parameters that control the HB and ACK rates of discovery endpoint readers/writers, and periodic refreshing of participant DATA from discovery participant readers/writers). It also allow you to configure asynchronous writers in order to send data with a larger size than the transport message size.

DOMAIN_PARTICIPANT_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 8.5.4) — specifies the number of local and remote entities expected in the system.

WIRE_PROTOCOL QosPolicy (DDS Extension) (Section 8.5.9) — specifies the rtps_app_id and rtps_host_id that uniquely identify the participant in the domain.

The other important parameter is the domain ID: DomainParticipants can only discover each other if they belong to the same domain. The domain ID is a parameter passed to the create_participant() operation (see Section 8.3.1).

14.4Debugging Discovery

To understand the flow of messages during discovery, you can increase the verbosity of the messages logged by Connext so that you will see whenever a new entity is discovered, and whenever there is a match between a local entity and a remote entity.

This can be achieved with the logging API:

NDDSConfigLogger::get_instance()->set_verbosity_by_category

(NDDS_CONFIG_LOG_CATEGORY_ENTITIES, NDDS_CONFIG_LOG_VERBOSITY_STATUS_REMOTE);

Using the scenario in the summary diagram in Section 14.3.3, these are the messages as seen on DomainParticipant A:

[D0049|ENABLE]DISCPluginManager_onAfterLocalParticipantEnabled:announcing new local participant: 0XA0A01A1,0X5522,0X1,0X1C1 [D0049|ENABLE]DISCPluginManager_onAfterLocalParticipantEnabled:at {46c614d9,0C43B2DC}

(The above messages mean: First participant A DATA sent out when participant A is enabled.)

DISCSimpleParticipantDiscoveryPluginReaderListener_onDataAvailable:discovered new participant: host=0x0A0A01A1, app=0x0000552B, instance=0x00000001 DISCSimpleParticipantDiscoveryPluginReaderListener_onDataAvailable:at {46c614dd,8FA13C1F} DISCParticipantDiscoveryPlugin_assertRemoteParticipant:plugin discovered/updated remote participant: 0XA0A01A1,0X552B,0X1,0X1C1 DISCParticipantDiscoveryPlugin_assertRemoteParticipant:at {46c614dd,8FACE677} DISCParticipantDiscoveryPlugin_assertRemoteParticipant:plugin accepted new remote participant: 0XA0A01A1,0X552B,0X1,0X1C1 DISCParticipantDiscoveryPlugin_assertRemoteParticipant:at {46c614dd,8FACE677}

(The above messages mean: Received participant B DATA.)

DISCSimpleParticipantDiscoveryPlugin_remoteParticipantDiscovered:re-announcing participant self: 0XA0A01A1,0X5522,0X1,0X1C1

14-21

DISCSimpleParticipantDiscoveryPlugin_remoteParticipantDiscovered:at {46c614dd,8FC02AF7}

(The above messages mean: Resending participant A DATA to the newly discovered remote participant.)

PRESPsService_linkToLocalReader:assert remote 0XA0A01A1,0X552B,0X1,0X200C2, local 0x000200C7 in reliable reader service

PRESPsService_linkToLocalWriter:assert remote 0XA0A01A1,0X552B,0X1,0X200C7, local 0x000200C2 in reliable writer service

PRESPsService_linkToLocalWriter:assert remote 0XA0A01A1,0X552B,0X1,0X4C7, local 0x000004C2 in reliable writer service

PRESPsService_linkToLocalWriter:assert remote 0XA0A01A1,0X552B,0X1,0X3C7, local 0x000003C2 in reliable writer service

PRESPsService_linkToLocalReader:assert remote 0XA0A01A1,0X552B,0X1,0X4C2, local 0x000004C7 in reliable reader service

PRESPsService_linkToLocalReader:assert remote 0XA0A01A1,0X552B,0X1,0X3C2, local 0x000003C7 in reliable reader service

PRESPsService_linkToLocalReader:assert remote 0XA0A01A1,0X552B,0X1,0X100C2, local 0x000100C7 in best effort reader service

(The above messages mean: Automatic matching of the discovery readers and writers. A built-in remote endpoint's object ID always ends with Cx.)

DISCSimpleParticipantDiscoveryPluginReaderListener_onDataAvailable:discovered mod- ified participant: host=0x0A0A01A1, app=0x0000552B, instance=0x00000001 DISCParticipantDiscoveryPlugin_assertRemoteParticipant:plugin discovered/updated remote participant: 0XA0A01A1,0X552B,0X1,0X1C1 DISCParticipantDiscoveryPlugin_assertRemoteParticipant:at {46c614dd,904D876C}

(The above messages mean: Received participant B DATA.)

DISCPluginManager_onAfterLocalEndpointEnabled:announcing new local publication: 0XA0A01A1,0X5522,0X1,0X80000003 DISCPluginManager_onAfterLocalEndpointEnabled:at {46c614d9,1013B9F0}

DISCSimpleEndpointDiscoveryPluginPDFListener_onAfterLocalWriterEnabled:announcing new publication: 0XA0A01A1,0X5522,0X1,0X80000003 DISCSimpleEndpointDiscoveryPluginPDFListener_onAfterLocalWriterEnabled:at {46c614d9,101615EB}

(The above messages mean: Publication C DATA has been sent.)

DISCSimpleEndpointDiscoveryPlugin_subscriptionReaderListenerOnDataAvailable:dis- covered subscription: 0XA0A01A1,0X552B,0X1,0X80000004 DISCSimpleEndpointDiscoveryPlugin_subscriptionReaderListenerOnDataAvailable:at {46c614dd,94FAEFEF}

DISCEndpointDiscoveryPlugin_assertRemoteEndpoint:plugin discovered/updated remote endpoint: 0XA0A01A1,0X552B,0X1,0X80000004 DISCEndpointDiscoveryPlugin_assertRemoteEndpoint:at {46c614dd,950203DF}

(The above messages mean: Receiving subscription D DATA from Node B.)

PRESPsService_linkToLocalWriter:assert remote 0XA0A01A1,0X552B,0X1,0X80000004, local 0x80000003 in best effort writer service

(The above message means: User-created DataWriter C and DataReader D are matched.)

[D0049|DELETE_CONTAINED]DISCPluginManager_onAfterLocalEndpointDeleted:announcing disposed local publication: 0XA0A01A1,0X5522,0X1,0X80000003 [D0049|DELETE_CONTAINED]DISCPluginManager_onAfterLocalEndpointDeleted:at {46c61501,288051C8} [D0049|DELETE_CONTAINED]DISCSimpleEndpointDiscoveryPluginPDFListener_onAfterLocalW riterDeleted:announcing disposed publication: 0XA0A01A1,0X5522,0X1,0X80000003 [D0049|DELETE_CONTAINED]DISCSimpleEndpointDiscoveryPluginPDFListener_onAfterLocalW riterDeleted:at {46c61501,28840E15}

14-22

(The above messages mean: Publication C DATA(delete) has been sent.)

DISCPluginManager_onBeforeLocalParticipantDeleted:announcing before disposed local participant: 0XA0A01A1,0X5522,0X1,0X1C1 DISCPluginManager_onBeforeLocalParticipantDeleted:at {46c61501,28A11663}

(The above messages mean: Participant A DATA(delete) has been sent.)

DISCParticipantDiscoveryPlugin_removeRemoteParticipantsByCookie:plugin removing 3 remote entities by cookie DISCParticipantDiscoveryPlugin_removeRemoteParticipantsByCookie:at {46c61501,28E38A7C} DISCParticipantDiscoveryPlugin_removeRemoteParticipantI:plugin discovered dis- posed remote participant: 0XA0A01A1,0X552B,0X1,0X1C1 DISCParticipantDiscoveryPlugin_removeRemoteParticipantI:at {46c61501,28E68E3D} DISCParticipantDiscoveryPlugin_removeRemoteParticipantI:remote entity removed from database: 0XA0A01A1,0X552B,0X1,0X1C1 DISCParticipantDiscoveryPlugin_removeRemoteParticipantI:at {46c61501,28E68E3D}

(The above messages mean: Removing discovered entities from local database, before shutting down.)

As you can see, the messages are encoded, since they are primarily used by RTI support personnel.

For more information on the message logging API, see Controlling Messages from Connext (Section 21.2).

If you notice that a remote entity is not being discovered, check the QoS related to discovery (see Section 14.3.4).

If a remote entity is discovered, but does not match with a local entity as expected, check the QoS of both the remote and local entity.

14.5Ports Used for Discovery

There are two kinds of traffic in a Connext application: discovery (meta) traffic, and user traffic. Meta-traffic is for data (declarations) that is sent between the automatically-created discovery writers and readers; user traffic is for data that is sent between user-created DataWriters and DataReaders. To keep the two kinds of traffic separate, Connext uses different ports, as described below.

Note: The ports described in this section are used for incoming data. Connext uses ephemeral ports for outbound data.

Connext uses the RTPS wire protocol. The discovery protocols defined by RTPS rely on well- known ports to initiate discovery. These well-known ports define the multicast and unicast ports on which a Participant will listen for meta-traffic from other Participants. The meta-traffic contains the information required by Connext to establish the presence of remote Entities in the network.

The well-known incoming ports are defined by RTPS in terms of port mapping expressions with several tunable parameters. This allows you to customize what network ports are used for receiving data by Connext. These parameters are shown in Table 14.2. (For defaults and valid ranges, please see the API Reference HTML documentation.)

In order for all Participants in a system to correctly discover each other, it is important that they all use the same port mapping expressions.

14-23

Table 14.2 WireProtocol QosPolicy’s rtps_well_known_ports (DDS_RtpsWellKnownPorts_t)

Type

Field Name

Description

 

 

 

 

 

The base port offset. All mapped well-known ports are

 

port_base

offset by this value. Resulting ports must be within the

 

 

range imposed by the underlying transport.

 

 

 

 

domain_id_gain

Tunable gain parameters. See Section 14.5.4.

 

 

DDS_Long

participant_id_gain

 

 

 

 

builtin_multicast_port_offset

Additional offset for meta-traffic port. See Inbound Ports

 

builtin_unicast_port_offset

for Meta-Traffic (Section 14.5.1).

 

 

 

 

user_multicast_port_offset

Additional offset for user traffic port. See Inbound Ports for

 

user_unicast_port_offset

User Traffic (Section 14.5.2).

 

 

 

In addition to the parameters listed in Table 14.2, the port formulas described below depend on:

The domain ID specified when the DomainParticipant is created (see Section 8.3.1). The domain ID ensures no port conflicts exist between Participants belonging to different domains. This also means that discovery traffic in one domain is not visible to

DomainParticipants in other domains.

The participant_id is a field in the WIRE_PROTOCOL QosPolicy (DDS Extension) (Section 8.5.9), see Section 8.5.9.1. The participant_id ensures that unique unicast port numbers are assigned to DomainParticipants belonging to the same domain on a given host.

Backwards Compatibility: Connext 4.5 supports the standard DDS Interoperability Wire Protocol based on the Real-time Publish-Subscribe (RTPS) protocol. Because this protocol is not compatible with that used by RTI Data Distribution Service 4.2c and older, applications built with 4.5 (as well as 4.4, 4.3, and 4.2e) will not interoperate with applications built with versions 4.2c and older. The default port mapping from domainID and participant index has also been changed according to the new interoperability specification. The message types and formats used by RTPS have also changed.

Port Aliasing: When modifying the port mapping parameters, avoid port aliasing. This would result in undefined discovery behavior. The chosen parameter values will also determine the maximum possible number of domains in the system and the maximum number of participants per domain. Additionally, any resulting mapped port number must be within the range imposed by the underlying transport. For example, for UDPv4, this range is typically between 1024 and 65535.

14.5.1Inbound Ports for Meta-Traffic

The Wire Protocol QosPolicy’s rtps_well_known_ports.metatraffic_unicast_port determines the port used for receiving meta-traffic using unicast:

metatraffic_unicast_port = port_base +

(domain_id_gain * Domain ID) + (participant_id_gain * participant_id) +

builtin_unicast_port_offset

Similarly, rtps_well_known_ports.metatraffic_multicast_port receiving meta-traffic using multicast. The corresponding specified via multicast_receive_addresses (see Section 8.5.2.4).

determines the port used for multicast group addresses are

metatraffic_multicast_port = port_base +

(domain_id_gain * Domain ID) + builtin_multicast_port_offset

14-24

Note: Multicast is only used for meta-traffic if a multicast address is specified in the NDDS_DISCOVERY_PEERS environment variable or file or if the multicast_receive_addresses field of the DISCOVERY_CONFIG QosPolicy (DDS Extension) (Section 8.5.3) is set.

14.5.2Inbound Ports for User Traffic

RTPS also defines the default multicast and unicast ports on which DataReaders and DataWriters receive user traffic. These default ports can be overridden using the DataReader’s TRANSPORT_MULTICAST QosPolicy (DDS Extension) (Section 7.6.5) and TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23), or the DataWriter’s TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23).

The WireProtocol QosPolicy’s rtps_well_known_ports.usertraffic_unicast_port determines the port used for receiving user data using unicast:

usertraffic_unicast_port = port_base +

(domain_id_gain * Domain ID) + (participant_id_gain * participant_id)+ user_unicast_port_offset

Similarly, rtps_well_known_ports.usertraffic_multicast_port determines the port used for receiving user data using multicast. The corresponding multicast group addresses can be configured using the TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23).

usertraffic_multicast_port = port_base +

(domain_id_gain * Domain ID) + user_multicast_port_offset

14.5.3Automatic Selection of participant_id and Port Reservation

The WIRE_PROTOCOL QosPolicy (DDS Extension) (Section 8.5.9) rtps_reserved_ports_mask field determines what type of ports are reserved when the DomainParticipant is enabled. See Choosing Participant IDs (Section 8.5.9.1).

14.5.4Tuning domain_id_gain and participant_id_gain

The domain_id_gain is used as a multiplier of the domain ID. Together with participant_id_gain (Section 14.5.4), these values determine the highest domain ID and participant_id allowed on this network.

In general, there are two ways to set up the domain_id_gain and participant_id_gain parameters.

If domain_id_gain > participant_id_gain, it results in a port mapping layout where all DomainParticipants in a domain occupy a consecutive range of domain_id_gain ports. Precisely, all ports occupied by the domain fall within:

(port_base + (domain_id_gain * Domain ID))

and:

(port_base + (domain_id_gain * (Domain ID + 1)) - 1)

In this case, the highest domain ID is limited only by the underlying transport's maximum port. The highest participant_id, however, must satisfy:

max_participant_id < (domain_id_gain / participant_id_gain)

14-25

On the contrary, if domain_id_gain <= participant_id_gain, it results in a port mapping layout where a given domain's DomainParticipant instances occupy ports spanned across the entire valid port range allowed by the underlying transport. For instance, it results in the following potential mapping:

Mapped Port

Domain ID

Participant ID

 

 

 

 

 

 

 

1

2

 

 

 

0

 

 

 

 

 

higher port number

1

1

 

 

 

0

 

 

 

 

 

 

1

0

 

 

lower port number

0

 

 

 

 

In this case, the highest participant_id maximum port. The highest domain_id,

is limited only by the underlying transport's however, must satisfy:

max_domain_id < (participant_id_gain / domain_id_gain)

The domain_id_gain also determines the range of the port-specific offsets:

domain_id_gain >

abs(builtin_multicast_port_offset - user_multicast_port_offset)

and

domain_id_gain >

abs(builtin_unicast_port_offset - user_unicast_port_offset)

Violating this may result in port aliasing and undefined discovery behavior.

The participant_id_gain also determines the range of builtin_unicast_port_offset and user_unicast_port_offset.

participant_id_gain >

abs(builtin_unicast_port_offset - user_unicast_port_offset)

In all cases, the resulting ports must be within the range imposed by the underlying transport.

14-26

Chapter 15 Transport Plugins

Connext has a pluggable-transports architecture. The core of Connext is transport agnostic—it does not make any assumptions about the actual transports used to send and receive messages. Instead, Connext uses an abstract "transport API" to interact with the transport plugins that implement that API. A transport plugin implements the abstract transport API, and performs the actual work of sending and receiving messages over a physical transport.

There are essentially three categories of transport plugins:

Builtin Transport Plugins Connext comes with a set of commonly used transport plugins. These ‘builtin’ plugins include UDPv4, UDPv6, and shared memory. So that Connext applications can work out-of-the-box, some of these are enabled by default (see TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7)).

Extension Transport Plugins RTI offers extension transports, including RTI Secure WAN Transport (see Chapter 24 and Chapter 25) and RTI TCP Transport (see Chapter 36).

Custom-developed Transport Plugins RTI supports the use of custom transport plugins. This is a powerful capability that distinguishes Connext from competing middleware approaches. If you are interested in developing a custom transport plugin for Connext, please contact your local RTI representative or email sales@rti.com.

This chapter describes the following:

Builtin Transport Plugins (Section 15.1)

Extension Transport Plugins (Section 15.2)

The NDDSTransportSupport Class (Section 15.3)

Explicitly Creating Builtin Transport Plugin Instances (Section 15.4)

Setting Builtin Transport Properties of the Default Transport Instance—get/ set_builtin_transport_properties() (Section 15.5)

Setting Builtin Transport Properties with the PropertyQosPolicy (Section 15.6)

Installing Additional Builtin Transport Plugins with register_transport() (Section 15.7)

Installing Additional Builtin Transport Plugins with PropertyQosPolicy (Section 15.8)

Other Transport Support Operations (Section 15.9)

15-1

15.1Builtin Transport Plugins

There are two ways in which the builtin transport plugins may be registered:

Default builtin Transport Instances: Builtin transports that are turned "on" in the TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7) are implicitly registered when (a) the DomainParticipant is enabled, (b) the first DataWriter/DataReader is created, or (c) you look up a builtin DataReader (by calling lookup_datareader() on a

Subscriber), whichever happens first. The builtin transport plugins have default properties. If you want to change these properties, do so before1 the transports are registered.

Other Transport Instances: There are two ways to install non-default builtin transport instances:

Transport plugins may be explicitly registered by first creating an instance of the transport plugin (by calling NDDS_Transport_UDPv4_new(),

NDDS_Transport_UDPv6_new() or NDDS_Transport_Shmem_new(), see Section 15.4), then calling register_transport() (Section 15.7). (For example, suppose you want an extra instance of a transport.) (Not available for the Java or .NET API.)

Additional builtin transport instances can also be installed through the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17).

To configure the properties of the builtin transports:

Set properties by calling set_builtin_transport_property() (see Section 15.5) or

Specify predefined property strings in the DomainParticipant’s PropertyQosPolicy, as described in Section 15.6.

For other builtin transport instances:

If the builtin transport plugin is created with NDDS_Transport_UDPv4_new(),

NDDS_Transport_UDPv6_new() or NDDS_Transport_Shmem_new(), properties can be specified during creation time. See Explicitly Creating Builtin Transport Plugin Instances (Section 15.4).

If the additional builtin transport instances are installed through the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17), the properties of the builtin transport plugins can also be specified through that same QosPolicy.

15.2Extension Transport Plugins

If you want to change the properties for an extension transport plugin, do so before1 the plugin is registered.

There are two ways to install an extension transport plugin:

Implicit Registration: Transports can be installed through the predefined strings in the DomainParticipant’s PropertyQosPolicy. Once the transport’s properties are specified in the PropertyQosPolicy, the transport will be implicitly registered when (a) the DomainParticipant is enabled, (b) the first DataWriter/DataReader is created, or (c) you look up a builtin DataReader (by calling lookup_datareader() on a Subscriber), whichever happens first.

1.Any transport property changes made after the plugin is registered will have no effect.

15-2

QosPolicies can also be configured from XML resources (files, strings)—with this approach, you can change the QoS without recompiling the application. The QoS settings are automatically loaded by the DomainParticipantFactory when the first DomainPartici- pant is created. For more information, see Chapter 17: Configuring QoS with XML.

Explicit Registration: Transports may be explicitly registered by first creating an instance of the transport plugin (see Section 15.4) and then calling register_transport() (see Section 15.7).

15.3The NDDSTransportSupport Class

The register_transport() and set_builtin_transport_property() operations are part of the

NDDSTransportSupport class, which includes the operations listed in Table 15.1.

Table 15.1 Transport Support Operations

Operation

Description

Reference

 

 

 

get_transport_plugin

Retrieves a previously registered transport plugin.

 

 

 

Section 15.7

register_transport

Registers a transport plugin for use with a

DomainParticipant.

 

 

 

 

 

 

get_builtin_transport_property

Gets the properties used to create a builtin transport

 

 

plugin.

Section 15.5

set_builtin_transport_property

Sets the properties used to create a builtin transport

 

 

plugin.

 

add_send_route

Adds a route for outgoing messages.

Section 15.9.1

 

 

 

add_receive_route

Adds a route for incoming messages.

Section 15.9.2

 

 

 

lookup_transport

Looks up a transport plugin within a DomainParticipant.

Section 15.9.3

 

 

 

15.4Explicitly Creating Builtin Transport Plugin Instances

The builtin transports (UDPv4, UDPv6, and Shared Memory) are implicitly created by default (if they are enabled via the TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7)). Therefore, you only need to explicitly create a new instance if you want an extra instance (suppose you want two UDPv4 transports, one with special settings).

Transport plugins may be explicitly registered by first creating an instance of the transport plugin and then calling register_transport() (Section 15.7). (For example, suppose you want an extra instance of a transport.) (Not available for the Java API.)

To create an instance of a builtin transport plugin, use one of the following functions:

NDDS_Transport_Plugin* NDDS_Transport_UDPv4_new (

const struct NDDS_Transport_UDPv4_Property_t * property_in)

NDDS_Transport_Plugin* NDDS_Transport_UDPv4_new (

const struct NDDS_Transport_UDPv4_Property_t * property_in)

NDDS_Transport_Plugin* NDDS_Transport_Shmem_new (

const struct NDDS_Transport_Shmem_Property_t * property_in)

property_in Desired behavior of this transport. May be NULL for default properties.

For details on using these functions, please see the API Reference HTML documentation.

15-3

Your application may create and register multiple instances of these transport plugins with Connext. This may be done to partition the network interfaces across multiple domains. However, note that the underlying transport, the operating system's IP layer, is still a "singleton." For example, if a unicast transport has already bound to a port, and another unicast transport tries to bind to the same port, the second attempt will fail.

15.5Setting Builtin Transport Properties of the Default Transport Instance—get/set_builtin_transport_properties()

Perhaps you want to use one of the builtin transports, but need to modify the properties. (For default values, please see the API Reference HTML documentation.) Used together, the two operations below allow you to customize properties of the builtin transport when it is implicitly registered (see Section 15.1).

Note: Another way to change the properties is with the Property QosPolicy, see Section 15.6. Changing properties with the Property QosPolicy will overwrite the properties set by calling set_builtin_transport_property().

DDS_ReturnCode_t NDDSTransportSupport::get_builtin_transport_property (DDSDomainParticipant * participant_in,

DDS_TransportBuiltinKind builtin_transport_kind_in,

struct NDDS_Transport_Property_t &builtin_transport_property_inout)

DDS_ReturnCode_t NDDSTransportSupport::set_builtin_transport_property (DDSDomainParticipant * participant_in,

DDS_TransportBuiltinKind builtin_transport_kind_in, const struct NDDS_Transport_Property_t

&builtin_transport_property_in)

participant_in A valid non-NULL DomainParticipant that has not been enabled. If the DomainParticipant if already enabled when this operation is called, your transport property changes will not be reflected in the transport used by the DomainParticipant's DataWriters and DataReaders.

builtin_transport_kind_in The builtin transport kind for which to specify the properties.

builtin_transport_property_inout (Used by the “get” operation only.) The storage area where the retrieved property will be output. The specific type required by the builtin_transport_kind_in must be used.

builtin_transport_property_in (Used that will be used to the create the builtin_transport_kind_in

by the “set” operation only.) The new transport property the builtin transport plugin. The specific type required by must be used.

In this example, we want to use the builtin UDPv4 transport, but with modified properties.

/* Before reaching this point, create a disabled DomainParticipant */ struct NDDS_Transport_UDPv4_Property_t property =

NDDS_TRANSPORT_UDPV4_PROPERTY_DEFAULT;

if (NDDSTransportSupport::get_builtin_transport_property( participant, DDS_TRANSPORTBUILTIN_UDPv4, (struct NDDS_Transport_Property_t&)property) !=

DDS_RETCODE_OK) {

printf("**Error: get builtin transport property\n");

}

/* Make your desired changes here */

/* For example, to increase the UDPv4 max msg size to 64K: */ property.parent.message_size_max = 65535; property.recv_socket_buffer_size = 65535;

15-4

property.send_socket_buffer_size = 65535;

if (NDDSTransportSupport::set_builtin_transport_property( participant, DDS_TRANSPORTBUILTIN_UDPv4,

(struct NDDS_Transport_Property_t&)property) != DDS_RETCODE_OK) {

printf("***Error: set builtin transport property\n");

}

/* Enable the participant to turn on communications with other participants in the domain using the new properties for the automatically registered builtin transport plugins.*/

if (entity->enable() != DDS_RETCODE_OK) { printf("***Error: failed to enable entity\n");

}

Note: Builtin transport property changes will have no effect after the builtin transport has been registered. The builtin transports are implicitly registered when (a) the DomainParticipant is enabled, (b) the first DataWriter/DataReader is created, or (c) you lookup a builtin DataReader, whichever happens first.

Note: If message_size_max is increased from the default for any of the built-in transports, or if custom transports are used, then the buffer_size in the RECEIVER_POOL QosPolicy (DDS Extension) (Section 8.5.6) of the DomainParticipant should also be increased accordingly.

15.6Setting Builtin Transport Properties with the PropertyQosPolicy

The PROPERTY QosPolicy (DDS Extension) (Section 6.5.17) allows you to set name/value pairs of data and attach them to an entity, such as a DomainParticipant.

To assign properties, use the add_property() operation:

DDS_ReturnCode_t DDSPropertyQosPolicyHelper::add_property (DDS_PropertyQosPolicy policy,

const char * name, const char * value, DDS_Boolean propagate)

For more information on add_property() and the other operations in the DDSPropertyQosPolicyHelper class, please see Table 6.56, “PropertyQoSPolicyHelper Operations,” on page 6-114, as well as the API Reference HTML documentation.

The ‘name’ part of the name/value pairs is a predefined string. The property names for the builtin transports are described in these tables:

Table 15.2, “Properties for the Builtin UDPv4 Transport,” on page 15-6

Table 15.3, “Properties for Builtin UDPv6 Transport,” on page 15-11

Table 15.4, “Properties for Builtin Shared-Memory Transport,” on page 15-15 See also:

“Notes Regarding Loopback and Shared Memory” (Section 15.6.1 on page 15-17)

“Setting the Maximum Gather-Send Buffer Count for UDPv4 and UDPv6” (Section 15.6.2 on page 15-17)

“Formatting Rules for IPv6 ‘Allow’ and ‘Deny’ Address Lists” (Section 15.6.3 on page 15- 18)

Note: Changing properties with the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17) will overwrite any properties set by calling set_builtin_transport_property().

15-5

Table 15.2 Properties for the Builtin UDPv4 Transport

Property Name

 

(prefix with

Property Value Description

‘dds.transport.UDPv4.builtin.’)

 

 

 

 

 

 

Number of bits in a 16-byte address that are used by the transport. Should

 

be between 0 and 128.

parent.address_bit_count

For example, for an address range of 0-255, the address_bit_count should

 

be set to 8. For the range of addresses used by IPv4 (4 bytes), it should be

 

set to 32.

 

 

 

A bitmap that defines various properties of the transport to the Connext

 

core.

parent.properties_bitmap

Currently, the only property supported is whether or not the transport

 

plugin will always loan a buffer when Connext tries to receive a message

 

using the plugin. This is in support of a zero-copy interface.

 

 

 

Specifies the maximum number of buffers that Connext can pass to the

 

send() method of a transport plugin.

 

The transport plugin send() API supports a gather-send concept, where

 

the send() call can take several discontiguous buffers, assemble and send

 

them in a single message. This enables Connext to send a message from

 

parts obtained from different sources without first having to copy the parts

 

into a single contiguous buffer.

parent.

However, most transports that support a gather-send concept have an

gather_send_buffer_count_max

upper limit on the number of buffers that can be gathered and sent. Setting

 

this value will prevent Connext from trying to gather too many buffers into

 

a send call for the transport plugin.

 

Connext requires all transport-plugin implementations to support a gather-

 

send of least a minimum number of buffers. This minimum number is

 

NDDS_TRANSPORT_PROPERTY_GATHER_SEND_BUFFER_COUNT_MIN.

 

See Setting the Maximum Gather-Send Buffer Count for UDPv4 and

 

UDPv6 (Section 15.6.2).

 

 

 

The maximum size of a message in bytes that can be sent or received by

 

the transport plugin.

parent.message_size_max

This value must be set before the transport plugin is registered, so that

Connext can properly use the plugin.

 

 

If you set this higher than the default, then the DomainParticipant’s

 

ReceiverPoolQosPolicy’s buffer_size should also be changed.

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. Interfaces must be specified as comma-separated strings,

 

with each comma delimiting an interface.

 

For example, the following are acceptable strings:

 

192.168.1.1

 

192.168.1.*

 

192.168.*

 

192.*

parent.allow_interfaces_list

ether0

 

If the list is non-empty, this "white" list is applied before the

 

parent.deny_interfaces_list list. The DomainParticipant will use the

 

resulting list of interfaces to inform its remote participant(s) about which

 

unicast addresses may be used to contact the DomainParticipant.

 

The resulting list restricts reception to a particular set of interfaces for

 

unicast UDP. Multicast output will still be sent and may be received over

 

the interfaces in the list (if multicast is supported on the platform).

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

15-6

Table 15.2 Properties for the Builtin UDPv4 Transport

Property Name

 

(prefix with

Property Value Description

‘dds.transport.UDPv4.builtin.’)

 

 

 

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, deny the use of these interfaces.

 

Interfaces must be specified as comma-separated strings, with each

 

comma delimiting an interface.

 

For example, the following are acceptable strings:

 

192.168.1.1

 

192.168.1.*

 

192.168.*

parent.deny_interfaces_list

192.*

 

ether0

 

This "black" list is applied after the parent.allow_interfaces_list list and

 

filters out the interfaces that should not be used for receiving data.

 

The resulting list restricts reception to a particular set of interfaces for

 

unicast UDP. Multicast output will still be sent and may be received over

 

the interfaces in the list (if multicast is supported on the platform).

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, allow the use of multicast only on

 

these interfaces. If the list is empty, allow the use of all the allowed

 

interfaces.

 

Interfaces must be specified as comma-separated strings, with each

 

comma delimiting an interface.

 

This list sub-selects from the allowed interfaces that are obtained after

parent.

applying the parent.allow_interfaces_list "white" list and the

allow_multicast_interfaces_list

parent.deny_interfaces_list "black" list. From that resulting list, parent.

 

deny_multicast_interfaces_list is applied. Multicast output will be sent

 

and may be received over the interfaces in the resulting list (if multicast is

 

supported on the platform).

 

If this list is empty, all the allowed interfaces may potentially be used for

 

multicast.

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, deny the use of those interfaces for

 

multicast.

 

Interfaces should be specified as comma-separated strings, with each

parent.

comma delimiting an interface.

This "black" list is applied after the parent. allow_multicast_interfaces_list

deny_multicast_interfaces_list

list and filters out the interfaces that should not be used for multicast. The

 

 

final resulting list will be those interfaces that—if multicast is available—

 

will be used for multicast sends.

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

15-7

Table 15.2 Properties for the Builtin UDPv4 Transport

Property Name

 

 

(prefix with

 

Property Value Description

‘dds.transport.UDPv4.builtin.’)

 

 

 

 

 

 

 

Size in bytes of the send buffer of a socket used for sending. On most

 

operating systems, setsockopt() will be called to set the SENDBUF to the

 

value of this parameter.

 

This value must be greater than or equal to the property,

send_socket_buffer_size

parent.message_size_max.

 

The maximum value is operating system-dependent.

 

If

NDDS_TRANSPORT_UDPV4_SOCKET_BUFFER_SIZE_OS_DEFAULT,

 

then setsockopt() (or equivalent) will not be called to size the send buffer

 

of the socket.

 

 

 

Size in bytes of the receive buffer of a socket used for receiving.

 

On most operating systems, setsockopt() will be called to set the

 

RECVBUF to the value of this parameter.

 

This value must be greater than or equal to the property,

recv_socket_buffer_size

parent.message_size_max. The maximum value is operating system-

dependent.

 

 

Default: NDDS_TRANSPORT_UDPV4_MESSAGE_SIZE_MAX_DEFAULT.

 

If NDDS_TRANSPORT_UDPV4_SOCKET_BUFFER_SIZE_OS_DEFAULT, then

 

setsockopt() (or equivalent) will not be called to size the receive buffer of

 

the socket.

 

 

 

Allows the transport plugin to use unicast UDP for sending and receiving.

 

By default, it will be turned on. Also by default, it will use all the allowed

unicast_enabled

network interfaces that it finds up and running when the plugin is

 

instanced.

 

Can be 1 (enabled) or 0 (disabled).

 

 

 

Allows the transport plugin to use multicast for sending and receiving.

 

You can turn multicast on or off for this plugin. The default is that

multicast_enabled

multicast is on and the plugin will use the all network interfaces allowed

 

for multicast that it finds up and running when the plugin is instanced.

 

Can be 1 (enabled) or 0 (disabled).

 

 

 

Value for the time-to-live parameter for all multicast sends using this

multicast_ttl

plugin. This is used to set the TTL of multicast packets sent by this

 

transport plugin.

 

 

 

Prevents the transport plugin from putting multicast packets onto the

 

loopback interface.

 

If disabled, then when sending multicast packets, do not put a copy on the

 

loopback interface. This will prevent other applications on the same node

multicast_loopback_disabled

(including itself) from receiving those packets.

This is set to 0 by default. So multicast loopback is enabled. Turning off

 

 

multicast loopback (set to 1) may result in minor performance gains when

 

using multicast.

 

Note: Windows CE does not support multicast loopback. This field is

 

ignored for Windows CE targets.

 

 

 

15-8

Table 15.2 Properties for the Builtin UDPv4 Transport

Property Name

 

 

 

 

 

 

(prefix with

 

Property Value Description

 

 

‘dds.transport.UDPv4.builtin.’)

 

 

 

 

 

 

 

 

 

 

 

Prevents the transport plugin from using the IP loopback interface. Three

 

values are allowed:

 

 

 

 

 

0: Forces local traffic to be sent over loopback, even if a more efficient

 

transport (such as shared memory) is installed (in which case traffic will be

 

sent over both transports).

 

 

 

 

ignore_loopback_interface

1: Disables local traffic via this plugin. The IP loopback interface will not

be used, even if no NICs are discovered. This is useful when you want

 

applications running on the same node to use a more efficient transport

 

(such as shared memory) instead of the IP loopback.

 

 

 

-1: Automatic. Lets Connext decide among the above two choices. If a

 

shared memory transport plugin is available for local traffic, the effective

 

value is 1 (i.e., disable UPV4 local traffic). Otherwise, the effective value is

 

0, i.e., use UDPv4 for local traffic also.

 

 

 

 

 

 

This property is only supported on Windows platforms with statically

 

configused IP addresses.

 

 

 

 

 

It allows/disallows the use of interfaces that are not reported as UP (by the

 

operating system) in the UDPv4 transport. Two values are allowed:

 

0: Allow interfaces that are reported as DOWN.

 

 

 

 

Setting this value to 0 supports communication scenarios in which

 

interfaces are enabled after the participant is created. Once the interfaces

ignore_nonup_interfaces

are enabled, discovery will not occur until the participant sends the next

periodic

announcement

(controlled

by

the

parameter

 

 

participant_qos.discovery_config.participant_liveliness_

 

 

assert_period).

 

 

 

 

 

To reduce discovery time, you may want to decrease the value of

 

participant_liveliness_assert_period.

 

 

 

 

For the above scenario, there is one caveat: non-UP interfaces must have a

 

static IP assigned.

 

 

 

 

 

1 (default): Do not allow interfaces that are reported as DOWN.

 

 

 

 

If ignore_nonup_interfaces is 0, the UDPv4 transport creates a new thread

interface_poll_period

to query the status of the interfaces. The interface_poll_period specifies

the polling period in milliseconds for performing this query.

 

 

 

 

This property’s value is ignored if ignore_nonup_interfaces is 1.

 

 

 

 

Controls whether or not to reuse receive resources. Setting this to 0

reuse_multicast_receive_resource

(FALSE) prevents multicast crosstalk by uniquely configuring a port and

creating a receive thread for each multicast group address.

 

 

 

 

Affects Linux systems only; ignored for non-Linux systems.

 

 

 

 

 

 

 

 

15-9

Table 15.2 Properties for the Builtin UDPv4 Transport

Property Name

(prefix with Property Value Description ‘dds.transport.UDPv4.builtin.’)

 

Prevents the transport plugin from using a network interface that is not

 

reported as RUNNING by the operating system.

 

 

 

The transport checks the flags reported by the operating system for each

 

network interface upon initialization. An interface which is not reported as

 

UP will not be used. This property allows the same check to be extended to

 

the IFF_RUNNING flag implemented by some operating systems. The

 

RUNNING flag is defined to mean that "all resources are allocated", and

ignore_nonrunning_interfaces

may be off if there is no link detected, e.g., the network cable is unplugged.

Two values are allowed:

 

 

 

 

 

 

0: Do not check the RUNNING flag when enumerating interfaces, just

 

make sure the interface is UP.

 

 

 

1: Check the flag when enumerating interfaces, and ignore those that are

 

not reported as RUNNING. This can be used on some operating systems

 

to cause the transport to ignore interfaces that are enabled but not

 

connected to the network.

 

 

 

 

 

 

 

Prevents the transport plugin from doing a zero copy.

 

 

 

By default, this plugin will use the zero copy on OSs that offer it. While

 

this is good for performance, it may sometime tax the OS resources in a

 

manner that cannot be overcome by the application.

 

 

 

The best example is if the hardware/device driver lends the buffer to the

no_zero_copy

application itself. If the application does not return the loaned buffers soon

enough, the node may error or malfunction. In case you cannot

 

 

reconfigure the hardware, device driver, or the OS to allow the zero-copy

 

feature to work for your application, you may have no choice but to turn

 

off zero-copy.

 

 

 

By default this is set to 0, so Connext will use the zero-copy API if offered

 

by the OS.

 

 

 

 

 

Controls the blocking behavior of send sockets. CHANGING THIS

 

FROM THE DEFAULT CAN CAUSE SIGNIFICANT PERFORMANCE

 

PROBLEMS. Currently two values are defined:

 

 

send_blocking

NDDS_TRANSPORT_UDPV4_BLOCKING_ALWAYS:

Sockets

are

blocking (default socket options for Operating System).

 

 

 

 

 

 

NDDS_TRANSPORT_UDPV4_BLOCKING_NEVER: Sockets are modified

 

to make them non-blocking. This is not a supported configuration and

 

may cause significant performance problems.

 

 

 

 

 

Mask for the transport priority field. This is used in conjunction with

 

transport_priority_mapping_low and transport_priority_mapping_high

 

to define the mapping from the TRANSPORT_PRIORITY QosPolicy

 

(Section 6.5.21) to the IPv4 TOS field. Defines a contiguous region of bits in

 

the 32-bit transport priority value that is used to generate values for the

transport_priority_mask

IPv4 TOS field on an outgoing socket.

 

 

 

For example, the value 0x0000ff00 causes bits 9-16 (8 bits) to be used in the

 

mapping. The value will be scaled from the mask range (0x0000 - 0xff00 in

 

this case) to the range specified by low and high.

 

 

 

If the mask is set to zero, then the transport will not set IPv4 TOS for send

 

sockets.

 

 

15-10

Table 15.2 Properties for the Builtin UDPv4 Transport

 

Property Name

 

 

 

(prefix with

Property Value Description

 

 

‘dds.transport.UDPv4.builtin.’)

 

 

 

 

 

 

 

 

 

 

 

transport_priority_mapping_low

Sets the low and high values of the output range to IPv4 TOS.

 

 

 

These values are used in conjunction with transport_priority_mask to

 

 

 

 

 

 

define the mapping from the TRANSPORT_PRIORITY QosPolicy (Section

 

 

transport_priority_mapping_high

6.5.21) to the IPv4 TOS field. Defines the low and high values of the output

 

 

range for scaling.

 

 

 

 

 

 

Note that IPv4 TOS is generally an 8-bit value.

 

 

 

 

 

 

 

Controls whether or not to reuse receive resources. Setting this to 0

 

 

reuse_multicast_receive_resource

(FALSE) prevents multicast crosstalk by uniquely configuring a port and

 

 

creating a receive thread for each multicast group address.

 

 

 

 

 

 

Affects Linux systems only; ignored for non-Linux systems.

 

 

 

 

 

 

 

Maximum size in bytes of protocol overhead, including headers.

 

 

 

This value is the maximum size, in bytes, of protocol-related overhead.

 

 

 

Normally, the overhead accounts for UDP and IP headers. The default

 

 

protocol_overhead_max

value is set to accommodate the most common UDP/IP header size.

 

 

Note that when parent.message_size_max plus this overhead is larger than

 

 

 

 

 

 

the UDPv4 maximum message size (65535 bytes), the middleware will

 

 

 

automatically reduce the effective message_size_max to 65535 minus this

 

 

 

overhead.

 

 

 

 

 

Table 15.3 Properties for Builtin UDPv6 Transport

 

 

 

 

 

Property Name (prefix with

Description

 

‘dds.transport.UDPv6.builtin.’)

 

 

 

 

 

 

 

 

 

 

 

Number of bits in a 16-byte address that are used by the transport. Should

 

 

be between 0 and 128.

 

parent.address_bit_count

For example, for an address range of 0-255, this address_bit_count should

 

 

be set to 8. For the range of addresses used by IPv4 (4 bytes), it should be set

 

 

to 32.

 

 

 

 

 

A bitmap that defines various properties of the transport to the Connext

 

 

core.

 

parent.properties_bitmap

Currently, the only property supported is whether or not the transport

 

 

plugin will always loan a buffer when Connext tries to receive a message

 

 

using the plugin. This is in support of a zero-copy interface.

 

 

 

 

 

Specifies the maximum number of buffers that Connext can pass to the

 

 

send() method of a transport plugin.

 

 

The transport plugin send() API supports a gather-send concept, where the

 

 

send() call can take several discontiguous buffers, assemble and send them

 

 

in a single message. This enables Connext to send a message from parts

 

 

obtained from different sources without first having to copy the parts into a

 

parent.gather_send_buffer_

single contiguous buffer.

 

count_max

However, most transports that support a gather-send concept have an

 

 

upper limit on the number of buffers that can be gathered and sent. Setting

 

 

this value will prevent Connext from trying to gather too many buffers into a

 

 

send call for the transport plugin.

 

 

Connext requires all transport-plugin implementations to support a gather-

 

 

send of least a minimum number of buffers. This minimum number is

 

 

NDDS_TRANSPORT_PROPERTY_GATHER_SEND_BUFFER_COUNT_MIN.

 

 

 

 

15-11

Table 15.3 Properties for Builtin UDPv6 Transport

Property Name (prefix with

 

 

Description

 

 

 

‘dds.transport.UDPv6.builtin.’)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The maximum size of a message in bytes that can be sent or received by the

 

transport plugin.

 

 

 

 

 

 

This value must be set before the transport plugin is registered, so that

parent.message_size_max

Connext can properly use the plugin.

 

 

 

 

 

If you set this higher than the default, then the DomainParticipant’s

 

ReceiverPoolQosPolicy’s buffer_size should also be changed.

 

 

See also: Note on page 15-5.

 

 

 

 

 

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name.

 

 

 

 

 

 

Interfaces must be specified as comma-separated strings, with each comma

 

delimiting an interface. See “Formatting Rules for IPv6 ‘Allow’ and ‘Deny’

 

Address Lists” (Section 15.6.3 on page 15-18).

 

 

 

 

If the list is non-empty, this "white" list is applied before the

parent.allow_interfaces_list

parent.deny_interfaces_list list. The DomainParticipant will use the resulting

list of interfaces to inform its remote participant(s) about which unicast

 

 

addresses may be used to contact the DomainParticipant.

 

 

 

The resulting list restricts reception to a particular set of interfaces for unicast

 

UDP. Multicast output will still be sent and may be received over the

 

interfaces in the list (if multicast is supported on the platform).

 

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

 

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, deny the use of these interfaces.

 

Interfaces must be specified as comma-separated strings, with each comma

 

delimiting an interface. See “Formatting Rules for IPv6 ‘Allow’ and ‘Deny’

 

Address Lists” (Section 15.6.3 on page 15-18).

 

 

 

parent.deny_interfaces_list

This

"black" list is applied

after the

parent.allow_interfaces_list

list and

filters out the interfaces that should not be used.

 

 

 

 

 

 

 

 

The resulting list restricts reception to a particular set of interfaces for unicast

 

UDP. Multicast output will still be sent and may be received over the

 

interfaces in the list (if multicast is supported on the platform).

 

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

 

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, allow the use of multicast only these

 

interfaces; otherwise allow the use of all the allowed interfaces.

 

 

Interfaces must be specified as comma-separated strings, with each comma

 

delimiting an interface. See “Formatting Rules for IPv6 ‘Allow’ and ‘Deny’

 

Address Lists” (Section 15.6.3 on page 15-18).

 

 

 

 

This

list sub-selects from the allowed interfaces that are obtained after

parent.

applying the parent.allow_interfaces_list "white" list and the

allow_multicast_interfaces_list

parent.deny_interfaces_list

"black"

list.

Finally,

the

parent.

 

deny_multicast_interfaces_list is applied. Multicast output will be sent and

 

may be received over the interfaces in the resulting list (if multicast is

 

supported on the platform).

 

 

 

 

 

 

If this list is empty, all the allowed interfaces may potentially be used for

 

multicast.

 

 

 

 

 

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

 

 

 

 

 

 

 

 

 

15-12

Table 15.3 Properties for Builtin UDPv6 Transport

Property Name (prefix with

 

Description

‘dds.transport.UDPv6.builtin.’)

 

 

 

 

 

 

 

 

A list of strings, each identifying a range of interface addresses or an

 

interface name. If the list is non-empty, deny the use of those interfaces for

 

multicast.

 

Interfaces must be specified as comma-separated strings, with each comma

 

delimiting an interface. See “Formatting Rules for IPv6 ‘Allow’ and ‘Deny’

parent.

Address Lists” (Section 15.6.3 on page 15-18).

deny_multicast_interfaces_list

This "black" list is applied after the parent. allow_multicast_interfaces_list

 

list and filters out the interfaces that should not be used for multicast.

 

Multicast output will be sent and may be received over the interfaces in the

 

resulting list (if multicast is supported on the platform).

 

You must manage the memory of the list. The memory may be freed after

 

the DomainParticipant is deleted.

 

 

 

Size in bytes of the send buffer of a socket used for sending.

 

On most operating systems, setsockopt() will be called to set the SENDBUF

 

to the value of this parameter.

send_socket_buffer_size

This value must be greater than or equal to parent.message_size_max. The

maximum value is operating system-dependent.

 

 

If

NDDS_TRANSPORT_UDPV6_SOCKET_BUFFER_SIZE_OS_DEFAULT,

 

then setsockopt() (or equivalent) will not be called to size the send buffer of

 

the socket.

 

 

 

Size in bytes of the receive buffer of a socket used for receiving.

 

On most operating systems, setsockopt() will be called to set the RECVBUF

 

to the value of this parameter.

recv_socket_buffer_size

This value must be greater than or equal to parent.message_size_max. The

maximum value is operating system-dependent.

 

 

If

NDDS_TRANSPORT_UDPV6_SOCKET_-BUFFER_SIZE_OS_DEFAULT,

 

then setsockopt() (or equivalent) will not be called to size the receive buffer

 

of the socket.

 

 

 

Allows the transport plugin to use unicast UDP for sending and receiving.

 

By default, it will be turned on (1). Also by default, it will use all the

unicast_enabled

allowed network interfaces that it finds up and running when the plugin is

 

instanced.

 

Can be 1 (enabled) or 0 (disabled).

 

 

 

Allows the transport plugin to use multicast for sending and receiving.

 

You can turn multicast UDP on or off for this plugin. By default, it will be

multicast_enabled

turned on (1). Also by default, it will use the all network interfaces allowed

 

for multicast that it finds up and running when the plugin is instanced.

 

Can be 1 (enabled) or 0 (disabled).

 

 

 

Value for the time-to-live parameter for all multicast sends using this

multicast_ttl

plugin.

 

This is used to set the TTL of multicast packets sent by this transport plugin

 

 

 

Prevents the transport plugin from putting multicast packets onto the

 

loopback interface.

 

If disabled, then when sending multicast packets, Connext will not put a

 

copy on the loopback interface. This will prevent applications on the same

multicast_loopback_disabled

node (including itself) from receiving those packets.

This is set to 0 by default, meaning multicast loopback is enabled. Disabling

 

 

multicast loopback off (setting this value to 1) may result in minor

 

performance gains when using multicast.

 

Note: Windows CE does not support multicast loopback. This field is

 

ignored for Windows CE targets.

 

 

 

15-13

Table 15.3 Properties for Builtin UDPv6 Transport

Property Name (prefix with

Description

‘dds.transport.UDPv6.builtin.’)

 

Prevents the transport plugin from using the IP loopback interface. Three

 

values are allowed:

 

0: Enable local traffic via this plugin. This plugin will only use and report

 

the IP loopback interface if there are no other network interfaces (NICs) up

 

on the system.

ignore_loopback_interface

1: Disable local traffic via this plugin. Do not use the IP loopback interface

even if no NICs are discovered. This is useful when you want applications

 

running on the same node to use a more efficient plugin like Shared

 

Memory instead of the IP loopback.

 

-1: Automatic. Lets Connext decide among the above two choices. If a shared

 

memory transport plugin is available for local traffic, the effective value is 1

 

(i.e., disable UDPv4 local traffic). Otherwise, the effective value is 0, i.e., use

 

UDPv4 for local traffic also.

 

 

 

Prevents the transport plugin from using a network interface that is not

 

reported as RUNNING by the operating system.

 

The transport checks the flags reported by the operating system for each

 

network interface upon initialization. An interface which is not reported as

 

UP will not be used. This property allows the same check to be extended to

 

the IFF_RUNNING flag implemented by some operating systems. The

 

RUNNING flag is defined to mean that "all resources are allocated", and

ignore_nonrunning_interfaces

may be off if there is no link detected, e.g., the network cable is unplugged.

 

Two values are allowed:

 

0: Do not check the RUNNING flag when enumerating interfaces, just make

 

sure the interface is UP.

 

1: Check the flag when enumerating interfaces, and ignore those that are not

 

reported as RUNNING. This can be used on some operating systems to

 

cause the transport to ignore interfaces that are enabled but not connected

 

to the network.

 

 

 

Prevents the transport plugin from doing a zero copy.

 

By default, this plugin will use the zero copy on OSs that offer it. While this

 

is good for performance, it may sometime tax the OS resources in a manner

 

that cannot be overcome by the application.

no_zero_copy

The best example is if the hardware/device driver lends the buffer to the

application itself. If the application does not return the loaned buffers soon

 

enough, the node may error or malfunction. In case you cannot reconfigure

 

the H/W, device driver, or the OS to allow the zero-copy feature to work for

 

your application, you may have no choice but to turn off zero-copy.

 

By default this is set to 0, so Connext will use the zero-copy API if offered by

 

the OS.

 

 

 

Controls the blocking behavior of send sockets. CHANGING THIS FROM

 

THE DEFAULT CAN CAUSE SIGNIFICANT PERFORMANCE

 

PROBLEMS. Currently two values are defined:

send_blocking

NDDS_TRANSPORT_UDPV4_BLOCKING_ALWAYS: Sockets are blocking

(default socket options for Operating System).

 

 

NDDS_TRANSPORT_UDPV4_BLOCKING_NEVER: Sockets are modified

 

to make them non-blocking. This is not a supported configuration and

 

may cause significant performance problems.

 

 

 

Specifies whether the UDPv6 transport will process IPv4 addresses.

enable_v4mapped

Set this to 1 to turn on processing of IPv4 addresses. Note that this may

make it incompatible with use of the UDPv4 transport within the same

 

 

domain participant.

15-14

Table 15.3 Properties for Builtin UDPv6 Transport

Property Name (prefix with

Description

‘dds.transport.UDPv6.builtin.’)

 

Sets a mask for use of transport priority field.

 

If transport priority mapping is supported on the platforma, this mask is

 

used in conjunction with transport_priority_mapping_low and

 

transport_priority_mapping_high to define the mapping from the DDS

 

transport priority TRANSPORT_PRIORITY QosPolicy (Section 6.5.21) to the

 

IPv6 TCLASS field.

transport_priority_mask

Defines a contiguous region of bits in the 32-bit transport priority value that

 

is used to generate values for the IPv6 TCLASS field on an outgoing socket.

 

For example, the value 0x0000ff00 causes bits 9-16 (8 bits) to be used in the

 

mapping. The value will be scaled from the mask range (0x0000 - 0xff00 in

 

this case) to the range specified by low and high.

 

If the mask is set to zero, then the transport will not set IPv6 TCLASS for

 

send sockets.

 

 

transport_priority_mapping_low

Sets the low and high values of the output range to IPv6 TCLASS.

 

These values are used in conjunction with transport_priority_mask to

 

transport_priority_mapping_high

define the mapping from DDS transport priority to the IPv6 TCLASS field.

Defines the low and high values of the output range for scaling.

 

Note that IPv6 TCLASS is generally an 8-bit value.

a. Please refer to the Platform Notes to find out if the transport priority is supported on a specific platform.

Table 15.4 Properties for Builtin Shared-Memory Transport

Property Name

 

(prefix with

Property Value Description

‘dds.transport.shmem.builtin.’)

 

 

 

 

 

 

Number of bits in a 16-byte address that are used by the transport. Should

 

be between 0 and 128.

parent.address_bit_count

For example, for an address range of 0-255, this address_bit_count should

 

be set to 8. For the range of addresses used by IPv4 (4 bytes), it should be

 

set to 32.

 

 

 

A bitmap that defines various properties of the transport to the Connext

 

core.

parent.properties_bitmap

Currently, the only property supported is whether or not the transport

 

plugin will always loan a buffer when Connext tries to receive a message

 

using the plugin. This is in support of a zero-copy interface.

 

 

 

Specifies the maximum number of buffers that Connext can pass to the

 

send() method of a transport plugin.

 

The transport plugin send() API supports a gather-send concept, where the

 

send() call can take several discontiguous buffers, assemble and send them

 

in a single message. This enables Connext to send a message from parts

 

obtained from different sources without first having to copy the parts into

parent.gather_send_buffer_

a single contiguous buffer.

count_max

However, most transports that support a gather-send concept have an

 

upper limit on the number of buffers that can be gathered and sent. Setting

 

this value will prevent Connext from trying to gather too many buffers into

 

a send call for the transport plugin.

 

Connext requires all transport-plugin implementations to support a gather-

 

send of least a minimum number of buffers. This minimum is

 

NDDS_TRANSPORT_PROPERTY_GATHER_SEND_BUFFER_COUNT_MIN.

 

 

15-15

Table 15.4 Properties for Builtin Shared-Memory Transport

Property Name

 

(prefix with

Property Value Description

‘dds.transport.shmem.builtin.’)

 

 

 

 

 

 

The maximum size of a message in bytes that can be sent or received by the

 

transport plugin.

 

This value must be set before the transport plugin is registered, so that

parent.message_size_max

Connext can properly use the plugin.

 

If you set this higher than the default, then the DomainParticipant’s

 

ReceiverPoolQosPolicy’s buffer_size should also be changed.

 

See also: Note on page 15-5.

 

 

parent.allow_interfaces_list

 

 

 

parent.deny_interfaces_list

 

 

 

parent.

Not applicable to the Shared-Memory Transport

allow_multicast_interfaces_list

 

 

 

parent.

 

deny_multicast_interfaces_list

 

 

 

 

Number of messages that can be buffered in the receive queue. This is the

 

maximum number of messages that can be buffered in a RecvResource of

 

the Transport Plugin. This does not guarantee that the Transport-Plugin

received_message_count_max

will actually be able to buffer received_message_count_max messages of

 

the maximum size set in parent.message_size_max.

 

The total number of bytes that can be buffered for a RecvResource is

 

actually controlled by receive_buffer_size.

 

 

 

The total number of bytes that can be buffered in the receive queue.

 

This number controls how much memory is allocated by the plugin for the

 

receive queue (on a per RecvResource basis). The actual number of bytes

 

allocated is:

 

size = receive_buffer_size + message_size_max +

 

received_message_count_max * fixedOverhead

 

where fixedOverhead is some small number of bytes used by the queue data

 

structure.

 

If receive_buffer_size <

 

message_size_max * received_message_count_max, then the transport

 

plugin will not be able to store received_message_count_max messages of

 

size message_size_max.

 

If receive_buffer_size >

 

message_size_max * received_message_count_max, then there will be

receive_buffer_size

memory allocated that cannot be used by the plugin and thus wasted.

To optimize memory usage, specify a receive queue size less than that

 

 

required to hold the maximum number of messages which are all of the

 

maximum size.

 

In most situations, the average message size may be far less than the

 

maximum message size. So for example, if the maximum message size is

 

64K bytes, and you configure the plugin to buffer at least 10 messages, then

 

640K bytes of memory would be needed if all messages were 64K bytes.

 

Should this be desired, then receive_buffer_size should be set to 640K

 

bytes.

 

However, if the average message size is only 10K bytes, then you could set

 

the receive_buffer_size to 100K bytes. This allows you to optimize the

 

memory usage of the plugin for the average case and yet allow the plugin

 

to handle the extreme case.

 

The queue will always be able to hold 1 message of message_size_max

 

bytes, regardless of the value of receive_buffer_size.

 

 

15-16

15.6.1Notes Regarding Loopback and Shared Memory

By default, Connext uses shared memory to communicate with other DomainParticipants on the same node, and disables local traffic over the UDPv4 or UPDv6 loopback interface. Thus, by default, a Connext application with shared memory enabled will not communicate with other applications on the same node that don’t have shared memory enabled.

For example, suppose you have three Connext applications on the same node. Shared memory is enabled on Applications A and B, but disabled on Application C. In this scenario, A and B will communicate with each other, but they will not communicate with C.

You can change this behavior by setting the "ignore_loopback_interface" field of the UDPv4 transport properties to 0 on Applications A and B. This will force DomainParticipants with shared memory enabled to also communicate over UDPv4 or UDPv6 loopback (and thus find Application C without using shared memory). Alternatively, you can disable shared memory on A and B via the TransportBuiltinQosPolicy.

15.6.2Setting the Maximum Gather-Send Buffer Count for UDPv4 and UDPv6

To minimize memory copies, Connext uses the "gather send" API that may be available on the transport.

Some operating systems limit the number of gather buffers that can be given to the gather-send function. This limits Connext's ability to concatenate multiple samples into a single network message. An example is the UDP transport's sendmsg() call, which on some OSs (such as Solaris) can only take 16 gather buffers, limiting the number of samples that can be concatenated to five or six.

To match this limitation, Connext sets the UDPv4 and UDPv6 transport plug-ins' gather_send_buffer_count_max to 16 by default for all operating systems. This field is part of the NDDS_Transport_Property_t structure.

On VxWorks 5.5 operating systems, gather_send_buffer_count_max can be set as high as 63.

On Windows and INTEGRITY operating systems, gather_send_buffer_count_max can be set as high as 128.

On most other operating systems, gather_send_buffer_count_max can be set as high as 16.

If you are using an OS that allows more than 16 gather buffers for a sendmsg() call, you may increase the UDPv4 or UDPv6 transport plug-in's gather_send_buffer_count_max from the default up to your OS's limit (but no higher than 128).

For example, if your OS imposes a limit of 64 gather buffers, you may increase the gather_send_buffer_count_max up to 64. However, if your OS's gather-buffer limit is 1024, you may only increase the gather_send_buffer_count_max up to 128.

By changing gather_send_buffer_count_max, you can increase performance in the following situations:

When a DataWriter is sending multiple packets to a DataReader either because the DataReader is a late-joiner and needs to catch up, or because several packets were dropped and need to be resent. Changing the setting will help when the DataWriter needs to send or resend more than five or six packets at a time.

If your application has more than five or six DataWriters or DataReaders in a participant. (In this case, the change will make the discovery process more efficient.)

When using an asynchronous DataWriter, samples are sent asynchronously by a separate thread. Samples may not be sent immediately, but may be queued instead, depending on the settings of the associated FlowController. If multiple samples in the queue must be sent to the same destination, they will be coalesced into as few network packets as

15-17

possible. The number of samples that can be put in a single message is directly proportional to gather_send_buffer_count_max. Therefore, by maximizing gather_send_buffer_count_max, you can minimize the number of packets on the wire.

15.6.3Formatting Rules for IPv6 ‘Allow’ and ‘Deny’ Address Lists

This section describes how to format the strings in the properties that create “allow” and “deny” lists:

dds.transport.UDPv6.builtin.parent.allow_interfaces_list

dds.transport.UDPv6.builtin.parent.deny_interfaces_list

dds.transport.UDPv6.builtin.parent. allow_multicast_interfaces_list

dds.transport.UDPv6.builtin.parent. deny_multicast_interfaces_list

These properties may contain a list of strings, each identifying a range of interface addresses or an interface name. Interfaces should be specified as comma-separated strings, with each comma delimiting an interface.

The strings can be addresses and patterns in IPv6 notation. They are case-insensitive.

They may contain a wildcard '*' and can expand up to 4 digits in a block. The wildcard must be either leading or trailing (cannot be in the middle of the string). Multiple wildcards can be specified in a single filter, but only one wildcard can be specified per block (between colons). Table 15.5 shows some examples.

Table 15.5 Examples of IPv6 Address Filters

Example Filter

Equivalent Filters

Matches

 

 

 

 

 

 

*:*:*:*:*:*:*:*

 

Any IPv6 interface

 

 

 

FE80::*:*

fe80::*:*,

 

 

 

 

 

Fe80:0:0::*:*

 

 

 

 

 

Fe80:0:0:0:0:0:*:*

FE80:0000:0000:0000:0000:0000:xxxx:xxxx

 

 

 

FE80:aBC::202:2*:*:*2

 

FE80:0ABC:0000:0000:0202:2xxx:xxxx:xxx2

 

 

 

15.7Installing Additional Builtin Transport Plugins with register_transport()

After you create an instance of a transport plugin (see Section 15.4) , you have to register it.

The builtin transports (UDPv4, UDPv6, and Shared Memory) are implicitly registered by default (if they are enabled via the TRANSPORT_BUILTIN QosPolicy (DDS Extension) (Section 8.5.7)). Therefore, you only need to explicitly register a builtin transport if you want an extra instance of it (suppose you want two UDPv4 transports, one with special settings).

The register_transport() operation registers a transport plugin for use with a DomainParticipant and assigns it a network address. (Note: this operation is only available in the APIs other than Java or .NET. If you are using Java or .NET, use the Property QosPolicy to install additional transport plugins.)

NDDS_Transport_Handle_t NDDSTransportSupport::register_transport (DDSDomainParticipant * participant_in,

NDDS_Transport_Plugin * transport_in,

15-18

const DDS_StringSeq & aliases_in,

const NDDS_Transport_Address_t & network_address_in)

participant_in A non-NULL, disabled DomainParticipant.

transport_in A non-NULL transport plugin that is currently not registered with another

DomainParticipant.

aliases_in A non-NULL sequence of strings used as aliases to refer to the transport plugin symbolically. The transport plugin will be "available for use" to an Entity contained in the DomainParticipant, if the transport alias list associated with the Entity contains one of these transport aliases. An empty alias list represents a WILDCARD and matches ALL aliases. See Transport Aliases (Section 15.7.2).

network_address_in The network address at which to register this transport plugin. The least significant transport_in.property.address_bit_count will be truncated. The remaining bits are the network address of the transport plugin. See Transport Network Addresses (Section 15.7.3).

Note: You must ensure that the transport plugin instance is only used by one DomainParticipant at a time. See Section 15.7.1.

Upon success, a valid non-NIL transport handle is returned, representing the association between the DomainParticipant and the transport plugin. If the transport cannot be registered, NDDS_TRANSPORT_HANDLE_NIL is returned.

Note that a transport plugin's class name is automatically registered as an implicit alias for the plugin. Thus, a class name can be used to refer to all the transport plugin instances of that class.

The C and C++ APIs also have a operation to retrieve a registered transport plugin, get_transport_plugin().

NDDS_Transport_Plugin* get_transport_plugin(

DDSDomainParticipant* participant_in, const char* alias_in);

15.7.1Transport Lifecycles

If you create and register a transport plugin with a DomainParticipant, you are responsible for deleting it by calling its destructor. Builtin transport plugins are automatically managed by Connext if they are implicitly registered through the TransportBuiltinQosPolicy.

User-created transport plugins must not be deleted while they are is still in use by a DomainParticipant. This generally means that a user-created transport plugin instance can only be deleted after the DomainParticipant with which it was registered is deleted. Note that a transport plugin cannot be "unregistered" from a DomainParticipant.

A transport plugin instance cannot be registered with more than one DomainParticipant at a time. This requirement is necessary to guarantee the multi-threaded safety of the transport API.

Thus, if the same physical transport resources are to be used with multiple DomainParticipants in the same address space, the transport plugin should be written in such a way so that it can be instantiated multiple times—once for each DomainParticipant in the address space. Note that it is always possible to write the transport plugin so that multiple transport plugin instances share the same underlying resources; however the burden (if any) of guaranteeing multi-threaded safety to access shared resource shifts to the transport plugin developer.

15.7.2Transport Aliases

In order to use a transport plugin instance in a Connext application, it must be registered with a

DomainParticipant using the register_transport() operation (Section 15.7). register_transport() takes a pointer to the transport plugin instance, and in addition allows you to specify a sequence of "alias" strings to symbolically refer to the transport plugin. The same alias strings can be used to register more than one transport plugin.

15-19

Multiple transport plugins can be registered with a DomainParticipant. An alias symbolically refers to one or more transport plugins registered with the DomainParticipant. Pre-configured builtin transport plugin instances can be referred to using preconfigured aliases.

A transport plugin's class name is automatically used as an implicit alias. It can be used to refer to all the transport plugin instance of that class.

You can use aliases to refer to transport plugins in order to specify:

Transport plugins to use for discovery (see enabled_transports in DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2)), and for DataWriters and DataReaders (see TRANSPORT_SELECTION QosPolicy (DDS Extension) (Section 6.5.22)).

Multicast addresses on which to receive discovery messages (see multicast_receive_addresses in DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2)), and the multicast addresses and ports on which to receive user data (DDS_DataReaderQos::multicast).

Unicast ports used for user data (see TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23)) on both DataWriters and DataReaders.

Transport plugins used to parse an address string in a locator.

A DomainParticipant (and its contained entities) will start using a transport plugin after the DomainParticipant is enabled (see Enabling Entities (Section 4.1.2)). An entity will use all the transport plugins that match the specified transport QoS policy. All transport plugins are treated uniformly, regardless of how they were created or registered; there is no notion of some transports being more "special" that others.

15.7.3Transport Network Addresses

The address bits not used by the transport plugin for its internal addressing constitute its network address bits.

In order for Connext to properly route the messages, each unicast interface in the domain must have a unique address.

You specify the network address when installing a transport plugin via the register_transport() operation (Section 15.7). Choose the network address for a transport plugin so that the resulting fully qualified 128-bit address will be unique in the domain.

If two instances of a transport plugin are registered with a DomainParticipant, they need different network addresses so that their unicast interfaces will have unique, fully qualified 128-bit addresses.

While it is possible to create multiple transports with the same network address (this can be useful for certain situations), this requires special entity configuration for most transports to avoid clashes in resource use (e.g., sockets for UDPv4 transport).

15.8Installing Additional Builtin Transport Plugins with PropertyQosPolicy

Similar to default builtin transport instances, additional builtin transport instances can also be configured through PROPERTY QosPolicy (DDS Extension) (Section 6.5.17).

To install additional instances of builtin transport, the Properties listed in Table 15.6 are required.

15-20

Table 15.6 Properties for Dynamically Loading and Registering Additional Builtin Transport Plugins

Property Name

 

Description

 

 

 

 

 

 

dds.transport.load_plugins

Comma-separated list of <TRANSPORT_PREFIX>. Up to 8 entries may be

 

specified.

 

 

 

 

 

 

Indicates the additional builtin transport instances to be installed, and must

 

be in one of the following form, where <STRING> can be any string other

 

than “builtin”:

 

 

 

dds.transport.shmem.<STRING>

 

 

<TRANSPORT_PREFIX>

dds.transport.UDPv4.<STRING>

 

 

 

dds.transport.UDPv6.<STRING>

 

 

 

In the following examples in this table, <TRANSPORT_PREFIX> is used to

 

indicate one element of this string that is used as a prefix in the property

 

names for all the settings that are related to the plugin.

 

 

 

 

 

 

 

Optional.

 

 

 

 

Aliases used to register the transport to the DomainParticipant. Refer to the

<TRANSPORT_PREFIX>.

aliases_in

parameter in register_transport() (see Installing

Additional

Builtin Transport Plugins with register_transport() (Section 15.7)). Aliases

aliases

should be specified as a comma separated string, with each comma

 

 

delimiting an alias. If it is not specified, <TRANSPORT_PREFIX> is used as

 

the default alias for the plugin.

 

 

 

 

 

 

 

 

Optional.

 

 

 

 

Network address used to register the transport to the DomainParticipant.

<TRANSPORT_PREFIX>.

Refer to

network_address_in parameter in

register_transport() (see

Installing

Additional Builtin Transport Plugins

with register_transport()

network_address

(Section 15.7)). If it is not specified, the network_address_out output

 

 

parameter from NDDS_Transport_create_plugin is used. The default value

 

is a zeroed out network address.

 

 

 

 

 

 

 

 

Optional.

 

 

 

 

Property for creating the transport plugin. More than one

 

<TRANSPORT_PREFIX>.<property_name> can be specified. See Table 15.2

<TRANSPORT_PREFIX>.

on page 15-6 through Table 15.4 on page 15-15 for the property names that

can be used to configure the additional builtin transport instances. The only

<property_name>

difference is that the property name will be prefixed by

 

 

dds.transport.<builitn_transport_name>.<instance_name>,

where

 

<instance_name> is configured through the dds.transport.load_plugins

 

property instead of dds.transport.<builtin_transport_name>.builtin.

 

 

 

 

 

15.9Other Transport Support Operations

15.9.1Adding a Send Route

By default, a transport plugin will send outgoing messages using the network address range at which the plugin was registered.

The add_send_route() operation allows you to control the routing of outgoing messages, so that a transport plugin will only send messages to certain ranges of destination addresses.

Before using this operation, the DomainParticipant to which the transport is registered must be disabled.

DDS_ReturnCode_t NDDSTransportSupport::add_send_route

(const NDDS_Transport_Handle_t & transport_handle_in, const NDDS_Transport_Address_t & address_range_in, DDS_Long address_range_bit_count_in)

15-21

transport_handle_in A valid non-NIL transport handle as a result of a call to register_transport() (Section 15.7).

address_range_in The outgoing address range for which to use this transport plugin.

address_range_bit_count_in The number of most significant bits used to specify the address range.

It returns one of the standard return codes or DDS_RETCODE_PRECONDITION_NOT_MET.

The method can be called multiple times for a transport plugin, with different address ranges. You can set up a routing table to restrict the use of a transport plugin to send messages to selected addresses ranges.

Outgoing Address Range 1

—>

Transport Plugin

 

 

 

.

 

.

.

—>

.

.

 

.

 

 

 

Outgoing Address Range K

—>

Transport Plugin

 

 

 

15.9.2Adding a Receive Route

By default, a transport plugin will receive incoming messages using the network address range at which the plugin was registered.

The add_receive_route() operation allows you to configure a transport plugin so that it will only receive messages on certain ranges of addresses.

Before using this operation, the DomainParticipant to which the transport is registered must be disabled.

DDS_ReturnCode_t NDDSTransportSupport::add_receive_route (const NDDS_Transport_Handle_t & transport_handle_in,

const NDDS_Transport_Address_t & address_range_in, DDS_Long address_range_bit_count_in)

transport_handle_in A valid non-NIL transport handle as a result of a call to register_transport() (Section 15.7).

address_range_in The incoming address range for which to use this transport plugin.

address_range_bit_count_in The number of most significant bits used to specify the address range.

It returns one of the standard return codes or DDS_RETCODE_PRECONDITION_NOT_MET.

The method can be called multiple times for a transport plugin, with different address ranges.

Transport Plugin

<—

Incoming Address Range 1

 

 

 

.

 

.

.

<—

.

.

 

.

 

 

 

Transport Plugin

<—

Incoming Address Range M

 

 

 

You can set up a routing table to restrict the use of a transport plugin to receive messages from selected ranges. For example, you may restrict a transport plugin to:

Receive messages from a certain multicast address range.

Receive messages only on certain unicast interfaces (when multiple unicast interfaces are available on the transport plugin).

15-22

15.9.3Looking Up a Transport Plugin

If you need to get the handle associated with a transport plugin that is registered with a

DomainParticipant, use the lookup_transport() operation.

NDDS_Transport_Handle_t NDDSTransportSupport::lookup_transport (DDSDomainParticipant * participant_in,

DDS_StringSeq & aliases_out, NDDS_Transport_Address_t & network_address_out, NDDS_Transport_Plugin * transport_in )

participant_in A non-NULL DomainParticipant.

aliases_out A sequence of strings where the aliases used to refer to the transport plugin symbolically will be returned. NULL if not interested.

network_address_out The network address at which to register the transport plugin will be returned here. NULL if not interested.

transport_in A non-NULL transport plugin that is already registered with the

DomainParticipant.

If successful, this operation returns a valid non-NIL transport handle, representing the association between the DomainParticipant and the transport plugin; otherwise it returns a NDDS_TRANSPORT_HANDLE_NIL upon failure.

15-23

Chapter 16 Built-In Topics

This chapter discusses how to use Built-in Topics.

Connext must discover and keep track of remote entities, such as new participants in the domain. This information may also be important to the application itself, which may want to react to this discovery or access it on demand. To support these needs, Connext provides built-in Topics (“DCPSParticipant”, “DCPSPublication”, “DCPSSubscription” in Figure 14.2 on page 14- 8) and the corresponding built-in DataReaders that you can use to access this discovery information.

The discovery information is accessed just as if it is normal application data. This allows the application to know (either via listeners or by polling) when there are any changes in those values. Note that only entities that belong to a different DomainParticipant are being discovered and can be accessed through the built-in readers. Entities that are created within the local DomainParticipant are not included as part of the data that can be accessed by the built-in readers.

Built-in topics contain information about the remote entities, including their QoS policies. These QoS policies appear as normal fields inside the topic’s data, which can be read by means of the built-in Topic. Additional information is provided to identify the entity and facilitate the application logic.

16.1Listeners for Built-in Entities

Built-in entities have default listener settings:

The built-in Subscriber and its built-in topics have 'nil' listeners—all status bits are set in the listener masks, but the listener is NULL. This effectively creates a NO-OP listener that does not reset communication status.

Built-in DataReaders have null listeners with no status bits set in their masks.

This approach prevents callbacks to the built-in DataReader listeners from invoking your DomainParticipant’s listeners, and at the same time ensures that the status changed flag is not reset. For more information, see Table 4.4, “Effect of Different Combinations of Listeners and Status Bit Masks,” on page 4-20 and “Hierarchical Processing of Listeners” on page 4-21.

16-1

16.2Built-in DataReaders

Built-in DataReaders belong to a built-in Subscriber, which can be retrieved by using the DomainParticipant’s get_builtin_subscriber() operation. You can retrieve the built-in DataReaders by using the Subscriber’s lookup_datareader() operation, which takes the Topic name as a parameter. The built-in DataReader is created when lookup_datareader() is called on a built-in topic for the first time.

To conserve memory, built-in Subscribers and DataReaders are created only if and when you look them up. Therefore, if you do not want to miss any built-in data, you should look up the built-in readers before the DomainParticipant is enabled.

Table 16.1 through Table 16.4 describe the built-in topics and their data types. The USER_DATA QosPolicy (Section 6.5.25), TOPIC_DATA QosPolicy (Section 5.2.1) and GROUP_DATA QosPolicy (Section 6.4.4) are included as part of the built-in data type and are not used by Connext. Therefore, you can use them to send application-specific information.

Built-in topics can be used in conjunction with the ignore_*() operations to ignore certain entities (see Section 16.4).

Table 16.1 Participant Built-in Topic’s Data Type (DDS_ParticipantBuiltinTopicData)

Type

Field

Description

 

 

 

 

 

 

 

 

 

 

 

DDS_BuiltinTopicKey

key

Key to distinguish the discovered DomainParticipant

 

 

 

 

 

 

Data that can be set when the related DomainParticipant is

DDS_UserDataQosPolicy

user_data

created (via the USER_DATA QosPolicy (Section 6.5.25))

and that the application may use as it wishes (e.g., to

 

 

 

 

perform some security checking).

 

 

 

 

 

 

 

 

Pairs of names/values to be stored with the

DDS_PropertyQosPolicy

property

DomainParticipant. See PROPERTY QosPolicy

(DDS

Extension) (Section 6.5.17). The usage is

strictly

 

 

 

 

application-dependent.

 

 

 

 

 

 

 

 

 

 

 

DDS_ProtocolVersion_t

rtps_protocol_

Version number of the RTPS wire protocol used.

 

 

version

 

 

 

 

 

 

 

 

 

 

 

 

DDS_VendorId_t

rtps_vendor_id

ID of vendor implementing the RTPS wire protocol.

 

 

 

 

 

 

 

 

dds_builtin_

Bitmap set by the discovery plugins.

 

 

 

DDS_UnsignedLong

Each bit in this field indicates a built-in endpoint present

endpoints

 

for discovery.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

If the TransportUnicastQosPolicy is not specified when a

 

 

DataWriter/DataReader is created, the unicast_locators in

 

 

the corresponding Publication/Subscription built-in topic

 

default_unicast_

data will be empty. When

the unicast_locators

in

the

DDS_LocatorSeq

Publication/SubscriptionBuiltinTopicData is

empty,

the

locators

 

default_unicast_locators in the corresponding Participant

 

 

 

 

Builtin Topic Data is assumed.

 

 

 

 

 

If default_unicast_locators is empty, it defaults to

 

 

DomainParticipantQos.default_unicast.

 

 

 

 

 

 

 

 

 

DDS_ProductVersion_t

product_version

Vendor-specific parameter.

The current

version

of

Connext.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Name and role_name assigned to the DomainParticipant.

DDS_EntityNameQosPolicy

participant_name

See ENTITY_NAME QosPolicy (DDS Extension) (Section

 

 

6.5.9).

 

 

 

 

 

 

 

 

 

 

 

Table 16.5 lists the QoS of the built-in Subscriber and DataReader created for accessing discovery

data. These are provided for your reference only; they cannot be changed.

Note: The DDS_TopicBuiltinTopicData built-in topic (described in Table 16.4) is meant to convey information about discovered Topics. However, this topic's data is not sent separately

16-2

Table 16.2 Publication Built-in Topic’s Data Type (DDS_PublicationBuiltinTopicData)

Type

Field

 

Description

 

 

 

 

 

 

 

 

DDS_BuiltinTopicKey_t

key

Key to distinguish the discovered DataWriter

 

 

 

 

DDS_BuiltinTopicKey_t

participant_key

Key to distinguish the participant to

which the

discovered DataWriter belongs

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_String

topic_name

Topic name of the discovered DataWriter

 

 

 

 

 

 

DDS_String

type_name

Type name attached to the topic of the

discovered

DataWriter

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DurabilityQosPolicy

durability

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DurabilityService-

durability_service

 

 

 

 

 

QosPolicy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DeadlineQosPolicy

deadline

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DestinationOrder-

destination_order

 

 

 

 

 

QosPolicy

QosPolicies of the discovered DataWriter

 

 

 

 

 

DDS_LatencyBudget-

latency_budget

 

 

 

 

 

QosPolicy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_LivelinessQosPolicy

liveliness

 

 

 

 

 

 

 

 

 

 

 

 

DDS_ReliabilityQosPolicy

reliability

 

 

 

 

 

 

 

 

 

 

 

 

DDS_LifespanQosPolicy

lifespan

 

 

 

 

 

 

 

 

 

 

Data that can be set when the DataWriter is created (via

DDS_UserDataQosPolicy

user_data

the USER_DATA QosPolicy (Section 6.5.25)) and that

 

 

the application may use as it wishes.

 

 

 

 

 

 

 

 

 

DDS_OwnershipQosPolicy

ownership

 

 

 

 

 

 

 

 

 

 

 

 

DDS_OwnershipStrength-

ownership_strength

 

 

 

 

 

QosPolicy

 

 

 

 

 

 

QosPolicies of the discovered DataWriter

 

 

 

 

 

 

DDS_DestinationOrder-

destination_order

 

 

 

 

 

 

 

QosPolicy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_PresentationQosPolicy

presentation

 

 

 

 

 

 

 

 

 

 

Name of the partition, set in the PARTITION

DDS_PartitionQosPolicy

partition

QosPolicy (Section 6.4.5) for the publisher to which the

 

 

discovered DataWriter belongs

 

 

 

 

 

 

 

 

Data that can be set when the Topic (with which the

DDS_TopicDataQosPolicy

topic_data

discovered DataWriter is associated) is created (via the

TOPIC_DATA QosPolicy (Section 5.2.1)) and that the

 

 

 

 

application may use as it wishes.

 

 

 

 

 

 

 

Data that can be set when the Publisher to which the

DDS_GroupDataQosPolicy

group_data

discovered DataWriter belongs is created

(via the

GROUP_DATA QosPolicy (Section 6.4.4)) and that the

 

 

 

 

application may use as it wishes.

 

 

 

 

 

 

DDS_TypeObject *

type

Describes the type of the remote DataReader.

 

See the API Reference HTML documentation.

 

 

 

 

 

 

 

Type code information about this Topic. See Using

DDS_TypeCode *

type_code

Generated Types without Connext (Standalone)

 

 

(Section 3.7).

 

 

 

 

 

 

 

 

 

DDS_BuiltinTopicKey_t

publisher_key

The key of the

Publisher to

which the DataWriter

belongs.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Properties (pairs of names/values) assigned to the

DDS_PropertyQosPolicy

property

corresponding

DataWriter.

Usage

is

strictly

application-dependent. See PROPERTY

 

QosPolicy

 

 

 

 

 

(DDS Extension) (Section 6.5.17).

 

 

 

 

 

 

 

 

 

16-3

Table 16.2 Publication Built-in Topic’s Data Type (DDS_PublicationBuiltinTopicData)

 

Type

Field

 

 

 

Description

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

If the TransportUnicastQosPolicy is not specified when

 

 

 

 

a DataWriter/DataReader is created, the unicast_locators

 

 

 

 

in the corresponding Publication/Subscription built-in

 

DDS_LocatorSeq

unicast_locators

topic data will be empty. When the unicast_locators in

 

the

Publication/SubscriptionBuiltinTopicData

is

 

 

 

 

 

 

 

 

empty,

the

default_unicast_locators

in

the

 

 

 

 

corresponding Participant Builtin Topic Data is

 

 

 

 

assumed.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Virtual GUID for the corresponding DataWriter. For

 

DDS_GUID_t

virtual_guid

more information, see Durability and Persistence

 

 

 

 

Based on Virtual GUIDs (Section 12.2).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_ProtocolVersion_t

rtps_protocol_

Version number of the RTPS wire protocol in use.

 

 

version

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_VendorId_t

rtps_vendor_id

ID

of

the vendor implementing the

RTPS

wire

 

protocol.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_Product_Version_t

product_version

Vendor-specific

value. For

RTI, this is

the

current

 

version of Connext.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

When the MULTI_CHANNEL QosPolicy (DDS

 

 

 

 

Extension) (Section 6.5.14) is used on the discovered

 

 

 

 

DataWriter, the locator_filter contains the sequence of

 

 

 

 

LocatorFilters in that policy.

 

 

 

 

 

DDS_LocatorFilterQosPolicy

locator_filter

There is one LocatorFilter per DataWriter channel. A

 

 

 

 

channel is defined by a filter expression and a

 

 

 

 

sequence of multicast locators.

 

 

 

 

 

 

 

See LOCATOR_FILTER QoS Policy (DDS Extension)

 

 

 

 

(Section 16.2.1).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

disable_positive_

Vendor

specific parameter. Determines

whether

 

DDS_Boolean

matching

DataReaders

send

 

positive

 

acks

 

 

 

acknowledgements for reliability.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Name and role_name assigned to the DataWriter. See

 

DDS_EntityNameQosPolicy

publication_name

ENTITY_NAME QosPolicy (DDS Extension) (Section

 

 

 

 

6.5.9).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Table 16.3 Subscription Built-in Topic’s Data Type (DDS_SubscriptionBuiltinTopicData)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Type

Field

 

 

 

 

Description

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_BuiltinTopicKey_t

key

 

Key to distinguish the discovered DataReader.

 

 

 

 

 

 

 

 

 

 

 

 

DDS_BuiltinTopicKey_t

participant_key

 

Key

to

distinguish the participant to

which

the

 

 

discovered DataReader belongs.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

char *

topic_name

 

Topic name of the discovered DataReader.

 

 

 

 

 

 

 

 

 

 

 

 

char *

type_name

 

Type

name attached to the

Topic of the

discovered

 

 

DataReader.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DurabilityQosPolicy

durability

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_DeadlineQosPolicy

deadline

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_LatencyBudget-

latency_budget

 

 

 

 

 

 

 

 

 

 

QosPolicy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

QosPolicies of the discovered DataReader

 

 

 

 

DDS_LivelinessQosPolicy

liveliness

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_ReliabilityQosPolicy

reliability

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_OwnershipQosPolicy

ownership

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DDS_

destination_order

 

 

 

 

 

 

 

 

 

 

DestinationOrderQosPolicy

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

16-4

Table 16.3 Subscription Built-in Topic’s Data Type (DDS_SubscriptionBuiltinTopicData)

Type

Field

Description

 

 

 

 

 

 

 

 

Data that can be set when the DataReader is created (via

DDS_UserDataQosPolicy

user_data

the USER_DATA QosPolicy (Section 6.5.25)) and that

 

 

the application may use as it wishes.

 

 

 

DDS_

time_based_filter

 

TimeBasedFilterQosPolicy

QosPolicies of the discovered DataReader

 

DDS_PresentationQosPolicy

presentation

 

 

 

 

 

 

Name of the partition, set in the PARTITION QosPolicy

DDS_PartitionQosPolicy

partition

(Section 6.4.5) for the Subscriber to which the discovered

 

 

DataReader belongs.

 

 

 

 

 

Data that can be set when the Topic to which the

DDS_TopicDataQosPolicy

topic_data

discovered DataReader belongs is created (via the

TOPIC_DATA QosPolicy (Section 5.2.1)) and that the

 

 

 

 

application may use as it wishes.

 

 

 

 

 

Data that can be set when the Publisher to which the

DDS_GroupDataQosPolicy

group_data

discovered DataReader belongs is created (via the

GROUP_DATA QosPolicy (Section 6.4.4)) and that the

 

 

 

 

application may use as it wishes.

 

 

 

DDS_TypeObject *

type

Describes the type of the remote DataReader.

See the API Reference HTML documentation.

 

 

 

 

 

 

 

Indicates the type-consistency requirements of the

 

 

remote DataReader.

DDS_TypeConsistencyEnforc

type_consistency

See TYPE_CONSISTENCY_ENFORCEMENT

ementQosPolicy

QosPolicy (Section 7.6.6) and the Core Libraries and

 

 

 

Utilities Getting Started Guide Addendum for Extensible

 

 

Types

 

 

 

 

 

Type code information about this Topic. See Using

DDS_TypeCode *

type_code

Generated Types without Connext (Standalone)

 

 

(Section 3.7).

 

 

 

DDS_BuiltinTopicKey_t

subscriber_key

Key of the Subscriber to which the DataReader belongs.

 

 

 

 

 

Properties (pairs of names/values) assigned to the

DDS_PropertyQosPolicy

property

corresponding DataReader. Usage is strictly application-

dependent. See PROPERTY QosPolicy (DDS Extension)

 

 

 

 

(Section 6.5.17).

 

 

 

 

 

If the TransportUnicastQosPolicy is not specified when

 

 

a DataWriter/DataReader is created, the unicast_locators

 

 

in the corresponding Publication/Subscription builtin

DDS_LocatorSeq

unicast_locators

topic data will be empty. When the unicast_locators in

 

 

the Publication/SubscriptionBuiltinTopicData is empty,

 

 

the default_unicast_locators in the corresponding

 

 

Participant Builtin Topic Data is assumed.

 

 

 

DDS_LocatorSeq

multicast_locators

Custom multicast locators that the endpoint can specify.

 

 

 

DDS_ContentFilter-

content_filter_

Provides all the required information to enable content

Property_t

property

filtering on the writer side.

 

 

 

 

 

Virtual GUID for the corresponding DataReader. For

DDS_GUID_t

virtual_guid

more information, see Durability and Persistence Based

 

 

on Virtual GUIDs (Section 12.2).

 

 

 

DDS_ProtocolVersion_t

rtps_protocol_

Version number of the RTPS wire protocol in use.

version

 

 

 

 

 

DDS_VendorId_t

rtps_vendor_id

ID of the vendor implementing the RTPS wire protocol.

 

 

 

DDS_Product_Version_t

product_version

Vendor-specific value. For RTI, this is the current ver-

sion of Connext.

 

 

 

 

 

16-5

Table 16.3 Subscription Built-in Topic’s Data Type (DDS_SubscriptionBuiltinTopicData)

Type

Field

Description

 

 

 

 

 

 

 

disable_positive_

Vendor specific parameter. Determines whether match-

DDS_Boolean

ing DataReaders send positive acknowledgements for

acks

 

reliability.

 

 

 

 

 

 

 

Name and role_name assigned to the DataReader. See

DDS_EntityNameQosPolicy

subscription_name

ENTITY_NAME QosPolicy (DDS Extension) (Section

 

 

6.5.9).

 

 

 

Table 16.4 Topic Built-in Topic’s Data Type (DDS_TopicBuiltinTopicData) (See “Note:” on page 16-2)

 

Type

 

Field

Description

 

 

 

 

 

 

DDS_BuiltinTopicKey_t

key

Key to distinguish the discovered Topic

 

 

 

 

 

 

DDS_String

 

name

Topic name

 

 

 

 

 

 

DDS_String

 

type_name

type name attached to the Topic

 

 

 

 

 

 

DDS_DurabilityQosPolicy

durability

 

 

 

 

 

 

 

DDS_DurabilityServiceQosPolicy

durability_service

 

 

 

 

 

 

 

DDS_DeadlineQosPolicy

deadline

 

 

 

 

 

 

 

DDS_LatencyBudgetQosPolicy

latency_budget

 

 

 

 

 

 

 

DDS_LivelinessQosPolicy

liveliness

 

 

 

 

 

 

 

DDS_ReliabilityQosPolicy

reliability

 

 

 

 

 

QosPolicy of the discovered Topic

 

DDS_TransportPriorityQosPolicy

transport_priority

 

 

 

 

 

 

DDS_LifespanQosPolicy

lifespan

 

 

 

 

 

 

 

DDS_DestinationOrderQosPolicy

destination_

 

 

order

 

 

 

 

 

 

 

DDS_HistoryQosPolicy

history

 

 

 

 

 

 

 

DDS_ResourceLimitsQosPolicy

resource_limits

 

 

 

 

 

 

 

DDS_OwnershipQosPolicy

ownership

 

 

 

 

 

 

 

 

 

 

Data that can be set when the Topic to which the dis-

 

DDS_TopicDataQosPolicy

topic_data

covered DataReader belongs is created (via the

 

TOPIC_DATA QosPolicy (Section 5.2.1)) and that

 

 

 

 

 

 

 

 

the application may use as it wishes.

 

 

 

 

 

Table 16.5 QoS of Built-in Subscriber and DataReader

 

 

 

 

 

 

 

QosPolicy

 

 

Value

 

 

 

 

 

 

 

 

 

Deadline

period = infinite

 

 

 

 

 

DestinationOrder

kind = BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS

 

 

 

 

Durability

kind = TRANSIENT_LOCAL_DURABILITY_QOS

 

 

 

 

EntityFactory

autoenable_created_entities = TRUE

 

 

 

 

 

GroupData

value = empty sequence

 

 

 

 

 

History

kind = KEEP_LAST_HISTORY_QOS

 

depth = 1

 

 

 

 

 

 

 

 

 

 

 

 

LatencyBudget

duration = 0

 

 

 

 

 

 

Liveliness

kind = AUTOMATIC_LIVELINESS_QOS

 

lease_duration = infinite

 

 

 

 

 

 

 

 

Ownership

kind = SHARED_OWNERSHIP_QOS

 

 

 

 

 

 

Ownership Strength

value = 0

 

 

 

 

 

 

 

16-6

Table 16.5 QoS of Built-in Subscriber and DataReader

QosPolicy

Value

 

 

 

 

 

access_scope = TOPIC_PRESENTATION_QOS

Presentation

coherent_access = FALSE

 

ordered_access = FALSE

 

 

Partition

name = empty sequence

 

 

ReaderDataLifecycle

autopurge_nowriter_samples_delay = infinite

 

 

Reliability

kind = RELIABLE_RELIABILITY_QOS

max_blocking_time is irrelevant for the DataReader

 

 

 

 

Depends on setting of DomainParticipantResourceLimitsQosPolicy and Discovery-

 

ConfigQosPolicy in DomainParticipantQos:

 

max_samples = domainParticipantQos.discovery_config.

ResourceLimits

[participant/publication/subscription]_reader_resource_limits.max_samples

 

max_instances = domainParticipantQos.resource_limits.

 

[remote_writer/reader/participant]_allocation.max_count

 

max_samples_per_instance = 1

 

 

TimeBasedFilter

minimum_separation = 0

 

 

TopicData

value = empty sequence

 

 

UserData

value = empty sequence

 

 

and therefore a DataReader for DDS_TopicBuiltinTopicData will not receive any data. Instead, DDS_TopicBuiltinTopicData data is included in the information carried by the built-in topics for Publications and Subscriptions (DDS_PublicationBuiltinTopicData and DDS_SubscriptionBuiltinTopicData) and can be accessed with their built-in DataReaders.

16.2.1LOCATOR_FILTER QoS Policy (DDS Extension)

The LocatorFilter QoS Policy is only applicable to the built-in topic for a Publication (see Table 16.2, “Publication Built-in Topic’s Data Type (DDS_PublicationBuiltinTopicData),” on page 16-3).

Table 16.6 DDS_LocatorFilterQosPolicy

 

Type

Field Name

Description

 

 

 

 

 

 

 

 

 

 

 

A sequence of locator filters, described in Table 16.7 on page 16-7.

 

DDS_LocatorFilterSeq

locator_filters

There is one locator filter per DataWriter channel. If the length of the

 

 

 

sequence is zero, the DataWriter is not using multi-channel.

 

 

 

 

 

 

 

Name of the filter class used to describe the locator filter expressions.

 

char *

filter_name

The following two values are supported:

 

DDS_SQLFILTER_NAME

 

 

 

 

 

 

 

 

DDS_STRINGMATCHFILTER_NAME

 

 

 

 

 

Table 16.7 DDS_LocatorFilter_t

 

 

 

 

 

 

 

 

Type

Field Name

Description

 

 

 

 

 

DDS_LocatorSeq

locators

A sequence of multicast address locators for the locator filter. See

 

Table 16.8 on page 16-8.

 

 

 

 

 

 

 

 

 

 

 

 

A logical expression used to determine if the data will be published

 

 

filter_express

in the channel associated with this

locator filter. See “SQL Filter

 

char *

Expression Notation” on page 5-16

and “STRINGMATCH Filter

 

 

ion

Expression Notation” on page 5-23 for information about the expres-

 

 

 

 

 

 

sion syntax.

 

 

 

 

 

 

16-7

Table 16.8 DDS_Locator_t

Type

Field Name

Description

 

 

 

 

 

 

 

 

If the locator kind is DDS_LOCATOR_KIND_UDPv4a, the address

 

 

contains an IPv4 address. The leading 12 octets of the address must

 

 

be zero. The last 4 octets store the IPv4 address.

DDS_Long

kind

If the locator kind is DDS_LOCATOR_KIND_UDPv6a, the address

 

 

contains an IPv6 address. IPv6 addresses typically use a shorthand

 

 

hexadecimal notation that maps one-to-one to the 16 octets of the

 

 

address.

 

 

 

DDS_Octet[16]

address

The locator address.

 

 

 

DDS_UnsignedLong

port

The locator port number.

 

 

 

a.In C#, the locator kinds for UDPv4 and UDPv6 addresses are Locator_t.LOCATOR_KIND_UDPv4 and Locator_t.LOCATOR_KIND_UDPv6.

16.3Accessing the Built-in Subscriber

Getting the built-in subscriber allows you to retrieve the built-in readers of the built-in topics through the Subscriber’s lookup_datareader() operation. By accessing the built-in reader, you can access discovery information about remote entities.

// Lookup built-in reader DDSDataReader *builtin_reader =

builtin_subscriber->lookup_datareader(DDS_PUBLICATION_TOPIC_NAME); if (builtin_reader == NULL) {

// ... error

}

// Register listener to built-in reader MyPublicationBuiltinTopicDataListener builtin_reader_listener =

new MyPublicationBuiltinTopicDataListener(); if (builtin_reader->set_listener(builtin_reader_listener,

DDS_DATA_AVAILABLE_STATUS) != DDS_RETCODE_OK) {

// ... error

}

// enable domain participant

if (participant->enable() != DDS_RETCODE_OK) { // ... error

}

For example, you can call the DomainParticipant’s get_builtin_subscriber() operation, which will provide you with a built-in Subscriber. Then you can use that built-in Subscriber to call the Subscriber’s lookup_datareader() operation; this will retrieve the built-in reader. Another option is to register a Listener on the built-in subscriber instead, or poll for the status of the built-in subscriber to see if any of the built-in data readers have received data.

16.4Restricting Communication—Ignoring Entities

The ignore_participant() operation allows an application to ignore all communication from a specific DomainParticipant. Or for even finer control you can use the ignore_publication(), ignore_subscription(), and ignore_topic() operations. These operations are described below.

DDS_ReturnCode_t ignore_participant (const DDS_InstanceHandle_t &handle)

DDS_ReturnCode_t ignore_publication (const DDS_InstanceHandle_t &handle)

16-8

DDS_ReturnCode_t ignore_subscription (const DDS_InstanceHandle_t &handle) DDS_ReturnCode_t ignore_topic (const DDS_InstanceHandle_t &handle)

The entity to ignore is identified by the handle argument. It may be a local or remote entity. For ignore_publication(), the handle will be that of a local DataWriter or a discovered remote DataWriter. For ignore_subscription(), that handle will be that of a local DataReader or a discovered remote DataReader.

The safest approach for ignoring an entity is to call the ignore operation within the Listener callback of the built-in reader, or before any local entities are enabled. This will guarantee that the local entities (entities that are created by the local DomainParticipant) will never have a chance to establish communication with the remote entities (entities that are created by another DomainParticipant) that are going to be ignored.

If the above is not possible and a remote entity is to be ignored after the communication channel has been established, the remote entity will still be removed from the database of the local application as if it never existed. However, since the remote application is not aware that the entity is being ignored, it may potentially be expecting to receive messages or continuing to send messages. Depending on the QoS of the remote entity, this may affect the behavior of the remote application and may potentially stop the remote application from communicating with other entities.

You can use this operation in conjunction with the ParticipantBuiltinTopicData to implement access control. You can pass application data associated with a DomainParticipant in the USER_DATA QosPolicy (Section 6.5.25). This application data is propagated as a field in the built-in topic. Your application can use the data to implement an access control policy.

Ignore operations, in conjunction with the Built-in Topic Data, can be used to implement access control. You can pass data associated with an entity in the USER_DATA QosPolicy (Section 6.5.25), GROUP_DATA QosPolicy (Section 6.4.4) or TOPIC_DATA QosPolicy (Section 5.2.1). This data is propagated as a field in the built-in topic. When data for a built-in topic is received, the application can check the user_data, group_data or topic_data field of the remote entity, determine if it meets the security requirement, and ignore the remote entity if necessary.

See also: Chapter 14: Discovery.

16.4.1Ignoring Specific Remote DomainParticipants

The ignore_participant() operation is used to instruct Connext to locally ignore a remote

DomainParticipant. It causes Connext to locally behave as if the remote DomainParticipant does not exist.

DDS_ReturnCode_t ignore_participant (const DDS_InstanceHandle_t & handle)

After invoking this operation, Connext will locally ignore any Topic, publication, or subscription that originates on that DomainParticipant. (If you only want to ignore specific publications or subscriptions, see Section 16.4.2 instead.) Figure 16.1, “Ignoring Participants,” on page 16-10 provides an example.

Caution: There is no way to reverse this operation. You can add to the peer list, however—see Section 8.5.2.3.

16.4.2Ignoring Publications and Subscriptions

You can instruct Connext to locally ignore a publication or subscription. A publication/ subscription is defined by the association of a Topic name, user data and partition set on the Publisher/Subscriber. After this call, any data written related to associated DataWriter/DataReader will be ignored.

The entity to ignore is identified by the handle argument. For ignore_publication(), the handle will be that of a DataWriter. For ignore_subscription(), that handle will be that of a DataReader.

16-9

Figure 16.1 Ignoring Participants

class MyParticipantBuiltinTopicDataListener : public DDSDataReaderListener {

public:

virtual void on_data_available(DDSDataReader *reader); // ......

};

void MyParticipantBuiltinTopicdataListener::on_data_available( DDSDataReader *reader) {

DDSParticipantBuiltinTopicDataDataReader *builtinTopicDataReader = DDSParticipantBuiltinTopicDataDataReader *) reader;

DDS_ParticipantBuiltinTopicDataSeq data_seq; DDS_SampleInfoSeq info_seq;

int = 0;

if (builtinTopicDataReader->take(data_seq, info_seq, DDS_LENGTH_UNLIMITED, DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE) != DDS_RETCODE_OK){ // ... error

}

for (i = 0; i < data_seq.length(); ++i) { if (info_seq[i].valid_data) {

// check user_data for access control if (data_seq[i].user_data[0] != 0x9) {

if ( builtinTopicDataReader->get_subscriber()->get_participant()

->ignore_participant(info_seq[i].instance_handle) != DDS_RETCODE_OK) {

// ... error

}

}

}

}

if (builtinTopicDataReader->return_loan(data_seq, info_seq) != DDS_RETCODE_OK) {

// ... error

}

}

This operation can be used to ignore local and remote entities:

For local entities, you can obtain the handle argument by calling the get_instance_handle() operation for that particular entity.

For remote entities, you can obtain the handle argument from the DDS_SampleInfo structure retrieved when reading data samples available for the entity’s built-in

DataReader.

DDS_ReturnCode_t ignore_publication (const DDS_InstanceHandle_t & handle) DDS_ReturnCode_t ignore_subscription (const DDS_InstanceHandle_t & handle)

Caution: There is no way to reverse these operations.

Figure 16.2, “Ignoring Publications,” on page 16-11 provides an example.

16.4.3Ignoring Topics

The ignore_topic() operation instructs Connext to locally ignore a Topic. This means it will locally ignore any publication or subscription to the Topic.

DDS_ReturnCode_t ignore_topic (const DDS_InstanceHandle_t & handle)

Caution: There is no way to reverse this operation.

16-10

Figure 16.2 Ignoring Publications

class MyPublicationBuiltinTopicDataListener : public DDSDataReaderL- istener {

public:

virtual void on_data_available(DDSDataReader *reader); // ......

};

void MyPublicationBuiltinTopicdataListener::on_data_available( DDSDataReader *reader) {

DDSPublicationBuiltinTopicDataReader *builtinTopicDataReader = (DDS_PublicationBuiltinTopicDataReader *)reader;

DDS_PublicationBuiltinTopicDataSeq data_seq; DDS_SampleInfoSeq info_seq;

int = 0;

if (builtinTopicDataReader->take(data_seq, info_seq, DDS_LENGTH_UNLIMITED, DDS_ANY_SAMPLE_STATE, DDS_ANY_VIEW_STATE, DDS_ANY_INSTANCE_STATE)

!= DDS_RETCODE_OK) { // ... error

}

for (i = 0; i < data_seq.length(); ++i) { if (info_seq[i].valid_data) {

// check user_data for access control if (data_seq[i].user_data[0] != 0x9) {

if (builtinTopicDataReader->get_subscriber() ->get_participant()

->ignore_publication(info_seq[i].instance_handle)

!= DDS_RETCODE_OK) { // ... error

}

}

}

}

if (builtinTopicDataReader->return_loan(data_seq, info_seq) != DDS_RETCODE_OK) {

// ... error

}

}

If you know that your application will never publish or subscribe to data under certain topics, you can use this operation to save local resources.

The Topic to ignore is identified by the handle argument. This handle is the one that appears in the DDS_SampleInfo retrieved when reading the data samples from the built-in DataReader to the Topic.

16-11

Chapter 17 Configuring QoS with XML

Connext entities are configured by means of QosPolicies. The QoS may be set programmatically in one of the following ways:

Directly when the entity is created as an additional argument to the create_<entity>() operation.

Directly via the set_qos() operation on the entity.

Indirectly as a default QoS on the factory for the entity (set_default_<entity>_qos() operations on Publisher, Subscriber, DomainParticipant, DomainParticipantFactory)

Entities can also be configured from an XML file or XML string. With this feature, you can change QoS configurations simply by changing the XML file or string—you do not have to recompile the application. This chapter describes how to configure Connext entities using XML:

Example XML File (Section 17.1)

How to Load XML-Specified QoS Settings (Section 17.2)

How to Use XML-Specified QoS Settings (Section 17.3)

XML File Syntax (Section 17.4)

Using Environment Variables in XML (Section 17.5)

XML String Syntax (Section 17.6)

How the XML is Validated (Section 17.7)

Configuring QoS with XML (Section 17.8)

QoS Profiles (Section 17.9)

QoS Libraries (Section 17.10)

URL Groups (Section 17.11)

Configuring Logging Via XML (Section 17.12)

17-1

17.1Example XML File

The QoS configuration of a Entity can be loaded from an XML file or string. Let's look at a very basic configuration file, just to get an idea of its contents. You will learn the meaning of each line as you read the rest of this chapter:

<?xml version="1.0" encoding="ISO-8859-1"?> <!-- A XML configuration file -->

<dds version = 5.0.0> <qos_library name="RTILibrary">

<!--Individual QoS are shortcuts for QoS Profiles with 1 QoS-> <datawriter_qos name="KeepAllWriter">

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history> </datawriter_qos>

<!-- A Qos Profile is a set of related QoS --> <qos_profile name="StrictReliableCommunicationProfile">

<datawriter_qos> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datawriter_qos> <datareader_qos>

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datareader_qos>

</qos_profile> </qos_library>

</dds>

See $NDDSHOME/resource/qos_profiles_5.0.x1/xml/NDDS_QOS_PROFILES.example.xml for another example; this file contains the default QoS values for all entity kinds.

17.2How to Load XML-Specified QoS Settings

If specified, XML-specified QoS settings are automatically loaded by the DomainParticipantFactory. There are several ways to load XML QoS profiles into your application.

The following list presents the various approaches, listed by load order:

$NDDSHOME/resource/qos_profiles_5.0.x/xml/NDDS_QOS_PROFILES.xml

This file is loaded automatically if it exists (not the default) and ignore_resource_profile in the PROFILE QosPolicy (DDS Extension) (Section 8.4.2) is FALSE (the default). NDDS_QOS_PROFILES.xml does not exist by default. However, NDDS_QOS_PROFILES.example.xml is shipped with the host bundle of

1.x stands for the version number of the current release.

17-2

the product; you can copy it to NDDS_QOS_PROFILES.xml and modify it for your own use. The file contains the default QoS values that will be used for all entity kinds. (First to be loaded)

URL Groups in NDDS_QOS_PROFILES

URL groups (see URL Groups (Section 17.11)) separated by semicolons referenced by the environment variable NDDS_QOS_PROFILES are loaded automatically if they exist and ignore_environment_profile in PROFILE QosPolicy (DDS Extension) (Section 8.4.2) is FALSE (the default).

<working directory>/USER_QOS_PROFILES.xml

This file is loaded automatically if it exists and ignore_user_profile in PROFILE QosPolicy (DDS Extension) (Section 8.4.2) is FALSE (the default).

URL groups in url_profile

URL groups (see URL Groups (Section 17.11)) referenced by url_profile (in PROFILE QosPolicy (DDS Extension) (Section 8.4.2)) will be loaded automatically if specified.

XML strings in string_profile

The sequence of XML strings referenced by string_profile (in PROFILE QosPolicy (DDS Extension) (Section 8.4.2)) will be loaded automatically if specified. (Last to be loaded)

You may use a combination of the above approaches.

The location of the XML documents (only files and strings are supported) is specified using URL (Uniform Resource Locator) format. For example:

File Specification: file:///usr/local/default_dds.xml

String Specification: str://"<dds><qos_library>…</qos_library></dds>"

If you omit the URL schema name, Connext will assume a file name. For example:

File Specification: /usr/local/default_dds.xml

Duplicate QoS profiles are not allowed. Connext will report an error message in these scenarios. To overwrite a QoS profile, use QoS Profile Inheritance (Section 17.9.2).

17.2.1Loading, Reloading and Unloading Profiles

You do not have to explicitly call load_profiles(). QoS profiles are loaded when any of these DomainParticipantFactory operations are called:

create_participant() (see Section 8.3.1)

create_participant_with_profile() (see Section 8.3.1)

get_<entity>_qos_from_profile() (where <entity> is participant, topic, publisher, subscriber, datawriter, or datareader) (see Section 8.2.5)

get_<entity>_qos_from_profile_w_topic_name() (where <entity> is topic, datawriter, or datareader) (see Section 8.2.5)

get_default_participant_qos() (see Section 8.2.2)

get_qos_profile_libraries() (See Section 17.10.1)

get_qos_profiles() (See Section 17.9.5)

load_profiles()

set_default_participant_qos_with_profile() (see Section 8.2.2)

set_default_library() (see Section 6.2.4.3)

set_default_profile() (see Section 6.2.4.3)

QoS profiles are reloaded when either of these DomainParticipantFactory operations are called:

17-3

reload_profiles()

set_qos() (see Section 4.1.7)

It is important to distinguish between loading and reloading:

Loading only happens when there are no previously loaded profiles. This could be when the profiles are loaded the first time or after a call to unload_profiles().

Reloading replaces all previously loaded profiles. Reloading a profile does not change the QoS of entities that have already been created with previously loaded profiles.

The DomainParticipantFactory also has an unload_profiles() operation that frees the resources associated with the XML QoS profiles.

DDS_ReturnCode_t unload_profiles()

17.3How to Use XML-Specified QoS Settings

You can use the operations listed in Table 17.1 to refer and use QoS profiles (see Section 17.9) described in XML files and XML strings.

Table 17.1 Operations for Working with QoS Profiles

Working With ...

Profile-Related Operations

Reference

 

 

 

 

 

 

DataReaders

set_qos_with_profile

Section 7.3.8.2

 

 

 

DataWriters

set_qos_with_profile

Section 6.3.15.2

 

 

 

 

create_datareader_with_profile

Section 7.3.1

 

 

 

 

create_datawriter_with_profile

Section 6.3.1

 

 

 

 

create_publisher_with_profile

Section 6.2.2

 

 

 

 

create_subscriber_with_profile

Section 7.2.2

 

 

 

 

create_topic_with_profile

Section 5.1.1

 

 

 

 

get_default_library

 

 

 

 

 

get_default_profile

Section 8.3.6.3

 

 

 

DomainParticipants

get_default_profile_library

 

 

 

set_default_datareader_qos_with_profile

Section 8.3.6.4

 

 

 

 

set_default_datawriter_qos_with_profile

 

 

 

 

 

 

set_default_library

Section 8.3.6.3

 

 

 

set_default_profile

 

 

 

 

 

 

set_default_publisher_qos_with_profile

 

 

 

 

 

set_default_subscriber_qos_with_profile

Section 8.3.6.4

 

 

 

 

set_default_topic_qos_with_profile

 

 

 

 

 

set_qos_with_profile

Section 8.3.6.2

 

 

 

17-4

Table 17.1 Operations for Working with QoS Profiles

Working With ...

Profile-Related Operations

Reference

 

 

 

 

create_participant_with_profile

Section 8.3.1

 

 

 

 

get_datareader_qos_from_profile

 

 

 

 

 

get_datawriter_qos_from_profile

Section 8.2.5

 

 

 

get_datawriter_qos_from_profile_w_topic_name

 

 

 

 

 

 

get_datareader_qos_from_profile_w_topic_name

 

 

 

 

 

get_default_library

 

 

 

 

 

get_default_profile

Section 8.2.1.1

 

 

 

 

get_default_profile_library

 

 

 

 

 

get_participant_qos_from_profile

 

 

 

 

 

get_publisher_qos_from_profile

 

DomainParticipantFactory

 

 

get_subscriber_qos_from_profile

Section 8.2.5

 

 

 

 

get_topic_qos_from_profile

 

 

 

 

 

get_topic_qos_from_profile_w_topic_name

 

 

 

 

 

get_qos_profiles

Section 17.9.5

 

 

 

 

get_qos_profile_libraries

Section 17.10.1

 

 

 

 

load_profiles

Section 17.2.1

 

 

 

reload_profiles

 

 

 

 

 

 

set_default_participant_qos_with_profile

Section 8.2.2

 

 

 

 

set_default_library

Section 8.2.1.1

 

 

 

set_default_profile

 

 

 

 

 

 

unload_profiles

Section 17.2.1

 

 

 

 

create_datawriter_with_profile

Section 6.2.2

 

 

 

 

get_default_library

 

 

 

 

 

get_default_profile

Section 6.2.4.3

 

 

 

Publishers

get_default_profile_library

 

 

 

set_default_datawriter_qos_with_profile

Section 6.2.4.4

 

 

 

 

 

set_default_library

Section 6.2.4.3

 

 

 

set_default_profile

 

 

 

 

 

 

set_qos_with_profile

Section 6.2.4.2

 

 

 

 

create_datareader_with_profile

Section 7.3.1

 

 

 

 

get_default_library

 

 

 

 

 

get_default_profile

Section 7.2.4.3

 

 

 

Subscribers

get_default_profile_library

 

 

 

set_default_datawriter_qos_with_profile

Section 7.2.4.4

 

 

 

 

 

set_default_library

Section 7.2.4.3

 

 

 

set_default_profile

 

 

 

 

 

 

set_qos_with_profile

Section 7.2.4.2

 

 

 

Topics

set_qos_with_profile

Section 5.1.3

 

 

 

17.4XML File Syntax

The XML configuration file must follow these syntax rules:

17-5

The syntax is XML and the character encoding is UTF-8.

Opening tags are enclosed in <>; closing tags are enclosed in </>.

A tag value is a UTF-8 encoded string. Legal values are alphanumeric characters. The middleware’s parser will remove all leading and trailing spaces1 from the string before it is processed.

For example, " <tag> value </tag>" is the same as "<tag>value</tag>".

All values are case-sensitive unless otherwise stated.

Comments are enclosed as follows: <!-- comment -->.

The root tag of the configuration file must be <dds> and end with </dds>.

The primitive types for tag values are specified in Table 17.2.

Table 17.2 Supported Tag Values

Type

 

 

 

Format

 

Notes

 

 

 

 

 

 

 

 

yesa,

1,

true,

BOOLEAN_TRUEa

or

 

DDS_Boolean

DDS_BOOLEAN_TRUEa: these all mean TRUE

 

Not case-sensitive

noa,

0,

false,

BOOLEAN_FALSEa

or

 

 

 

DDS_BOOLEAN_FALSEa: these all mean FALSE

 

 

A string. Legal values are those listed in the API

Must be specified as a string.

DDS_Enum

Reference HTML documentation for the Ca

or

(Do not use numeric values.)

 

Java API.

 

 

 

 

 

-2147483648 to 2147483647

 

 

DDS_Long

or 0x80000000 to 0x7fffffffa

 

A 32-bit signed integer

or LENGTH_UNLIMITED

 

 

 

 

 

or DDS_LENGTH_UNLIMITEDa

 

 

 

0 to 4294967296

 

 

 

DDS_UnsignedLong

or

 

 

 

 

A 32-bit unsigned integer

 

0 to 0xffffffffa

 

 

 

 

String

UTF-8 character string

 

All leading and trailing spaces

 

are ignored between two tags

 

 

 

 

 

 

 

 

 

 

 

 

 

a.These values will not be considered valid if you use the distributed XSD document to do validation at run-time with a code editor (see Section 17.7).

17.5Using Environment Variables in XML

The text within an XML tag can refer to environment variable. To do so, use the following notation:

$(MY_VARIABLE)

For example:

<element>

<name>The name is $(MY_NAME)</name> <value>The value is $(MY_VALUE)</value>

</element>

When the Connext XML parser parses the previous tags it will replace the references to environment variables with their actual values.

1. Leading and trailing spaces in enumeration fields will not be considered valid if you use the distributed XSD document to do validation at run-time with a code editor (see Section 17.7).

17-6

17.6XML String Syntax

XML profiles can be described using strings. This configuration is useful for architectures without a file system.

There are two different ways to configure Entities via XML strings:

String URLs are prefixed by the URI schema str:// and enclosed in double quotes. For example:

str://"<dds><qos_library>...</qos_library></dds>"

The string URLs can be specified in the environment variable NDDS_QOS_PROFILES as well as in the field url_profile in PROFILE QosPolicy (DDS Extension) (Section 8.4.2). Each string URL must contain a whole XML document.

The string_profile field in the PROFILE QosPolicy (DDS Extension) (Section 8.4.2) allows you to split an XML document into multiple strings. For example:

const char * MyXML[4] =

{

"<dds>",

"<qos_library name=\"MyLibrary\">", "</qos_library>",

"</dds>"

}; factoryQos.profile.string_profile.from_array(MyXML,4);

Only one XML document can be specified with the string_profile field.

17.7How the XML is Validated

17.7.1Validation at Run-Time

Connext validates the input XML files using a builtin Document Type Definition (DTD).

You can find a copy of the builtin DTD in $(NDDSHOME)/resource/qos_profiles_5.0.x1/ schema/rti_dds_qos_profiles.dtd. (This is only a copy of what the Connext core uses. Changing this file has no effect unless you specify its path with the <!DOCTYPE> tag, described below.)

You can overwrite the builtin DTD by using the XML tag, <!DOCTYPE>. For example, the following indicates that Connext must use a DTD file from a user’s directory to perform validation:

<!DOCTYPE dds SYSTEM "/local/joe/rti/dds/mydds.dtd">

The DTD path can be absolute, or relative to the application's current working directory.

If the specified file does not exist, you will see the following error:

RTIXMLDtdParser_parse:!open DTD file

If you do not specify the DOCTYPE tag in the XML file, the builtin DTD is used.

The XML files used by Connext can be versioned using the attribute version in the <dds> tag. For example:

<dds version="5.0.x">

...

1.x stands for the version number of the current release.

17-7

</dds>

Although the attribute version is not required during the validation process, it helps to detect DTD incompatibility scenarios by providing better error messages.

For example, if an application using Connext 5.0.x tries to load an XML file from Connext 4.5z and there is some incompatibility in the XML content, the following parsing error will be printed:

ATTENTION: The version declared in this file (4.5z) is different from the version of Connext (5.0.x). If these versions are not compatible, that incompatibility could be the cause of this error.

17.7.2XML File Validation During Editing

Connext provides DTD and XSD files that describe the format of the XML content. We recommend including a reference to one of these documents in the XML file that contains the QoS profiles—this provides helpful features in code editors such as Visual Studio and Eclipse, including validation and auto-completion while you are editing the XML file.

The DTD and XSD definitions of the XML elements are in $(NDDSHOME)/resource/qos_profiles_5.0.x/schema/rti_dds_qos_profiles.dtd and $(NDDSHOME)/resource/qos_profiles_5.0.x/schema/rti_dds_qos_profiles.xsd, respectively (in ‘5.0.x’, x stands for the version number of the current release).

To include a reference to the XSD document in your XML file, use the attribute xsi:noNamespaceSchemaLocation in the <dds> tag. For example:

<?xml version="1.0" encoding="UTF-8"?>

<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation=

"<NDDSHOME1>/resource/qos_profiles_5.0.x/schema/rti_dds_qos_profiles.xsd">

...

</dds>

To include a reference to the DTD document in your XML file use the <!DOCTYPE> tag. For example:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dds SYSTEM

"<NDDSHOME1>/resource/qos_profiles_5.0.x/schema/rti_dds_qos_profiles.dtd"> <dds>

...

</dds>

We recommend including a reference to the XSD file in the XML documents because it provides stricter validation and better auto-completion than the corresponding DTD file.

17.8Configuring QoS with XML

To configure the QoS for an Entity using XML, use the following tags:

<participant_factory_qos>

Note: The only QoS policies that can be configured for the DomainParticipantFactory are <entity_factory> and <logging>.

<participant_qos>

<publisher_qos>

1.Replace <NDDSHOME> with the installation directory of Connext.

17-8

<subscriber_qos>

<topic_qos>

<datawriter_qos> or <writer_qos> (writer_qos is valid only with DTD validation)

<datareader_qos> or <reader_qos> (reader_qos is valid only with DTD validation)

Each QoS can be identified by a name. The QoS can inherit its values from other QoSs described in the XML file. For example:

<datawriter_qos name="DerivedWriterQos" base_name="Lib::BaseWriterQos"> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

</datawriter_qos>

In the above example, the datawriter_qos named 'DerivedWriterQos' inherits the values from 'BaseWriterQos' in the library 'Lib'. The HistoryQosPolicy kind is set to KEEP_ALL_HISTORY_QOS.

Each XML tag with an associated name can be uniquely identified by its fully qualified name in C++ style.

The writer, reader and topic QoSs can also contain an attribute called topic_filter that will be used to associate a set of topics to a specific QoS when that QoS is part of a QoS profile. See Topic Filters (Section 17.9.3) and QoS Profiles (Section 17.9).

17.8.1QosPolicies

The fields in a QosPolicy are described in XML using a 1-to-1 mapping with the equivalent C representation. For example, the Reliability QosPolicy is represented with the following C structures:

struct DDS_Duration_t { DDS_Long sec; DDS_UnsignedLong nanosec;

}

struct DDS_ReliabilityQosPolicy { DDS_ReliabilityQosPolicyKind kind; DDS_Duration_t max_blocking_time;

}

The equivalent representation in XML is as follows:

<reliability>

<kind></kind> <max_blocking_time>

<sec></sec>

<nanosec></nanosec> </max_blocking_time>

</reliability>

17.8.2Sequences

In general, sequences in QosPolicies are described with the following XML format:

<a_sequence_member_name> <element>...</element> <element>...</element>

...

</a_sequence_member_name>

Each element of the sequence is enclosed in an <element> tag. For example:

<property>

17-9

<value>

<element>

<name>my name</name> <value>my value</value>

</element>

<element>

<name>my name2</name> <value>my value2</value>

</element>

</value>

</property>

A sequence without elements represents a sequence of length 0. For example:

<discovery>

<!-- initial_peers sequence contains zero elements --> <initial_peers/>

</discovery>

For sequences that may have a default initialization that is not empty (such as the initial_peers field in the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2)), using the above construct would result in an empty list and not the default value. So to simply show a sequence for the sake of completeness, but not change its default value, comment it out, as follows:

<discovery>

<!-- initial_peers sequence contains the default value --> <!-- <initial_peers/> -->

</discovery>

As a general rule, sequences defined in a derived1 QoS will replace the corresponding sequences in the base QoS. For example, consider the following:

<qos_profile name="MyBaseProfile">

<participant_qos> <discovery>

<initial_peers>

<element>192.168.1.1</element>

<element>192.168.1.2</element>

</initial_peers> </discovery>

</participant> </qos_profile>

<qos_profile name="MyDerivedProfile" base_name="MyBaseProfile">

<participant_qos> <discovery>

<initial_peers>

<element>192.168.1.3</element>

</initial_peers> </discovery>

</participant> </qos_profile>

The initial peers sequence defined above in the participant QoS of MyDerivedProfile will contain a single element with a value 192.168.1.3. The elements 192.168.1.1 and 192.168.1.2 will not be inherited. However, there is one exception to this behavior. The <property> tag provides an attribute called inherit that allows you to choose the inheritance behavior for the sequence defined within the tag.

The <property> tag provides an attribute called inherit that allows you to choose the inheritance behavior for the sequence defined within the tag.

1. The concepts of derived and base QoS are described in QoS Profile Inheritance (Section 17.9.2).

17-10

By default, the value of the attribute inherit is true. Therefore, the <property> tag defined within a derived QoS profile will inherit its elements from the <property> tag defined within a base QoS profile.

In the following example, the property sequence defined in the participant QoS of MyDerivedProfile will contain two properties:

dds.transport.UDPv4.builtin.send_socket_buffer_size will be inherited from the base profile and have the value 524288.

dds.transport.UDPv4.builtin.recv_socket_buffer_size will overwrite the value defined in the base QoS profile with 1048576.

<qos_profile name="MyBaseProfile">

<participant_qos>

<property>

<value>

<element>

<name>dds.transport.UDPv4.builtin.send_socket_buffer_size</name> <value>524288</value>

</element>

<element>

<name>dds.transport.UDPv4.builtin.recv_socket_buffer_size</name> <value>2097152</value>

</element>

</value>

</discovery>

</property> </qos_profile>

<qos_profile name="MyDerivedProfile" base_name="MyBaseProfile">

<participant_qos>

<property>

<value>

<element>

<name>dds.transport.UDPv4.builtin.recv_socket_buffer_size</name> <value>1048576</value>

</element>

</value>

</discovery>

</property> </qos_profile>

To discard all the properties defined in the base QoS profile, set inherit to false.

In the following example, the property sequence defined in the participant QoS of MyDerivedProfile will contain a single property named dds.transport.UDPv4.builtin.recv_socket_buffer_size, with a value of 1048576. The property dds.transport.UDPv4.builtin.send_socket_buffer_size will not be inherited.

<qos_profile name="MyBaseProfile">

<participant_qos> <property>

<value>

<element>

<name>dds.transport.UDPv4.builtin.send_socket_buffer_size</name> <value>524288</value>

</element>

<element>

<name>dds.transport.UDPv4.builtin.recv_socket_buffer_size</name> <value>2097152</value>

</element>

</value>

</discovery>

17-11

</property> </qos_profile>

<qos_profile name="MyDerivedProfile" base_name="MyBaseProfile">

<participant_qos>

<property inherit="false">

<value>

<element>

<name>dds.transport.UDPv4.builtin.recv_socket_buffer_size</name> <value>1048576</value>

</element>

</value>

</discovery>

</property> </qos_profile>

17.8.3Arrays

In general, the arrays contained in the QosPolicies are described with the following XML format:

<an_array_member_name> <element>...</element> <element>...</element>

...

</an_array_member_name>

Each element of the array is enclosed in an <element> tag.

As a special case, arrays of octets are represented with a single XML tag enclosing an array of decimal/hexadecimal values between 0..255 separated with commas.

For example:

<reader_qos>

...

<protocol> <virtual_guid>

<value>1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16</value> </virtual_guid>

</protocol> </reader_qos>

17.8.4Enumeration Values

Enumeration values are represented using their C or Java string representation. For example:

<history> <kind>DDS_KEEP_ALL_HISTORY_QOS</kind>

</history>

or

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

When the XSD document is used for validation during editing (see Section 17.7.2), only the Java representation is valid.

17.8.5Time Values (Durations)

You can use the following special values for fields that require seconds or nanoseconds:

DURATION_INFINITE_SEC or DDS_DURATION_INFINITE_SEC,

17-12

DURATION_ZERO_SEC or DDS_DURATION_ZERO_SEC,

DURATION_INFINITE_NSEC or DDS_DURATION_INFINITE_NSEC

DURATION_ZERO_NSEC or DDS_DURATION_ZERO_NSEC

For example:

<deadline>

<period> <sec>DURATION_INFINITE_SEC</sec>

<nanosec>DURATION_INFINITE_NSEC</nanosec> </period>

</deadline>

When the XSD document is used for validation during editing (see Section 17.7.2), only the values without the DDS prefix are considered valid.

17.8.6Transport Properties

You can configure transport plugins using the DomainParticipant’s PROPERTY QosPolicy (DDS Extension) (Section 6.5.17).

Properties for the builtin transports are described in Setting Builtin Transport Properties with the PropertyQosPolicy (Section 15.6).

Properties for other transport plugins such as RTI TCP Transport1are described in their respective chapters in this manual.

For example:

<participant_qos> <property> <value>

<element>

<name>dds.transport.UDPv4.builtin.parent.message_size_max

</name>

<value>65536</value>

</element>

<element> <name>dds.transport.UDPv4.builtin.send_socket_buffer_size </name>

<value>131072</value>

</element>

<element> <name>dds.transport.UDPv4.builtin.recv_socket_buffer_size </name>

<value>262144</value>

</element>

</value>

</property> </participant_qos>

17.8.7Thread Settings

See Table 19.1, “XML Tags for ThreadSettings_t,” on page 19-5.

1. RTI TCP Transport is included with Connext, but is not enabled by default.

17-13

17.9QoS Profiles

A QoS profile groups a set of related QoS, usually one per entity, identified by a name. For example:

<qos_profile name="StrictReliableCommunicationProfile"> <datawriter_qos>

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datawriter_qos>

<datareader_qos> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datareader_qos>

</qos_profile>

Duplicate QoS profiles are not allowed. To overwrite a QoS profile, use QoS Profile Inheritance (Section 17.9.2).

There are functions that allow you to create Entities using profiles, such as create_participant_with_profile() (Section 8.3.1), create_topic_with_profile() (Section 5.1.1), etc.

If you create an entity using a profile without a QoS definition or an inherited QoS definition (see QoS Profile Inheritance (Section 17.9.2)) for that class of entity, Connext uses the default QoS.

Example 1:

<qos_profile name="BatchStrictReliableCommunicationProfile" base_name="StrictReliableCommunicationProfile"> <datawriter_qos>

<batch>

<enable>true</enable>

</batch> </datawriter_qos>

</qos_profile>

The DataReader QoS value in the profile BatchStrictReliableCommunicationProfile is inherited from the profile StrictReliableCommunicationProfile.

Example 2:

<qos_profile name="BatchProfile"> <datawriter_qos>

<batch>

<enable>true</enable>

</batch> </datawriter_qos>

</qos_profile>

The DataReader QoS value in the profile BatchProfile is the default Connext QoS.

17-14

17.9.1QoS Profiles with a Single QoS

The definition of an individual QoS outside a profile is a shortcut for defining a QoS profile with a single QoS. For example:

<datawriter_qos name="KeepAllWriter"> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

</datawriter_qos>

is equivalent to:

<qos_profile name="KeepAllWriter"> <datawriter_qos>

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history> </datawriter_qos>

</qos_profile>

17.9.2QoS Profile Inheritance

An individual QoS or profile can inherit values from other QoSs or profiles described in the XML file by using the attribute, base_name.

Inheriting from other XML Files: A QoS or QoS Profile may inherit values from other QoSs or QoS Profiles described in different XML files. A QoS or profile can only inherit from other QoS policies or profiles that have already been loaded. The order in which XML resources are loaded is described in Section 17.2.

The following examples show how to inherit from other profiles:

Example 1:

<qos_library name=”Library”> <qos_profile name="BaseProfile">

<datawriter_qos>

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

<qos_profile name="DerivedProfile" base_name="BaseProfile"> <datawriter_qos>

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

</qos_library>

The writer_qos and reader_qos in DerivedProfile inherit their values from the corresponding QoS in BaseProfile.

Example 2:

<qos_library name=”Library”> <datareader_qos name="BaseProfile">

...

</datareader_qos>

<datareader_qos name="DerivedProfile" base_name="BaseProfile">

17-15

...

</datareader_qos> </qos_library>

The datareader_qos in DerivedProfile inherits its values from the datareader_qos of BaseProfile. In this example, the datareader_qos definition is a shortcut for a profile definition with a single QoS (see Section 17.9.1).

Example 3:

<qos_library name=”Library”> <qos_profile name="Profile1">

<datawriter_qos name="BaseWriterQoS">

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

<qos_profile name="Profile2">

<datawriter_qos name="DerivedWriterQos" base_name="Profile1::BaseWriterQos">

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

</qos_library>

The datawriter_qos in Profile2 inherits its values from the datawriter_qos in Profile1. The datareader_qos in Profile2 will not inherit the values from the corresponding QoS in Profile1.

Example 4:

<qos_library name=”Library”>

<qos_profile name="Profile1">

<datawriter_qos>

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

<qos_profile name="Profile2"> <datawriter_qos name="BaseWriterQoS">

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

<qos_profile name="Profile3" base_name="Profile1">

<datawriter_qos name="DerivedWriterQos" base_name="Profile2::BaseWriterQos">

...

</datawriter_qos> <datareader_qos>

...

</datareader_qos> </qos_profile>

17-16

</qos_library></qos_library>

The datawriter_qos in Profile3 inherits its values from the datawriter_qos in Profile2. The datareader_qos in Profile3 inherits its values from the datareader_qos in Profile1.

Example 5:

<qos_library name=”Library”> <datareader_qos name="BaseProfile">

...

</datareader_qos>

<profile name="DerivedProfile" base_name="BaseProfile"> <datareader_qos>

...

</datareader_qos>

</profile> </qos_library>

The datareader_qos in DerivedProfile inherits its values from the datareader_qos in BaseProfile.

17.9.3Topic Filters

A QoS profile may contain several writer, reader and topic QoSs. Connext will select a QoS based on the evaluation of a filter expression on the topic name. The filter expression is specified as an attribute in the XML QoS definition. For example:

<qos_profile name="StrictReliableCommunicationProfile">

<datawriter_qos topic_filter="A*">

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datawriter_qos>

<datawriter_qos topic_filter="B*">

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> <resource_limits>

<max_samples>128</max_samples> <max_samples_per_instance>128 </max_samples_per_instance> <initial_samples>128</initial_samples> <max_instances>1</max_instances> <initial_instances>1</initial_instances>

</resource_limits> </datawriter_qos>

</qos_profile>

If topic_filter is not specified in a QoS, Connext will assume the filter '*'. The QoSs with an explicit topic_filter attribute definition will be evaluated in order; they have precedence over a QoS without a topic_filter expression.

The topic_filter attribute is only used with the following APIs:

DomainParticipantFactory:

17-17

get_<entity>_qos_from_profile_w_topic_name() (where <entity> may be topic, datareader, or datareader; see Section 8.2.5)

DomainParticipant:

create_datawriter_with_profile() (see Creating DataWriters (Section 6.3.1))

create_datareader_with_profile() (see Creating DataReaders (Section 7.3.1)

create_topic_with_profile() (see Creating Topics (Section 5.1.1))

Publisher:

create_datawriter_with_profile() (see Creating DataWriters (Section 6.3.1)) Subscriber:

create_datareader_with_profile() (see Creating DataReaders (Section 7.3.1))

Topic:

set_qos_with_profile() (see Setting Topic QosPolicies (Section 5.1.3)) DataWriter:

set_qos_with_profile() (see Changing QoS Settings After the Publisher Has Been Created (Section 6.2.4.2))

DataReader:

set_qos_with_profile() (see Setting DataReader QosPolicies (Section 7.3.8))

Other APIs will ignore QoSs with a topic_filter value different than "*". A QoS Profile with QoSs using topic_filter can also inherits from other QoS Profiles. In this case, inheritance will consider the value of the topic_filter expression.

Example 1:

<qos_library name=”Library”> <qos_profile name="BaseProfile">

<datawriter_qos>

...

</datawriter_qos>

<datawriter_qos topic_filter="T1*">

...

</datawriter_qos>

<datawriter_qos topic_filter="T2*">

...

</datawriter_qos> </qos_profile>

<qos_profile name="DerivedProfile" base_name="BaseProfile"> <datawriter_qos topic_filter="T11">

...

</datawriter_qos>

<datawriter_qos topic_filter="T21">

...

</datawriter_qos>

<datawriter_qos topic_filter="T31">

...

</datawriter_qos> </qos_profile>

</qos_library>

The datawriter_qos with topic_filter T11 in DerivedProfile will inherit its values from the datawriter_qos with topic_filter T1* in BaseProfile. The datawriter_qos with topic_filter T21 in DerivedProfile will inherit its values from the datawriter_qos with topic_filter T2* in BaseProfile. The datawriter_qos with topic_filter T31 in DerivedProfile will inherit its values from the datawriter_qos without topic_filter in BaseProfile.

17-18

Example 2:

<qos_library name=”Library”> <qos_profile name="BaseProfile">

<datawriter_qos topic_filter="T1*">

...

</datawriter_qos>

<datawriter_qos name="T2DataWriterQoS" topic_filter="T2*">

...

</datawriter_qos> </qos_profile>

<qos_profile name="DerivedProfile" base_name="BaseProfile"> <datawriter_qos topic_filter="T11"

base_name="BaseProfile::T2DataWriterQoS">

...

</datawriter_qos>

<datawriter_qos topic_filter="T21">

...

</datawriter_qos> </qos_profile>

</qos_library>

Although the topic_filter expressions do not match, the datawriter_qos with topic_filter T11 in DerivedProfile will inherit its values from the datawriter_qos with topic_filter T2* in BaseProfile. topic_filter is not used with inheritance from QoS to QoS. The datawriter_qos with topic_filter T21 in DerivedProfile will inherit its values from the datawriter_qos with topic_filter T2* in BaseProfile.

Example 3:

<qos_library name=”Library”>

<datawriter_qos name="BaseQos" topic_filter="T1">

...

</datawriter_qos>

<datawriter_qos name="DerivedQos" base_name="BaseQos" topic_filter="T2">

...

</datawriter_qos> </qos_library>

In the case of a single QoS profile, although the topic_filter expressions do not match, the datawriter_qos named DerivedQos with topic_filter T2 will inherit its values from the datawriter_qos named BaseQos with topic_filter T1.

17.9.4Overwriting Default QoS Values

There are two ways to overwrite the default QoS used for new entities with values from a profile: programmatically and with an XML attribute.

You can overwrite the default QoS programmatically with set_default_<entity>_qos_with_profile() (where <entity> is participant, topic, publisher, subscriber, datawriter, or datareader)

You can overwrite the default QoS using the XML attribute is_default_qos with the

<qos_profile> tag

Only for the DomainParticipantFactory: You can overwrite the default QoS using the XML attribute is_default_participant_factory_profile. This attribute has precedence over is_default_qos if both are set.

In the following example, the DataWriter and DataReader default QoS will be overwritten with the values specified in a profile named ‘StrictReliableCommunicationProfile’:

17-19

<qos_profile name="StrictReliableCommunicationProfile" is_default_qos="true">

<datawriter_qos> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datawriter_qos> <datareader_qos>

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datareader_qos>

</qos_profile>

If multiple profiles are configured to overwrite the default QoS, only the last one parsed applies.

Example:

In this example, the profile used to configure the default QoSs will be

StrictReliableCommunicationProfile.

<qos_profile name="BestEffortCommunicationProfile" is_default_qos="true">

...

</qos_profile>

<qos_profile name="StrictReliableCommunicationProfile" is_default_qos="true">

...

</qos_profile>

17.9.5Get Qos Profiles

To get a list of loaded QoS profiles, call the DomainParticipantFactory’s get_qos_profiles() operation, which returns the names of all profiles within a specified QoS library. Either the input QoS library name must be specified or the default profile library must have been set prior to calling this function.

DDS_ReturnCode_t get_qos_profiles (struct DDS_StringSeq *profile_names, const char *library_name)

17.10 QoS Libraries

A QoS Library is a named set of QoS profiles.

One configuration file may have several QoS libraries, each one defining its own QoS profiles. All QoS libraries must be declared within <dds> and </dds> tags. For example:

<dds>

<qos_library name="RTILibrary">

<!-- Individual QoSs are shortcuts for QoS Profiles with 1 QoS --> <datawriter_qos name="KeepAllWriter">

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history> </datawriter_qos>

17-20

<!-- Qos Profile -->

<qos_profile name="StrictReliableCommunicationProfile">

<datawriter_qos> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datawriter_qos> <datareader_qos>

<history> <kind>KEEP_ALL_HISTORY_QOS</kind>

</history>

<reliability> <kind>RELIABLE_RELIABILITY_QOS</kind>

</reliability> </datareader_qos>

</qos_profile>

</qos_library> </dds>

A QoS library can be reopened within the same configuration file or across different configuration files. For example:

<dds>

<qos_library name="RTILibrary">

...

</qos_library>

...

<qos_library name="RTILibrary">

...

</qos_library> </dds>

17.10.1 Get Qos Profile Libraries

To get a list of available QoS libraries, call the DomainParticipantFactory’s get_qos_profile_libraries() operation, which returns the names of all QoS libraries that have been loaded by Connext.

DDS_ReturnCode_t get_qos_profile_libraries (struct DDS_StringSeq *profile_names)

17.11 URL Groups

To provide redundancy and fault tolerance, you can specify multiple locations for a single XML document via URL groups. The syntax of a URL group is:

[URL1 | URL2 | URL2 | ... | URLn]

For example:

[file:///usr/local/default_dds.xml | file:///usr/local/alternative_default_dds.xml]

Only one of the elements in the group will be loaded by Connext, starting from the left.

Brackets are not required for groups with a single URL.

The NDDS_QOS_PROFILES environment variable contains a set of URL groups separated by semicolons. For example, on Linux and Solaris systems:

17-21

setenv NDDS_QOS_PROFILES [file:///usr/local/default_dds.xml|file:///usr/local/ alternative_default_dds.xml];[str://"<dds><qos_library name="MyQosLibrary"></ qos_library></dds>"]

The url_profile field in the PROFILE QosPolicy (DDS Extension) (Section 8.4.2) will contain a sequence of URL groups.

17.12 Configuring Logging Via XML

Logging can be configured via XML using the DomainParticipantFactory’s LoggingQosPolicy. See Section 21.2.2 for additional details.

17-22

Chapter 18 Multi-channel DataWriters

In Connext, producers publish data to a Topic, identified by a topic name; consumers subscribe to a Topic and optionally to specific content by means of a content-filter expression.

A Market Data Example:

A producer can publish data on the Topic "MarketData" which can be defined as a structured record containing fields that identify the exchange (e.g., "NYSE" or "NASDAQ"), the stock symbol (e.g., "APPL" or "JPM"), volume, bid and ask prices, etc.

Similarly, a consumer may want to subscribe to data on the "MarketData" Topic, but only if the exchange is "NYSE" or the symbol starts with the letter "M." Or the consumer may want all the data from the "NYSE" whose volume exceeds a certain threshold, or may want MarketData for a specific stock symbol, regardless of the exchange, and so on.

The middleware’s efficient implementation of content-filtering is critical for scenarios such as the above "Market Data" example, where there are large numbers of consumers, large volumes of data, or Topics that transmit information about many data-objects or subjects (e.g., individual stocks).

Traditionally, middleware products use four approaches to implement content filtering: Producer-based, Consumer-based, Server-based, and Network Switch-based.

Producer-based approaches push the burden of filtering to the producer side. The producer knows what each consumer wants and delivers to the consumer only the data that matches the consumer's filter. This approach is suitable when using point-to-point protocols such as TCP—it saves bandwidth and lowers the load on the consumer—but it does not work if data is distributed via multicast. Also, this approach does not scale to large numbers of consumers, because the producer would be overburdened by the need to filter for each individual consumer.

Consumer-based approaches push the burden of filtering to the consumer side. The producer sends all the data to every consumer and the middleware on the consumer side decides whether the application wants it or not, automatically filtering the unwanted data. This approach is simple and fits well in systems that use multicast protocols as a transport. But the approach is not efficient for consumers that want small subsets of the data, since the consumers have to spend a lot of time filtering unwanted data. This approach is also unsuitable for systems with large volumes of data, such as the above Market Data system.

18-1

Server-based approaches push the burden of filtering to a third component: a server or broker. This approach has some scalability advantages—the server can be run on a more powerful computer and can be federated to handle a large number of consumers. Some providers also provide hardware-assisted filtering in the server. However, the server- based approach significantly increases latency and jitter. It is also far more expensive to deploy and manage.

Network Switch-based approaches leverage the network hardware, specifically advanced (IGMP snooping) network switches, to offload most of the burden of filtering from the producers and consumers without introducing additional hardware, servers or proxies. This approach preserves the low latency and ease of deployment of the brokerless approaches while still providing most of the off-loading and scalability benefits of the broker.

RTI supports the producer-based, consumer-based and network-switch approaches to content filtering:

RTI automatically uses the producer-based and consumer-based approaches as soon as it detects a consumer that specifies a content filter. The producer-based approach is used if the consumer is receiving data over a point-to-point protocol (i.e., not multicast) and the number of consumers that specify filters is reasonably low (below 32). Otherwise, RTI uses a subscriber-based approach.

To use the more scalable network-switched based approach, an application must configure the DataWriter as a Multi-channel DataWriter. This concept is described in the following section.

18.1What is a Multi-channel DataWriter?

A Multi-channel DataWriter is a DataWriter that is configured to send data over multiple multicast addresses, according to some filtering criteria applied to the data.

To determine which multicast addresses will be used to send the data, the middleware evaluates a set of filters that are configured for the DataWriter. Each filter "guards" a channel—a set of multicast addresses. Each time a multi-channel DataWriter writes data, the filters are applied. If a filter evaluates to true, the data is sent over that filter’s associated channel (set of multicast addresses). We refer to this type of filter as a Channel Guard filter.

Multi-channel DataWriters can be used to trade off network bandwidth with the unnecessary processing of unwanted data for situations where there are multiple DataReaders who are interested in different subsets of data that come from the same data stream (Topic). For example, in Financial applications, the data stream may be quotes for different stocks at an exchange. Applications usually only want to receive data (quotes) for only a subset of the stocks being traded. In tracking applications, a data stream may carry information on hundreds or thousands of objects being tracked, but again, applications may only be interested in a subset.

The problem is that the most efficient way to deliver data to multiple applications is to use multicast so that a data value is only sent once on the network for any number of subscribers to the data. However, using multicast, an application will receive all of the data sent and not just the data in which it is interested, thus extra CPU time is wasted to throw away unwanted data. With this QoS, you can analyze the data-usage patterns of your applications and optimize network vs. CPU usage by partitioning the data into multiple multicast streams. While network bandwidth is still being conserved by sending data only once using multicast, most applications will only need to listen to a subset of the multicast addresses and receive a reduced amount of unwanted data.

Note: Your system can gain more of the benefits of using multiple multicast groups if your network uses Layer 2 Ethernet switches. Layer 2 switches can be configured to only route multicast packets to those ports that have added membership to specific multicast groups.

18-2

Figure 18.1 Multi-channel Data Flow

Figure 18.2 Multi-channel Evaluation

Using those switches will ensure that only the multicast packets used by applications on a node are routed to the node; all others are filtered-out by the switch.

18-3

18.2How to Configure a Multi-channel DataWriter

To configure a multi-channel DataWriter, simply define a list of all its channels in the DataWriter’s MULTI_CHANNEL QosPolicy (DDS Extension) (Section 6.5.14).

Each channel consists of filter criterion to apply to the data and a set of multicast destinations (transport, address, port) that will be used for sending data that matches the filter. You can think of this sequence of channels as a table like the one shown below:

If the Data Matches this Filter...

Send the Data to these Multicast Destinations

 

 

Symbol MATCH '[A-K]*

UDPv4:225.0.0.1:9000

 

 

Symbol MATCH '[L-Q]*

UDPv4:225.0.0.2:9001

 

 

Symbol MATCH '[P-Z]*

UDPv4:225.0.0.3:9002; 225.0.0.4:9003;

 

 

The example C++ code in Figure 18.3 on page 4 shows how to configure the channels.

Figure 18.3 Using the MULTI_CHANNEL QosPolicy

//initialize writer_qos with default values publisher->get_default_datawriter_qos(writer_qos);

//Initialize MULTI_CHANNEL Qos Policy

//Assign the filter name

//Possible options: DDS_STRINGMATCHFILTER_NAME, DDS_SQLFILTER_NAME writer_qos.multi_channel.filter_name = (char*) DDS_STRINGMATCHFILTER_NAME;

//Create two channels

writer_qos.multi_channel.channels.ensure_length(2,2);

// First channel writer_qos.multi_channel.channels[0].filter_expression =

DDS_String_dup("Symbol MATCH '[A-M]*'"); writer_qos.multi_channel.channels[0].multicast_settings.ensure_length(1,1); writer_qos.multi_channel.channels[0].multicast_settings[0].receive_port = 8700; writer_qos.multi_channel.channels[0].multicast_settings[0].receive_address =

DDS_String_dup("239.255.1.1");

// Second channel writer_qos.multi_channel.channels[1].multicast_settings.ensure_length(1,1); writer_qos.multi_channel.channels[1].multicast_settings[0].receive_port = 8800; writer_qos.multi_channel.channels[1].multicast_settings[0].receive_address =

DDS_String_dup("239.255.1.2"); writer_qos.multi_channel.channels[1].filter_expression =

DDS_String_dup("Symbol MATCH '[N-Z]*'");

// Create writer

writer = publisher->create_datawriter(

topic, writer_qos, NULL, DDS_STATUS_MASK_NONE);

The MULTI_CHANNEL QosPolicy is propagated along with discovery traffic. The value of this policy is available in the builtin topic for the publication (see the locator_filter field in Table 16.2, “Publication Built-in Topic’s Data Type (DDS_PublicationBuiltinTopicData),” on page 16-3).

18-4

18.2.1Limitations

When considering use of a multi-channel DataWriter, please be aware of the following limitations:

A DataWriter that uses the MULTI_CHANNEL QosPolicy will ignore multicast and unicast addresses specified on the reader side through the TRANSPORT_MULTICAST QosPolicy (DDS Extension) (Section 7.6.5) and TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23). The DataWriter will not publish samples on these locators.

Multi-channel DataWriters cannot be configured to use the Durable Writer History feature (described in Section 12.3).

Multi-channel DataWriters do not support fragmentation of large data.

Multi-channel DataWriters cannot be configured for asynchronous publishing (described in Section 6.4.1).

Multi-channel DataWriters rely on the rtps_object_id in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) to be DDS_RTPS_AUTO_ID (which causes automatic assignment of object IDs to channels).

To guarantee reliable delivery, a DataReader's PRESENTATION QosPolicy (Section 6.4.6) must be set to per-instance ordering (DDS_INSTANCE_PRESENTATION_QOS, the default value), instead of per-topic ordering (DDS_TOPIC_PRESENTATION_QOS), and the matching DataWriter's MULTI_CHANNEL QosPolicy (DDS Extension) (Section 6.5.14) must use expressions that only refer to key fields.

18.3Multi-channel Configuration on the Reader Side

No special changes are required in a subscribing application to get data from a multi-channel

DataWriter.

If you want the DataReader to subscribe to only a subset of the channels, use a ContentFilteredTopic, as described in Section 5.4. For example:

// Create a content filtered topic

contentFilter = participant->create_contentfilteredtopic_with_filter( "FilteredTopic",

topic,

"symbol MATCH 'NYE/BAC,NASDAQ/MSFT,NASDAQ/GOOG", parameters,

DDS_STRINGMATCHFILTER_NAME);

// Create a DataReader that uses the content filtered topic reader = subscriber->create_datareader(

contentFilter, DDS_DATAREADER_QOS_DEFAULT, NULL,0);

From there, Connext takes care of all the necessary steps:

The DataReader automatically discovers all the DataWriters—including multi-channel DataWriters—for the Topic it subscribes to.

When the DataReader discovers a multi-channel DataWriter, it also discovers the list of channels used by that DataWriter.

When the multi-channel DataWriter discovers a DataReader, it also discovers the content filters specified by that DataReader, if any.

With all this information, Connext automatically determines which channels are of "interest" to the DataReader.

18-5

A DataReader is interested in a channel if and only if the set of data values for which the channel guard filter evaluates to TRUE intersects the set of data values for which the DataReader's content filter evaluates to TRUE. If a DataReader does not use a content filter, then it is interested in all the channels.

Figure 18.4 Filter Intersection

In this scenario, the DataReader is interested in Channel1 and Channel2, but not Channel3.

Market Data Example, continued:

If the channel guard filter for Channel 1 is 'Symbol MATCH '[A-K]*' then the channel will only transfer data for stocks whose symbol starts with a letter in the A to K range.

That is, it will transfer data on 'APPL', "GOOG', and 'IBM', but not on 'MSFT', 'ORCL', or 'YHOO'. Channel 1 will be of interest to DataReaders whose content filter includes at least one stock whose symbol starts with a letter in the A to K range.

A DataReader that specifies a content filter such as "Symbol MATCH 'IBM, YHOO' " will be interested in Channel1.

A DataReader that specifies a content filter such as "Symbol MATCH '[G-M]*'" will also be interested in Channel1.

A DataReader that specifies a content filter such as "Symbol MATCH '[M-T]*' " will not be interested in Channel1.

18.4Where Does the Filtering Occur?

If Multi-channel DataWriters are used, the filtering can occur in three places:

Filtering at the DataWriter (Section 18.4.1)

Filtering at the DataReader (Section 18.4.2)

Filtering on the Network Hardware (Section 18.4.3)

18.4.1Filtering at the DataWriter

Each time data is written, the DataWriter evaluates each of the channel guard filters to determine which channels will transmit the data. This filtering occurs on the DataWriter.

18-6

Filtering on the DataWriter side is scalable because the number of filter evaluations depends only on the number of channels, not on the number of DataReaders. Usually, the number of channels is smaller than the number of possible DataReaders.

As explained in Section 18.7, if the channel guard filters are configured to only look at the "key" fields in the data, the channel filtering becomes a very efficient lookup operation.

18.4.2Filtering at the DataReader

The DataReader will listen on the multicast addresses that correspond to the channels of interest (see Section 18.3). When a channel is 'of interest', it means that it is possible for the channel to transmit data that meets the content filter of the DataReader, however the channel may also transmit data that does not pass the DataReader's content filter. Therefore, the DataReader has to filter all incoming data on that channel to determine if it passes its content filter.

Market Data Example, continued:

Channel 1, identified by guard filter "Symbol MATCH '[A-M]*'", will be of interest to DataReaders whose content filter includes at least one stock whose symbol starts with a letter in the A to K range.

A DataReader with content filter "Symbol MATCH 'GOOG'" will listen on Channel1.

In addition to 'GOOG', the DataReader will also receive samples corresponding to stock symbols such as 'MSFT' and 'APPL'. The DataReader must filter these samples out.

As explained in Section 18.7, if the DataReader’s content filters are configured to only look at the "key" fields in the data, the DataReader filtering becomes a very efficient lookup operation.

18.4.3Filtering on the Network Hardware

DataReaders will only listen to multicast addresses that correspond to the channels of interest. The multicast traffic generated in other channels will be filtered out by the network hardware (routers, switches).

Layer 3 routers will only forward multicast traffic to the actual destination ports. However, by default, layer 2 switches treat multicast traffic as broadcast traffic. To take advantage of network filtering with layer 2 devices, they must be configured with IGMP snooping enabled (see Section 18.7.1).

18.5Fault Tolerance and Redundancy

To achieve fault tolerance and redundancy, configure the DataWriter’s MULTI_CHANNEL QosPolicy (DDS Extension) (Section 6.5.14) to publish a sample over multiple channels or over different multicast addresses within a single channel. Figure 18.5 shows how to use overlapping channels.

If a sample is published to multiple multicast addresses, a DataReader may receive multiple copies of the sample. By default, duplicates are discarded by the DataReader and not provided to the application. To change this default behavior, use the Durable Reader State property, dds.data_reader.state.filter_redundant_samples (see Section 12.4.4).

18-7

Figure 18.5 Using the MULTI_CHANNEL QosPolicy with Overlapping Channels

//initialize writer_qos with default values publisher->get_default_datawriter_qos(writer_qos);

//Initialize MULTI_CHANNEL Qos Policy

//Assign the filter name

//Possible options: DDS_STRINGMATCHFILTER_NAME and DDS_SQLFILTER_NAME writer_qos.multi_channel.filter_name = (char*) DDS_STRINGMATCHFILTER_NAME;

//Create two channels

writer_qos.multi_channel.channels.ensure_length(2,2);

// First channel writer_qos.multi_channel.channels[0].filter_expression =

DDS_String_dup("Symbol MATCH '[A-M]*'"); writer_qos.multi_channel.channels[0].multicast_settings.ensure_length(2,2); writer_qos.multi_channel.channels[0].multicast_settings[0].receive_port = 8700; writer_qos.multi_channel.channels[0].multicast_settings[0].receive_address =

DDS_String_dup("239.255.1.1");

// Second channel writer_qos.multi_channel.channels[1].multicast_settings.ensure_length(1,1); writer_qos.multi_channel.channels[1].multicast_settings[0].receive_port = 8800; writer_qos.multi_channel.channels[1].multicast_settings[0].receive_address =

DDS_String_dup("239.255.1.2"); writer_qos.multi_channel.channels[1].filter_expression =

DDS_String_dup("Symbol MATCH '[C-Z]*'");

//Symbols starting with [C-M] will be published in two different channels

//Create writer

writer = publisher->create_datawriter(

topic, writer_qos, NULL, DDS_STATUS_MASK_NONE);

18.6Reliability with Multi-Channel DataWriters

18.6.1Reliable Delivery

Reliable delivery is only guaranteed when the access_scope in the Subscriber's PRESENTATION QosPolicy (Section 6.4.6) is set to DDS_INSTANCE_PRESENTATION_QOS (default value) and the filters in the DataWriter's MULTI_CHANNEL QosPolicy (DDS Extension) (Section 6.5.14)) are keyed-only based.

Market Data Example, continued:

Given the following IDL description for our MarketData topic type:

Struct MarketData { string<255> Symbol; //@key double Price;

}

A guard filter "Symbol MATCH 'APPL'" is keyed-only based.

A guard filter "Symbol MATCH 'APPL' and Price < 100" is not keyed-only based.

If any of the guard filters are based on non-key fields, Connext only guarantees reception of the most recent data from the multi-Channel DataWriter.

18-8

18.6.2Reliable Protocol Considerations

Reliability is maintained on a per-channel basis. Each channel has its own reliability channel send queue. The size of that queue is limited by max_samples in the RESOURCE_LIMITS QosPolicy (Section 6.5.20) and/or max_batches in DATA_WRITER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 6.5.4). The protocol parameters described in the DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) (Section 6.5.3) are applied per channel, with the following exceptions:

low_watermark and high_watermark: The low watermark and high watermark control the queue levels (in number of samples) that determine when to switch between regular and fast heartbeat rates (see Section 6.5.3.1). With multi-channel DataWriters, high_watermark and low_watermark refer to the DataWriter’s queue (not the reliability channel queue). Therefore, periodic heartbeating cannot be controlled on a per-channel basis.

Important: With multi-channel DataWriters, low_watermark and high_watermark refer to application samples even if batching is enabled. This behavior differs from the one without multi-channel DataWriters (where low_watermark and high_watermark refer to batches).

heartbeats_per_max_samples: This field defines the number of heartbeats per send queue. For multi-channel DataWriters, the value is applied per channel. However, the send queue size that is used to calculate the a piggyback heartbeat rate is defined per DataWriter (see max_samples in RESOURCE_LIMITS QosPolicy (Section 6.5.20)).

Important: With multi-channel DataWriters, heartbeats_per_max_samples refers to

samples even if batching is enabled. This behavior differs from the one without multi- channel DataWriters (where heartbeats_per_max_samples refers to batches, Section 6.5.3).

With batching and multi-channel DataWriters, the size of the DataWriter’s send queue should be configured using max_samples (in RESOURCE_LIMITS QosPolicy (Section 6.5.20)) instead of max_batches (in DATA_WRITER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 6.5.4)) in order to take advantage of heartbeats_per_max_samples.

18.7Performance Considerations

18.7.1Network-Switch Filtering

By default, multicast traffic is treated as broadcast traffic by layer 2 switches. To avoid flooding the network with broadcast traffic and take full advantage of network filtering, the layer 2 switches should be configured to use IGMP snooping. Refer to your switch’s manual for specific instructions.

When IGMP snooping is enabled, a switch can route a multicast packet to just those ports that subscribe to it, as seen in Figure 18.6.

Figure 18.6 IGMP Snooping

18-9

18.7.2DataWriter and DataReader Filtering

Where Does the Filtering Occur? (Section 18.4) describes the three places where filtering can occur with Multi-channel DataWriters. To improve performance when filtering occurs on the reader and/or writer sides, use filter expressions that are only based on keys (see Section 2.2.2). Then the results of the filter are cached in a hash table on a per-key basis.

Market Data Example, continued:

The filter expressions in the Market Data example are based on the value of the field, Symbol. To make filter operations on this field more efficient, declare Symbol as a key. For example:

struct {

string<MAX_SYMBOL_SIZE> Symbol; //@key

}

You can also improve performance by increasing the number of buckets associated with the hash table. To do so, use the instance_hash_buckets field in the RESOURCE_LIMITS QosPolicy (Section 6.5.20) on both the writer and reader sides. A higher number of buckets will provide better performance, but requires more resources.

18-10

Chapter 19 Connext Threading Model

This chapter describes the internal threads that Connext uses for sending and receiving data, maintaining internal state, and calling user code when events occur such as the arrival of new data samples. It may be important for you to understand how these threads may interact with your application.

A DomainParticipant uses three types of threads. The actual number of threads depends on the configuration of various QosPolicies as well as the implementation of the transports used by the DomainParticipant to send and receive data.

Database Thread (Section 19.1)

Event Thread (Section 19.2)

Receive Threads (Section 19.3)

Exclusive Areas, Connext Threads and User Listeners (Section 19.4)

Controlling CPU Core Affinity for RTI Threads (Section 19.5)

Through various QosPolicies, the user application can configure the priorities and other properties of the threads created by Connext. In real-time systems, the user often needs to set the priorities of all threads in an application relative to each other for the proper operation of the system.

19.1Database Thread

Connext uses internal data structures to store information about locally-created and remotely- discovered Entities. In addition, it will store various objects and data used by Connext for maintaining proper communications between applications. This “database” is created for each

DomainParticipant.

As Entities and objects are created and deleted during the normal operation of the user application, different entries in the database may be created and deleted as well. Because multiple threads may access objects stored in the database simultaneously, the deletion and removal of an object from the database happens in two phases to support thread safety.

When an entry/object in the database is deleted either through the actions of user code or as a result of a change in system state, it is only marked for deletion. It cannot be actually deleted and removed from the database until Connext can be sure that no threads are still accessing the object. Instead, the actual removal of the object is delegated to an internal thread that Connext spawns to periodically wake up and purge the database of deleted objects.

19-1

This thread is known as the Database thread (also referred to as the database cleanup thread).

Only one Database thread is created for each DomainParticipant.

The DATABASE QosPolicy (DDS Extension) (Section 8.5.1) of the DomainParticipant configures both the resources used by the database as well as the properties of the cleanup thread. Specifically, the user may want to use this QosPolicy to set the priority, stack size and thread options of the cleanup thread. You must set these options before the DomainParticipant is created, because once the cleanup thread is started as a part of participant creation, these properties cannot be changed.

The period at which the database-cleanup thread wakes up to purge deleted objects is also set in the DATABASE QosPolicy. Typically, this period is set to a long time (on the order of a minute) since there is no need to waste CPU cycles to wake up a thread only to find nothing to do.

However, when a DomainParticipant is destroyed, all of the objects created by the DomainParticipant will be destroyed as well. Many of these objects are stored in the database, and thus must be destroyed by the cleanup thread. The DomainParticipant cannot be destroyed until the database is empty and is destroyed itself. Thus, there is a different parameter in the DATABASE QosPolicy, shutdown_cleanup_period, that is used by the database cleanup thread when the DomainParticipant is being destroyed. Typically set to be on the order of a second, this parameter reduces the additional time needed to destroy a DomainParticipant simply due to waiting for the cleanup thread to wake up and purge the database.

19.2Event Thread

During operation, Connext must wake up at different intervals to check the condition of many different time-triggered or periodic events. These events are usually to determine if something happened or did not happen within a specified time. Often the condition must be checked periodically as long as the Entity for which the condition applies still exists. Also, the DomainParticipant may need to do something periodically to maintain connections with remote

Entities.

For example, the DEADLINE QosPolicy (Section 6.5.5) is used to ensure that DataWriters have published data or DataReaders have received data within a specified time period. Similarly, the LIVELINESS QosPolicy (Section 6.5.13) configures Connext both to check periodically to see if a DataWriter has sent a liveliness message and to send liveliness messages periodically on the behalf of a DataWriter. As a last example, for reliable connections, heartbeats must be sent periodically from the DataWriter to the DataReader so that the DataReader can acknowledge the data that it has received, see Chapter 10: Reliable Communications.

The checking of whether or not deadlines have been missed, the invoking of user-installed Listener callbacks to notify the application missed deadlines, and the sending of heartbeats to maintain reliable connections are all done with an internal Connext thread called the Event thread.

Only one Event thread is created per DomainParticipant.

The EVENT QosPolicy (DDS Extension) (Section 8.5.5) of the DomainParticipant configures both the properties and resources of the Event thread. Specifically, the user may want to use this QosPolicy to set the priority, stack size and thread options of the Event thread. You must set these options before the DomainParticipant is created, because once the Event thread is started as a part of participant creation, these properties cannot be changed.

The EVENT QosPolicy also configures the maximum number of events that can be handled by the Event thread. While the Event thread can only service a single event at a time, it must maintain a queue to hold events that are pending. The initial_count and max_count parameters of the QosPolicy set the initial and maximum size of the queue.

19-2

The priority of the Event thread should be carefully set with respect to the priorities of the other threads in a system. While many events can tolerate some amount of latency between the time that the event expires and the time that the Event thread services the event, there may be application-specific events that must be handled as soon as possible.

For example, if an application uses the liveliness of a remote DataWriter to infer the correct operation of a remote application, it may be critical for the user code in the DataReader Listener callback, on_liveliness_changed(), to be called by the Event thread as soon as it can be determined that the remote application has died. The operating system uses the priority of the Event thread to schedule this action.

19.3Receive Threads

Connext uses internal threads, known as Receive threads, to process the data packets received via underlying network transports. These data packets may contain meta-traffic exchanged by DomainParticipants for discovery, or user data (and meta-data to support reliable connections) destined for local DataReaders.

As a result of processing packets received by a transport, a Receive thread may respond by sending packets on the network. Discovery packets may be sent to other DomainParticipants in response to ones received. ACK/NACK packets are sent in response to heartbeats to support a reliable connection.

When a data-sample arrives, the Receive thread is responsible for deserializing and storing the data in the receive queue of a DataReader as well as invoking the on_data_available() DataReaderListener callback (see Section 7.3.4).

The number of Receive threads that Connext will create for a DomainParticipant depends on how you have configured the QosPolicies of DomainParticipants, DataWriters and DataReaders as well as on the implementation of a particular transport. The behavior of the builtin transports is well specified. However, if a custom transport is installed for a DomainParticipant, you will have to understand how the custom transport works to predict how many Receive threads will be created.

The following discussion applies on a per-transport basis. A single Receive thread will only service a single transport.

Connext will try to create receive resources1 for every port of every transport on which it is configured to receive messages. The TRANSPORT_UNICAST QosPolicy (DDS Extension) (Section 6.5.23) for DomainParticipant, DataWriters, and DataReaders, the TRANSPORT_MULTICAST QosPolicy (DDS Extension) (Section 7.6.5) for DataReaders and the DISCOVERY QosPolicy (DDS Extension) (Section 8.5.2) for DomainParticipants all configure the number of ports and the number of transports that Connext will try to use for receiving messages.

Generally, transports will require Connext to create a new receive resource for every unique port number. However, this is both dependent on how the underlying physical transport works and the implementation of the transport plug-in used by Connext. Sometimes Connext only needs to create a single receive resource for any number of ports.

When Connext finds that it is configured to receive data on a port for a transport for which it has not already created a receive resource, it will ask the transport if any of the existing receive resources created for the transport can be shared. If so, then Connext will not have to create a new receive resource. If not, then Connext will.

1. If UDPv4 was the only transport that Connext supports, then we would have called these receive resources, sockets.

19-3

The TRANSPORT_UNICAST, TRANSPORT_MULTICAST, and DISCOVERY QosPolicies allow you customize ports for receiving user data (on a per-DataReader basis) and meta-traffic (DataWriters and DomainParticipants); ports can be also set differently for unicast and multicast.

How do receive resources relate to Receive threads? Connext will create a Receive thread to service every receive resource that is created. If you use a socket analogy, then for every socket created, Connext will use a separate thread to process the data received on that socket.

So how many thread will Connext create by default–using only the builtin UDPv4 and shared memory transports and without modifying any QosPolicies?

Three Receive threads are created for meta-traffic1:

2 for unicast (one for UDPv4, one for shared memory)

1 for multicast (for UDPv4)2

Two Receive threads created for user data:

2 for unicast (UDPv4, shared memory)

0 for multicast (because user data is not sent via multicast by default)

Therefore, by default, you will have a total of five Receive threads per DomainParticipant. By using only a single transport and disabling multicast, a DomainParticipant can have as few as 2 Receive threads.

Similar to the Database and Event threads, a Receive thread is configured by the RECEIVER_POOL QosPolicy (DDS Extension) (Section 8.5.6). However, note that the thread properties in the RECEIVER_POOL QosPolicy apply to all Receive threads created for the

DomainParticipant.

19.4Exclusive Areas, Connext Threads and User Listeners

Connext Event and Receive threads may invoke user code through the Listener callbacks installed on different Entities while executing internal Connext code. In turn, user code inside the callbacks may invoke Connext APIs that reenter the internal code space of Connext. For thread safety, Connext allocates and uses mutual exclusion semaphores (mutexes).

As discussed in Section 4.5, when multiple threads and multiple mutexes are mixed together, deadlock may result. To prevent deadlock from occurring, Connext is designed using careful analysis and following rules that force mutexes to be taken in a certain order when a thread must take multiple mutexes simultaneously.

However, because the Event and Receive threads already hold mutexes when invoking user callbacks, and because the Connext APIs that the user code can invoke may try to take other mutexes, deadlock may still result. Thus, to prevent user code to cause internal Connext threads to deadlock, we have created a concept called Exclusive Areas (EA) that follow rules that prevent deadlock. The more EAs that exist in a system, the more concurrency is allowed through Connext code. However, the more EAs that exist, the more restrictions on the Connext APIs that are allowed to be invoked in Entity Listener callbacks.

The EXCLUSIVE_AREA QosPolicy (DDS Extension) (Section 6.4.3) control how many EAs will be created by Connext. For a more detailed discussion on EAs and the restrictions on the use of Connext APIs within Entity Listener methods, please see Exclusive Areas (EAs) (Section 4.5).

1.Meta-traffic refers to traffic internal to Connext related to dynamic discovery (see Chapter 14: Discovery).

2.Multicast is not supported by shared memory transports.

19-4

19.5Controlling CPU Core Affinity for RTI Threads

Two fields in the DDS_ThreadSettings_t structure are related to CPU core affinity: cpu_list and cpu_rotation.

Note: Although DDS_ThreadSettings_t is used in the Event, Database, ReceiverPool, and AsynchronousPublisher QoS policies, cpu_list and cpu_rotation are only relevant in the RECEIVER_POOL QosPolicy (DDS Extension) (Section 8.5.6).

While most thread-related QoS settings apply to a single thread, the ReceiverPool QoS policy’s thread-settings control every receive thread created. In this case, there are several schemes to map M threads to N processors; cpu_rotation controls which scheme is used.

The cpu_rotation determines how cpu_list affects processor affinity for thread-related QoS policies that apply to multiple threads. If cpu_list is empty, cpu_rotation is irrelevant since no affinity adjustment will occur. Suppose instead that cpu_list ={0,1} and that the middleware creates three receive threads: {A, B, C}. If cpu_rotation is set to CPU_NO_ROTATION, threads A, B and C will have the same processor affinities (0-1), and the OS will control thread scheduling within this bound.

CPU affinities are commonly denoted with a bitmask, where set bits represent allowed processors to run on. This mask is printed in hex, so a CPU affinity of 0-1 can be represented by the mask 0x3.

If cpu_rotation is CPU_RR_ROTATION, each thread will be assigned in round-robin fashion to one of the processors in cpu_list; perhaps thread A to 0, B to 1, and C to 0. Note that the order in which internal middleware threads spawn is unspecified.

The Platform Notes describe which architectures support this feature.

Table 19.1 describes the XML tags that you can use to configure thread settings. For more information, see Thread Settings (Section 17.8.7)

Table 19.1 XML Tags for ThreadSettings_t

Tags within

 

Number

Description

of Tags

<thread>

 

Allowed

 

 

 

 

 

 

 

 

 

Each <element> specifies a processor on which the thread may run.

 

 

<cpu_list>

 

<cpu_list>

<element>value</element>

0 or 1

</cpu_list>

 

 

 

Only applies to platforms that support controlling CPU core affinity (see

 

 

Section 19.5 and the Platform Notes).

 

 

 

 

 

Determines how the CPUs in <cpu_list> will be used by the thread. The

 

 

value can be either:

 

 

• THREAD_SETTINGS_CPU_NO_ROTATION

 

 

The thread can run on any listed processor, as determined by OS

 

<cpu_rotation>

scheduling.

0 or 1

 

• THREAD_SETTINGS_CPU_RR_ROTATION

 

 

The thread will be assigned a CPU from the list in round-robin order.

 

 

Only applies to platforms that support controlling CPU core affinity (see the

 

 

Platform Notes).

 

 

 

 

19-5

Table 19.1 XML Tags for ThreadSettings_t

Tags within

 

 

Number

 

Description

of Tags

<thread>

 

 

 

Allowed

 

 

 

 

 

 

 

 

 

 

A collection of flags used to configure threads of execution. Not all of these

 

 

options may be relevant for all operating systems. May include these bits:

 

 

STDIO

 

 

FLOATING_POINT

 

<mask>

REALTIME_PRIORITY

0 or 1

 

PRIORITY_ENFORCE

 

 

It can also be set to a combination of the above bits by using the “or” symbol

 

 

(|), such as STDIO|FLOATING_POINT.

 

 

Default: MASK_DEFAULT

 

 

 

 

 

Thread priority. The value can be specified as an unsigned integer or one of

 

 

the following strings.

 

 

• THREAD_PRIORITY_DEFAULT

 

 

• THREAD_PRIORITY_HIGH

 

 

• THREAD_PRIORITY_ABOVE_NORMAL

 

 

• THREAD_PRIORITY_NORMAL

 

 

• THREAD_PRIORITY_BELOW_NORMAL

 

 

• THREAD_PRIORITY_LOW

 

 

When using an unsigned integer, the allowed range is platform-dependent.

 

<priority>

When thread priorities are configured using XML, the values are considered

0 or 1

 

native priorities.

 

 

Example:

 

 

<thread>

 

 

 

<mask>STDIO|FLOATING_POINT</mask>

 

 

 

<priority>10</priority>

 

 

 

<stack_size>THREAD_STACK_SIZE_DEFAULT</stack_size>

 

 

</thread>

 

 

When the XML file is loaded using the Java API, the priority is a native

 

 

priority, not a Java thread priority.

 

 

 

 

 

Thread stack size, specified as an unsigned integer or set to the string

 

<stack_size>

THREAD_STACK_SIZE_DEFAULT. The allowed range is platform-

0 or 1

 

dependent.

 

 

 

 

 

19-6

Chapter 20 Sample-Data Memory Management

This chapter describes how Connext manages the memory for the data samples that are sent by

DataWriters and received by DataReaders.

20.1Sample-Data Memory Management for DataWriters

To configure sample-data memory management on the writer side, use the PROPERTY QosPolicy (DDS Extension) (Section 6.5.17). Table 20.1 lists the supported memory-management properties for DataWriters.

Table 20.1 Sample-Data Memory Management Properties for DataWriters

Property

Description

 

 

 

 

 

If the serialized size of the sample is <= pool_buffer_max_size:

 

The buffer is obtained from a pre-allocated pool and released when the

 

DataWriter is deleted.

 

If the serialized size of the sample is > pool_buffer_max_size:

dds.data_writer.

The buffer is dynamically allocated from the heap and returned to the

history.memory_manager.

heap when the sample is removed from the DataWriter’s queue.

fast_pool.pool_buffer_max_size

Default: -1 (UNLIMITED). All sample buffers are obtained from the pre-

 

allocated pool; the buffer size is the maximum serialized size of the sam-

 

ples, as returned by the type plugin get_serialized_sample_max_size()

 

operation.

 

See Memory Management without Batching (Section 20.1.1).

 

 

 

Only supported when using the Java API.

 

Defines the minimum size of the buffer that will be used to serialize

 

samples.

dds.data_writer.

When a DataWriter is created, the Java layer will allocate a buffer of this

history.memory_manager.

size and associate it with the DataWriter.

java_stream.min_size

Default: -1 (UNLIMITED). This is a sentinel that refers to the maximum

 

serialized size of a sample, as returned by the type plugin

 

get_serialized_sample_max_size() operation

 

See Writer-Side Memory Management when Using Java (Section 20.1.3).

 

 

20-1

Table 20.1 Sample-Data Memory Management Properties for DataWriters

Property

Description

 

 

 

 

 

Only supported when using the Java API.

 

A boolean value that controls the growth of the serialization buffer.

dds.data_writer.

If set to 0 (default): The buffer will not be reallocated unless the serialized

size of a new sample is greater than the current buffer size.

history.memory_manager.

If set to 1: The buffer will be reallocated with each new sample to a

java_stream.trim_to_size

smaller size in order to just fit the sample serialized size. The new size

 

 

cannot be smaller than min_size.

 

See Writer-Side Memory Management when Using Java (Section 20.1.3).

 

 

20.1.1Memory Management without Batching

When the write() operation is called on a DataWriter that does not have batching enabled, the DataWriter serializes (marshals) the input sample and stores it in the DataWriter’s queue (see Figure 20.1). The size of this queue is limited by initial_samples/max_samples in the RESOURCE_LIMITS QosPolicy (Section 6.5.20).

Each sample in the queue has an associated serialization buffer in which the DataWriter will serialize the sample. This buffer is either obtained from a pre-allocated pool (if the serialized size of the sample is <= dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size) or the buffer is dynamically allocated from the heap (if the serialized size of the sample is > dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size). See Table 20.1,

The default value of pool_buffer_max_size is -1 (UNLIMITED). In this case, all the samples come from the pre-allocated pool and the size of the buffers is the maximum serialized size of the samples as returned by the type plugin get_serialized_sample_max_size() operation. The default value is optimum for real-time applications where determinism and predictability is a must. The trade-off is higher memory usage, especially in cases where the maximum serialized size of a sample is large.

20.1.2Memory Management with Batching

When the write() operation is called on a DataWriter for which batching is enabled (see BATCH QosPolicy (DDS Extension) (Section 6.5.2)), the DataWriter serializes (marshals) the input sample into the current batch buffer (see Figure 20.2). When the batch is flushed, it is stored in the DataWriter’s queue along with its samples. The DataWriter queue can be sized based on:

The number of samples, using initial_samples/max_samples (both set in the RESOURCE_LIMITS QosPolicy (Section 6.5.20))

The number of batches, using initial_batches/max_batches (both set in the DATA_WRITER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 6.5.4))

Or a combination of max_samples and max_batches

When batching is enabled, the memory associated with the batch buffers always comes from a pre-allocated pool. The size of the buffers is determined by the QoS values max_samples and max_data_bytes (both set in the BATCH QosPolicy (DDS Extension) (Section 6.5.2)) as follows:

If max_data_bytes is a finite value, the size of the buffer is the minimum of this value and the maximum serialized size of a sample (max_sample_serialized_size) as returned by the type-plugin get_serialized_sample_max_size(), since that batch must contain at least one sample).

Otherwise, the size of the buffer is calculated by (batch.max_samples * max_sample_serialized_size).

20-2

Figure 20.1 DataWriter Actions when Batching is Disabled

Notice that for variable-size samples (for example, samples containing sequences) it is good practice to size the buffer based on max_data_bytes, since this leads to more efficient memory usage.

Note: The value of the property dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size is ignored by

DataWriters with batching enabled.

20.1.3Writer-Side Memory Management when Using Java

When the Java API is used, Connext allocates a Java buffer per DataWriter; this buffer is used to serialize the Java samples published by the DataWriters. After a sample is serialized into a Java buffer, the result is copied into the underlying native buffer described in Memory Management without Batching (Section 20.1.1) and Memory Management with Batching (Section 20.1.2).

20-3

Figure 20.2 DataWriter Actions when Batching is Enabled

You can use the following two DataWriter properties to control memory allocation for the Java buffers that are used for serialization (see Table 20.1):

dds.data_writer.history.memory_manager.java_stream.min_size

dds.data_writer.history.memory_manager.java_stream.trim_to_size

20.1.4Writer-Side Memory Management when Working with Large Data

Large samples are samples with a large maximum size relative to the memory available to the application. Notice the use of the word maximum, as opposed to actual size.

As described in Memory Management without Batching (Section 20.1.1), by default, the middleware preallocates the samples in the DataWriter queue to their maximum serialized size. This may lead to high memory-usage in DataWriters where the maximum serialized size of a sample is large.

20-4

For example, let’s consider a video conferencing application:

struct VideoFrame { boolean keyFrame;

sequence<octet,1024000> data;

};

The above IDL definition can be used to work with video streams.

Each frame is transmitted as a sequence of octets with a maximum size of 1 MB. In this example, the video stream has two types of frames: I-Frames (also called key frames) and P-Frames (also called delta frames). I-Frames represent full images and do not require information about the preceding frames in order to be decoded. P-frames require information about the preceding frames in order to be decoded.

A video stream consists of a sequence of frames in which I-Frames are followed by multiple P- frames. The number of P-frames between I-Frames affects the video quality since, in a non- reliable configuration, losing a P-frame will degrade the image quality until the next I-frame is received.

For our use case, let’s assume that I-frames may require 1 MB, while P-Frames require less than 32 KB. Also, there are 20 times more P-Frames than I-Frames.

Although the actual size of the frames sent by the Connext application is usually significantly smaller than 1 MB since they are P-Frames, the default memory management will use 1 MB per frame in the DataWriter queue. If resource_limits.max_samples is 256, the DataWriter may end up allocating 256 MB.

Using some domain-specific knowledge, such as the fact that most of the P-Frames have a size smaller than 32 KB, we can optimize memory usage in the DataWriter’s queue while still maintaining determinism and predictability for the majority of the frames sent on the wire.

The following XML file shows how to optimize the memory usage for the previous example (rather than focusing on efficient usage of the available network bandwidth).

<?xml version="1.0"?>

<!-- XML QoS Profile for large data -->

<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<!-- QoS Library containing the QoS profile used for large data --> <qos_library name="ReliableLargeDataLibrary">

<!-- QoS profile to optimize memory usage in DataWriters sending large images

-->

<qos_profile name="ReliableLargeDataProfile" is_default_qos="true"> <!-- QoS used to configure the DataWriter -->

<datawriter_qos> <resource_limits>

<max_samples>32</max_samples>

<!— No need to pre-allocate 32 images unless needed --> <initial_samples>1</initial_samples>

</resource_limits> <property>

<value>

<!-- For frames with size smaller or equal to 33 KB the serialization buffer is obtained from a pre-allocated pool. For sizes greater than 33 KB, the DataWriter will use dynamic memory allocation.

--> <element>

<name> dds.data_writer.history.memory_manager.fast_pool.pool_buffer_max_size

</name>

<value>33792</value>

</element>

<!-- Java will use a 33 KB buffer to serialize all

20-5

frames with a size smaller than or equal to 33 KB. When an I-frame is published, Java will reallocate the serialization buffer to match the serialized size of the new frame.

--> <element>

<name> dds.data_writer.history.memory_manager.java_stream.min_size

</name>

<value>33792</value>

</element>

<element>

<name> dds.data_writer.history.memory_manager.java_stream.trim_to_size

</name>

<value>1</value>

</element>

</value>

</property> </datawriter_qos>

</qos_profile> </qos_library>

</dds>

Working with large data samples will likely require throttling the network traffic generated by single samples. For additional information on shaping network traffic, see FlowControllers (DDS Extension) (Section 6.6).

20.2Sample-Data Memory Management for DataReaders

The data samples received by a DataReader are deserialized (demarshaled) and stored in the DataReader’s queue (see Figure 20.3). The size of this queue is limited by initial_samples/ max_samples in the RESOURCE_LIMITS QosPolicy (Section 6.5.20).

20.2.1Memory Management for DataReaders Using Generated Type-Plugins

Figure 20.3 shows how samples are processed and added to the DataReader’s queue.

The RTPS DATA samples received by a DataReader can be either batch samples or individual samples. The DataReader queue does not store batches. Therefore, each one of the samples within a batch will be deserialized and processed individually.

When the DataReader processes a new sample, it will deserialize it into a sample obtained from a pre-allocated pool. To provide predictability and determinism, the sample obtained from the pool is allocated to its maximum size. For example, with the following IDL type, each sample in the DataReader queue will consume 1 MB, even if the actual size is smaller.

struct VideoFrame { boolean keyFrame;

sequence<octet,1024000> data;

};

Currently, there are no configuration settings to change the default memory allocation policy for DataReaders using type plugin code generated by rtiddsgen.

20-6

Figure 20.3 Adding Samples to DataReader’s Queue

20.2.2Reader-Side Memory Management when Using Java

When the Java API is used with DataReaders using generated type-plugins, Connext allocates a Java buffer per DataReader; this buffer is used to copy the native serialized data, so that the received samples can be deserialized into the Java objects obtained from the sample pool in Figure 20.3.

20-7

You can use the DataReader properties in Table 20.2 to control memory allocation for the Java buffer used for deserialization:

Table 20.2 Sample-Data Memory Management Properties for DataReaders when Using Java API

Property

Description

 

 

 

 

 

Only supported when using the Java API.

 

Defines the minimum size of the buffer used for the serialized data.

dds.data_reader.

When a DataReader is created, the Java layer will allocate a buffer of this size

history.memory_manager.

and associate it with the DataReader.

java_stream.min_size

Default: -1 (UNLIMITED) This is a sentinel to refer to the maximum serialized

 

 

size of a sample, as returned by the type plugin method

 

get_serialized_sample_max_size().

 

 

 

Only supported when using the Java API.

dds.data_reader.

A Boolean value that controls the growth of the deserialization buffer.

If set to 0 (the default), the buffer will not be re-allocated unless the serialized

history.memory_manager.

java_stream.trim_to_size

size of a new sample is greater than the current buffer size.

If set to 1, the buffer will be re-allocated with each new sample in order to just

 

 

fit the sample serialized size. The new size cannot be smaller than min_size.

 

 

20.2.3Memory Management for DynamicData DataReaders

Unlike DataReaders that use generated type-plugin code, DynamicData DataReaders provide configuration mechanisms to optimize the memory usage for use cases involving large data samples.

A DynamicData sample stored in the DataReader’s queue has an associated underlying buffer that contains the serialized representation of the sample. The buffer is allocated according to the configuration provided in the serialization member of the DynamicDataProperty_t used to create the DynamicDataTypeSupport (see Interacting Dynamically with User Data Types (Section 3.8)).

struct DDS_DynamicDataProperty_t {

...

DDS_DynamicDataTypeSerializationProperty_t serialization;

}

struct DDS_DynamicDataTypeSerializationProperty_t {

...

DDS_UnsignedLong max_size_serialized; DDS_UnsignedLong min_size_serialized; DDS_Boolean trim_to_size;

}

Table 20.3 describes the members of DDS_DynamicDataTypeSerializationProperty_t.

Table 20.3 struct DDS_DynamicDataTypeSerializationProperty_t

Name

Description

 

 

 

Defines the maximum size of the buffer that will contain the serialized sample.

max_size_serialized

Default: 0xFFFFFFFF, indicates that Connext must use the maximum serialized size

 

of a sample according to the type information. Except in very specific scenarios, the

 

value max_size_serialized should always be the default.

 

 

 

Defines the minimum size of the buffer used to hold the serialized data in a

min_size_serialized

DynamicData object.

Default: 0xFFFFFFFF, a sentinel that indicates that this value must be equal to the

 

 

value specified in max_size_serialized.

 

 

20-8

Table 20.3 struct DDS_DynamicDataTypeSerializationProperty_t

Name

Description

Controls the growth of the serialization buffer in a DynamicData object.

If set to 0 (default): The buffer will not be reallocated unless the serialized size of the

incoming sample is greater than the current buffer size.

trim_to_size

If set to 1: The buffer of a DynamicData object obtained from the sample pool will be re-allocated to just fit the size of the serialized data of the incoming sample. The new size cannot be smaller than min_size_serialized.

Figure 20.4 shows how samples are allocated in the DataReader queue for DynamicData

DataReaders.

Figure 20.4 How samples are Allocated in DataReader Queue for DynamicData DataReaders

20-9

20.2.5Memory Management for Fragmented Samples

When a DataWriter writes samples with a serialized size greater than the minimum of the largest transport message sizes across all transports installed with the DataWriter, the samples are fragmented into multiple RTPS fragment messages.

The different fragments associated with a sample are assembled in the DataReader side into a single buffer that will contain the sample serialized data after the last fragment is received.

By default, the DataReader keeps a pool of pre-allocated serialization buffers that will be used to reconstruct the serialized data of a sample from the different fragments. Each buffer hold one individual sample and it has a size equal to the maximum serialized size of a sample. The pool size can be configured using the QoS values initial_fragmented_samples and max_fragmented_samples in DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension) (Section 7.6.2).

The main disadvantage in pre-allocating the serialization buffers is an increase in memory usage, especially when the maximum serialized of a sample is quite large. Connext offers a setting that allows memory for a sample to be allocated from the heap the first time a fragment is received. The amount of memory allocated equals the amount of memory needed to store all fragments in the sample.

20.2.6Reader-Side Memory Management when Working with Large Data

This section describes how to configure the DataReader side of the videoconferencing application introduced in Writer-Side Memory Management when Working with Large Data (Section 20.1.4) to optimize memory usage.

The following XML file can be used to optimize the memory usage in the previous example:

<?xml version="1.0"?>

<!-- XML QoS Profile for large data -->

<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<!-- QoS Library containing the QoS profile used for large data --> <qos_library name="ReliableLargeDataLibrary">

<!-- QoS profile used to optimize the memory usage in a DataWriter sending large data images

-->

<qos_profile name="ReliableLargeDataProfile" is_default_qos="true"> <!-- QoS used to configure the DataWriter -->

<datareader_qos> <history>

<kind>KEEP_ALL_HISTORY_QOS</kind> </history>

<resource_limits> <max_samples>32</max_samples>

<!— No need to pre-allocate 32 frames unless needed --> <initial_samples>1</initial_samples>

</resource_limits>

<reader_resource_limits>

<!-- Since the video frame samples have a large maximum serialized size we can configure the fragmented samples pool to use dynamic memory allocation. As an alternative, reduce max_fragmented_samples. However, that may cause fragment retransmission.

--> <dynamically_allocate_fragmented_samples>

1

20-10

</dynamically_allocate_fragmented_samples> </reader_resource_limits>

<property>

<value>

<!-- Java will use a buffer of 33KB to deserialize all frames with a serialized size smaller or equal than 33KB. When an I-frame is received, Java will re-allocate the deserialization buffer to match the serialized size of the new frame.

--> <element>

<name> dds.data_writer.history.memory_manager.java_stream.min_size

</name>

<value>33792</value>

</element>

<element>

<name> dds.data_writer.history.memory_manager.java_stream.trim_to_size

</name>

<value>1</value>

</element>

</value>

</property> </qos_profile>

</qos_library> </dds>

20-11

Chapter 21 Troubleshooting

This chapter contains tips on troubleshooting Connext applications. For an up-to-date list of frequently asked questions, see the RTI Support Portal, accessible from https:// support.rti.com—select the Find Solution link to see sample code, general information on Connext, performance information, troubleshooting tips, and technical details.

This chapter contains the following sections:

What Version am I Running? (Section 21.1)

Controlling Messages from Connext (Section 21.2)

21.1What Version am I Running?

There are two ways to obtain version information:

By looking at the revision files, as described in Section 21.1.1.

Programmatically at run time, as described in Section 21.1.2.

21.1.1Finding Version Information in Revision Files

In the top-level directory of your Connext installation (${NDDSHOME}), you will find text files that include revision information. The files are named rev_<product>_rtidds.<version>. For example, you might see files called rev_host_rtidds.5.0.x and rev_persistence_rtidds5.0.x

(where x is a release-specific number). Each file contains more details, such as a patch level and if the product is license managed.

For example:

Host Build 5.0.x rev 04 (0x04050200)

The revision files for Connext target libraries are in the same directory as the libraries (${NDDSHOME}/lib/<architecture>).

21.1.2Finding Version Information Programmatically

The methods in the NDDSConfigVersion class can be used to retrieve version information for the Connext product, the core library, and the C, C++ or Java libraries.

The version information includes four fields:

A major version number

A minor version number

21-1

A release number

A build number

Table 21.4 lists the available operations (they will vary somewhat depending on the programming language you are using; consult the API Reference HTML documentation for more information).

Table 21.1 NDDSConfigVersion Operations

 

Purpose

 

Operation

 

Description

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

get_product_version

 

Gets version information for the Connext product.

To

retrieve

version

 

 

 

get_core_version

 

Gets version information for the Connext core library.

information

in

a

 

 

 

get_c_api_version

 

Gets version information for the Connext C library.

structured format

 

 

 

 

 

 

 

 

 

 

get_cpp_api_version

 

Gets version information for the Connext C++ library.

 

 

 

 

 

 

To

retrieve

version

 

 

Converts the version information for each library into a

information

in string

to_string

 

string. The strings for each library are put in a single

format

 

 

 

 

hyphen-delimited list.

 

 

 

 

The get_product_version() operation

returns a reference to a structure of type

DDS_ProductVersion_t:

 

 

struct NDDS_Config_ProductVersion_t { DDS_Char major;

DDS_Char minor; DDS_Char release; DDS_Char revision;

};

The other get_*_version() operations return a reference to a structure of type NDDS_Config_LibraryVersion_t:

struct NDDS_Config_LibraryVersion_t { DDS_Long major;

DDS_Long minor; char release; DDS_Long build;

};

The to_string() operation returns version information for the Connext core, followed by the C and C++ API libraries, separated by hyphens. For example:

nddscore1.0g.rev0-nddsc1.0g.rev1-nddscpp1.0g.rev0

Core

C API:

C++ API:

major: 1

major: 1

major: 1

minor: 0

minor: 0

minor: 0

release: g

release: g

release:g

build: 0

build: 1

build: 0

21.2Controlling Messages from Connext

Connext provides several types of messages to help you debug your system and alert you to errors during run time. You can control how much information is reported and where it is logged.

21-2

How much information is logged is known as the verbosity setting. Table 21.2 describes the increasing verbosity levels.

Table 21.2 Message Logging Verbosity Levels

Verbosity

 

(NDDS_CONFIG_

Description

LOG_VERBOSITY_*)

 

 

 

SILENT

No messages will be logged. (lowest verbosity)

 

 

ERROR (default level

Log only high-priority error messages. An error indicates something is wrong

with how Connext is functioning. The most common cause of this type of error

for all categories)

is an incorrect configuration.

 

 

 

 

Additionally log warning messages. A warning indicates that Connext is

WARNING

taking an action that may or may not be what you intended. Some

 

configuration information is also logged at this verbosity to aid in debugging.

 

 

STATUS_LOCAL

Additionally log verbose information about the lifecycles of local Connext

objects.

 

 

 

STATUS_REMOTE

Additionally log verbose information about the lifecycles of remote Connext

objects.

 

 

 

STATUS_ALL

Additionally log verbose information about periodic activities and Connext

threads. (highest verbosity)

 

 

 

Note that the verbosities are cumulative: logging at a high verbosity means also logging all lower verbosity messages. If you change nothing, the default verbosity will be set to

NDDS_CONFIG_LOG_VERBOSITY_ERROR.

Caution: Logging at high verbosities can be detrimental to your application's performance. You should generally not set the verbosity above NDDS_CONFIG_LOG_VERBOSITY_WARNING, unless you are debugging a specific problem.

You will typically change the verbosity of all of Connext at once. However, in the event that such a strategy produces too much output, you can further discriminate among the messages you would like to see. The types of messages logged by Connext fall into the categories listed in Table 21.3; each category can be set to a different verbosity level.

Table 21.3 Message Logging Categories

Category

 

(NDDS_CONFIG_

Description

LOG_CATEGORY_*)

 

 

 

PLATFORM

Messages about the underlying platform (hardware and OS).

 

 

COMMUNICATION

Messages about data serialization and deserialization and network traffic.

 

 

DATABASE

Messages about the internal database of Connext objects.

 

 

ENTITIES

Messages about local and remote entities and the discovery process.

 

 

API

Messages about Connext’s API layer (such as method argument validation).

 

 

The methods in the NDDSConfigLogger class can be used to change verbosity settings, as well as the destination for logged messages. Table 21.4 lists the available operations; consult the API Reference HTML documentation for more information.

Table 21.4 NDDSConfigLogger Operations

Purpose

Operation

Description

 

 

 

 

 

 

 

 

Gets the current verbosity.

Change Verbosity for all

get_verbosity

If per-category verbosities are used, returns the

Categories

 

highest verbosity of any category.

 

set_verbosity

Sets the verbosity of all categories.

 

 

 

21-3

Table 21.4 NDDSConfigLogger Operations

Purpose

 

Operation

Description

 

 

 

 

 

 

 

 

Change Verbosity for

a

get_verbosity_by_category

Gets/Sets the verbosity for a specific category.

Specific Category

 

set_verbosity_by_category

 

 

 

 

 

 

 

 

 

Returns the file to which messages are being

 

 

get_output_file

logged, or NULL for the default destination

 

 

 

(standard output on most platforms).

 

 

 

 

 

 

set_output_file

Redirects future logged messages to the specified

Change Destination

of

file (or NULL to return to the default).

 

Logged Messages

 

get_output_device

Returns the logging device installed with the

 

 

logger.

 

 

 

 

 

 

 

 

 

 

Registers a specified logging device with the log-

 

 

set_output_device

ger. See Customizing the Handling of Generated

 

 

 

Log Messages (Section 21.2.3)

 

 

 

 

 

 

get_print_format

Gets/Sets the current message format that

Change Message Format

 

Connext is using to log diagnostic information.

set_print_format

 

 

See Format of Logged Messages (Section 21.2.1).

 

 

 

 

 

 

 

21.2.1Format of Logged Messages

You can control the amount of information in each message with the set_print_format() operation. The format options are listed in Table 21.5.

Table 21.5 Message Formats

Message Format

 

(NDDS_CONFIG_LOG_

Description

PRINT_FORMAT_*)

 

 

 

DEFAULT

Message, method name, and activity context.

 

 

TIMESTAMPED

Message, method name, activity context, and timestamp.

 

 

VERBOSE

Message with all available context information (includes thread

identifier, activity context).

 

 

 

VERBOSE_TIMESTAMPED

Message with all available context information and timestamp.

 

 

DEBUG

Information for internal debugging by RTI personnel.

 

 

MINIMAL

Message number, method name.

 

 

MAXIMAL

All available fields.

 

 

Of course, you are not likely to recognize all of the method names; many of the operations that perform logging are deep within the implementation of Connext. However, in case of errors, logging will typically take place at several points within the call stack; the output thus implies the stack trace at the time the error occurred. You may only recognize the name of the operation that was the last to log its message (i.e., the function that called all the others); however, the entire stack trace is extremely useful to RTI support personnel in the event that you require assistance.

You may notice that many of the logged messages begin with an exclamation point character. This convention indicates an error and is intended to be reminiscent of the negation operator in many programming languages. For example, the message “!create socket”in the second line of the above stack trace means “cannot create socket.”

21.2.1.1Timestamps

Reported times are in seconds from a system-dependent starting time; these are equivalent to the output format from Connext. The timestamp is in the form "ssssss.mmmmmm" where <ssssss> is a number of seconds, and <mmmmm> is a fraction of a second expressed in

21-4

microseconds. Enabling timestamps will result in some additional overhead for clock access for every message that is logged.

Logging of timestamps is not enabled by default. To enable it, use NDDS_Config_Logger method set_print_format().

21.2.1.2Thread identification

Thread identification strings uniquely identify for active thread when a message is output to the console. A thread may be a user (application) thread or one of several types of internal threads. The possible thread types are:

user thread: U<threadID>

receive thread: rR<thread index><domain ID><app ID>, where thread index is an integer identifying this receive thread

event thread: revt<domain ID><app ID>

asynchronous publisher thread: rDsp

Logging of thread IDs are not enabled by default. To enable it, use NDDS_Config_Logger method set_print_format().

21.2.1.3Hierarchical Context

Many middleware APIs now store information in thread-specific storage about the current operation, as well as information about which domain (and participant ID) was active, and which entities were being operated on. In the case of objects that are associated with topics, the topic name is also stored.

The context field is output by default.

21.2.1.4Explanation of Context Strings

Domain context

Dxxyy

In this case, xx = participant ID, yy = domain #. For example, D0149 means “domain 49, participant 01.”

Entity context

Operation on an entity will specify the object and a numeric ID, such as Writer(001A1). The name will be one of the following:

String

Object type

 

 

Participant

DDS_DomainParticipant

 

 

Pub

DDS_Publisher

 

 

Sub

DDS_Subscriber

 

 

Topic

DDS_Topic

 

 

Writer

DDS_<*>DataWriter

 

 

Reader

DDS_<*>DataReader

 

 

Topic Context

T=Hello refers to topic "Hello."

The operations which report context include:

21-5

String

Operation

 

 

 

 

Entity operations

 

 

 

ENABLE

Entity::enable

 

 

GET_QOS

Entity::get_qos

 

 

SET_QOS

Entity::set_qos

 

 

GET_LISTENER

Entity::get_listener

 

 

SET_LISTENER

Entity::set_listener

 

 

Factory operations (DP Factory, Participant, Pub/Sub)

 

 

CREATE <Entity>

Factory::create_<entity>

 

 

DELETE <Entity>

Factory::delete_<entity>

 

 

GET_DEFAULT_QOS <Entity>

Factory::get_default_<entity>_qos

 

 

SET_DEFAULT_QOS <Entity>

Factory::set_default_<entity>_qos

 

 

Participant-specific operations

 

 

 

GET_PUBS

Participant::get_publishers

 

 

GET_SUBS

Participant::get_subscribers

 

 

LOOKUP Topic(<name>)

Participant::lookup_topicdescription

 

 

LOOKUP FlowController(<name>)

Participant::lookup_flowcontroller

 

 

IGNORE <Entity>(<host ID>)

Participant::ignore_<entity>

 

 

21.2.2Configuring Logging via XML

Logging can also be configured using the DomainParticipantFactory’s LOGGING QosPolicy (DDS Extension) (Section 8.4.1) with the tags, <participant_factory_qos><logging>. The fields in the LoggingQosPolicy are described in XML using a 1-to-1 mapping with the equivalent C representation shown below:

struct DDS_LoggingQosPolicy { NDDS_Config_LogVerbosity verbosity; NDDS_Config_LogCategory category; NDDS_Config_LogPrintFormat print_format; char * output_file;

};

The equivalent representation in XML:

<participant_factory_qos> <logging>

<verbosity></verbosity>

<category></category> <print_format></print_format> <output_file></output_file>

</logging> </participant_factory_qos>

The attribute <is_default_participant_factory_profile> can be set to true for the <qos_profile> tag to indicate from which profile to use <participant_factory_qos>. If multiple QoS profiles have <is_default_participant_factory_profile> set to true, the last profile with <is_default_participant_factory_profile> set to true will be used.

If none of the profiles have set <is_default_participant_factory_profile> to true, the profile with <is_default_qos> set to true will be used.

In the following example, DefaultProfile2 will be used:

<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/rti_dds_qos_profiles.xsd">

<!-- Qos Library -->

21-6

<qos_library name="DefaultLibrary"> <qos_profile name="DefaultProfile1"

is_default_participant_factory_profile ="true"> <participant_factory_qos>

<logging>

<verbosity>ALL</verbosity>

<category>ENTITY</category> <print_format>MAXIMAL</print_format> <output_file>LoggerOutput1.txt</output_file>

</logging> </participant_factory_qos>

</qos_profile>

<qos_profile name="DefaultProfile2" is_default_participant_factory_profile ="true"> <participant_factory_qos>

<logging>

<verbosity>WARNING</verbosity>

<category>API</category> <print_format>VERBOSE_TIMESTAMPED</print_format> <output_file>LoggerOutput2.txt</output_file>

</logging> </participant_factory_qos>

</qos_profile>

<qos_profile name="DefaultProfile3" is_default_qos="true"> <participant_factory_qos>

<logging>

<verbosity>ERROR</verbosity>

<category>DATABASE</category> <print_format>VERBOSE</print_format> <output_file>LoggerOutput3.txt</output_file>

</logging> </participant_factory_qos>

</qos_profile> </qos_library>

</dds>

Note: The LoggingQosPolicy is currently the only QoS policy that can be configured using the <participant_factory_qos> tag.

21.2.3Customizing the Handling of Generated Log Messages

By default, the log messages generated by Connext are sent to the standard output. You can redirect the log messages to a file by using the set_output_file() operation,

To further customize the management of the generated log messages, you can use the Logger’s set_output_device() operation to install a user-defined logging device. The logging device must implement an interface with two operations: write() and close().

Connext will call the write() operation to write a new log message to the input device. The log message provides the text and the verbosity corresponding to the message.

Connext will call the close() operation when the logging device is uninstalled.

Note: It is not safe to make any calls to the Connext core library including calls to

DDS_DomainParticipant_get_current_time() from any of the logging device operations.

For additional details on user-defined logging devices, see the API Reference HTML documentation (under Modules, RTI Connext API Reference, Configuration Utilities).

21-7

Index, Part 3

A

accept_unknown_peers 14-9 access control 16-9

ACKNACK messages 10-2 to 10-3, 10-19 API message category 21-3

B

batching small samples 10-15 best-effort delivery 10-1 blocking time 10-7

built-in Subscriber 16-2, 16-8

QoS 16-2 built-in topics 16-1

bundling messages 10-4

C

categories of messages 21-3 channel guard filter 18-2 collaborative DataWriters 11-1

COMMUNICATION message category 21-3 conserving CPU and bandwidth 10-17 controlling queue depth 10-13

CPU core affinity 19-5 CPU usage 10-17

D

database cleanup thread 19-1 DATABASE message category 21-3 DataReaderResouceLimits QoS 10-10 DataWriterProtocol QoS 10-13 DCPSParticipant 16-1 DCPSPublication 16-1 DCPSSubscription 16-1 DDS_ParticipantBuiltinTopicData 16-2

debugging error messages 14-21, 21-2, 21-4 depth of queues 10-13 direct_communication 12-13

discovery debugging 14-21 definition 14-1

endpoint phase 14-2

endpoint readers/writers 14-15 entities used 14-8

late-joiners 10-19 participant phase 14-2 refresh mechanism 14-9 related QoS 14-20 summary diagram 14-20

domain_id_gain 14-25 DomainParticipantFactory

operations on 15-3 Durability QoS 10-19, 13-4 durable writer history 12-5, 13-4

E

endpoint discovery 14-2 ENTITIES message category 21-3 entityID 14-2

environment variables NDDS_DISCOVERY_PEERS 14-3

error messages 14-21, 21-2 format of 21-4

ERROR verbosity 21-3 event thread 19-1 to 19-2

F

framing Heartbeat 10-24

G

get_builtin_subscriber() 16-2 get_c_version() 21-2 get_core_api_version() 21-2 get_cpp_api_version() 21-2 get_output_device() 21-4 get_output_file() 21-4 get_verbosity() 21-3

GUID (Globally Unique ID) 14-2

H

HB messages. See heartbeats. heartbeat_period 10-13

diagram 10-14 heartbeats 10-3

controlling 10-13

how many resent 10-16 how often 10-13

types of 10-24 heartbeats_per_max_samples 10-15

diagram 10-16 historical data 10-19 HISTORY QoS 10-13 History QoS

depth 10-13

I

ignore_participant() 16-8 ignore_publication() 16-8 ignore_subscription() 16-8 ignore_topic() 16-8 inheriting QoS profiles 17-15 initial peers list 14-3

inter-participant reader/writer 14-14

K

keep duration 10-18

L

late-joiners 10-19, 13-4 discovery of 14-19 layer 2 switches 18-2

logged error messages 21-3 logging devices 21-7 lookup_datareader() 16-2, 16-8

M

matching writers and readers 14-2 to 14-3 max_blocking_time (Reliability QoS) 10-7 max_bytes_per_nack_response

(RtpsReliableWriterProtocol_t) 10-15 max_heartbeat_retries (RtpsReliableWriterProtocol_t) 10-

16

max_nack_response_delay (DataWriterProtocol QoS) 10-17 max_samples (ResourceLimits QoS) 10-15 max_samples_per_instance (ResourceLimits QoS) 10-13 max_samples_per_remote_writer 10-10

message bundling 10-4 message storms 10-19 meta data

ports for 14-24 meta-traffic

definition 19-4 multicast

ports used 14-24

Index-1

N

NACKs

non-progressing 10-17 NDDS_Config_LibraryVersion_t structure 21-2 NDDS_DISCOVERY_PEERS 14-3 NDDSConfigLogger class 21-3

operations on 21-3 NDDSConfigVersion class 21-1 non-progressing NACK 10-17

P

packet loss 10-10

participant DATA messages 14-2, 14-9 participant_assert_liveliness_period 14-9 participant_id_gain 14-25 participant_liveliness_assert_period 14-9 participant_liveliness_lease_duration_period

(DiscoveryConfig QoS) 14-9

ParticipantBuiltinTopicData 16-9 peers list 14-2 to 14-3 peer-to-peer communication 12-13 piggyback heartbeats 10-14

PLATFORM message category 21-3 ports 14-24 to 14-25

PRSTDataReader 12-13

PRSTDataWriter 12-13 publication DATA messages 14-2

Q

QoS

DataWriterProtocol 10-13 Durability 10-19

for built-in Subscribers and DataReaders 16-2 History 10-13

queue depths 10-13

R

receive queue 10-2 example 10-12

size 10-7, 10-9, 10-11, 10-13 receive thread 19-1, 19-3 relay communication 12-13 reliable delivery 10-1 to 10-2

blocking time 10-7 definition 10-2 diagram 10-5 strict 10-13 tuning 10-6

repair packages 10-15 restricting communication 16-8 retries

controlling 10-13 RTPS protocol 10-3

basic behavior diagram 10-4 bundling messages 10-4 reliable behavior diagram 10-5

rtps_app_id 14-21 rtps_host_id 14-21

S

samples

resending 10-15, 10-17 send queue

blocking time 10-7 max_samples 10-9 purpose of 10-2

size 10-7 to 10-8, 10-10 to 10-11, 10-13 size (formula) 10-9 to 10-10

send window 10-8 set_output_device() 21-4 set_output_file() 21-4 set_verbosity() 21-3

SILENT verbosity 21-3 speculative caching 10-28 STATUS_ALL verbosity 21-3 STATUS_LOCAL verbosity 21-3 STATUS_REMOTE verbosity 21-3 submessage bundling 10-4

T

thread-pinning 19-5 to_string() 21-2

U

unicast

ports used 14-25 user data

ports for 14-25

V

verbosity 21-3 version query 21-1

virtual GUID 11-1, 12-4

W

WARNING verbosity 21-3 writing data

blocked send queue 10-7

Index-2