Skip to content
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: copy names from mmapped memory before closing iterator #22040

Merged
merged 3 commits into from
Aug 4, 2021

Conversation

davidby-influx
Copy link
Contributor

@davidby-influx davidby-influx commented Aug 4, 2021

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000
tsdb/index.go Outdated
return
}

ls, lsfi, err := exprToSlice(e.LHS)
Copy link
Contributor

Choose a reason for hiding this comment

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

lsfi.ToSlice() gives ls right? Why name both?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because ToSlice() can fail on none-slice-based iterators.

tsdb/index.go Outdated
if e.Op == influxql.OR {
return bytesutil.Union(lhs, rhs), nil
return newFileMeasurementSliceIterator(bytesutil.Union(ls, rs), mis), nil
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly it might be clearer if this read like:

newFileMeasurementSliceIterator(bytesutil.Union(lsfi.UnsafeToSlice(), rsfi.UnsafeToSlice), MeasurementIterators{lsfi, rsfi})

Also would it be clearer to name MeasurementIterators as MeasurementIteratorClosers ?

@@ -968,6 +988,46 @@ func (itr *measurementSliceIterator) Next() (name []byte, err error) {
return name, nil
}

func (itr *measurementSliceIterator) ToSlice() [][]byte {
return itr.names
Copy link
Contributor

Choose a reason for hiding this comment

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

Having both ToSlice and Next on the same object seems prone to accident...

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe if it was named RemainingAsSlice or something that suggests it is part of the iteration interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

UnderlyingSlice() is the new name

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, UnderlyingSlice still hints to me that the slice is the whole thing instead of eaten away at by Next, but ok.


mitr, err := is.measurementIterator()
if err != nil {
return nil, err
} else if mitr == nil {
return nil, nil
}
defer mitr.Close()
defer func() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I've recently seen a pattern using sync.Once to do this sort of thing, and I like it.

Copy link
Contributor

Choose a reason for hiding this comment

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

E.g.:

// early:
once sync.Once{}
defer once.Do(mitr.Close)

//later
var toReturn ...
once.Do(func(){
  toReturn = newFileMeasurementSliceIterator(names, MeasurementIterators{mitr}), nil
})
return toReturn

So you're guaranteed just one of either the close or the delegation of the close succeeds.

Copy link
Contributor

Choose a reason for hiding this comment

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

This comment doesn't need to be taken for the PR, just an observation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that this pattern won't work here, because the initial defer once.Do(mitr.Close) will be called first, before the close on the toReturn. And that's what is causing the panic; a close on the iterator before the data is read out. But it's a nice pattern for other places.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm - I think it would work, here's a playground example. But not necessary here. https://play.golang.org/p/35qO7UPTOyG

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I see; Once works differently than a similar construct I used in another language; my mistake.

Copy link
Contributor

@lesam lesam left a comment

Choose a reason for hiding this comment

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

See comments

@lesam
Copy link
Contributor

lesam commented Aug 4, 2021

Mostly naming comments.

@davidby-influx davidby-influx requested a review from lesam August 4, 2021 18:42
@@ -1422,24 +1471,24 @@ func (is IndexSet) measurementNamesByExpr(auth query.FineAuthorizer, expr influx
case *influxql.ParenExpr:
return is.measurementNamesByExpr(auth, e.Expr)
default:
return nil, fmt.Errorf("Invalid measurement expression %#v", expr)
return nil, fmt.Errorf("invalid measurement expression %#v", expr)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@@ -949,6 +949,11 @@ func (a MeasurementIterators) Close() (err error) {
return err
}

type MeasurementSliceIterator interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is much nicer than ToSlice as a free function.

@lesam
Copy link
Contributor

lesam commented Aug 4, 2021

Ugh - you hit a new flaky test, I have a PR up to fix: #22038

@davidby-influx davidby-influx merged commit a989f8f into master-1.x Aug 4, 2021
@davidby-influx davidby-influx deleted the DSB_infor_panic_new branch August 4, 2021 20:16
davidby-influx added a commit that referenced this pull request Aug 4, 2021
This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)
davidby-influx added a commit that referenced this pull request Aug 4, 2021
This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)
davidby-influx added a commit that referenced this pull request Aug 4, 2021
This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)
davidby-influx added a commit that referenced this pull request Aug 4, 2021
…22058)

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)

Closes #22001
davidby-influx added a commit that referenced this pull request Aug 4, 2021
…22059)

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)

closes #22002
davidby-influx added a commit that referenced this pull request Aug 4, 2021
…22060)

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes #22000

(cherry picked from commit a989f8f)

closes #22003
chengshiwen pushed a commit to chengshiwen/influxdb that referenced this pull request Aug 11, 2024
…ta#22040)

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes influxdata#22000
chengshiwen pushed a commit to chengshiwen/influxdb that referenced this pull request Aug 27, 2024
…ta#22040)

This fix ensures that memory-mapped files are not released
before pointers into them are copied into heap memory.
MeasurementNamesByExpr() and MeasurementNamesByPredicate() can
cause panics by copying memory from mmapped files that have been
released. The functions they call use iterators to files which
are closed (releasing the mmapped files) before the memory is
safely copied to the heap.

closes influxdata#22000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants