8.3.8 Accessing and Managing Instances (Working with Keyed Data Types)

This section describes how instances work on DataReaders. This section applies only to data types that use keys; see 2.4 DDS Samples, Instances, and Keys. See also 6. Working with Instances.

A DataReader receives updates about instances and instance state changes as DATA_AVAILABLE statuses, the same way it receives data updates. (See 8.3.7.1 DATA_AVAILABLE Status.) DataReaders can access instance state as part of the SampleInfo that is returned when calling any variant of read() or take() (such as read_instance() or take_instance()).

8.3.8.1 Instance States

As seen in Figure 8.11: Instance States , Connext DDS keeps an instance_state for each instance:

Instances can cycle through these phases as seen in the state diagram below, becoming NOT_ALIVE and then becoming ALIVE again. To track these transitions, there is metadata the DataReader can query called generation counts. (See 8.3.8.2 Generation Counts and Ranks.)

The events that cause the instance_state to change can depend on the setting of the 7.5.17 OWNERSHIP QosPolicy:

Figure 8.11: Instance States

Transitions shown with dashes are only available if propagate_dispose_of_unregistered_instances = true

Since the instance_state in the SampleInfo structure is a per-instance concept, all DDS data samples related to the same instance that are returned by read() or take() will have the same value for instance_state. This means that if there are samples for that instance in the DataReader’s queue that were received when the instance was ALIVE, and a subsequent dispose message is received, the samples’ metadata will indicate that the instance’s state is NOT_ALIVE_DISPOSED in all of them.

Note: The instance_state always reflects the current state of the instance at the time of reading.

Figure 8.12: Before and After Dispose Received

When the dispose message is received (the box with the X, with valid_data = false), all samples for the flight 265 instance in the queue are marked as NOT_ALIVE_DISPOSED, even those that contain live data from when the instance was ALIVE.

Note: If an instance transitions its state to NOT_ALIVE_NO_WRITERS due to one or more DataWriters losing liveliness, it will not transition back to ALIVE if the DataWriter regains liveliness. It only returns to the ALIVE state if a DataWriter writes a new sample of the instance.

8.3.8.2 Generation Counts and Ranks

Generation counts and ranks allow your application to distinguish DDS samples belonging to different ‘generations’ of the instance. It is possible for an instance to become alive, be disposed and become not-alive, and then cycle again from alive to not-alive states during the operation of an application. Each time an instance becomes alive defines a new generation for the instance.

It is possible that an instance may cycle through alive and not-alive states multiple times before the application accesses the DDS data samples for the instance. This means that the DDS data samples returned by read() and take() may cross generations. That is, some DDS samples were published when the instance was alive in one generation and other DDS samples were published when the instance transitioned through the non-alive state into the alive state again. It may be important to your application to distinguish the DDS data samples by the generation in which they were published.

Each DataReader keeps two counters for each instance it detects (recall that instances are distinguished by their key values):

The disposed_generation_count and no_writers_generation_count fields in the SampleInfo structure capture a snapshot of the corresponding counters at the time the corresponding DDS sample was received.

The sample_rank and generation_rank in the SampleInfo structure are computed relative to the sequence of DDS samples returned by read() or take():

By using the sample_rank, generation_rank and absolute_generation_rank information in the SampleInfo structure, your application can determine exactly what happened to the instance and thus make appropriate decisions of what to do with the DDS data samples received for the instance. For example:

The ‘generation count’ and ‘rank’ values are statistics that are locally generated by each DataReader and maintained as part of the metadata for the instance that they refer to. Therefore, if the instance is reclaimed and then returns at a later point in time, these counters will all restart at 0.

8.3.8.3 Valid Data Flag

The SampleInfo structure’s valid_data flag indicates whether the DDS sample contains data or is only used to communicate a change in the instance_state of the instance.

Normally, each DDS sample contains both a SampleInfo structure and some data. However, there are situations in which the DDS sample only contains the SampleInfo and does not have any associated data. This occurs when Connext DDS notifies the application of a change of state for an instance for which there is no associated data. An example is when Connext DDS detects that an instance has no writers and changes the corresponding instance_state to NOT_ALIVE_NO_WRITERS.

If the valid_data flag is TRUE, then the DDS sample contains valid data. If the flag is FALSE, the DDS sample contains no data.

To ensure correctness and portability, your application must check the valid_data flag prior to accessing the data associated with the DDS sample, and only access the data if it is TRUE. The value of data is undefined when the valid_data flag is false.

8.3.8.4 Looking up an Instance Handle

Some operations, such as read_instance(), require an instance_handle parameter. If you need to get such a handle, you can call the FooDataReader’s lookup_instance() operation, which takes a sample with key fields specified as a parameter and returns a handle to that instance.

DDS_InstanceHandle_t lookup_instance (const Foo & key_holder)

The instance must have been received by the DataReader in order for the DataReader to look it up. If the instance is not known to the DataReader, this operation returns DDS_HANDLE_NIL.

8.3.8.5 Getting the Key Value for an Instance

Once you have an instance handle (using lookup_instance(), as part of a status change notification, or through the SampleInfo), you can use the DataReader’s get_key_value() operation to retrieve the value of the key of the corresponding instance. The key fields of the data structure passed into get_key_value() will be filled out with the original values used to generate the instance handle. The key fields are defined when the data type is defined; see 2.4 DDS Samples, Instances, and Keys for more information.

If you set propagate_dispose_of_unregistered_instances to true and wish to call get_key_value() for instances for which only a dispose sample has been received, the serialize_key_with_dispose field in the 7.5.5 DATA_WRITER_PROTOCOL QosPolicy (DDS Extension) must be set to true.

8.3.8.6 Instance Resource Limits and Memory Management

In Connext DDS, memory is primarily pre-allocated when creating entities. When data is keyed, the memory associated with each instance used for storing instance-specific metadata is allocated when the DataReader is created. Memory is not freed at runtime, unless you delete an entity. Instead, memory is made available to be reused by the DataReader, or “reclaimed”.

The DataReader can receive a number of instances defined by the 7.5.22 RESOURCE_LIMITS QosPolicy and 8.6.2 DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension). It is also important to understand that an instance has two parts that make up the instance metadata: an active state and a minimum state. The resource limits control the amount of active state and minimum state that should be maintained.

8.3.8.6.1 Active State and Minimum State

An instance can be either attached or detached in the DataReader queue and is composed of two parts, which make up the instance metadata: an active state and a minimum state.

Figure 8.13: Active and Minimum Instance States

An instance is considered attached when the DataReader is actively managing all possible state that can be associated with an instance, including the associated samples, the instance and view states, generation and sample ranks, the list of remote writers that are known to be writing the instance, and so on. Only attached instances can have associated samples. A DataReader keeps both the active and the minimum state for attached instances.

An instance is considered detached when the DataReader is only maintaining the minimum state for the instance. The minimum state includes information such as the last source timestamp, the keyhash, and the list of virtual writers for the instance. Minimum state information is kept only if keep_minimum_state_for_instances in the 8.6.2 DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension) is TRUE (by default, it is). The information in the minimum state is used to maintain system consistency in situations where instances may transition between being attached and being detached due to system disconnections or resource limits. The minimum state is needed when the Durable Reader State, MultiChannel DataWriters, or RTI Persistence Service is used, or in any system where instances may transition from being attached to being detached. In these scenarios, it’s possible that samples for the detached instances may be received later on because of non-VOLATILE durability settings or the instance has become ALIVE again. The minimum state allows Connext DDS to keep the right amount of information to maintain system consistency without having to waste resources (memory and CPU) by keeping information around that is no longer relevant.

Currently, an instance can transition from being attached to being detached under the following conditions:

To better understand why the minimum state can be helpful, consider a situation in which there is a network disconnection and DomainParticipant liveliness is lost. When liveliness is lost, all of the instances being written by the DataWriters in the lost DomainParticipant will become detached (if they don’t have any associated samples). If the 7.5.9 DURABILITY QosPolicy kind is non-VOLATILE, upon reconnection the DataWriter will resend the samples in its queue to the DataReader, and the DataReader will be able filter out samples that it had already received previous to the disconnection based on the information that it has kept as part of the minimum state.

8.3.8.6.2 Instance Resource Limit QoS Policies

The 7.5.22 RESOURCE_LIMITS QosPolicy and 8.6.2 DATA_READER_RESOURCE_LIMITS QosPolicy (DDS Extension) include the following fields that affect the number of instances that can be received:

The 8.6.3 READER_DATA_LIFECYCLE QoS Policy controls whether the DataReader can remove data from the queue if instance state becomes NOT_ALIVE_NO_WRITERS or NOT_ALIVE_DISPOSED.

© 2020 RTI