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

Added DefaultCpuHealthChecker #4673

Merged
merged 41 commits into from
Mar 28, 2024
Merged

Conversation

Bue-von-hon
Copy link
Contributor

@Bue-von-hon Bue-von-hon commented Feb 14, 2023

Motivation:

Resolves part of #1854

Some users may want to use CPU usage to determine if their instance is healthy.
From now on, Users can perform health checks in the following ways

HealthChecker cpuHealthChecker = HealthChecker.ofCpu(80, 80);
cpuHealthChecker.isHealthy();

Modifications:

  • Added DefaultCpuHealthChecker that checks cpu-usage by OperatingSystemMXBean

Result:

A User can health-check with system-cpu-usage

p.s.
Implemented this by referencing ProcessorMetrics.java in the micrometer core.

Motivation:

Resolves part of line#1854

Some users may want to use CPU usage to determine if their instance is healthy.
From now on, Users can perform health checks in the following ways

```
DefaultCpuHealthChecker cpuHealthChecker = new DefaultCpuHealthChecker(20.0);
cpuHealthChecker.isHealthy();
```

Modifications:

- Added DefaultCpuHealthChecker that checks cpu-usage by OperatingSystemMXBean

Result:

A User can health-check with system-cpu-usage

p.s.
Implemented this by referencing ProcessorMetrics.java in the micrometer core.
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Looks good overall! Left some minor comments

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also leave a link where this class was forked from?

Suggested change
// Forked from https://github.com/micrometer-metrics/micrometer/blob/8339d57bef8689beb8d7a18b429a166f6595f2af/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!

import java.util.Arrays;
import java.util.List;

public class DefaultCpuHealthChecker implements HealthChecker{
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we also add integration tests for this class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm struggling with writing tests.😭
To do the test, need to modify the CPU usage of the system and it is very difficult.
I'm considering passing a Method class through the constructor.(Dependency Injection with @VisibleForTesting annotation).
p.s.
I looked at micrometer's ProcessorMetricsTest.java and spring-boot's AvailabilityStateHealthIndicatorTests.java but didn't get much help.😭

Copy link
Contributor

@jrhee17 jrhee17 Mar 6, 2023

Choose a reason for hiding this comment

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

To do the test, need to modify the CPU usage of the system and it is very difficult.

I don't think this is necessary 😅 I think just verifying that DefaultCpuHealthChecker gathers system cpu usage information is enough.
We may expose targetCpuUsage, targetProcessCpuLoad (with @VisibleForTesting) and check that they are not NaN for example.


private final double targetCpuUsage;

public DefaultCpuHealthChecker(final double targetCpuUsage) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we accept arguments for both targetSystemCpuLoad and targetProcessCpuLoad so that users can configure limits separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!

}
}

private Class<?> getFirstClassFound(final List<String> classNames) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private Class<?> getFirstClassFound(final List<String> classNames) {
@Nullable
private Class<?> getFirstClassFound(final List<String> classNames) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!

Modifications:

- Fixed DefaultCpuHealthChecker
added copyright header, added javadocs,
Change health check from range-based to ratio-based,
added nullable annotations,
added requireNonNull method for nullable method calls
Copy link
Contributor Author

@Bue-von-hon Bue-von-hon left a comment

Choose a reason for hiding this comment

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

@jrhee17 Fixed based on a code review!

p.s.
Another PR on the same issue commented on the ratio-based.
I thought it was reasonable so applied it!

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!


private final double targetCpuUsage;

public DefaultCpuHealthChecker(final double targetCpuUsage) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!

import java.util.Arrays;
import java.util.List;

public class DefaultCpuHealthChecker implements HealthChecker{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm struggling with writing tests.😭
To do the test, need to modify the CPU usage of the system and it is very difficult.
I'm considering passing a Method class through the constructor.(Dependency Injection with @VisibleForTesting annotation).
p.s.
I looked at micrometer's ProcessorMetricsTest.java and spring-boot's AvailabilityStateHealthIndicatorTests.java but didn't get much help.😭

}
}

private Class<?> getFirstClassFound(final List<String> classNames) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

i added!

added ending period into first sentence of Javadoc
Comment on lines 61 to 62
this.targetCpuUsage = (double) cpuUsage / cpuIdle;
this.targetProcessCpuLoad = (double) processCpuUsage / processCpuIdle;
Copy link
Contributor

Choose a reason for hiding this comment

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

Question) I'm unsure why we're dividing usage by idleness. Can't we just receive two doubles targetCpuUsage, targetProcessCpuLoad as parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using two is more readable. fixed it!

Comment on lines 59 to 60
public DefaultCpuHealthChecker(final int cpuUsage, final int cpuIdle,
final int processCpuUsage, final int processCpuIdle) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of exposing this public constructor, can we use a static factory method to instantiate this checker?

Suggested change
public DefaultCpuHealthChecker(final int cpuUsage, final int cpuIdle,
final int processCpuUsage, final int processCpuIdle) {
private DefaultCpuHealthChecker(final int cpuUsage, final int cpuIdle,
final int processCpuUsage, final int processCpuIdle) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jrhee17 i Fixed it! And moved the factory method I created earlier to DefaultCpuHealthChecker.

Comment on lines 29 to 31
/**
* Forked from <a href="https://github.com/micrometer-metrics/micrometer/blob/8339d57bef8689beb8d7a18b429a166f6595f2af/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java">ProcessorMetrics.java</a> in the micrometer core.
*/
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be part of Javadoc. Could you use // instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Fixed!


private double invoke(final Method method) {
try {
return (double) method.invoke(operatingSystemBean);
Copy link
Contributor

Choose a reason for hiding this comment

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

This method will be frequently invoked whenever HealthCheckService receives a request, hence MethodHandles might be a better choice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using the lookup#unreflect method, extracted the MethodHandle object from the Method class!

/**
* Forked from <a href="https://github.com/micrometer-metrics/micrometer/blob/8339d57bef8689beb8d7a18b429a166f6595f2af/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java">ProcessorMetrics.java</a> in the micrometer core.
*/
public class DefaultCpuHealthChecker implements HealthChecker {
Copy link
Contributor

Choose a reason for hiding this comment

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

If we hide the implementation from the public API, we can get more freedom on changes.
Should we add a factory method of this class to HealthChecker and make this package-private?

// The simplest one.
HealthChecker.ofCpu(cpuUsage);
// A complex configuation.
HealthChecker.ofCpu(cpuUsage, cpuIdle...);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha, so let's make this class pakage-private and add a factory method to HealthChecker!

Bue-von-hon and others added 8 commits March 11, 2023 14:58
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…aultCpuHealthChecker.java

Co-authored-by: Ikhun Um <ih.pert@gmail.com>
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…aultCpuHealthChecker.java

Co-authored-by: Trustin Lee <t@motd.kr>
…aultCpuHealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
@jrhee17 jrhee17 modified the milestones: 1.23.0, 1.24.0 Mar 17, 2023
…est class for cpuHealthChecker, factory method into HealthChecker

Motivation:

Apply readability and performance improvements suggested in code reviews.
Added comment on public method.
Added test for DefaultCpuHealthChecker.

Modifications:
- Fixed DefaultCpuHealthChecker#invoke method to use methodHandlers instead of reflection for better performance.
- Fixed a complex constructor to be more readable.
- Fixed the systemCpuUsage and processCpuUsage's access modifier for testing.
- Added javadoc for public method in DefaultCpuHealthChecker.
- Added Factory Method into HealthChecker.
- Added test class for DefaultCpuHealthChecker.

Result:

better performing DefaultCpuHealthChecker and tests.
Copy link
Contributor Author

@Bue-von-hon Bue-von-hon left a comment

Choose a reason for hiding this comment

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

@jrhee17 I sent a new commit based on the code review.
Hope you like it🙂

Comment on lines 29 to 31
/**
* Forked from <a href="https://github.com/micrometer-metrics/micrometer/blob/8339d57bef8689beb8d7a18b429a166f6595f2af/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java">ProcessorMetrics.java</a> in the micrometer core.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Fixed!

Comment on lines 61 to 62
this.targetCpuUsage = (double) cpuUsage / cpuIdle;
this.targetProcessCpuLoad = (double) processCpuUsage / processCpuIdle;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using two is more readable. fixed it!

/**
* Forked from <a href="https://github.com/micrometer-metrics/micrometer/blob/8339d57bef8689beb8d7a18b429a166f6595f2af/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/system/ProcessorMetrics.java">ProcessorMetrics.java</a> in the micrometer core.
*/
public class DefaultCpuHealthChecker implements HealthChecker {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha, so let's make this class pakage-private and add a factory method to HealthChecker!


private double invoke(final Method method) {
try {
return (double) method.invoke(operatingSystemBean);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using the lookup#unreflect method, extracted the MethodHandle object from the Method class!

Modification:
- Move the static factory method to DefaultCpuHealthChecker.
@Bue-von-hon
Copy link
Contributor Author

Gentle Ping @trustin @ikhoon @jrhee17

@ikhoon ikhoon modified the milestones: 1.27.0, 1.28.0 Jan 16, 2024
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
@Bue-von-hon Bue-von-hon marked this pull request as draft January 30, 2024 23:08
Copy link
Contributor Author

@Bue-von-hon Bue-von-hon left a comment

Choose a reason for hiding this comment

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

I misunderstood your comment about testing.
I've rewritten the test correctly.
PTAL @jrhee17

@Bue-von-hon Bue-von-hon marked this pull request as ready for review February 26, 2024 07:37
@Bue-von-hon
Copy link
Contributor Author

@jrhee17 gentle ping

2 similar comments
@Bue-von-hon
Copy link
Contributor Author

@jrhee17 gentle ping

@Bue-von-hon
Copy link
Contributor Author

@jrhee17 gentle ping

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Sorry about the delay 😅 Looks like almost the last round for me!

return null;
}

static CpuHealthChecker of(double targetCpuUsage, double targetProcessCpuLoad) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of:

  1. Moving this to HealthChecker.java
  2. Renaming this method from of to ofCpu
  3. Making this method public so that users can actually use this class
  4. Adding javadocs explaining what this health checker does, and what each parameter does

Copy link
Contributor

Choose a reason for hiding this comment

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

I saw this comment late:

#4673 (comment)

I'm fine if this is handled separately, I would just like to note that users won't be able to access this method since it is not public

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

It looks great once @jrhee17 comments are addressed.
Thanks for your effort on this work! ❤️🚀

@Bue-von-hon Bue-von-hon marked this pull request as draft March 21, 2024 07:34
Bue-von-hon and others added 6 commits March 22, 2024 12:57
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
…HealthChecker.java

Co-authored-by: jrhee17 <guins_j@guins.org>
Motivation:
Add a factory method so that users can actually use the CPU health checker.

Modification:
- Fix(HealthChecker): added a factory method.
- Fix(CpuHealthChecker): removed factory method and added a little more clarity in the description.
- Fix(CpuHealthCheckerTest): renamed the method and modified it to use the newly created factory method.

Result:
Users will be able to use the newly created health checker, and a detailed description will improve usability.
@Bue-von-hon
Copy link
Contributor Author

@jrhee17 I've addressed everything you commented on.

The fact that the factory method was mentioned twice, I felt it was important. I implemented it by adding a static method to the interface. It was the best I could do, as a way to keep compatibility across subclasses as much as possible.

p.s.
I'm concerned about the class(CpuHealthChecker) being exposed outside of its intended scope. The IDE is also warning me about this.

@Bue-von-hon Bue-von-hon marked this pull request as ready for review March 22, 2024 05:59
Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Added a minor commit mostly fixing javadocs, but looks good to me! Thanks for your patience @Bue-von-hon ! 🙇 👍 🙇

@minwoox
Copy link
Member

minwoox commented Mar 27, 2024

Thanks a lot for your patience, @Bue-von-hon!

@jrhee17 jrhee17 merged commit 1be0dd1 into line:main Mar 28, 2024
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants