[ 
https://issues.apache.org/jira/browse/THRIFT-3084?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14490814#comment-14490814
 ] 

James E. King, III commented on THRIFT-3084:
--------------------------------------------

Hi Randy,

I do understand what you are saying however I must respectfully disagree with 
the argument.  Please take a look at the files I posted in THRIFT-3083.  I 
think the elegance of boiling down a server to the implementation of "a client 
connected", "a client disconnected", and the ability to add additional 
post-conditions to serve() is exactly what C++ was intended for... I think what 
you are arguing for is the ability to subclass the servers as well as make the 
servers understandable to junior C++ engineers.  I feel that it is extremely 
important to have consistent serve() and client handling behavior which is why 
I introduced 3081 and 3083 into Jira.

My biggest concern with not doing this work is that the different framework 
servers will behave in different ways even though they intend to provide the 
same post-conditons.  Case in point, if you look at the pre-THRIFT-3083 
TThreadPoolServer::serve() handling if TTransportException, is is quite 
different from TSimpleServer and TThreadedSevrer, when in fact it wants to be 
the same since there is only on TServerTransport, and there should be 
consistent exception behavior for TServerTransport no matter what is consuming 
it.  One logs some issues and one does not.  In another case, TSimpleServer 
will attempt to close the client if an error occurs, but the TThreadedServer 
only attempts to close the input and output protocol transports and makes to 
attempt to close the client. Both subclasses need to enforce the same setof 
post-conditions so why do they differ/  They differ because identical code was 
copied and modified instad of being consolidated.

With the introduction of THRIFT-2441 I had to modify three implementations of 
TServer to ensure they were properly handling TTransportException(INTERRUPTED). 
 I think it makes sense to have a single instantiation of the server serve() 
and a single instantiation of the client run loop since the generated code 
depends on reliable interface contracts, as does the TServerTransport.

Given the processing logic in a TSimpleServer and a TThread*Server wants to be 
identical ffor a given client, this is why I introduced THRIFT-3081.  On 
further observation is was clear that the three sevrers save the non-blocking 
server had extremely similar logic patterns, so it made sense to me to 
consolidate and {{standardize}} their behavior so that folks using the 
framework could experience consistent behavior when switch between server types 
provided by the framework.  I belive the combination of 2441, 3081, 3083, and 
3084 will yield a minimum viable production quality server provided by the 
framework proper, where folks who want to use Thrift will no longer need to 
roll their own TSevrer to achieve enterprise quality behavior.  This should 
encourage adoption and reduce intergation effort.  Given other projects like 
protobuf only provide the protocol portion, enhancing the transport and server 
should further distance the project as useful from alternatives.

One thing I absolutely love about the original concept of thrift was the clear 
separation of processor, protocol and transport.  I find this to be unique 
within the field and the changes I have made [hopefully] do not violate those 
core axioms.  TO answer your question - yes, TSimpleServer and any other 
TServer including TThreadPoolServer should handle clients in a consistent 
manner.  The differences between the servers has been boiled down to the 
following three items:

1. How do I handle a new client connection?
2. How do I handle a client that has disconnected?
3. How to I ensure that a post-condition of serve() is that no clients are 
connected?

The changes in THRIFT-3083 make this very clear and easy to do.

In argument to your statement that TSimpleServer be self-contained and distinct 
from other servers, , if TSimpleServer and TThreadedServer have 90% common code 
and 10% divergent code in the respect of items 1 and 3 above, why should the 
common code be replicated such tht is has a chance to diverge in behavior?  It 
should notbe different, because it is in fact the same, and I would encourage 
any C++ engineer to avoid code duplication with the same set of changes that I 
have submitted to the project, because in the end when code is not duplicated, 
it only needs to be tested once to ensure it is functioning properly.

TServerTransport makes it easy (apart from the crazy constructor variances) for 
anyone to make a server that differs in the areas where it matters most - the 
three items I listed as 1,2,3 previously.

Thrift-3084 is specifically to ensure we do not accept() more clients than the 
server allows; THRIFT-3083 is the right place to discuss TServer subclass code 
duplication and the merits of such.  I would argue the changes I have made 
simplify maintenance because now there is only one place in the code one needs 
to change the common client processing loop (THRIFT-3081) or the common server 
processing loop (THRIFT-3083).\

> C++ add concurrent client limit to threaded servers
> ---------------------------------------------------
>
>                 Key: THRIFT-3084
>                 URL: https://issues.apache.org/jira/browse/THRIFT-3084
>             Project: Thrift
>          Issue Type: Improvement
>          Components: C++ - Library
>    Affects Versions: 0.8, 0.9, 0.9.1, 0.9.2
>            Reporter: James E. King, III
>
> The TThreadedServer and TThreadPoolServer do not impose limits on the number 
> of simultaneous connections, which is not useful in production as bad clients 
> can drive a server to consume too many file descriptors or have too many 
> threads.
> 1. Add a barrier to TServerTransport that will be checked before accept().
> 2. In the onClientConnected override (see THRIFT-3083) if the server reaches 
> the limit of the number of accepted clients, enable the barrier.
> 3. In the onClientDisconnected override if the count of connected clients 
> falls below the maximum concurrent limit, clear the barrier.  This will allow 
> the limit to be changed dynamically at runtime (lowered) with drain off 
> clients until more can be accepted.
> Alternate proposal: Implement a Semaphore and have the servers block the 
> serve() thread if the client that arrived puts the server at the concurrent 
> client limit.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to