Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions internal/base/exit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2026 The LevelDB-Go and Pebble Authors. All rights reserved. Use
// of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.

package base

import "os"

// Exit is called when Pebble needs to terminate the process due to an
// invariant violation. By default it calls os.Exit, but embedders
// (e.g. CockroachDB) can override it to route through their own fatal
// logging infrastructure for better crash visibility.
var Exit = func(code int) {
os.Exit(code)
}
3 changes: 1 addition & 2 deletions internal/base/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"context"
"fmt"
"log"
"os"
"sync"

"github.com/cockroachdb/pebble/internal/invariants"
Expand Down Expand Up @@ -41,7 +40,7 @@ func (defaultLogger) Errorf(format string, args ...interface{}) {
// Fatalf implements the Logger.Fatalf interface.
func (defaultLogger) Fatalf(format string, args ...interface{}) {
_ = log.Output(2, fmt.Sprintf(format, args...))
os.Exit(1)
Exit(1)
}

// InMemLogger implements Logger using an in-memory buffer (used for testing).
Expand Down
3 changes: 2 additions & 1 deletion internal/cache/block_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"unsafe"

"github.com/cockroachdb/pebble/internal/base"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/pebble/internal/manual"
"github.com/cockroachdb/swiss"
Expand Down Expand Up @@ -58,7 +59,7 @@ func newBlockMap(initialCapacity int) *blockMap {
m := obj.(*blockMap)
if !m.closed {
fmt.Fprintf(os.Stderr, "%p: block-map not closed\n", m)
os.Exit(1)
base.Exit(1)
}
})
return m
Expand Down
2 changes: 1 addition & 1 deletion internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func NewWithShards(size int64, shards int) *Cache {
fmt.Fprintf(os.Stderr, "%s\n", strings.Join(c.tr.msgs, "\n"))
}
c.tr.Unlock()
os.Exit(1)
base.Exit(1)
}
})
return c
Expand Down
10 changes: 5 additions & 5 deletions internal/cache/clockpro.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,17 +500,17 @@ func (c *shard) metaCheck(e *entry) {
if _, ok := c.entries[e]; ok {
fmt.Fprintf(os.Stderr, "%p: %s unexpectedly found in entries map\n%s",
e, e.key, debug.Stack())
os.Exit(1)
base.Exit(1)
}
if c.blocks.findByValue(e) {
fmt.Fprintf(os.Stderr, "%p: %s unexpectedly found in blocks map\n%#v\n%s",
e, e.key, &c.blocks, debug.Stack())
os.Exit(1)
base.Exit(1)
}
if c.files.findByValue(e) {
fmt.Fprintf(os.Stderr, "%p: %s unexpectedly found in files map\n%#v\n%s",
e, e.key, &c.files, debug.Stack())
os.Exit(1)
base.Exit(1)
}
// NB: c.hand{Hot,Cold,Test} are pointers into a single linked list. We
// only have to traverse one of them to check all of them.
Expand All @@ -532,7 +532,7 @@ func (c *shard) metaCheck(e *entry) {
if e == t {
fmt.Fprintf(os.Stderr, "%p: %s unexpectedly found in blocks list\n%s",
e, e.key, debug.Stack())
os.Exit(1)
base.Exit(1)
}
if t == c.handHot {
break
Expand All @@ -546,7 +546,7 @@ func (c *shard) metaCheck(e *entry) {
c.countHot, c.sizeHot, c.countCold, c.sizeCold, c.countTest, c.sizeTest,
countHot, sizeHot, countCold, sizeCold, countTest, sizeTest,
debug.Stack())
os.Exit(1)
base.Exit(1)
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion internal/cache/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync/atomic"
"unsafe"

"github.com/cockroachdb/pebble/internal/base"
"github.com/cockroachdb/pebble/internal/buildtags"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/pebble/internal/manual"
Expand Down Expand Up @@ -186,7 +187,7 @@ func entryAllocNew() *entry {
e := obj.(*entry)
if *e != (entry{}) {
fmt.Fprintf(os.Stderr, "%p: entry was not freed", e)
os.Exit(1)
base.Exit(1)
}
})
return e
Expand Down
3 changes: 2 additions & 1 deletion internal/cache/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"unsafe"

"github.com/cockroachdb/errors"
"github.com/cockroachdb/pebble/internal/base"
"github.com/cockroachdb/pebble/internal/buildtags"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/pebble/internal/manual"
Expand Down Expand Up @@ -70,7 +71,7 @@ func Alloc(n int) *Value {
if v.buf != nil {
fmt.Fprintf(os.Stderr, "%p: cache value was not freed: refs=%d\n%s",
v, v.refs(), v.ref.traces())
os.Exit(1)
base.Exit(1)
}
})
return v
Expand Down
2 changes: 1 addition & 1 deletion mem_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func checkMemTable(obj interface{}) {
m := obj.(*memTable)
if m.arenaBuf.Data() != nil {
fmt.Fprintf(os.Stderr, "%v: memTable buffer was not freed\n", m.arenaBuf)
os.Exit(1)
base.Exit(1)
}
}

Expand Down
5 changes: 3 additions & 2 deletions objstorage/objstorageprovider/vfs_readable.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"runtime/debug"
"sync"

"github.com/cockroachdb/pebble/internal/base"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/pebble/objstorage"
"github.com/cockroachdb/pebble/vfs"
Expand Down Expand Up @@ -55,7 +56,7 @@ func newFileReadable(
invariants.SetFinalizer(r, func(obj interface{}) {
if obj.(*fileReadable).file != nil {
fmt.Fprintf(os.Stderr, "Readable %s was not closed\n%s", filename, stack)
os.Exit(1)
base.Exit(1)
}
})
}
Expand Down Expand Up @@ -112,7 +113,7 @@ var readHandlePool = sync.Pool{
invariants.SetFinalizer(i, func(obj interface{}) {
if obj.(*vfsReadHandle).r != nil {
fmt.Fprintf(os.Stderr, "ReadHandle was not closed")
os.Exit(1)
base.Exit(1)
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion open.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
v := obj.(*atomic.Value)
if err := v.Load(); err == nil {
fmt.Fprintf(os.Stderr, "%s: unreferenced DB not closed\n", dPtr)
os.Exit(1)
base.Exit(1)
}
})

Expand Down
11 changes: 11 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"bytes"
"fmt"
"io"
"os"
"regexp"
"runtime"
"slices"
Expand Down Expand Up @@ -943,6 +944,12 @@ type Options struct {
// LoggerAndTracer is used for writing log messages and traces.
LoggerAndTracer LoggerAndTracer

// ExitFunc is called when Pebble needs to terminate the process due to
// an invariant violation. If nil, os.Exit is used. Embedders (e.g.
// CockroachDB) can set this to route exits through their own fatal
// logging infrastructure for better crash visibility.
ExitFunc func(code int)

// MaxManifestFileSize is the maximum size the MANIFEST file is allowed to
// become. When the MANIFEST exceeds this size it is rolled over and a new
// MANIFEST is created.
Expand Down Expand Up @@ -1675,6 +1682,10 @@ func (o *Options) EnsureDefaults() {
if o.Logger == nil {
o.Logger = DefaultLogger
}
if o.ExitFunc == nil {
o.ExitFunc = os.Exit
}
base.Exit = o.ExitFunc
if o.EventListener == nil {
o.EventListener = &EventListener{}
}
Expand Down
2 changes: 1 addition & 1 deletion sstable/colblk/keyspan.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ var keyspanIterPool = sync.Pool{
invariants.SetFinalizer(i, func(obj interface{}) {
if i := obj.(*KeyspanIter); i.handle.Valid() {
fmt.Fprintf(os.Stderr, "KeyspanIter.handle is not nil: %#v\n", i.handle)
os.Exit(1)
base.Exit(1)
}
})
}
Expand Down
8 changes: 4 additions & 4 deletions sstable/reader_iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ func checkSingleLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlo
i := obj.(*singleLevelIterator[I, PI, D, PD])
if h := PD(&i.data).Handle(); h.Valid() {
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %#v\n", h)
os.Exit(1)
base.Exit(1)
}
if h := PI(&i.index).Handle(); h.Valid() {
fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %#v\n", h)
os.Exit(1)
base.Exit(1)
}
}

Expand All @@ -218,10 +218,10 @@ func checkTwoLevelIterator[I any, PI indexBlockIterator[I], D any, PD dataBlockI
i := obj.(*twoLevelIterator[I, PI, D, PD])
if h := PD(&i.secondLevel.data).Handle(); h.Valid() {
fmt.Fprintf(os.Stderr, "singleLevelIterator.data.handle is not nil: %#v\n", h)
os.Exit(1)
base.Exit(1)
}
if h := PI(&i.secondLevel.index).Handle(); h.Valid() {
fmt.Fprintf(os.Stderr, "singleLevelIterator.index.handle is not nil: %#v\n", h)
os.Exit(1)
base.Exit(1)
}
}
2 changes: 1 addition & 1 deletion sstable/rowblk/rowblk_fragment_iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,6 @@ func checkFragmentBlockIterator(obj interface{}) {
i := obj.(*fragmentIter)
if h := i.blockIter.Handle(); h.Valid() {
fmt.Fprintf(os.Stderr, "fragmentBlockIter.blockIter.handle is not nil: %#v\n", h)
os.Exit(1)
base.Exit(1)
}
}
Loading