dive into sobjectizer 5.5. third part. coops

40
Dive into SObjectizer-5.5 SObjectizer Team, May 2015 Third Part: More About Coops

Upload: yauheni-akhotnikau

Post on 16-Aug-2015

55 views

Category:

Software


4 download

TRANSCRIPT

Dive into SObjectizer-5.5

SObjectizer Team, May 2015

Third Part: More About Coops

This is the next part of the serie of presentations with deep introduction into features of SObjectizer-5.5.

This part is dedicated to such important feature as cooperations. In particular:

● parent-child relationship;● resource lifetime management;● reg/dereg notificators.

SObjectizer Team, May 2015

Cooperation (or coop in short) is a way for binding several tightly related agents into a whole entity.

Coop is registered in SObjectizer Environment in a transactional manner: all agents from the cooperation must be registered successfully or no one of them.

When a coop is being deregistered all its agents are deregistered and destroyed at the same time.

SObjectizer Team, May 2015

There could be agents which require creation of additional coop(s) for performing their work.

Let's imagine an agent which is responsible of receiving and processing of some requests.

Payment requests, for example.

SObjectizer Team, May 2015

Processing of each payment request requires several operations:● checking payments parameters,● checking the operation risks,● checking the availability of funds● and so on...

The request receiver could do those actions for every payment by itself. But this will lead to very complicated logic of request receiver.

SObjectizer Team, May 2015

There is much simpler approach: delegation of processing of one request to a separate processor agent.

In that case the request receiver will only receive new requests and create new coops with actual request processors for each new request.

Receiver and processor agents will have more simple logic and it is good.

But there will be a new question...

SObjectizer Team, May 2015

Who and how will control lifetime of all cooperations with request processors?

SObjectizer Team, May 2015

Very simple view of that problem:

Someone could call deregister_coop() for request receiver’s coop. As result all coops with request processors must be deregistered too.

But how it could be done?

SObjectizer Team, May 2015

Such feature as child coop is coming into play here.

SObjectizer Team, May 2015

SObjectizer-5 allows to mark new coop as a child of any existing coop.

In this case SObjectizer guarantees that all children coops will be deregistered and destroyed before its parent coop.

SObjectizer Team, May 2015

It means that if we have, for example, a parent coop with name “manager” and a child coop with name “request_receiver” and do call:

env.deregister_coop( "manager", so_5::rt::dereg_reason::normal );

Then SObjectizer-5 will deregister and destroy “request_receiver” and only then “manager” coop will be deregistered and destroyed.

SObjectizer Team, May 2015

Child coop could have its own child coops too.

It means that “request_receiver” coop could have any number of child coops like “request_processor_1”, “request_processor_2” and so on.

All of them will be automatically destroyed when top-level parent coop “manager” is deregistered.

SObjectizer Team, May 2015

Parent-child relationship between coops allows to build coops hierarchies: a top-level coop creates child coops, those create its own child coops and so on.

If a top-level coop is being deregistered by some reason then all its child coops (and children of children and so on) will be deregistered too. A programmer could no takes care about this.

SObjectizer Team, May 2015

There are several ways to make a child coop...

SObjectizer Team, May 2015

The first way, the oldest and verbose:

auto coop = env.create_coop( "request_processor_1" );coop->set_parent_coop_name( "request_receiver" );... // Filling the coop.env.register_coop( std::move( coop ) );

Coop “request_processor_1” will be a child for coop “request_receiver”.

SObjectizer Team, May 2015

The second one applicable if there is an agent from the parent coop:

void parent_agent::make_new_coop(){ auto coop = so_5::rt::create_child_coop( *this, "request_processor_1" ); ... // Filling the coop. so_environment().register_coop( std::move( coop ) );}

Name of the parent coop will be set automatically.

SObjectizer Team, May 2015

The third one is available since v.5.5.5:

void parent_agent::make_new_coop(){ so_5::rt::introduce_child_coop( *this, "request_processor_1", []( so_5::rt::agent_coop_t & coop ) { ... // Filling the coop. } );}

Name of the parent coop will be set automatically.

SObjectizer Team, May 2015

Resource Lifetime Management

SObjectizer Team, May 2015

Sometimes it is necessary to manage lifetime of some resources.

For example all agents from your coop should have a reference to the same DB connection object.

If this connection object is allocated dynamically you can pass a shared_ptr to every agent's constructor. Something like:

SObjectizer Team, May 2015

class first_agent : public so_5::rt::agent_t { ...public : first_agent( context_t ctx, std::shared_ptr< db_connection > conn, ... ); ...private : std::shared_ptr< db_connection > connection_;};

class second_agent : public so_5::rt::agent_t { ...public : second_agent( context_t ctx, std::shared_ptr< db_connection > conn, ... ); ...private : std::shared_ptr< db_connection > connection_;};

env.introduce_coop( []( so_5::rt::agent_coop_t & coop ) { std::shared_ptr< db_connection > connection = connect_to_db(...);

coop.make_agent< first_agent >( connection, ... ); coop.make_agent< second_agent >( connection, ... ); ...} );

SObjectizer Team, May 2015

In such case you make tight coupling between application domain logic of your agents and DB connection lifetime management.

SObjectizer Team, May 2015

What if this lifetime management need to be changed in the future? What if connection object is controlled by someone else and you have a simple reference to it (not shared_ptr)?

You will need to do some rewriting...

SObjectizer Team, May 2015

class first_agent : public so_5::rt::agent_t { ...public : first_agent( context_t ctx, db_connection & conn, ... ); // The constructor has to be changed. ...private : db_connection & connection_; // Declaration of the member has to be changed.};

class second_agent : public so_5::rt::agent_t { ...public : second_agent( context_t ctx, db_connection & conn, ... ); // The constructor has to be changed. ...private : db_connection & connection_; // Declaration of the member has to be changed.};

...db_connection & conn = receive_connection_from_somewhere();env.introduce_coop( [&conn]( so_5::rt::agent_coop_t & coop ) { coop.make_agent< first_agent >( conn, ... ); coop.make_agent< second_agent >( conn, ... ); ...} );

SObjectizer Team, May 2015

SObjectizer-5 has a tool for decoupling resource lifetime management from agent's domain-specific logic.

This is agent_coop_t::take_under_control().

Method agent_coop_t::take_under_control() allows to pass dynamically allocated object under the control of the cooperation.

The object placed under control will be deallocated only after destroying all agents of the coop.

SObjectizer Team, May 2015

The behaviour of take_under_control() allows to use the reference to controlled object even from agent's destructor...

SObjectizer Team, May 2015

class request_processor : public so_5::rt::agent_t{public : request_processor( context_t ctx, db_connection & connection ); ~request_processor() { if( !commited() ) // Assume that this reference is still valid. connection_.commit(); } ...private : db_connection & connection_;};...env.introduce_coop( []( so_5::rt::agent_coop_t & coop ){ // Place DB connection under the control of the cooperation. auto & connection = *coop.take_under_control(create_connection(...)); // Reference to DB connection will be valid even in requests_processor's destructor. coop->make_agent< request_processor >( connection ); ...} );

SObjectizer Team, May 2015

Parent-child relationship could also be used for resource lifetime management...

SObjectizer Team, May 2015

class request_receiver : public so_5::rt::agent_t{ std::unique_ptr< db_connection > connection_; ...public : virtual void so_evt_start() override { connection_ = make_db_connection(...); ... } ...private : void evt_new_request( const request & evt ) { // New request handler must be created. so_5::rt::introduce_child_coop( *this, [=]( so_5::rt::agent_coop_t & coop ) { // Reference to DB connection will be valid even in requests_processor's destructor. // It is because agents from child cooperation will be destroyed before any of // agents from the parent cooperation. coop->make_agent< request_processor >( connection ); ... } ); }};

SObjectizer Team, May 2015

It is not easy to detect precise moments when a coop is completely registered or completely deregistered.

The biggest problem is a detection of complete coop deregistration.

It is because calling to environment_t::deregister_coop() just initiates coop deregistration process. But the entire process of deregistration could take a long time.

SObjectizer Team, May 2015

To simplify that there are such things as registration and deregistration notificators.

Notificator could be bound to a coop and it will be called when coop registration/deregistration process is finished.

SObjectizer Team, May 2015

The simplest reg/dereg notificators:env.introduce_coop( []( so_5::rt::agent_coop_t & coop ) { coop.add_reg_notificator( []( so_5::rt::environment_t &, const std::string & name ) { std::cout << "registered: " << name << std::endl; } ); coop.add_dereg_notificator( []( so_5::rt::environment_t &, const std::string & name, const so_5::rt::coop_dereg_reason_t & ) { std::cout << "deregistered: " << name << std::endl; } ); ...} );

Name of coop will be printed to stdout on coop registrationand deregistration.

SObjectizer Team, May 2015

Usually reg/dereg notifications are used for sending messages to some mboxes.

Because this scenario is widely used there are two ready-to-use notificators in SObjectizer-5.5.

They are created by make_coop_reg_notificator() and make_coop_dereg_notificator() functions...

SObjectizer Team, May 2015

The standard notificators:

auto notify_mbox = env.create_local_mbox();env.introduce_coop( [&]( so_5::rt::agent_coop_t & coop ) { // An instance of so_5::rt::msg_coop_registered will be sent to notify_mbox // when the cooperation is registered. coop.add_reg_notificator( so_5::rt::make_coop_reg_notificator( notify_mbox ) );

// An instance of so_5::rt::msg_coop_deregistered will be sent to // notify_mbox when the cooperation is deregistered. coop.add_dereg_notificator( so_5::rt::make_coop_dereg_notificator( notify_mbox ) ); ...} );

SObjectizer Team, May 2015

Coop dereg notificators can be used for implementation of Erlang-like supervisors.

For example, request receiver could receive notification about deregistration of child coops and restart them if they fail...

SObjectizer Team, May 2015

Very simple way of controlling a child (1/2):class request_receiver : public so_5::rt::agent_t{public : virtual void so_define_agent() override { // msg_coop_deregistered must be handled. so_subscribe_self().event( &request_receiver::evt_child_finished ); ... } ...private : void evt_new_request( const request & req ) { auto child_name = store_request_info( req ); so_5::rt::introduce_child_coop( child_name, [&]( so_5::rt::agent_coop_t & coop ) { ... // Filling the coop with agents. // Dereg notificator is necessary to receive info about child disappearance.

SObjectizer Team, May 2015

Very simple way of controlling a child (2/2): // Standard notificator will be used. coop.add_dereg_notificator( // We want a message to request_receiver direct_mbox. so_5::rt::make_coop_dereg_notificator( so_direct_mbox() ) ); } ); ... }

void evt_child_finished( const so_5::rt::msg_coop_deregistered & evt ) { // If child cooperation failed its dereg reason will differ // from the normal value. if( so_5::rt::dereg_reason::normal != evt.m_reason.reason() ) recreate_child_coop( evt.m_coop_name ); else remove_request_info( evt.m_coop_name ); }...};

SObjectizer Team, May 2015

That is almost all what it needs to be known about agent coops.

There are yet more issues like coop deregistration reasons and exception reaction inheritance…

But those topics will be covered in the next parts of “Dive into SObjectizer-5.5”

SObjectizer Team, May 2015

Additional Information:

Project’s home: http://sourceforge.net/projects/sobjectizer

Documentation: http://sourceforge.net/p/sobjectizer/wiki/

Forum: http://sourceforge.net/p/sobjectizer/discussion/

Google-group: https://groups.google.com/forum/#!forum/sobjectizer

GitHub mirror: https://github.com/masterspline/SObjectizer