mina-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Alan D. Cabrera" <l...@toolazydogs.com>
Subject Re: [MINA 3.0] Initial thoughts on FilterChain
Date Fri, 27 Nov 2009 17:36:04 GMT

On Nov 27, 2009, at 9:26 AM, Emmanuel Lecharny wrote:

> On Fri, Nov 27, 2009 at 5:28 PM, Alan D. Cabrera  
> <list@toolazydogs.com> wrote:
>>
>> On Nov 26, 2009, at 7:51 AM, Emmanuel Lecharny wrote:
>>
>>> About the Filters and the Chain, I also have some initial thoughts  
>>> (even
>>> if they are not recent). Here they are.
>>>
>>> First, let's start with what we really need :
>>> o A chain has a start
>>> o A chain has an end
>>> o Both start and end are Filter instances
>>> o We have as many filters as we need in the chain
>>> o Filters can be added or removed dynamically
>>> o A chain is instanciated for each session (if we have 10K  
>>> sessions, we
>>> have 10K instances of chain)
>>> o A filter can be activated (enabled) or disabled, dynamically
>>>
>>> That being said, we have many ways to implement such a mechanism.  
>>> let's
>>> consider we are in a Filter, and we want to call the next filter.  
>>> We have
>>> three options here :
>>> * The NextFilter is already computed, and is a part of the  
>>> Filter's data.
>>> This resolves to a direct call :
>>> ...
>>> nextFilter.<action>( params );
>>> ...
>>>
>>> * The NextFilter is computed dynamically :
>>> ...
>>> nextFilter = computeNext( params );
>>> nextFilter.<action>( params );
>>> ...
>>>
>>> * The nextFilter is computed by the caller, and is a parameter :
>>>
>>> public void <action>( Filter nextFilter, <other params>... ) {
>>> ...
>>> nextFilter.<action>( params );
>>> ...
>>>
>>> The current version (2.0) implements the third form. From the  
>>> debugging
>>> PoV, it's a nightmare, as you have to step through many  
>>> intermediate calls
>>> (in fact, an other call for each filter). The StackTrace is heavy.  
>>> The next
>>> filter is not known when you are in a Filter, as it's computed  
>>> outside of
>>> the current method.
>>>
>>> The first approach is way simpler, as is the second one.
>>>
>>> Let's go a bit deeper : if there is an executor in the chain,  
>>> suddenly
>>> things get a bit muddy. The "one session = one chain instance" is  
>>> still
>>> correct, but we may have two (or more) messages being processed on  
>>> one
>>> session on the same chain. If the chain is dynamically modified,  
>>> then it may
>>> be ok for the thread modifying the chain, but not for the other  
>>> threads.
>>> Here is a picture describing this mechanism :
>>>
>>>
>>> Thread1                                                  /--->
>>> [Filter3]--->[tail]
>>> Thread2 [head]---> [filter1]--->[filter2]--->[executor]-*---->
>>> [Filter3]--->[tail]
>>> Thread3                                                  \--->
>>> [Filter3]--->[tail]
>>
>> Whoa.  Look at that ascii art.  No wonder your Dell had an  
>> aneurism.  ;)
>>
>>> Now, if the Thread 2 modify the chain to add a Filter4, then there  
>>> is no
>>> guarantee that Thread1 will call it. That means we must protect  
>>> the chain
>>> against concurrent modifications.
>>>
>>> Another drawback of the first approach is that you must declare an
>>> instance of a Filter for your chain, as it carries some  
>>> information specific
>>> to your session. We would rather have a stateless Filter...
>>>
>>> The second approach require an external system to give you the next
>>> filter. It can be the session, which is passed as a parameter for  
>>> each
>>> filter call.In this case, if the external system is protected  
>>> against
>>> concurrent modifications, then we are safe (except if we want each  
>>> thread to
>>> define its own chain in the previous example... In this case, the  
>>> executor
>>> must be responsible for the chain duplication, and probably store  
>>> it in a
>>> ThreadLocal variable )
>>
>> In the second approach, won't we end up with the same problems as  
>> Mina 2.0,
>> "From the debugging PoV, it's a nightmare, as you have to step  
>> through many
>> intermediate calls (in fact, an other call for each filter). The  
>> StackTrace
>> is heavy. The next filter is not known when you are in a Filter, as  
>> it's
>> computed outside of the current method"?
>
> No : the next is computed inside the method :
>
> ...
> nextFilter = computeNext( params );
> nextFilter.<action>( params );
> ...
>
> so you can immediately know which is the next filter (as it's stored
> in the nextFilter variable) and you don't have one more call in the
> stack, as once you get the nextFilter, you are done. This was not the
> case in MINA 2.0, because the computation was delegated to an
> intermediate object.
>
>
>> To make things clear in my mind, where would we need such a feature?
>
> If you get a message that can be decoded in many different ways, for
> instance, depending on the first byte (just an exemple). The
> computeNext method can take some parameter, and returns the next
> filter, depending on the context. Ok, at the end of the day, it's a
> state machine :)
>
>>  Couldn't we just load all the filters in the chain and they decide  
>> whether
>> or not to do anything?
>
> Yes, of course, and we should probably implement most of the protocols
> this way. The idea is to keep the full stuff simple, but allow a user
> to go a bit further if needed. At this point, we may want to offer
> differnt kind of Chain implementation ...


Maybe we could align the design more closely w/ a state machine then  
and make it crazy easy to implement one.

Let IoSession be the state machine.  We could have filters belong to  
various states.  Filter chains would be assembled depending on the  
state of the IoSession.  Any filter can decide the change the state of  
the IoSession for the next message coming down the pipeline.



Regards,
Alan




Mime
View raw message