stasher::client->subscribeserverstatus(): server status reports

#include <iostream>
#include <stasher/client.H>
#include <stasher/serverstatuscallback.H>
#include <x/fmtsize.H>

#include <string>

class mycallbackObj : public stasher::serverstatuscallbackObj {

public:
	mycallbackObj() {}
	~mycallbackObj() noexcept {}

	void serverinfo(const stasher::userhelo &serverinfo) override
	{
		std::cout << "Connected to " << serverinfo.nodename
			  << " (cluster " << serverinfo.clustername << ")"
			  << std::endl;

		std::cout << "Maximum "
			  << serverinfo.limits.maxobjects
			  << " objects, "
			  << x::fmtsize(serverinfo.limits.maxobjectsize)
			  << " aggregate object size, per transaction."
			  << std::endl
			  << "Maximum "
			  << serverinfo.limits.maxsubs
			  << " concurrent subscriptions." << std::endl;
	}

	void state(const stasher::clusterstate &state) override
	{
		std::cout << "Current master: " << state.master
			  << std::endl;

		for (auto &node:state.nodes)
		{
			std::cout << "    Peer: " << node << std::endl;
		}

		std::cout << "Quorum: full="
			  << x::tostring(state.full)
			  << ", majority="
			  << x::tostring(state.majority) << std::endl;
	}
};


void serverstatussubscriber()
{
	stasher::client client=stasher::client::base::connect();

	std::cout << "Subscribing to server status, press ENTER to stop"
		  << std::endl;

	auto subscriber=x::ref<mycallbackObj>::create();

	stasher::subscribeserverstatusresults results=
		client->subscribeserverstatus(subscriber);

	std::cout << "Subscription status: "
		  << x::tostring(results->status)
		  << std::endl;

	x::ref<x::obj> mcguffin=results->mcguffin;

	x::ref<x::obj> cancel_mcguffin=results->cancel_mcguffin;

	std::string dummy;
	std::getline(std::cin, dummy);
}

int main()
{
	try {
		serverstatussubscriber();
	} catch (const x::exception &e)
	{
		std::cerr << e << std::endl;
		exit(1);
	}

	return 0;
}

This is a subscription to a notification mechanism of the server's status. The parameter to stasher::client->subscribeserverstatus() is an x::ref to a subclass of stasher::client::base::subscriberObj that implements the two methods shown in this example.

The serverinfo() callback gets invoked once, shortly after subscribeserverstatus() returns (or just before it, dependencies on how fast the thread hamster runs). It reports the object repository server's name, and the resource limits for the connection.

The state() callback gets invoked shortly after serverinfo() returns, and any time thereafter when its reported information changes. The state() callback reports which node is the master controller, and all the connected peer nodes; as well as the quorum status.

stasher::client->subscribeserverstatus() returns a stasher::subscribeserverstatusresults, which is an x::ref to a reference-counted object with the following members:

status

The subscription status status, with stasher::req_processed_stat indicating that the server status subscription is succesfully established.

mcguffin

A mcguffin representing the subscription. There is no formal unsubscribe(), rather than subscription remains open as long as the mcguffin exists. Stopping the subscription involves simply letting the mcguffin go out of scope and get destroyed.

While a subscription remains open, the client object holds a strong reference on the server status subscriber callback. When the mcguffin goes out of scope and gets destroyed, if the client connection thread is in the middle of invoking the one of its callbacks, or is busy with something else at the moment, there may be a slight delay before the subscription gets wrapped up, and the connection thread releases its reference on the subscriber callback object; and it's remotely possible that one of the callbacks can get invoked at the same time, or just after, the mcguffin goes out of scope and gets destroyed.

cancel_mcguffin

The flip side of the coin. This is a mcguffin that's owned by the client connection thread. When the subscription gets closed, for any reason, the client connection thread releases its reference on the mcguffin.

It's possible that the server status subscription can get closed even before its mcguffin goes out of scope and gets destroyed, and the cancellation mcguffin provides the means for detecting this situation. The normal sequence of events when the subscription gets closed goes like this;

The last step also happens spontaneously in the event that the client connection thread's connection to the server breaks for any reason. The subscription does not get reopened automatically, when a new client connection thread reconnects to the stasher server. Attaching a destructor callback to the cancellation mcguffin (and releasing the reference on the cancellation mcguffin, and the stasher::subscribeserverstatusresults object) provides the means for detecting and handling this situation.

Note

The cancellation mcguffin's destructor callbacks also gets invoked when the subscription gets closed in the regular way, by destroying the subscription mcguffin.

Example:

$ ./subscribeserverstatus
Subscribing to server status, press ENTER to stop
Connected to octopus.objrepo.example.com (cluster objrepo.example.com)
Maximum 10 objects, 32 Mb aggregate object size, per transaction.
Maximum 10 concurrent subscriptions.
Current master: octopus.objrepo.example.com
    Peer: monster.objrepo.example.com
Subscription status: Transaction/request processed
Quorum: full=true, majority=true
Current master: octopus.objrepo.example.com
    Peer: monster.objrepo.example.com
Quorum: full=false, majority=false
Current master: monster.objrepo.example.com
    Peer: monster.objrepo.example.com
Quorum: full=false, majority=false
Current master: monster.objrepo.example.com
    Peer: monster.objrepo.example.com
Quorum: full=true, majority=true

Note

The limitations on callback methods described later, in the section called “What asynchronous C++ API methods can and cannot do”, apply to these callbacks too.