-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Don't call Marshal in unmarshalerEmbeddedStructsHookFunc #14213
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
base: main
Are you sure you want to change the base?
Don't call Marshal in unmarshalerEmbeddedStructsHookFunc #14213
Conversation
CodSpeed Performance ReportMerging #14213 will degrade performances by 31.6%Comparing
|
| Benchmark | BASE |
HEAD |
Change | |
|---|---|---|---|---|
| ⚡ | zstdNoConcurrency |
65.1 µs | 36 µs | +81.02% |
| ⚡ | BenchmarkSplittingBasedOnItemCountHugeLogs |
44 ms | 34.8 ms | +26.27% |
| ⚡ | BenchmarkSplittingBasedOnItemCountHugeMetrics |
119.2 ms | 93.6 ms | +27.42% |
| ⚡ | BenchmarkSplittingBasedOnItemCountManyMetricsSlightlyAboveLimit |
112.8 ms | 85.1 ms | +32.47% |
| ❌ | BenchmarkLogsToProto |
3.1 µs | 4.3 µs | -28.68% |
| ❌ | BenchmarkTracesToProto |
4.7 µs | 6.3 µs | -25.73% |
| ❌ | BenchmarkTraceSizeBytes |
298.8 µs | 436.9 µs | -31.6% |
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (89.47%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #14213 +/- ##
==========================================
- Coverage 92.17% 92.17% -0.01%
==========================================
Files 668 668
Lines 41463 41485 +22
==========================================
+ Hits 38219 38237 +18
- Misses 2212 2214 +2
- Partials 1032 1034 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
mx-psi
left a comment
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 seems to fix the issue that motivated this over at #14203
I would be okay with
breaking unmarshaling of structs with multiple identically-named fields
I am a bit worried about how to test this. Are there any tests we could add here for this?
|
There are already tests for the current embedded unmarshaler behavior, but I think it would be worth adding one using |
|
I added a test that uses a type similar to |
|
This is pretty sensitive stuff, so I think more reviews would be good to ensure it won't break anything that's not currently tested for. |
confmap/internal/decoder.go
Outdated
| // by implementing the Unmarshaler interface. | ||
| func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue { | ||
| func unmarshalerEmbeddedStructsHookFunc(settings UnmarshalOptions) mapstructure.DecodeHookFuncValue { | ||
| // Recursive calls need to ignore sibling keys |
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 couldn't parse this comment.
The PR description was very helpful. I suggest maybe adding a comment to give future readers an overview of what's happening on lines 200-265 or so.
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 reworded the comment and moved that code to the place it's needed (just before the recursive call to Decode). Tell me if that makes things clearer.
|
I'll merge this on Tuesday if there have been no comments on this or earlier if @evan-bradley approves before then |
Context
confmapuses amapstructurehook to call theUnmarshalmethod on types which define it as part of the unmarshaling process, butmapstructurehooks are not called for squashed fields, so we have a separate hook which manually inspects all structs for squashed fields that implementUnmarshaler.This second hook currently returns a modified version of the raw YAML config, to be used by
mapstructureto finish unmarshaling the full struct. This is a problem, because anUnmarshalmethod can set the state of the struct in arbitrary ways, which we want to preserve withoutmapstructureoverwriting fields. (SeeTestEmbeddedUnmarshaler)The way this is currently worked around is by marshaling the embedded struct into a map, and then copying that map back into the raw config, in the hope that nothing will change when the raw config is mapped back into the struct.
However, this assumption breaks when dealing with a
configopaque.Stringinside an embeddedUnmarshaler. That type has the very deliberate behavior of returning[REDACTED]when marshaled instead of its true contents, meaning marshaling then unmarshaling does not round trip.Description
This PR attempts to fix this issue and make embedded
Unmarshalers more reliable by modifying the hook so that it returns a fully unmarshaled struct, leadingmapstructureto do nothing. (This is what the basicUnmarshalerhook does to avoid "interference".)This means the hook needs to somehow unmarshal the non-
Unmarshalersubfields manually, without affecting the input or output ofUnmarshal. I did this by constructing a custom "partial" struct usingreflect.StructOfwhich only contains the non-Unmarshalerfields, copying the initial values into it, callingDecodeon it, then copying the output values back into the original struct.In a previous version of this PR, I kept most of the hook intact, but changed the logic so that we clear fields from the raw config when they might conflict with the embedded struct (instead of setting those fields to the embedded struct's current value). However, this has the side effect of breaking unmarshaling of structs with multiple identically-named fields, of which there is at least one in contrib (both
docker.Configandscraperhelper.ControllerConfighave aTimeoutfield, and one of them implementsUnmarshaler).