Writer
Writers are used to publish samples. Three writer types are supported:
- single-key writer: a single key writer publishes samples for a single data element identified by the key provided on creation of the writer
- multi-key writer: a multi-key writer publishes samples for multiple data elements identified by a set of keys provided on creation of the writer
- any-key writer: an any-key writer publishes samples for any data element. No keys are provided when creating the writer.
A writer is created using a topic plus optionally a configuration structure. Each writer keeps the history of the samples written with this writer. Depending on the reader configuration, this history, or part of it, is transmitted to the reader when it connects. The application can be notified when readers connect to or disconnect from a writer by registering listener functions with this writer.
A name can also be assigned to the writer on creation. Readers will use this name for the listener notification and the sample origin.
On this page:
Creation
Single-key writer
A single-key writer enables the publisher to publish samples for a single key. The key is provided when the writer is created. A single-key writer object is created by calling the SingleKeyWriter
constructor or by using the makeSingleKeyWriter
factory function:
template<typename Key, typename Value, typename UpdateTag=std::string> class SingleKeyWriter : public Writer<Key, Value, UpdateTag> { public: SingleKeyWriter(const Topic<Key, Value, UpdateTag>& topic, const Key& key, const string& name = string(), const WriterConfig& config = WriterConfig()); }; template<typename K, typename V, typename UT> SingleKeyWriter<K, V, UT> makeSingleKeyWriter(const Topic<K, V, UT>& topic, const typename Topic<K, V, UT>::KeyType& key, const string& name = string(), const WriterConfig& config = WriterConfig());
The constructor accepts a topic parameter, a key and an optional name and writer configuration structure.
The makeSingleKeyWriter
function accepts the same parameters. The key, value and update tag template parameters (K, V, UT) don't need to be provided when using this function, they are automatically deduced by the compiler from the topic parameter.
For example, to create a writer to write temperatures for the room "kitchen":
DataStorm::Topic<string, float> temperatures = ...; SingleKeyWriter<string, float> writer1(temperatures, "kitchen"); // using the constructor auto writer2 = makeSingleKeyWriter(temperatures, "kitchen"); // using the factory function auto writer3 = std::make_shared<SingleKeyWriter<string, float>>(temperatures, "kitchen"); // create the writer on the heap using the constructor
When a single-key writer is created, DataStorm notifies connected peers. If these peers have readers matching the writer's key, the readers connect to the writer in order to receive samples from it.
Multi-key writer
A multi-key writer enables the publisher to publish samples for multiple keys. The keys are provided when the writer is created. A multi-key writer object is created by calling the MultiKeyWriter
constructor or by using the makeMultiKeyWriter
factory function:
template<typename Key, typename Value, typename UpdateTag=std::string> class MultiKeyWriter : public Writer<Key, Value, UpdateTag> { public: MultiKeyWriter(const Topic<Key, Value, UpdateTag>& topic, const std::vector<Key>& keys, const string& name = string(), const WriterConfig& config = WriterConfig()); }; template<typename K, typename V, typename UT> MultiKeyWriter<K, V, UT> makeMultiKeyWriter(const Topic<K, V, UT>& topic, const std::vector<typename Topic<K, V, UT>::KeyType>& keys, const string& name = string(), const WriterConfig& config = WriterConfig());
The constructor accepts a topic parameter, a collection of keys and an optional name and writer configuration structure.
The makeMultiKeyWriter
function accepts the same parameters. The key, value and update tag template parameters (K, V, UT) don't need to be provided when using this function, they are automatically deduced by the compiler from the topic parameter.
For example, to create a writer to write temperatures for the room "kitchen" and "living room":
DataStorm::Topic<string, float> temperatures = ...; MultiKeyWriter<string, float> writer1(temperatures, {"kitchen", "living room"}); // using the constructor auto writer2 = makeMultiKeyWriter(temperatures, {"kitchen", "living room"}); // using the factory function auto writer3 make_shared<MultiKeyWriter<string, float>>(temperatures, vector<string> {"kitchen", "living room"}); // create the writer on the heap using the constructor
When a multi-key writer is created, DataStorm notifies connected peers. If these peers have readers matching at least one of the writer's keys, the readers connect to the writer in order to receive samples from it.
Any-key writer
An any-key writer enables the publisher to publish samples for any keys. Contrary to single-key or multi-key writers, no keys are provided when the writer is created. An any-key writer object is created by calling the MultiKeyWriter
constructor described above. An empty vector of keys is specified for the keys parameter. A makeAnyKeyWriter
factory function is also provided:
template<typename K, typename V, typename UT> MultiKeyWriter<K, V, UT> makeAnyKeyWriter(const Topic<K, V, UT>& topic, const string& name = string(), const WriterConfig& config = WriterConfig());
The makeMultiKeyWriter
function accepts the same parameters as the MultiKeyWriter constructors except for the keys parameter which is missing. The key, value and update tag template parameters (K, V, UT) don't need to be provided, they are automatically deduced by the compiler from the topic parameter.
For example, to create a writer to write temperatures for any room:
DataStorm::Topic<string, float> temperatures = ...; AnyKeyWriter<string, float> writer1(temperatures); // using the constructor auto writer2 = makeAnyKeyWriter(temperatures); // using the factory function auto writer3 make_shared<MultiKeyWriter<string, float>>(temperatures); // create the writer on the heap using the constructor
When an any-key writer is created, DataStorm notifies connected peers. Since an any-key writer might publish samples for any keys, all the readers will connect to the any-key writer to potentially receive samples from it if it publishes samples for matching keys.
Publishing samples
To publish samples, the single-key writer KeyWriter
class provides four methods:
add(const Value&)
publishes anAdd
sample with the given valueupdate(const Value&)
publishes anUpdate
sample with the given valueremove(const Value&)
publishes aRemove
sample with the given valuetemplate<typename UpdateValue> std::function<void(const UpdateValue&)> partialUpdate(const UpdateTag&)
to obtain a function to publish aPartialUpdate
sample
The MultiKeyWriter
class for multi-key and any-key writers provides the same methods except that all these methods have an additional key parameter as the first parameter. An application can use these methods as follow to publish samples:
auto single = makeSingleKeyWriter(temperatures, "kitchen"); single.add(10.0f); // Add a kitchen temperature with the 10º initial value single.update(20.5f); // Update the kitchen temperature to 20.5º single.remove(); // Remove the kitchen temperature auto any = makeAnyKeyWriter(temperatures); any.add("kitchen", 10.0f); any.update("kitchen", 21.5f); any.update("living room", 16.5f); any.remove("living room");
The add
and remove
methods are useful to provide lifecycle information to subscribers. However, DataStorm doesn't require them to be called to start publishing samples for a given key. It also doesn't impose any restrictions on the order that they are called: you can call remove
before calling add
for example. The reader will receive a Remove
sample before receiving the Add
sample.
The partial update method is a template method that accepts the partial update tag and returns a function which can be used to publish partial updates. The application needs to provide the UpdateValue
template parameter. The UpdateValue
type is the type that will be used to marshal the partial update value over-the-wire. It's important that this type matches the update value type registered with the updater on the topic. If the type doesn't match, readers won't be able to unmarshal the update value. Here's an example that demonstrates how to use the partial update method:
auto single = makeSingleKeyWriter(temperatures, "kitchen"); auto increment = single.partialUpdate<int>("increment"); increment(2); // 2º increment of the temperature auto any = makeAnyKeyWriter(temperatures); auto incrementany = any.partialUpdate<int>("increment"); incrementany("living room", 2); incrementany("kitchen", 2);
History
A writer queues the samples it publishes in its sample history queue. This queue is used to provide the initial samples to newly connected readers. It can also be accessed by the application using the following writer methods:
getLast
returns the last published samplegetAll
returns all the published samples queued in the writer history
The number of samples kept in queue depends on a number of configuration variables. By default a writer only keeps the last published sample in its history.
Configuration
A writer supports several configuration parameters:
class Config { Ice::optional<int> sampleCount; Ice::optional<int> sampleLifetime; Ice::optional<ClearHistoryPolicy> clearHistory; }; class WriterConfig : public Config { Ice::optional<int> priority; };
The parameters are specified as optional values. You can only configure the parameters you need. If a parameter is not set, the existing value will be used.
Configuration parameters for writers can be specified and overriden at several levels:
- with configuration properties prefixed with
DataStorm.Topic
(e.g.:DataStorm.Topic.SampleCount
). - with configuration properties prefixed with
DataStorm.Topic.<topic name>
(e.g.:DataStorm.Topic.temperatures.ClearHistory
). - the topic default writer configuration structure set with the
setWriterDefaultConfig
method of theDataStorm::Topic
class. - when the writer is created using the last parameter of the writer make factory function presented above.
A parameter set a lower level (configuration provided to the writer make factory function) overrides the one set at a higher level (e.g.: configuration properties).
Sample count
The sample count configuration specifies the number of samples that will be kept in the writer queue. When the writer queue is full, the oldest samples are removed to make room for the newer published samples.
Sample lifetime
The sample lifetime configuration specifies how long a sample will be kept in the history. Samples older than the sample lifetime are removed from the queue.
Clear history
The history can be automatically cleared when a new sample is published and depending on the clear history policy configuration:
enum struct ClearHistoryPolicy { OnAdd, OnRemove, OnAll, OnAllExceptPartialUpdate, Never };
The clear history policy is based on the sample event. It can be cleared for all events or only for Add
or Remove
. The history can also be cleared for all events except if it's a partial update. This can be useful if you want the writer to keep all the partial updates between full updates.
Priority
You can set a priority for the writer using the priority configuration. A reader can be configured to discard samples based on the priority of the writer so that it only accept samples from the writer with the highest priority.
Listeners
Connected keys
The writer maintains a set of connected keys. This set represents all the keys for which readers are connected. The two listener functions registered with the writer's onConnectedKeys
method enable the application to monitor the changes to this set.
enum struct CallbackReason { Initialize, Add, Remove }; template<typename Key, typename Value, typename UpdateTag> class Writer { void onConnectedKeys(std::function<void(std::vector<Key>)> init, std::function<void(CallbackReason, Key)> update); };
The init callback is called with the initial set of connected keys. It's called immediately after the onConnectedKeys
method returns. The update callback accepts two parameters: an enumeration to specify if the callback is called following the connection or disconnection of a key and the key.
Connected readers
Readers connected with the writer can be monitored by registering two callback functions with the onConnectedReaders
method:
template<typename Key, typename Value, typename UpdateTag> class Writer { void onConnectedReaders(std::function<void(std::vector<std::string>)> init, std::function<void(CallbackReason, std::string)> update); };
The init callback is called with the initial set of connected readers. It's called immediately after the onConnectedReaders
method returns. The update callback accepts two parameters: an enumeration to specify if the callback is called following the connection or disconnection of a reader and the name of the reader.
Reader names are specified when the reader is created. If no name is provided, a default name is computed by DataStorm. This name is guaranteed to be unique within the scope of the DataStorm node.
Coordination
The writer class supports methods to coordinate with readers:
hasReaders
to check if readers are connectedwaitReaders
to wait for a given number of readers to connectwaitForNoReaders
to wait for all the readers to disconnect
In addition, the getConnectedKeys
method returns the set of keys for which readers are connected to and the getConnectedReaders
method returns the names of the connected readers. The wait methods will raise NodeShutdownException
if the node is shutdown.