-
Notifications
You must be signed in to change notification settings - Fork 20.3k
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
metrics: apply selected changes from upstream #28031
Conversation
49f73f5
to
853ab0c
Compare
The metrics library has some peculiar designs. And after diving into it a bit, I figured out how those peculiar patterns emerged. OriginFirst, start off with a minimal representation of a 'point': type point struct {
x, y int
} And since we want to do updates, and deliver a type updatingPoint struct {
p point
mu sync.Mutex
} We keep an internal We already have a mutex for when to deliver/replace a Snapshot(), so we might aswell use the same func (u *updatingPoint) setX(int newX){
mu.Lock()
defer mu.Unlock()
u.p.x = newX
} ChangeAt some point, someone made the type point struct {
x atomic.Uint64
y atomic.Uint64
}
func (u *updatingPoint) setX(int newX){
u.p.x.Store(newX)
} This was not a great change.
A more suitable change, at this point, would have been to redesign it to deliver atomic-free minimal "readonly" snapshot, but instead use an atomic-based internal representation: // A true readonly point
type point struct {
x, y int
}
type updatingPoint struct {
x atomic.Uint64
y atomic.Uint64
}
func (u *updatingPoint) Snapshot(){
return &point{ x.Load(), y.Load() }
}
func (u *updatingPoint) setX(int newX){
p.x.Store(newX)
} And along the way, when the I have made this change to |
Hm, an even better, clear and logical restructuring is to define it thus:
I have a follow-up PR for this. It removes a lot of code, and a lot of edgecases: in practice, we never read on the metrics, only on the snapshots. But due to the mandate that they must be readable, it makes the synchronization more complex than it needs to be. |
ref rcrowley/go-metrics@9073533 Remove locks from the hot-path in meter.go and add parallel benchmark metrics: simplify meter.go metrics: refactor meter
4e4e27f
to
16d39dd
Compare
I would prefer to merge the larger PR, over this one. This is an ok PR, but with the read/write split happening in #28035, it becomes easier to understand the semantics regarding concurrency and locking. So although in total that one is larger, I think piecemeal-wise, it is easier for a reviewer to understand. |
Closing this in favour of #28035 |
Back in 2018 with PR #15910, we took an external metrics-lib and made it internal.
Since then, we have applied updates, and also some updates have been applied in the source repo. In this PR, I will cherry-pick and adapt some of those changes that make sense.
For
meter
, the change becomes lock-free, but it slightly changes the semantics too (same way upstream also does it: https://github.com/rcrowley/go-metrics/blob/master/meter.go ) . It is not racy, as in, the golang race detector would not find any flaws. That said, with the locks removed, it is possible that during short intervals, the results are inconsistent. If an update happens while atick
happens, therate1
ewma may contain a datapoint that has not been added to therate5
ewma. I don't think it's a problem, and neither does upstream, but still worth mentioning.