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

ScheduledReporter.start() starts with undesired delay #999

Merged
merged 11 commits into from Jan 9, 2017

Conversation

@sofax
Copy link
Contributor

@sofax sofax commented Aug 16, 2016

ScheduledReporter.start() incorrectly uses the interval size for the start delay, too:

    public void start(long period, TimeUnit unit) {
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    report();
                } catch (RuntimeException ex) {
                    LOG.error("RuntimeException thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex);
                }
            }
        }, period, period, unit);
    }

This is a serious problem when the interval is sufficiently big. I suggest to add an alternative version of ScheduledReporter.start() to fix the bug without affecting code that relies on this behavior:

    public void start(long period, long initialDelay, TimeUnit unit) {
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    report();
                } catch (RuntimeException ex) {
                    LOG.error("RuntimeException thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex);
                }
            }
        }, initialDelay, period, unit);
    }
@ryantenney ryantenney changed the title Pull request for https://github.com/dropwizard/metrics/issues/998 ScheduledReporter.start() starts with undesired delay Aug 16, 2016
@arteam
Copy link
Member

@arteam arteam commented Jan 5, 2017

Hello @sofax,

Sorry to get back to you late. I like your pull request, could you please rebase it against the 3.2-development branch? Due changes from another pull requests (respectively #1018), it now has merge conflicts and can't be merged. Thank you.

Copy link
Member

@arteam arteam left a comment

Thanks for actualizing your changes! I've added a couple of comments regarding the implementation.

* @param initialDelay the time to delay the first execution
* @param unit the unit for {@code period}
*/
public void start(long period, long initialDelay, TimeUnit unit) {

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

I think it would be better to change the order of parameters, so initialDelay comes first, to maintain the same order as in the scheduleAtFixedRate method.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Agreed. :)

* @param unit the unit for {@code period}
*/
public void start(long period, long initialDelay, TimeUnit unit) {
executor.scheduleAtFixedRate(new Runnable() {

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

I think we could avoid duplication and implement the method as:

synchronized public void start(long initialDelay, long period, TimeUnit unit) {
        if (this.scheduledFuture != null) {
            throw new IllegalArgumentException("Reporter already started");
        }
        this.scheduledFuture = executor.scheduleAtFixedRate(new Runnable() {
           @Override
           public void run() {
               try {
                   report();
               } catch (RuntimeException ex) {
                   LOG.error("RuntimeException thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex);
               }
           }
       }, initialDelay, period, unit);
   }

as then make the start(long period, TimeUnit unit) method to delegate to it as

start(period, period, unit)

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Again, agreed.

The lines

        if (this.scheduledFuture != null) {
            throw new IllegalArgumentException("Reporter already started");
        }

are new, but they do look reasonable to me.

@@ -64,6 +64,9 @@ protected String getAllowedOrigin() {

/**
* Returns the name of the parameter used to specify the jsonp callback, if any.
*

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

It seems to me as an unrelated change. Could you please revert it?

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Sorry, already working on it - it got pushed too soon.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

This change has been reverted.

@@ -145,6 +145,26 @@ public void run() {
}

/**
* Starts the reporter polling at the given period.

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

Could you add a simple unit test to ScheduledReporterTest? A simple test which verifies that the scheduler wakes up with a correct initial delay. Very similar to the pollsPeriodically test.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Will do. All those tests are a little bit unreliable though, as the executor services rely on the system time instead of using a dedicated (mockable) clock.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Actually, instead of waiting for the reporter to actually do its work, it would be much better to just verify that the method scheduleAtFixedRate() was called with the correct arguments. There is no need to test the executor itself, as that's a collaborator that has already been tested elsewhere.

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

Actually, instead of waiting for the reporter to actually do its work, it would be much better to just verify that the method scheduleAtFixedRate() was called with the correct arguments. There is no need to test the executor itself, as that's a collaborator that has already been tested elsewhere.

Agrred. This would work, too.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Also, the tests are not isolated. The collaborators (mocks) should be initialized per test method, not per class.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Gnnhhh ... my IDE is configured to replace ".*" imports with the explicit ones - but I did NOT want to push those changes, as they were not necessary for the fix.

This comment has been minimized.

@sofax

sofax Jan 9, 2017
Author Contributor

Test cases have been added.

This comment has been minimized.

@arteam

arteam Jan 9, 2017
Member

Gnnhhh ... my IDE is configured to replace ".*" imports with the explicit ones - but I did NOT want to push those changes, as they were not necessary for the fix.

I think expanding star imports is a good change, we can leave it.

@arteam arteam removed the unable to merge label Jan 9, 2017
@arteam arteam added this to the 3.2.0 milestone Jan 9, 2017
@arteam
arteam approved these changes Jan 9, 2017
@arteam arteam merged commit 0ccce2c into dropwizard:3.2-development Jan 9, 2017
1 check passed
1 check passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@arteam
Copy link
Member

@arteam arteam commented Jan 9, 2017

Thank you for your contribution!

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

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.