-
Notifications
You must be signed in to change notification settings - Fork 41.4k
Rework subtypes populating code algorithm #7010
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
Conversation
@mwojterski Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
@mwojterski Thank you for signing the Contributor License Agreement! |
while (supertype != Object.class) { | ||
supertype = supertype.getSuperclass(); | ||
if (this.exceptions.containsKey(supertype)) { | ||
this.subtypes.put(type, supertype); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like more than a typo correction. You've swapped the key and value in this call to put
. Was that intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi! Yes It was intentional as I've mentioned in the first comment to this PR. I also changed variable name from subtype
to supertype
because I suspect it was the reason for initial mistake (swapped put arguments) since it was suggesting different relation between arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then shouldn't the containsKey
check have been updated too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. The way I'm reading this code, we only want to save this particular supertype
if we have an explicit exception mapping for it, if not, we should continue upwards till we find the one or there is no more types to check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes. It's a different map. Well, if nothing else, this discussion has shown that the code's confusing to me at least.
I also think that the PR has shown that we're missing some tests. You've changed the behaviour, but haven't had to change any tests. That's bad. I'd like to see some tests around this before we change anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class has a nice battery of tests, but what's more important I haven't changed the contract (behaviour visible from the outside is still the same), but rather fixed an internal optimization. I can't see how I may test this particular internal implementation detail without exposing it, which I think would be a bad thing. If you could shed some light on the way you'd wish such test to be implemented, I'd be most grateful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear, I was saying that the current code and tests are bad, not your PR.
Given that it would appear that the optimisation is faulty in its current state, and no one has raised it as a performance problem, I'm very tempted to remove it and simplify the code. If the optimisation is worth having then it's worth testing. To do that, the filter will need to be refactored. I would create a separate package-private class that's sole responsibility is mapping from a status or exception to an error path and implement it in such a way that it can be spied on or whatever to verify that the optimisation is working as intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry if I seemed offensive, I didn't mean to. I was thinking as well that if nobody noticed it wasn't working, than maybe it's redundant and it'd be better to just remove this piece to reduce complexity. But since I'm no expert on performance optimizations, please decide if you want to keep it and I should refactor & test it, or you wish it simply removed.
Tagged to 1.4.2 but let's not merge this in it's current form. We'll look at Andy's suggestion above. |
I've removed the optimisation as JMH told me that it wasn't worth the effort due to the tiny amount of time that's taken looking up the mapping. Here's the benchmark that I used: import java.util.HashMap;
import java.util.Map;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
public class ErrorPathMappingBenchmark {
@State(Scope.Benchmark)
public static class BenchmarkState {
private final Map<Class<?>, String> exceptions = new HashMap<Class<?>, String>();
private final Map<Class<?>, Class<?>> superTypes = new HashMap<Class<?>, Class<?>>();
public BenchmarkState() {
this.exceptions.put(Exception.class, "/500");
this.superTypes.put(UnsatisfiedDependencyException.class, Exception.class);
}
}
@Benchmark
public String deepException(BenchmarkState state) {
Class<?> type = UnsatisfiedDependencyException.class;
return getErrorPath(state, type);
}
@Benchmark
public String deepExceptionUsingSuperTypes(BenchmarkState state) {
Class<?> type = UnsatisfiedDependencyException.class;
return getErrorPathUsingSuperTypes(state, type);
}
@Benchmark
public String shallowException(BenchmarkState state) {
Class<?> type = Exception.class;
return getErrorPath(state, type);
}
private String getErrorPathUsingSuperTypes(BenchmarkState state, Class<?> type) {
Class<?> superType = state.superTypes.get(type);
if (superType != null) {
return state.exceptions.get(superType);
}
while (type != Object.class) {
String path = state.exceptions.get(type);
if (path != null) {
return path;
}
type = type.getSuperclass();
}
return "global";
}
private String getErrorPath(BenchmarkState state, Class<?> type) {
while (type != Object.class) {
String path = state.exceptions.get(type);
if (path != null) {
return path;
}
type = type.getSuperclass();
}
return "global";
}
} |
If I'm reading this correctly,
subtypes
contains mapping for exceptions that themselves doesn't haveerror-path
mapped, but their super class has, therefore to skip checking inheritance chain each time, they have this shortcut mapping. But here, map is populated the other way around! This might be due to the naming glitch: local variable subtype should probably be named _super_type since it contains parent type. Then mapping would go like: maptype
tosupertype
- which is what is going on. Maybe map should also be namedsupertypes
?