Partitioning#

Introduction#

What you’ll learn

In this module, you’ll learn how to scale your Connext distributed system using different partitioning strategies.

You will explore:

  • Domain partitioning based on domain IDs and domain partitions

  • Publisher and subscriber partitions

  • Data partitioning with content filtering

  • Connecting different partitions with Routing Service

Most modules in this guide asked you to create simple systems in which all applications can communicate with each other. But as your system grows, you need to consider how to partition it so it can scale.

We’ll start with the simplest way to partition a system: by domain. Then we’ll look at a more flexible and dynamic approach based on partition names. We’ll also explore how content filtering allows data partitioning. Finally, we’ll use Routing Service to implement a layered databus in which different partitions exchange some data.

How to complete this module#

To complete this module, you’ll need the following:

  • 20-30 minutes

  • Some familiarity with the basic concepts introduced in the Publish-Subscribe module.

To complete the optional exercises, you’ll need the following:

  • A Connext installation (full installation). See Get Started.

  • A text editor or IDE to write code and configuration.

1. Domain partitioning#

Domain IDs#

When you write a Connext application, the first entity you usually create is a DomainParticipant.

The most basic way to partition a system is by creating DomainParticipants on different domains (identified by the domain ID):

Application 1#
participant1 = dds.DomainParticipant(domainId: 1)
Application 2#
participant2 = dds.DomainParticipant(domainId: 2)

DomainParticipants on different domains don’t communicate with each other in any way. Any Topic you create on participant1 will be invisible to participant2.

Domain IDs are assigned when you create a DomainParticipant and can’t be changed later.

Domain partitions#

Domain IDs have several limitations:

  • They only offer a limited range of values because they map to physical ports.

  • As integers, they are hard to manage at scale.

  • They can’t be changed at runtime.

Domain partitions allow restricting communication between DomainParticipants created on the same domain and don’t have the limitations of domain IDs.

A DomainParticipant can belong to multiple partitions, providing a more flexible way to partition your system. When two DomainParticipants belong to at least one common partition, they can communicate with each other.

For example, Application 1 and Application 2, configured as follows, can communicate with each other (assuming they run on the same domain ID) because they share the partition “USA/CA”:

USER_QOS_PROFILES.xml (Application 1)#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <qos_library name="MyLibrary">
        <qos_profile name="MyProfile" is_default_qos="true">
            <participant_qos>
                <partition>
                    <name>
                        <element>USA/CA</element>
                        <element>USA/OR</element>
                    </name>
                </partition>
            </participant_qos>
        </qos_profile>
    </qos_library>
</dds>
USER_QOS_PROFILES.xml (Application 2)#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <qos_library name="MyLibrary">
        <qos_profile name="MyProfile" is_default_qos="true">
            <participant_qos>
                <partition>
                    <name>
                        <element>USA/CA</element>
                    </name>
                </partition>
            </participant_qos>
        </qos_profile>
    </qos_library>
</dds>

If Application 2 later changes its partition, it would stop communicating with Application 1:

application2.py#
# ...
qos = participant.qos
qos.partition.name = ["USA/TX"]
participant.qos = qos

Partition names support wildcards. For example, Application 3 can declare interest in communicating with all partitions in the USA as follows:

USER_QOS_PROFILES.xml (Application 3)#
...
<participant_qos>
    <partition>
        <name>
            <element>USA/*</element>
        </name>
    </partition>
</participant_qos>

Note that DomainParticipants on different partitions still exchange some discovery traffic, unlike DomainParticipants on different domains.

Connext also provides Domain Tags, which are a lightweight alternative to partitions. They solve the limitations of domain IDs, but can’t be changed after the DomainParticipant is created.

2. Publisher and subscriber partitions#

Publisher and subscriber partitions allow isolating publishers and subscribers on the same Topic. They work the same way as domain partitions, but at the level of individual publishers and subscribers, allowing more fine-grained control.

These partitions can be configured as follows:

USER_QOS_PROFILES.xml#
<?xml version="1.0" encoding="UTF-8"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://community.rti.com/schema/current/rti_dds_profiles.xsd">
    <qos_library name="MyLibrary">
        <qos_profile name="MyProfile" is_default_qos="true">
            <publisher_qos>
                <partition>
                    <name>
                        <element>USA/CA</element>
                    </name>
                </partition>
            </publisher_qos>

            <subscriber_qos>
                <partition>
                    <name>
                        <element>USA/OR</element>
                    </name>
                </partition>
            </subscriber_qos>
        </qos_profile>
    </qos_library>
</dds>

The following optional exercise shows how to configure a publisher and a subscriber for the same Topic but on different partitions. It also demonstrates how the application can change the partitions at runtime.

Partition QoS code example (optional)

This example is available on RTI’s GitHub examples repository under examples/connext_dds/partitions.

The example creates a publisher and subscribers for the same arbitrary Topic, initially with a matching partition, configured as follows:

USER_QOS_PROFILES.xml (excerpt)#
<qos_library name="partitions_Library">
    <qos_profile name="partitions_Profile" is_default_qos="true">

        <publisher_qos>
            <partition>
                <name>
                    <element>ABC</element>
                    <element>foo</element>
                </name>
            </partition>
        </publisher_qos>

        <subscriber_qos>
            <partition>
                <name>
                    <element>ABC</element>
                    <element>X*Z</element>
                </name>
            </partition>
        </subscriber_qos>
        <!-- ... -->
    </qos_profile>
</qos_library>

The publisher then periodically updates the partitions at runtime:

publisher_qos = self.publisher.qos
publisher_qos.partition.name = ["bar"]
self.publisher.qos = publisher_qos

The partition updates cause the subscriber to stop receiving data or to start receiving it again.

See the README.md files under examples/connext_dds/partitions for instructions to run the example.

3. Data partitioning with content filtering#

Content filtering is a flexible and scalable way to partition the data space within a Topic.

With a content filter, each subscriber only receives the data that matches the filter. Moreover, publishers will attempt to send each Topic update only to those subscribers for which the data passes their filter. Writer-side filtering helps scale the number of applications and the data volume by reducing CPU usage and network traffic.

In the home automation example in the Publish-Subscribe module, you can partition the data for the “WindowStatus” Topic by room name in the subscriber applications:

subscriber1.py#
# ...
topic = dds.Topic(participant, "WindowStatus", DeviceStatus)
content_filtered_topic = dds.ContentFilteredTopic(
    topic,
    "LivingRoomFilter",
    dds.Filter("room_name = 'LivingRoom'")
)

This subscriber only receives updates where the field room_name equals "LivingRoom".

You can add a second subscriber for room_name='Kitchen'.

subscriber2.py#
# ...
topic = dds.Topic(participant, "WindowStatus", DeviceStatus)
content_filtered_topic = dds.ContentFilteredTopic(
    topic,
    "BedroomFilter",
    dds.Filter("room_name = 'Bedroom'")
)

Adding new subscribers scales thanks to writer-side filtering, because publishers will only send a “WindowStatus” update to the subscribers with a matching room_name.

We discuss this topic in more detail in the Content Filtering module.

4. Layered databus with Routing Service#

When you partition your system, you may still need some communication between partitions.

Routing Service is a flexible tool that allows defining routes between different Connext subsystems, creating a layered databus. Data routed through Routing Service can be transformed, filtered, or aggregated. Applications that communicate through Routing Service do so transparently.

For example, say you have two separate domains (domain 1 and domain 2) with several Topics each. Now you want updates for one of the Topics on domain 1 (let’s say “Temperature”) to be also available on domain 2, while keeping everything else isolated. You can do that seamlessly with Routing Service.

Routing Service Topic Bridge

The following is a simple configuration file for Routing Service that forwards all updates for “Temperature” from domain 1 to domain 2:

my_router.xml#
<?xml version="1.0"?>
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/current/rti_routing_service.xsd">
  <routing_service name="MyRouter">
    <domain_route name="DomainRoute">
      <participant name="domain1">
        <domain_id>1</domain_id>
      </participant>
      <participant name="domain2">
        <domain_id>2</domain_id>
      </participant>
      <session name="Session">
        <auto_topic_route name="TemperatureTopic">
          <input participant="domain1">
            <allow_topic_name_filter>Temperature</allow_topic_name_filter>
            <allow_registered_type_name_filter>*</allow_registered_type_name_filter>
          </input>
          <output participant="domain2">
            <allow_topic_name_filter>Temperature</allow_topic_name_filter>
            <allow_registered_type_name_filter>*</allow_registered_type_name_filter>
          </output>
        </auto_topic_route>
      </session>
    </domain_route>
  </routing_service>
</dds>

And you can run it as follows:

<install dir>/bin/rtiroutingservice -cfgFile my_router.xml -cfgName MyRouter

In the following optional exercise, you’ll create the Topics in the XML example above and see how Routing Service forwards the “Temperature” topic from domain 1 to domain 2.

Run the Routing Service example (optional)

To simulate the publisher and subscriber applications, you’ll run rtiddsping, a command-line tool that can publish and subscribe to a Topic with a specified name. (In this example, the data type is an integer that increases in value, and not an actual temperature or device status, but that’s not important.)

You’ll need six terminals to run the example. First, set up the environment on each terminal.

Setting up the environment for Connext

Use the following script to configure your shell environment variables to run Connext executables and load dynamic libraries.

If you’re using Bash, run this:

$ source <installdir>/resource/scripts/rtisetenv_<architecture>.bash

If you’re using the Z shell, run this:

$ source <installdir>/resource/scripts/rtisetenv_<architecture>.zsh

If you’re using Bash, run this:

$ source <installdir>/resource/scripts/rtisetenv_<architecture>.bash

If you’re using the Z shell, run this:

$ source <installdir>/resource/scripts/rtisetenv_<architecture>.zsh

If you’re using the tcsh shell, run this:

$ source <installdir>/resource/scripts/rtisetenv_<architecture>.tcsh
> <installdir>\resource\scripts\rtisetenv_<architecture>.bat

When a directory name has a space, enclose the path in quotation marks. For example: "C:\Program Files\rti_connext_dds-<version>\resource\scripts\rtisetenv_x64Win64VS2017.bat".

<installdir> refers to the installation directory for Connext.

And run each command in a separate terminal:

$ rtiddsping -domain 1 -topic Temperature -pub
$ rtiddsping -domain 1 -topic Temperature -sub
$ rtiddsping -domain 1 -topic DeviceStatus -pub
$ rtiddsping -domain 1 -topic DeviceStatus -sub
$ rtiddsping -domain 2 -topic Temperature -sub

You should see subscribers on domain 1 receiving data, since each has a corresponding publisher on domain 1. But the subscriber on domain 2 doesn’t receive any data.

Now run Routing Service with the previous configuration file:

$ rtiroutingservice -cfgFile my_router.xml -cfgName MyRouter

You should see the “Temperature” subscriber on domain 2 start receiving data. If you kill the Routing Service process, the subscriber on domain 2 will stop receiving data.

Learn more#

This module introduced the different ways you can partition your system to scale it, including a basic use case for Routing Service.

Next Steps

Related modules:

  • Discovery. Learn the basics of the discovery process and run applications on different machines.

  • Content Filtering. Content filtering is a way to partition your data space and scale your system.

  • Debugging. Use command-line and graphical tools to debug why your applications don’t communicate.

More about partitioning and isolating applications:

More about Routing Service:

Was this page helpful?

Back to Learn