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

Dive into SObjectizer 5.5. Third part. Coops

Aug 16, 2015

Download

Software

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Dive into SObjectizer 5.5. Third part. Coops

Dive into SObjectizer-5.5

SObjectizer Team, May 2015

Third Part: More About Coops

Page 2: Dive into SObjectizer 5.5. Third part. 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

Page 3: Dive into SObjectizer 5.5. Third part. Coops

Parent-Child Relationship

SObjectizer Team, May 2015

Page 4: Dive into SObjectizer 5.5. Third part. Coops

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

Page 5: Dive into SObjectizer 5.5. Third part. Coops

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

Page 6: Dive into SObjectizer 5.5. Third part. Coops

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

Page 7: Dive into SObjectizer 5.5. Third part. Coops

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

Page 8: Dive into SObjectizer 5.5. Third part. Coops

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

SObjectizer Team, May 2015

Page 9: Dive into SObjectizer 5.5. Third part. Coops

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

Page 10: Dive into SObjectizer 5.5. Third part. Coops

Such feature as child coop is coming into play here.

SObjectizer Team, May 2015

Page 11: Dive into SObjectizer 5.5. Third part. Coops

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

Page 12: Dive into SObjectizer 5.5. Third part. Coops

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

Page 13: Dive into SObjectizer 5.5. Third part. Coops

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

Page 14: Dive into SObjectizer 5.5. Third part. Coops

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

Page 15: Dive into SObjectizer 5.5. Third part. Coops

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

SObjectizer Team, May 2015

Page 16: Dive into SObjectizer 5.5. Third part. Coops

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

Page 17: Dive into SObjectizer 5.5. Third part. Coops

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

Page 18: Dive into SObjectizer 5.5. Third part. Coops

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

Page 19: Dive into SObjectizer 5.5. Third part. Coops

Resource Lifetime Management

SObjectizer Team, May 2015

Page 20: Dive into SObjectizer 5.5. Third part. Coops

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

Page 21: Dive into SObjectizer 5.5. Third part. Coops

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

Page 22: Dive into SObjectizer 5.5. Third part. Coops

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

SObjectizer Team, May 2015

Page 23: Dive into SObjectizer 5.5. Third part. Coops

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

Page 24: Dive into SObjectizer 5.5. Third part. Coops

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

Page 25: Dive into SObjectizer 5.5. Third part. Coops

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

Page 26: Dive into SObjectizer 5.5. Third part. Coops

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

SObjectizer Team, May 2015

Page 27: Dive into SObjectizer 5.5. Third part. Coops

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

Page 28: Dive into SObjectizer 5.5. Third part. Coops

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

SObjectizer Team, May 2015

Page 29: Dive into SObjectizer 5.5. Third part. Coops

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

Page 31: Dive into SObjectizer 5.5. Third part. Coops

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

Page 32: Dive into SObjectizer 5.5. Third part. Coops

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

Page 33: Dive into SObjectizer 5.5. Third part. Coops

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

Page 34: Dive into SObjectizer 5.5. Third part. Coops

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

Page 35: Dive into SObjectizer 5.5. Third part. Coops

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

Page 36: Dive into SObjectizer 5.5. Third part. Coops

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

Page 37: Dive into SObjectizer 5.5. Third part. Coops

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

Page 38: Dive into SObjectizer 5.5. Third part. Coops

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

Page 39: Dive into SObjectizer 5.5. Third part. Coops

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

Page 40: Dive into SObjectizer 5.5. Third part. Coops

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