An individual QoS Profile or Entity QoS (e.g., <datawriter_qos>) can inherit values from other QoS Profiles, and/or be composed out of QoS Snippets. In inheritance, a base_name attribute is used to inherit from a single, previously loaded QoS Profile. With composition, a <base_name> tag is used to specify a list of one or more QoS Snippets to overlay upon the base profile, creating a new composed profile. The following sections describe how these methods can be used, including best practices. See also 19.6 XML File Syntax.
An individual QoS Profile can inherit values from other QoS Profiles described in the XML file by using the attribute base_name.
A QoS Profile may also inherit values from other QoS Profiles described in different XML files. A QoS Profile can only inherit from other QoS Profiles that have already been loaded. The order in which XML resources are loaded is described in 19.5 How to Load XML-Specified QoS Settings.
The following examples show how to inherit from other profiles:
Inheritance Example 1:
<qos_library name=”Library”>
<qos_profile name="BaseProfile"> <datawriter_qos> ... </datawriter_qos> <datareader_qos> ... </datareader_qos> </qos_profile>
<!-- use the base_name attribute to inherit from another profile -->
<qos_profile name="DerivedProfile" base_name="BaseProfile"> <datawriter_qos> <batch>
<enable>true</enable>
<max_samples>100</max_samples>
<max_data_bytes>LENGTH_UNLIMITED</max_data_bytes>
</batch>
</datawriter_qos> <datareader_qos> ... </datareader_qos> </qos_profile> </qos_library>
In this example, the QoS Profile called DerivedProfile is constructed via inheritance from the QoS Profile BaseProfile. The profile DerivedProfile inherits BaseProfile by referencing the base profile in the qos_profile attribute base_name="BaseProfile". This means that the datawriter_qos and datareader_qos in DerivedProfile inherit their values from the corresponding datawriter_qos and datareader_qos in BaseProfile. The QoS Profile DerivedProfile first initializes all its QoS policies with the values obtained from BaseProfile. Then it applies any QoS policies explicitly listed in its own definition to override the initialized values. In this example, MyDerivedProfile only modifies the BatchQos policy on the DataWriter QoS.
If a QoS Profile definition does not specify the base_name attribute, then it is initialized from the builtin defaults provided by Connext DDS. See 19.3.1 Built-in QoS Profiles.
Inheritance Example 2:
<qos_library name=”Library”>
<datareader_qos name="BaseProfile"> ... </datareader_qos> <datareader_qos name="DerivedProfile" base_name="BaseProfile" ... </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 QoS Profile definition with a single QoS.
Inheritance 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. Since Profile2 doesn’t inherit from any other QoS Profile, the datareader_qos values will be taken from the builtin defaults. See 19.3.1 Built-in QoS Profiles.
Inheritance 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> </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.
Inheritance 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.
Inheritance Example 6:
Global_QoS.xml
<qos_library name="GlobalLibrary">
<qos_profile name="GlobalProfileA">
</qos_profile
</qos_library>
Component_QoS.xml
<qos_library name="ComponentLibrary">
<qos_profile name="ComponentProfileA" basename="GlobalLibrary::GlobalProfileA">
</qos_profile
</qos_library>
Previous examples show that a QoS Profile or QoS can inherit values from other QoS Profiles or QoSes, which should already be loaded. In this example, a QoS Profile inherits values from another QoS Profile defined in a separate QoS Library, in another file. This is a typical use case where QoSes are constructed by separating them into multiple files. In this example, Global_QoS.xml has to be loaded before Component_QoS.xml.
To learn more about how to load multiple files in your application, see 19.5 How to Load XML-Specified QoS Settings.
While useful, initializing a QoS Profile from a single base QoS Profile can also be limiting. For example, assume you have the configuration shown in Figure 19.1: Single Inheritance Example
Figure 19.1: Single Inheritance Example
If you wanted to incorporate monitoring into the QoS Profiles app_1 and app_2, the only option with inheritance would be to create two new QoS Profiles, each inheriting from app_1 and app_2 respectively, and to copy the monitoring XML configuration into each of the two new QoS Profiles as shown in Figure 19.2: Duplication of Configuration in Inheritance. This results in significant XML code duplication and leads to maintainability issues.
Figure 19.2: Duplication of Configuration in Inheritance
The following section describes how to handle the above scenario using QoS Profile composition.
QoS Profile composition uses QoS Snippets to more easily update profiles that you use or inherit. QoS Snippets are small pieces of well-formed, reusable XML QoS that configure a single aspect of QoS, such as enabling monitoring or security.
In the previous example, you could add the monitoring configuration to the new QoS Profiles app_mon_1 and app_mon_2 by referring to a QoS Snippet that configures monitoring.
Figure 19.3: One Reusable Configuration Snippet
QoS Snippets are intended to be composed into other QoS Snippets and QoS Profiles. As shown in the example below, the syntax used to define a QoS Snippet is the same as that of a QoS Profile, but the intent and usage are different.
The following is an example of the syntax used to define and use QoS Snippets.
Composition Example 1:
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet1">
<datareader_qos>
<reliability>
<kind>RELIABLE_RELIABILITY</kind>
</reliability>
</datareader_qos>
</qos_profile>
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet2">
<datareader_qos>
<durability>
<kind>TRANSIENT_LOCAL_DURABILITY</kind>
</durability>
</datareader_qos>
</qos_profile>
<qos_profile name="Profile1">
<datawriter_qos>
<publication_name>
<name>SampleDataWriter_A</name>
</publication_name>
</datawriter_qos>
</qos_profile>
<!-- This QoS Profile definition uses the Snippets -->
<qos_profile name="MyDerivedAndComposedProfile" base_name="Profile1">
<base_name>
<element>Snippet1</element>
<element>Snippet2</element>
</base_name>
<datareader_qos>
<history>
<kind>KEEP_LAST_HISTORY_QOS</kind>
<depth>6</depth>
</history>
</datareader_qos>
</qos_profile>
In this example, a QoS Profile inherits from another QoS Profile and uses composition to weave in policies from two QoS Snippets. Specifically, MyDerivedAndComposedProfile is constructed by inheriting from Profile1, then by overlaying Snippet1 and Snippet2. Finally, MyDerivedAndComposedProfile applies its own QoS policies, which overwrite any others. See also 19.3.3.2.2 Order and Precedence of Inheritance.
It is recommended to use fully qualified names in the element tag if there is ambiguity in the QoS Profile or QoS Snippet names you have loaded in your application.
The process of inheriting QoS Profiles and composing from QoS Snippets works as follows:
You inherit a QoS Profile, but overlay one or more Qos Snippets. Inherit a QoS Profile because you want to subsume the complete definition of the QoS policies for a particular use case. Overlay QoS Snippets onto a QoS Profile so that you override only a single aspect of QoS: for instance, only what is logically associated with monitoring.
Values are inherited from the specified elements in the <base_name> tag, in order from top to bottom. Values inherited from elements lower in the order (Snippet2 in the examples) will overwrite the same values (if present) from elements higher up (Snippet1 in the examples). Remember that the QoS, QoS Profile, or QoS Snippet should already be loaded as a part of your XML file. (See 19.5 How to Load XML-Specified QoS Settings.)
In the following example, MyDerivedAndComposedProfile inherits from Profile1, keeping Profile1's SampleDataWriter_A but getting <durability> and <reliability> from the Snippets rather than from Profile1. Finally, MyDerivedAndComposedProfile applies its own local <history> policies.
Composition Example 2:
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet1">
<datareader_qos>
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
</reliability>
</datareader_qos>
</qos_profile>
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet2">
<datareader_qos>
<durability>
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
</durability>
</datareader_qos>
</qos_profile>
<qos_profile name="Profile1">
<datawriter_qos>
<publication_name>
<name>SampleDataWriter_A</name>
</publication_name>
</datawriter_qos>
<datareader_qos>
<durability>
<kind>VOLATILE_DURABILITY_QOS</kind>
</durability>
<reliability>
<kind>BEST_EFFORT_RELIABILITY_QOS</kind>
</reliability>
</datareader_qos>
</qos_profile>
<!-- This QoS Profile definition uses the Snippets -->
<qos_profile name="MyDerivedAndComposedProfile" base_name="Profile1">
<base_name>
<element>Snippet1</element>
<element>Snippet2</element>
</base_name>
<datareader_qos>
<history>
<kind>KEEP_LAST_HISTORY_QOS</kind>
<depth>6</depth>
</history>
</datareader_qos>
</qos_profile>
The final values in MyDerivedAndComposedProfile will be as follows (map the colors in the example to what actually gets used), as shown here:
<qos_profile name="MyDerivedAndComposedProfile">
<datareader_qos>
<reliability>
<kind>RELIABLE_RELIABILITY</kind>
</reliability>
<history>
<kind>KEEP_LAST_HISTORY_QOS</kind>
<depth>6</depth>
</history>
<durability>
<kind>TRANSIENT_LOCAL_DURABILITY</kind>
</durability>
</datareader_qos>
<datawriter_qos>
<publication_name>
<name>SampleDataWriter_A</name>
</publication_name>
</datawriter_qos>
</qos_profile>
Composition Example 3
Imagine that Example 2 had the following Snippets instead:
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet1">
<datawriter_qos>
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
<max_blocking_time>
<sec>5</sec>
<nanosec>0</nanosec>
</max_blocking_time>
</reliability>
</datawriter_qos>
</qos_profile>
<!-- This is a QoS Snippet -->
<qos_profile name="Snippet2">
<datawriter_qos>
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
<max_blocking_time>
<nanosec>1000000</nanosec>
</max_blocking_time>
</reliability>
</datawriter_qos>
</qos_profile>
<!-- This QoS Profile definition uses the Snippets -->
<qos_profile name="MyDerivedAndComposedProfile" base_name="Profile1">
<base_name>
<element>Snippet1</element>
<element>Snippet2</element>
</base_name>
</qos_profile>
In this example, Snippet2's nanosec overwrites Snippet1's. But since Snippet2 does not specify a sec, Snippet1's sec is used. The resultant QoS is a combination of the two reliability policies:
<!-- The above example combines the reliability settings because one QoS Snippet is overlaid on the other -->
<reliability>
<kind>RELIABLE_RELIABILITY_QOS</kind>
<max_blocking_time>
<sec>5</sec>
<nanosec>1000000</nanosec>
</max_blocking_time>
</reliability>
Imagine now that the QoS Snippets in the above example were reversed, and Snippet2 was listed first in the file. Snippet2 would apply a nanosec of 1000000; then Snippet1 would overwrite that with 0 and apply its sec of 5. The result would be a max_blocking_time of 5 seconds and 0 nanoseconds.
You can use the rtixmloutpututility utility to see what the final QoS values will be in your system when composition and inheritance complete their derivations. See 19.3.3.5 Viewing Resolved QoS Values.
Composition Example 4:
If you specify <base_name> for a QoS Profile and also specify <base_name> for a QoS within it, the <base_name> tag or attribute in the QoS will take precedence. That is, <base_name> from the QoS Profile will be ignored for the QoS specifying its own <base_name>.
The following example illustrates this concept:
<dds>
<qos_library>
<qos_profile name=”ParentProfile”>
<base_name>
<element>A</element>
<element>B</element>
</base_name>
...
<datawriter_qos name=”DW_QoS”>
<base_name>
<element>C</element>
<element>D</element>
</base_name>
</datawriter_qos>
</qos_profile>
</qos_library>
</dds>
In this example, since DW_QoS has its own list for the <base_name> tag, DW_QoS will only inherit values from C and D. It will NOT inherit anything from A and B specified as a part of ParentProfile, since its own <base_name> tag overrides it.
XML QoS Profile inheritance and composition provide a powerful way to define configurations, allowing flexibility and reusability. It is important to understand the underlying mechanics and follow the best practices described below to maximize usability and avoid unexpected results.
These best practices are illustrated in the following figure and further described in the sections that follow.
Figure 19.4: Best Practices for Inheritance and Composition
In Figure 19.4: Best Practices for Inheritance and Composition, imagine the results produced by the dotted box, as already illustrated in the previous examples. These results are inherited by QoS Profile 3. QoS Profile 3's snippets are then applied. (QoS Snippet 5 inherits from two other snippets first.) Finally, any policies in QoS Profile 3 that differ from the results produced by the inheritance from profiles 1 and 2 are applied.
Another way to look at Figure 19.4: Best Practices for Inheritance and Composition is as a tree whose nodes are applied in this order, where "QP" refers to the QoS Profiles in the figure and "QS" refers to the QoS Snippets in the figure:
When defining a QoS Profile, decide whether you are:
These two options are fundamentally different.
A QoS Profile is intended to define the QoS policies used to create a DDS Entity. Therefore, it should match a specific application use case (e.g., sending alarms or streaming periodic data). Moreover, because the QoS Profile will be used to create a DDS Entity, it implicitly defines values for all the QoS Policies that apply to the entity.
When defining a QoS Profile, choose the builtin QoS Profile that most closely matches your use case. Use that builtin QoS Profile as a base profile. For example:
<qos_profile name="MyProfile" base_name="BuiltinQosLibExp::Pattern.AlarmEvent">
<!-- modify the profile by composing with QoS Snippets -->
<!-- modify the profile by overriding the QoS policies explicitly -->
</qos_profile>
Give the QoS Profile a name that makes clear its intended use, as well as the fact that it is a QoS Profile (instead of a QoS Snippet). For example, use “Profile” as a suffix in the name of the QoS Profile or some other consistent naming convention.
A QoS Snippet is intended as a generic block of QoS policies for composition into QoS Profiles and other QoS Snippets. For example, configuring monitoring, configuring Security, and configuring a FlowController are good uses for QoS Snippets.
QoS Snippets should focus on a single aspect of QoS policy and try not to set unrelated policies. This maximizes composability, avoiding interfering with policies set by other QoS Snippets.
QoS Snippets should be generic and reusable across systems and deployments. Therefore, it does not make sense to constrain their applicability using the <topic_filter> element within their definition. Doing so may also result in conflict with topic filters set on QoS Profiles that use those QoS Snippets.
Give the QoS Snippet a name that makes clear its intended use, as well as the fact that it is a QoS Snippet (not a regular QoS Profile). For example, use “Snippet” as a suffix in the name of the QoS Snippet or some other consistent naming convention.
Aside from its use for creating DDS Entities, a QoS Profile may be used as the base definition of another QoS Profile. For example:
<qos_profile name="MyDerivedProfile" base_name="MyBaseProfile">
...
</qos_profile>
When used for inheritance, the derived profile is initialized with the policies of the base profile.
A profile should never be used for composition. That is, it should not be referenced within the <base_name> element:
<qos_profile name="MyDerivedProfile">
<base_name>
<element>MyBaseProfile</element> <!-- never do this -->
</base_name>
</qos_profile>
Because a QoS Profile implicitly defines all the QoS policies, using it for composition would have the unintended effect of potentially overriding all the policies.
QoS Snippets are small pieces of well-formed XML QoS intended to configure a single aspect of a QoS. The proper way to use them is for the composition of other QoS Profiles and QoS Snippets. Therefore, they must only appear within the <base_name> tag "element." For example:
<qos_profile name="MyComposedProfile" base_name="MyBaseProfile">
<base_name>
<element>Snippet1</element>
<element>Snippet2</element>
<element>Snippet3</element>
</base_name>
...
</qos_profile>
<qos_profile name="MyComposedSnippet">
<base_name>
<element>Snippet1</element>
<element>Snippet2</element>
<element>Snippet3</element>
</base_name>
...
</qos_profile>
Do not use a QoS Snippet for inheritance. For example
<!-- do not do this -->
<qos_profile name="MyComposedProfile" base_name="Snippet1">
<base_name>
<element>Snippet2</element>
<element>Snippet3</element>
</base_name>
...
</qos_profile>
If you use a QoS Snippet for inheritance (i.e., for initializing another Qos Profile), you are using something that was not intended to be a full definition; thus, it may overlook the proper configuration of certain policies for your system.
The QoS configuration of DDS Entities can be specified using QoS Profiles. This is a convenient mechanism that allows separation of configuration from the functional logic of your application.
The Connext DDS API contains several operations that reference QoS Profiles by name, such as create_participant_with_profile() and create_topic_with_profile(). These operations are used to either create DDS Entities with the QoS policies referenced by the profile name, or to initialize the Entity QoS structure with the QoS policies referenced by the profile. Either way, these operations should not be called using a QoS Snippet name as the reference.
QoS Snippets should be developed with reuse in mind and should not use the <topic_filter> element within the definition of the QoS Snippet.
<!-- do not do this -->
<qos_profile name="MySnippet">
<datawriter_qos topic_filter="Alarm">
<reliability>
<kind>RELIABLE_RELIABILITY</kind>
</reliability>
</datawriter_qos>
<datawriter_qos topic_filter="SensorUpdate">
<reliability>
<kind>BEST_EFFORTS_RELIABILITY</kind>
</reliability>
</datawriter_qos>
...
</qos_profile>
The <topic_filter> element conditionally defines the QoS Profile depending on the Topic name associated with the Entity being created or configured. Since the QoS Snippet is not intended to create or configure DDS Entities directly, it does not make sense to use the <topic_filter> element in its definition.
Connext DDS uses the same syntax for the creation of QoS Profiles and QoS Snippets. Therefore, it does not enforce the conventions described here. Although Connext DDS will not detect or prevent violation of these conventions (e.g., if you use a QoS Profile for composition), following these conventions is strongly encouraged to avoid unexpected results. Furthermore, future versions of Connext DDS may introduce different syntax that allows differentiating QoS Profiles from QoS Snippets and enforces the conventions. If you follow these conventions now, you can continue using them without violating future syntax.
The final value for a QoS configuration, especially when using inheritance and QoS Snippet composition, can be visualized at runtime in a variety of ways:
The logged QoS when using logging, or the to_string functions, will show only the QoS settings that are different from the documented default (several to_string overloads can override this behavior). The documented default refers to the default value of a policy as specified by the API reference HTML documentation.
Here is an example of a to_string function in the Modern C++ API:
using namespace rti::all;
DataWriterQos the_qos = writer.qos();
// Obtain a string representation of the DataWriterQos object
// Only differences with respect to the documented default will be included
std::string the_string = to_string(the_qos);
// Create another DataWriterQos object and change some policies
DataWriterQos other_qos;
other_qos << Reliability::BestEffort();
// The differences with respect to the other_qos object will now be stored to the string
the_string = to_string(the_qos, other_qos);
// Finally, we can print the entire QoS object (not just differences)
the_string = to_string(the_qos, rti::core::qos_print_all);
For older releases, or where code change/recompilation isn't possible, you can use rtixmloutpututility to visualize the end result of your QoS settings at entity creation time.
rtixmloutpututility allows you to see the final QoS values your entities will receive after inheritance and composition are resolved. Here is an example usage of this utility:
$ ./rtixmloutpututility
-qosFile '/home/xxx/Documents/Tests/CORE-9446/USER_QOS_PROFILES.xml;/home/xxx/Documents/Tests/CORE-1375/USER_QOS_PROFILES.xml'
-profilePath Data_Library::Data_Profile
-outputFile Dummy.txt
-qosTag domain_participant_qos/property
To get this utility, including more information about its options and usage, please see: https://github.com/rticommunity/rticonnextdds-xml-output-utility.
© 2021 RTI