-
Notifications
You must be signed in to change notification settings - Fork 71
Fix data race #307
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
Fix data race #307
Conversation
The test reveals the race condition and prepares for refactor.
| } | ||
|
|
||
| go func() { | ||
| s.CopyInto(&theCopy) |
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.
CopyInto documents the function is not thread safe ... do we really call it in multi-threaded contexts normally?
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.
CopyInto documents the function is not thread safe
Didn't see that. Will also remove the documents in this PR
do we really call it in multi-threaded contexts normally
I think yes. From the original logs of integration test: APIServer will call sigs.k8s.io/structured-merge-diff/v6/typed.ParseableType.FromStructured() for each request in different goroutine. Please see the link in the PR description for detail.
schema/elements.go
Outdated
|
|
||
| once sync.Once | ||
| m map[string]TypeDef | ||
| m atomic.Pointer[map[string]*TypeDef] |
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.
If we end up trying to make CopyInto thread-safe, let's keep this change as minimal as possible ... keep the once behavior and semantics, and just switch m to being an atomic.Pointer to the original type which we can Load()/Store() safely lock-free. Let's not change the value type to a pointer in this PR
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.
OK, I will remove the last commit from this PR then.
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 still think my original proposal is better. But as you wish, now I'm keeping the once.
goos: darwin
goarch: arm64
pkg: sigs.k8s.io/structured-merge-diff/v6/schema
cpu: Apple M2 Pro
│ base.txt │ fix+once.txt │
│ sec/op │ sec/op vs base │
FindFieldCached-12 8.542n ± 1% 8.698n ± 2% +1.82% (p=0.000 n=10)
FindFieldNew-12 246.8n ± 5% 259.8n ± 2% +5.27% (p=0.011 n=10)
MakeMap-12 92.38n ± 1% 93.34n ± 1% ~ (p=0.190 n=10)
geomean 57.97n 59.53n +2.69%
│ base.txt │ fix+once.txt │
│ B/op │ B/op vs base │
FindFieldCached-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
FindFieldNew-12 1.188Ki ± 0% 1.195Ki ± 0% +0.66% (p=0.000 n=10)
MakeMap-12 400.0 ± 0% 400.0 ± 0% ~ (p=1.000 n=10) ¹
geomean ² +0.22% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
│ base.txt │ fix+once.txt │
│ allocs/op │ allocs/op vs base │
FindFieldCached-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹
FindFieldNew-12 7.000 ± 0% 8.000 ± 0% +14.29% (p=0.000 n=10)
MakeMap-12 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=10) ¹
geomean ² +4.55% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
This change now have no significant performance impact.
c1ae363 to
9cd5e06
Compare
| // CopyInto this instance of Map into the other | ||
| // If other is nil this method does nothing. | ||
| // If other is already initialized, overwrites it with this instance | ||
| // Warning: Not thread safe |
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 still don't think this is thread-safe with respect to dst
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.
OK, if you mean it is not safe to read dst when we are copying, then yes. Updated doc to clarify this.
Use an additional atomic.Pointer
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: huww98, liggitt The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
With the added test in the first commit:
This is discovered in https://prow.k8s.io/view/gs/kubernetes-ci-logs/logs/ci-kubernetes-integration-race-master/1979871352273768448
The second commit fixed this. And the third commit optimized the memory comsumption a little by the way. And here is the benchmark result from my laptop:
We got both significant speed and allocate bytes improvements. Only downside is one extra allotation op for new map allocation (presumed due to the pointer to map used in atomic.Pointer). But this is not at the hot path, so I think it should be fine.