beep4j-user Mailing List for beep4j
Status: Alpha
Brought to you by:
rasss
You can subscribe to this list here.
2007 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(3) |
Sep
(4) |
Oct
(13) |
Nov
(3) |
Dec
(15) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2008 |
Jan
(25) |
Feb
(2) |
Mar
|
Apr
|
May
(17) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Simon <co...@gm...> - 2008-05-24 13:18:11
|
I've added synchronization to DefaultChannelController. Due to missing synhronization it was possible that the sequence numbers got out of sync resulting in protocol exceptions at the receiver (thanks Martin for your analysis). Additionally, I've incorporated the patch from Martin for the MessageHeader class. If content type, charsets, and transfer encoding have the default value, they are no longer added to the resulting output. The changes are on the branches/0-1 branch. The latest snapshot is in the maven snapshot repository of beep4j. I plan to release beep4j 0.1.2 incorporating the latest fixes based on 0.1.1. So, if you have the time to test this version let me know whether it really works. Simon |
From: Thomson, M. <Mar...@an...> - 2008-05-21 04:55:35
|
The reason it works is either due to the number 4294967285 being converted into a two's complement java 'int' before the subtraction (the int has a value of -11); or, it relies on how the negative result is converted from long to int (-4294967246 is, I think, 0xffffffff00000032 in 64-bit two's complement, which looks a lot like 50 if you trim off the first 32 bits). I'd suggest that relying on this behaviour is riskier than being more explicit. It relies on too many things that aren't clear to the reader: (modulo == 1L << 32), java int being two's complement and 32-bit, java long being two's complement. The optimization (such as it is) works for all cases 'except' the case where start > position. That is, when position wraps, but start hasn't yet. I'd hardly consider the optimization worthwhile, except for the fact that it is more readable and works... Cheers, Martin > -----Original Message----- > From: Simon [mailto:co...@gm...] > Sent: Wednesday, 21 May 2008 2:38 PM > To: beep4j user list > Cc: Thomson, Martin > Subject: Re: [beep4j-user] seqno incrementing past 4 gigabytes > > start = 4294967285 > position = 4294967285 > windowSize = 50 > start + windowSize = 4294967335 > getEnd() = 39 > > getEnd() - position = -4294967246 > > but... > > (int) (getEnd() - position) = 50 > > Well, that is interesting, but it works. See SlidingWindowTest in > branches/0-1. > > And I'm no fan of such "optimizations" (avoiding a modulo operator in > a case that's rare). > > Simon > > On May 21, 2008, at 6:18 AM, Thomson, Martin wrote: > > > The problem that I wanted to solve was where getEnd() returned a > small > > number (i.e. start + windowSize > modulo) but position is still > large. > > > > The case that you are looking for is where start and position are > both > > very large, but less than windowSize from the end (i.e. modulo - > > start < > > windowSize). E.g. If you set start = modulo - 1000, position = > > modulo > > - 500, and windowSize = 4096 (as normal), then the result from this > > is: > > > > getEnd() = 3096 > > > > getEnd() - position = very large negative number. > > > > Not using modulo is more efficient and works in all cases except this > > one. > > > >> -----Original Message----- > >> From: Simon [mailto:co...@gm...] > >> Sent: Wednesday, 21 May 2008 7:15 AM > >> To: beep4j user list > >> Cc: Thomson, Martin > >> Subject: Re: [beep4j-user] seqno incrementing past 4 gigabytes > >> > >> > >> On May 20, 2008, at 6:58 AM, Thomson, Martin wrote: > >> > >>> There are two places in beep4j where the rollover of the seqno > field > >>> isn't correctly handled. Of course, given that you need to send 4 > >>> gigabytes of data over a single channel to reach this point, it's > >>> probably a little theoretical... > >>> > >>> First is in SlidingWindow.remaining(), which currently uses: > >>> > >>> return getEnd() - position; > >>> > >>> This doesn't work if the end has just rolled around to zero, but > the > >>> positioning hasn't. This should look like: > >>> > >>> if (position < start) { > >>> return (int) ((start + windowSize) % modulo - position); > >>> } else { > >>> return (int) (start - position + windowSize); > >>> } > >>> > >> > >> I've tried to create a unit test that highlights the bug (see > > branches/ > >> 0-1). However, I've failed. The following equivalences hold > >> > >> getEnd() == (start + windowSize) % modulo > >> > >> So, the if block is identical to the original method body. And the > >> else branch is identical as well, because we can leave out the > modulo > >> (start + windowSize cannot be biger than the maximum). > >> > >>> The other place is in the incrementing of the seqno in > >>> DefaultChannelController. This doesn't roll over at all, so when > > 4Gb > >>> comes around, things will probably break. I've added a method > >>> getNextSeqno that does the incrementing and rollover... > >>> > >>> private long getNextSeqno(int messageSize) { > >>> long nextSeqno = this.seqno; > >>> this.seqno = (this.seqno + messageSize) % (1L << 32); > >>> return nextSeqno; > >>> } > >>> > >> > >> I've fixed that one on the branches/0-1 branch. I'll add your > >> proposed > >> synchronization later on. > >> > >> Simon > >> > > > > --------------------------------------------------------------------- > --------------------------- > > This message is for the designated recipient only and may > > contain privileged, proprietary, or otherwise private information. > > If you have received it in error, please notify the sender > > immediately and delete the original. Any unauthorized use of > > this email is prohibited. > > --------------------------------------------------------------------- > --------------------------- > > [mf2] > > > ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Simon <co...@gm...> - 2008-05-21 04:37:43
|
start = 4294967285 position = 4294967285 windowSize = 50 start + windowSize = 4294967335 getEnd() = 39 getEnd() - position = -4294967246 but... (int) (getEnd() - position) = 50 Well, that is interesting, but it works. See SlidingWindowTest in branches/0-1. And I'm no fan of such "optimizations" (avoiding a modulo operator in a case that's rare). Simon On May 21, 2008, at 6:18 AM, Thomson, Martin wrote: > The problem that I wanted to solve was where getEnd() returned a small > number (i.e. start + windowSize > modulo) but position is still large. > > The case that you are looking for is where start and position are both > very large, but less than windowSize from the end (i.e. modulo - > start < > windowSize). E.g. If you set start = modulo - 1000, position = > modulo > - 500, and windowSize = 4096 (as normal), then the result from this > is: > > getEnd() = 3096 > > getEnd() - position = very large negative number. > > Not using modulo is more efficient and works in all cases except this > one. > >> -----Original Message----- >> From: Simon [mailto:co...@gm...] >> Sent: Wednesday, 21 May 2008 7:15 AM >> To: beep4j user list >> Cc: Thomson, Martin >> Subject: Re: [beep4j-user] seqno incrementing past 4 gigabytes >> >> >> On May 20, 2008, at 6:58 AM, Thomson, Martin wrote: >> >>> There are two places in beep4j where the rollover of the seqno field >>> isn't correctly handled. Of course, given that you need to send 4 >>> gigabytes of data over a single channel to reach this point, it's >>> probably a little theoretical... >>> >>> First is in SlidingWindow.remaining(), which currently uses: >>> >>> return getEnd() - position; >>> >>> This doesn't work if the end has just rolled around to zero, but the >>> positioning hasn't. This should look like: >>> >>> if (position < start) { >>> return (int) ((start + windowSize) % modulo - position); >>> } else { >>> return (int) (start - position + windowSize); >>> } >>> >> >> I've tried to create a unit test that highlights the bug (see > branches/ >> 0-1). However, I've failed. The following equivalences hold >> >> getEnd() == (start + windowSize) % modulo >> >> So, the if block is identical to the original method body. And the >> else branch is identical as well, because we can leave out the modulo >> (start + windowSize cannot be biger than the maximum). >> >>> The other place is in the incrementing of the seqno in >>> DefaultChannelController. This doesn't roll over at all, so when > 4Gb >>> comes around, things will probably break. I've added a method >>> getNextSeqno that does the incrementing and rollover... >>> >>> private long getNextSeqno(int messageSize) { >>> long nextSeqno = this.seqno; >>> this.seqno = (this.seqno + messageSize) % (1L << 32); >>> return nextSeqno; >>> } >>> >> >> I've fixed that one on the branches/0-1 branch. I'll add your >> proposed >> synchronization later on. >> >> Simon >> > > ------------------------------------------------------------------------------------------------ > This message is for the designated recipient only and may > contain privileged, proprietary, or otherwise private information. > If you have received it in error, please notify the sender > immediately and delete the original. Any unauthorized use of > this email is prohibited. > ------------------------------------------------------------------------------------------------ > [mf2] > |
From: Thomson, M. <Mar...@an...> - 2008-05-21 04:18:52
|
The problem that I wanted to solve was where getEnd() returned a small number (i.e. start + windowSize > modulo) but position is still large. The case that you are looking for is where start and position are both very large, but less than windowSize from the end (i.e. modulo - start < windowSize). E.g. If you set start = modulo - 1000, position = modulo - 500, and windowSize = 4096 (as normal), then the result from this is: getEnd() = 3096 getEnd() - position = very large negative number. Not using modulo is more efficient and works in all cases except this one. > -----Original Message----- > From: Simon [mailto:co...@gm...] > Sent: Wednesday, 21 May 2008 7:15 AM > To: beep4j user list > Cc: Thomson, Martin > Subject: Re: [beep4j-user] seqno incrementing past 4 gigabytes > > > On May 20, 2008, at 6:58 AM, Thomson, Martin wrote: > > > There are two places in beep4j where the rollover of the seqno field > > isn't correctly handled. Of course, given that you need to send 4 > > gigabytes of data over a single channel to reach this point, it's > > probably a little theoretical... > > > > First is in SlidingWindow.remaining(), which currently uses: > > > > return getEnd() - position; > > > > This doesn't work if the end has just rolled around to zero, but the > > positioning hasn't. This should look like: > > > > if (position < start) { > > return (int) ((start + windowSize) % modulo - position); > > } else { > > return (int) (start - position + windowSize); > > } > > > > I've tried to create a unit test that highlights the bug (see branches/ > 0-1). However, I've failed. The following equivalences hold > > getEnd() == (start + windowSize) % modulo > > So, the if block is identical to the original method body. And the > else branch is identical as well, because we can leave out the modulo > (start + windowSize cannot be biger than the maximum). > > > The other place is in the incrementing of the seqno in > > DefaultChannelController. This doesn't roll over at all, so when 4Gb > > comes around, things will probably break. I've added a method > > getNextSeqno that does the incrementing and rollover... > > > > private long getNextSeqno(int messageSize) { > > long nextSeqno = this.seqno; > > this.seqno = (this.seqno + messageSize) % (1L << 32); > > return nextSeqno; > > } > > > > I've fixed that one on the branches/0-1 branch. I'll add your proposed > synchronization later on. > > Simon > ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Simon <co...@gm...> - 2008-05-20 21:15:26
|
On May 20, 2008, at 6:58 AM, Thomson, Martin wrote: > There are two places in beep4j where the rollover of the seqno field > isn't correctly handled. Of course, given that you need to send 4 > gigabytes of data over a single channel to reach this point, it's > probably a little theoretical... > > First is in SlidingWindow.remaining(), which currently uses: > > return getEnd() - position; > > This doesn't work if the end has just rolled around to zero, but the > positioning hasn't. This should look like: > > if (position < start) { > return (int) ((start + windowSize) % modulo - position); > } else { > return (int) (start - position + windowSize); > } > I've tried to create a unit test that highlights the bug (see branches/ 0-1). However, I've failed. The following equivalences hold getEnd() == (start + windowSize) % modulo So, the if block is identical to the original method body. And the else branch is identical as well, because we can leave out the modulo (start + windowSize cannot be biger than the maximum). > The other place is in the incrementing of the seqno in > DefaultChannelController. This doesn't roll over at all, so when 4Gb > comes around, things will probably break. I've added a method > getNextSeqno that does the incrementing and rollover... > > private long getNextSeqno(int messageSize) { > long nextSeqno = this.seqno; > this.seqno = (this.seqno + messageSize) % (1L << 32); > return nextSeqno; > } > I've fixed that one on the branches/0-1 branch. I'll add your proposed synchronization later on. Simon |
From: Alan D. C. <li...@to...> - 2008-05-20 20:27:11
|
I ran across this problem. I have an ftp-like app where I send down terabytes of data so I was running into this problem all the time. Regards, Alan On May 19, 2008, at 9:58 PM, Thomson, Martin wrote: > There are two places in beep4j where the rollover of the seqno field > isn't correctly handled. Of course, given that you need to send 4 > gigabytes of data over a single channel to reach this point, it's > probably a little theoretical... > > First is in SlidingWindow.remaining(), which currently uses: > > return getEnd() - position; > > This doesn't work if the end has just rolled around to zero, but the > positioning hasn't. This should look like: > > if (position < start) { > return (int) ((start + windowSize) % modulo - position); > } else { > return (int) (start - position + windowSize); > } > > The other place is in the incrementing of the seqno in > DefaultChannelController. This doesn't roll over at all, so when 4Gb > comes around, things will probably break. I've added a method > getNextSeqno that does the incrementing and rollover... > > private long getNextSeqno(int messageSize) { > long nextSeqno = this.seqno; > this.seqno = (this.seqno + messageSize) % (1L << 32); > return nextSeqno; > } > > Ta, > Martin > > ------------------------------------------------------------------------------------------------ > This message is for the designated recipient only and may > contain privileged, proprietary, or otherwise private information. > If you have received it in error, please notify the sender > immediately and delete the original. Any unauthorized use of > this email is prohibited. > ------------------------------------------------------------------------------------------------ > [mf2] > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Microsoft > Defy all challenges. Microsoft(R) Visual Studio 2008. > http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ > _______________________________________________ > beep4j-user mailing list > bee...@li... > https://lists.sourceforge.net/lists/listinfo/beep4j-user > |
From: Thomson, M. <Mar...@an...> - 2008-05-20 04:58:23
|
There are two places in beep4j where the rollover of the seqno field isn't correctly handled. Of course, given that you need to send 4 gigabytes of data over a single channel to reach this point, it's probably a little theoretical... First is in SlidingWindow.remaining(), which currently uses: return getEnd() - position; This doesn't work if the end has just rolled around to zero, but the positioning hasn't. This should look like: if (position < start) { return (int) ((start + windowSize) % modulo - position); } else { return (int) (start - position + windowSize); } The other place is in the incrementing of the seqno in DefaultChannelController. This doesn't roll over at all, so when 4Gb comes around, things will probably break. I've added a method getNextSeqno that does the incrementing and rollover... private long getNextSeqno(int messageSize) { long nextSeqno = this.seqno; this.seqno = (this.seqno + messageSize) % (1L << 32); return nextSeqno; } Ta, Martin ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Thomson, M. <Mar...@an...> - 2008-05-20 04:43:01
|
I have just spent a little time looking at the flow control mechanism built in to the TCP mapping for BEEP [RFC 3081]. Based on a reading of RFC 3080, it would appear that if the previous frame was marked as incomplete (continuation indicator of '*') the receiver should reject any frame it receives that isn't a continuation of that message. However, if the sender were to respect this and withhold a SEQ frame until the message was completely sent (as I suggested in an earlier email) there is a potential for deadlock. If both peers reach the end of their send windows at approximately the same time, neither end is able to send the SEQ frame that will allow the other to continue. RFC 3081 isn't very clear on this point, unless you take the following statement to imply that SEQ frames can be sent at any time: Further, SEQ frames for a channel must have higher priority than messages for that channel. Thus, I retract my earlier suggestion to have the SEQ frame sent only after a completed message. I have tested code that put the SEQ frame on the send queue, but the only option that works is to ensure that the SEQ frame is sent immediately. The existing code is adequate for this purpose (and refactoring it to ensure that the SEQ frame is treated like a real frame has far-reaching impacts on the rest of beep4j). I do however recommend that you synchronize access to the DefaultChannelController. All the public methods of this class change its mutable state and in a multi-threaded system, responses that are generated can be corrupted in a range of ways if these aren't protected adequately. I am reasonably certain that this wont cause a deadlock (the only other applicable lock is the one on SessionImpl, which is always taken first). Cheers, Martin ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Simon <co...@gm...> - 2008-05-07 17:35:09
|
On May 7, 2008, at 7:02 AM, Thomson, Martin wrote: > Now that my misconceptions about BEEP have been laid to rest, I'm > wondering if anyone has ever considered alternative approaches to the > message throughput problem caused by the serial nature of channels. > > Looking at RFC 3080, it is now clearly evident that the message number > parameter is almost completely redundant. Seqno is sufficient as a > sanity check on message ordering and if messages are responded to > sequentially, the channel identifier provides enough information to > correlate request and response. The only exception appears to be for > ANS messages [1]. At least from a debugging / tracing point of view the message number parameter makes a lot of sense. And it allows to detect protocol violations, such as when a peer is not sending the replies in the correct order. > > > There is no protocol reason for preventing out of order messages. I > can > also prove that with a small change, beep4j accepts out of order > messages with no adverse effects. The only reason this is not done is > that the RFC says "thou shalt not". Unfortunately, this seems to just > be an arbitrary decision; the reasoning has not been retained for our > benefit. I'd be interested in learning of any sound, technical reason > why the protocol must operate in this fashion (as opposed to reasons > why > it isn't so big a limitation, I've heard those already). > The idea I'm currently entertaining is to provide an initial tuning > profile for BEEP that enables the out-of-order responses, using the > message number as a true means of correlating request and response > [2]. > If this profile were accepted, subsequent continuous channels would be > able to send responses as they are processed without regard for > ordering. Unfortunately, this would affect all channels in the > session > and could prevent channels from co-existing, assuming that certain > applications or channel profiles require this serial behaviour (e.g. > DELETE then PUT example from the previous mail [3]). Of course, peers > that provide purely serial profiles wouldn't have to offer or accept > the > profile. > I propose that you send those concerns / questions to the beep community list (see http://beepcore.org/support.html). > I'm interested in how you would see a tuning profile working with > beep4j. I've never thought about how to implement tuning profiles because I've simply never had a use for the existing tuning profiles. Additionally, as you have guessed, a tuning profile requires quite a bit of access to the session internals, which at that point in the lifetime of beep4j I do not feel comfortable to provide. > > > My thought is that tuning profiles [4] are best implemented within > beep4j; they need access to certain internals that aren't really > appropriate for the public API. My initial thoughts were that the > profile would be offered at the discretion of the user (i.e. by > registering the profile URI in SessionHandler.connectionEstablished). > beep4j would handle the details internally. When channel open is > requested, the profile URI could be compared to the set of supported > (and offered) profiles. Channel handlers for the tuning profile would > have access to the extended interface for the session > (InternalSession) > which could be extended with those additional features necessary for > dealing with the profile. (e.g. setSynchronous(boolean) or > startTLS()) > > Cheers, > Martin > > > [1] I can't see how interleaving split ANS frames could ever be a good > idea, or why it deserves this special exemption from the rule, but > it's > there in black and white. > > [2] Yes, I'm fully capable of working within the limitations, but > that's > sub-optimal and I find the limitation irritating enough to do > something > about it. > > [3] That example is actually misleading. BEEP doesn't mandate an > order > to processing; therefore, the only way to guarantee the order of > execution is to wait for a response to the first request. > > [4] I've also looked at the TLS filter example in Mina and it looks > relatively easy to implement. Basic support for the TLS profile would > be another interesting project. > ------------------------------------------------------------------------------------------------ > This message is for the designated recipient only and may > contain privileged, proprietary, or otherwise private information. > If you have received it in error, please notify the sender > immediately and delete the original. Any unauthorized use of > this email is prohibited. > ------------------------------------------------------------------------------------------------ > [mf2] > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by the 2008 JavaOne(SM) Conference > Don't miss this year's exciting event. There's still time to save > $100. > Use priority code J8TL2D2. > http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone > _______________________________________________ > beep4j-user mailing list > bee...@li... > https://lists.sourceforge.net/lists/listinfo/beep4j-user |
From: Sam R. <sro...@wu...> - 2008-05-07 16:37:02
|
On Tue, May 06, 2008 at 10:02:29PM -0700, Thomson, Martin wrote: > There is no protocol reason for preventing out of order messages. I can > also prove that with a small change, beep4j accepts out of order > messages with no adverse effects. The only reason this is not done is > that the RFC says "thou shalt not". Unfortunately, this seems to just > be an arbitrary decision; the reasoning has not been retained for our > benefit. The reasoning is retained in RFC3117: Channels provide [...] the basis for parallelism. Remember the last parameter in the command line of a BXXP frame? The "part of the application" that gets the message is identified by a channel number. There are applications that want sequential processing, aka "pipelineing". Those applications use channels to achieve this. There are applications that require parallel processing. Those applications use multiple channels. There is a BEEP mailing list. Marshall Rose is on it. You might be better off directing your criticisms of his protocol design to him. Good luck, Sam |
From: Thomson, M. <Mar...@an...> - 2008-05-07 06:41:38
|
This modified patch actually checks for the SEQ message before trying to send it. This one has been tested successfully. Cheers, Martin > -----Original Message----- > From: Thomson, Martin > Sent: Tuesday, 6 May 2008 3:58 PM > To: 'Simon'; beep4j user list > Subject: RE: [beep4j-user] Concurrency issues > > Hi Simon, > > Thanks for the prompt response. > > Unfortunately for me, it's not the info I was hoping for. The text you > point out in Section 2.6.1 of RFC 3080 severely limits the usability of > BEEP for asynchronous applications. Interesting to note that beep4j > seems quite capable of sending replies out of order...something that > I'm quite happy about because it makes my asynchronous hack much > easier. (that is, please don't fix this ;) > > Anyhow, at least I now know why it is behaving as I am seeing. I'll > look at exploring other avenues. I might come back in a little while > with a big feature patch :) > > As a side note - forcing synchrony on your callers limits the > flexibility offered by the interface. I see that as a bad idea. To > give an example - tasks assigned to a thread pool might be used to > generate requests and process responses. If the same thread from that > pool generated a request to two channels (presumably in different > tasks); only the second reply handler would be found by beep4j - the > reply handler for the first would never see any response. > > > Regarding the bad seqno problem, this is a pure threading issue. > Generating a test case is non-trivial for any concurrent code. I can > try to explain though, now that I've looked into it further. > > ResponseHandler.sendRPY is being invoked (near-)simultaneously on two > different threads. Both threads reach nextFrame() at about the same > time. > > -A-> } else if (senderWindow.remaining() >= MINIMUM_FRAME_SIZE) > { > LOG.debug("split frame at position " + > senderWindow.remaining() > + " (channel=" + channel + ")"); > -B-> Frame[] split = frame.split(senderWindow.remaining()); > frames.addFirst(split[1]); > return split[0]; > } else { > > > Both frames are too big to fit, and they both pass the line marked with > 'A' at about the same time. The first thread continues, the second > stops somewhere between 'A' and 'B'. The first thread sends its > message, getting the remainder of the window. The second thread then > re-awakens and consequently gets 0 bytes. By this point the second > thread has already decided to send a frame, to it continues even though > the frame is empty. The weird ordering of subsequent frames occurs > because frames.addFirst() is called for the remainder of the message > from the first thread before it is called by the second thread, leaving > the remainder of the message from the second thread at the head of the > list. > > Looking at the call chain for sendRPY, it is not synchronized anywhere > in the beep4j package, whereas all of the other calls are synchronized > on the SessionImpl instance. Since the call path for sendRPY doesn't > traverse SessionImpl, using that lock isn't an (easy) option, so it is > probably best to ensure that DefaultChannelController is thread safe. > > I've attached a patch for DefaultChannelController that makes it thread > safe, assuming that frames, window and senderWindow are the only > mutable state in the class. > > The also includes a fix for sending the SEQ frame between the two parts > of a split frame. I defer the sending of the SEQ if there isn't enough > room to send another frame (senderWindow.remaining() < > MINIMUM_FRAME_SIZE), assuming that the last frame was incomplete if the > window has closed. > > Cheers, > Martin > > > > -----Original Message----- > > From: Simon [mailto:co...@gm...] > > Sent: Tuesday, 6 May 2008 6:33 AM > > To: beep4j user list > > Cc: Thomson, Martin > > Subject: Re: [beep4j-user] Concurrency issues > > > > Hi > > > > First of all thanks for your interest in beep4j. My comments are > > inlined below. However, first I have to tell you that I didn't have > > much time to work on beep4j recently. So it is kind of unmaintained > > (as you might have guessed from the activity statistics). However, > > 0.1.1 seems to be successfully used by few people. Of course, it > might > > depend on how the application uses beep4j. Anyways, I'm more than > > happy to incorporate patches and help you find your way around the > > beep4j codebase. > > > > Cheers, > > Simon > > > > On May 5, 2008, at 9:08 AM, Thomson, Martin wrote: > > > > > Hi, > > > > > > I'm seeing a few problems with beep4j under high loads in > concurrent > > > applications. > > > > > > > > > ~ Out of order messages > > > > > > On inspection of the code in SessionImpl.java, it appears that out > of > > > order receipt of response messages causes a protocol exception > (which > > > drops the connection). The replyListeners collection doesn't > > maintain > > > the set of ReplyListener instances for each channel based on the > > > message > > > number - the collection is a list. When a response arrives it > pulls > > > the > > > first item of the list and raises the exception if the number > doesn't > > > match. > > > > > > One of the main advantages that BEEP provides is the out of order > > > receipt of responses. If this feature wasn't available, we might > as > > > well be using HTTP. > > > > BEEP does provide out of order receipt of messages on different > > channels. However, replies on the same channel must be received in > > order. See section 2.6 of RFC 3080. The only exception (which I find > > somewhat strange) are frames of different ANSwers to the same MSG. > > > > > > > > I have a small patch that changes SessionImpl.java from 0.1.1 that > > > uses > > > a Map rather than a List, i.e. > > > Map<Integer, Map<Integer, ReplyListenerHolder>> replyListeners > > > > > > I can send out that patch; however, it appears that there are a > large > > > number of changes between 0.1.1 and what is on the SVN trunk. I am > > > concerned that the changes don't fix this and could cause more > > > problems. > > > > > > It's difficult to follow the thread of execution; but based on a > > rough > > > inspection, I see that the ReplyListener is now saved in > > > FilterChainTargetHolder, which uses a ThreadLocal. This would > > > present a > > > problem if there were two requests sent from the same thread; the > > > second > > > (and any subsequent requests from that thread) would overwrite the > > > ReplyListener from the first. > > > > > > > The code in the trunk is really not tested that much. So it probably > > still contains lots of bugs. The main feature is filtering on > channels > > (interceptors), well known from other specs (e.g. servlet filters). > > Additionally, the whole internals of beep4j were rewritten to work on > > frames instead of full assembled messages. This gives the receiver > the > > chance to operate on frames (if he desires so) and so for example to > > terminate the session if the sender sends huges messages in an > attempt > > to cause an OutOfMemoryError in the receiver. > > > > The ThreadLocals must only be used in a synchronous call. i.e. when > > you send a message, the ReplyListener is put into the ThreadLocal and > > later picked up at the end of the filter chain. However, when the > > control returns to your application code, it should be reset and > there > > should be no way you can override it, whether from the same thread or > > another one. Of course, it depends also on the used filters (i.e. a > > filter introducing asynchrony might cause troubles here if not > > properly implemented). > > > > I believe that the trunk implementation should really be the > > implementation to be pushed. > > > > > > > > ~ Impact of congestion control for TCP mapping > > > > > > We are running some tests using a single BEEP channel to send a > > > moderately high rate of messages between two applications. Both > are > > > using beep4j. Both do non-trivial processing on multiple threads. > > We > > > frequently get errors with seqno mismatches. > > > > > > The following (abridged) sequence is direct from a Wireshark packet > > > capture: > > > > > > RPY 3 12 * 3784 312 > > > <!-- first part of reply to 3-12 --> > > > END > > > RPY 3 9 * 4257 0 > > > END > > > SEQ 3 23232 4096 > > > RPY 3 9 . 4257 473 > > > <!-- reply to 3-9 --> > > > END > > > RPY 3 12 . 4096 161 > > > <!-- second part of reply to 3-12 --> > > > END > > > RPY 3 6 . 5203 473 > > > <!-- reply to 3-6 --> > > > END > > > > > > As you can see - out of order message numbers on replies (I'm using > > > the > > > SessionImpl.java patch mentioned above). More alarming is the fact > > > that > > > beep4j sends the first part of RPY 3-9 before finishing RPY 3-12. > > > What > > > is causes the failure is the seqno mismatch it sees when the first > > > (empty) part of RPY 3-9 is found. > > > > Frames on the same channel must be sent in order. Does this also > > happen without your patch (which I think violates the BEEP > > specification, see above)? > > > > > > > > > > > I think that the problem is that when there are two frames in the > > > queue > > > and the sender window (i.e. that advertised by the other end) is > too > > > small, TCPMapping splits the first frame (correctly), puts the > > > remainder > > > back on the queue (correctly), but then continues to try to process > > > subsequent frames. It should not be sending anything more (except > > > maybe > > > the SEQ message) when it does not have any space left in the > window. > > > > > > The following might fix that part: > > > > > > private Frame nextFrame() { > > > + if (frames.isEmpty() || senderWindow.remaining() <= 0) { > > > - if (frames.isEmpty()) { > > > return null; > > > > > > But I'm not sure what the calling context expects from sendFrames. > I > > > expect that it should manage, but I'll need to test this further to > > > find > > > out. > > > > > > > The whole frame splitting code in nextFrame() should not shuffle > > frames. If a frame is split, the first part is sent, the second part > > is added to the front of the queue and thus will be the first one to > > be sent the next time. From a quick glance at the code I do not see > > how this could happen. Could you send a unit test reproducing that > > problem on the ChannelHandler implementation. > > > > > > > > On the other hand, the following looks like a problem, but it might > > > actually work. When TCPMapping receives a SEQ message it > immediately > > > calls transport.sendBytes(createSEQFrame(channel, ackno, > windowSize)) > > > rather than creating and queuing up a new Frame object. > > > > > > > When TCPMapping receives a SEQ frame it merely updates the send > window > > size. Then, it tries to send queued frames. However, the SEQ frame is > > only created and sent after receiving ordinary frames (see > > frameReceived). Could you please clarify that one? Anyways, sending > > the SEQ frame directly might actually be a real problem (see below). > > > > > I understand how this might be desirable, but it might be better > (for > > > the sake of interoperability) to defer the sending of this message > if > > > there is an outstanding split frame. From RFC 3080: > > > > > > When a message is segmented and sent as several frames, those > > frames > > > must be sent sequentially, without any intervening frames from > > other > > > messages on the same channel. > > > > > > I have a few partially formed thoughts on how to solve this, but > I'll > > > get back to that tomorrow. I'll test the simple senderWindow check > > > once > > > I have both fixes together. > > > > Hm, that's right. I've never thought about the situation where a SEQ > > frame is interleaved between two frames of a message. So, a patch > that > > solves the problem is very welcome. > > > > > > > > > > > Let me know if anyone wants the SessionImpl.java patch for 0.1.1. > > > > > > Cheers, > > > Martin > > > ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Thomson, M. <Mar...@an...> - 2008-05-07 05:02:30
|
Now that my misconceptions about BEEP have been laid to rest, I'm wondering if anyone has ever considered alternative approaches to the message throughput problem caused by the serial nature of channels. Looking at RFC 3080, it is now clearly evident that the message number parameter is almost completely redundant. Seqno is sufficient as a sanity check on message ordering and if messages are responded to sequentially, the channel identifier provides enough information to correlate request and response. The only exception appears to be for ANS messages [1]. There is no protocol reason for preventing out of order messages. I can also prove that with a small change, beep4j accepts out of order messages with no adverse effects. The only reason this is not done is that the RFC says "thou shalt not". Unfortunately, this seems to just be an arbitrary decision; the reasoning has not been retained for our benefit. I'd be interested in learning of any sound, technical reason why the protocol must operate in this fashion (as opposed to reasons why it isn't so big a limitation, I've heard those already). The idea I'm currently entertaining is to provide an initial tuning profile for BEEP that enables the out-of-order responses, using the message number as a true means of correlating request and response [2]. If this profile were accepted, subsequent continuous channels would be able to send responses as they are processed without regard for ordering. Unfortunately, this would affect all channels in the session and could prevent channels from co-existing, assuming that certain applications or channel profiles require this serial behaviour (e.g. DELETE then PUT example from the previous mail [3]). Of course, peers that provide purely serial profiles wouldn't have to offer or accept the profile. I'm interested in how you would see a tuning profile working with beep4j. My thought is that tuning profiles [4] are best implemented within beep4j; they need access to certain internals that aren't really appropriate for the public API. My initial thoughts were that the profile would be offered at the discretion of the user (i.e. by registering the profile URI in SessionHandler.connectionEstablished). beep4j would handle the details internally. When channel open is requested, the profile URI could be compared to the set of supported (and offered) profiles. Channel handlers for the tuning profile would have access to the extended interface for the session (InternalSession) which could be extended with those additional features necessary for dealing with the profile. (e.g. setSynchronous(boolean) or startTLS()) Cheers, Martin [1] I can't see how interleaving split ANS frames could ever be a good idea, or why it deserves this special exemption from the rule, but it's there in black and white. [2] Yes, I'm fully capable of working within the limitations, but that's sub-optimal and I find the limitation irritating enough to do something about it. [3] That example is actually misleading. BEEP doesn't mandate an order to processing; therefore, the only way to guarantee the order of execution is to wait for a response to the first request. [4] I've also looked at the TLS filter example in Mina and it looks relatively easy to implement. Basic support for the TLS profile would be another interesting project. ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Thomson, M. <Mar...@an...> - 2008-05-06 23:58:32
|
BEEP defines a default value for the content type header. beep4j doesn't take advantage of this, and always provides a content-type header. I have a attached a small patch for MessageHeader.java that enables the suppression of the content-type header when sending messages. With the patch, the content-type header is not set unless explicitly set. When receiving a message, it reports application/octet-stream to the application if the header is missing. It's a fairly trivial thing, and you might say that bit misers should not be using text-based protocols, but it does halve the overhead for each message. Cheers, Martin ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Sam R. <sro...@wu...> - 2008-05-06 17:16:15
|
On Mon, May 05, 2008 at 10:58:25PM -0700, Thomson, Martin wrote: > Unfortunately for me, it's not the info I was hoping for. The text you > point out in Section 2.6.1 of RFC 3080 severely limits the usability of > BEEP for asynchronous applications. Interesting to note that beep4j > seems quite capable of sending replies out of order...something that I'm > quite happy about because it makes my asynchronous hack much easier. > (that is, please don't fix this ;) If its a protocol violation, it won't interop with other implementations. > As a side note - forcing synchrony on your callers limits the > flexibility offered by the interface. I see that as a bad idea. To > give an example - tasks assigned to a thread pool might be used to > generate requests and process responses. If the same thread from that > pool generated a request to two channels (presumably in different > tasks); only the second reply handler would be found by beep4j - the > reply handler for the first would never see any response. Its possible you are just not seeing how BEEP should be used to achieve your goals. BEEP supports both serialized and parallelized interactions, through the use of channels. BEEP gives serial processing within a channel. This is really important for applications when you want to do things in order. Example would be an HTTP-like protocol over BEEP. send a DELETE "/a" msg, send a POST "/a" msg. You want the resource deleted before you POST to create the new one. Technically, BEEP doesn't force requests to be handled serially, its up to the specific application profile designer to decide how they want to handle requests, but it does force replying in order. If you want asynchrony, like you want to DELETE "/a" and POST "/b", and want them able to progress seperately, you use different channels. It sounds like you have a pool of threads that should act independently (one should not block while another is making a request). In that case, each should have its own independent channel. This can be done by grabbing a free channel from a pool of them. Here's a description of the issue, and some Vortex library functions that help deal with it: http://www.aspl.es/fact/files/af-arch/vortex/html/group__vortex__channel__pool_ga7130833a4500c9647fd84a Cheers, Sam |
From: Thomson, M. <Mar...@an...> - 2008-05-06 05:58:44
|
Hi Simon, Thanks for the prompt response. Unfortunately for me, it's not the info I was hoping for. The text you point out in Section 2.6.1 of RFC 3080 severely limits the usability of BEEP for asynchronous applications. Interesting to note that beep4j seems quite capable of sending replies out of order...something that I'm quite happy about because it makes my asynchronous hack much easier. (that is, please don't fix this ;) Anyhow, at least I now know why it is behaving as I am seeing. I'll look at exploring other avenues. I might come back in a little while with a big feature patch :) As a side note - forcing synchrony on your callers limits the flexibility offered by the interface. I see that as a bad idea. To give an example - tasks assigned to a thread pool might be used to generate requests and process responses. If the same thread from that pool generated a request to two channels (presumably in different tasks); only the second reply handler would be found by beep4j - the reply handler for the first would never see any response. Regarding the bad seqno problem, this is a pure threading issue. Generating a test case is non-trivial for any concurrent code. I can try to explain though, now that I've looked into it further. ResponseHandler.sendRPY is being invoked (near-)simultaneously on two different threads. Both threads reach nextFrame() at about the same time. -A-> } else if (senderWindow.remaining() >= MINIMUM_FRAME_SIZE) { LOG.debug("split frame at position " + senderWindow.remaining() + " (channel=" + channel + ")"); -B-> Frame[] split = frame.split(senderWindow.remaining()); frames.addFirst(split[1]); return split[0]; } else { Both frames are too big to fit, and they both pass the line marked with 'A' at about the same time. The first thread continues, the second stops somewhere between 'A' and 'B'. The first thread sends its message, getting the remainder of the window. The second thread then re-awakens and consequently gets 0 bytes. By this point the second thread has already decided to send a frame, to it continues even though the frame is empty. The weird ordering of subsequent frames occurs because frames.addFirst() is called for the remainder of the message from the first thread before it is called by the second thread, leaving the remainder of the message from the second thread at the head of the list. Looking at the call chain for sendRPY, it is not synchronized anywhere in the beep4j package, whereas all of the other calls are synchronized on the SessionImpl instance. Since the call path for sendRPY doesn't traverse SessionImpl, using that lock isn't an (easy) option, so it is probably best to ensure that DefaultChannelController is thread safe. I've attached a patch for DefaultChannelController that makes it thread safe, assuming that frames, window and senderWindow are the only mutable state in the class. The also includes a fix for sending the SEQ frame between the two parts of a split frame. I defer the sending of the SEQ if there isn't enough room to send another frame (senderWindow.remaining() < MINIMUM_FRAME_SIZE), assuming that the last frame was incomplete if the window has closed. Cheers, Martin > -----Original Message----- > From: Simon [mailto:co...@gm...] > Sent: Tuesday, 6 May 2008 6:33 AM > To: beep4j user list > Cc: Thomson, Martin > Subject: Re: [beep4j-user] Concurrency issues > > Hi > > First of all thanks for your interest in beep4j. My comments are > inlined below. However, first I have to tell you that I didn't have > much time to work on beep4j recently. So it is kind of unmaintained > (as you might have guessed from the activity statistics). However, > 0.1.1 seems to be successfully used by few people. Of course, it might > depend on how the application uses beep4j. Anyways, I'm more than > happy to incorporate patches and help you find your way around the > beep4j codebase. > > Cheers, > Simon > > On May 5, 2008, at 9:08 AM, Thomson, Martin wrote: > > > Hi, > > > > I'm seeing a few problems with beep4j under high loads in concurrent > > applications. > > > > > > ~ Out of order messages > > > > On inspection of the code in SessionImpl.java, it appears that out of > > order receipt of response messages causes a protocol exception (which > > drops the connection). The replyListeners collection doesn't > maintain > > the set of ReplyListener instances for each channel based on the > > message > > number - the collection is a list. When a response arrives it pulls > > the > > first item of the list and raises the exception if the number doesn't > > match. > > > > One of the main advantages that BEEP provides is the out of order > > receipt of responses. If this feature wasn't available, we might as > > well be using HTTP. > > BEEP does provide out of order receipt of messages on different > channels. However, replies on the same channel must be received in > order. See section 2.6 of RFC 3080. The only exception (which I find > somewhat strange) are frames of different ANSwers to the same MSG. > > > > > I have a small patch that changes SessionImpl.java from 0.1.1 that > > uses > > a Map rather than a List, i.e. > > Map<Integer, Map<Integer, ReplyListenerHolder>> replyListeners > > > > I can send out that patch; however, it appears that there are a large > > number of changes between 0.1.1 and what is on the SVN trunk. I am > > concerned that the changes don't fix this and could cause more > > problems. > > > > It's difficult to follow the thread of execution; but based on a > rough > > inspection, I see that the ReplyListener is now saved in > > FilterChainTargetHolder, which uses a ThreadLocal. This would > > present a > > problem if there were two requests sent from the same thread; the > > second > > (and any subsequent requests from that thread) would overwrite the > > ReplyListener from the first. > > > > The code in the trunk is really not tested that much. So it probably > still contains lots of bugs. The main feature is filtering on channels > (interceptors), well known from other specs (e.g. servlet filters). > Additionally, the whole internals of beep4j were rewritten to work on > frames instead of full assembled messages. This gives the receiver the > chance to operate on frames (if he desires so) and so for example to > terminate the session if the sender sends huges messages in an attempt > to cause an OutOfMemoryError in the receiver. > > The ThreadLocals must only be used in a synchronous call. i.e. when > you send a message, the ReplyListener is put into the ThreadLocal and > later picked up at the end of the filter chain. However, when the > control returns to your application code, it should be reset and there > should be no way you can override it, whether from the same thread or > another one. Of course, it depends also on the used filters (i.e. a > filter introducing asynchrony might cause troubles here if not > properly implemented). > > I believe that the trunk implementation should really be the > implementation to be pushed. > > > > > ~ Impact of congestion control for TCP mapping > > > > We are running some tests using a single BEEP channel to send a > > moderately high rate of messages between two applications. Both are > > using beep4j. Both do non-trivial processing on multiple threads. > We > > frequently get errors with seqno mismatches. > > > > The following (abridged) sequence is direct from a Wireshark packet > > capture: > > > > RPY 3 12 * 3784 312 > > <!-- first part of reply to 3-12 --> > > END > > RPY 3 9 * 4257 0 > > END > > SEQ 3 23232 4096 > > RPY 3 9 . 4257 473 > > <!-- reply to 3-9 --> > > END > > RPY 3 12 . 4096 161 > > <!-- second part of reply to 3-12 --> > > END > > RPY 3 6 . 5203 473 > > <!-- reply to 3-6 --> > > END > > > > As you can see - out of order message numbers on replies (I'm using > > the > > SessionImpl.java patch mentioned above). More alarming is the fact > > that > > beep4j sends the first part of RPY 3-9 before finishing RPY 3-12. > > What > > is causes the failure is the seqno mismatch it sees when the first > > (empty) part of RPY 3-9 is found. > > Frames on the same channel must be sent in order. Does this also > happen without your patch (which I think violates the BEEP > specification, see above)? > > > > > > > I think that the problem is that when there are two frames in the > > queue > > and the sender window (i.e. that advertised by the other end) is too > > small, TCPMapping splits the first frame (correctly), puts the > > remainder > > back on the queue (correctly), but then continues to try to process > > subsequent frames. It should not be sending anything more (except > > maybe > > the SEQ message) when it does not have any space left in the window. > > > > The following might fix that part: > > > > private Frame nextFrame() { > > + if (frames.isEmpty() || senderWindow.remaining() <= 0) { > > - if (frames.isEmpty()) { > > return null; > > > > But I'm not sure what the calling context expects from sendFrames. I > > expect that it should manage, but I'll need to test this further to > > find > > out. > > > > The whole frame splitting code in nextFrame() should not shuffle > frames. If a frame is split, the first part is sent, the second part > is added to the front of the queue and thus will be the first one to > be sent the next time. From a quick glance at the code I do not see > how this could happen. Could you send a unit test reproducing that > problem on the ChannelHandler implementation. > > > > > On the other hand, the following looks like a problem, but it might > > actually work. When TCPMapping receives a SEQ message it immediately > > calls transport.sendBytes(createSEQFrame(channel, ackno, windowSize)) > > rather than creating and queuing up a new Frame object. > > > > When TCPMapping receives a SEQ frame it merely updates the send window > size. Then, it tries to send queued frames. However, the SEQ frame is > only created and sent after receiving ordinary frames (see > frameReceived). Could you please clarify that one? Anyways, sending > the SEQ frame directly might actually be a real problem (see below). > > > I understand how this might be desirable, but it might be better (for > > the sake of interoperability) to defer the sending of this message if > > there is an outstanding split frame. From RFC 3080: > > > > When a message is segmented and sent as several frames, those > frames > > must be sent sequentially, without any intervening frames from > other > > messages on the same channel. > > > > I have a few partially formed thoughts on how to solve this, but I'll > > get back to that tomorrow. I'll test the simple senderWindow check > > once > > I have both fixes together. > > Hm, that's right. I've never thought about the situation where a SEQ > frame is interleaved between two frames of a message. So, a patch that > solves the problem is very welcome. > > > > > > > Let me know if anyone wants the SessionImpl.java patch for 0.1.1. > > > > Cheers, > > Martin > > ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Simon <co...@gm...> - 2008-05-05 20:33:17
|
Hi First of all thanks for your interest in beep4j. My comments are inlined below. However, first I have to tell you that I didn't have much time to work on beep4j recently. So it is kind of unmaintained (as you might have guessed from the activity statistics). However, 0.1.1 seems to be successfully used by few people. Of course, it might depend on how the application uses beep4j. Anyways, I'm more than happy to incorporate patches and help you find your way around the beep4j codebase. Cheers, Simon On May 5, 2008, at 9:08 AM, Thomson, Martin wrote: > Hi, > > I'm seeing a few problems with beep4j under high loads in concurrent > applications. > > > ~ Out of order messages > > On inspection of the code in SessionImpl.java, it appears that out of > order receipt of response messages causes a protocol exception (which > drops the connection). The replyListeners collection doesn't maintain > the set of ReplyListener instances for each channel based on the > message > number - the collection is a list. When a response arrives it pulls > the > first item of the list and raises the exception if the number doesn't > match. > > One of the main advantages that BEEP provides is the out of order > receipt of responses. If this feature wasn't available, we might as > well be using HTTP. BEEP does provide out of order receipt of messages on different channels. However, replies on the same channel must be received in order. See section 2.6 of RFC 3080. The only exception (which I find somewhat strange) are frames of different ANSwers to the same MSG. > > I have a small patch that changes SessionImpl.java from 0.1.1 that > uses > a Map rather than a List, i.e. > Map<Integer, Map<Integer, ReplyListenerHolder>> replyListeners > > I can send out that patch; however, it appears that there are a large > number of changes between 0.1.1 and what is on the SVN trunk. I am > concerned that the changes don't fix this and could cause more > problems. > > It's difficult to follow the thread of execution; but based on a rough > inspection, I see that the ReplyListener is now saved in > FilterChainTargetHolder, which uses a ThreadLocal. This would > present a > problem if there were two requests sent from the same thread; the > second > (and any subsequent requests from that thread) would overwrite the > ReplyListener from the first. > The code in the trunk is really not tested that much. So it probably still contains lots of bugs. The main feature is filtering on channels (interceptors), well known from other specs (e.g. servlet filters). Additionally, the whole internals of beep4j were rewritten to work on frames instead of full assembled messages. This gives the receiver the chance to operate on frames (if he desires so) and so for example to terminate the session if the sender sends huges messages in an attempt to cause an OutOfMemoryError in the receiver. The ThreadLocals must only be used in a synchronous call. i.e. when you send a message, the ReplyListener is put into the ThreadLocal and later picked up at the end of the filter chain. However, when the control returns to your application code, it should be reset and there should be no way you can override it, whether from the same thread or another one. Of course, it depends also on the used filters (i.e. a filter introducing asynchrony might cause troubles here if not properly implemented). I believe that the trunk implementation should really be the implementation to be pushed. > > ~ Impact of congestion control for TCP mapping > > We are running some tests using a single BEEP channel to send a > moderately high rate of messages between two applications. Both are > using beep4j. Both do non-trivial processing on multiple threads. We > frequently get errors with seqno mismatches. > > The following (abridged) sequence is direct from a Wireshark packet > capture: > > RPY 3 12 * 3784 312 > <!-- first part of reply to 3-12 --> > END > RPY 3 9 * 4257 0 > END > SEQ 3 23232 4096 > RPY 3 9 . 4257 473 > <!-- reply to 3-9 --> > END > RPY 3 12 . 4096 161 > <!-- second part of reply to 3-12 --> > END > RPY 3 6 . 5203 473 > <!-- reply to 3-6 --> > END > > As you can see - out of order message numbers on replies (I'm using > the > SessionImpl.java patch mentioned above). More alarming is the fact > that > beep4j sends the first part of RPY 3-9 before finishing RPY 3-12. > What > is causes the failure is the seqno mismatch it sees when the first > (empty) part of RPY 3-9 is found. Frames on the same channel must be sent in order. Does this also happen without your patch (which I think violates the BEEP specification, see above)? > > > I think that the problem is that when there are two frames in the > queue > and the sender window (i.e. that advertised by the other end) is too > small, TCPMapping splits the first frame (correctly), puts the > remainder > back on the queue (correctly), but then continues to try to process > subsequent frames. It should not be sending anything more (except > maybe > the SEQ message) when it does not have any space left in the window. > > The following might fix that part: > > private Frame nextFrame() { > + if (frames.isEmpty() || senderWindow.remaining() <= 0) { > - if (frames.isEmpty()) { > return null; > > But I'm not sure what the calling context expects from sendFrames. I > expect that it should manage, but I'll need to test this further to > find > out. > The whole frame splitting code in nextFrame() should not shuffle frames. If a frame is split, the first part is sent, the second part is added to the front of the queue and thus will be the first one to be sent the next time. From a quick glance at the code I do not see how this could happen. Could you send a unit test reproducing that problem on the ChannelHandler implementation. > > On the other hand, the following looks like a problem, but it might > actually work. When TCPMapping receives a SEQ message it immediately > calls transport.sendBytes(createSEQFrame(channel, ackno, windowSize)) > rather than creating and queuing up a new Frame object. > When TCPMapping receives a SEQ frame it merely updates the send window size. Then, it tries to send queued frames. However, the SEQ frame is only created and sent after receiving ordinary frames (see frameReceived). Could you please clarify that one? Anyways, sending the SEQ frame directly might actually be a real problem (see below). > I understand how this might be desirable, but it might be better (for > the sake of interoperability) to defer the sending of this message if > there is an outstanding split frame. From RFC 3080: > > When a message is segmented and sent as several frames, those frames > must be sent sequentially, without any intervening frames from other > messages on the same channel. > > I have a few partially formed thoughts on how to solve this, but I'll > get back to that tomorrow. I'll test the simple senderWindow check > once > I have both fixes together. Hm, that's right. I've never thought about the situation where a SEQ frame is interleaved between two frames of a message. So, a patch that solves the problem is very welcome. > > > Let me know if anyone wants the SessionImpl.java patch for 0.1.1. > > Cheers, > Martin > > ------------------------------------------------------------------------------------------------ > This message is for the designated recipient only and may > contain privileged, proprietary, or otherwise private information. > If you have received it in error, please notify the sender > immediately and delete the original. Any unauthorized use of > this email is prohibited. > ------------------------------------------------------------------------------------------------ > [mf2] > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by the 2008 JavaOne(SM) Conference > Don't miss this year's exciting event. There's still time to save > $100. > Use priority code J8TL2D2. > http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone > _______________________________________________ > beep4j-user mailing list > bee...@li... > https://lists.sourceforge.net/lists/listinfo/beep4j-user |
From: Thomson, M. <Mar...@an...> - 2008-05-05 07:09:58
|
Hi, I'm seeing a few problems with beep4j under high loads in concurrent applications. ~ Out of order messages On inspection of the code in SessionImpl.java, it appears that out of order receipt of response messages causes a protocol exception (which drops the connection). The replyListeners collection doesn't maintain the set of ReplyListener instances for each channel based on the message number - the collection is a list. When a response arrives it pulls the first item of the list and raises the exception if the number doesn't match. One of the main advantages that BEEP provides is the out of order receipt of responses. If this feature wasn't available, we might as well be using HTTP. I have a small patch that changes SessionImpl.java from 0.1.1 that uses a Map rather than a List, i.e. Map<Integer, Map<Integer, ReplyListenerHolder>> replyListeners I can send out that patch; however, it appears that there are a large number of changes between 0.1.1 and what is on the SVN trunk. I am concerned that the changes don't fix this and could cause more problems. It's difficult to follow the thread of execution; but based on a rough inspection, I see that the ReplyListener is now saved in FilterChainTargetHolder, which uses a ThreadLocal. This would present a problem if there were two requests sent from the same thread; the second (and any subsequent requests from that thread) would overwrite the ReplyListener from the first. ~ Impact of congestion control for TCP mapping We are running some tests using a single BEEP channel to send a moderately high rate of messages between two applications. Both are using beep4j. Both do non-trivial processing on multiple threads. We frequently get errors with seqno mismatches. The following (abridged) sequence is direct from a Wireshark packet capture: RPY 3 12 * 3784 312 <!-- first part of reply to 3-12 --> END RPY 3 9 * 4257 0 END SEQ 3 23232 4096 RPY 3 9 . 4257 473 <!-- reply to 3-9 --> END RPY 3 12 . 4096 161 <!-- second part of reply to 3-12 --> END RPY 3 6 . 5203 473 <!-- reply to 3-6 --> END As you can see - out of order message numbers on replies (I'm using the SessionImpl.java patch mentioned above). More alarming is the fact that beep4j sends the first part of RPY 3-9 before finishing RPY 3-12. What is causes the failure is the seqno mismatch it sees when the first (empty) part of RPY 3-9 is found. I think that the problem is that when there are two frames in the queue and the sender window (i.e. that advertised by the other end) is too small, TCPMapping splits the first frame (correctly), puts the remainder back on the queue (correctly), but then continues to try to process subsequent frames. It should not be sending anything more (except maybe the SEQ message) when it does not have any space left in the window. The following might fix that part: private Frame nextFrame() { + if (frames.isEmpty() || senderWindow.remaining() <= 0) { - if (frames.isEmpty()) { return null; But I'm not sure what the calling context expects from sendFrames. I expect that it should manage, but I'll need to test this further to find out. On the other hand, the following looks like a problem, but it might actually work. When TCPMapping receives a SEQ message it immediately calls transport.sendBytes(createSEQFrame(channel, ackno, windowSize)) rather than creating and queuing up a new Frame object. I understand how this might be desirable, but it might be better (for the sake of interoperability) to defer the sending of this message if there is an outstanding split frame. From RFC 3080: When a message is segmented and sent as several frames, those frames must be sent sequentially, without any intervening frames from other messages on the same channel. I have a few partially formed thoughts on how to solve this, but I'll get back to that tomorrow. I'll test the simple senderWindow check once I have both fixes together. Let me know if anyone wants the SessionImpl.java patch for 0.1.1. Cheers, Martin ------------------------------------------------------------------------------------------------ This message is for the designated recipient only and may contain privileged, proprietary, or otherwise private information. If you have received it in error, please notify the sender immediately and delete the original. Any unauthorized use of this email is prohibited. ------------------------------------------------------------------------------------------------ [mf2] |
From: Simon <co...@gm...> - 2008-02-10 20:26:23
|
Hi Alan I've added a ChannelFilter interface that allows to filter all the "events" that happen on a channel (receiving messages, sending messages, ...). Based on that I've reimplemented the beep4j internals to work on frames instead of messages (see my last post 'Frames vs. Messages'). So, now it is possible to have a ChannelHandler operating on frames and to limit incoming message sizes. The old message-based mode is still supported by adding a MessageAssemblingFilter to the filter chain. I'll definitely have to write a user guide soon that shows how to use these new features. In the meantime, feel free to ask any questions you have. Simon On Feb 10, 2008, at 6:00 PM, Alan D. Cabrera wrote: > Hi Simon, > > I noticed that you've checked in a good chunk of work. Can you > characterize what these changes entail? Thanks. > > > Regards, > Alan > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Microsoft > Defy all challenges. Microsoft(R) Visual Studio 2008. > http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ > _______________________________________________ > beep4j-user mailing list > bee...@li... > https://lists.sourceforge.net/lists/listinfo/beep4j-user |
From: Alan D. C. <li...@to...> - 2008-02-10 17:00:06
|
Hi Simon, I noticed that you've checked in a good chunk of work. Can you characterize what these changes entail? Thanks. Regards, Alan |
From: Simon <co...@gm...> - 2008-01-22 20:49:59
|
I've had some time to think through the discussions we've recently had on the list. Here now the changes I propose. I'll change the whole internals of beep4j so that it works only on BEEP frames on the receiving side. Of course, this also has some impact on the public API: ChannelHandler +receivedMSG(message:Object,reply:Reply) ReplyHandler +receivedRPY(message:Object) +receivedERR(message:Object) +receivedANS(message:Object) +receivedNUL(message:Object) You may have noticed that I've implemented channel filters (trunk). I'll implement two filters. One I'll call MessageAssemblingFilter that converts the stream of frames into Message objects and passes those Message objects down the filter chain (ultimately to the ChannelHandler / ReplyHandler). The other I'll call MessageSizeLimittingFilter that should be used before the MessageAssemblingFilter to limit the size of BEEP messages. Of course, this filter would have to be configured with a strategy how to handle oversized messages. One note on the type (java.lang.Object) of the message parameter in the interfaces above: the general object type leaves it open to implement channel filters that convert to any type the application desires. The default (without filters) is a Frame object. What do you think? Simon |
From: Simon <co...@gm...> - 2008-01-17 18:53:11
|
On Jan 17, 2008, at 4:46 PM, Alan D. Cabrera wrote: > > On Jan 16, 2008, at 9:32 PM, Simon wrote: > >> >> On Jan 15, 2008, at 8:31 AM, Alan D. Cabrera wrote: >> >>> >>> On Jan 14, 2008, at 9:34 PM, Simon wrote: >>> >>>> >>>> On Jan 14, 2008, at 9:55 PM, Sam Roberts wrote: >>>> >>>>> On Mon, Jan 14, 2008 at 11:46:31AM -0800, Simon wrote: >>>>>> >>>>>> How would you combine those two ways of message processing into >>>>>> the >>>>>> current code? What kind of API do you envision? >>>>> >>>>> Not sure. Maybe if the frame level handlers are registered, they >>>>> get >>>>> called. If not, the library internally accumulates frames into a >>>>> message, and calls the message hander with the entire message, as >>>>> it >>>>> does now? >>>> >>>> Or maybe I could just pass frames through beep4j and accumulate the >>>> messages before calling the ChannelHandler / ReplyHandler if >>>> desired. >>>> So the default would be to pass frames unless something is done >>>> about >>>> it. >>>> >>> >>> What about making the FrameHandler a FrameHandler interceptor stack? >>> The same for ChannelHandler/ReplyHandler. >> >> I don't want to allow FrameHandler to be implemented by application >> programmers (at least not at the current place in code). It is just >> to low level. The whole channel management profile and message >> correlation is implemented on top of this functionality and would >> have to be reimplemented by the application programmer. > > Not sure how using an interceptor stack means that the application > programmers would be managing the message correlation, etc. I was > thinking that the programmer could add interceptors before and/or > after yours. Let's zero in on this disconnect between you and I. > As mentioned in an another e-Mail there are two somewhat orthogonal issues. One is doing something about too large incoming messages and the other is handling individual frames (instead of assembled messages). From your statements it seems you just want a way to solve the first issue (that was your original point). Do you envision any other way the FrameHandler interceptors might be useful? I mean, most of the BEEP specific stuff happens inside the Session (channel management, message correlation, state management, ...). The FrameHandlers are very raw, they can not even send a response. If it is possible to provide a solution for the second issue mentioned above (by making the whole beep4j library message operate on frames) then your problem can easily be solved. If I find a serious reason why this is not a good idea, I'll rather implement the callback from FrameHandler to ChannelHandler directly in beep4j. I consider the FrameHandler interface (and its implementations) an internal implementation detail I'd rather not expose to users of beepj4. >> A solution that sounds more promising is to reimplement beep4j so >> that frames are passes through the library up to the >> ChannelHandler / ReplyHandler. This would shift the responsibility >> to assemble messages to the application. Possibly, the assembling >> could be done by a ChannelFilter. I think the frame based mode is >> feasible. The big question is how the API will look like. Once the >> trunk stabilizes I'll try to come up with a proposal. > > If I understand you correctly this would make the ChannelHandlers and > ReplyHandlers pretty bulky. I would much rather have a collaboration > between my CH/RH instances and the FH interceptors. This will keep my > handlers focused, easier to read and debug. Why would that be? I don't think we'll understand each other without a concrete code / API. I'll think about it and post my ideas to the list when I'm confident that it's feasible. Simon |
From: Alan D. C. <li...@to...> - 2008-01-17 15:46:54
|
On Jan 16, 2008, at 9:32 PM, Simon wrote: > > On Jan 15, 2008, at 8:31 AM, Alan D. Cabrera wrote: > >> >> On Jan 14, 2008, at 9:34 PM, Simon wrote: >> >>> >>> On Jan 14, 2008, at 9:55 PM, Sam Roberts wrote: >>> >>>> On Mon, Jan 14, 2008 at 11:46:31AM -0800, Simon wrote: >>>>> >>>>> How would you combine those two ways of message processing into >>>>> the >>>>> current code? What kind of API do you envision? >>>> >>>> Not sure. Maybe if the frame level handlers are registered, they >>>> get >>>> called. If not, the library internally accumulates frames into a >>>> message, and calls the message hander with the entire message, as >>>> it >>>> does now? >>> >>> Or maybe I could just pass frames through beep4j and accumulate the >>> messages before calling the ChannelHandler / ReplyHandler if >>> desired. >>> So the default would be to pass frames unless something is done >>> about >>> it. >>> >> >> What about making the FrameHandler a FrameHandler interceptor stack? >> The same for ChannelHandler/ReplyHandler. > > I don't want to allow FrameHandler to be implemented by application > programmers (at least not at the current place in code). It is just > to low level. The whole channel management profile and message > correlation is implemented on top of this functionality and would > have to be reimplemented by the application programmer. Not sure how using an interceptor stack means that the application programmers would be managing the message correlation, etc. I was thinking that the programmer could add interceptors before and/or after yours. Let's zero in on this disconnect between you and I. > A solution that sounds more promising is to reimplement beep4j so > that frames are passes through the library up to the > ChannelHandler / ReplyHandler. This would shift the responsibility > to assemble messages to the application. Possibly, the assembling > could be done by a ChannelFilter. I think the frame based mode is > feasible. The big question is how the API will look like. Once the > trunk stabilizes I'll try to come up with a proposal. If I understand you correctly this would make the ChannelHandlers and ReplyHandlers pretty bulky. I would much rather have a collaboration between my CH/RH instances and the FH interceptors. This will keep my handlers focused, easier to read and debug. Regards, Alan |
From: Simon <co...@gm...> - 2008-01-17 05:38:49
|
I see three requirements from this whole thread: - process BEEP frames instead of reassembled messages - allow termination of session (terminates underlying TCP connection) - provide a way to limit message sizes These three are (potentially) overlapping (e.g. the first allows the implementation of the last requirement). I definitely see the value of all these requirements, however I'll not give any guarantees as when they might get implemented. I'm doing this in my spare time... Feel free to provide suggestions / patches / ... Simon |
From: Simon <co...@gm...> - 2008-01-17 05:32:37
|
On Jan 15, 2008, at 8:31 AM, Alan D. Cabrera wrote: > > On Jan 14, 2008, at 9:34 PM, Simon wrote: > >> >> On Jan 14, 2008, at 9:55 PM, Sam Roberts wrote: >> >>> On Mon, Jan 14, 2008 at 11:46:31AM -0800, Simon wrote: >>>> >>>> How would you combine those two ways of message processing into the >>>> current code? What kind of API do you envision? >>> >>> Not sure. Maybe if the frame level handlers are registered, they get >>> called. If not, the library internally accumulates frames into a >>> message, and calls the message hander with the entire message, as it >>> does now? >> >> Or maybe I could just pass frames through beep4j and accumulate the >> messages before calling the ChannelHandler / ReplyHandler if desired. >> So the default would be to pass frames unless something is done about >> it. >> > > What about making the FrameHandler a FrameHandler interceptor stack? > The same for ChannelHandler/ReplyHandler. I don't want to allow FrameHandler to be implemented by application programmers (at least not at the current place in code). It is just to low level. The whole channel management profile and message correlation is implemented on top of this functionality and would have to be reimplemented by the application programmer. A solution that sounds more promising is to reimplement beep4j so that frames are passes through the library up to the ChannelHandler / ReplyHandler. This would shift the responsibility to assemble messages to the application. Possibly, the assembling could be done by a ChannelFilter. I think the frame based mode is feasible. The big question is how the API will look like. Once the trunk stabilizes I'll try to come up with a proposal. Simon |
From: Alan D. C. <li...@to...> - 2008-01-15 07:31:41
|
On Jan 14, 2008, at 9:34 PM, Simon wrote: > > On Jan 14, 2008, at 9:55 PM, Sam Roberts wrote: > >> On Mon, Jan 14, 2008 at 11:46:31AM -0800, Simon wrote: >>> >>> On Jan 14, 2008, at 8:16 PM, Sam Roberts wrote: >>> >>>> On Mon, Jan 14, 2008 at 10:19:50AM -0800, Alan D. Cabrera wrote: >>>>> I want to limit the size of incoming messages so that an error >>>>> gets >>>>> generated if the size of the incoming message exceeds an pre-set >>>>> size. Ideally this would be part of the channel handler >>>>> interface. >>>> >>>> Here's some thoughts on another way to deal with this: >>>> >>>> I would like to see b4j allow closer interaction with the flow >>>> control, >>>> so that a receiver can receive incomplete messages as they arrive, >>>> and >>>> process them. In XMLish terms, this would be a "SAX-like" API, >>>> whereas >>>> b4j currently has a "DOM-like" API, only. They are both useful in >>>> different circumstances. >>> >>> BEEP is inherently message oriented. How would you process the >>> incoming message fragments? XML for instance is not really parseable >>> in chunks (unless you have an InputStream that blocks until the >>> whole >>> message arrives). Ok, a data transfer profile might profit from this >>> kind of processing as not as much data needs to be kept in memory. >> >> I'm not sure what you mean about XML. Have you never used a streaming >> XML decoder, or are we talking across each other? Expat parses in >> chunks: >> >> http://www.xml.com/pub/a/1999/09/expat/index.html?page=2 >> > > Didn't know that this kind of API exists. I'm not aware of a similar > out of the box API in Java. You'd probably have to create a special > InputStream that blocks when there are still more frames expected. > >>> How would you combine those two ways of message processing into the >>> current code? What kind of API do you envision? >> >> Not sure. Maybe if the frame level handlers are registered, they get >> called. If not, the library internally accumulates frames into a >> message, and calls the message hander with the entire message, as it >> does now? > > Or maybe I could just pass frames through beep4j and accumulate the > messages before calling the ChannelHandler / ReplyHandler if desired. > So the default would be to pass frames unless something is done about > it. > What about making the FrameHandler a FrameHandler interceptor stack? The same for ChannelHandler/ReplyHandler. Regards, Alan |