7. Persistence Service¶
Using default QoS, the DataWriter history is only available for DataReader throughout the DataWriter’s life. This means that the history does not persist between DataWriter initializations and therefore it is on an empty state on DataWriter creation. Similarly, the DataReader history does not persist the DataReader’s life, thus also being empty on DataReader creation. However, eProsima Fast DDS offers the possibility to configure the DataWriter’s history to be stored in a persistent database, so that the DataWriter can load its history from it on creation. Furthermore, DataReaders can be configured to store the last notified change in the database, so that they can recover their state on creation.
This mechanism allows recovering a previous state on starting the Data Distribution Service, thus adding robustness to applications in the case of, for example, unexpected shutdowns. Configuring the persistence service, DataWriters and DataReaders can resume their operation from the state in which they were when the shutdown occurred.
Note
Mind that DataReaders do not store their history into the database, but rather the last notified change from the DataWriter. This means that they will resume operation where they left, but they will not have the previous information, since that was already notified to the application.
7.1. Configuration¶
The configuration of the persistence service is accomplished by setting of the appropriate DataWriter and DataReader
DurabilityQosPolicy, and by specifying the suitable properties for each entity’s (DomainParticipant
, DataWriter,
or DataReader) PropertyPolicyQos.
For the Persistence Service to have any effect, the
DurabilityQosPolicyKind
needs to be set toTRANSIENT_DURABILITY_QOS
.A persistence identifier (
Guid_t
) must be set for the entity using the propertydds.persistence.guid
. This identifier is used to load the appropriate data from the database, and also to synchronize DataWriter and DataReader between restarts. The GUID consists of 16 bytes separated into two groups:The first 12 bytes correspond to the
GuidPrefix_t
.The last 4 bytes correspond to the
EntityId_t
.
The persistence identifier is specified using a string of 12 dot-separated bytes, expressed in hexadecimal base, followed by a vertical bar separator (
|
) and another 4 dot-separated bytes, also expressed in hexadecimal base (see Example). For selecting an appropriate GUID for the DataReader and DataWriter, please refer to RTPS standard (section 9.3.1 The Globally Unique Identifier (GUID)).A persistence plugin must be configured for managing the database using property
dds.persistence.plugin
(see PERSISTENCE:SQLITE3 built-in plugin):
7.2. PERSISTENCE:SQLITE3 built-in plugin¶
This plugin provides persistence through a local database file using SQLite3 API.
To activate the plugin, dds.persistence.plugin
property must be added to the PropertyPolicyQos of the
DomainParticipant, DataWriter, or DataReader with value builtin.SQLITE3
.
Furthermore, dds.persistence.sqlite3.filename
property must be added to the entities PropertyPolicyQos,
specifying the database file name.
These properties are summarized in the following table:
Property name |
Property value |
---|---|
|
|
|
Name of the file used for persistent storage. |
Note
To avoid undesired delays caused by concurrent access to the SQLite3 database, it is advisable to specify a different database file for each DataWriter and DataReader.
Important
The plugin set in the PropertyPolicyQos of DomainParticipant only applies if that of the DataWriter/DataReader does no exist or is invalid.
7.3. Example¶
This example shows how to configure the persistence service using PERSISTENCE:SQLITE3 built-in plugin plugin both from C++ and using eProsima Fast DDS XML profile files (see XML profiles).
/*
* In order for this example to be self-contained, all the entities are created programatically, including the data
* type and type support. This has been done using Fast DDS Dynamic Types API, but it could be substituted with a
* Fast DDS-Gen generated type support if an IDL file is available. The Dynamic Type created here is the equivalent
* of the following IDL:
*
* struct persistence_topic_type
* {
* unsigned long index;
* string message;
* };
*/
// Configure persistence service plugin for DomainParticipant
DomainParticipantQos pqos;
pqos.properties().properties().emplace_back("dds.persistence.plugin", "builtin.SQLITE3");
pqos.properties().properties().emplace_back("dds.persistence.sqlite3.filename", "persistence.db");
DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, pqos);
/********************************************************************************************************
* CREATE TYPE AND TYPE SUPPORT
*********************************************************************************************************
* This part could be replaced if IDL file and Fast DDS-Gen are available.
* The type is created with name "persistence_topic_type"
* Additionally, create a data object and populate it, just to show how to do it
********************************************************************************************************/
// Create a struct builder for a type with name "persistence_topic_type"
const std::string topic_type_name = "persistence_topic_type";
eprosima::fastrtps::types::DynamicTypeBuilder_ptr struct_type_builder(
eprosima::fastrtps::types::DynamicTypeBuilderFactory::get_instance()->create_struct_builder());
struct_type_builder->set_name(topic_type_name);
// The type consists of two members, and index and a message. Add members to the struct.
struct_type_builder->add_member(0, "index",
eprosima::fastrtps::types::DynamicTypeBuilderFactory::get_instance()->create_uint32_type());
struct_type_builder->add_member(1, "message",
eprosima::fastrtps::types::DynamicTypeBuilderFactory::get_instance()->create_string_type());
// Build the type
eprosima::fastrtps::types::DynamicType_ptr dyn_type_ptr = struct_type_builder->build();
// Create type support and register the type
TypeSupport type_support(new eprosima::fastrtps::types::DynamicPubSubType(dyn_type_ptr));
type_support.register_type(participant);
// Create data sample a populate data. This is to be used when calling `writer->write()`
eprosima::fastrtps::types::DynamicData* dyn_helloworld;
dyn_helloworld = eprosima::fastrtps::types::DynamicDataFactory::get_instance()->create_data(dyn_type_ptr);
dyn_helloworld->set_uint32_value(0, 0);
dyn_helloworld->set_string_value("HelloWorld", 1);
/********************************************************************************************************
* END CREATE TYPE AND TYPE SUPPORT
********************************************************************************************************/
// Create a topic
Topic* topic = participant->create_topic("persistence_topic_name", topic_type_name, TOPIC_QOS_DEFAULT);
// Create a publisher and a subscriber with default QoS
Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
// Configure DataWriter's durability and persistence GUID so it can use the persistence service
DataWriterQos dwqos = DATAWRITER_QOS_DEFAULT;
dwqos.durability().kind = TRANSIENT_DURABILITY_QOS;
dwqos.properties().properties().emplace_back("dds.persistence.guid",
"77.72.69.74.65.72.5f.70.65.72.73.5f|67.75.69.64");
DataWriter* writer = publisher->create_datawriter(topic, dwqos);
// Configure DataReaders's durability and persistence GUID so it can use the persistence service
DataReaderQos drqos = DATAREADER_QOS_DEFAULT;
drqos.durability().kind = TRANSIENT_DURABILITY_QOS;
drqos.properties().properties().emplace_back("dds.persistence.guid",
"72.65.61.64.65.72.5f.70.65.72.73.5f|67.75.69.64");
DataReader* reader = subscriber->create_datareader(topic, drqos);
<?xml version="1.0" encoding="UTF-8" ?>
<dds>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<!-- DomainParticipant configuration -->
<participant profile_name="persistence_service_participant">
<rtps>
<propertiesPolicy>
<properties>
<!-- Select persistence plugin -->
<property>
<name>dds.persistence.plugin</name>
<value>builtin.SQLITE3</value>
</property>
<!-- Database file name -->
<property>
<name>dds.persistence.sqlite3.filename</name>
<value>persistence_service.db</value>
</property>
</properties>
</propertiesPolicy>
</rtps>
</participant>
<!-- DataWriter configuration -->
<data_writer profile_name="persistence_service_data_writer">
<qos>
<!-- Set durability to TRANSIENT_DURABILITY_QOS -->
<durability>
<kind>TRANSIENT</kind>
</durability>
</qos>
<propertiesPolicy>
<properties>
<!-- Persistence GUID -->
<property>
<name>dds.persistence.guid</name>
<value>77.72.69.74.65.72.5f.70.65.72.73.5f|67.75.69.64</value>
</property>
</properties>
</propertiesPolicy>
</data_writer>
<data_reader profile_name="persistence_service_data_reader">
<qos>
<!-- Set durability to TRANSIENT_DURABILITY_QOS -->
<durability>
<kind>TRANSIENT</kind>
</durability>
</qos>
<propertiesPolicy>
<properties>
<!-- Persistence GUID -->
<property>
<name>dds.persistence.guid</name>
<value>72.65.61.64.65.72.5f.70.65.72.73.5f|67.75.69.64</value>
</property>
</properties>
</propertiesPolicy>
</data_reader>
</profiles>
</dds>
Note
For instructions on how to create DomainParticipants, DataReaders, and DataWriters, please refer to Profile based creation of a DomainParticipant, Profile based creation of a DataWriter, and Profile based creation of a DataReader respectively.