141 lines
4.4 KiB
Go
141 lines
4.4 KiB
Go
// Copyright 2021 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 pebble
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/cockroachdb/pebble/internal/arenaskl"
|
|
"github.com/cockroachdb/pebble/internal/base"
|
|
"github.com/cockroachdb/pebble/internal/keyspan"
|
|
"github.com/cockroachdb/pebble/internal/rangekey"
|
|
)
|
|
|
|
const rangeKeyArenaSize = 1 << 20
|
|
|
|
// RangeKeysArena is an in-memory arena in which range keys are stored.
|
|
//
|
|
// This is a temporary type that will eventually be removed.
|
|
type RangeKeysArena struct {
|
|
once sync.Once
|
|
skl arenaskl.Skiplist
|
|
arena *arenaskl.Arena
|
|
fragCache keySpanCache
|
|
}
|
|
|
|
// applyFlushedRangeKeys is a temporary hack to support in-memory only range
|
|
// keys. We use much of the same code that we will use for the memtable, but we
|
|
// use a separate arena that exists beyond the lifetime of any individual
|
|
// memtable. For as long as we're relying on this hack, a single *pebble.DB may
|
|
// only store as many range keys as fit in this arena.
|
|
//
|
|
// TODO(jackson): Remove applyFlushedRangeKeys when range keys are persisted.
|
|
func (d *DB) applyFlushedRangeKeys(flushable []*flushableEntry) error {
|
|
var added uint32
|
|
for i := 0; i < len(flushable); i++ {
|
|
iter := flushable[i].newRangeKeyIter(nil)
|
|
if iter == nil {
|
|
continue
|
|
}
|
|
d.maybeInitializeRangeKeys()
|
|
|
|
for s := iter.First(); s.Valid(); s = iter.Next() {
|
|
// flushable.newRangeKeyIter provides a FragmentIterator, which
|
|
// iterates over parsed keyspan.Spans.
|
|
//
|
|
// While we're faking a flush, we just want to write the original
|
|
// key into the global arena, so we need to re-encode the span's
|
|
// keys into internal key-value pairs.
|
|
//
|
|
// This awkward recombination will be removed when flushes implement
|
|
// range-key logic, coalescing range keys and constructing internal
|
|
// keys.
|
|
err := rangekey.Encode(s, func(k base.InternalKey, v []byte) error {
|
|
err := d.rangeKeys.skl.Add(k, v)
|
|
switch {
|
|
case err == nil:
|
|
added++
|
|
return nil
|
|
case errors.Is(err, arenaskl.ErrRecordExists):
|
|
// It's possible that we'll try to add a key to the arena twice
|
|
// during metamorphic tests that reset the synced state. Ignore.
|
|
// When range keys are actually flushed to stable storage, this
|
|
// will go away.
|
|
return nil
|
|
default:
|
|
// err != nil
|
|
return err
|
|
}
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if added > 0 {
|
|
d.rangeKeys.fragCache.invalidate(added)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *DB) maybeInitializeRangeKeys() {
|
|
// Lazily construct the global range key arena, so that tests that
|
|
// don't use range keys don't need to allocate this long-lived
|
|
// buffer.
|
|
d.rangeKeys.once.Do(func() {
|
|
arenaBuf := make([]byte, rangeKeyArenaSize)
|
|
d.rangeKeys.arena = arenaskl.NewArena(arenaBuf)
|
|
d.rangeKeys.skl.Reset(d.rangeKeys.arena, d.cmp)
|
|
d.rangeKeys.fragCache = keySpanCache{
|
|
cmp: d.cmp,
|
|
formatKey: d.opts.Comparer.FormatKey,
|
|
skl: &d.rangeKeys.skl,
|
|
constructSpan: rangekey.Decode,
|
|
}
|
|
})
|
|
}
|
|
|
|
func (d *DB) newRangeKeyIter(
|
|
it *iteratorRangeKeyState, seqNum uint64, batch *Batch, readState *readState, opts *IterOptions,
|
|
) keyspan.FragmentIterator {
|
|
d.maybeInitializeRangeKeys()
|
|
|
|
// TODO(jackson): Preallocate iters, mergingIter, rangeKeyIter in a
|
|
// structure analogous to iterAlloc.
|
|
var iters []keyspan.FragmentIterator
|
|
|
|
// If there's an indexed batch with range keys, include it.
|
|
if batch != nil {
|
|
if rki := batch.newRangeKeyIter(opts); rki != nil {
|
|
iters = append(iters, rki)
|
|
}
|
|
}
|
|
|
|
// Next are the flushables: memtables and large batches.
|
|
for i := len(readState.memtables) - 1; i >= 0; i-- {
|
|
mem := readState.memtables[i]
|
|
// We only need to read from memtables which contain sequence numbers older
|
|
// than seqNum.
|
|
if logSeqNum := mem.logSeqNum; logSeqNum >= seqNum {
|
|
continue
|
|
}
|
|
if rki := mem.newRangeKeyIter(opts); rki != nil {
|
|
iters = append(iters, rki)
|
|
}
|
|
}
|
|
|
|
// For now while range keys are not fully integrated into Pebble, all range
|
|
// keys ever written to the DB are persisted in the d.rangeKeys arena.
|
|
frags := d.rangeKeys.fragCache.get()
|
|
if len(frags) > 0 {
|
|
iters = append(iters, keyspan.NewIter(d.cmp, frags))
|
|
}
|
|
it.rangeKeyIter = rangekey.InitUserIteration(
|
|
d.cmp, seqNum, &it.alloc.merging, &it.alloc.defraging, iters...,
|
|
)
|
|
return it.rangeKeyIter
|
|
}
|