19.3.3 QoS Profile Inheritance and Composition

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.

19.3.3.1 QoS Profile Inheritance

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.

19.3.3.1.1 Limitations of QoS Profile Inheritance

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.

19.3.3.2 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.

19.3.3.2.1 How Inheritance and Composition Work Together

The process of inheriting QoS Profiles and composing from QoS Snippets works as follows:

  1. The QoS policies are initialized from those in the base profile, using the base_name attribute of the <qos_profile> tag. If the base_name attribute is not present, then the policies are initialized from the builtin defaults defined by Connext DDS.
  2. The policies are overridden with those defined in the QoS Snippets listed inside the <base_name> XML tag. The QoS Snippets are applied in the order in which they appear. So the first QoS Snippet (Snippet1 in the example above) overrides the policies that were set from the inherited base QoS Profile (Profile1 in the example), the second QoS Snippet (Snippet2 in the example) overrides whatever was the result of applying Snippet1, and so on.
  3. The policies that appear explicitly as elements in the QoS Profile are applied. These override the policies set by the base QoS Profile and the QoS Snippets. In this example, a KEEP_LAST_HISTORY_DEPTH of 6 overrides whatever was set by the base QoS Profile and the QoS Snippets.

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.

19.3.3.2.2 Order and Precedence of Inheritance

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.

19.3.3.3 Best Practices for Inheritance and Composition

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:

  1. QP1 (because inheritance says we start all the way back at the first inherited profile)
  2. QS1, then QS2 (because snippets are overlaid next)
  3. QP2 (because it may have deltas that overwrite what has been composed so far)
  4. QS3, then QS4 (because QS5 inherits from QS3 and QS4 first)
  5. QS5 (because it may have deltas that overwrite QS3 and QS4)
  6. QP3 (because it may have deltas that overwrite everything composed so far)
19.3.3.3.1 Differentiate between QoS Profiles and QoS Snippets

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.

19.3.3.3.2 Use QoS Profiles for inheritance only, never composition

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.

19.3.3.3.3 Use QoS Snippets for composition, never inheritance

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.

19.3.3.3.4 Use QoS Profiles, not QoS Snippets, to create DDS Entities

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.

19.3.3.3.5 Keep QoS Snippets generic and reusable

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.

19.3.3.4 Enforcement of QoS Profile and QoS Snippet Conventions

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.

19.3.3.5 Viewing Resolved QoS Values

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:

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