This document describes how to build extensions for ResourcePool. As you will see you need only a few lines of code to create your own resource type. Afterwards you will be able to use this resource for ResourcePool and ResourcePool::LoadBalancer.
A resource by means of ResourcePool is some perl object which could benefit from the feature set provided by ResourcePool and ResourcePool::LoadBalancer. The reason why I use such a meaningless sentence to describe is what a resource is, is that there is no other clear separation which could be used. In many cases a resource is something which has a connection to a server like a database connection. But ResourcePool is not limited to such resources, to be honest I have some plans for ResourcePool which will result in the ability to use ResourcePool for LWP requests (including load balancing and fail over) and so on. But that's not finished yet. If it is easier for you, think of a resource as a connection to a server.
Every ResourcePool resource consists of two major parts:
- a resource adapter
Is a adapter which gives each resource a common interface. This interface covers basic requirements such as closing a resource. ResourcePool::Resource acts as a base class for all resources.
- a factory
Is used to construct resources on demand. Is usually not more than a storage of the required information to create a resource. ResourcePool::Factory acts as a base class for all factories.
If you have implemented this two packages you are nearly done. The only missing parts are: documentation, tests and uploading your extension to CPAN.
A resource adapter has to provide a generic interface to ResourcePool for your resource. This interface is used to interact with the resource. Some of the methods are optional, you can just skip them if you have carefully derived your own resource adapter from ResourcePool::Resource which will provide some defaults for you.
Each resource adapter manages exactly one resource. This means for example one database connection or whatever your resource might be. The pooling and management of more resources is done by ResourcePool and does not require any attention of your resource adapter.
I will describe the methods of this class in the order in which they are used by ResourcePool.
The constructor of a resource adapter will only be used from the according factory. You have all the freedom you can imagine for the prototype of the constructor (as you have it in every derived class in every programming language). I suggest to name your constructor new() (a perl convention, not a language requirement).
The actual work the constructor has to do is to create the resource (e.g. database connection) and storing it in its private data. If everything went fine the constructor must return its bless()ed reference. Otherwise you have the option to return undef to indicate that the creation of a resource failed.
If your constructor returns an object, ResourcePool will add this resource to its internal pool of available resources.
This method is used to check the vitality of this resource. ResourcePool calls this method before it uses the resource to check it's validity. ResourcePool will not use this resource any more if this method returns a false value.
The precheck() method is very important to the high availability functionality of ResourcePool and ResourcePool::LoadBalancer. This method, together with postcheck(), builds the backbone of the fault detection mechanism. If you want ResourcePool to detect broken resources you need to carefully implement this method to return a false value if the resource is not longer valid.
This method is most probably implemented by doing so nop (no operation) with the resource. Choose the cheapest non modifying operation possible and analyze it's result. If everything is as expected return true otherwise false. (The DBI extension does a ping() to detect broken connections)
This method can also be used to implement some code which restores a state for this resource. This way you can guarantee a common state of the resources you obtain from ResourcePool. Imagine a database connection pool which guarantees you that AutoCommit is off on the handles you get from the pool, even if you have returned a connection where you manually changed the AutoCommit setting.
If your precheck() method returns a true value, ResourcePool will go on and supply the resource to the client code. Otherwise it will throw this resource away and performing the recovery as described in the ResourcePool documentation.
This method just returns the internally stored resource. You have to return the actual resource you are implementing the adapter for. E.g. the DBI handle or whatever your extension manages. The return value of this method will be passed to the client code as return value of the get() method.
ResourcePool will than remove this resource from it's list of available resources (so that a used resource can not be used simultaneously).
From the moment on where this method returns you lose control over this resource. Later, when you get back the resource from the client (which has called the free() method of ResourcePool) you can not make any assumptions of the state of the resource. The client could have done everything which can be done with this resource, including closing it (e.g. disconnecting from the database).
The postcheck() method is very similar to the previously described precheck() method. The only difference is that the postcheck() method is called after the client has returned a resource to the pool.
As mentioned above, you can not make any assumptions of the state of the resource. The client could have done everything. As for the precheck() method you can use this method also to place some code which restores the original state or doing some cleanup (e.g. a rollback() on a database connection).
ResourcePool will throw this resource away if this method returns a false value, otherwise ResourcePool will add this resource to it's list of available resources and use it again if required (the cycle starts again with the precheck() method).
This method is used (surprise surprise) to close the resource. It's called immediately before the resource adapter itself gets destroyed.
The normal life cycle of a resource adapter ends here. The remaining method are used to handle failure...
This method is used to close a resource which is known to be broken (either because one of precheck() or postcheck() failed or the client used the fail() method of ResourcePool to hand it back to the pool.
The difference to the close() method is very subtle. For many resource there is no difference anyway. For some others you might want to skip some cleanup operations which will fail anyway if the resource is broken.
The following simplified call-graph tries to demonstrate the most important important processes which take place with your resource. Please note that this is reduced to the processes relevant to the resource adapter class.
Client . ResourcePool and . resource adapter code . LoadBalancer . . . --------> get() ----------------------> precheck() -----------+ . . | . +------------------------<-----------------------+ . | on failure . . +------------------------> fail_close() ---------+ . | tries another . | . | available resource <---------------------------+ . | . . | on success . . +------------------------> get_plain_resource() -+ . . | --------<-----------------------------<-----------------------+ . . . . --------> free() ---------------------> postcheck() ----------+ . . | . +------------------------<-----------------------+ . | on failure . . +------------------------> fail_close() ---------| . | on success . . +--> Add back to pool . . . . . --------> fail() ---------------------> fail_close() ---------| . .
(the free() and fail() methods do return meaningful values which are in not directly related to the resource adapter implementation.)
The diagram is separated into three sections: the leftmost section represents the user code, this is the software which uses ResourcePool; the middle one represents the ResourcePool core. For this diagram it makes no difference if you are using ResourcePool::LoadBalancer as well; the right most section represents the resource adapter of the extension.
The purpose of a factory is to store information which is required to create a resource. The factory is constructed and configured by the user code and then passed to the ResourcePool on construction.
Returns a resource adapter (a ResourcePool::Resource derived class). ResourcePool uses this method to create new resources as required. As a result of the fact the the constructor of a resource adapter may fail and return undef, the create_resource method is also allowed to return undef. In that case ResourcePool would apply the error handling as configured.
This method is used if ResourcePool does some error reporting. You should return a human readable string which describes the resources which are created through this factory. For a database resource this could be the data-source name.
As you have learned every ResourcePool consists of two parts. The namespace where you should place this two packages are ResourcePool::Factory and ResourcePool::Resource. Below this two namespaces you should add the complete perl-class name for which resource your extension is. E.g. The Net::LDAP factory and resource adapter are called ResourcePool::Factory::Net::LDAP and ResourcePool::Resource::Net::LDAP.
If you upload you extension to CPAN you should name the package according to your resource adapter name.
The reality delivers the best examples. Please have a look into the implementation of the already existing extensions like DBI or Net::LDAP.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.