Skip to content

Fix Memory release not correctly in Multipart Decoder - #11188

Merged
normanmaurer merged 1 commit into
netty:4.1from
fredericBregier:fixMemoryReleaseMultipartDecoder
Apr 29, 2021
Merged

normanmaurer merged 1 commit into
netty:4.1from
fredericBregier:fixMemoryReleaseMultipartDecoder

Conversation

@fredericBregier

@fredericBregier fredericBregier commented Apr 22, 2021 •

Copy link
Copy Markdown
Member

Motivation:
2 years ago a change remove the default clearing of all HttpData, whatever
they are disk based or memory based.

A lot of users were probably releasing HttpData directly, so there was no issue.
But now, it seems, and as the Javadoc said, that decoder.destroy() shall clean up
also Memory based HttpData, and not only Disk based HttpData as currently.

Change:
Add in destroy() method the necessary code to release if necessary
the underlying Memory based HttpDatas.

Change one Junit Test (using Mixed, Memory and Disk based factories)
in order to check the correctness of this behavior and to really act
as a handler (releasing buffers or requests).

Modify one Junit core to check validity when a delimiter is present in the Chunk
but not CRLF/LF (false delimiter), to ensure correctness.

Result:
No more issue on memory leak

Note that still the List and the Map are not cleaned, since they were not
before. No change is done on this, since it could produce backward issue compatibility.

Fix issues #11175 and #11184

@jameskleeh

Copy link
Copy Markdown
Contributor

This does not fix #11174

@fredericBregier

Copy link
Copy Markdown
Member Author

Have you tested it ? If so, then, I don't understand the issue correctly.
I mean what you report seems that data are not released correctly.

Does it concerned the "offered" chunks ? If so, then in Netty, the standard http codec handler does that for you. This is not the job of the MultipartDecoder.

So, I was believing that it was the allocated data from the MultipartDecoder. As it used the same Pool allocator than original chunk, they could be Direct Memory too. And therefore, giving your issue.
Can you explained more ?

@jameskleeh

Copy link
Copy Markdown
Contributor

@fredericBregier I'll post a reply in my issue

@fredericBregier

Copy link
Copy Markdown
Member Author

Notice: I think there is something wrong with this undecodedChunk when we end the offer part. It might have a refCnt > 1.
So I added a new commit for this. I know this shouldn't be, but that's what we already faced up in a previous cases and were hoping this simple fix with simple release was ok. Maybe not ? So I introduce this while loop.

@jameskleeh

Copy link
Copy Markdown
Contributor

@fredericBregier I'll try exercising the app with this change and report back

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from dfccd9e to 80088bf Compare April 22, 2021 20:46
@fredericBregier

Copy link
Copy Markdown
Member Author

@jameskleeh Sorry, wrong commit again (too late, I need to sleep). If you clone it, redo it please.

@jameskleeh

jameskleeh commented Apr 22, 2021 •

Copy link
Copy Markdown
Contributor

@fredericBregier This logic doesn't get executed in my example. The issue is the release in the destroy method. The first call to release() returns false and doesn't deallocate the memory. I verified calling it a second time resolves the issue.

Edit: That fixes the issue for 4.1.59, however it does not for 4.1.63

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from 80088bf to 1f9ec02 Compare April 23, 2021 08:02
@fredericBregier

Copy link
Copy Markdown
Member Author

@jameskleeh I guess I found the reason.
When the undecodedChunk is free up when max size is reached, it was badly released.
Moreover, when the first chunk is provided, instead of copying as for all other items, it is just retained. But as it is shared with the HttpRequest. So it will be released by HttpRequest releasing (or first HttpChunk release).
So I change to copy as other cases, and still check if undecodedChunk has to be released more than once (according to my test, it shall not, so we may remove the while loop for a unique release call).

Can you recheck ?

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from 1f9ec02 to 42e0a14 Compare April 23, 2021 08:34
@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer I agree, that's why I remove the second one in destroy.
The first one, in offer, is necessary according to the previous code (the if that preceeds says refCnt > 1, so needing more than one release).
However, note that according to my test (and using no more retained for the first Chunk), even the first one (in offer) could be of no use (never goes within the else part).

@normanmaurer

Copy link
Copy Markdown
Member

@fredericBregier not sure if there is just some miss-understanding. If there is any while(!...release()) calls needed there is another bug which you just "hide".

@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer
I fully agree. That's why I first removed the second one (destroy), but let at that time the first one (offer).
The first one, we discussed about it some months ago and you propose this solution, but when I look again at that point, as there is a if refCnt() ==1 , the else part is implicitely more than 1.
However, with the change made on allocation of undecodedChunk for first chunk, my guess is that this else case never occurs.
So I propose you to remove this while loop also.

2 possibilities:

  1. remove the "if/else" and stay with the content of "if" (no release), assuming the refCnt > 1 never occurs
  2. keep the "if/else" but with no while loop (only one release but strange since refCnt > 1)

WDYT?

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from 42e0a14 to e8e8610 Compare April 23, 2021 09:32
@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer I opt for the moment on the second solution (keeping your code), but I believe the else might be never used.
It is harmless...

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from e8e8610 to a101b9b Compare April 23, 2021 14:27
@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer I have completed the last commit with 2 things, in addition to remove all while loop

  • in constructor, if the decoder raised an exception, one of the implementation was not doing a call to destroy
  • fix Junit tests to act as within a handler where the http codec release the chunks or requests
    It seems it was the reason of the memory leak detected within the CI/CD and probably related to issues too.

@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer So the tests passed with no more Memory leak.

@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch 5 times, most recently from 695ae4a to b7b1476 Compare April 28, 2021 11:16
@normanmaurer

Copy link
Copy Markdown
Member

@fredericBregier can you please squash and adjust the commit message ?

Motivation:
2 years ago a change remove the default clearing of all HttpData, whatever
they are disk based or memory based.

A lot of users were probably releasing HttpData directly, so there was no issue.
But now, it seems, and as the Javadoc said, that `decoder.destroy()` shall clean up
also Memory based HttpData, and not only Disk based HttpData as currently.

Change:
Add in `destroy()` method the necessary code to release if necessary
the underlying Memory based HttpDatas.

Change one Junit Test (using Mixed, Memory and Disk based factories)
in order to check the correctness of this behavior and to really act
as a handler (releasing buffers or requests).

Modify one Junit core to check validity when a delimiter is present in the Chunk
but not CRLF/LF (false delimiter), to ensure correctness.

Result:
No more issue on memory leak

Note that still the List and the Map are not cleaned, since they were not
before. No change is done on this, since it could produce backward issue compatibility.

Fix issues netty#11175 and netty#11184
@fredericBregier
fredericBregier force-pushed the fixMemoryReleaseMultipartDecoder branch from b7b1476 to 6d8e346 Compare April 29, 2021 08:49
@fredericBregier

Copy link
Copy Markdown
Member Author

@normanmaurer Done, CI/CD rechecking now ;-)

@normanmaurer normanmaurer changed the title Fix Memory release not correctly in Multipart Decoder @fredericBregier Fix Memory release not correctly in Multipart Decoder Apr 29, 2021
@normanmaurer normanmaurer changed the title @fredericBregier Fix Memory release not correctly in Multipart Decoder Fix Memory release not correctly in Multipart Decoder Apr 29, 2021
@normanmaurer
normanmaurer merged commit e733049 into netty:4.1 Apr 29, 2021
@normanmaurer

Copy link
Copy Markdown
Member

@fredericBregier thanks a lot!

@normanmaurer normanmaurer added this to the 4.1.64.Final milestone Apr 29, 2021
normanmaurer pushed a commit that referenced this pull request Apr 29, 2021
Motivation:
2 years ago a change remove the default clearing of all HttpData, whatever
they are disk based or memory based.

A lot of users were probably releasing HttpData directly, so there was no issue.
But now, it seems, and as the Javadoc said, that `decoder.destroy()` shall clean up
also Memory based HttpData, and not only Disk based HttpData as currently.

Change:
- Add in `destroy()` method the necessary code to release if necessary
the underlying Memory based HttpDatas.

- Change one Junit Test (using Mixed, Memory and Disk based factories)
in order to check the correctness of this behavior and to really act
as a handler (releasing buffers or requests).

- Modify one Junit core to check validity when a delimiter is present in the Chunk
but not CRLF/LF (false delimiter), to ensure correctness.

Result:
No more issue on memory leak

Note that still the List and the Map are not cleaned, since they were not
before. No change is done on this, since it could produce backward issue compatibility.

Fix issues #11175 and #11184
raidyue pushed a commit to raidyue/netty that referenced this pull request Jul 8, 2022
Motivation:
2 years ago a change remove the default clearing of all HttpData, whatever
they are disk based or memory based.

A lot of users were probably releasing HttpData directly, so there was no issue.
But now, it seems, and as the Javadoc said, that `decoder.destroy()` shall clean up
also Memory based HttpData, and not only Disk based HttpData as currently.

Change:
- Add in `destroy()` method the necessary code to release if necessary
the underlying Memory based HttpDatas.

- Change one Junit Test (using Mixed, Memory and Disk based factories)
in order to check the correctness of this behavior and to really act
as a handler (releasing buffers or requests).

- Modify one Junit core to check validity when a delimiter is present in the Chunk
but not CRLF/LF (false delimiter), to ensure correctness.

Result:
No more issue on memory leak

Note that still the List and the Map are not cleaned, since they were not
before. No change is done on this, since it could produce backward issue compatibility.

Fix issues netty#11175 and netty#11184
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants