You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Leo Sutic <le...@inspireinfrastructure.com> on 2003/10/23 10:34:53 UTC

[RT] Pooling Pains

All,

I've been thinking about pooled components and how to get rid of the 
repeated lookup/release that they impose on every use, since we can't 
know whether a component is pooled, singleton or transient.

One solution is the Phoenix way: make all components threadsafe. But 
this doesn't work in applications like Cocoon where multiple, individual

component instances are used, unless an intermediary layer is introduced

in the form of a GeneratorManager. I.e. instead of doing:

    void handleRequest() {
        Generator g = (Generator) manager.lookup( Generator.ROLE );
        ....
    }

you would do:

    private GeneratorManager gm = null;

    void service( ServiceManager manager ) {
        gm = (GeneratorManager) manager.lookup( GeneratorManager.ROLE );
    }

    void handleRequest() {
        Generator g = gm.getGenerator();
        ....
    }

The advantages of this approach are:

 + some semblance of usefulness is restored to the compose() method.

 + no release() - the Generator can self-terminate at endDocument, or 
after generate().

However, the above turns into a lot of XManager interfaces. While they
are write-once, use often, and can be created by code generators, I 
still would like to stay away from them, because we still run into the 
wall of not knowing whether a component is pooled or not, meaning that 
every component must have an associated XManager interface. Bummer. Or?


What types of pooling do we have?

I see two different types of pooling: one type where we don't care about

object identity and one where we do. The second type is when we'd like
to have a permanent reference to something but for various reasons
can't.
An example would be an SQL Connection. We'd like to have a permanent
Connection, but due to resource constraints we simply can't.

But the second type is more interesting: This is the one used in Cocoon
for SAXTransformers - the client looks up X transformers and *expects*
to
get X different instances. I.e. in this case the "don't care whether
pooled
or not" is already broken. It had better be pooled, or transient. In
this
case, the interface (SAXTransformer) is inherently *not shareable* among
different clients.

I would suggest that all pooling of components where the interface isn't
shareable, such as a SAXTransformer, move to the XManager type of use.
Another approach would be to declare a framework-wide PoolManager
interface

    public interface PoolManager {
        Object get () throws Exception;
        void release (Object o) throws Exception;
    }

For the first case, with Connections, one can consider them to be of the
PerThread variant, with a twist:

 1. Proxy the Connection.

 2. Upon first call, the proxy obtains a real Connection.

 3. Upon close() the proxy releases the underlying Connection.

 4. Goto 2.

This will have the illusion of a persistent connection.

So we get the following lifestyles:

 1. Singleton - one instance shared among all clients.
                Automatic release according to component 
                dependencies when container shuts down.


    private MailService mailService = null;

    void service( ServiceManager manager ) {
        mailService = (MailService) manager.lookup( MailService.ROLE );
    }

    void handleRequest() {
        mailService.send( "Hello World!" );
    }

 2. Pooled/Shared - PerThread, in essence, but pooled in the 
    background. Appears to client as Singleton. Automatic
    release when interface method is called (close ()), disposal
    when container shuts down. Re-aquire from pool when
    needed.

    private Connection connection = null;

    void service( ServiceManager manager ) {
        connection = (Connection) manager.lookup( Connection.ROLE );
    }

    void handleRequest() {
        Statement s = connection.prepareStatement (); // Aquire
underlying connection from pool.
        ....

        connection.close(); // Drop connection back into pool.
    }


 3. Pooled/Not Shared - Like transient, but pooled behind the
                        scene.

    private GeneratorManager gm = null;

    void service( ServiceManager manager ) {
        gm = (GeneratorManager) manager.lookup( GeneratorManager.ROLE );
    }

    void handleRequest() {
        Generator g = gm.getGenerator(); // But Pooled!
        ....
    }

 4. Transient - XManager factory pattern.
                Release or no release, depends on XManager
                interface.

    private GeneratorManager gm = null;

    void service( ServiceManager manager ) {
        gm = (GeneratorManager) manager.lookup( GeneratorManager.ROLE );
    }

    void handleRequest() {
        Generator g = gm.getGenerator(); // New instance every time
        ....
    }

Bottom line is that I think each service interface not only specifies
what methods are available, but also limits the choices for lifestyle.
For example, a SAXTransformer can't be ThreadSafe. This means that we
must care to some extent whether the underlying implementation is pooled
or shared.

I think the above four lifestyles, very similar to the ones in Fortress,
maps well to most/all real-life interfaces.

/LS


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org