DetectMemAccessPastTx flag added, default false. Allow badger to run at full speed rather than with debugging code on by default

This commit is contained in:
Jason Aten 2020-08-01 21:40:54 -04:00
parent 0820babc44
commit b806322c5a
2 changed files with 42 additions and 23 deletions

View file

@ -1588,29 +1588,35 @@ func (tx *BadgerTx) toContainer(typ byte, v []byte) (r *roaring.Container) {
return nil
}
// For safety we copy v, since it lives in BadgerDB's memory-mapped vlog-file,
// and Badger will recycle it after tx ends with rollback or commit.
// We copy into Go runtime GC managed memory. Technically we don't need
// to do this if all of our use stays within the lifetime
// of the badger transaction we were started on. Hence:
//
// TODO: performance tuning might want w := v here, if we can guarantee no access to memory past the Tx lifetime.
//
// Problem is, at least some tests appear to not respect transaction boundaries...
//
// Seebs suggested this nice variation: we could use individual mmaps for these
// copies, which would be unusable in production, but workable for testing, and then unmap them,
// which would get us probable segfaults on future accesses to them.
//
w := make([]byte, len(v))
copy(w, v)
// the copy above makes green: // green go test -v -run TestAPI_ImportColumnAttrs
//w := v // if instead of append we use v directly, it causes red: go test -v -run TestAPI_ImportColumnAttrs
var w []byte
if tx.doAllocZero {
// Do electric fence-inspired bad-memory read detection.
//
// The v []byte lives in BadgerDB's memory-mapped vlog-file,
// and Badger will recycle it after tx ends with rollback or commit.
//
// Problem is, at least some operations were not respecting transaction boundaries.
// This technique helped us find them. The rowCache was an example.
//
// See the global const DetectMemAccessPastTx
// at the top of txfactory.go to activate/deactivate this.
//
// Seebs suggested this nice variation: we could use individual mmaps for these
// copies, which would be unusable in production, but workable for testing, and then unmap them,
// which would get us probable segfaults on future accesses to them.
//
// The go runtime also has an -efence flag which may be similarly useful if really pressed.
//
w = make([]byte, len(v))
copy(w, v)
// register w so we can catch out-of-tx memory access
tx.acMu.Lock()
defer tx.acMu.Unlock()
tx.ourAllocs = append(tx.ourAllocs, w)
// register w so we can catch out-of-tx memory access
tx.acMu.Lock()
defer tx.acMu.Unlock()
tx.ourAllocs = append(tx.ourAllocs, w)
} else {
w = v
}
switch typ {
case containerArray:

View file

@ -49,6 +49,19 @@ const (
// Can be overridden with env variable PILOSA_TXSRC for testing.
const DefaultTxsrc = RoaringTxn
// DetectMemAccessPastTx true helps us catch places in api and executor
// where mmapped memory is being accessed after the point in time
// which the transaction has committed or rolled back. Since
// memory segments will be recycled by the underlying databases,
// this can lead to corruption. When DetectMemAccessPastTx is true,
// code in badger.go will copy the transactionally viewed memory before
// returning it for bitmap reading, and then zero it or overwrite it
// with -2 when the Tx completes.
//
// Should be false for production.
//
const DetectMemAccessPastTx = false
var sep = string(os.PathSeparator)
// TxFactory abstracts the creation of Tx interface-level
@ -175,7 +188,7 @@ func NewTxFactory(txsrc string, dir, name string, openExisting bool) (f *TxFacto
}
// electric-fence like finding of access to mmapped data beyond
// transaction end time.
f.badgerDB.doAllocZero = true
f.badgerDB.doAllocZero = DetectMemAccessPastTx
}
switch ty {