28.3 TypeLookup Service

The TypeLookup Service is a builtin DDS service, first defined in the Extensible Types 1.2 specification, that allows DomainParticipants to dynamically discover the full type definitions of remote endpoints at runtime. It uses a request/reply protocol built on top of dedicated builtin DataWriters and DataReaders to exchange type information between participants.

28.3.1 Wire Representations of Types (TypeCode, TypeObject v1, and TypeObject v2)

Connext supports three mechanisms for propagating type information during endpoint discovery:

  • TypeCode (legacy): TypeCode is the original type description mechanism, predating the Extensible Types specification. It is a compact, runtime representation of an IDL-defined type (similar to CORBA TypeCode) that is propagated inline within endpoint discovery announcements (using PID_TYPECODE). TypeCode remains supported for backward compatibility and interoperability with older Connext releases.
  • TypeObject v1 (legacy): The complete type description (TypeObject) is embedded directly inside the endpoint discovery announcements (i.e., the Publication and Subscription builtin topic data). This means the full type definition is received as part of endpoint discovery itself, and no additional requests are needed to perform type-consistency enforcement. However, this approach carries a significant cost: the full serialized type object is included in every endpoint announcement, which increases the size of discovery traffic—especially for complex or deeply nested types. This is especially problematic for systems that utilize features that result in frequent endpoint discovery updates, such as those with endpoint partitions or ContentFilteredTopic filter expression updates. It also means that types that an application is not interested in (or already has locally) are still propagated, again resulting in an increase in the size of discovery traffic.
  • TypeObject v2 (default): Instead of propagating the full type definition inline, endpoint discovery announcements carry only lightweight TypeInformation, which contains TypeIdentifiers (equivalence hashes) of the type. These hashes uniquely identify a type but do not describe its structure. To obtain the actual type definition, Connext invokes the TypeLookup Service to request the full TypeObject v2 from the remote participant. The full type definition only needs to be requested when a remote endpoint is using a type unknown to the local participant. When types (and therefore TypeIdentifiers) match exactly, no type lookup requests are made. This is more efficient for discovery traffic because the TypeInformation is a fixed, small size regardless of type complexity. The tradeoff is that endpoint matching may be deferred until the full type has been retrieved.

When both TypeObject v1 and TypeObject v2 are available for a given remote endpoint, Connext favors TypeObject v1 because it is already present and does not require any additional network requests. TypeObject v2 matching via the TypeLookup Service is only used when TypeObject v1 is not available (which is the default behavior of Connext).

The typical flow for resolving a remote type when using TypeObject v2 is as follows:

  1. The local participant receives the TypeInformation of the remote endpoint from the remote builtin PublicationBuiltinTopic or SubscriptionBuiltinTopic DataWriter.
  2. Connext determines whether to resolve the received TypeInformation to a TypeObject v2. By default, Connext only tries to resolve types that have a local endpoint that has the same Topic and compatible QoS. (This behavior can be configured using DiscoveryConfigQos::request_types_filter. See 28.3.4 Integration with DynamicData (request_types_filter).)
  3. Resolving a TypeIdentifier to a TypeObject v2 means invoking TypeLookup Service to obtain the TypeObject v2 of the type described by the TypeIdentifier, and the TypeObject v2s of all of its dependent types.

  4. If the type is not already known locally, a series of requests and replies between the TypeLookup Service endpoints are made to resolve the top-level type and all of its dependencies. For a full description of the protocol and the possible requests that may be made, see the OMG 'Extensible and Dynamic Topic Types for DDS' specification, version 1.4.
  5. After sending a request, the service reschedules a follow-up request using an exponential backoff strategy (starting at two seconds, doubling each attempt) up to a maximum of 10 attempts. This protects against asymmetric liveliness losses where a request is sent but the reply is lost. Once the type is fully resolved, all outstanding requests for that type are cleared and no further retries are scheduled.

  6. Once the type is resolved, type-consistency enforcement is performed and endpoint matching completes.

(With TypeObject v1, there is no logic required to resolve types, since the entire TypeObject is always sent in every endpoint data sample.)

TypeLookup Service (TypeObject v2) offers several efficiency advantages over inline TypeObject v1 propagation:

  • Reduced discovery bandwidth usage: TypeObject v2 endpoint announcements carry only small, fixed-size TypeIdentifiers (equivalence hashes) instead of full serialized type definitions. For complex types this can reduce endpoint announcement size by orders of magnitude.
  • Type caching: Once a TypeObject v2 is retrieved via the TypeLookup Service, it is stored locally. If multiple remote endpoints use the same type, the type only needs to be resolved once. With TypeObject v1, every endpoint announcement redundantly carries the full type.
  • Deduplication of requests: The TypeLookup Service tracks outstanding requests and avoids sending duplicate requests for the same type to the same remote participant—for example, when multiple remote endpoints of the same type are discovered simultaneously. Additionally, Connext will not send parallel requests to different participants for the same type if it discovers them at the same time, preventing Connext from flooding the network with duplicate requests.
  • Directed communication: Requests and replies are sent as directed writes to the specific remote participant, avoiding unnecessary network traffic to other participants.

See "Table 5.2 Enabling Different Type Representations," in Type Representation, in the RTI Connext Core Libraries Extensible Types Guide, for information on enabling or disabling the different type representations.

28.3.2 Tuning TypeLookup Service (TypeObject v2) Behavior

The following QoS can be used to tune the TypeLookup Service's behavior:

Table 28.1 QoS for Tuning TypeLookup Service

Configuration Option

Default

Description

DiscoveryConfigQos::enabled_builtin_channels

BUILTIN_CHANNEL_MASK_DEFAULT (includes the TypeLookup channel)

Bitmask that controls which builtin channels are enabled. The default mask (BUILTIN_CHANNEL_MASK_DEFAULT) enables TypeLookup Service (the builtin service used to propagate TypeObject v2), along with the Service Request channel. The TYPE_LOOKUP_SERVICE_CHANNEL value controls whether or not TypeLookup Service is enabled. See 56.3 DISCOVERY_CONFIG QosPolicy (DDS Extension).

DiscoveryConfigQos::request_types_filter

NULL (disabled)

An fnmatch-style pattern (supporting `*`, `?`, and `[...]` wildcards) matched against the Topic name and Topic name aliases of discovered remote endpoints. When a Topic name matches the filter, types are requested proactively at endpoint discovery time, even if no local endpoint exists or matches yet. This is essential for DynamicData applications that require discovering the types before creating local endpoints that use them. The filter solves this by triggering type requests at discovery time regardless of whether a local endpoint exists.

See 56.3 DISCOVERY_CONFIG QosPolicy (DDS Extension).

dds.participant.discovery_config.type_lookup_service.verify_type_identifiers (property)

false (disabled)

If true, the received TypeObject v2 in a getTypes reply is hashed and compared against the TypeIdentifier it was received with. If they do not match, the reply is dropped. This provides integrity verification (i.e., security) at the cost of additional computation. Note that enabling this property may affect interoperability between Connext and other vendors. (If the vendor doesn't zero out the padding bytes when serializing, Connext won't be able to verify the type identifiers, won't accept the TypeLookup reply, and therefore won't be able to complete discovery.)

dds.participant.discovery_config.type_lookup_service.request_delay (property)

2s

Configures the delay between TypeLookup requests sent to different remote participants for the same TypeIdentifier.

It is rare that you would need to set this property—only if you notice excessive network traffic with redundant requests of the same type (in which case, set this property to a longer delay) or, conversely, discovery taking a long time to complete and you believe that participants may be coming and going before they finish responding to TypeLookup Service requests (in which case, decrease this value).

See the description of this property in the Property Reference Guide for more information.

28.3.3 Security Integration

When the Security Plugins are enabled, the TypeLookup Service supports both a non-secure and secure TypeLookup Service channel. The channel selection follows these rules:

  • If the remote participant is currently authenticated, requests are sent on the secure TypeLookup Service channel.
  • Otherwise, requests are sent on the non-secure channel.
  • Replies are always sent on the same channel (secure or non-secure) that the request was received on.
  • Types used exclusively by secure topics are only served in response to requests on the secure channel, ensuring that type definitions for secure Topics are not leaked to unauthenticated participants.

28.3.4 Integration with DynamicData (request_types_filter)

DynamicData allows applications to create endpoints without having the type at compile time. One of the ways that an application may acquire the type is through endpoint discovery, by accessing the type_code member in either the PublicationBuiltinTopicData or SubscriptionBuiltinTopicData. TypeObject v2 and the TypeLookup Service fundamentally change how types are exchanged between applications by only sending them when they are needed by an application. For this reason, DynamicData applications that require discovering types must configure which Topics they want to request types for since Connext does not request types for all Topics by default. See 31.2 Built-in DataReaders.

The DiscoveryConfigQosPolicy::request_types_filter field controls which Topics a participant will proactively request types for using the TypeLookup Service. It is an fnmatch-style pattern (supporting `*`, `?`, and `[...]` wildcards) that are matched against the Topic name of discovered remote endpoints.

When a remote endpoint's Topic name matches the filter, the participant will try to resolve the type, even if there is no local Topic match. The TypeLookup Service requests the type immediately when the remote endpoint is discovered, even if there is no local endpoint that needs to match with it. Without the filter, types are only requested on-demand when a local endpoint is created that needs to match a remote endpoint of an unknown type.

28.3.5 Effect on Builtin DataReader Listeners

TypeLookup Service changes how and when builtin endpoint discovery samples (i.e., PublicationBuiltinTopicData and SubscriptionBuiltinTopicData) are delivered to the application through the builtin PublicationBuiltinTopic and SubscriptionBuiltinTopic DataReader listeners. This is particularly important because the type_code field in these samples is derived from the resolved TypeObject, and its availability depends on the state of type resolution.

28.3.5.1 How type_code is Populated

When a builtin endpoint discovery sample is generated, Connext populates the type_code field using the best type representation available at that moment, in the following priority order:

  1. TypeObject v1: If the remote endpoint sent a TypeObject v1 inline in its discovery announcement, the type_code is converted from the TypeObject v1. The type_code is always available immediately.
  2. TypeObject v2: If TypeObject v1 is not available but the TypeObject v2 has been resolved through the TypeLookup Service, the type_code is converted from the resolved TypeObject v2. The OMG 'Extensible and Dynamic Topic Types for DDS' specification, version 1.4 defines two different representations of a TypeObject v2; COMPLETE and MINIMAL. For more information on the differences between these representations, refer to the specification. If the COMPLETE TypeObject was resolved, the type_code will contain full member names and type names; if only the MINIMAL TypeObject was resolved, the type_code will contain name hashes instead of human-readable names. Note that if the request_types_filter is set, Connext will only provide a COMPLETE TypeObject in the builtin on_data_available listener. If Connext resolves a MINIMAL TypeObject before the COMPLETE one, the listener is called with a NULL type_code.

28.3.5.2 Sample Delivery and the type_code == NULL Case

Because type resolution via the TypeLookup Service is asynchronous, the builtin DataReader's on_data_available listener may be called before the TypeObject v2 has been fully resolved. When this happens, the type_code field in the PublicationBuiltinTopicData or SubscriptionBuiltinTopicData sample will be NULL (assuming no TypeObject v1 or inline TypeCode was provided by the remote endpoint).

Once the TypeLookup Service completes type resolution, Connext re-generates the builtin Topic sample from the remote endpoint record—now with the resolved TypeObject v2—and delivers it to the builtin DataReader as a new sample. This means the on_data_available listener will be called a second time for the same remote endpoint, and this time the type_code field will be fully populated.

If you are using the type_code in your application, you must handle the case where type_code is NULL. The recommended pattern is as follows:

  1. In the on_data_available callback, check whether type_code is NULL.
  2. If type_code is NULL, skip processing for that sample—the type has not yet been resolved.
  3. When the listener is called again with the same endpoint (after type resolution), type_code will be non-NULL, and the application can proceed to construct the DynamicType and create DynamicData endpoints.