Pooled vs Unpooled ByteBufs for storage operations

We use BinaryDocument for a lot of what we do with couchbase. When it comes time to store a new document (or update an existing one), we need have a ByteBuf so we can use the static create methods on BinaryDocument. At this point in the code, we have a byte[] that represents the binary content to store. As of now, we are using Unpooled.copiedBuffer to get our ByteBuf to use. In looking at Netty the docs for Unpooled, I’m thinking that Unpooled.wrappedBuffer is going to be more efficient. In looking further, I’m starting to wonder if I should instead be using pooled ByteBufs for maximum efficiency. So my question is, is it worth the effort to switch over to pooled buffers (making sure to release them when done with the storage operation) if I’m trying to make my code as efficient and high throughput as possible, or is the performance benefit negligible?

Hi @cbax007,

great questions, as usual :wink:

The client internally uses pooled buffers where possible, with some exceptions that we’re planning to address in future releases. The main benefit of pooled buffers is reduced GC needed to reclaim those allocated objects, since they are part of the pool. But here comes the tricky thing: you need to be very careful to release them when not needed anymore, otherwise you risk leaking out of the pool. We’re still hunting down missed releases in the java sdk and the core package, sometimes its not obvious. If you go down that route, I recommend you to use this in all of your tests:

static {
    ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
}

We are using it as part of the integration tests and it has served us well so far.

Also note that if you pass content down the client, it is very subtle when it is going to be released. If you want to pool, I recommend you to wait for 2.1 because there we reduce copies and have much better release logic in place. With this version it should be possible to grab a buffer from the pool, pass it down as a content and it will be freed once written successfully.

Note that when it comes to a wrapped buffer, I’m not 100% sure. I think since your byte[] is allocated on the heap, netty needs to transform it into a direct byte buffer down anyways before writing it to NIO. I could be mistaken, but I think the wrapped buffer is really to give you a much nicer API to work on it rather to be more efficient.

Currently you can see we are also using unpooled: https://github.com/couchbase/couchbase-java-client/blob/master/src/main/java/com/couchbase/client/java/transcoder/JsonTranscoder.java#L53, but the response path is completely based on the pool. We are planning as part of 2.1 to move this also onto the pool for increased gc efficiency.

Btw, we’re releasing a 2.1-dp today (mostly for people to poke around with N1QL DP4 support), but you can start your experiments there too.

@daschl, thanks for the detailed and very informative response. I’m going to defer my work for converting to pooled until 2.1 is released. No sense in doing all of this work explicitly myself if you guys are going to be doing it for me in 2.1. The need is not pressing, so we can certainly wait. In the mean time, I will play around a bit with 2.1-dp vs 2.0.3 from a performance perspective (memory usage and gc activity specifically) to see if 2.1 is indeed going to be helpful to me.

Also, I will hold off converting from copiedBuffer to wrappedBuffer as it doesn’t seem like there is any real benefit there. Thanks again for the helpful response.

@cbax007 no worries, glad you’re exploring all the dark alleys :wink:

Btw, 2.1.0-dp is released: http://blog.couchbase.com/n1ql-dp4-java-sdk

Note that 2.1.0-dp does not include the encode pooling, it’s on the todo list… but if you use the BinaryDocument we can’t pool for you anyways since you pass the ByteBuf into the system. You should benefit from the pooling then if you use the regular document types where we do the conversion into ByteBuf for you.

@daschl, Okay. Good to know. I think I’ll wait until you guys have encode pooling for the other document types and then pattern what I end up doing around that as it will probably be similar. Thanks for the heads up.