stasher::manager
->manage_object
(): automatically retrieve an object any time it changes#include <stasher/client.H> #include <stasher/manager.H> #include <stasher/managedhierarchymonitor.H> #include <x/destroycallbackflag.H> #include <x/fditer.H> #include <iterator> #include <iostream> // An example of a stasher::managedobjectObj implementation. class mymanagedobjectObj : public stasher::managedobjectObj { public: mymanagedobjectObj() { } ~mymanagedobjectObj() { } // Invoked when the connection gets established, or breaks. void connection_update(stasher::req_stat_t status) override { std::cout << ("Connection update: " + x::tostring(status) + "\n") << std::flush; } // Invoked when the object gets created or updated. // If the object exists at the time that the subscription got opened, // or after reestablishing a connection with the server, this callback // gives the object's uuid and its contents. void updated(const std::string &objname, const x::uuid &uuid, const x::fd &contents) override { std::cout << objname << " (" << x::tostring(uuid) << "):" << std::endl; std::copy(x::fdinputiter(contents), x::fdinputiter(), std::ostreambuf_iterator<char>(std::cout)); std::cout << std::endl; } // Invoked when the object gets removed. // This callback gets invoked if the object does not exist at the time // that the subscription got opened, or after reestablishing a // connection with the server. void removed(const std::string &objname) override { std::cout << objname << ": removed" << std::endl; } }; void monitor(const std::list<std::string> &objects) { x::destroyCallbackFlag::base::guard guard; auto client=stasher::client::base::connect(); guard(client); // When we return from this func, make sure this goes away. auto manager=stasher::manager::create(L"", "10 seconds"); std::cout << "Starting subscriptions, press ENTER to stop" << std::endl; auto monitor=x::ref<mymanagedobjectObj>::create(); std::list<x::ref<x::obj> > mcguffins; for (auto &name:objects) { mcguffins.push_back(manager-> manage_object(client, name, monitor)); } std::string dummy; std::getline(std::cin, dummy); } int main(int argc, char **argv) { try { std::list<std::string> args(argv+1, argv+argc); monitor(args); } catch (const x::exception &e) { std::cerr << e << std::endl; exit(1); } return 0; }
A managed object goes the next step after a
managed subscriber by automatically
retrieving the current value of the object when the subscription gets
opened and any time the subscribed to object changes.
This is for individual objects only, see
the section called “
(): maintaining an internal snapshot of an object hierarchy”
for a comparable interface for subscribed hierarchies.
stasher::manager
->manage_hierarchymonitor
Define a subclass of
stasher::managedobjectObj
and implement its methods, as shown in this example.
()
takes three parameters: a client connection, the name of an object
and an stasher::manager
->manage_objectx::ref
to a subclass of
stasher::managedobjectObj
.
As shown in this example, the same instance can be installed for more than
one managed object.
manage_object
() does not retrieve the object's
current value, before returning. It sets up a subscription, and sets the
gears in motion. The client connection thread is on the job, and the initial callback to
updated
() or removed
()
is pending, and will follow soon (unless the connection with the
server is broken, as described below, in which case stay tuned).
To see how this works,
run managedobject.C
and give one or more object names
on the command line. The main execution thread sets up the
subscriptions, and waits for Enter. All the output comes
from the callbacks, as documented in the example.
Use simpleput.C
or stasher to
create, update, and remove the managed objects:
$ ./managedobject fruits/apple fruits/grape
Starting subscriptions, press ENTER to stop
Connection update: Transaction/request processed
Connection update: Transaction/request processed
fruits/apple (IdAzcA5Ga9YjVW00dTtmJm00000eyWG00318n4AS):
delicious
fruits/grape: removed
fruits/apple (JdAzcA5Ga9YjVW00dTtmJm00000eyWG00318n4AS):
very delicious
fruits/grape (KdAzcA5Ga9YjVW00dTtmJm00000eyWG00318n4AS):
juicy
Connection update: Connection to the server failed
Connection update: Connection to the server failed
Connection update: Transaction/request processed
Connection update: Transaction/request processed
fruits/grape (KdAzcA5Ga9YjVW00dTtmJm00000eyWG00318n4AS):
juicy
fruits/apple (JdAzcA5Ga9YjVW00dTtmJm00000eyWG00318n4AS):
very delicious
fruits/grape: removed
()
returns a mcguffin that represents the object it's managing. There is no
explicit un-manage method. The manager stops managing the object when
its mcguffin goes out of scope and gets destroyed.
stasher::manager
->manage_object
The
connection_update
() callback reports the status
of the subscription.
connection_update
() gets invoked shortly after
manage_object
() returns (if not right before it),
and any time the status of the connection with the server changes.
stasher::req_processed_stat
reports a success in
opening the managed object's subscription.
A connection_update
() of
stasher::req_disconnected_stat
indicates that the
connection with the stasher server was lost. In this example, the connection
gets lost, and then reestablished by the manager shortly thereafter.
The manager keeps trying to reconnect, periodically, reporting the
result to connection_update
(). Other error codes,
besides
stasher::req_processed_stat
and
stasher::req_disconnected_stat
, indicates a failure
to establish a subscription for the managed object, for some other
reason.
A connection_update
() of
stasher::req_processed_stat
gets shortly followed by
either an updated
() or a
removed
() callback.
In the example above, the two messages after the initial pair of
“Connection update: Transaction/request processed” are
callbacks reporting the initial value of each object
at the time the managed subscription gets established:
either the existing object's uuid, and its contents, or that the
managed object does not exist (but the subscription is still active,
waiting for someone to create the object).
This is the case after the initial
stasher::req_processed_stat
, after a managed object
subscription gets opened, and whenever the manager reconnects to the
server after recovering from a broken connection.
After the initial updated
() or
removed
(),
additional calls to
updated
() and
removed
() report whenever the object changes,
accordingly. In the example above, there are two updates, one for
“fruits/apple” and one for “fruits/grape”
before the connection with the server gets broken.
It's possible to get two removed
() in
a row, or two updated
() in a row with the same
uuid and contents. This occurs in two marginal situations:
Another application updates the managed object at the same time that the managed object subscription gets established, or when the manager reconnects to the server.
The object gets updated quickly two or more times. The first updated
value cannot be retrieved, quickly enough, before the
second update gets processed.
updated
() and/or
removed
() reports the most recent object
contents/status, each time.
The application should keep track of the object's state as it knows,
and compare it to the latest reported state by
updated
() or
removed
(), and proceed accordingly.
The way to look at
updated
() and
removed
() is that they report the latest known
value or state of the object, every time; and any change to the object
triggers an update.
The above example shows a typical output of managing two objects:
“fruits/apple”, which exists already at the time
that managedobject.C
starts, and
“fruits/grape” which does not.
In this example, the subscriptions for both objects get established,
and each individually managed object's
connection_update
()
gets called with stasher::req_processed_stat
(reporting “Transaction/request processed”).
This is
followed by an updated
() call for
“fruits/apple”, and removed
() for
th “fruits/grape” that does not exist.
Additional removed
() and
updated
() callbacks follow,
as these object get updated periodically. Some time later, the
connection with the server breaks, for some reason (reported for each
managed object individually); the manager
tries again later and reestablished the connection, then gets the current
value of each managed object (since it might've changed since the
connection was lost).
managedobject.C
assumes that these
objects contain plain
text. updated
() reads the object's open file
descriptor, and copies them to output.
All managed objects' callbacks have certain limitations, see the section called “What asynchronous C++ API methods can and cannot do” for more information.
Each managed object counts towards the client's connection limits.