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

Pluggable resource leak detector #5392

Closed
wants to merge 1 commit into from
Closed

Pluggable resource leak detector #5392

wants to merge 1 commit into from

Conversation

artgon
Copy link
Contributor

@artgon artgon commented Jun 14, 2016

Allow users of Netty to plug in their own leak detector for the purpose of instrumentation.

Motivation:

We are rolling out a large Netty deployment and want to be able to track the amount of leaks we're seeing in production via custom instrumentation. In order to achieve this today, I had to plug in a custom ByteBufAllocator into the bootstrap and have it initialize a custom ResourceLeakDetector. Due to these classes mostly being marked final or having private or static methods, a lot of the code had to be copy-pasted and it's quite ugly.

Modifications:

  • I've added a static loader method for the ResourceLeakDetector in AbstractByteBuf that tries to instantiate the class passed in via the -Dio.netty.customResourceLeakDetector, otherwise falling back to the default one.
  • I've modified ResourceLeakDetector to be non-final and to have the reporting broken out in to methods that can be overridden.

Result:

You can instrument leaks in your application by just adding something like the following:

public class InstrumentedResourceLeakDetector<T> extends ResourceLeakDetector<T> {

    @Monitor("InstanceLeakCounter")
    private final AtomicInteger instancesLeakCounter;

    @Monitor("LeakCounter")
    private final AtomicInteger leakCounter;

    public InstrumentedResourceLeakDetector(Class<T> resource) {
        super(resource);
        this.instancesLeakCounter = new AtomicInteger();
        this.leakCounter = new AtomicInteger();
    }

    @Override
    protected void reportTracedLeak(String records) {
        super.reportTracedLeak(records);
        leakCounter.incrementAndGet();
    }

    @Override
    protected void reportUntracedLeak() {
        super.reportUntracedLeak();
        leakCounter.incrementAndGet();
    }

    @Override
    protected void reportInstancesLeak() {
        super.reportInstancesLeak();
        instancesLeakCounter.incrementAndGet();
    }
}

static final ResourceLeakDetector<ByteBuf> leakDetector = loadResourceLeakDetector();

private static ResourceLeakDetector<ByteBuf> loadResourceLeakDetector() {
String customLeakDetector = SystemPropertyUtil.get("io.netty.customResourceLeakDetector");
Copy link
Member

Choose a reason for hiding this comment

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

Please use PrivilegedAction to try loading this.

@normanmaurer
Copy link
Member

@artgon This looks awesome! Just a few comments.

Please also sign the ICLA:

http://netty.io/s/icla

And adjust the commit message to match our template:

http://netty.io/wiki/writing-a-commit-message.html

@normanmaurer
Copy link
Member

normanmaurer commented Jun 14, 2016

@artgon Also I wonder if it would be good to add something like:

public abstract class ResourceLeakDetectorFactory {
    private static volatile ResourceLeakDetectorFactory INSTANCE = new ResourceLeakDetectorFactory() {....};

    protected ResourceLeakDetectorFactory() { .... }

    public static ResourceLeakDetectorFactory instance() {
        return INSTANCE;
    }

    public static void setResourceLeakDetectorFactory(ResourceLeakDetectorFactory factory) {
        INSTANCE = factory;
    }

    public ResourceLeakDetector(Class<?> clazz) {
        ....
    }
}

Then add all the static loading stuff in this implementation and also provide a setter to do it program and use the factory to obtain the instances. This way the code of loading is in one place and will work even if somewhere else a ResourceLeakDetector is needed.

@artgon
Copy link
Contributor Author

artgon commented Jun 14, 2016

Great, thanks for the feedback. I've signed the agreement and I'll make the changes you suggested. Looks like I followed that commit message template on the comment instead of the commit. Do you want me to just copy it over?

@normanmaurer
Copy link
Member

Yes please :)

Am 14.06.2016 um 08:15 schrieb Arthur Gonigberg notifications@github.com:

Great, thanks for the feedback. I've signed the agreement and I'll make the changes you suggested. Looks like I followed that commit message template on the comment instead of the commit. Do you want me to just copy it over?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

@artgon
Copy link
Contributor Author

artgon commented Jun 14, 2016

@normanmaurer Yeah, I think that's a good idea. Let me go back to the drawing board and elaborate on this a bit.

@normanmaurer
Copy link
Member

Cool :) Let me know if you have questions

Am 14.06.2016 um 08:22 schrieb Arthur Gonigberg notifications@github.com:

@normanmaurer Yeah, I think that's a good idea. Let me go back to the drawing board and elaborate on this a bit.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@normanmaurer
Copy link
Member

Just ping me when you are ready for another review!

logger.error("Class {} does not inherit from ResourceLeakDetector.", customLeakDetector);
}
}
} catch (Exception e) {
Copy link
Member

Choose a reason for hiding this comment

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

catch a Throwable instead of Exception

Copy link
Member

Choose a reason for hiding this comment

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

+1

@artgon artgon force-pushed the 4.1 branch 2 times, most recently from ab9cbe0 to 33a1c3d Compare June 15, 2016 07:19
@artgon
Copy link
Contributor Author

artgon commented Jun 15, 2016

@normanmaurer I've taken another run at it, let me know what you think.

@normanmaurer
Copy link
Member

Let me check

Am 15.06.2016 um 09:22 schrieb Arthur Gonigberg notifications@github.com:

@normanmaurer I've taken another run at it, let me know what you think.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

INSTANCE = factory;
}

public abstract <T> ResourceLeakDetector<T> loadResourceLeakDetector(final Class<T> resource);
Copy link
Member

Choose a reason for hiding this comment

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

Please rename to newResourceLeakDetector to be more inline with the rest of netty.

Copy link
Member

Choose a reason for hiding this comment

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

also javadocs please :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yes of course. Will do :)

What about the general approach? Is that what you had in mind with the factory?

Copy link
Member

Choose a reason for hiding this comment

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

Yep

*
* @param records - the trace of the leak
*/
protected void reportTracedLeak(String records, String resourceType) {
Copy link
Member

Choose a reason for hiding this comment

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

just a nit but could you swap both of these.

try {
if (customLeakDetector != null) {
Class<?> detectorClass = Class.forName(customLeakDetector, true,
PlatformDependent.getSystemClassLoader());
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be done just one time in a static block. There is no need to load the class multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it can but it'll be slightly ugly because it will have to handle the ClassNotFoundException.

Copy link
Member

Choose a reason for hiding this comment

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

Whats the problem ? Just catch Throwable log it and always use the default impl later on. We do this in several places in netty.

@normanmaurer
Copy link
Member

normanmaurer commented Jun 16, 2016

@artgon just four comments. Rest looks awesome!

}

ResourceLeakDetector<T> resourceLeakDetector = new ResourceLeakDetector<T>(resource);
logger.debug("Loaded default ResourceLeakDetector: {}", resourceLeakDetector);
Copy link
Member

Choose a reason for hiding this comment

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

if we remove the log message for loading above please also remove this one

@normanmaurer
Copy link
Member

@artgon you rock! Will pull this in shortly. Thanks again for this awesome contribution.

@normanmaurer
Copy link
Member

@artgon Could you also update AbstractDnsMessage to use the factory ? Once done I will pull in.

Allow users of Netty to plug in their own leak detector for the purpose
of instrumentation.

Motivation:

We are rolling out a large Netty deployment and want to be able to
track the amount of leaks we're seeing in production via custom
instrumentation. In order to achieve this today, I had to plug in a
custom `ByteBufAllocator` into the bootstrap and have it initialize a
custom `ResourceLeakDetector`. Due to these classes mostly being marked
`final` or having private or static methods, a lot of the code had to
be copy-pasted and it's quite ugly.

Modifications:

* I've added a static loader method for the `ResourceLeakDetector` in
`AbstractByteBuf` that tries to instantiate the class passed in via the
`-Dio.netty.customResourceLeakDetector`, otherwise falling back to the
default one.
* I've modified `ResourceLeakDetector` to be non-final and to have the
reporting broken out in to methods that can be overridden.

Result:

You can instrument leaks in your application by just adding something
like the following:

```java
public class InstrumentedResourceLeakDetector<T> extends
ResourceLeakDetector<T> {

    @monitor("InstanceLeakCounter")
    private final AtomicInteger instancesLeakCounter;

    @monitor("LeakCounter")
    private final AtomicInteger leakCounter;

    public InstrumentedResourceLeakDetector(Class<T> resource) {
        super(resource);
        this.instancesLeakCounter = new AtomicInteger();
        this.leakCounter = new AtomicInteger();
    }

    @OverRide
    protected void reportTracedLeak(String records) {
        super.reportTracedLeak(records);
        leakCounter.incrementAndGet();
    }

    @OverRide
    protected void reportUntracedLeak() {
        super.reportUntracedLeak();
        leakCounter.incrementAndGet();
    }

    @OverRide
    protected void reportInstancesLeak() {
        super.reportInstancesLeak();
        instancesLeakCounter.incrementAndGet();
    }
}
```
@artgon
Copy link
Contributor Author

artgon commented Jun 20, 2016

Okay, I've made the changes.

@normanmaurer
Copy link
Member

@artgon ❤️... will pull in once the CI runs finishes

@normanmaurer
Copy link
Member

Cherry-picked into 4.0 (cad22bb) and 4.1 (3288cac)

@normanmaurer
Copy link
Member

@artgon thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants