expressions are a mix of :-) nd :-( Next time just let me know when you folks plan a debugging marathon :-) May be we can use some screen-sharing tool and I can also participate means listen :-)
It would have been a good learning. thanks ashish On Fri, Feb 19, 2010 at 8:04 PM, Emmanuel Lecharny <[email protected]> wrote: > Hi, > > here is a memo describing the debugging session we did yesterday with > Julien. I have added TODO each time I think we can do better. > > > > MINA analyze when processing one single message. The server receives "hello" > and should return "HELLO". > > - We start in the main IoProcessor loop, after a select() returning 1 on a > channel set with OP_READ, as we have just received the message. > - the process() method is called. It does two things : > 1) if the session is readable and not suspended, read the channel > 2) is the session is writable and not suspended, add the session to the > queue of session ready for flush, if not already there. > > TODO : at this point, we have no idea if the session has some pending > writes. Check if the OP_WRTE flag has been set on the selectionKey > > - in the read() method, we allocate a buffer with the size configured for > the session. Usually, it's way to big if you just have hundreds of bytes to > read, and if you set the config to 65000… > > TODO : use smaller buffers > > - Then we call the chain's messageReceived() method > > - In the ProtocolCodecFilter, we grab the decoder instance, which is stored > in the session's Attributes > > TODO : There is no good reason to store the instance in the attributes. It > should be part of the session parameters. > > - then we create a new instance of a decoderOut. > > TODO : The only reason we have such an object created here is that we need > to create a queue to store the result of the decoding. > > - We now decode the incoming data, until the buffer is empty. We may have > more than one message in the buffer, so each one of them is decoded and > enqueued in the encoderOut queue. > > - The doDecode() method is called. This is the user provided code. The > decoded message is enqueued, and we do that until the doDecode() method > returns false (meaning we don't have anymore message to decode) > > - We call the encodedOut.flush() method to go up in the chain > > TODO : it should be done directly here > > - For each decoded message in the queue, call the next filter > messageReceived() event > > TODO : we went through the tail filter, which is updating some stats used by > the idle session handler, but this should be done in another place. Everyone > does not want to deal with idle sessions … My be an IdleFilter could help ? > > - In the handler, we process the message, and write the response calling > session.write( response ) > > - The session.write() method create a writeFuture and a WriteRequest object > containing the response, the recipient's address and the future > > TODO : this WriteFuture is never used. > > - We go back to the chain in reverse order, firing the FilterWrite method > > TODO : This should be a separate chain > > - We get the encoded and we create an encoderOut instance, containing a > queue > > TODO : here, unless the encoder creates more than one buffer to be sent, > there is no need to create a queue. We can also call the encoder, get back > the result and flush it, doing so receptively until all the encoded pieces > has been generated (but I feel like we better get back a set of buffers > instead as a response. No need of a queue) > > - The user's provided encoder method is called, the encoded buffer is > enqueued. It can still be a plain Message, if we want to go though another > encoder > > - At this point, we call the flushWithoutFuture() method which loop on the > encoderOut queue to send all the encoded buffers > > - for each encoded message in the queue, we create an EncodedWriteRequest > encapsulating the encoded buffer, and we go down in the chain > > - The WriteRequest is enqueued in the session WriteRequestQueue, and if the > session is not suspended for write, we call the processor.flus() method > > TODO : there is no reason to call this method now. Also the > session.getProcessor() method ask the processor pool for the session's > processor, which is useless, as each session is attached to a single > IoProtocol. We must store not the pool, but the processor > > - If the session is not already in the queue of session waiting to be > flushed, we add it into this queue, then we wake up the selector > > TODO : Waking up the selector at this point is questionable. It's probably > totally useless. > > - Ok, we go back, and call the filterWrite() method once again in the > protocolCodecFilter, with a new MessageWriteRequest instance, encapsulating > the response. > > TODO : WTF ??? Why do we process the same message twice ??? > > - In the HeadFilter, we grab the message, which is empty. In fact, this > empty message is used as a marker for the 'end of message'. Nevertheless, we > add this empty message to the WriteRequestQueue > > - We ask the processor to flush again the session, but as the session has > already been added into the scheduledForFlush queue, nothing is done > > - And we are done with all the process() method. We come back to the main > select loop > > - Time to process the session waiting to be flushed now… This is done with a > call to the flush(currentTime) method. We check that we have sessions ready > to be flushed first. If so, we loop on all those sessions. > > Note : a session may be marked as ready to be flushed either because we just > have some new message for it, or because a big message hasn't be written to > the client completely n a previous loop. > > At first, we remove the session from the queue of scheduled sessions. We > will put it back if the full message hasn't been written later. If the > session is open, we call the pocessor.flushNow() method > > - There, we grab he WriteRequestQueue for this session. There is something > obscure done here : we compute some number based on the max read and write > buffer. > > TODO : remove all this crapity… > > We reset the OP_WRITE flag (it may have been set in a previous call). > > The session stores the current buffer being flushed in a special holder. if > it's null, we take the first message from the queue and stores it into this > session. > > TODO : This is absolutely useless. At this point, we know exactly where we > left when we wrote it to the client. it's enough to keep the buffer in the > queue, peeking it instead of polling it. We just remove it when the message > has been completely sent. > > - We now call the writeBuffer() method, responsible for the writing of the > data to the client. > > TODO : There is a totally useless loop in this method, where we try to write > to the client up to 256 times, just in case the client is slow, I guess. > This is overkilling. We have *no idea* why this loop exists, except that > back in the past, some strange bug was fixed with such a 'workaround' > > - The buffer is written to the channel, and we get back the number of > written bytes. If we have written all the bytes, we call the fireMessage() > method. The currentWriteRequest is cleared in the session. The WriteFuture > is now set to Written state (useless, as nobody uses this information). > > TODO : Get rid of useless tasks > > - In the PotocolCodecFilter, as the WriteRequest is the encodedWriteRequest, > we immediately return. > > TODO Why the hell are we calling the messageSent() method at all as we just > don't process the information here ??? probably because we have no clue > about the fact that the WriteRequest is a message at this point. It has to > be fixed, it's sucking useless CPU > > - Then we are back, and process the ext message in the queue, which is the > empty message (used as a end-of-message marker) > > - The writeBuffer() method just do nothing, as this message is empty. It > just call the messageSent() method to inform the handler that the message > has been sent. So we go up in the chain to the handler, and back. > > - There is now a stupid things done (one more …) : as we didn't wrote > anything, the number of written bytes is 0, so we consider that the socket > is full, and we switch the OP_WRITE flag to true. This is a 100% guarantee > that we will do a full select loop again, for nothing… > > TODO : FIX ME !!! > > - When we are back from the flushNow() method, we get a false, as we didn't' > flushed the empty message. But we are done with the list of sessions > scheduled for a flush. > > - and we get back to the select(), which will exit immediately with at least > one selectionKey set with OP_WRITE, as wear still waiting to write the empty > message !!!. That means we will process two reads for one write. > > - once we enter again in the mail select loop, the select() returns 1, we go > back to the process() method, do nothing in the read part, but put back the > session onto the scheduledForFlush queue, in order to be processed again in > the flush() call. > > - In the flush() method, we reset the OP_WRITE flag, so that we aren't woke > up again, and we are done. > > > > That's it !!! Lot of hacks, lot of useless things, lot of potential speedup > expected ! > > > > > -- > Regards, > Cordialement, > Emmanuel Lécharny > www.nextury.com
