0

I asked (and answered myself) this question a couple of days ago, and resolved the problem, but I can't quite understand why the problem was solved and was hoping to get some clarification.

Essentially, I have implemented a jax-rs-based REST service that retrieves information from a RavenDB database and returns that content in a stream. The problem that I had was an unclosed database results iterator, which caused the REST service to hang (and accept no further requests) after exactly 10 requests.

My code is, roughly, as follows:

public Response ...
    {
        (...)

        StreamingOutput adminAreaStream = new StreamingOutput()
        {
            ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();

            @Override
            public void write(OutputStream output) throws IOException, WebApplicationException
            {
                try(IDocumentSession currentSession = ServiceListener.ravenDBStore.openSession())
                {
                    Writer writer = new BufferedWriter(new OutputStreamWriter(output));
                    (...)   
                    CloseableIterator<StreamResult<AdministrativeArea>> results;
                    (...)
                    writer.flush();
                    writer.close();
                    results.close();
                    currentSession.advanced().clear();
                    currentSession.close();
                }
                catch (Exception e)
                {
                    System.out.println("Exception: " + e.getMessage() + e.getStackTrace());
                }
            }
        };
        if(!requestIsValid)
            return Response.status(400).build();
        else
            return Response.ok(adminAreaStream).build();
    }

From what I understand about the object lifecycle in Java, or rather more specifically object reachability and garbage collection, even though I didn't properly close that CleasableIterator, it should go out of scope/become unreachable by the time my method finishes with either a 400 or 200 status - and therefore get garbage collected.

Just to be clear: I am certainly not suggesting that one shouldn't properly close opened connections etc. - I AM doing that now - or rely on Java's garbage collection mechanism to save us from lazy/unclean coding... I am just struggling to understand exactly how those unclosed iterators could have caused the Tomcat behaviour observed.

In fact, my assumption is that we don't even need to know the details about the iterator's implementation, because at the "galactic level" of Java the object lifecycle, implementation differences are irrelevant. => "Once an object has become unreachable, it doesn't matter exactly how it was coded". The only thing I can imagine is that Tomcat somehow, (through its container mechanism ?), slightly changes the game here, and causes things to "hang around". Could someone please shed some light on this ?

Thanks in advance !

Hiro Protagonist
  • 474
  • 3
  • 15

1 Answers1

2

The CloseableIterator refers to a CloseableHttpResponse which refers to a HTTP connection. No finalizer releases the response or the connection, when CloseableIterator is not reachable anymore. You created a connection leak. Your bug is similar to the one described here: https://phillbarber.blogspot.com/2014/02/lessons-learned-from-connection-leak-in.html

See here why finalize methods to release resources are a bad idea: https://www.baeldung.com/java-finalize

chromanoid
  • 545
  • 3
  • 14
  • Thanks - that's EXACTLY what's happening... so is it fair to say that, in my scenario, it comes down to the fact that my http connections were exhausted in Tomcat ? It's peculiar, because the number 10 seems "very human" - and in my server.xml, I can't find an explicit limit of 10 (Neither maxConnections, nor maxThreads) ! Is it implied if nothing is specifically stated ? – Hiro Protagonist Nov 29 '18 at 09:36
  • 1
    Look here: https://github.com/ravendb/ravendb-jvm-client/blob/v4.1.2/src/main/java/net/ravendb/client/http/RequestExecutor.java#L1001 `HttpClientBuilder httpClientBuilder = HttpClients.custom().setMaxConnPerRoute(10);` It's hard coded into the RavenDB client. You can change the value by setting `RequestExecutor.configureHttpClient`. IMO this is not a very clean solution... – chromanoid Nov 29 '18 at 13:09
  • 1
    The RavenDB Java client library manages its own HTTP connections (via Apache HttpComponents library) and uses an internal connection pool that is independent to anything you can configure in Tomcat. – chromanoid Nov 29 '18 at 13:17
  • Here’s [another reason not to use finalize methods to release resources](https://stackoverflow.com/q/26642153/2711488)… – Holger Nov 29 '18 at 18:25
  • @chromanoid Perfect answer - thank you very much ! I had a hunch that it was something out of Tomcat's control... – Hiro Protagonist Nov 29 '18 at 19:09