Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-352

Apache HTTP Client 4 connection release problems

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Major Major
    • 2.0-beta-2
    • None
    • None
    • None

      Problem #1.

      We use RestEasy 1.2.1. The RestEasy client we use in combination with the ApacheHttpClient4Executor. It doesn't release connections back to the connection pool on its own, unless you always consume the entity and even then it is kind of flaky. This is basically taking down our server in production so I would say this needs some documentation and advise on proper workarounds at the very least.

      basically this code:

      for(int i=0;i<200;i++)

      { ClientResponse response = this.ourRestEasyClient.getIt("invalidId"); // will produce a 404 }

      This will exhaust connections in the pool. With out configuration (ThreadSafeConnectionManager, 20 connections per route and a stale connection cleanup thread) it basically will start throwing exceptions. Debugging the connection manager confirms RestEasy never releases the connections back to the pool.

      Basically this is a design problem with JAX-RS and RestEasy and I guess you must be aware of problems in this area. To work around this I implemented this fix, which mostly works (except for problem #2).

      for(int i=0;i<200;i++)

      { BaseClientResponse<SomeDto> response =null; response = (BaseClientResponse<SomeDto>) this.ourClient.getIt("invalidid"); // will produce a 404 }

      finally

      { forceReleaseConnection(response); }

      }

      void forceReleaseConnection(ClientResponse<T> clientResponse) {
      try {
      if (clientResponse != null) {
      BaseClientResponse<T> response = (BaseClientResponse<T>) clientResponse;
      BaseClientResponseStreamFactory streamFactory = response.getStreamFactory();
      if(streamFactory != null)

      { streamFactory.getInputStream(); }
      response.releaseConnection();
      }
      } catch (IOException e) { LOG.error("error releasing connection", e); } catch (ClassCastException e) { LOG.fatal("client response object is not of type org.jboss.resteasy.client.core.BaseClientResponse<T>"); }
      }

      Problem #2
      Note that I actually have to request the inputStream in order to be able to release the connection. Just calling releaseConnection silently fails unless you do that (null check on the stream object). So even if you dutifully call releaseConnection, it won't actually do that unless you first attempt to get the entity.

      Problem #3:

      Line 80 in ApacheHttpClient4Executor:

      stream = new SelfExpandingBufferredInputStream(res.getEntity().getContent());

      This triggers an NPE if there is no entity to consume. So I have modified forceReleaseConnection to catch the npe as well.

      public static <T> void forceReleaseConnection(ClientResponse<T> clientResponse) {
      try {
      if (clientResponse != null) {
      BaseClientResponse<T> response = (BaseClientResponse<T>) clientResponse;
      BaseClientResponseStreamFactory streamFactory = response.getStreamFactory();
      if(streamFactory != null) { streamFactory.getInputStream(); }

      response.releaseConnection();
      }
      } catch(NullPointerException e)

      { // happens when the response has no entity and forceReleaseConnection is called anyway. }

      catch (IOException e)

      { LOG.error("error releasing connection", e); }

      catch (ClassCastException e)

      { LOG.fatal("client response object is not of type org.jboss.resteasy.client.core.BaseClientResponse<T>"); }

      }

      This is the only way I know of that guarantees connections that RestEasy uses are actually released back to the connection pool. I think fundamentally the way RestEasy hides the underlying details of connection handling are flawed and dangerous to use.

      I hope this helps you to improve robustness in RestEasy, which I think is a major issue right now. BTW. a proper fix should probably involve a bit of rearchitecting. Essentially connection handling without some guaranteed cleanup in a finally block is just irresponsible in any production system.

      Regards,

      Jilles van Gurp

            patriot1burke@gmail.com Bill Burke (Inactive)
            patriot1burke@gmail.com Bill Burke (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: