You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mina.apache.org by Emmanuel Lecharny <el...@gmail.com> on 2008/11/07 02:59:59 UTC
[Chain refactoring] Some fedback and ideas
Hi guys,
here are some thoughts and ideas I have had those last few days,
thoughts that I have experimented in branches. It's not finished yet,
but I think I have something likely to work well and be easier to use
than the current implementation.
So here are the gathered thoughts, please feel free to give me feedback :
Refactoring the current chain : possible implementations
---------------------------------------------------------
So, guys, after having spent a few days thinking and experiencing, here
are the different possibilities I have thought about. I will try to list
all of them, with their pros and cons.
1) The chain, and the chain builder
-----------------------------------
We use a chain of filters to process events. An event, once processed by
either the Acceptor/Connector or the Processor, is pushed through a
chain of filters, up to a handler, and a tail filter which does nothing
but terminate the chain. This chain is associated witheach new session,
and copied from a template, which is created before we start the
client/server (depending on what you are iomplementing).
The chain template is created using a chain builder. The idea is to
first create a chain template, which will be copied when a new session
is created, so that we will be able to dynamically modify the copied
chain within the session without modifying the template (for instance,
you can dynamically add a logging filter during a session)
So we have two different vision for the chain, a template, and N
instance of this template (one per session).
1.1) Operations on a chain
------------------------
We want to be able to walk the chain, jumping from one filter to the
next one. We also want to add or remove filters, dynamically. Both
chains (template and instances) dn't support all of these operations :
it's obviously useless to process event on the template, we just want to
be able to manage the filters into the chain while building it. Also,
the copy should be fast and cost as less as memory as possible.
When an event is processed, it goes through events following this logic :
processEventXXX() {
doPreOperation();
callNextFilter();
doPostOperation()
}
the callNexrFilter() is the important part of the problem : how do we
jump to the next filter, allow a dynamic modification of the chain, and
protect the chain against concurrent modifications ?
It also has to be fast, and easy to debug... Challenging !
2) Proposed solutions
---------------------
2.1) Using List<T>
---------------------------------
We can imagine that filters are stored into a list, and that we process
an event through all the filters this way :
for (Filter filter:listOfFilters ) {
filter.processEventXXX();
}
It will obviously work, be easy to debug, be fast, but we will have two
problems here :
- we have to use a synchronized list, in order to guarantee that we
can't modify the list while jumping from one filter to another
- we can't any more call pre and post processing.
The first point is a bit problematic, because we have to lock all the
list. Performance can suffer, but not that much, considering that we
don't have so much chain modifications (hopefully)
The real issue is more serious. One improvement would be to have such a
loop :
for (Filter filter:listOfFilters ) {
filter.doPreXXX()
filter.processEventXXX();
filter.doPostXXX()
}
but this is far from being enough.
How do we handle the list modification ? As it's a list, we have all the
LIST API to do that : add, add(index), etc. Easy, as we can access this
list from the session object.
2.2) Using a linked list
------------------------
Instead of using a list, we can also link the filters. Passing from one
filter to the other will then be just a matter of doing :
F1.processEventXXX()
pre processing
nextFilter.processEventXXX()
post processing
The nextFilter is a field pointing to the next filter in the chain.
This solution has the big advantage of being very simple to implement,
allows pre and post processing, whatever it can be, and fast and easy to
debug.
The main issue is that it's difficult to protect this chain from
concurrent modification, unless you synchronize the nextFilter field :
F1.processEventXXX()
pre processing
synchronized( nextFilter ) {
nextFilter.processEventXXX()
}
post processing
This may be acceptable, though. The cost of checking if this
synchronization might be not null, as we can see that if the chain
contains many filters,
we will have as many synchronized sections as we have filter, plus the
time during which the section is synchronized is the global execution time.
Modifying the list is quite simple. As each filter has a link to the
next filter, it's just a matter of manipulating this list to add or
remove a filter. Obviously, adding or removing a filter back in the
chain is useless.
2.3 Using a double level structure
----------------------------------
The idea is to separate the filters from the chain. We will store the
chain into a list, and each element fo this list is a filter. This list
is handled by the session.
Here is the code :
// First invocation
processEvent(XXX) {
session.getFilter(0).processEventXXX( session, 0 );
}
// Nth invocation
Fx.processEventXXX(session, pos) {
pre processing
pos++
session.getFilter(pos).processEventXXX(session, pos)
post processing
}
// And of course, the TailFilter won't call the next filter :)
How do we modifiy the list ?
Fx.processEventXXX(session, pos) {
pre processing
pos++
// Add a new filter to the chain
session.addFiler(pos, filter)
session.getFilter(pos).processEventXXX(session, pos)
post processing
}
The session.addFilter() method will just update the list, and then the
next call will be done. If we use a CopyOnWriteArrayList, we are thread
safe.
So far, using this method, we have all the advantages and none of the
inconvenients we have seen in the other methods.
3) Choice
---------
If we cant to keep all the existing functionalites MINA has, solution
2.3 is obviously the way to go. Well implemented, it will also be fast
and easy to debug, and, last, not least, it's close to what we curently
have, but with less code and a better interface.
--
--
cordialement, regards,
Emmanuel Lécharny
www.iktek.com
directory.apache.org
Re: [Chain refactoring] Some fedback and ideas
Posted by Julien Vermillard <jv...@archean.fr>.
Hi you know how much I prefer 2.1 but the need of post() method force
us reworking all the filters, so 2.3 is the way to go.
Keep 2.1 for the big 3.0 API breakup ;)
Julien
On Fri, 07 Nov 2008 02:59:59 +0100
Emmanuel Lecharny <el...@gmail.com> wrote:
> Hi guys,
>
> here are some thoughts and ideas I have had those last few days,
> thoughts that I have experimented in branches. It's not finished yet,
> but I think I have something likely to work well and be easier to use
> than the current implementation.
>
> So here are the gathered thoughts, please feel free to give me
> feedback :
>
>
> Refactoring the current chain : possible implementations
> ---------------------------------------------------------
>
> So, guys, after having spent a few days thinking and experiencing,
> here are the different possibilities I have thought about. I will try
> to list all of them, with their pros and cons.
>
> 1) The chain, and the chain builder
> -----------------------------------
>
> We use a chain of filters to process events. An event, once processed
> by either the Acceptor/Connector or the Processor, is pushed through
> a chain of filters, up to a handler, and a tail filter which does
> nothing but terminate the chain. This chain is associated witheach
> new session, and copied from a template, which is created before we
> start the client/server (depending on what you are iomplementing).
>
> The chain template is created using a chain builder. The idea is to
> first create a chain template, which will be copied when a new
> session is created, so that we will be able to dynamically modify the
> copied chain within the session without modifying the template (for
> instance, you can dynamically add a logging filter during a session)
>
> So we have two different vision for the chain, a template, and N
> instance of this template (one per session).
>
> 1.1) Operations on a chain
> ------------------------
> We want to be able to walk the chain, jumping from one filter to the
> next one. We also want to add or remove filters, dynamically. Both
> chains (template and instances) dn't support all of these
> operations : it's obviously useless to process event on the template,
> we just want to be able to manage the filters into the chain while
> building it. Also, the copy should be fast and cost as less as memory
> as possible.
>
> When an event is processed, it goes through events following this
> logic :
>
> processEventXXX() {
> doPreOperation();
> callNextFilter();
> doPostOperation()
> }
>
> the callNexrFilter() is the important part of the problem : how do we
> jump to the next filter, allow a dynamic modification of the chain,
> and protect the chain against concurrent modifications ?
>
> It also has to be fast, and easy to debug... Challenging !
>
> 2) Proposed solutions
> ---------------------
>
> 2.1) Using List<T>
> ---------------------------------
>
> We can imagine that filters are stored into a list, and that we
> process an event through all the filters this way :
>
> for (Filter filter:listOfFilters ) {
> filter.processEventXXX();
> }
>
> It will obviously work, be easy to debug, be fast, but we will have
> two problems here :
> - we have to use a synchronized list, in order to guarantee that we
> can't modify the list while jumping from one filter to another
> - we can't any more call pre and post processing.
>
> The first point is a bit problematic, because we have to lock all the
> list. Performance can suffer, but not that much, considering that we
> don't have so much chain modifications (hopefully)
> The real issue is more serious. One improvement would be to have such
> a loop :
>
> for (Filter filter:listOfFilters ) {
> filter.doPreXXX()
> filter.processEventXXX();
> filter.doPostXXX()
> }
>
> but this is far from being enough.
>
> How do we handle the list modification ? As it's a list, we have all
> the LIST API to do that : add, add(index), etc. Easy, as we can
> access this list from the session object.
>
>
> 2.2) Using a linked list
> ------------------------
>
> Instead of using a list, we can also link the filters. Passing from
> one filter to the other will then be just a matter of doing :
>
> F1.processEventXXX()
> pre processing
> nextFilter.processEventXXX()
> post processing
>
> The nextFilter is a field pointing to the next filter in the chain.
>
> This solution has the big advantage of being very simple to
> implement, allows pre and post processing, whatever it can be, and
> fast and easy to debug.
>
> The main issue is that it's difficult to protect this chain from
> concurrent modification, unless you synchronize the nextFilter field :
> F1.processEventXXX()
> pre processing
> synchronized( nextFilter ) {
> nextFilter.processEventXXX()
> }
> post processing
>
> This may be acceptable, though. The cost of checking if this
> synchronization might be not null, as we can see that if the chain
> contains many filters,
> we will have as many synchronized sections as we have filter, plus
> the time during which the section is synchronized is the global
> execution time.
>
> Modifying the list is quite simple. As each filter has a link to the
> next filter, it's just a matter of manipulating this list to add or
> remove a filter. Obviously, adding or removing a filter back in the
> chain is useless.
>
> 2.3 Using a double level structure
> ----------------------------------
> The idea is to separate the filters from the chain. We will store the
> chain into a list, and each element fo this list is a filter. This
> list is handled by the session.
> Here is the code :
>
> // First invocation
> processEvent(XXX) {
> session.getFilter(0).processEventXXX( session, 0 );
> }
>
> // Nth invocation
> Fx.processEventXXX(session, pos) {
> pre processing
> pos++
> session.getFilter(pos).processEventXXX(session, pos)
> post processing
> }
>
> // And of course, the TailFilter won't call the next filter :)
>
> How do we modifiy the list ?
>
> Fx.processEventXXX(session, pos) {
> pre processing
> pos++
> // Add a new filter to the chain
> session.addFiler(pos, filter)
> session.getFilter(pos).processEventXXX(session, pos)
> post processing
> }
>
> The session.addFilter() method will just update the list, and then
> the next call will be done. If we use a CopyOnWriteArrayList, we are
> thread safe.
>
> So far, using this method, we have all the advantages and none of the
> inconvenients we have seen in the other methods.
>
> 3) Choice
> ---------
>
> If we cant to keep all the existing functionalites MINA has, solution
> 2.3 is obviously the way to go. Well implemented, it will also be
> fast and easy to debug, and, last, not least, it's close to what we
> curently have, but with less code and a better interface.
>
Re: [Chain refactoring] Some fedback and ideas no--signature
Posted by Emmanuel Lecharny <el...@gmail.com>.
Steve Ulrich wrote:
>> 3) Choice
>> ---------
>>
>> If we cant to keep all the existing functionalites MINA has, solution
>> 2.3 is obviously the way to go. Well implemented, it will also be fast
>> and easy to debug, and, last, not least, it's close to what we curently
>> have, but with less code and a better interface.
>>
>
> I think a combination of the three would be good:
>
> An Iterator like object is passed to the Filter:
>
> processEventXXX(message, chainIterator){
> //preprocessing
> chainIterator.next().processEventXXX()
> //postprocessing
> }
>
> This way you don't need to deal witch an index.
>
That's an excellent idea ! I will have to create a third branch to do
that... I must admit that having to increment the index when switching
from one filter to another is, well, ugly ?
> If the data the Iterator is based of is immutable,
This is not the case. But the iterator may use internally a list which
will be protected. Using a CoWAL, this is just fine.
> synchronization isn't needed. Just replace the data and the next created iterator will use it.
> Pro:
> No need for ugly and unsage indices
> No need to decide wich way to iterate the list, bottom-up or top-down are handled by different iterators
> Synchronisation isn't needed
> Pre and post processing
> easy usage, easy implementation
> Con:
> overhead for iterator creation ?
>
> regards
>
Thanks Steve !
--
--
cordialement, regards,
Emmanuel Lécharny
www.iktek.com
directory.apache.org
RE: [Chain refactoring] Some fedback and ideas no--signature
Posted by Steve Ulrich <st...@proemion.com>.
> Emmanuel Lecharny [mailto:elecharny@gmail.com] wrote:
>
> Hi guys,
Hi!
> here are some thoughts and ideas I have had those last few days,
> thoughts that I have experimented in branches. It's not finished yet,
> but I think I have something likely to work well and be easier to use
> than the current implementation.
>
> So here are the gathered thoughts, please feel free to give me feedback
> :
>
>
> Refactoring the current chain : possible implementations
[snip]
> ---------------------------------------------------------
>
> 2) Proposed solutions
> ---------------------
>
[snip]
In my opinion, the 2.1 and 2.2 solution is nearly the same, just the way you iterate the filter is different:
2.1: Iterations outside of the filter (Con: no pre/post processing)
2.3: Index based iteration inside the filter (Con: looks ugly to me ;-), indices aren't safe if you use ExecutorFilter)
2.2 is somewhat different: You're using a simple kind of LinkedList and pass the current filter the entry of the "next" element
>
> 3) Choice
> ---------
>
> If we cant to keep all the existing functionalites MINA has, solution
> 2.3 is obviously the way to go. Well implemented, it will also be fast
> and easy to debug, and, last, not least, it's close to what we curently
> have, but with less code and a better interface.
I think a combination of the three would be good:
An Iterator like object is passed to the Filter:
processEventXXX(message, chainIterator){
//preprocessing
chainIterator.next().processEventXXX()
//postprocessing
}
This way you don't need to deal witch an index.
If the data the Iterator is based of is immutable, synchronization isn't needed. Just replace the data and the next created iterator will use it.
Pro:
No need for ugly and unsage indices
No need to decide wich way to iterate the list, bottom-up or top-down are handled by different iterators
Synchronisation isn't needed
Pre and post processing
easy usage, easy implementation
Con:
overhead for iterator creation ?
regards
Steve