Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid buf copy and fix buf leak #101

Merged
merged 1 commit into from
Mar 11, 2019
Merged

Conversation

jilen
Copy link
Contributor

@jilen jilen commented Mar 8, 2019

No description provided.

@codecov
Copy link

codecov bot commented Mar 8, 2019

Codecov Report

Merging #101 into master will increase coverage by 0.03%.
The diff coverage is 85.71%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #101      +/-   ##
============================================
+ Coverage      78.9%   78.94%   +0.03%     
  Complexity     1011     1011              
============================================
  Files           258      258              
  Lines          3655     3661       +6     
  Branches        487      487              
============================================
+ Hits           2884     2890       +6     
  Misses          531      531              
  Partials        240      240
Impacted Files Coverage Δ Complexity Δ
...jasync/sql/db/mysql/decoder/ResultSetRowDecoder.kt 100% <100%> (ø) 4 <0> (ø) ⬇️
...hub/jasync/sql/db/mysql/codec/MySQLFrameDecoder.kt 91.4% <100%> (ø) 37 <0> (ø) ⬇️
.../jasync/sql/db/postgresql/parsers/DataRowParser.kt 100% <100%> (ø) 4 <0> (ø) ⬇️
...async/sql/db/mysql/codec/MySQLConnectionHandler.kt 78.37% <81.81%> (+0.72%) 57 <0> (ø) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ecf5edd...35972d9. Read the comment docs.

@@ -164,7 +164,9 @@ class MySQLConnectionHandler(
}
ServerMessage.BinaryRow -> {
val m = message as BinaryRowMessage
this.currentQuery!!.addRow(this.binaryRowDecoder.decode(m.buffer, this.currentColumns))
val decodedRow = this.binaryRowDecoder.decode(m.buffer, this.currentColumns)
m.buffer.release()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my understanding is you don't need to release the buffer from BytesToMessageDecoder

@@ -222,7 +222,8 @@ class MySQLFrameDecoder(val charset: Charset, private val connectionId: String)

return if (this.totalColumns == this.processedColumns) {
if (this.isPreparedStatementExecute) {
val row = slice.readBytes(slice.readableBytes())
val row = slice.retainedSlice()
slice.readerIndex(slice.readerIndex() + slice.readableBytes())
Copy link
Contributor

@andy-yx-chen andy-yx-chen Mar 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, according to https://netty.io/4.1/api/io/netty/handler/codec/ByteToMessageDecoder.html

Some methods such as ByteBuf.readBytes(int) will cause a memory leak if the returned buffer is not released or added to the out List. Use derived buffers like ByteBuf.readSlice(int) to avoid leaking memory.

we should have this fix, but why not just readSlice? then you don't need to release it in the file above

Copy link
Contributor Author

@jilen jilen Mar 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not retained, the buf will be released while current handler finished, and cannot be accessed in another handler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andy-yx-chen Replaced with readRetainedSlice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jilen you are right, please help to check if there are other places that have the same issue here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oshai , we need you to take a look at this one, good finding actually

@oshai
Copy link
Contributor

oshai commented Mar 8, 2019

I will review it as soon as possible. I just wonder why this leak wasn't noticed until now. Will also compare it to mauricio.

@andy-yx-chen
Copy link
Contributor

I will review it as soon as possible. I just wonder why this leak wasn't noticed until now. Will also compare it to mauricio.

thanks, I think the fix is valid, I also read the source code for netty, the readBytes will return a buffer from MessageDecoder's ctx.allocator, which is pooled, and need to be explicitly release

@oshai
Copy link
Contributor

oshai commented Mar 9, 2019

@jilen Thanks for raising the issue!
I want to be sure that I understand the change: readBytes is not efficient because it is copying the buffer (BTW I found 3 places like that). Instead, you would like to use readRetainedSlice which is a view on the same buffer with it's own ref count thus more efficient.

In addition, the copied buffer was probably leaking. I tried using -Dio.netty.leakDetection.level=paranoid but there wasn't any message. On the other hand checking m.buffer.refCnt() after a while was == 1. If you can bring some more details or ideas how to make sure we can obeserve the leak it will be great.

What I did find is the following:

Would be glad to get more input before merging it.

@jilen
Copy link
Contributor Author

jilen commented Mar 11, 2019

@oshai Yes, those bufs could be replaced with readRetainedSlice, and be released later. And netty's leak detection relies on gc. So it might not always be reliable. For me, sometimes it will report leak, while other times it will not.

@jilen jilen force-pushed the fix_buf_leak branch 2 times, most recently from 8c1a8f2 to cefde94 Compare March 11, 2019 02:22
@andy-yx-chen
Copy link
Contributor

again, whether detector reports the leak or not, the fact is readBytes in MessageDecoder will cause leak, as it will allocate a buffer in handler context.alloc, which needs to be release explicitly as the document says

@oshai oshai merged commit fff9c64 into jasync-sql:master Mar 11, 2019
@oshai
Copy link
Contributor

oshai commented Mar 11, 2019

@jilen Thanks again for the fix!

@jilen jilen deleted the fix_buf_leak branch March 11, 2019 09:07
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