users@grizzly.java.net

Re: Upload a large file without oom with Grizzly

From: Ryan Lubke <ryan.lubke_at_oracle.com>
Date: Tue, 03 Sep 2013 10:15:05 -0700

Good catch. Fixed.

Sébastien Lorber wrote:
>
>
> Hello,
>
>
> There's a little mistake in the grizzly ahc provider relative to the
> write queue size.
>
>
> https://github.com/AsyncHttpClient/async-http-client/blob/b5d97efe9fe14113ea92fb1f7db192a2d090fad7/src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java#L419
>
> As you can see, the TransportCustomizer is called, and then the write
> queue size (among other things) is set to AUTO_SIZE (instead of
> previously UNLIMITED)
>
> clientTransport.getAsyncQueueIO().getWriter()
>
> .setMaxPendingBytesPerConnection(AsyncQueueWriter.AUTO_SIZE);
>
>
>
> I think the default settings like this AUTO_SIZE attribute should be
> set before the customization of the transport, or they would override
> the value we customized.
>
>
> This is actually my case, since I can't reproduce my "bug" which is
> "high memory consumption", even when using -1 / UNLIMITED in the
> TransportCustomizer.
>
>
> This could work fine for me with AUTO_SIZE, but I'd rather be able to
> tune this parameter during load tests to see the effect.
>
>
>
>
>
>
>
>
>
> 2013/8/31 Sebastien Lorber <lorber.sebastien_at_gmail.com
> <mailto:lorber.sebastien_at_gmail.com>>
>
> Thanks i will ckeck that on monday. I can now upload a 500m file
> with 40m heap size ;)
>
> Envoyé de mon iPhone
>
> Le 30 août 2013 à 20:49, Ryan Lubke <ryan.lubke_at_oracle.com
> <mailto:ryan.lubke_at_oracle.com>> a écrit :
>
>> I'm going to be updating the Grizzly provider such that AUTO_SIZE
>> (not AUTO_TUNE) is the default, so you can avoid the use of the
>> TransportCustomizer.
>>
>> Ryan Lubke wrote:
>>> Regarding your tuning question, I would probably set the value
>>> to AsyncQueueWriter.AUTO_TUNE (this will be four times the
>>> socket write buffer) and see how that works.
>>>
>>> Ryan Lubke wrote:
>>>> A question first. With these changes, your memory usage is
>>>> more inline with what you were looking for?
>>>>
>>>> Sébastien Lorber wrote:
>>>>> By the way, do you have any idea when the 1.7.20 will be
>>>>> released (with these new improvements?)
>>>>>
>>>>> We would like to know if we wait for a release or if we
>>>>> install our own temp release on Nexus :)
>>>>>
>>>>>
>>>>> 2013/8/30 Sébastien Lorber <lorber.sebastien_at_gmail.com
>>>>> <mailto:lorber.sebastien_at_gmail.com>>
>>>>>
>>>>> Thank you, it works fine!
>>>>>
>>>>>
>>>>> I just had to modify a single line after your commit.
>>>>>
>>>>> com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider#initializeTransport
>>>>> ->
>>>>> clientTransport.getAsyncQueueIO().getWriter().setMaxPendingBytesPerConnection(10000);
>>>>>
>>>>>
>>>>> If I let the initial value (-1) it won't block, canWrite
>>>>> always returns true
>>>>>
>>>>>
>>>>> Btw, on AHC I didn't find any way to pass this value as a
>>>>> config attribute, neither the size of the write buffer you
>>>>> talked about.
>>>>>
>>>>> So in the end, is there a way with current AHC code to use
>>>>> this "canWrite = false" behavior?
>>>>> If not, can you please provide a way to set this behavior
>>>>> on v1.7.20 ? thanks
>>>>>
>>>>>
>>>>> PS: does it make sens to use the same number of bytes un
>>>>> the feed(Buffer) method and in the
>>>>> setMaxPendingBytesPerConnection(10000); ? do you have any
>>>>> tuning recommandation?
>>>>>
>>>>>
>>>>>
>>>>> 2013/8/29 Ryan Lubke <ryan.lubke_at_oracle.com
>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>
>>>>> Please disregard.
>>>>>
>>>>>
>>>>> Ryan Lubke wrote:
>>>>>> Sébastien,
>>>>>>
>>>>>> Could you also provide a sample of how you're
>>>>>> performing your feed?
>>>>>>
>>>>>> Thanks,
>>>>>> -rl
>>>>>>
>>>>>> Ryan Lubke wrote:
>>>>>>> Sébastien,
>>>>>>>
>>>>>>> I'd recommend looking at Connection.canWrite() [1]
>>>>>>> and Connection.notifyCanWrite(WriteListener) [1]
>>>>>>>
>>>>>>> By default, Grizzly will configure the async write
>>>>>>> queue length to be four times the write buffer size
>>>>>>> (which is based off the socket write buffer).
>>>>>>> When this queue exceeds this value, canWrite() will
>>>>>>> return false.
>>>>>>>
>>>>>>> When this occurs, you can register a WriteListener
>>>>>>> to be notified when the queue length is below the
>>>>>>> configured max and then simulate blocking
>>>>>>> until the onWritePossible() callback has been invoked.
>>>>>>>
>>>>>>> ----------------------------------------------------------------
>>>>>>>
>>>>>>> final FutureImpl<Boolean> future =
>>>>>>> Futures.createSafeFuture();
>>>>>>>
>>>>>>> // Connection may be obtained by calling
>>>>>>> FilterChainContext.getConnection().
>>>>>>> connection.notifyCanWrite(new WriteHandler() {
>>>>>>>
>>>>>>> @Override
>>>>>>> public void onWritePossible() throws
>>>>>>> Exception {
>>>>>>> future.result(Boolean.TRUE);
>>>>>>> }
>>>>>>>
>>>>>>> @Override
>>>>>>> public void onError(Throwable t) {
>>>>>>>
>>>>>>> future.failure(Exceptions.makeIOException(t));
>>>>>>> }
>>>>>>> });
>>>>>>>
>>>>>>> try {
>>>>>>> final long writeTimeout = 30;
>>>>>>> future.get(writeTimeout,
>>>>>>> TimeUnit.MILLISECONDS);
>>>>>>> } catch (ExecutionException e) {
>>>>>>> HttpTransactionContext httpCtx =
>>>>>>> HttpTransactionContext.get(connection);
>>>>>>> httpCtx.abort(e.getCause());
>>>>>>> } catch (Exception e) {
>>>>>>> HttpTransactionContext httpCtx =
>>>>>>> HttpTransactionContext.get(connection);
>>>>>>> httpCtx.abort(e);
>>>>>>> }
>>>>>>>
>>>>>>> ---------------------------------------------------------------------
>>>>>>>
>>>>>>> [1]
>>>>>>> http://grizzly.java.net/docs/2.3/apidocs/org/glassfish/grizzly/OutputSink.html.
>>>>>>>
>>>>>>> Sébastien Lorber wrote:
>>>>>>>> Ryan, I've did some other tests.
>>>>>>>>
>>>>>>>>
>>>>>>>> It seems that using a blocking queue in the
>>>>>>>> FeedableBodyGenerator is totally useless because
>>>>>>>> the thread consuming it is not blocking and the
>>>>>>>> queue never blocks the feeding, which was my
>>>>>>>> intention in the beginning. Maybe it depends on the
>>>>>>>> IO strategy used?
>>>>>>>> I use AHC default which seems to
>>>>>>>> use SameThreadIOStrategy so I don't think it's
>>>>>>>> related to the IO strategy.
>>>>>>>>
>>>>>>>>
>>>>>>>> So in the end I can upload a 70m file with a heap
>>>>>>>> of 50m, but I have to put a Thread.sleep(30)
>>>>>>>> between each 100k Buffer send to the
>>>>>>>> FeedableBodyGenerator
>>>>>>>>
>>>>>>>> The connection with the server is not good here,
>>>>>>>> but in production it is normally a lot better as
>>>>>>>> far as I know.
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> I've tried things
>>>>>>>> like clientTransport.getAsyncQueueIO().getWriter().setMaxPendingBytesPerConnection(100000);
>>>>>>>> but it doesn't seem to work for me.
>>>>>>>>
>>>>>>>> I'd like the Grizzly internals to block when there
>>>>>>>> are too much pending bytes to send. Is it possible?
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> PS: I've just been able to send a 500mo file with
>>>>>>>> 100mo heap, but it needed a sleep of 100ms between
>>>>>>>> each 100k Buffer sent to the bodygenerator
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> 2013/8/29 Sébastien Lorber
>>>>>>>> <lorber.sebastien_at_gmail.com
>>>>>>>> <mailto:lorber.sebastien_at_gmail.com>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> By chance do you if I can remove the
>>>>>>>> MessageCloner used in the SSL filter?
>>>>>>>> SSLBaseFilter$OnWriteCopyCloner
>>>>>>>>
>>>>>>>> It seems to allocate a lot of memory.
>>>>>>>> I don't really understand why messages have to
>>>>>>>> be cloned, can I remove this? How?
>>>>>>>>
>>>>>>>>
>>>>>>>> 2013/8/29 Sébastien Lorber
>>>>>>>> <lorber.sebastien_at_gmail.com
>>>>>>>> <mailto:lorber.sebastien_at_gmail.com>>
>>>>>>>>
>>>>>>>>
>>>>>>>> I'm trying to send a 500m file for my tests
>>>>>>>> with a heap of 400m.
>>>>>>>>
>>>>>>>> In our real use cases we would probably
>>>>>>>> have files under 20mo but we want to reduce
>>>>>>>> the memory consumption because we can have
>>>>>>>> x parallel uploads on the same server
>>>>>>>> according to the user activity.
>>>>>>>>
>>>>>>>> I'll try to check if using this
>>>>>>>> BodyGenerator reduced the memory footprint
>>>>>>>> or if it's almost like before.
>>>>>>>>
>>>>>>>>
>>>>>>>> 2013/8/28 Ryan Lubke <ryan.lubke_at_oracle.com
>>>>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>>>>
>>>>>>>> At this point in time, as far as the
>>>>>>>> SSL buffer allocation is concerned,
>>>>>>>> it's untunable.
>>>>>>>>
>>>>>>>> That said, feel free to open a feature
>>>>>>>> request.
>>>>>>>>
>>>>>>>> As to your second question, there is no
>>>>>>>> suggested size. This is all very
>>>>>>>> application specific.
>>>>>>>>
>>>>>>>> I'm curious, how large of a file are
>>>>>>>> you sending?
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Sébastien Lorber wrote:
>>>>>>>>> I have seen a lot of buffers which
>>>>>>>>> have a size of 33842 and it seems the
>>>>>>>>> limit is near half the capacity.
>>>>>>>>>
>>>>>>>>> Perhaps there's a way to tune that
>>>>>>>>> buffer size so that it consumes less
>>>>>>>>> memory?
>>>>>>>>> Is there an ideal Buffer size to send
>>>>>>>>> to the feed method?
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> 2013/8/28 Ryan Lubke
>>>>>>>>> <ryan.lubke_at_oracle.com
>>>>>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>>>>>
>>>>>>>>> I'll be reviewing the PR today,
>>>>>>>>> thanks again!
>>>>>>>>>
>>>>>>>>> Regarding the OOM: as it stands
>>>>>>>>> now, for each new buffer that is
>>>>>>>>> passed to the SSLFilter, we
>>>>>>>>> allocate a buffer twice the size
>>>>>>>>> in order to
>>>>>>>>> accommodate the encrypted result.
>>>>>>>>> So there's an increase.
>>>>>>>>>
>>>>>>>>> Depending on the socket
>>>>>>>>> configurations of both endpoints,
>>>>>>>>> and how fast the remote is reading
>>>>>>>>> data, it could
>>>>>>>>> be the write queue is becoming too
>>>>>>>>> large. We do have a way to
>>>>>>>>> detect this situation, but I'm
>>>>>>>>> pretty sure
>>>>>>>>> the Grizzly internals are
>>>>>>>>> currently shielded here. I will
>>>>>>>>> see what I can do to allow users
>>>>>>>>> to leverage this.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Sébastien Lorber wrote:
>>>>>>>>>> Hello,
>>>>>>>>>>
>>>>>>>>>> I've made my pull request.
>>>>>>>>>> https://github.com/AsyncHttpClient/async-http-client/pull/367
>>>>>>>>>>
>>>>>>>>>> With my usecase it works, the
>>>>>>>>>> file is uploaded like before.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> But I didn't notice a big memory
>>>>>>>>>> improvement.
>>>>>>>>>>
>>>>>>>>>> Is it possible that SSL doesn't
>>>>>>>>>> allow to stream the body or
>>>>>>>>>> something like that?
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> In memory, I have a lot of:
>>>>>>>>>> - HeapByteBuffer
>>>>>>>>>> Which are hold by SSLUtils$3
>>>>>>>>>> Which are hold by BufferBuffers
>>>>>>>>>> Which are hold by WriteResult
>>>>>>>>>> Which are hold by
>>>>>>>>>> AsyncWriteQueueRecord
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Here is an exemple of the OOM
>>>>>>>>>> stacktrace:
>>>>>>>>>>
>>>>>>>>>> java.lang.OutOfMemoryError: Java
>>>>>>>>>> heap space
>>>>>>>>>> at
>>>>>>>>>> java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:57)
>>>>>>>>>> at
>>>>>>>>>> java.nio.ByteBuffer.allocate(ByteBuffer.java:331)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLUtils.allocateOutputBuffer(SSLUtils.java:342)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLBaseFilter$2.grow(SSLBaseFilter.java:117)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLConnectionContext.ensureBufferSize(SSLConnectionContext.java:392)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLConnectionContext.wrap(SSLConnectionContext.java:272)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLConnectionContext.wrapAll(SSLConnectionContext.java:227)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLBaseFilter.wrapAll(SSLBaseFilter.java:404)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLBaseFilter.handleWrite(SSLBaseFilter.java:319)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLFilter.accurateWrite(SSLFilter.java:255)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ssl.SSLFilter.handleWrite(SSLFilter.java:143)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider$SwitchingSSLFilter.handleWrite(GrizzlyAsyncHttpProvider.java:2503)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.ExecutorResolver$8.execute(ExecutorResolver.java:111)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.FilterChainContext.write(FilterChainContext.java:853)
>>>>>>>>>> at
>>>>>>>>>> org.glassfish.grizzly.filterchain.FilterChainContext.write(FilterChainContext.java:720)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.client.providers.grizzly.FeedableBodyGenerator.flushQueue(FeedableBodyGenerator.java:132)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.client.providers.grizzly.FeedableBodyGenerator.feed(FeedableBodyGenerator.java:101)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.client.providers.grizzly.MultipartBodyGeneratorFeeder$FeedBodyGeneratorOutputStream.write(MultipartBodyGeneratorFeeder.java:222)
>>>>>>>>>> at
>>>>>>>>>> java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
>>>>>>>>>> at
>>>>>>>>>> java.io.BufferedOutputStream.write(BufferedOutputStream.java:126)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.multipart.FilePart.sendData(FilePart.java:179)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.multipart.Part.send(Part.java:331)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.multipart.Part.sendParts(Part.java:397)
>>>>>>>>>> at
>>>>>>>>>> com.ning.http.client.providers.grizzly.MultipartBodyGeneratorFeeder.feed(MultipartBodyGeneratorFeeder.java:144)
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Any idea?
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> 2013/8/27 Ryan Lubke
>>>>>>>>>> <ryan.lubke_at_oracle.com
>>>>>>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>>>>>>
>>>>>>>>>> Excellent! Looking forward
>>>>>>>>>> to the pull request!
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Sébastien Lorber wrote:
>>>>>>>>>>> Ryan thanks, it works fine,
>>>>>>>>>>> I'll make a pull request on
>>>>>>>>>>> AHC tomorrow with a better
>>>>>>>>>>> code using the same Part
>>>>>>>>>>> classes that already exist.
>>>>>>>>>>>
>>>>>>>>>>> I created an OutputStream
>>>>>>>>>>> that redirects to the
>>>>>>>>>>> BodyGenerator feeder.
>>>>>>>>>>>
>>>>>>>>>>> The problem I currently have
>>>>>>>>>>> is that the feeder feeds the
>>>>>>>>>>> queue faster than the async
>>>>>>>>>>> thread polling it :)
>>>>>>>>>>> I need to expose a limit to
>>>>>>>>>>> that queue size or
>>>>>>>>>>> something, will work on
>>>>>>>>>>> that, it will be better than
>>>>>>>>>>> a thread sleep to slow down
>>>>>>>>>>> the filepart reading
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> 2013/8/27 Ryan Lubke
>>>>>>>>>>> <ryan.lubke_at_oracle.com
>>>>>>>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>>>>>>>
>>>>>>>>>>> Yes, something like
>>>>>>>>>>> that. I was going to
>>>>>>>>>>> tackle adding something
>>>>>>>>>>> like this today. I'll
>>>>>>>>>>> follow up with something
>>>>>>>>>>> you can test out.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Sébastien Lorber wrote:
>>>>>>>>>>>> Ok thanks!
>>>>>>>>>>>>
>>>>>>>>>>>> I think I see what I
>>>>>>>>>>>> could do, probably
>>>>>>>>>>>> something like that:
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> FeedableBodyGenerator
>>>>>>>>>>>> bodyGenerator = new
>>>>>>>>>>>> FeedableBodyGenerator();
>>>>>>>>>>>>
>>>>>>>>>>>> MultipartBodyGeneratorFeeder
>>>>>>>>>>>> bodyGeneratorFeeder =
>>>>>>>>>>>> new
>>>>>>>>>>>> MultipartBodyGeneratorFeeder(bodyGenerator);
>>>>>>>>>>>> Request
>>>>>>>>>>>> uploadRequest1 = new
>>>>>>>>>>>> RequestBuilder("POST")
>>>>>>>>>>>> .setUrl("url")
>>>>>>>>>>>>
>>>>>>>>>>>> .setBody(bodyGenerator)
>>>>>>>>>>>> .build();
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> ListenableFuture<Response>
>>>>>>>>>>>> asyncRes = asyncHttpClient
>>>>>>>>>>>>
>>>>>>>>>>>> .prepareRequest(uploadRequest1)
>>>>>>>>>>>> .execute(new
>>>>>>>>>>>> AsyncCompletionHandlerBase());
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> bodyGeneratorFeeder.append("param1","value1");
>>>>>>>>>>>>
>>>>>>>>>>>> bodyGeneratorFeeder.append("param2","value2");
>>>>>>>>>>>>
>>>>>>>>>>>> bodyGeneratorFeeder.append("fileToUpload",fileInputStream);
>>>>>>>>>>>>
>>>>>>>>>>>> bodyGeneratorFeeder.end();
>>>>>>>>>>>>
>>>>>>>>>>>> Response
>>>>>>>>>>>> uploadResponse =
>>>>>>>>>>>> asyncRes.get();
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Does it seem ok to you?
>>>>>>>>>>>>
>>>>>>>>>>>> I guess it could be
>>>>>>>>>>>> interesting to provide
>>>>>>>>>>>> that
>>>>>>>>>>>> MultipartBodyGeneratorFeeder
>>>>>>>>>>>> class to AHC or Grizzly
>>>>>>>>>>>> since some other people
>>>>>>>>>>>> may want to achieve the
>>>>>>>>>>>> same thing
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> 2013/8/26 Ryan Lubke
>>>>>>>>>>>> <ryan.lubke_at_oracle.com
>>>>>>>>>>>> <mailto:ryan.lubke_at_oracle.com>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Sébastien Lorber wrote:
>>>>>>>>>>>>
>>>>>>>>>>>> Hello,
>>>>>>>>>>>>
>>>>>>>>>>>> I would like to
>>>>>>>>>>>> know if it's
>>>>>>>>>>>> possible to
>>>>>>>>>>>> upload a file
>>>>>>>>>>>> with AHC /
>>>>>>>>>>>> Grizzly in
>>>>>>>>>>>> streaming, I
>>>>>>>>>>>> mean without
>>>>>>>>>>>> loading the
>>>>>>>>>>>> whole file
>>>>>>>>>>>> bytes in memory.
>>>>>>>>>>>>
>>>>>>>>>>>> The default
>>>>>>>>>>>> behavior seems
>>>>>>>>>>>> to allocate a
>>>>>>>>>>>> byte[] which
>>>>>>>>>>>> contans the
>>>>>>>>>>>> whole file, so
>>>>>>>>>>>> it means that
>>>>>>>>>>>> my server can
>>>>>>>>>>>> be OOM if too
>>>>>>>>>>>> many users
>>>>>>>>>>>> upload a large
>>>>>>>>>>>> file in the
>>>>>>>>>>>> same time.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> I've tryied
>>>>>>>>>>>> with a Heap and
>>>>>>>>>>>> ByteBuffer
>>>>>>>>>>>> memory
>>>>>>>>>>>> managers, with
>>>>>>>>>>>> reallocate=true/false
>>>>>>>>>>>> but no more
>>>>>>>>>>>> success.
>>>>>>>>>>>>
>>>>>>>>>>>> It seems the
>>>>>>>>>>>> whole file
>>>>>>>>>>>> content is
>>>>>>>>>>>> appended wto
>>>>>>>>>>>> the
>>>>>>>>>>>> BufferOutputStream,
>>>>>>>>>>>> and then the
>>>>>>>>>>>> underlying
>>>>>>>>>>>> buffer is written.
>>>>>>>>>>>>
>>>>>>>>>>>> At least this
>>>>>>>>>>>> seems to be the
>>>>>>>>>>>> case with AHC
>>>>>>>>>>>> integration:
>>>>>>>>>>>> https://github.com/AsyncHttpClient/async-http-client/blob/6faf1f316e5546110b0779a5a42fd9d03ba6bc15/providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/bodyhandler/PartsBodyHandler.java
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> So, is there a
>>>>>>>>>>>> way to patch
>>>>>>>>>>>> AHC to stream
>>>>>>>>>>>> the file so
>>>>>>>>>>>> that I could
>>>>>>>>>>>> eventually
>>>>>>>>>>>> consume only
>>>>>>>>>>>> 20mo of heap
>>>>>>>>>>>> while uploading
>>>>>>>>>>>> a 500mo file?
>>>>>>>>>>>> Or is this
>>>>>>>>>>>> simply
>>>>>>>>>>>> impossible with
>>>>>>>>>>>> Grizzly?
>>>>>>>>>>>> I didn't notice
>>>>>>>>>>>> anything
>>>>>>>>>>>> related to that
>>>>>>>>>>>> in the
>>>>>>>>>>>> documentation.
>>>>>>>>>>>>
>>>>>>>>>>>> It's possible with
>>>>>>>>>>>> the
>>>>>>>>>>>> FeedableBodyGenerator.
>>>>>>>>>>>> But if you're tied
>>>>>>>>>>>> to using Multipart
>>>>>>>>>>>> uploads, you'd
>>>>>>>>>>>> have to convert the
>>>>>>>>>>>> multipart data to
>>>>>>>>>>>> Buffers manually
>>>>>>>>>>>> and send using the
>>>>>>>>>>>> FeedableBodyGenerator.
>>>>>>>>>>>> I'll take a closer
>>>>>>>>>>>> look to see if this
>>>>>>>>>>>> area can be improved.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Btw in my case
>>>>>>>>>>>> it is a file
>>>>>>>>>>>> upload. I
>>>>>>>>>>>> receive a file
>>>>>>>>>>>> with CXF and
>>>>>>>>>>>> have to
>>>>>>>>>>>> transmit it to
>>>>>>>>>>>> a storage
>>>>>>>>>>>> server (like
>>>>>>>>>>>> S3). CXF
>>>>>>>>>>>> doesn't consume
>>>>>>>>>>>> memory bevause
>>>>>>>>>>>> it is streaming
>>>>>>>>>>>> the large fle
>>>>>>>>>>>> uploads to the
>>>>>>>>>>>> file system,
>>>>>>>>>>>> and then
>>>>>>>>>>>> provides an
>>>>>>>>>>>> input stream on
>>>>>>>>>>>> that file.
>>>>>>>>>>>>
>>>>>>>>>>>> Thanks
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>
>>>>>
>