.. include:: vars.rst .. _section-core-concepts: Core Concepts ************* This section aims to provide a deeper understanding of what |CDS| is made of, to give you the required insight to configure and use it effectively. You will learn about: - :ref:`Domain lists `: Sets the context for |CDS| in DDS domains. - :ref:`Transport `: Shows how the transports are modeled in |CDS|, which is the basis for communication with |DPs|. - :ref:`Forwarder `: Explains the active logic of |CDS| that is in charge of processing the incoming participant announcements, building the system discovery state, and providing the information to all |DPs|. - :ref:`Database `: Describes the collection of discovered |DP| information that |CDS| maintains to represent the current state of the system. .. _section-domain-lists: Domain Lists ============ A single |CDS| can forward participant announcements in *multiple DDS domains*. Yet |CDS| preserves the isolation of *DDS domain IDs* such that a |DP| only discovers other |DPs| with the same *DDS domain ID*. .. figure:: static/CdsMultiDomain.svg :figwidth: 100 % :alt: Cloud Discovery Service works with Multiple Domains :name: FigureCdsMultiDomain :align: center Cloud Discovery Service works with Multiple Domains By providing a *domain list*, you can control on which domains |CDS| operates. A domain list is a representation of domains by their IDs. You can learn more about configuring domain lists in :ref:`section-config-domain-list`. Support for multiple domain IDs can simplify system deployments, allowing a few |CDS| instances to provide discovery services for all possible expected domain IDs, even if all these domain IDs are not known in advance. .. _section-domain-list-tags: Domain Tags ----------- In large-scale systems or multi-tenant networks, such as those found in *Cloud* deployments, the isolation provided by the DDS domain ID may not be sufficient: * The maximum number of domain IDs is limited and not sufficiently large. In deployments that require many independent DDS systems in a common network, the domain ID would not be sufficient to provide isolation. * Managing the assignment of numeric domain IDs to independent systems or projects can be cumbersome. Numeric IDs cannot easily leverage existing organizational or project boundaries such as project name, department, organization, etc. For these reasons, the DDS domain concept has been extended to include *Domain Tags*. A domain tag is a logical sub-division within a domain. It is defined as a string. |DPs| associated to different domain tags will not discover each other even if they are in the same *domain ID*. You can think of a *Domain Tag* as the DDS equivalent of a network VLAN. :numref:`FigureDomainTag` shows an overview of the domain tag concept. .. figure:: static/DomainTags.svg :figwidth: 100 % :alt: Domain Tags :name: FigureDomainTag :align: center Domain Tags A |DP| can be associated with only one domain tag. In a given domain, all the |DPs| may exchange participant announcements in order to initiate the discovery process between them. However, only |DPs| that have **the same domain tag** will discover each other. Domain tags allow you to divide your domain into as many logically isolated spaces as you need. A domain tag is represented by a string *tag name*. You can define as many tags as you need. The tag name may include any ASCII character. .. note:: The absence of a domain tag is treated like a special value of the tag. |DPs| that do not specify a domain tag will communicate only with each other. Characteristics of domain tags: 1. **Single Tag per DomainParticipant**: A |DP| can be associated with a single domain tag. 2. **Immutability**: Domain tags are immutable. |DPs| specify the domain tag at creation time and it cannot be modified. |CDS| supports domain tags and forwards discovery information only to |DPs| with matching tuple of *(domainID, domainTag)*. :numref:`FigureCdsDomainTag` shows the domain tags' effect when |CDS| drives the discovery process. .. figure:: static/CdsDomainTags.svg :figwidth: 100 % :alt: |CDS| with Domain Tags :name: FigureCdsDomainTag :align: center |CDS| with Domain Tags |CDS| detects the domain tag of each |DP| and remembers that association. That way it can forward the participant announcements only to those |DPs| that belong to the same domain ID and tag. You can specify the domain tag for a |DP| at creation time via *DomainParticipantQos*. In particular, you need to propagate the well-known property ``dds.domain_participant.domain_tag``, whose value contains the name of the tag associated with the |DP|. You can specify this property through the *PropertyQosPolicy*. For more information, see :link_connext_dds_pro_um:`Choosing a Domain Tag <#users_manual/ChoosingDomainTag.htm>` in |CORE_MANUAL|. Specifying the Domain Tag in XML ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following XML snippet shows how to specify the domain tag within ````. .. code-block:: xml dds.domain_participant.domain_tag TagX true .. _section-domain-list-partitions: |DP_PARTITIONs_HEADING| ----------------------- |DP_PARTITIONs| provide a way to create different communication planes within a *(domain ID, domain Tag)*. |DPs| can join and leave these communication planes at any time while they are running. Partitioning at the |DP| level can be particularly useful in large, WAN, distributed systems (with thousands of |DPs|) in which not all |DPs| need to know about each other at any given time. Partitioning at the |DP| level helps reduce network, CPU, and memory utilization, because |DPs| without matching partitions will not exchange information about their |DWs| and |DRs|. To summarize, the characteristics of |DP_PARTITIONs| are: 1. **Mutable**: A |DP| can change its partition values at runtime. This allows flexibility within a running system. 2. **Multiple Values**: A |DP| can contain multiple values including wildcard characters for its partitions. This allows a |DP| to be a member of multiple partitions simultaneously. 3. **Efficient**: They are more efficient than *Endpoint Partitions*, since traffic is restricted only until the *Participant Discovery* level. No *Endpoint Discovery* takes place if partition values for two |DPs| don't match. |CDS| supports systems that utilize |DP_PARTITIONs|. It forwards announcements to |DPs| with matching tuple of *(domainID, domainTag)* **and** matching |DP_PARTITIONs|. :numref:`FigureCdsDomainParticipantPartitions` shows the |DP_PARTITIONs|' effect when |CDS| drives the discovery process. .. figure:: static/CdsDomainParticipantPartitions.svg :figwidth: 100 % :alt: |CDS| with |DP_PARTITIONs_HEADING| :name: FigureCdsDomainParticipantPartitions :align: center |CDS| with |DP_PARTITIONs_HEADING| |CDS| detects the |DP_PARTITIONs| of each |DP| and maintains a mapping for its matching |DPs|. That way it can forward the participant announcements only to those |DPs| that pass the matching criteria highlighted above. When a |DP| changes its |DP_PARTITIONs| value, CDS updates the mapping for the new value before forwarding the incoming participant announcement. You can specify the |DP_PARTITIONs| for a |DP| at creation time via *DomainParticipantQos* in its *Partition QoS Policy*. For XML, you need to set the ```` tag, whose value contains the list of partitions associated with the |DP|. You can also change the value for |DP_PARTITIONs| at runtime while the |DP| is running. For more information, see :link_connext_dds_pro_um:`Domain Participant Partitions <#users_manual/Creating_ParticipantPartitions.htm>` in |CORE_MANUAL|. .. note:: All |DPs| are members of at least one concrete partition. If a |DP| does not specify any partition or if it only specifies wildcard partitions, the |DP| is considered a member of the empty partition. The absence of a partition tag is treated like a special value (empty partition). |DPs| that do not specify a partition tag will communicate only with each other. Specifying |DP_PARTITIONs_HEADING| in XML ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following XML snippet shows how to specify the partition values within ````. .. code-block:: xml US A* Changing |DP_PARTITIONs_HEADING| in code ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following code snippet shows how to change the |DP_PARTITIONs| value at runtime via code. .. tabs:: .. group-tab:: C .. code-block:: c DDS_DomainParticipant *participant = NULL; struct DDS_DomainParticipantQos participantQos = DDS_DomainParticipantQos_INITIALIZER; /* QoS configuration happens here */ /* Create the participant */ participant = DDS_DomainParticipantFactory_create_participant( DDS_TheParticipantFactory, domainId, &participantQos, NULL, DDS_STATUS_MASK_NONE); if (participant == NULL) { /* Handle error */ } /* Participant is now up and running */ /* Get the QoS */ if (DDS_DomainParticipant_get_qos(participant, &participantQos)) != DDS_RETCODE_OK) { /* Handle error */ } /* Set the sequence length and values */ if (!DDS_StringSeq_ensure_length( &participantQos.partition.name, 3, 3)) { /* Handle error */ } *DDS_StringSeq_get_reference(&participantQos.partition.name, 0) = DDS_String_dup("US"); if (*DDS_StringSeq_get_reference(&participantQos.partition.name, 0) == NULL) { /* Handle error */ } *DDS_StringSeq_get_reference(&participantQos.partition.name, 1) = DDS_String_dup("Africa"); if (*DDS_StringSeq_get_reference(&participantQos.partition.name, 1) == NULL) { /* Handle error */ } *DDS_StringSeq_get_reference(&participantQos.partition.name, 2) = DDS_String_dup("Asia"); if (*DDS_StringSeq_get_reference(&participantQos.partition.name, 2) == NULL) { /* Handle error */ } /* Assign the new QoS */ if (DDS_DomainParticipant_set_qos(participant, &participantQos)) != DDS_RETCODE_OK) { /* Handle error */ } .. group-tab:: C++ .. code-block:: c++ using namespace dds::domain; using namespace dds::domain::qos; using namespace dds::core::policy; using namespace dds::core; DomainParticipantQos qos = DomainParticipant::default_participant_qos(); // QoS configuration happens here // Create the participant DomainParticipant participant(domain_id(), qos); // Participant is now up and running // Create a new partition value Partition newPartitionValues(StringSeq({ "US", "Africa", "Asia" })); // Update the QoS qos << newPartitionValues; // Assign the new QoS participant << qos; .. _section-transport: Transport ========= |CDS| allows you to select which transports to use to send and receive discovery traffic. The selection of transports defines the possible locations where the service can be reached. |DPs| require this information to communicate with the service. In a *Connext* application, |DPs| automatically configure the underlying transport ports based on the domain ID and the network capabilities of the host machine where the application runs. In |CDS|, this configuration is manual and explicit. .. note:: |CDS| only works with *unicast* transports. In this manual, any reference to the transport will always imply unicast. In particular, |CDS| allows you to choose: - **Transport class instance**: A *transport class* is a concrete realization of a networking transport (e.g., UDP, TCP, etc.). You can instantiate this class and uniquely identify it with a *transport alias*. In addition, for each instantiation you can configure properties specific to the implementation (e.g., network interfaces, receive buffer sizes, etc.). - **Transport Address**: A *transport address* represents an interface for a specific transport class (for example, an IP address for the UDP or TCP transport). - **Receive Port**: Identifies where the service listens for incoming data. For more information about ports, see :ref:`section-transport-about-ports`. Each transport instance-address tuple constitutes a Transport *Locator*, which is a unique data-reception endpoint. |CDS| creates send resources to put discovery traffic on the wire. *Connext* uses ephemeral ports for outbound data, hence a send resource is specified by a transport instance only. |CDS| creates a send resource for each transport instance specified. |CDS| relies on the same pluggable transport framework that is available for *Connext* applications to create and access transport resources. This implies you can use not only |RTI_CONNEXT| builtin transports, but also your own transport implementations. For more information, see :link_connext_dds_pro_um:`transport plugins <#users_manual/transports.htm>` in |CORE_MANUAL|. |DPs| can communicate with |CDS| as long as they are configured with the proper transport settings. Then they can reach a specific service instance in two ways: 1. By addressing the service instance through an :ref:`RTPS peer descriptor` which allows you to identify a DDS service in a generic way by its *locator*. |DPs| can include these in their lists of initial peers. 2. By addressing the service instance through a peer participant descriptor such that the resulting destination port, computed from the domain ID of the |DP| and well-known ports configuration, matches the listening port of the service. |CDS| determines how to reach |DPs| based on the locator information they propagate as part of their participant announcements. |CDS| sends discovery traffic to a |DP| by sending data to each of its locators. .. _section-domain-list-rtps-peer-descriptor: RTPS Peer Descriptor -------------------- A peer descriptor is a string representation of a set of locators for DDS |DPs|. It provides a compact way to indicate a list of locators where a |DP| can find other |DPs|. This is known as a **participant peer descriptor**, or simply a **peer descriptor**. *RTI Connext* applications use the *peer descriptor* to bootstrap the participant discovery process with other |DPs|. Refer to :link_connext_dds_pro_um:`discovery peer configuration <#users_manual/ConfigPeersListUsed_inDiscov.htm#discovery_507287096_336417>` in the |CORE_MANUAL|. The **RTPS peer descriptor** is another kind of peer descriptor that allows addressing a service with which you communicate through the *RTPS* protocol, and that does not necessarily imply the existence of a |DP|. |CDS| is an example of such a service. .. note:: The *RTPS peer descriptor* format is supported only by applications using *RTI DDS Connext* versions 5.3.0 and higher. The RTPS peer descriptor format is shown below: .. figure:: static/RtpsPeerDescriptorFormat.svg :figwidth: 100 % :alt: RTPS Peer Descriptor Format :name: FigureRtpsPeerDescriptorFormat :align: center RTPS Peer Descriptor Format :numref:`TableRtpsPeerDescriptorElements` describes all the elements in the RTPS peer descriptor. .. list-table:: RTPS Peer Descriptor Elements :widths: 10 25 15 10 :header-rows: 0 :name: TableRtpsPeerDescriptorElements * - **Element** - **Description** - **Required** - **Default** * - ``rtps`` - Keyword to indicate the RTPS descriptor kind. - Yes - * - ``@`` - Separator. - Only when ```` is specified. - * - ```` - Specifies a transport and an address. See :link_connext_dds_pro_um:`locator format <#users_manual/Peer_Descriptor_Format.htm#discovery_507287096_336444>`. - No - udpv4://localhost * - ``:`` - Separator. - Only when ```` is specified. - * - ```` - RTPS Peer receive port. See :ref:`section-transport-about-ports`. - No - 7400 Example: RTPS Peer Descriptors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: RTPS Peer Descriptor for **UDP/IP** Version 4 Transport :name: TableRtpsPeerDescUdp :widths: 10 4 20 4 8 :header-rows: 0 * - ``rtps`` - ``@`` - ``udpv4://192.169.1.1`` - ``:`` - ``7400`` .. list-table:: RTPS Peer Descriptor for a Generic **Starfabric** Transport :name: TableRtpsPeerDescStarfabric :widths: 10 4 20 4 8 :header-rows: 0 * - ``rtps`` - ``@`` - ``starfabric://FA::0#0/0/R`` - - Example: Transport Setup and Resulting RTPS Descriptor ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Assume |CDS| is configured with the following transport settings and running on host with address ``CDS-Host-IP-Address``: .. code-block:: xml udpv4 57410 |DPs| can send discovery traffic to this service by adding the following peer to their initial peer lists: .. code-block:: bash rtps@udpv4://:57410 .. _section-transport-about-ports: About Ports ----------- Discovery traffic through RTPS relies on well-known ports to establish communication between peers. These ports are a logical concept that allow multiplexing communications at the middleware layer. It is up to the underlying transport implementation to decide how to map these logical port numbers into the physical transport address scheme. For instance, the RTI builtin UDP transport directly maps the logical port number into the physical UDP port. The port mapping is especially important in *Cloud* environments where UDP and TCP dominate the transport layer. The presence of these protocols often requires knowing details about the communication ports to properly set up application services. In a |DP|, unless explicitly configured, ports are determined automatically based on the domain ID and participant ID. Also, the port mapping is fully controlled by the underlying transport. In |CDS|, the ports must be configured explicitly. Moreover, for the direct known RTI transport implementations that rely on UDP and TCP, |CDS| maps the (logical) receive port directly to the transport physical port. See :link_connext_dds_pro_um:`Ports Used for Discovery <#users_manual/Ports_Used_for_Discovery.htm#discovery_507287096_264722>` to learn more about RTPS ports. .. _section-forwarder: Forwarder ========= The forwarder is the |CDS| component where all the discovery logic resides. Its responsibility is to build/maintain the discovery state and forward participant announcements to the peer |DPs| so that they can discover each other. :numref:`FigureCdsForwarder` shows a representation of the forwarder element of |CDS|. .. figure:: static/CdsForwarder.svg :figwidth: 100 % :alt: Participant Announcement Forwarder :name: FigureCdsForwarder :align: center Participant Announcement Forwarder The forwarder is composed of four main blocks: - **Receiver**: The element in charge of retrieving incoming participant announcements. This element interfaces with the *receive resources*, which provide the participant announcements received from all the peer |DPs|. - **Announcement Queues**: The received participant announcements are placed in queues so they can be forwarded to their proper destinations. Participant announcements can be categorized in three classes: - *New*: Announcements from |DPs| that are considered new from the service perspective. - *Change/Update*: Announcements from |DPs| that the service has already seen but that contain content changes relative to the previous announcement from the same |DP|. For example, QoS or Locator changes or Locator resolutions for the |DP|. - *Refresh/Repeat*: Announcements from |DPs| that the service has already seen and that do not contain any content changes from the previously received announcement. This classification allows the forwarder to process the different categories differently, allowing you to prioritize and regulate the bandwidth used by each traffic class. - **Flow Controller**: The entity that removes announcements from the queue and forwards them to the proper destinations. The flow controller regulates the output announcement traffic. The flow controller uses the send resources to direct the announcements to the proper |DPs|. - **Event Manager**: This element is in charge of handling events like UDPv4 WAN locator resolution and announcement resend. Each of these events is described below: - *UDPv4 WAN Locator Resolution*: This event happens when the |RWT| is used as a transport in a WAN scenario. In such an environment due to the presence of a NAT, locators in the incoming participant announcement are resolved asynchronously to their public IP addresses. Once the locator is resolved by |CDS|, it creates a *job* of the *Change* type. For more details on this scenario refer to section :ref:`section-natTraversal`. - *Announcement Resend*: This event is triggered when |CDS| has been configured to perform resends. Resends are performed when a *New* or *Change* announcement is received. It is also performed after a *UDPv4 WAN Locator Resolution* event occurs, which is considered as a *Change*. Resend artifically generates *jobs* of the announcement kind *Repeat* that send out multiple copies of an incoming or resolved participant announcement. |br| Resends are particularly useful in networks with high probability of packet loss that hampers discovery speed. This is especially true when using the |RWT| for connectivity over the internet. For more details on configuring this mechanism refer to section :ref:`section-config-forwarder`. Flow Controller --------------- |CDS| incorporates a configurable flow controller, which allows shaping the generated output traffic as a result of forwarding participant announcements. The parameters that configure a flow controller are: - **Output capacity**: The maximum amount of announcements forwarded in a period of time. It is measured in jobs-per-time units (e.g., announcements per second). Each received announcement represents a *job* to forward the announcement to all discovered |DPs|. This parameter allows setting an upper bound to the output traffic to avoid network congestion. - **Maximum job burst**: The maximum amount of consecutive jobs that can be forwarded in a period of time. This parameter allows setting an upper limit to the output traffic peak. - **Flush period**: The period at which the flow controller attempts to forward pending announcements. .. note:: Because forwarding an announcement requires sending each announcement to multiple discovered participants, the actual output bandwidth depends on the number of participants. Operation Mode ^^^^^^^^^^^^^^ The flow controller generates *job tokens* at the rate specified by the *output capacity*. Forwarding an announcement takes exactly one job token. The forwarder queues announcements upon reception and attempts to forward them as soon as tokens are available. If tokens are not available, the announcement jobs will remain in the queue in a pending state. The forwarder wakes up at every *flush period* to check for available tokens to forward the pending announcements. If tokens are generated faster than jobs are received, the flow controller accumulates the tokens for future jobs. The flow controller will accumulate no more than the *maximum job burst* tokens. .. _section-database: Database ======== |CDS| uses an internal database to keep information about remote entities. This is the information that represents the discovery state of the system. This state is maintained upon reception of discovery information. Remote entries in the database are added or removed based on the received information. The database will release the resources for each removed entry as needed. |CDS| relies on a dedicated thread, which periodically cleans up any removed state from the database. This model enhances concurrency while maintaining thread safety. This element is equivalent to the :link_connext_dds_pro_um:`Database Thread <#users_manual/Database_Thread.htm#threading_3235324107_746159>` of a |DP|.