Hi Andy

> -----Original Message-----
> From: Andy Seaborne <[email protected]>
> Sent: Monday, July 5, 2021 7:39 PM
> To: [email protected]
> Subject: Re: Jena hangs while reading HTTP stream
> 
> 
> On 05/07/2021 14:02, Martynas Jusevičius wrote:
>  > HTTPClient is not running out of connections? It is known to hang in such
> cases.
> 
> It is not reopening connections for one request.
> If parsing starts, it has the connection until the end.
> 
> On 05/07/2021 14:10, Rob Vesse wrote:
> > That's a really good suggestion.  In the normal code flow do you ever call
> stream.close() ? And is createHttpClient() re-using an existing HttpClient
> object ? And is the hang only happening after some requests have
> succeeded ?
> >
> > It is possible what is happening is that you aren't closing the stream (and 
> > I
> don't believe Jena's parsers ever close the stream for you) so after so many
> requests (10 I think by default) you are exhausting the max connections per
> route for the HTTP Client.  If that is the case wrapping the use of the stream
> in a try-with-resources block may be the solution.
> 
> Yes - try-with-resources.
> (And a transaction.)
> 
> If the code is in a loop, and connections are not managed properly HttpClient
> can run out of connections. And the OS kernel can run out of sockets for that
> matter if too many connections happen in too short a time. (Jena's test suite
> has to deal with this - and it's easier case.) This is not HttpClient's fault.
> 
> Rob's right - RDFParser does not close a resource that is passed into the
> parser. If given a resource, the caller is responsible - try-with-resources or
> similar is expected.

Ivan omitted it in his example code, but that's already the case.
So it's more like:

Model sub = ModelFactory.createDefaultModel();

try (TypedInputStream stream = HttpOp.execHttpGet(requestURL, 
WebContent.contentTypeNTriples, createHttpClient(auth), null)) {
    // The following part sometimes hangs:
    RDFParser.create()
        .source(stream)
        .lang(Lang.NTRIPLES)
        .errorHandler(ErrorHandlerFactory.errorHandlerStrict)
        .parse(sub.getGraph());
} catch (Exception ex) {
    // Handle the exception
}

> 
> We are missing some facts.
> 
> 1/ What's sub.getGraph()?

The sub variable is instantiated a few lines before like so:

Model sub = ModelFactory.createDefaultModel();

> 
> 2/ (if reading to memory) Is the GC going crazy trying to free heap space
> because of other earlier work?

Perhaps, but we only observe the issue sporadically and cannot reproduce at 
will.

> 
> 3/ Does any data get processed then stop, or does it actually never start?

Hard to tell based on what is currently logged.

> 
> 4/ We haven't seen what the HttpClient setup is. It isn't the default.

Here's the code:

    private static HttpClient createHttpClient(String auth) {
        Header header = new BasicHeader(HttpHeaders.AUTHORIZATION, auth);
        List<Header> headers = Collections.singletonList(header);
        return 
HttpClients.custom().useSystemProperties().setDefaultHeaders(headers).build();
    }

I've implemented some changes to set timeouts on the default request 
configuration.
Hopefully that should at least prevent the application getting into this 'hung' 
state.

Cheers,
John

> 
>      Andy
> 
> >
> > Rob
> >
> > On 05/07/2021, 14:03, "Martynas Jusevičius" <[email protected]>
> wrote:
> >
> >      HTTPClient is not running out of connections? It is known to hang in 
> > such
> cases.
> >
> >      On Mon, Jul 5, 2021 at 2:58 PM james anderson <[email protected]>
> wrote:
> >      >
> >      > good afternoon;
> >      >
> >      > > On 2021-07-05, at 12:36:20, Andy Seaborne <[email protected]>
> wrote:
> >      > >
> >      > >
> >      > >
> >      > > On 05/07/2021 10:01, Ivan Lagunov wrote:
> >      > >> Hello,
> >      > >> We’re facing an issue with Jena reading n-triples stream over 
> > HTTP.
> In fact, our application hangs entirely while executing this piece of code:
> >      > >> Model sub = ModelFactory.createDefaultModel();
> >      > >> TypedInputStream stream = HttpOp.execHttpGet(requestURL,
> WebContent.contentTypeNTriples, createHttpClient(auth), null)
> >      > >> // The following part sometimes hangs:
> >      > >> RDFParser.create()
> >      > >>         .source(stream)
> >      > >>         .lang(Lang.NTRIPLES)
> >      > >>         .errorHandler(ErrorHandlerFactory.errorHandlerStrict)
> >      > >>         .parse(sub.getGraph());
> >      > >> // This point is not reached
> >      > > >
> >      > >> The issue is not persistent, moreover it happens infrequently.
> >      > >
> >      > > Then it looks like the data has stopped arriving but the 
> > connection is
> still open. (or the system has gone in GC overload due to heap pressure.)
> >      > >
> >      > > Is intermittent on the same data? Or is the data changing? because
> maybe the data can't be written properly and the sender stops sending,
> though I'd expect the sender to close the connection (it's now in an unknown
> state and can't be reused).
> >      > >
> >      > >> When it occurs, the RDF store server (we use Dydra for that) logs 
> > a
> successful HTTP 200 response for our call (truncated for readability):
> >      > >> HTTP/1.1" 200 3072/55397664 10.676/10.828 "application/n-triples"
> "-" "-" "Apache-Jena-ARQ/3.17.0" "127.0.0.1:8104"
> >      >
> >      > the situation involves an nginx proxy and an upstream sparql 
> > processor.
> >      >
> >      > >
> >      > > What do the fields mean?
> >      >
> >      > the line is an excerpt from an entry from the nginx request log. that
> line contains:
> >      >
> >      >   protocol  code  requestLength/responseLength
> upstreamElapsedTime/clientElapsedTIme  acceptType  -  -  clientAgemt
> upstreamPort
> >      >
> >      > >
> >      > > Is that 3072 bytes sent (so far) of 55397664?
> >      > >
> >      > > If so, is Content-Length set (and then chunk encoding isn't 
> > needed).
> >      >
> >      > likely not, as the response is (i believe) that from a sparql 
> > request,
> which is emitted as it is generated.
> >      >
> >      > >
> >      > > Unfortunately, in HTTP, 200 really means "I started to send 
> > stuff", not
> "I completed sending stuff". There is no way in HTTP 1/1 to signal an error
> after starting the response.
> >      >
> >      > that is true, but there are indications in other logs which imply 
> > that the
> sparql processor believes the response to have been completely sent to
> nginx.
> >      > there are several reasons to believe this.
> >      > the times and the 200 response code in the nginx log indicate
> completion.
> >      > otherwise, it would either indicate that it timed out, or would 
> > include a
> 499 code, to the effect that the client closed the connection before the
> response was sent.
> >      > neither is the case.
> >      > in addition, the elapsed time is well below that for which nginx 
> > would
> time out an upstream connection.
> >      >
> >      > >
> >      > > The HttpClient - how is it configured?
> >      > >
> >      > >> So it looks like the RDF store successfully executes the SPARQL
> query, responds with HTTP 200 and starts transferring the data with the
> chunked encoding. Then something goes wrong when Jena processes the
> input stream. I expect there might be some timeout behind the scenes while
> Jena reads the stream
> >      > >
> >      > > Does any data reach the graph?
> >      > >
> >      > > There is no timeout at the client end - otherwise you would get an
> exception. The parser is reading the input stream from Apache HttpClient. If
> it hangs, it's because the data has stopped arriving but the connection is 
> still
> open.
> >      > >
> >      > > You could try replacing .parse(graph) with parse(StreamRDF) and
> plug in a logging StreamRDF so you can see the progress, either sending on
> data to the graph or for investigation, merely logging.
> >      > >
> >      > > In HTTP 1.1, a streamed response requires chunk encoding only
> when the Content-Length isn't given.
> >      >
> >      > i believe, the content length is not given.
> >      >
> >      > >
> >      > > >
> >      > > , and it causes it to wait indefinitely. At the same time
> ErrorHandlerFactory.errorHandlerStrict does not help at all – no errors are
> logged.
> >      > >> Is there a way to configure the timeout behavior for the 
> > underlying
> Jena logic of processing HTTP stream? Ideally we want to abort the request if
> it times out and then retry it a few times until it succeeds.
> >      > >
> >      > > The HttpClient determines the transfer.
> >      > >
> >      > >    Andy
> >      > >
> >      > > FYI: RDFConnectionRemote is an abstraction to make this a little
> easier. No need to go to the low-level HttpOp.
> >      > >
> >      > >
> >      > > FYI: Jena 4.mumble.0 is likely to change to using jena.net.http as 
> > the
> HTTP code. There has to be some change anyway to get HTTP/2  (Apache
> HttpClient v5+, not v4, has HTTP/2 support).
> >      > >
> >      > > This will include a new Graph Store Protocol client.
> >      > >
> >      > >> Met vriendelijke groet, with kind regards,
> >      > >> Ivan Lagunov
> >      > >> Technical Lead / Software Architect
> >      > >> Skype: lagivan
> >      > >> Semaku B.V.
> >      > >> Torenallee 20 (SFJ3D) • 5617 BC Eindhoven • www.semaku.com
> >      >
> >
> >
> >
> >

Reply via email to