Skip to content
This repository has been archived by the owner on Aug 23, 2023. It is now read-only.

Run plan.Clean() upon error too + refactor pointslicepool #1858

Merged
merged 4 commits into from
Aug 28, 2020

Conversation

Dieterbe
Copy link
Contributor

@Dieterbe Dieterbe commented Jul 9, 2020

No description provided.

Copy link
Collaborator

@shanson7 shanson7 left a comment

Choose a reason for hiding this comment

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

Generally looks good, couple comments.

FYI, we recently disabled plan.Clean() due to some (very rare) corruption in responses we were seeing. Still waiting to see if disabling plan.Clean() fixes it. If it does, we'll need to do another round to figure out what is going on.

pointslicepool/pointslicepool.go Outdated Show resolved Hide resolved
@@ -36,7 +35,7 @@ func (s *FuncAbsolute) Exec(dataMap DataMap) ([]models.Series, error) {
serie.Target = fmt.Sprintf("absolute(%s)", serie.Target)
serie.Tags = serie.CopyTagsWith("absolute", "1")
serie.QueryPatt = fmt.Sprintf("absolute(%s)", serie.QueryPatt)
out := pointSlicePool.Get().([]schema.Point)
out := pointSlicePool.Get()
Copy link
Collaborator

@shanson7 shanson7 Jul 10, 2020

Choose a reason for hiding this comment

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

A lot of functions know how many datapoints they need and could use GetMin

Copy link
Contributor Author

Choose a reason for hiding this comment

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

true, but i don't have time now to go over all those

return &PointSlicePool{
defaultSize: defaultSize,
p: sync.Pool{
New: func() interface{} {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if we want to allocate a default slice here or just let it return nil. If someone calls GetMin(2001) and we didn't have anything in the pool, we will allocate two slices (capacities 2000 and 2001).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we return nil, then we put quite some burden on callers that call Get rather than GetMin. If those callers want to be efficient, they have to mind their own allocations, which defeats the point.

I suggest that if or when we do the work to transition call sites to GetMin, we can then apply your suggestion. Or even better, remove the Get method alltogether and make the hint mandatory.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you misunderstood. Basically, no callers should ever access PointSlicePool.p directly. This means PointSlicePool.Get() is free to just be an alias for GetMin(100) (or whatever) and GetMin has the logic to allocate if nil is returned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Basically, no callers should ever access PointSlicePool.p directly

sure. to be clear, this is already the case. (.p is a private property so nothing in a different package can access it)

This means PointSlicePool.Get() is free to just be an alias for GetMin(100) (or whatever) and GetMin has the logic to allocate if nil is returned.

Seems like the choice of the number 100 here is the same as the choice of DefaultPointSliceSize, so we may as well use DefaultPointSliceSize here.
so doing what we have ....

                        New: func() interface{} {
                                return make([]schema.Point, 0, defaultSize)
                        },

seems equivalent to calling GetMin(DefaultPointSliceSize), GetMin receiving a nil and then allocating a slice of the requested size.
(sorry i guess i'm still not getting what you're saying)

Copy link
Collaborator

Choose a reason for hiding this comment

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

seems equivalent to calling GetMin(DefaultPointSliceSize), GetMin receiving a nil and then allocating a slice of the requested size.

For the raw Get case, yes it is the same. But for GetMin(N) where N > DefaultPointSliceSize we save an allocation of DefaultPointSliceSize. For example, GetMin(2001) with an empty pool will currently allocate a cap(2000) slice, put it right back into the pool because it's too small and then allocate cap(2001) slice.

With the suggested implementation GetMin(2001) will get nil from the pool and will allocate the cap(2001) slice. No extra cap(2000) slice allocated.

So, under the assumption that at some point we can update the Get calls (almost) everywhere to use GetMin (which honestly should be very easy because in most funcs it's just len(serie.Datapoints)) then we should see the benefit of this approach.

Copy link
Contributor Author

@Dieterbe Dieterbe Aug 27, 2020

Choose a reason for hiding this comment

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

ok, now i see what you're saying.
how does 3754a85 look ?

one side-effect of this is that in theory regular calls to Get(), which previously would get a small (big enough to be useful, but smaller than p.defaultSize) slice, now would return that slice, and allocate a bigger one instead.
But in practice i don't see that happening because slices being added to the pool should all have come from either Get() or GetMin() anyway

@Dieterbe Dieterbe force-pushed the run-plan-clean-upon-error-too branch from 206cced to c2cc3fb Compare August 27, 2020 10:39
@Dieterbe
Copy link
Contributor Author

rebased on latest master.
can i get an approve?

@Dieterbe
Copy link
Contributor Author

@shanson7 :

FYI, we recently disabled plan.Clean() due to some (very rare) corruption in responses we were seeing. Still waiting to see if disabling plan.Clean() fixes it. If it does, we'll need to do another round to figure out what is going on.

is there an update on this? this is probably resolved as of the recent related fixes wrt the double put etc I think?

@shanson7
Copy link
Collaborator

@shanson7 :

FYI, we recently disabled plan.Clean() due to some (very rare) corruption in responses we were seeing. Still waiting to see if disabling plan.Clean() fixes it. If it does, we'll need to do another round to figure out what is going on.

is there an update on this? this is probably resolved as of the recent related fixes wrt the double put etc I think?

We haven't actually gotten a chance to rollout the latest fix. The last update I have is that we do conditional plan.Clean() (checking for overlaps as we return the slices to the pool), which is what aided me in finding the bug earlier. I'd like to rollout the fix and keep the "checked" plan.Clean() and ideally see no reoccurrences of the issue.

@Dieterbe Dieterbe merged commit c8e946c into master Aug 28, 2020
@Dieterbe Dieterbe deleted the run-plan-clean-upon-error-too branch August 28, 2020 17:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants