#include <stasher/heartbeat.H> class application_id; class application_status; typedef stasher::heartbeat<application_id, application_status> heartbeat; class heartbeatThreadObj : virtual public stasher::obj { heartbeat::obj_type *heartbeat_ptr; application_status status; public: heartbeatThreadObj(const application_status &initial_status) : status(initial_status) { } void run(const heartbeat &heartbeat_arg) { heartbeatptr= &*heartbeat_arg; // ... } void dispatch(const status_msg &msg) { status=msg.status; } void dispatch(const post_update_msg &msg) { heartbeatptr->update(msg.update_msg, status); } }; auto client=stasher::client::base::connect(); auto manager=stasher::manager::create(); auto thread=x::ref<heartbeatThreadObj>::create("initial_status"); x::run(thread, heartbeat::create(client, manager, "status", application_id("manager_node"), L"update_interval", std::chrono::minutes(10), L"stale_interval", std::chrono::minutes(5), [thread] (heartbeat::base::update_type_t update_type) { thread->post_update(update_type); }));
stasher::heartbeat
takes two template parameters,
the name of the class that serves as the application instance identifier
(each instance of the application must use a unique one), and a class that
serves as each instance's status.
All instances of the application must declare the same
and
application_id
.
application_status
and
application_id
must
support everything that any instance needs to use to identify itself, or to
announce its status.
The reference-counted application_status
stasher::heartbeat
's
create
() function takes the following arguments:
The client connection handle, and a manager object.
The name of the object in the repository where the heartbeat status gets posted to, “status” in this example.
The unique identifier for this application's instance, All instances of an application that post a heartbeat update must have a unique identifier.
Two application property names and the default value of each application property. See LIBCXX's documentation for more information on application properties.
In this example, they are called “update_interval”, which defaults to ten minutes; and “stale_interval”, which defaults to five minutes.
The first property specifies how often this heartbeat object updates its posted status. In this example, the update interval is ten minutes. Each posted status carries an expiration timestamp that's equivalent to the update interval plus an additional 50%, up to a maximum of another two minutes; in this case this becomes twelve minutes. The end result here is that every ten minutes the heartbeat object posts an updated status that expires in twelve minutes. The extra overhead provides for some overhead.
As part of updating its status in the shared heartbeat object,
stasher::heartbeat
checks for heartbeats from
other applications instances that already expired.
Expired heartbeats get removed completely
after the interval of time specified
by the stale interval. In this example, this application instance
removes all other instance's posted heartbeats five minutes after
they expire.
If all other instances also have a five minute stale interval setting, this results in the following: twelve minutes after this instance's last posted heartbeat, it expires; five minutes later, the next update to the heartbeat object removes this instance's status.
The actual time when the stale application status gets removed varies, since that happens only when an application posts a heartbeat object update. The maximum time that an instance's status could possibly remain in the shared heartbeat object before it gets removed can be computed as follows: the instance's expiration time (its periodic interval update plus the smaller of 50% of the periodic update interval or two minutes), plus the largest of the combined sum of another instance's heartbeat update interval and its configured stale interval (other instances of the application can have different periodic update and stale interval settings).
This is, of course, true as long as at least one other instance of the application remains running.
The last parameter to create()
is a functor that takes a
stasher::heartbeat<
parameter.
application_id
,application
>::base::update_type_tstasher::heartbeat
invokes the functor whenever
an update to the application status needs to be posted to the heartbeat
object.
The constructor installs a managed object subscription for the heartbeat object. The subscription remains active until the hearbeat object goes out of scope and gets destroyed, at which time the subscription is terminated.
The client connection handle and the manager object must remain in scope as long as the heartbeat object exists. They cannot be destroyed until the heartbeat object gets destroyed.
If a manager object gets destroyed, the heartbeat object may stop getting updated from the object repository.
The heartbeat object holds a reference on the client connection handle, and the application must destroy the heartbeat object before all of its own references to the client connection handle go out of scope. If they do, the heartbeat object ends up having the last reference to the client connection handle in scope. This will likely result in a deadlock, for the same reasons that are described in the section called “What asynchronous C++ API methods can and cannot do”.