Skip to content

Commit

Permalink
base: improve Comparer documentation and usability
Browse files Browse the repository at this point in the history
This change documents the fields of `Comparer` and adds code to fill
in `Equal` and `FormatKey` fields with reasonable defaults (replacing
the `equals` wrapper; note that the wrapper used `bytes.Equal` as the
default whereas we now fall back to `Compare` instead).

Note that `FormatKey` is used unconditionally in various error
messages.

I am investigating separately adding a default for `Split` as well.
  • Loading branch information
RaduBerinde committed Feb 26, 2024
1 parent bde41ee commit 306ea71
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 42 deletions.
6 changes: 3 additions & 3 deletions compaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ func newCompaction(
c := &compaction{
kind: compactionKindDefault,
cmp: pc.cmp,
equal: opts.equal(),
equal: opts.Comparer.Equal,
comparer: opts.Comparer,
formatKey: opts.Comparer.FormatKey,
inputs: pc.inputs,
Expand Down Expand Up @@ -782,7 +782,7 @@ func newDeleteOnlyCompaction(
c := &compaction{
kind: compactionKindDeleteOnly,
cmp: opts.Comparer.Compare,
equal: opts.equal(),
equal: opts.Comparer.Equal,
comparer: opts.Comparer,
formatKey: opts.Comparer.FormatKey,
logger: opts.Logger,
Expand Down Expand Up @@ -873,7 +873,7 @@ func newFlush(
c := &compaction{
kind: compactionKindFlush,
cmp: opts.Comparer.Compare,
equal: opts.equal(),
equal: opts.Comparer.Equal,
comparer: opts.Comparer,
formatKey: opts.Comparer.FormatKey,
logger: opts.Logger,
Expand Down
96 changes: 68 additions & 28 deletions internal/base/comparer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,27 @@ import (
)

// Compare returns -1, 0, or +1 depending on whether a is 'less than', 'equal
// to' or 'greater than' b. The two arguments can only be 'equal' if their
// contents are exactly equal. Furthermore, the empty slice must be 'less than'
// any non-empty slice. Compare is used to compare user keys, such as those
// passed as arguments to the various DB methods, as well as those returned
// from Separator, Successor, and Split.
// to' or 'greater than' b. The empty slice must be 'less than' any non-empty
// slice.
//
// Compare is used to compare user keys, such as those passed as arguments to
// the various DB methods, as well as those returned from Separator, Successor,
// and Split.
type Compare func(a, b []byte) int

// Equal returns true if a and b are equivalent. For a given Compare,
// Equal(a,b) must return true iff Compare(a,b) returns zero, that is,
// Equal is a (potentially faster) specialization of Compare.
// Equal returns true if a and b are equivalent.
//
// For a given Compare, Equal(a,b)=true iff Compare(a,b)=0; that is, Equal is a
// (potentially faster) specialization of Compare.
type Equal func(a, b []byte) bool

// AbbreviatedKey returns a fixed length prefix of a user key such that AbbreviatedKey(a)
// < AbbreviatedKey(b) iff a < b and AbbreviatedKey(a) > AbbreviatedKey(b) iff a > b. If
// AbbreviatedKey(a) == AbbreviatedKey(b) an additional comparison is required to
// determine if the two keys are actually equal.
// AbbreviatedKey returns a fixed length prefix of a user key such that
//
// AbbreviatedKey(a) < AbbreviatedKey(b) implies a < b, and
// AbbreviatedKey(a) > AbbreviatedKey(b) implies a > b.
//
// If AbbreviatedKey(a) == AbbreviatedKey(b), an additional comparison is
// required to determine if the two keys are actually equal.
//
// This helps optimize indexed batch comparisons for cache locality. If a Split
// function is specified, AbbreviatedKey usually returns the first eight bytes
Expand All @@ -38,9 +43,9 @@ type AbbreviatedKey func(key []byte) uint64
// FormatKey returns a formatter for the user key.
type FormatKey func(key []byte) fmt.Formatter

// FormatValue returns a formatter for the user value. The key is also
// specified for the value formatter in order to support value formatting that
// is dependent on the key.
// FormatValue returns a formatter for the user value. The key is also specified
// for the value formatter in order to support value formatting that is
// dependent on the key.
type FormatValue func(key, value []byte) fmt.Formatter

// Separator is used to construct SSTable index blocks. A trivial implementation
Expand All @@ -67,7 +72,8 @@ type Separator func(dst, a, b []byte) []byte
type Successor func(dst, a []byte) []byte

// ImmediateSuccessor is invoked with a prefix key ([Split(a) == len(a)]) and
// returns the smallest key that is larger than the given prefix a.
// returns the smallest prefix key that is larger than the given prefix a.
//
// ImmediateSuccessor must return a prefix key k such that:
//
// Split(k) == len(k) and Compare(k, a) > 0
Expand Down Expand Up @@ -120,27 +126,61 @@ type Split func(a []byte) int
// Comparer defines a total ordering over the space of []byte keys: a 'less
// than' relationship.
type Comparer struct {
Compare Compare
Equal Equal
AbbreviatedKey AbbreviatedKey
FormatKey FormatKey
FormatValue FormatValue
Separator Separator
Split Split
Successor Successor
// These fields must always be specified.
Compare Compare
AbbreviatedKey AbbreviatedKey
Separator Separator
Successor Successor

// ImmediateSuccessor is required if range keys are used.
ImmediateSuccessor ImmediateSuccessor

// Equal defaults to using Compare() == 0 if it is not specified.
Equal Equal
// FormatKey defaults to the DefaultFormatter if it is not specified.
FormatKey FormatKey

// These fields are optional.
Split Split
FormatValue FormatValue

// Name is the name of the comparer.
//
// The Level-DB on-disk format stores the comparer name, and opening a
// database with a different comparer from the one it was created with
// will result in an error.
// The on-disk format stores the comparer name, and opening a database with a
// different comparer from the one it was created with will result in an
// error.
Name string
}

// EnsureDefaults ensures that all non-optional fields are set.
//
// If c is nil, returns DefaultComparer.
//
// If any fields need to be set, returns a modified copy of c.
func (c *Comparer) EnsureDefaults() *Comparer {
if c == nil {
return DefaultComparer
}
if c.Equal != nil && c.FormatKey != nil {
return c
}
n := &Comparer{}
*n = *c
if n.Equal == nil {
cmp := n.Compare
n.Equal = func(a, b []byte) bool {
return cmp(a, b) == 0
}
}
if n.FormatKey == nil {
n.FormatKey = DefaultFormatter
}
return n
}

// DefaultFormatter is the default implementation of user key formatting:
// non-ASCII data is formatted as escaped hexadecimal values.
var DefaultFormatter = func(key []byte) fmt.Formatter {
var DefaultFormatter FormatKey = func(key []byte) fmt.Formatter {
return FormatBytes(key)
}

Expand Down
2 changes: 1 addition & 1 deletion open.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func Open(dirname string, opts *Options) (db *DB, err error) {
dirname: dirname,
opts: opts,
cmp: opts.Comparer.Compare,
equal: opts.equal(),
equal: opts.Comparer.Equal,
merge: opts.Merger.Merge,
split: opts.Comparer.Split,
abbreviatedKey: opts.Comparer.AbbreviatedKey,
Expand Down
13 changes: 3 additions & 10 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,15 +1072,15 @@ func (o *Options) EnsureDefaults() *Options {
if o == nil {
o = &Options{}
}
o.Comparer = o.Comparer.EnsureDefaults()

if o.BytesPerSync <= 0 {
o.BytesPerSync = 512 << 10 // 512 KB
}
if o.Cleaner == nil {
o.Cleaner = DeleteCleaner{}
}
if o.Comparer == nil {
o.Comparer = DefaultComparer
}

if o.Experimental.DisableIngestAsFlushable == nil {
o.Experimental.DisableIngestAsFlushable = func() bool { return false }
}
Expand Down Expand Up @@ -1232,13 +1232,6 @@ func (o *Options) AddEventListener(l EventListener) {
o.EventListener = &l
}

func (o *Options) equal() Equal {
if o.Comparer.Equal == nil {
return bytes.Equal
}
return o.Comparer.Equal
}

// initMaps initializes the Comparers, Filters, and Mergers maps.
func (o *Options) initMaps() {
for i := range o.Levels {
Expand Down

0 comments on commit 306ea71

Please sign in to comment.