Upgrade versions of vendored libs
This commit is contained in:
1
vendor/github.com/cockroachdb/errors/.travis.yml
generated
vendored
1
vendor/github.com/cockroachdb/errors/.travis.yml
generated
vendored
@@ -4,3 +4,4 @@ go:
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- 1.15.x
|
||||
- 1.16.x
|
||||
|
||||
45
vendor/github.com/cockroachdb/errors/Makefile.update-protos
generated
vendored
45
vendor/github.com/cockroachdb/errors/Makefile.update-protos
generated
vendored
@@ -6,13 +6,16 @@
|
||||
# go get github.com/gogo/protobuf/types
|
||||
# go get github.com/gogo/protobuf/protoc-gen-gogo
|
||||
#
|
||||
# Note: as of 2020-11-01, it is not (yet) possible to use gogoproto
|
||||
# v1.3.x to generate .pb.go files so that they are compatible with
|
||||
# CockroachDB. This is because CockroachDB hardcodes gogoproto at
|
||||
# version v1.2, and thus does not provide v1.3's
|
||||
# proto.GoGoProtoPackageIsVersion3.
|
||||
# Note: as of 2021-04-13, we like to use a custom protoc-gen-gogo
|
||||
# with additional options, to stabilize the marshalled
|
||||
# encoding of objects (so that they are deterministic
|
||||
# across marshal/unmarshal cycles) and reduce the memory footprint
|
||||
# of objects:
|
||||
#
|
||||
# For details, see https://github.com/cockroachdb/cockroach/issues/56378
|
||||
# vanity.TurnOnStable_MarshalerAll,
|
||||
# vanity.TurnOffGoUnrecognizedAll,
|
||||
# vanity.TurnOffGoUnkeyedAll,
|
||||
# vanity.TurnOffGoSizecacheAll,
|
||||
#
|
||||
# Until this is resolved, the "go get" commands above are not
|
||||
# adequate; instead:
|
||||
@@ -23,21 +26,33 @@
|
||||
#
|
||||
# 2. run `make -f Makefile.update-protos` with this PATH active.
|
||||
|
||||
PROTOS := $(wildcard errorspb/*.proto)
|
||||
export SHELL := env PWD=$(CURDIR) bash
|
||||
|
||||
PROTOS := $(wildcard \
|
||||
errbase/internal/*.proto \
|
||||
errorspb/*.proto \
|
||||
extgrpc/*.proto \
|
||||
exthttp/*.proto \
|
||||
grpc/*.proto \
|
||||
markers/internal/*.proto \
|
||||
)
|
||||
GO_SOURCES = $(PROTOS:.proto=.pb.go)
|
||||
|
||||
SED = sed
|
||||
SED_INPLACE := $(shell $(SED) --version 2>&1 | grep -q GNU && echo -i || echo "-i ''")
|
||||
|
||||
all: $(PROTOS)
|
||||
protoc \
|
||||
-I$$GOPATH/src/ \
|
||||
-I$$GOPATH/src/github.com \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf/protobuf \
|
||||
--gogoroach_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,plugins=grpc,import_prefix=:. \
|
||||
$(PROTOS:%=$(GOPATH)/src/github.com/cockroachdb/errors/%)
|
||||
mv -f github.com/cockroachdb/errors/errorspb/*.pb.go errorspb/
|
||||
set -e; for dir in $(sort $(dir $(PROTOS))); do \
|
||||
protoc \
|
||||
-I. \
|
||||
-I$$GOPATH/src/ \
|
||||
-I$$GOPATH/src/github.com \
|
||||
-I$$GOPATH/src/github.com/cockroachdb/errors \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf \
|
||||
-I$$GOPATH/src/github.com/gogo/protobuf/protobuf \
|
||||
--gogoroach_out=Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types,plugins=grpc,import_prefix=:. \
|
||||
$$dir/*.proto; \
|
||||
done
|
||||
$(SED) $(SED_INPLACE) -E \
|
||||
-e '/import _ /d' \
|
||||
-e 's!import (fmt|math) "github.com/(fmt|math)"! !g' \
|
||||
|
||||
10
vendor/github.com/cockroachdb/errors/README.md
generated
vendored
10
vendor/github.com/cockroachdb/errors/README.md
generated
vendored
@@ -11,7 +11,8 @@ automatically formats error details and strips them of PII.
|
||||
|
||||
See also [the design RFC](https://github.com/cockroachdb/cockroach/blob/master/docs/RFCS/20190318_error_handling.md).
|
||||
|
||||
[](https://travis-ci.org/cockroachdb/errors)
|
||||

|
||||
[](https://pkg.go.dev/github.com/cockroachdb/errors)
|
||||
|
||||
Table of contents:
|
||||
|
||||
@@ -422,10 +423,10 @@ func myLeafDecoder(_ string, details []string, _ proto.Message) error {
|
||||
|
||||
(For an example, see the `withTelemetry` type in [`telemetry/with_telemetry.go`](telemetry/with_telemetry.go).)
|
||||
|
||||
**The only case where you need a custom encoder is when your error
|
||||
__The only case where you need a custom encoder is when your error
|
||||
type contains some fields that are not reflected in the error message
|
||||
(so you can't extract them back from there), and are not PII-free and
|
||||
thus cannot be reported as "safe details".**
|
||||
thus cannot be reported as "safe details".__
|
||||
|
||||
To take inspiration from examples, see the following types in the
|
||||
library that need a custom encoder:
|
||||
@@ -538,6 +539,9 @@ Example use:
|
||||
|
||||
## API (not constructing error objects)
|
||||
|
||||
The following is a summary of the non-constructor API functions, grouped by category.
|
||||
Detailed documentation can be found at: https://pkg.go.dev/github.com/cockroachdb/errors
|
||||
|
||||
```go
|
||||
// Access causes.
|
||||
func UnwrapAll(err error) error
|
||||
|
||||
67
vendor/github.com/cockroachdb/errors/barriers/barriers.go
generated
vendored
67
vendor/github.com/cockroachdb/errors/barriers/barriers.go
generated
vendored
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -36,7 +37,7 @@ func Handled(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return HandledWithMessage(err, err.Error())
|
||||
return HandledWithSafeMessage(err, redact.Sprint(err))
|
||||
}
|
||||
|
||||
// HandledWithMessage is like Handled except the message is overridden.
|
||||
@@ -46,7 +47,14 @@ func HandledWithMessage(err error, msg string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &barrierError{maskedErr: err, msg: msg}
|
||||
return HandledWithSafeMessage(err, redact.Sprint(msg))
|
||||
}
|
||||
|
||||
// HandledWithSafeMessage is like Handled except the message is overridden.
|
||||
// This can be used e.g. to hide message details or to prevent
|
||||
// downstream code to make assertions on the message's contents.
|
||||
func HandledWithSafeMessage(err error, msg redact.RedactableString) error {
|
||||
return &barrierErr{maskedErr: err, smsg: msg}
|
||||
}
|
||||
|
||||
// HandledWithMessagef is like HandledWithMessagef except the message
|
||||
@@ -55,47 +63,48 @@ func HandledWithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &barrierError{maskedErr: err, msg: fmt.Sprintf(format, args...)}
|
||||
return &barrierErr{maskedErr: err, smsg: redact.Sprintf(format, args...)}
|
||||
}
|
||||
|
||||
// barrierError is a leaf error type. It encapsulates a chain of
|
||||
// barrierErr is a leaf error type. It encapsulates a chain of
|
||||
// original causes, but these causes are hidden so that they inhibit
|
||||
// matching via Is() and the Cause()/Unwrap() recursions.
|
||||
type barrierError struct {
|
||||
type barrierErr struct {
|
||||
// Message for the barrier itself.
|
||||
// In the common case, the message from the masked error
|
||||
// is used as-is (see Handled() above) however it is
|
||||
// useful to cache it here since the masked error may
|
||||
// have a long chain of wrappers and its Error() call
|
||||
// may be expensive.
|
||||
msg string
|
||||
smsg redact.RedactableString
|
||||
// Masked error chain.
|
||||
maskedErr error
|
||||
}
|
||||
|
||||
var _ error = (*barrierError)(nil)
|
||||
var _ errbase.SafeDetailer = (*barrierError)(nil)
|
||||
var _ errbase.SafeFormatter = (*barrierError)(nil)
|
||||
var _ fmt.Formatter = (*barrierError)(nil)
|
||||
var _ error = (*barrierErr)(nil)
|
||||
var _ errbase.SafeDetailer = (*barrierErr)(nil)
|
||||
var _ errbase.SafeFormatter = (*barrierErr)(nil)
|
||||
var _ fmt.Formatter = (*barrierErr)(nil)
|
||||
|
||||
// barrierError is an error.
|
||||
func (e *barrierError) Error() string { return e.msg }
|
||||
// barrierErr is an error.
|
||||
func (e *barrierErr) Error() string { return e.smsg.StripMarkers() }
|
||||
|
||||
// SafeDetails reports the PII-free details from the masked error.
|
||||
func (e *barrierError) SafeDetails() []string {
|
||||
func (e *barrierErr) SafeDetails() []string {
|
||||
var details []string
|
||||
for err := e.maskedErr; err != nil; err = errbase.UnwrapOnce(err) {
|
||||
sd := errbase.GetSafeDetails(err)
|
||||
details = sd.Fill(details)
|
||||
}
|
||||
details = append(details, redact.Sprintf("masked error: %+v", e.maskedErr).Redact().StripMarkers())
|
||||
return details
|
||||
}
|
||||
|
||||
// Printing a barrier reveals the details.
|
||||
func (e *barrierError) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
|
||||
func (e *barrierErr) Format(s fmt.State, verb rune) { errbase.FormatError(e, s, verb) }
|
||||
|
||||
func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
|
||||
p.Print(e.msg)
|
||||
func (e *barrierErr) SafeFormatError(p errbase.Printer) (next error) {
|
||||
p.Print(e.smsg)
|
||||
if p.Detail() {
|
||||
p.Printf("-- cause hidden behind barrier\n%+v", e.maskedErr)
|
||||
}
|
||||
@@ -106,19 +115,37 @@ func (e *barrierError) SafeFormatError(p errbase.Printer) (next error) {
|
||||
func encodeBarrier(
|
||||
ctx context.Context, err error,
|
||||
) (msg string, details []string, payload proto.Message) {
|
||||
e := err.(*barrierError)
|
||||
e := err.(*barrierErr)
|
||||
enc := errbase.EncodeError(ctx, e.maskedErr)
|
||||
return e.msg, e.SafeDetails(), &enc
|
||||
return string(e.smsg), e.SafeDetails(), &enc
|
||||
}
|
||||
|
||||
// A barrier error is decoded exactly.
|
||||
func decodeBarrier(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
||||
enc := payload.(*errbase.EncodedError)
|
||||
return &barrierError{msg: msg, maskedErr: errbase.DecodeError(ctx, *enc)}
|
||||
return &barrierErr{smsg: redact.RedactableString(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
|
||||
}
|
||||
|
||||
// Previous versions of barrier errors.
|
||||
func decodeBarrierPrev(ctx context.Context, msg string, _ []string, payload proto.Message) error {
|
||||
enc := payload.(*errbase.EncodedError)
|
||||
return &barrierErr{smsg: redact.Sprint(msg), maskedErr: errbase.DecodeError(ctx, *enc)}
|
||||
}
|
||||
|
||||
// barrierError is the "old" type name of barrierErr. We use a new
|
||||
// name now to ensure a different decode function is used when
|
||||
// importing barriers from the previous structure, where the
|
||||
// message is not redactable.
|
||||
type barrierError struct {
|
||||
msg string
|
||||
maskedErr error
|
||||
}
|
||||
|
||||
func (b *barrierError) Error() string { return "" }
|
||||
|
||||
func init() {
|
||||
tn := errbase.GetTypeKey((*barrierError)(nil))
|
||||
errbase.RegisterLeafDecoder(errbase.GetTypeKey((*barrierError)(nil)), decodeBarrierPrev)
|
||||
tn := errbase.GetTypeKey((*barrierErr)(nil))
|
||||
errbase.RegisterLeafDecoder(tn, decodeBarrier)
|
||||
errbase.RegisterLeafEncoder(tn, encodeBarrier)
|
||||
}
|
||||
|
||||
9
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
9
vendor/github.com/cockroachdb/errors/errbase/adapters.go
generated
vendored
@@ -37,6 +37,11 @@ func decodeErrorString(_ context.Context, msg string, _ []string, _ proto.Messag
|
||||
return goErr.New(msg)
|
||||
}
|
||||
|
||||
// context.DeadlineExceeded uses a custom type.
|
||||
func decodeDeadlineExceeded(_ context.Context, _ string, _ []string, _ proto.Message) error {
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
|
||||
// errors.fundamental from github.com/pkg/errors cannot be encoded
|
||||
// exactly because it includes a non-serializable stack trace
|
||||
// object. In order to work with it, we encode it by dumping
|
||||
@@ -183,6 +188,8 @@ func init() {
|
||||
baseErr := goErr.New("")
|
||||
RegisterLeafDecoder(GetTypeKey(baseErr), decodeErrorString)
|
||||
|
||||
RegisterLeafDecoder(GetTypeKey(context.DeadlineExceeded), decodeDeadlineExceeded)
|
||||
|
||||
pkgE := pkgErr.New("")
|
||||
RegisterLeafEncoder(GetTypeKey(pkgE), encodePkgFundamental)
|
||||
|
||||
@@ -191,9 +198,11 @@ func init() {
|
||||
ws := pkgErr.WithStack(baseErr)
|
||||
RegisterWrapperEncoder(GetTypeKey(ws), encodePkgWithStack)
|
||||
|
||||
registerOsPathErrorMigration() // Needed for Go 1.16.
|
||||
pKey := GetTypeKey(&os.PathError{})
|
||||
RegisterWrapperEncoder(pKey, encodePathError)
|
||||
RegisterWrapperDecoder(pKey, decodePathError)
|
||||
|
||||
pKey = GetTypeKey(&os.LinkError{})
|
||||
RegisterWrapperEncoder(pKey, encodeLinkError)
|
||||
RegisterWrapperDecoder(pKey, decodeLinkError)
|
||||
|
||||
2
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/errbase/decode.go
generated
vendored
@@ -23,6 +23,8 @@ import (
|
||||
)
|
||||
|
||||
// DecodeError decodes an error.
|
||||
//
|
||||
// Can only be called if the EncodedError is set (see IsSet()).
|
||||
func DecodeError(ctx context.Context, enc EncodedError) error {
|
||||
if w := enc.GetWrapper(); w != nil {
|
||||
return decodeWrapper(ctx, w)
|
||||
|
||||
116
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
116
vendor/github.com/cockroachdb/errors/errbase/format_error.go
generated
vendored
@@ -349,66 +349,76 @@ func (s *state) formatRecursive(err error, isOutermost, withDetail bool) {
|
||||
|
||||
bufIsRedactable := false
|
||||
|
||||
printDone := false
|
||||
for _, fn := range specialCases {
|
||||
if handled, desiredShortening := fn(err, (*safePrinter)(s), cause == nil /* leaf */); handled {
|
||||
printDone = true
|
||||
bufIsRedactable = true
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
switch v := err.(type) {
|
||||
case SafeFormatter:
|
||||
bufIsRedactable = true
|
||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !printDone {
|
||||
switch v := err.(type) {
|
||||
case SafeFormatter:
|
||||
bufIsRedactable = true
|
||||
desiredShortening := v.SafeFormatError((*safePrinter)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
}
|
||||
|
||||
case Formatter:
|
||||
desiredShortening := v.FormatError((*printer)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
case Formatter:
|
||||
desiredShortening := v.FormatError((*printer)(s))
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
}
|
||||
|
||||
case fmt.Formatter:
|
||||
// We can only use a fmt.Formatter when both the following
|
||||
// conditions are true:
|
||||
// - when it is the leaf error, because a fmt.Formatter
|
||||
// on a wrapper also recurses.
|
||||
// - when it is not the outermost wrapper, because
|
||||
// the Format() method is likely to be calling FormatError()
|
||||
// to do its job and we want to avoid an infinite recursion.
|
||||
if !isOutermost && cause == nil {
|
||||
v.Format(s, 'v')
|
||||
if st, ok := err.(StackTraceProvider); ok {
|
||||
// This is likely a leaf error from github/pkg/errors.
|
||||
// The thing probably printed its stack trace on its own.
|
||||
seenTrace = true
|
||||
// We'll subsequently simplify stack traces in wrappers.
|
||||
s.lastStack = st.StackTrace()
|
||||
}
|
||||
} else {
|
||||
s.formatSimple(err, cause)
|
||||
case fmt.Formatter:
|
||||
// We can only use a fmt.Formatter when both the following
|
||||
// conditions are true:
|
||||
// - when it is the leaf error, because a fmt.Formatter
|
||||
// on a wrapper also recurses.
|
||||
// - when it is not the outermost wrapper, because
|
||||
// the Format() method is likely to be calling FormatError()
|
||||
// to do its job and we want to avoid an infinite recursion.
|
||||
if !isOutermost && cause == nil {
|
||||
v.Format(s, 'v')
|
||||
if st, ok := err.(StackTraceProvider); ok {
|
||||
// This is likely a leaf error from github/pkg/errors.
|
||||
// The thing probably printed its stack trace on its own.
|
||||
seenTrace = true
|
||||
// We'll subsequently simplify stack traces in wrappers.
|
||||
s.lastStack = st.StackTrace()
|
||||
}
|
||||
} else {
|
||||
s.formatSimple(err, cause)
|
||||
}
|
||||
|
||||
default:
|
||||
default:
|
||||
// Handle the special case overrides for context.Canceled,
|
||||
// os.PathError, etc for which we know how to extract some safe
|
||||
// strings.
|
||||
//
|
||||
// We need to do this in the `default` branch, instead of doing
|
||||
// this above the switch, because the special handler could call a
|
||||
// .Error() that delegates its implementation to fmt.Formatter,
|
||||
// errors.Safeformatter or errors.Formattable, which brings us
|
||||
// back to this method in a call cycle. So we need to handle the
|
||||
// various interfaces first.
|
||||
printDone := false
|
||||
for _, fn := range specialCases {
|
||||
if handled, desiredShortening := fn(err, (*safePrinter)(s), cause == nil /* leaf */); handled {
|
||||
printDone = true
|
||||
bufIsRedactable = true
|
||||
if desiredShortening == nil {
|
||||
// The error wants to elide the short messages from inner
|
||||
// causes. Do it.
|
||||
for i := range s.entries {
|
||||
s.entries[i].elideShort = true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if !printDone {
|
||||
// If the error did not implement errors.Formatter nor
|
||||
// fmt.Formatter, but it is a wrapper, still attempt best effort:
|
||||
// print what we can at this level.
|
||||
|
||||
24
vendor/github.com/cockroachdb/errors/errbase/oserror_go116.go
generated
vendored
Normal file
24
vendor/github.com/cockroachdb/errors/errbase/oserror_go116.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 The Cockroach Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
// +build go1.16
|
||||
|
||||
package errbase
|
||||
|
||||
import "io/fs"
|
||||
|
||||
func registerOsPathErrorMigration() {
|
||||
// The os.PathError type was migrated to io.fs.PathError in Go 1.16.
|
||||
RegisterTypeMigration("os", "*os.PathError", &fs.PathError{})
|
||||
}
|
||||
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
19
vendor/github.com/cockroachdb/errors/errbase/oserror_pre116.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2021 The Cockroach Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
// +build !go1.16
|
||||
|
||||
package errbase
|
||||
|
||||
func registerOsPathErrorMigration() {}
|
||||
21
vendor/github.com/cockroachdb/errors/errorspb/errors.go
generated
vendored
Normal file
21
vendor/github.com/cockroachdb/errors/errorspb/errors.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2021 The Cockroach Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package errorspb
|
||||
|
||||
// IsSet returns true if the EncodedError contains an error, or false if it is
|
||||
// empty.
|
||||
func (m *EncodedError) IsSet() bool {
|
||||
return m.Error != nil
|
||||
}
|
||||
840
vendor/github.com/cockroachdb/errors/errorspb/errors.pb.go
generated
vendored
840
vendor/github.com/cockroachdb/errors/errorspb/errors.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
153
vendor/github.com/cockroachdb/errors/errorspb/hintdetail.pb.go
generated
vendored
153
vendor/github.com/cockroachdb/errors/errorspb/hintdetail.pb.go
generated
vendored
@@ -1,13 +1,15 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: github.com/cockroachdb/errors/errorspb/hintdetail.proto
|
||||
// source: errorspb/hintdetail.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type StringPayload struct {
|
||||
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
|
||||
@@ -28,21 +30,21 @@ func (m *StringPayload) Reset() { *m = StringPayload{} }
|
||||
func (m *StringPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*StringPayload) ProtoMessage() {}
|
||||
func (*StringPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_hintdetail_429641491537a364, []int{0}
|
||||
return fileDescriptor_d99b88ec9cc4bc22, []int{0}
|
||||
}
|
||||
func (m *StringPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *StringPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *StringPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StringPayload.Merge(dst, src)
|
||||
func (m *StringPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StringPayload.Merge(m, src)
|
||||
}
|
||||
func (m *StringPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -56,10 +58,26 @@ var xxx_messageInfo_StringPayload proto.InternalMessageInfo
|
||||
func init() {
|
||||
proto.RegisterType((*StringPayload)(nil), "cockroach.errorspb.StringPayload")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("errorspb/hintdetail.proto", fileDescriptor_d99b88ec9cc4bc22) }
|
||||
|
||||
var fileDescriptor_d99b88ec9cc4bc22 = []byte{
|
||||
// 143 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0x2d, 0x2a, 0xca,
|
||||
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0xcf, 0xc8, 0xcc, 0x2b, 0x49, 0x49, 0x2d, 0x49, 0xcc, 0xcc, 0xd1,
|
||||
0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||
0xd0, 0x83, 0x29, 0x52, 0x52, 0xe4, 0xe2, 0x0d, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x0f, 0x48, 0xac,
|
||||
0xcc, 0xc9, 0x4f, 0x4c, 0x11, 0x12, 0xe0, 0x62, 0xce, 0x2d, 0x4e, 0x97, 0x60, 0x54, 0x60, 0xd4,
|
||||
0xe0, 0x0c, 0x02, 0x31, 0x9d, 0xb4, 0x4e, 0x3c, 0x94, 0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2,
|
||||
0x23, 0x39, 0xc6, 0x1b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1,
|
||||
0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x38, 0x60, 0xc6, 0x25, 0xb1, 0x81, 0x6d,
|
||||
0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xe2, 0xbd, 0x3a, 0x3e, 0x86, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *StringPayload) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,27 +85,35 @@ func (m *StringPayload) Marshal() (dAtA []byte, err error) {
|
||||
}
|
||||
|
||||
func (m *StringPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *StringPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Msg) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i -= len(m.Msg)
|
||||
copy(dAtA[i:], m.Msg)
|
||||
i = encodeVarintHintdetail(dAtA, i, uint64(len(m.Msg)))
|
||||
i += copy(dAtA[i:], m.Msg)
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return i, nil
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintHintdetail(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovHintdetail(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *StringPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -103,14 +129,7 @@ func (m *StringPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovHintdetail(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozHintdetail(x uint64) (n int) {
|
||||
return sovHintdetail(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -130,7 +149,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -158,7 +177,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -168,6 +187,9 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -179,7 +201,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthHintdetail
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -197,6 +219,7 @@ func (m *StringPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -228,10 +251,8 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -248,72 +269,34 @@ func skipHintdetail(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthHintdetail
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowHintdetail
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipHintdetail(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupHintdetail
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthHintdetail
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowHintdetail = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthHintdetail = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowHintdetail = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupHintdetail = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/hintdetail.proto", fileDescriptor_hintdetail_429641491537a364)
|
||||
}
|
||||
|
||||
var fileDescriptor_hintdetail_429641491537a364 = []byte{
|
||||
// 161 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4f, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x19, 0x99,
|
||||
0x79, 0x25, 0x29, 0xa9, 0x25, 0x89, 0x99, 0x39, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42,
|
||||
0x70, 0xd5, 0x7a, 0x30, 0x45, 0x4a, 0x8a, 0x5c, 0xbc, 0xc1, 0x25, 0x45, 0x99, 0x79, 0xe9, 0x01,
|
||||
0x89, 0x95, 0x39, 0xf9, 0x89, 0x29, 0x42, 0x02, 0x5c, 0xcc, 0xb9, 0xc5, 0xe9, 0x12, 0x8c, 0x0a,
|
||||
0x8c, 0x1a, 0x9c, 0x41, 0x20, 0xa6, 0x93, 0xd6, 0x89, 0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31,
|
||||
0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb,
|
||||
0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x14, 0x07, 0xcc, 0xb8, 0x24, 0x36,
|
||||
0xb0, 0x4d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x71, 0x36, 0x88, 0xa4, 0x00, 0x00,
|
||||
0x00,
|
||||
}
|
||||
|
||||
190
vendor/github.com/cockroachdb/errors/errorspb/markers.pb.go
generated
vendored
190
vendor/github.com/cockroachdb/errors/errorspb/markers.pb.go
generated
vendored
@@ -1,13 +1,16 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: github.com/cockroachdb/errors/errorspb/markers.proto
|
||||
// source: errorspb/markers.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// MarkPayload is the error payload for a forced marker.
|
||||
// See errors/markers/markers.go and the RFC on
|
||||
@@ -32,21 +35,21 @@ func (m *MarkPayload) Reset() { *m = MarkPayload{} }
|
||||
func (m *MarkPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*MarkPayload) ProtoMessage() {}
|
||||
func (*MarkPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_markers_2a35e57f2b6b5666, []int{0}
|
||||
return fileDescriptor_84c3fd24ba37816d, []int{0}
|
||||
}
|
||||
func (m *MarkPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *MarkPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *MarkPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_MarkPayload.Merge(dst, src)
|
||||
func (m *MarkPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_MarkPayload.Merge(m, src)
|
||||
}
|
||||
func (m *MarkPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -60,10 +63,30 @@ var xxx_messageInfo_MarkPayload proto.InternalMessageInfo
|
||||
func init() {
|
||||
proto.RegisterType((*MarkPayload)(nil), "cockroach.errorspb.MarkPayload")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("errorspb/markers.proto", fileDescriptor_84c3fd24ba37816d) }
|
||||
|
||||
var fileDescriptor_84c3fd24ba37816d = []byte{
|
||||
// 198 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4b, 0x2d, 0x2a, 0xca,
|
||||
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0xcf, 0x4d, 0x2c, 0xca, 0x4e, 0x2d, 0x2a, 0xd6, 0x2b, 0x28, 0xca,
|
||||
0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0xa9,
|
||||
0x90, 0x12, 0x85, 0xab, 0x85, 0x30, 0x20, 0x4a, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x4c,
|
||||
0x7d, 0x10, 0x0b, 0x22, 0xaa, 0x14, 0xc7, 0xc5, 0xed, 0x9b, 0x58, 0x94, 0x1d, 0x90, 0x58, 0x99,
|
||||
0x93, 0x9f, 0x98, 0x22, 0x24, 0xc0, 0xc5, 0x9c, 0x5b, 0x9c, 0x2e, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1,
|
||||
0x19, 0x04, 0x62, 0x0a, 0xd9, 0x72, 0xb1, 0x96, 0x54, 0x16, 0xa4, 0x16, 0x4b, 0x30, 0x29, 0x30,
|
||||
0x6b, 0x70, 0x1b, 0x29, 0xea, 0x61, 0xda, 0xa8, 0xe7, 0x0a, 0x62, 0x84, 0x54, 0x16, 0xa4, 0x82,
|
||||
0x8c, 0x72, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x08, 0xa2, 0xcb, 0x49, 0xeb, 0xc4, 0x43, 0x39,
|
||||
0x86, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0xbc, 0xf1, 0x48, 0x8e, 0xf1, 0xc1, 0x23,
|
||||
0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x8a,
|
||||
0x03, 0x66, 0x4c, 0x12, 0x1b, 0xd8, 0x49, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x18, 0x43,
|
||||
0x45, 0xef, 0xed, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *MarkPayload) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,39 +94,49 @@ func (m *MarkPayload) Marshal() (dAtA []byte, err error) {
|
||||
}
|
||||
|
||||
func (m *MarkPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *MarkPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Msg) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintMarkers(dAtA, i, uint64(len(m.Msg)))
|
||||
i += copy(dAtA[i:], m.Msg)
|
||||
}
|
||||
if len(m.Types) > 0 {
|
||||
for _, msg := range m.Types {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintMarkers(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Types[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintMarkers(dAtA, i, uint64(size))
|
||||
}
|
||||
i += n
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
if len(m.Msg) > 0 {
|
||||
i -= len(m.Msg)
|
||||
copy(dAtA[i:], m.Msg)
|
||||
i = encodeVarintMarkers(dAtA, i, uint64(len(m.Msg)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintMarkers(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovMarkers(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *MarkPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -125,14 +158,7 @@ func (m *MarkPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovMarkers(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozMarkers(x uint64) (n int) {
|
||||
return sovMarkers(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -152,7 +178,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -180,7 +206,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -190,6 +216,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -209,7 +238,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -218,6 +247,9 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -232,7 +264,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthMarkers
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -250,6 +282,7 @@ func (m *MarkPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -281,10 +314,8 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -301,75 +332,34 @@ func skipMarkers(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthMarkers
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowMarkers
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipMarkers(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupMarkers
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthMarkers
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowMarkers = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthMarkers = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowMarkers = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupMarkers = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/markers.proto", fileDescriptor_markers_2a35e57f2b6b5666)
|
||||
}
|
||||
|
||||
var fileDescriptor_markers_2a35e57f2b6b5666 = []byte{
|
||||
// 216 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x49, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0xb9, 0x89,
|
||||
0x45, 0xd9, 0xa9, 0x45, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0xa5, 0x7a,
|
||||
0x30, 0x15, 0x52, 0xc6, 0x44, 0x9a, 0x04, 0x61, 0x40, 0x0c, 0x92, 0x12, 0x49, 0xcf, 0x4f, 0xcf,
|
||||
0x07, 0x33, 0xf5, 0x41, 0x2c, 0x88, 0xa8, 0x52, 0x1c, 0x17, 0xb7, 0x6f, 0x62, 0x51, 0x76, 0x40,
|
||||
0x62, 0x65, 0x4e, 0x7e, 0x62, 0x8a, 0x90, 0x00, 0x17, 0x73, 0x6e, 0x71, 0xba, 0x04, 0xa3, 0x02,
|
||||
0xa3, 0x06, 0x67, 0x10, 0x88, 0x29, 0x64, 0xcb, 0xc5, 0x5a, 0x52, 0x59, 0x90, 0x5a, 0x2c, 0xc1,
|
||||
0xa4, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa8, 0x87, 0xe9, 0x1e, 0x3d, 0x57, 0x10, 0x23, 0xa4, 0xb2,
|
||||
0x20, 0x15, 0x64, 0x94, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x10, 0x5d, 0x4e, 0x5a, 0x27,
|
||||
0x1e, 0xca, 0x31, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x8d, 0x47, 0x72, 0x8c,
|
||||
0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72,
|
||||
0x0c, 0x51, 0x1c, 0x30, 0x63, 0x92, 0xd8, 0xc0, 0x4e, 0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff,
|
||||
0xdc, 0x2d, 0x59, 0x6b, 0x29, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
2
vendor/github.com/cockroachdb/errors/errorspb/markers.proto
generated
vendored
2
vendor/github.com/cockroachdb/errors/errorspb/markers.proto
generated
vendored
@@ -2,7 +2,7 @@ syntax = "proto3";
|
||||
package cockroach.errorspb;
|
||||
option go_package = "errorspb";
|
||||
|
||||
import "github.com/cockroachdb/errors/errorspb/errors.proto";
|
||||
import "errorspb/errors.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
// MarkPayload is the error payload for a forced marker.
|
||||
|
||||
225
vendor/github.com/cockroachdb/errors/errorspb/tags.pb.go
generated
vendored
225
vendor/github.com/cockroachdb/errors/errorspb/tags.pb.go
generated
vendored
@@ -1,13 +1,16 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: github.com/cockroachdb/errors/errorspb/tags.proto
|
||||
// source: errorspb/tags.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +21,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// TagsPayload is the error payload for a WithContext
|
||||
// marker.
|
||||
@@ -32,21 +35,21 @@ func (m *TagsPayload) Reset() { *m = TagsPayload{} }
|
||||
func (m *TagsPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*TagsPayload) ProtoMessage() {}
|
||||
func (*TagsPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{0}
|
||||
return fileDescriptor_2f0d1dc5c54e9f63, []int{0}
|
||||
}
|
||||
func (m *TagsPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TagsPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TagsPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagsPayload.Merge(dst, src)
|
||||
func (m *TagsPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagsPayload.Merge(m, src)
|
||||
}
|
||||
func (m *TagsPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -66,21 +69,21 @@ func (m *TagPayload) Reset() { *m = TagPayload{} }
|
||||
func (m *TagPayload) String() string { return proto.CompactTextString(m) }
|
||||
func (*TagPayload) ProtoMessage() {}
|
||||
func (*TagPayload) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_tags_c75c5e322ef6b31c, []int{1}
|
||||
return fileDescriptor_2f0d1dc5c54e9f63, []int{1}
|
||||
}
|
||||
func (m *TagPayload) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TagPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TagPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagPayload.Merge(dst, src)
|
||||
func (m *TagPayload) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TagPayload.Merge(m, src)
|
||||
}
|
||||
func (m *TagPayload) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -95,10 +98,30 @@ func init() {
|
||||
proto.RegisterType((*TagsPayload)(nil), "cockroach.errorspb.TagsPayload")
|
||||
proto.RegisterType((*TagPayload)(nil), "cockroach.errorspb.TagPayload")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("errorspb/tags.proto", fileDescriptor_2f0d1dc5c54e9f63) }
|
||||
|
||||
var fileDescriptor_2f0d1dc5c54e9f63 = []byte{
|
||||
// 201 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2d, 0x2a, 0xca,
|
||||
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0x2f, 0x49, 0x4c, 0x2f, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
|
||||
0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0x49, 0x4b, 0x89, 0xa4,
|
||||
0xe7, 0xa7, 0xe7, 0x83, 0xa5, 0xf5, 0x41, 0x2c, 0x88, 0x4a, 0x25, 0x77, 0x2e, 0xee, 0x90, 0xc4,
|
||||
0xf4, 0xe2, 0x80, 0xc4, 0xca, 0x9c, 0xfc, 0xc4, 0x14, 0x21, 0x0b, 0x2e, 0x16, 0x90, 0x31, 0x12,
|
||||
0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x72, 0x7a, 0x98, 0xe6, 0xe8, 0x85, 0x24, 0xa6, 0x43, 0x55,
|
||||
0x3b, 0xb1, 0x9c, 0xb8, 0x27, 0xcf, 0x10, 0x04, 0xd6, 0xa1, 0x64, 0xc2, 0xc5, 0x85, 0x90, 0x11,
|
||||
0x12, 0xe0, 0x62, 0x2e, 0x49, 0x4c, 0x97, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x85,
|
||||
0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x93, 0xd6,
|
||||
0x89, 0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c,
|
||||
0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1,
|
||||
0x1c, 0x43, 0x14, 0x07, 0xcc, 0xe2, 0x24, 0x36, 0xb0, 0x8b, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff,
|
||||
0xff, 0x0a, 0x90, 0xd1, 0xc9, 0xf2, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *TagsPayload) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,29 +129,36 @@ func (m *TagsPayload) Marshal() (dAtA []byte, err error) {
|
||||
}
|
||||
|
||||
func (m *TagsPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TagsPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tags) > 0 {
|
||||
for _, msg := range m.Tags {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintTags(dAtA, i, uint64(msg.Size()))
|
||||
n, err := msg.MarshalTo(dAtA[i:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
for iNdEx := len(m.Tags) - 1; iNdEx >= 0; iNdEx-- {
|
||||
{
|
||||
size, err := m.Tags[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTags(dAtA, i, uint64(size))
|
||||
}
|
||||
i += n
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *TagPayload) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,33 +166,42 @@ func (m *TagPayload) Marshal() (dAtA []byte, err error) {
|
||||
}
|
||||
|
||||
func (m *TagPayload) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TagPayload) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tag) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintTags(dAtA, i, uint64(len(m.Tag)))
|
||||
i += copy(dAtA[i:], m.Tag)
|
||||
}
|
||||
if len(m.Value) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i -= len(m.Value)
|
||||
copy(dAtA[i:], m.Value)
|
||||
i = encodeVarintTags(dAtA, i, uint64(len(m.Value)))
|
||||
i += copy(dAtA[i:], m.Value)
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
return i, nil
|
||||
if len(m.Tag) > 0 {
|
||||
i -= len(m.Tag)
|
||||
copy(dAtA[i:], m.Tag)
|
||||
i = encodeVarintTags(dAtA, i, uint64(len(m.Tag)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTags(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTags(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *TagsPayload) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -197,14 +236,7 @@ func (m *TagPayload) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovTags(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTags(x uint64) (n int) {
|
||||
return sovTags(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -224,7 +256,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -252,7 +284,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -261,6 +293,9 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -275,7 +310,7 @@ func (m *TagsPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -305,7 +340,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -333,7 +368,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -343,6 +378,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -362,7 +400,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= (uint64(b) & 0x7F) << shift
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -372,6 +410,9 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -383,7 +424,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTags
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -401,6 +442,7 @@ func (m *TagPayload) Unmarshal(dAtA []byte) error {
|
||||
func skipTags(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -432,10 +474,8 @@ func skipTags(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -452,75 +492,34 @@ func skipTags(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTags
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTags
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipTags(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTags
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthTags
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTags = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthTags = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTags = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupTags = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/tags.proto", fileDescriptor_tags_c75c5e322ef6b31c)
|
||||
}
|
||||
|
||||
var fileDescriptor_tags_c75c5e322ef6b31c = []byte{
|
||||
// 218 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4c, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x25, 0x89,
|
||||
0xe9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0x75, 0x7a, 0x30, 0x69, 0x29,
|
||||
0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0xb4, 0x3e, 0x88, 0x05, 0x51, 0xa9, 0xe4, 0xce, 0xc5, 0x1d,
|
||||
0x92, 0x98, 0x5e, 0x1c, 0x90, 0x58, 0x99, 0x93, 0x9f, 0x98, 0x22, 0x64, 0xc1, 0xc5, 0x02, 0x32,
|
||||
0x46, 0x82, 0x51, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4e, 0x0f, 0xd3, 0x1c, 0xbd, 0x90, 0xc4, 0x74,
|
||||
0xa8, 0x6a, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xc0, 0x3a, 0x94, 0x4c, 0xb8, 0xb8, 0x10,
|
||||
0x32, 0x42, 0x02, 0x5c, 0xcc, 0x25, 0x89, 0xe9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x20,
|
||||
0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x13, 0x58, 0x0c, 0xc2, 0x71,
|
||||
0xd2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x6f, 0x3c,
|
||||
0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e,
|
||||
0x3c, 0x96, 0x63, 0x88, 0xe2, 0x80, 0x59, 0x9c, 0xc4, 0x06, 0x76, 0xb1, 0x31, 0x20, 0x00, 0x00,
|
||||
0xff, 0xff, 0xa6, 0x1b, 0x01, 0x30, 0x10, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
138
vendor/github.com/cockroachdb/errors/errorspb/testing.pb.go
generated
vendored
138
vendor/github.com/cockroachdb/errors/errorspb/testing.pb.go
generated
vendored
@@ -1,13 +1,15 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: github.com/cockroachdb/errors/errorspb/testing.proto
|
||||
// source: errorspb/testing.proto
|
||||
|
||||
package errorspb
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import io "io"
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
@@ -18,7 +20,7 @@ var _ = math.Inf
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// TestError is meant for use in testing only.
|
||||
type TestError struct {
|
||||
@@ -28,21 +30,21 @@ func (m *TestError) Reset() { *m = TestError{} }
|
||||
func (m *TestError) String() string { return proto.CompactTextString(m) }
|
||||
func (*TestError) ProtoMessage() {}
|
||||
func (*TestError) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_testing_d9751a0a5e86c096, []int{0}
|
||||
return fileDescriptor_0551f0d913d6118f, []int{0}
|
||||
}
|
||||
func (m *TestError) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *TestError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalTo(b)
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
func (dst *TestError) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TestError.Merge(dst, src)
|
||||
func (m *TestError) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_TestError.Merge(m, src)
|
||||
}
|
||||
func (m *TestError) XXX_Size() int {
|
||||
return m.Size()
|
||||
@@ -56,10 +58,25 @@ var xxx_messageInfo_TestError proto.InternalMessageInfo
|
||||
func init() {
|
||||
proto.RegisterType((*TestError)(nil), "cockroach.errorspb.TestError")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("errorspb/testing.proto", fileDescriptor_0551f0d913d6118f) }
|
||||
|
||||
var fileDescriptor_0551f0d913d6118f = []byte{
|
||||
// 118 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4b, 0x2d, 0x2a, 0xca,
|
||||
0x2f, 0x2a, 0x2e, 0x48, 0xd2, 0x2f, 0x49, 0x2d, 0x2e, 0xc9, 0xcc, 0x4b, 0xd7, 0x2b, 0x28, 0xca,
|
||||
0x2f, 0xc9, 0x17, 0x12, 0x4a, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce, 0xd0, 0x83, 0xa9,
|
||||
0x50, 0xe2, 0xe6, 0xe2, 0x0c, 0x49, 0x2d, 0x2e, 0x71, 0x05, 0xf1, 0x9d, 0xb4, 0x4e, 0x3c, 0x94,
|
||||
0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x1b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c,
|
||||
0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2,
|
||||
0x38, 0x60, 0x1a, 0x93, 0xd8, 0xc0, 0x66, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xf3,
|
||||
0x3e, 0x92, 0x6d, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *TestError) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalTo(dAtA)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,21 +84,28 @@ func (m *TestError) Marshal() (dAtA []byte, err error) {
|
||||
}
|
||||
|
||||
func (m *TestError) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TestError) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return i, nil
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTesting(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTesting(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
return base
|
||||
}
|
||||
func (m *TestError) Size() (n int) {
|
||||
if m == nil {
|
||||
@@ -93,14 +117,7 @@ func (m *TestError) Size() (n int) {
|
||||
}
|
||||
|
||||
func sovTesting(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTesting(x uint64) (n int) {
|
||||
return sovTesting(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
@@ -120,7 +137,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
@@ -140,7 +157,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTesting
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -158,6 +175,7 @@ func (m *TestError) Unmarshal(dAtA []byte) error {
|
||||
func skipTesting(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -189,10 +207,8 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
@@ -209,70 +225,34 @@ func skipTesting(dAtA []byte) (n int, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTesting
|
||||
}
|
||||
return iNdEx, nil
|
||||
iNdEx += length
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTesting
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipTesting(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
depth++
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTesting
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthTesting
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTesting = fmt.Errorf("proto: integer overflow")
|
||||
ErrInvalidLengthTesting = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTesting = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupTesting = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("github.com/cockroachdb/errors/errorspb/testing.proto", fileDescriptor_testing_d9751a0a5e86c096)
|
||||
}
|
||||
|
||||
var fileDescriptor_testing_d9751a0a5e86c096 = []byte{
|
||||
// 136 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x49, 0xcf, 0x2c, 0xc9,
|
||||
0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x4f, 0xce, 0x2e, 0xca, 0x4f, 0x4c, 0xce,
|
||||
0x48, 0x49, 0xd2, 0x4f, 0x2d, 0x2a, 0xca, 0x2f, 0x2a, 0x86, 0x52, 0x05, 0x49, 0xfa, 0x25, 0xa9,
|
||||
0xc5, 0x25, 0x99, 0x79, 0xe9, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x42, 0x70, 0xa5, 0x7a,
|
||||
0x30, 0x15, 0x4a, 0xdc, 0x5c, 0x9c, 0x21, 0xa9, 0xc5, 0x25, 0xae, 0x20, 0xbe, 0x93, 0xd6, 0x89,
|
||||
0x87, 0x72, 0x0c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0x78, 0xe3, 0x91, 0x1c, 0xe3,
|
||||
0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c,
|
||||
0x43, 0x14, 0x07, 0x4c, 0x63, 0x12, 0x1b, 0xd8, 0x4c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x8a, 0x2c, 0x50, 0x5f, 0x8b, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
29
vendor/github.com/cockroachdb/errors/markers/markers.go
generated
vendored
29
vendor/github.com/cockroachdb/errors/markers/markers.go
generated
vendored
@@ -28,12 +28,22 @@ import (
|
||||
// Is determines whether one of the causes of the given error or any
|
||||
// of its causes is equivalent to some reference error.
|
||||
//
|
||||
// As in the Go standard library, an error is considered to match a
|
||||
// reference error if it is equal to that target or if it implements a
|
||||
// method Is(error) bool such that Is(reference) returns true.
|
||||
//
|
||||
// Note: the inverse is not true - making an Is(reference) method
|
||||
// return false does not imply that errors.Is() also returns
|
||||
// false. Errors can be equal because their network equality marker is
|
||||
// the same. To force errors to appear different to Is(), use
|
||||
// errors.Mark().
|
||||
//
|
||||
// Note: if any of the error types has been migrated from a previous
|
||||
// package location or a different type, ensure that
|
||||
// RegisterTypeMigration() was called prior to Is().
|
||||
func Is(err, reference error) bool {
|
||||
if reference == nil {
|
||||
return err == reference
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Direct reference comparison is the fastest, and most
|
||||
@@ -42,6 +52,11 @@ func Is(err, reference error) bool {
|
||||
if c == reference {
|
||||
return true
|
||||
}
|
||||
// Compatibility with std go errors: if the error object itself
|
||||
// implements Is(), try to use that.
|
||||
if tryDelegateToIsMethod(c, reference) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
@@ -67,6 +82,13 @@ func Is(err, reference error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func tryDelegateToIsMethod(err, reference error) bool {
|
||||
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(reference) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HasType returns true iff err contains an error whose concrete type
|
||||
// matches that of referenceType.
|
||||
func HasType(err error, referenceType error) bool {
|
||||
@@ -125,6 +147,11 @@ func IsAny(err error, references ...error) bool {
|
||||
if c == refErr {
|
||||
return true
|
||||
}
|
||||
// Compatibility with std go errors: if the error object itself
|
||||
// implements Is(), try to use that.
|
||||
if tryDelegateToIsMethod(c, refErr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if c == nil {
|
||||
// This special case is to support a comparison to a nil
|
||||
|
||||
10
vendor/github.com/cockroachdb/errors/markers_api.go
generated
vendored
10
vendor/github.com/cockroachdb/errors/markers_api.go
generated
vendored
@@ -19,6 +19,16 @@ import "github.com/cockroachdb/errors/markers"
|
||||
// Is determines whether one of the causes of the given error or any
|
||||
// of its causes is equivalent to some reference error.
|
||||
//
|
||||
// As in the Go standard library, an error is considered to match a
|
||||
// reference error if it is equal to that target or if it implements a
|
||||
// method Is(error) bool such that Is(reference) returns true.
|
||||
//
|
||||
// Note: the inverse is not true - making an Is(reference) method
|
||||
// return false does not imply that errors.Is() also returns
|
||||
// false. Errors can be equal because their network equality marker is
|
||||
// the same. To force errors to appear different to Is(), use
|
||||
// errors.Mark().
|
||||
//
|
||||
// Note: if any of the error types has been migrated from a previous
|
||||
// package location or a different type, ensure that
|
||||
// RegisterTypeMigration() was called prior to Is().
|
||||
|
||||
67
vendor/github.com/cockroachdb/errors/report/report.go
generated
vendored
67
vendor/github.com/cockroachdb/errors/report/report.go
generated
vendored
@@ -15,7 +15,6 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -23,7 +22,7 @@ import (
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/errors/withstack"
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
sentry "github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// BuildSentryReport builds the components of a sentry report. This
|
||||
@@ -76,13 +75,13 @@ import (
|
||||
// Given this mapping, an error object is decomposed as follows:
|
||||
//
|
||||
// (1)/(7): <filename>:<lineno> (<functionname>)
|
||||
// (3)/(8): <error type>: <first safe detail line, if any>
|
||||
// (3)/(8): first line of verbose error printout
|
||||
// (4): not populated in this function, caller is to manage this
|
||||
// (5): detailed structure of the entire error object, with references to "additional data"
|
||||
// and additional "exception" objects
|
||||
// (5): detailed structure of the entire error object, with references to
|
||||
// additional "exception" objects
|
||||
// (9): generated from innermost stack trace
|
||||
// (6): every exception object after the 1st reports additional stack trace contexts
|
||||
// (11): "additional data" populated from safe detail payloads
|
||||
// (11): the detailed error types and their error mark.
|
||||
//
|
||||
// If there is no stack trace in the error, a synthetic Exception
|
||||
// object is still produced to provide visual detail in the Sentry UI.
|
||||
@@ -119,17 +118,22 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
var firstDetailLine string
|
||||
|
||||
// longMsgBuf will become the Message field, which contains the full
|
||||
// structure of the error with cross-references to "Exception" and
|
||||
// "Additional data" fields.
|
||||
// structure of the error.
|
||||
var longMsgBuf strings.Builder
|
||||
redactedErrStr := redact.Sprint(err).Redact()
|
||||
if redactedErrStr != redactedMarker {
|
||||
if f, l, _, ok := withstack.GetOneLineSource(err); ok {
|
||||
fmt.Fprintf(&longMsgBuf, "%s:%d: ", f, l)
|
||||
}
|
||||
firstDetailLine = redactedErrStr.StripMarkers()
|
||||
fmt.Fprintf(&longMsgBuf, "%v\n--\n", firstDetailLine)
|
||||
if f, l, _, ok := withstack.GetOneLineSource(err); ok {
|
||||
fmt.Fprintf(&longMsgBuf, "%s:%d: ", f, l)
|
||||
}
|
||||
// Include the verbose error printout, with sensitive bits redacted out.
|
||||
verboseErr := redact.Sprintf("%+v", err).Redact().StripMarkers()
|
||||
if verboseErr != redactedMarker {
|
||||
idx := strings.IndexByte(verboseErr, '\n')
|
||||
if idx == -1 {
|
||||
firstDetailLine = verboseErr
|
||||
} else {
|
||||
firstDetailLine = verboseErr[:idx]
|
||||
}
|
||||
}
|
||||
fmt.Fprint(&longMsgBuf, verboseErr)
|
||||
|
||||
// sep is used to separate the entries in the longMsgBuf / Message
|
||||
// payload.
|
||||
@@ -159,6 +163,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
// layer. We iterate in this order because we want to describe the
|
||||
// error from innermost to outermost layer in longMsgBuf and
|
||||
// typesBuf.
|
||||
longMsgBuf.WriteString("\n-- report composition:\n")
|
||||
for i := len(details) - 1; i >= 0; i-- {
|
||||
// Collect the type name for this layer of error wrapping, towards
|
||||
// the "error types" additional data field.
|
||||
@@ -204,10 +209,6 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
// Now decide what kind of payload we want to add to the Event
|
||||
// object.
|
||||
|
||||
// genExtra will remember whether we are adding an
|
||||
// additional payload or not.
|
||||
var genExtra bool
|
||||
|
||||
// Is there a stack trace?
|
||||
if st := stacks[i]; st != nil {
|
||||
var excType strings.Builder
|
||||
@@ -245,17 +246,21 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
exceptions = append(exceptions, exc)
|
||||
} else {
|
||||
// No stack trace.
|
||||
// Are there safe details? If so, print them.
|
||||
// Are there safe details? If so, print the first safe detail
|
||||
// string (we're assuming that all the important bits will
|
||||
// also be included in the verbose printout, so there's no
|
||||
// need to dig out more safe strings here.)
|
||||
//
|
||||
// TODO(knz): the SafeDetails API is not really meant for Sentry
|
||||
// reporting. Once we have more experience to prove that the
|
||||
// verbose printout is sufficient, we can remove the SafeDetails
|
||||
// from sentry reports.
|
||||
//
|
||||
// Note: we only print the details if no stack trace was found
|
||||
// at that level. This is because stack trace annotations also
|
||||
// produce the stack trace as safe detail string.
|
||||
genExtra = len(details[i].SafeDetails) > 1
|
||||
if len(details[i].SafeDetails) > 0 {
|
||||
d := details[i].SafeDetails[0]
|
||||
if d != "" {
|
||||
genExtra = true
|
||||
}
|
||||
if j := strings.IndexByte(d, '\n'); j >= 0 {
|
||||
d = d[:j]
|
||||
}
|
||||
@@ -269,18 +274,6 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are we generating another extra for the safe detail strings?
|
||||
if genExtra {
|
||||
stKey := fmt.Sprintf("%d: details", extraNum)
|
||||
var extraStr bytes.Buffer
|
||||
for _, d := range details[i].SafeDetails {
|
||||
fmt.Fprintln(&extraStr, strings.ReplaceAll(d, "\n", "\n "))
|
||||
}
|
||||
extras[stKey] = extraStr.String()
|
||||
fmt.Fprintf(&longMsgBuf, " (%d)", extraNum)
|
||||
extraNum++
|
||||
}
|
||||
}
|
||||
|
||||
if extraNum > 1 {
|
||||
@@ -354,7 +347,7 @@ func BuildSentryReport(err error) (event *sentry.Event, extraDetails map[string]
|
||||
return event, extras
|
||||
}
|
||||
|
||||
var redactedMarker = redact.RedactableString(redact.RedactedMarker())
|
||||
var redactedMarker = redact.RedactableString(redact.RedactedMarker()).StripMarkers()
|
||||
|
||||
// ReportError reports the given error to Sentry. The caller is responsible for
|
||||
// checking whether telemetry is enabled, and calling the sentry.Flush()
|
||||
|
||||
2
vendor/github.com/cockroachdb/errors/report/reportables.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/report/reportables.go
generated
vendored
@@ -18,7 +18,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// StackTrace is an object suitable for inclusion in errors that can
|
||||
|
||||
2
vendor/github.com/cockroachdb/errors/report_api.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/report_api.go
generated
vendored
@@ -16,7 +16,7 @@ package errors
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors/report"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
// BuildSentryReport builds the components of a sentry report. This
|
||||
|
||||
2
vendor/github.com/cockroachdb/errors/withstack/reportable.go
generated
vendored
2
vendor/github.com/cockroachdb/errors/withstack/reportable.go
generated
vendored
@@ -23,7 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors/errbase"
|
||||
"github.com/cockroachdb/sentry-go"
|
||||
"github.com/getsentry/sentry-go"
|
||||
pkgErr "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
||||
2
vendor/github.com/cockroachdb/logtags/buffer.go
generated
vendored
2
vendor/github.com/cockroachdb/logtags/buffer.go
generated
vendored
@@ -67,7 +67,7 @@ func (b *Buffer) Merge(other *Buffer) *Buffer {
|
||||
if b == nil || len(b.tags) == 0 {
|
||||
return other
|
||||
}
|
||||
if len(other.tags) == 0 {
|
||||
if other == nil || len(other.tags) == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/.gitignore
generated
vendored
2
vendor/github.com/cockroachdb/pebble/.gitignore
generated
vendored
@@ -2,3 +2,5 @@
|
||||
cpu.*.prof
|
||||
heap.prof
|
||||
mutex.prof
|
||||
# Testing artifacts
|
||||
meta.*.test
|
||||
|
||||
22
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
22
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
@@ -4,6 +4,7 @@ GOFLAGS :=
|
||||
STRESSFLAGS :=
|
||||
TAGS := invariants
|
||||
TESTS := .
|
||||
LATEST_RELEASE := $(shell git fetch origin && git branch -r --list '*/crl-release-*' | grep -o 'crl-release-.*$$' | sort | tail -1)
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
@@ -13,13 +14,14 @@ all:
|
||||
@echo " make stress"
|
||||
@echo " make stressrace"
|
||||
@echo " make stressmeta"
|
||||
@echo " make crossversion-meta"
|
||||
@echo " make mod-update"
|
||||
@echo " make clean"
|
||||
|
||||
override testflags :=
|
||||
.PHONY: test
|
||||
test:
|
||||
${GO} test -mod=vendor -tags '$(TAGS)' ${testflags} -run ${TESTS} ${PKG}
|
||||
${GO} test -tags '$(TAGS)' ${testflags} -run ${TESTS} ${PKG}
|
||||
|
||||
.PHONY: testrace
|
||||
testrace: testflags += -race -timeout 20m
|
||||
@@ -36,14 +38,25 @@ stressmeta: override STRESSFLAGS += -p 1
|
||||
stressmeta: override TESTS = TestMeta$$
|
||||
stressmeta: stress
|
||||
|
||||
.PHONY: crossversion-meta
|
||||
crossversion-meta:
|
||||
git checkout ${LATEST_RELEASE}; \
|
||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/${LATEST_RELEASE}.test'; \
|
||||
git checkout -; \
|
||||
${GO} test -c ./internal/metamorphic -o './internal/metamorphic/crossversion/head.test'; \
|
||||
${GO} test -tags '$(TAGS)' ${testflags} -v -run 'TestMetaCrossVersion' ./internal/metamorphic/crossversion --version '${LATEST_RELEASE},${LATEST_RELEASE},${LATEST_RELEASE}.test' --version 'HEAD,HEAD,./head.test'
|
||||
|
||||
.PHONY: stress-crossversion
|
||||
stress-crossversion:
|
||||
STRESS=1 ./scripts/run-crossversion-meta.sh crl-release-21.2 crl-release-22.1 crl-release-22.2 master
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
${GO} generate -mod=vendor ${PKG}
|
||||
${GO} generate ${PKG}
|
||||
|
||||
mod-update:
|
||||
${GO} get -u
|
||||
${GO} mod tidy
|
||||
${GO} mod vendor
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -70,7 +83,7 @@ endif
|
||||
|
||||
.PHONY: format
|
||||
format:
|
||||
for _file in $$(gofmt -s -l . | grep -vE '^vendor/'); do \
|
||||
for _file in $$(gofmt -s -l .); do \
|
||||
gofmt -s -w $$_file ; \
|
||||
done
|
||||
|
||||
@@ -80,4 +93,5 @@ ifneq ($(git_dirty),)
|
||||
$(error format-check must be invoked on a clean repository)
|
||||
endif
|
||||
$(MAKE) format
|
||||
git diff
|
||||
$(MAKE) git-clean-check
|
||||
|
||||
472
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
472
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
@@ -78,7 +78,9 @@ func (d DeferredBatchOp) Finish() error {
|
||||
// RangeKeyUnsets, and/or RangeKeyDeletes that are applied atomically. Batch
|
||||
// implements the Reader interface, but only an indexed batch supports reading
|
||||
// (without error) via Get or NewIter. A non-indexed batch will return
|
||||
// ErrNotIndexed when read from .
|
||||
// ErrNotIndexed when read from. A batch is not safe for concurrent use, and
|
||||
// consumers should use a batch per goroutine or provide their own
|
||||
// synchronization.
|
||||
//
|
||||
// Indexing
|
||||
//
|
||||
@@ -233,13 +235,21 @@ type Batch struct {
|
||||
|
||||
// Fragmented range deletion tombstones. Cached the first time a range
|
||||
// deletion iterator is requested. The cache is invalidated whenever a new
|
||||
// range deletion is added to the batch.
|
||||
tombstones []keyspan.Span
|
||||
// range deletion is added to the batch. This cache can only be used when
|
||||
// opening an iterator to read at a batch sequence number >=
|
||||
// tombstonesSeqNum. This is the case for all new iterators created over a
|
||||
// batch but it's not the case for all cloned iterators.
|
||||
tombstones []keyspan.Span
|
||||
tombstonesSeqNum uint64
|
||||
|
||||
// Fragmented range key spans. Cached the first time a range key iterator is
|
||||
// requested. The cache is invalidated whenever a new range key
|
||||
// (RangeKey{Set,Unset,Del}) is added to the batch.
|
||||
rangeKeys []keyspan.Span
|
||||
// (RangeKey{Set,Unset,Del}) is added to the batch. This cache can only be
|
||||
// used when opening an iterator to read at a batch sequence number >=
|
||||
// tombstonesSeqNum. This is the case for all new iterators created over a
|
||||
// batch but it's not the case for all cloned iterators.
|
||||
rangeKeys []keyspan.Span
|
||||
rangeKeysSeqNum uint64
|
||||
|
||||
// The flushableBatch wrapper if the batch is too large to fit in the
|
||||
// memtable.
|
||||
@@ -287,6 +297,16 @@ func newIndexedBatch(db *DB, comparer *Comparer) *Batch {
|
||||
return &i.batch
|
||||
}
|
||||
|
||||
// nextSeqNum returns the batch "sequence number" that will be given to the next
|
||||
// key written to the batch. During iteration keys within an indexed batch are
|
||||
// given a sequence number consisting of their offset within the batch combined
|
||||
// with the base.InternalKeySeqNumBatch bit. These sequence numbers are only
|
||||
// used during iteration, and the keys are assigned ordinary sequence numbers
|
||||
// when the batch is committed.
|
||||
func (b *Batch) nextSeqNum() uint64 {
|
||||
return uint64(len(b.data)) | base.InternalKeySeqNumBatch
|
||||
}
|
||||
|
||||
func (b *Batch) release() {
|
||||
if b.db == nil {
|
||||
// The batch was not created using newBatch or newIndexedBatch, or an error
|
||||
@@ -378,12 +398,14 @@ func (b *Batch) Apply(batch *Batch, _ *WriteOptions) error {
|
||||
switch kind {
|
||||
case InternalKeyKindRangeDelete:
|
||||
b.tombstones = nil
|
||||
b.tombstonesSeqNum = 0
|
||||
if b.rangeDelIndex == nil {
|
||||
b.rangeDelIndex = batchskl.NewSkiplist(&b.data, b.cmp, b.abbreviatedKey)
|
||||
}
|
||||
err = b.rangeDelIndex.Add(uint32(offset))
|
||||
case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
||||
b.rangeKeys = nil
|
||||
b.rangeKeysSeqNum = 0
|
||||
if b.rangeKeyIndex == nil {
|
||||
b.rangeKeyIndex = batchskl.NewSkiplist(&b.data, b.cmp, b.abbreviatedKey)
|
||||
}
|
||||
@@ -638,6 +660,7 @@ func (b *Batch) DeleteRangeDeferred(startLen, endLen int) *DeferredBatchOp {
|
||||
b.countRangeDels++
|
||||
if b.index != nil {
|
||||
b.tombstones = nil
|
||||
b.tombstonesSeqNum = 0
|
||||
// Range deletions are rare, so we lazily allocate the index for them.
|
||||
if b.rangeDelIndex == nil {
|
||||
b.rangeDelIndex = batchskl.NewSkiplist(&b.data, b.cmp, b.abbreviatedKey)
|
||||
@@ -647,17 +670,13 @@ func (b *Batch) DeleteRangeDeferred(startLen, endLen int) *DeferredBatchOp {
|
||||
return &b.deferredOp
|
||||
}
|
||||
|
||||
// Experimental returns the experimental write API, backed by the same batch.
|
||||
func (b *Batch) Experimental() ExperimentalWriter {
|
||||
return experimentalBatch{b}
|
||||
}
|
||||
|
||||
type experimentalBatch struct {
|
||||
*Batch
|
||||
}
|
||||
|
||||
// RangeKeySet implements the ExperimentalWriter interface.
|
||||
func (b experimentalBatch) RangeKeySet(start, end, suffix, value []byte, _ *WriteOptions) error {
|
||||
// RangeKeySet sets a range key mapping the key range [start, end) at the MVCC
|
||||
// timestamp suffix to value. The suffix is optional. If any portion of the key
|
||||
// range [start, end) is already set by a range key with the same suffix value,
|
||||
// RangeKeySet overrides it.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeySet returns.
|
||||
func (b *Batch) RangeKeySet(start, end, suffix, value []byte, _ *WriteOptions) error {
|
||||
suffixValues := [1]rangekey.SuffixValue{{Suffix: suffix, Value: value}}
|
||||
internalValueLen := rangekey.EncodedSetValueLen(end, suffixValues[:])
|
||||
|
||||
@@ -687,6 +706,7 @@ func (b *Batch) incrementRangeKeysCount() {
|
||||
b.countRangeKeys++
|
||||
if b.index != nil {
|
||||
b.rangeKeys = nil
|
||||
b.rangeKeysSeqNum = 0
|
||||
// Range keys are rare, so we lazily allocate the index for them.
|
||||
if b.rangeKeyIndex == nil {
|
||||
b.rangeKeyIndex = batchskl.NewSkiplist(&b.data, b.cmp, b.abbreviatedKey)
|
||||
@@ -695,8 +715,15 @@ func (b *Batch) incrementRangeKeysCount() {
|
||||
}
|
||||
}
|
||||
|
||||
// RangeKeyUnset implements the ExperimentalWriter interface.
|
||||
func (b experimentalBatch) RangeKeyUnset(start, end, suffix []byte, _ *WriteOptions) error {
|
||||
// RangeKeyUnset removes a range key mapping the key range [start, end) at the
|
||||
// MVCC timestamp suffix. The suffix may be omitted to remove an unsuffixed
|
||||
// range key. RangeKeyUnset only removes portions of range keys that fall within
|
||||
// the [start, end) key span, and only range keys with suffixes that exactly
|
||||
// match the unset suffix.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyUnset
|
||||
// returns.
|
||||
func (b *Batch) RangeKeyUnset(start, end, suffix []byte, _ *WriteOptions) error {
|
||||
suffixes := [1][]byte{suffix}
|
||||
internalValueLen := rangekey.EncodedUnsetValueLen(end, suffixes[:])
|
||||
|
||||
@@ -722,8 +749,14 @@ func (b *Batch) rangeKeyUnsetDeferred(startLen, internalValueLen int) *DeferredB
|
||||
return &b.deferredOp
|
||||
}
|
||||
|
||||
// RangeKeyDelete implements the ExperimentalWriter interface.
|
||||
func (b experimentalBatch) RangeKeyDelete(start, end []byte, _ *WriteOptions) error {
|
||||
// RangeKeyDelete deletes all of the range keys in the range [start,end)
|
||||
// (inclusive on start, exclusive on end). It does not delete point keys (for
|
||||
// that use DeleteRange). RangeKeyDelete removes all range keys within the
|
||||
// bounds, including those with or without suffixes.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyDelete
|
||||
// returns.
|
||||
func (b *Batch) RangeKeyDelete(start, end []byte, _ *WriteOptions) error {
|
||||
deferredOp := b.RangeKeyDeleteDeferred(len(start), len(end))
|
||||
copy(deferredOp.Key, start)
|
||||
copy(deferredOp.Value, end)
|
||||
@@ -769,6 +802,14 @@ func (b *Batch) Empty() bool {
|
||||
return len(b.data) <= batchHeaderLen
|
||||
}
|
||||
|
||||
// Len returns the current size of the batch in bytes.
|
||||
func (b *Batch) Len() int {
|
||||
if len(b.data) <= batchHeaderLen {
|
||||
return batchHeaderLen
|
||||
}
|
||||
return len(b.data)
|
||||
}
|
||||
|
||||
// Repr returns the underlying batch representation. It is not safe to modify
|
||||
// the contents. Reset() will not change the contents of the returned value,
|
||||
// though any other mutation operation may do so.
|
||||
@@ -799,6 +840,10 @@ func (b *Batch) SetRepr(data []byte) error {
|
||||
// NewIter returns an iterator that is unpositioned (Iterator.Valid() will
|
||||
// return false). The iterator can be positioned via a call to SeekGE,
|
||||
// SeekPrefixGE, SeekLT, First or Last. Only indexed batches support iterators.
|
||||
//
|
||||
// The returned Iterator observes all of the Batch's existing mutations, but no
|
||||
// later mutations. Its view can be refreshed via RefreshBatchSnapshot or
|
||||
// SetOptions().
|
||||
func (b *Batch) NewIter(o *IterOptions) *Iterator {
|
||||
if b.index == nil {
|
||||
return &Iterator{err: ErrNotIndexed}
|
||||
@@ -808,45 +853,94 @@ func (b *Batch) NewIter(o *IterOptions) *Iterator {
|
||||
|
||||
// newInternalIter creates a new internalIterator that iterates over the
|
||||
// contents of the batch.
|
||||
func (b *Batch) newInternalIter(o *IterOptions) internalIterator {
|
||||
if b.index == nil {
|
||||
return newErrorIter(ErrNotIndexed)
|
||||
}
|
||||
return &batchIter{
|
||||
func (b *Batch) newInternalIter(o *IterOptions) *batchIter {
|
||||
iter := &batchIter{}
|
||||
b.initInternalIter(o, iter)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (b *Batch) initInternalIter(o *IterOptions, iter *batchIter) {
|
||||
*iter = batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.index.NewIter(o.GetLowerBound(), o.GetUpperBound()),
|
||||
// NB: We explicitly do not propagate the batch snapshot to the point
|
||||
// key iterator. Filtering point keys within the batch iterator can
|
||||
// cause pathological behavior where a batch iterator advances
|
||||
// significantly farther than necessary filtering many batch keys that
|
||||
// are not visible at the batch sequence number. Instead, the merging
|
||||
// iterator enforces bounds.
|
||||
//
|
||||
// For example, consider an engine that contains the committed keys
|
||||
// 'bar' and 'bax', with no keys between them. Consider a batch
|
||||
// containing keys 1,000 keys within the range [a,z]. All of the
|
||||
// batch keys were added to the batch after the iterator was
|
||||
// constructed, so they are not visible to the iterator. A call to
|
||||
// SeekGE('bax') would seek the LSM iterators and discover the key
|
||||
// 'bax'. It would also seek the batch iterator, landing on the key
|
||||
// 'baz' but discover it that it's not visible. The batch iterator would
|
||||
// next through the rest of the batch's keys, only to discover there are
|
||||
// no visible keys greater than or equal to 'bax'.
|
||||
//
|
||||
// Filtering these batch points within the merging iterator ensures that
|
||||
// the batch iterator never needs to iterate beyond 'baz', because it
|
||||
// already found a smaller, visible key 'bax'.
|
||||
snapshot: base.InternalKeySeqNumMax,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Batch) newRangeDelIter(_ *IterOptions) keyspan.FragmentIterator {
|
||||
if b.index == nil {
|
||||
return newErrorKeyspanIter(ErrNotIndexed)
|
||||
}
|
||||
func (b *Batch) newRangeDelIter(o *IterOptions, batchSnapshot uint64) *keyspan.Iter {
|
||||
// Construct an iterator even if rangeDelIndex is nil, because it is allowed
|
||||
// to refresh later, so we need the container to exist.
|
||||
iter := new(keyspan.Iter)
|
||||
b.initRangeDelIter(o, iter, batchSnapshot)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (b *Batch) initRangeDelIter(_ *IterOptions, iter *keyspan.Iter, batchSnapshot uint64) {
|
||||
if b.rangeDelIndex == nil {
|
||||
return nil
|
||||
iter.Init(b.cmp, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Fragment the range tombstones the first time a range deletion iterator is
|
||||
// requested. The cached tombstones are invalidated if another range deletion
|
||||
// tombstone is added to the batch.
|
||||
if b.tombstones == nil {
|
||||
frag := &keyspan.Fragmenter{
|
||||
Cmp: b.cmp,
|
||||
Format: b.formatKey,
|
||||
Emit: func(s keyspan.Span) {
|
||||
b.tombstones = append(b.tombstones, s)
|
||||
},
|
||||
}
|
||||
it := &batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.rangeDelIndex.NewIter(nil, nil),
|
||||
}
|
||||
fragmentRangeDels(frag, it, int(b.countRangeDels))
|
||||
// requested. The cached tombstones are invalidated if another range
|
||||
// deletion tombstone is added to the batch. This cache is only guaranteed
|
||||
// to be correct if we're opening an iterator to read at a batch sequence
|
||||
// number at least as high as tombstonesSeqNum. The cache is guaranteed to
|
||||
// include all tombstones up to tombstonesSeqNum, and if any additional
|
||||
// tombstones were added after that sequence number the cache would've been
|
||||
// cleared.
|
||||
nextSeqNum := b.nextSeqNum()
|
||||
if b.tombstones != nil && b.tombstonesSeqNum <= batchSnapshot {
|
||||
iter.Init(b.cmp, b.tombstones)
|
||||
return
|
||||
}
|
||||
|
||||
return keyspan.NewIter(b.cmp, b.tombstones)
|
||||
tombstones := make([]keyspan.Span, 0, b.countRangeDels)
|
||||
frag := &keyspan.Fragmenter{
|
||||
Cmp: b.cmp,
|
||||
Format: b.formatKey,
|
||||
Emit: func(s keyspan.Span) {
|
||||
tombstones = append(tombstones, s)
|
||||
},
|
||||
}
|
||||
it := &batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.rangeDelIndex.NewIter(nil, nil),
|
||||
snapshot: batchSnapshot,
|
||||
}
|
||||
fragmentRangeDels(frag, it, int(b.countRangeDels))
|
||||
iter.Init(b.cmp, tombstones)
|
||||
|
||||
// If we just read all the tombstones in the batch (eg, batchSnapshot was
|
||||
// set to b.nextSeqNum()), then cache the tombstones so that a subsequent
|
||||
// call to initRangeDelIter may use them without refragmenting.
|
||||
if nextSeqNum == batchSnapshot {
|
||||
b.tombstones = tombstones
|
||||
b.tombstonesSeqNum = nextSeqNum
|
||||
}
|
||||
}
|
||||
|
||||
func fragmentRangeDels(frag *keyspan.Fragmenter, it internalIterator, count int) {
|
||||
@@ -861,7 +955,7 @@ func fragmentRangeDels(frag *keyspan.Fragmenter, it internalIterator, count int)
|
||||
// individual []keyspan.Key slices with a single element each.
|
||||
keyBuf := make([]keyspan.Key, 0, count)
|
||||
for key, val := it.First(); key != nil; key, val = it.Next() {
|
||||
s := rangedel.Decode(*key, val, keyBuf)
|
||||
s := rangedel.Decode(*key, val.InPlaceValue(), keyBuf)
|
||||
keyBuf = s.Keys[len(s.Keys):]
|
||||
|
||||
// Set a fixed capacity to avoid accidental overwriting.
|
||||
@@ -871,34 +965,57 @@ func fragmentRangeDels(frag *keyspan.Fragmenter, it internalIterator, count int)
|
||||
frag.Finish()
|
||||
}
|
||||
|
||||
func (b *Batch) newRangeKeyIter(_ *IterOptions) keyspan.FragmentIterator {
|
||||
if b.index == nil {
|
||||
return newErrorKeyspanIter(ErrNotIndexed)
|
||||
}
|
||||
func (b *Batch) newRangeKeyIter(o *IterOptions, batchSnapshot uint64) *keyspan.Iter {
|
||||
// Construct an iterator even if rangeKeyIndex is nil, because it is allowed
|
||||
// to refresh later, so we need the container to exist.
|
||||
iter := new(keyspan.Iter)
|
||||
b.initRangeKeyIter(o, iter, batchSnapshot)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (b *Batch) initRangeKeyIter(_ *IterOptions, iter *keyspan.Iter, batchSnapshot uint64) {
|
||||
if b.rangeKeyIndex == nil {
|
||||
return nil
|
||||
iter.Init(b.cmp, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Fragment the range keys the first time a range key iterator is requested.
|
||||
// The cached range keys are invalidated if another range key is added to
|
||||
// the batch.
|
||||
if b.rangeKeys == nil {
|
||||
frag := &keyspan.Fragmenter{
|
||||
Cmp: b.cmp,
|
||||
Format: b.formatKey,
|
||||
Emit: func(s keyspan.Span) {
|
||||
b.rangeKeys = append(b.rangeKeys, s)
|
||||
},
|
||||
}
|
||||
it := &batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.rangeKeyIndex.NewIter(nil, nil),
|
||||
}
|
||||
fragmentRangeKeys(frag, it, int(b.countRangeKeys))
|
||||
// The cached spans are invalidated if another range key is added to the
|
||||
// batch. This cache is only guaranteed to be correct if we're opening an
|
||||
// iterator to read at a batch sequence number at least as high as
|
||||
// rangeKeysSeqNum. The cache is guaranteed to include all range keys up to
|
||||
// rangeKeysSeqNum, and if any additional range keys were added after that
|
||||
// sequence number the cache would've been cleared.
|
||||
nextSeqNum := b.nextSeqNum()
|
||||
if b.rangeKeys != nil && b.rangeKeysSeqNum <= batchSnapshot {
|
||||
iter.Init(b.cmp, b.rangeKeys)
|
||||
return
|
||||
}
|
||||
|
||||
return keyspan.NewIter(b.cmp, b.rangeKeys)
|
||||
rangeKeys := make([]keyspan.Span, 0, b.countRangeKeys)
|
||||
frag := &keyspan.Fragmenter{
|
||||
Cmp: b.cmp,
|
||||
Format: b.formatKey,
|
||||
Emit: func(s keyspan.Span) {
|
||||
rangeKeys = append(rangeKeys, s)
|
||||
},
|
||||
}
|
||||
it := &batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.rangeKeyIndex.NewIter(nil, nil),
|
||||
snapshot: batchSnapshot,
|
||||
}
|
||||
fragmentRangeKeys(frag, it, int(b.countRangeKeys))
|
||||
iter.Init(b.cmp, rangeKeys)
|
||||
|
||||
// If we just read all the range keys in the batch (eg, batchSnapshot was
|
||||
// set to b.nextSeqNum()), then cache the range keys so that a subsequent
|
||||
// call to initRangeKeyIter may use them without refragmenting.
|
||||
if nextSeqNum == batchSnapshot {
|
||||
b.rangeKeys = rangeKeys
|
||||
b.rangeKeysSeqNum = nextSeqNum
|
||||
}
|
||||
}
|
||||
|
||||
func fragmentRangeKeys(frag *keyspan.Fragmenter, it internalIterator, count int) error {
|
||||
@@ -913,7 +1030,7 @@ func fragmentRangeKeys(frag *keyspan.Fragmenter, it internalIterator, count int)
|
||||
// individual []keyspan.Key slices with a single element each.
|
||||
keyBuf := make([]keyspan.Key, 0, count)
|
||||
for ik, val := it.First(); ik != nil; ik, val = it.Next() {
|
||||
s, err := rangekey.Decode(*ik, val, keyBuf)
|
||||
s, err := rangekey.Decode(*ik, val.InPlaceValue(), keyBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -967,7 +1084,9 @@ func (b *Batch) Reset() {
|
||||
b.memTableSize = 0
|
||||
b.deferredOp = DeferredBatchOp{}
|
||||
b.tombstones = nil
|
||||
b.tombstonesSeqNum = 0
|
||||
b.rangeKeys = nil
|
||||
b.rangeKeysSeqNum = 0
|
||||
b.flushable = nil
|
||||
b.commit = sync.WaitGroup{}
|
||||
b.commitErr = nil
|
||||
@@ -1132,6 +1251,11 @@ type batchIter struct {
|
||||
batch *Batch
|
||||
iter batchskl.Iterator
|
||||
err error
|
||||
// snapshot holds a batch "sequence number" at which the batch is being
|
||||
// read. This sequence number has the InternalKeySeqNumBatch bit set, so it
|
||||
// encodes an offset within the batch. Only batch entries earlier than the
|
||||
// offset are visible during iteration.
|
||||
snapshot uint64
|
||||
}
|
||||
|
||||
// batchIter implements the base.InternalIterator interface.
|
||||
@@ -1141,72 +1265,89 @@ func (i *batchIter) String() string {
|
||||
return "batch"
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
// Ignore trySeekUsingNext since the batch may have changed, so using Next
|
||||
// would be incorrect.
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.SeekGE(key)
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
func (i *batchIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
// Ignore TrySeekUsingNext if the view of the batch changed.
|
||||
if flags.TrySeekUsingNext() && flags.BatchJustRefreshed() {
|
||||
flags = flags.DisableTrySeekUsingNext()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.SeekGE(key, flags)
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
return i.SeekGE(key, trySeekUsingNext)
|
||||
return i.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (i *batchIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.SeekLT(key)
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) First() (*InternalKey, []byte) {
|
||||
func (i *batchIter) First() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.First()
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Last() (*InternalKey, []byte) {
|
||||
func (i *batchIter) Last() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.Last()
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Next() (*InternalKey, []byte) {
|
||||
func (i *batchIter) Next() (*InternalKey, base.LazyValue) {
|
||||
ikey := i.iter.Next()
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *batchIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
ikey := i.iter.Prev()
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
return ikey, i.Value()
|
||||
if ikey == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Key() *InternalKey {
|
||||
return i.iter.Key()
|
||||
}
|
||||
|
||||
func (i *batchIter) Value() []byte {
|
||||
func (i *batchIter) value() []byte {
|
||||
offset, _, keyEnd := i.iter.KeyInfo()
|
||||
data := i.batch.data
|
||||
if len(data[offset:]) == 0 {
|
||||
@@ -1227,10 +1368,6 @@ func (i *batchIter) Value() []byte {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *batchIter) Valid() bool {
|
||||
return i.iter.Valid()
|
||||
}
|
||||
|
||||
func (i *batchIter) Error() error {
|
||||
return i.err
|
||||
}
|
||||
@@ -1474,6 +1611,8 @@ func (b *flushableBatch) newRangeKeyIter(o *IterOptions) keyspan.FragmentIterato
|
||||
return keyspan.NewIter(b.cmp, b.rangeKeys)
|
||||
}
|
||||
|
||||
func (b *flushableBatch) containsRangeKeys() bool { return len(b.rangeKeys) > 0 }
|
||||
|
||||
func (b *flushableBatch) inuseBytes() uint64 {
|
||||
return uint64(len(b.data) - batchHeaderLen)
|
||||
}
|
||||
@@ -1517,36 +1656,40 @@ func (i *flushableBatchIter) String() string {
|
||||
}
|
||||
|
||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||
// package. Ignore trySeekUsingNext since we don't expect this optimization
|
||||
// to provide much benefit here at the moment.
|
||||
func (i *flushableBatchIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
// package. Ignore flags.TrySeekUsingNext() since we don't expect this
|
||||
// optimization to provide much benefit here at the moment.
|
||||
func (i *flushableBatchIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := base.MakeSearchKey(key)
|
||||
i.index = sort.Search(len(i.offsets), func(j int) bool {
|
||||
return base.InternalCompare(i.cmp, ikey, i.getKey(j)) <= 0
|
||||
})
|
||||
if i.index >= len(i.offsets) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.key = i.getKey(i.index)
|
||||
if i.upper != nil && i.cmp(i.key.UserKey, i.upper) >= 0 {
|
||||
i.index = len(i.offsets)
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
|
||||
// pebble package.
|
||||
func (i *flushableBatchIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return i.SeekGE(key, trySeekUsingNext)
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return i.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *flushableBatchIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (i *flushableBatchIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := base.MakeSearchKey(key)
|
||||
i.index = sort.Search(len(i.offsets), func(j int) bool {
|
||||
@@ -1554,80 +1697,80 @@ func (i *flushableBatchIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
})
|
||||
i.index--
|
||||
if i.index < 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.key = i.getKey(i.index)
|
||||
if i.lower != nil && i.cmp(i.key.UserKey, i.lower) < 0 {
|
||||
i.index = -1
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *flushableBatchIter) First() (*InternalKey, []byte) {
|
||||
func (i *flushableBatchIter) First() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
if len(i.offsets) == 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.index = 0
|
||||
i.key = i.getKey(i.index)
|
||||
if i.upper != nil && i.cmp(i.key.UserKey, i.upper) >= 0 {
|
||||
i.index = len(i.offsets)
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
// Last implements internalIterator.Last, as documented in the pebble
|
||||
// package.
|
||||
func (i *flushableBatchIter) Last() (*InternalKey, []byte) {
|
||||
func (i *flushableBatchIter) Last() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
if len(i.offsets) == 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.index = len(i.offsets) - 1
|
||||
i.key = i.getKey(i.index)
|
||||
if i.lower != nil && i.cmp(i.key.UserKey, i.lower) < 0 {
|
||||
i.index = -1
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
// Note: flushFlushableBatchIter.Next mirrors the implementation of
|
||||
// flushableBatchIter.Next due to performance. Keep the two in sync.
|
||||
func (i *flushableBatchIter) Next() (*InternalKey, []byte) {
|
||||
func (i *flushableBatchIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if i.index == len(i.offsets) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.index++
|
||||
if i.index == len(i.offsets) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.key = i.getKey(i.index)
|
||||
if i.upper != nil && i.cmp(i.key.UserKey, i.upper) >= 0 {
|
||||
i.index = len(i.offsets)
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
func (i *flushableBatchIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *flushableBatchIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if i.index < 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.index--
|
||||
if i.index < 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.key = i.getKey(i.index)
|
||||
if i.lower != nil && i.cmp(i.key.UserKey, i.lower) < 0 {
|
||||
i.index = -1
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
func (i *flushableBatchIter) getKey(index int) InternalKey {
|
||||
@@ -1637,20 +1780,16 @@ func (i *flushableBatchIter) getKey(index int) InternalKey {
|
||||
return base.MakeInternalKey(key, i.batch.seqNum+uint64(e.index), kind)
|
||||
}
|
||||
|
||||
func (i *flushableBatchIter) Key() *InternalKey {
|
||||
return &i.key
|
||||
}
|
||||
|
||||
func (i *flushableBatchIter) Value() []byte {
|
||||
func (i *flushableBatchIter) value() base.LazyValue {
|
||||
p := i.data[i.offsets[i.index].offset:]
|
||||
if len(p) == 0 {
|
||||
i.err = base.CorruptionErrorf("corrupted batch")
|
||||
return nil
|
||||
return base.LazyValue{}
|
||||
}
|
||||
kind := InternalKeyKind(p[0])
|
||||
if kind > InternalKeyKindMax {
|
||||
i.err = base.CorruptionErrorf("corrupted batch")
|
||||
return nil
|
||||
return base.LazyValue{}
|
||||
}
|
||||
var value []byte
|
||||
var ok bool
|
||||
@@ -1661,10 +1800,10 @@ func (i *flushableBatchIter) Value() []byte {
|
||||
_, value, ok = batchDecodeStr(i.data[keyEnd:])
|
||||
if !ok {
|
||||
i.err = base.CorruptionErrorf("corrupted batch")
|
||||
return nil
|
||||
return base.LazyValue{}
|
||||
}
|
||||
}
|
||||
return value
|
||||
return base.MakeInPlaceValue(value)
|
||||
}
|
||||
|
||||
func (i *flushableBatchIter) Valid() bool {
|
||||
@@ -1698,25 +1837,29 @@ func (i *flushFlushableBatchIter) String() string {
|
||||
return "flushable-batch"
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (i *flushFlushableBatchIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (i *flushFlushableBatchIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) First() (*InternalKey, []byte) {
|
||||
func (i *flushFlushableBatchIter) First() (*InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
key, val := i.flushableBatchIter.First()
|
||||
if key == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
entryBytes := i.offsets[i.index].keyEnd - i.offsets[i.index].offset
|
||||
*i.bytesIterated += uint64(entryBytes) + i.valueSize()
|
||||
@@ -1725,21 +1868,21 @@ func (i *flushFlushableBatchIter) First() (*InternalKey, []byte) {
|
||||
|
||||
// Note: flushFlushableBatchIter.Next mirrors the implementation of
|
||||
// flushableBatchIter.Next due to performance. Keep the two in sync.
|
||||
func (i *flushFlushableBatchIter) Next() (*InternalKey, []byte) {
|
||||
func (i *flushFlushableBatchIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if i.index == len(i.offsets) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.index++
|
||||
if i.index == len(i.offsets) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.key = i.getKey(i.index)
|
||||
entryBytes := i.offsets[i.index].keyEnd - i.offsets[i.index].offset
|
||||
*i.bytesIterated += uint64(entryBytes) + i.valueSize()
|
||||
return &i.key, i.Value()
|
||||
return &i.key, i.value()
|
||||
}
|
||||
|
||||
func (i flushFlushableBatchIter) Prev() (*InternalKey, []byte) {
|
||||
func (i flushFlushableBatchIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: Prev unimplemented")
|
||||
}
|
||||
|
||||
@@ -1780,7 +1923,10 @@ func batchSort(
|
||||
) {
|
||||
b := i.(*Batch)
|
||||
if b.Indexed() {
|
||||
return b.newInternalIter(nil), b.newRangeDelIter(nil), b.newRangeKeyIter(nil)
|
||||
pointIter := b.newInternalIter(nil)
|
||||
rangeDelIter := b.newRangeDelIter(nil, math.MaxUint64)
|
||||
rangeKeyIter := b.newRangeKeyIter(nil, math.MaxUint64)
|
||||
return pointIter, rangeDelIter, rangeKeyIter
|
||||
}
|
||||
f := newFlushableBatch(b, b.db.opts.Comparer)
|
||||
return f.newIter(nil), f.newRangeDelIter(nil), f.newRangeKeyIter(nil)
|
||||
|
||||
3
vendor/github.com/cockroachdb/pebble/checkpoint.go
generated
vendored
3
vendor/github.com/cockroachdb/pebble/checkpoint.go
generated
vendored
@@ -157,7 +157,8 @@ func (d *DB) Checkpoint(
|
||||
fs := syncingFS{
|
||||
FS: d.opts.FS,
|
||||
syncOpts: vfs.SyncingFileOptions{
|
||||
BytesPerSync: d.opts.BytesPerSync,
|
||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||
BytesPerSync: d.opts.BytesPerSync,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
766
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
766
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
File diff suppressed because it is too large
Load Diff
99
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
99
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
@@ -13,7 +13,9 @@ import (
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/bytealloc"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
)
|
||||
|
||||
// compactionIter provides a forward-only iterator that encapsulates the logic
|
||||
@@ -156,7 +158,11 @@ type compactionIter struct {
|
||||
// Additionally, it is the internal state when the code is moving to the
|
||||
// next key so it can determine whether the user key has changed from
|
||||
// the previous key.
|
||||
key InternalKey
|
||||
key InternalKey
|
||||
// keyTrailer is updated when `i.key` is updated and holds the key's
|
||||
// original trailer (eg, before any sequence-number zeroing or changes to
|
||||
// key kind).
|
||||
keyTrailer uint64
|
||||
value []byte
|
||||
valueCloser io.Closer
|
||||
// Temporary buffer used for storing the previous user key in order to
|
||||
@@ -198,8 +204,11 @@ type compactionIter struct {
|
||||
// Reference to the range deletion tombstone fragmenter (e.g.,
|
||||
// `compaction.rangeDelFrag`).
|
||||
rangeDelFrag *keyspan.Fragmenter
|
||||
rangeKeyFrag *keyspan.Fragmenter
|
||||
// The fragmented tombstones.
|
||||
tombstones []keyspan.Span
|
||||
// The fragmented range keys.
|
||||
rangeKeys []keyspan.Span
|
||||
// Byte allocator for the tombstone keys.
|
||||
alloc bytealloc.A
|
||||
allowZeroSeqNum bool
|
||||
@@ -218,6 +227,7 @@ func newCompactionIter(
|
||||
iter internalIterator,
|
||||
snapshots []uint64,
|
||||
rangeDelFrag *keyspan.Fragmenter,
|
||||
rangeKeyFrag *keyspan.Fragmenter,
|
||||
allowZeroSeqNum bool,
|
||||
elideTombstone func(key []byte) bool,
|
||||
elideRangeTombstone func(start, end []byte) bool,
|
||||
@@ -229,6 +239,7 @@ func newCompactionIter(
|
||||
iter: iter,
|
||||
snapshots: snapshots,
|
||||
rangeDelFrag: rangeDelFrag,
|
||||
rangeKeyFrag: rangeKeyFrag,
|
||||
allowZeroSeqNum: allowZeroSeqNum,
|
||||
elideTombstone: elideTombstone,
|
||||
elideRangeTombstone: elideRangeTombstone,
|
||||
@@ -237,6 +248,9 @@ func newCompactionIter(
|
||||
i.rangeDelFrag.Cmp = cmp
|
||||
i.rangeDelFrag.Format = formatKey
|
||||
i.rangeDelFrag.Emit = i.emitRangeDelChunk
|
||||
i.rangeKeyFrag.Cmp = cmp
|
||||
i.rangeKeyFrag.Format = formatKey
|
||||
i.rangeKeyFrag.Emit = i.emitRangeKeyChunk
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -244,7 +258,12 @@ func (i *compactionIter) First() (*InternalKey, []byte) {
|
||||
if i.err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
i.iterKey, i.iterValue = i.iter.First()
|
||||
var iterValue LazyValue
|
||||
i.iterKey, iterValue = i.iter.First()
|
||||
i.iterValue, _, i.err = iterValue.Value(nil)
|
||||
if i.err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if i.iterKey != nil {
|
||||
i.curSnapshotIdx, i.curSnapshotSeqNum = snapshotIndex(i.iterKey.SeqNum(), i.snapshots)
|
||||
}
|
||||
@@ -282,13 +301,13 @@ func (i *compactionIter) Next() (*InternalKey, []byte) {
|
||||
i.pos = iterPosCurForward
|
||||
i.valid = false
|
||||
for i.iterKey != nil {
|
||||
if i.iterKey.Kind() == InternalKeyKindRangeDelete {
|
||||
// Return the range tombstone so the compaction can use it for
|
||||
// file truncation and add it to the fragmenter. We do not set `skip`
|
||||
// to true before returning as there may be a forthcoming point key
|
||||
// with the same user key and sequence number. Such a point key must be
|
||||
// visible (i.e., not skipped over) since we promise point keys are
|
||||
// not deleted by range tombstones at the same sequence number.
|
||||
if i.iterKey.Kind() == InternalKeyKindRangeDelete || rangekey.IsRangeKey(i.iterKey.Kind()) {
|
||||
// Return the span so the compaction can use it for file truncation and add
|
||||
// it to the relevant fragmenter. We do not set `skip` to true before
|
||||
// returning as there may be a forthcoming point key with the same user key
|
||||
// and sequence number. Such a point key must be visible (i.e., not skipped
|
||||
// over) since we promise point keys are not deleted by range tombstones at
|
||||
// the same sequence number.
|
||||
//
|
||||
// Although, note that `skip` may already be true before reaching here
|
||||
// due to an earlier key in the stripe. Then it is fine to leave it set
|
||||
@@ -456,10 +475,11 @@ func (i *compactionIter) skipInStripe() {
|
||||
}
|
||||
|
||||
func (i *compactionIter) iterNext() bool {
|
||||
i.iterKey, i.iterValue = i.iter.Next()
|
||||
// We should never see an exclusive sentinel in the compaction input.
|
||||
if i.iterKey != nil && i.iterKey.IsExclusiveSentinel() {
|
||||
panic(fmt.Sprintf("pebble: unexpected exclusive sentinel in compaction input, trailer = %x", i.iterKey.Trailer))
|
||||
var iterValue LazyValue
|
||||
i.iterKey, iterValue = i.iter.Next()
|
||||
i.iterValue, _, i.err = iterValue.Value(nil)
|
||||
if i.err != nil {
|
||||
i.iterKey = nil
|
||||
}
|
||||
return i.iterKey != nil
|
||||
}
|
||||
@@ -487,7 +507,26 @@ func (i *compactionIter) nextInStripe() stripeChangeType {
|
||||
return newStripe
|
||||
}
|
||||
key := i.iterKey
|
||||
if !i.equal(i.key.UserKey, key.UserKey) {
|
||||
|
||||
// NB: The below conditional is an optimization to avoid a user key
|
||||
// comparison in many cases. Internal keys with the same user key are
|
||||
// ordered in (strictly) descending order by trailer. If the new key has a
|
||||
// greater or equal trailer, or the previous key had a zero sequence number,
|
||||
// the new key must have a new user key.
|
||||
//
|
||||
// A couple things make these cases common:
|
||||
// - Sequence-number zeroing ensures ~all of the keys in L6 have a zero
|
||||
// sequence number.
|
||||
// - Ingested sstables' keys all adopt the same sequence number.
|
||||
if i.keyTrailer <= base.InternalKeyZeroSeqnumMaxTrailer || key.Trailer >= i.keyTrailer {
|
||||
if invariants.Enabled && i.equal(i.key.UserKey, key.UserKey) {
|
||||
prevKey := i.key
|
||||
prevKey.Trailer = i.keyTrailer
|
||||
panic(fmt.Sprintf("pebble: invariant violation: %s and %s out of order", key, prevKey))
|
||||
}
|
||||
i.curSnapshotIdx, i.curSnapshotSeqNum = snapshotIndex(key.SeqNum(), i.snapshots)
|
||||
return newStripe
|
||||
} else if !i.equal(i.key.UserKey, key.UserKey) {
|
||||
i.curSnapshotIdx, i.curSnapshotSeqNum = snapshotIndex(key.SeqNum(), i.snapshots)
|
||||
return newStripe
|
||||
}
|
||||
@@ -502,6 +541,10 @@ func (i *compactionIter) nextInStripe() stripeChangeType {
|
||||
return sameStripeNonSkippable
|
||||
}
|
||||
return newStripe
|
||||
case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
||||
// Range keys are interleaved at the max sequence number for a given user
|
||||
// key, so we should not see any more range keys in this stripe.
|
||||
panic("unreachable")
|
||||
case InternalKeyKindInvalid:
|
||||
if i.curSnapshotIdx == origSnapshotIdx {
|
||||
return sameStripeNonSkippable
|
||||
@@ -726,6 +769,7 @@ func (i *compactionIter) saveKey() {
|
||||
i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...)
|
||||
i.key.UserKey = i.keyBuf
|
||||
i.key.Trailer = i.iterKey.Trailer
|
||||
i.keyTrailer = i.iterKey.Trailer
|
||||
}
|
||||
|
||||
func (i *compactionIter) cloneKey(key []byte) []byte {
|
||||
@@ -766,9 +810,6 @@ func (i *compactionIter) Close() error {
|
||||
|
||||
// Tombstones returns a list of pending range tombstones in the fragmenter
|
||||
// up to the specified key, or all pending range tombstones if key = nil.
|
||||
// exclude specifies if the specified key is exclusive or inclusive.
|
||||
// When exclude = true, all returned range tombstones are truncated to the
|
||||
// specified key.
|
||||
func (i *compactionIter) Tombstones(key []byte) []keyspan.Span {
|
||||
if key == nil {
|
||||
i.rangeDelFrag.Finish()
|
||||
@@ -783,6 +824,22 @@ func (i *compactionIter) Tombstones(key []byte) []keyspan.Span {
|
||||
return tombstones
|
||||
}
|
||||
|
||||
// RangeKeys returns a list of pending fragmented range keys up to the specified
|
||||
// key, or all pending range keys if key = nil.
|
||||
func (i *compactionIter) RangeKeys(key []byte) []keyspan.Span {
|
||||
if key == nil {
|
||||
i.rangeKeyFrag.Finish()
|
||||
} else {
|
||||
// The specified end key is exclusive; no versions of the specified
|
||||
// user key (including range tombstones covering that key) should
|
||||
// be flushed yet.
|
||||
i.rangeKeyFrag.TruncateAndFlushTo(key)
|
||||
}
|
||||
rangeKeys := i.rangeKeys
|
||||
i.rangeKeys = nil
|
||||
return rangeKeys
|
||||
}
|
||||
|
||||
func (i *compactionIter) emitRangeDelChunk(fragmented keyspan.Span) {
|
||||
// Apply the snapshot stripe rules, keeping only the latest tombstone for
|
||||
// each snapshot stripe.
|
||||
@@ -815,6 +872,14 @@ func (i *compactionIter) emitRangeDelChunk(fragmented keyspan.Span) {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *compactionIter) emitRangeKeyChunk(fragmented keyspan.Span) {
|
||||
// Elision of snapshot stripes happens in rangeKeyCompactionTransform, so no need to
|
||||
// do that here.
|
||||
if len(fragmented.Keys) > 0 {
|
||||
i.rangeKeys = append(i.rangeKeys, fragmented)
|
||||
}
|
||||
}
|
||||
|
||||
// maybeZeroSeqnum attempts to set the seqnum for the current key to 0. Doing
|
||||
// so improves compression and enables an optimization during forward iteration
|
||||
// to skip some key comparisons. The seqnum for an entry can be zeroed if the
|
||||
|
||||
209
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
209
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
@@ -19,10 +19,7 @@ import (
|
||||
// heuristic.
|
||||
const minIntraL0Count = 4
|
||||
|
||||
const levelMultiplier = 10
|
||||
|
||||
type compactionEnv struct {
|
||||
bytesCompacted *uint64
|
||||
earliestUnflushedSeqNum uint64
|
||||
earliestSnapshotSeqNum uint64
|
||||
inProgressCompactions []compactionInfo
|
||||
@@ -146,10 +143,18 @@ type pickedCompaction struct {
|
||||
// startLevel is the level that is being compacted. Inputs from startLevel
|
||||
// and outputLevel will be merged to produce a set of outputLevel files.
|
||||
startLevel *compactionLevel
|
||||
// outputLevel is the level that files are being produced in. In default
|
||||
// compactions, outputLevel is equal to startLevel+1 except when startLevel
|
||||
// is 0 in which case it is equal to compactionPicker.baseLevel().
|
||||
|
||||
// outputLevel is the level that files are being produced in. outputLevel is
|
||||
// equal to startLevel+1 except when:
|
||||
// - if startLevel is 0, the output level equals compactionPicker.baseLevel().
|
||||
// - in multilevel compaction, the output level is the lowest level involved in
|
||||
// the compaction
|
||||
outputLevel *compactionLevel
|
||||
|
||||
// extraLevels contain additional levels in between the input and output
|
||||
// levels that get compacted in multi level compactions
|
||||
extraLevels []*compactionLevel
|
||||
|
||||
// adjustedOutputLevel is the output level used for the purpose of
|
||||
// determining the target output file size, overlap bytes, and expanded
|
||||
// bytes, taking into account the base level.
|
||||
@@ -161,7 +166,7 @@ type pickedCompaction struct {
|
||||
// where startLevel == 0 that were generated by L0Sublevels.
|
||||
lcf *manifest.L0CompactionFiles
|
||||
|
||||
// L0 sublevel info is used for compactions out of L0. It is nil for all
|
||||
// L0SublevelInfo is used for compactions out of L0. It is nil for all
|
||||
// other compactions.
|
||||
l0SublevelInfo []sublevelInfo
|
||||
|
||||
@@ -252,37 +257,71 @@ func newPickedCompactionFromL0(
|
||||
return pc
|
||||
}
|
||||
|
||||
func (pc *pickedCompaction) setupInputs(opts *Options, diskAvailBytes uint64) bool {
|
||||
// maybeExpandedBounds is a helper function for setupInputs which ensures the
|
||||
// pickedCompaction's smallest and largest internal keys are updated iff
|
||||
// the candidate keys expand the key span. This avoids a bug for multi-level
|
||||
// compactions: during the second call to setupInputs, the picked compaction's
|
||||
// smallest and largest keys should not decrease the key span.
|
||||
func (pc *pickedCompaction) maybeExpandBounds(smallest InternalKey, largest InternalKey) {
|
||||
emptyKey := InternalKey{}
|
||||
if base.InternalCompare(pc.cmp, smallest, emptyKey) == 0 {
|
||||
if base.InternalCompare(pc.cmp, largest, emptyKey) != 0 {
|
||||
panic("either both candidate keys are empty or neither are empty")
|
||||
}
|
||||
return
|
||||
}
|
||||
if base.InternalCompare(pc.cmp, pc.smallest, emptyKey) == 0 {
|
||||
if base.InternalCompare(pc.cmp, pc.largest, emptyKey) != 0 {
|
||||
panic("either both pc keys are empty or neither are empty")
|
||||
}
|
||||
pc.smallest = smallest
|
||||
pc.largest = largest
|
||||
return
|
||||
}
|
||||
if base.InternalCompare(pc.cmp, pc.smallest, smallest) >= 0 {
|
||||
pc.smallest = smallest
|
||||
}
|
||||
if base.InternalCompare(pc.cmp, pc.largest, largest) <= 0 {
|
||||
pc.largest = largest
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *pickedCompaction) setupInputs(
|
||||
opts *Options, diskAvailBytes uint64, startLevel *compactionLevel,
|
||||
) bool {
|
||||
// maxExpandedBytes is the maximum size of an expanded compaction. If
|
||||
// growing a compaction results in a larger size, the original compaction
|
||||
// is used instead.
|
||||
maxExpandedBytes := expandedCompactionByteSizeLimit(opts, pc.adjustedOutputLevel, diskAvailBytes)
|
||||
maxExpandedBytes := expandedCompactionByteSizeLimit(
|
||||
opts, pc.adjustedOutputLevel, diskAvailBytes,
|
||||
)
|
||||
|
||||
// Expand the initial inputs to a clean cut.
|
||||
var isCompacting bool
|
||||
pc.startLevel.files, isCompacting = expandToAtomicUnit(pc.cmp, pc.startLevel.files, false /* disableIsCompacting */)
|
||||
startLevel.files, isCompacting = expandToAtomicUnit(pc.cmp, startLevel.files, false /* disableIsCompacting */)
|
||||
if isCompacting {
|
||||
return false
|
||||
}
|
||||
pc.smallest, pc.largest = manifest.KeyRange(pc.cmp, pc.startLevel.files.Iter())
|
||||
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp, startLevel.files.Iter()))
|
||||
|
||||
// Determine the sstables in the output level which overlap with the input
|
||||
// sstables, and then expand those tables to a clean cut. No need to do
|
||||
// this for intra-L0 compactions; outputLevel.files is left empty for those.
|
||||
if pc.startLevel.level != pc.outputLevel.level {
|
||||
if startLevel.level != pc.outputLevel.level {
|
||||
pc.outputLevel.files = pc.version.Overlaps(pc.outputLevel.level, pc.cmp, pc.smallest.UserKey,
|
||||
pc.largest.UserKey, pc.largest.IsExclusiveSentinel())
|
||||
pc.outputLevel.files, isCompacting = expandToAtomicUnit(pc.cmp, pc.outputLevel.files, false /* disableIsCompacting */)
|
||||
pc.outputLevel.files, isCompacting = expandToAtomicUnit(pc.cmp, pc.outputLevel.files,
|
||||
false /* disableIsCompacting */)
|
||||
if isCompacting {
|
||||
return false
|
||||
}
|
||||
pc.smallest, pc.largest = manifest.KeyRange(pc.cmp,
|
||||
pc.startLevel.files.Iter(), pc.outputLevel.files.Iter())
|
||||
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp,
|
||||
startLevel.files.Iter(), pc.outputLevel.files.Iter()))
|
||||
}
|
||||
|
||||
// Grow the sstables in pc.startLevel.level as long as it doesn't affect the number
|
||||
// Grow the sstables in startLevel.level as long as it doesn't affect the number
|
||||
// of sstables included from pc.outputLevel.level.
|
||||
if pc.lcf != nil && pc.startLevel.level == 0 && pc.outputLevel.level != 0 {
|
||||
if pc.lcf != nil && startLevel.level == 0 && pc.outputLevel.level != 0 {
|
||||
// Call the L0-specific compaction extension method. Similar logic as
|
||||
// pc.grow. Additional L0 files are optionally added to the compaction at
|
||||
// this step. Note that the bounds passed in are not the bounds of the
|
||||
@@ -336,16 +375,16 @@ func (pc *pickedCompaction) setupInputs(opts *Options, diskAvailBytes uint64) bo
|
||||
}
|
||||
}
|
||||
if sizeSum+pc.outputLevel.files.SizeSum() < maxExpandedBytes {
|
||||
pc.startLevel.files = manifest.NewLevelSliceSeqSorted(newStartLevelFiles)
|
||||
startLevel.files = manifest.NewLevelSliceSeqSorted(newStartLevelFiles)
|
||||
pc.smallest, pc.largest = manifest.KeyRange(pc.cmp,
|
||||
pc.startLevel.files.Iter(), pc.outputLevel.files.Iter())
|
||||
startLevel.files.Iter(), pc.outputLevel.files.Iter())
|
||||
} else {
|
||||
*pc.lcf = oldLcf
|
||||
}
|
||||
}
|
||||
} else if pc.grow(pc.smallest, pc.largest, maxExpandedBytes) {
|
||||
pc.smallest, pc.largest = manifest.KeyRange(pc.cmp,
|
||||
pc.startLevel.files.Iter(), pc.outputLevel.files.Iter())
|
||||
} else if pc.grow(pc.smallest, pc.largest, maxExpandedBytes, startLevel) {
|
||||
pc.maybeExpandBounds(manifest.KeyRange(pc.cmp,
|
||||
startLevel.files.Iter(), pc.outputLevel.files.Iter()))
|
||||
}
|
||||
|
||||
if pc.startLevel.level == 0 {
|
||||
@@ -359,23 +398,27 @@ func (pc *pickedCompaction) setupInputs(opts *Options, diskAvailBytes uint64) bo
|
||||
// grow grows the number of inputs at c.level without changing the number of
|
||||
// c.level+1 files in the compaction, and returns whether the inputs grew. sm
|
||||
// and la are the smallest and largest InternalKeys in all of the inputs.
|
||||
func (pc *pickedCompaction) grow(sm, la InternalKey, maxExpandedBytes uint64) bool {
|
||||
func (pc *pickedCompaction) grow(
|
||||
sm, la InternalKey, maxExpandedBytes uint64, startLevel *compactionLevel,
|
||||
) bool {
|
||||
if pc.outputLevel.files.Empty() {
|
||||
return false
|
||||
}
|
||||
grow0 := pc.version.Overlaps(pc.startLevel.level, pc.cmp, sm.UserKey,
|
||||
grow0 := pc.version.Overlaps(startLevel.level, pc.cmp, sm.UserKey,
|
||||
la.UserKey, la.IsExclusiveSentinel())
|
||||
grow0, isCompacting := expandToAtomicUnit(pc.cmp, grow0, false /* disableIsCompacting */)
|
||||
if isCompacting {
|
||||
return false
|
||||
}
|
||||
if grow0.Len() <= pc.startLevel.files.Len() {
|
||||
if grow0.Len() <= startLevel.files.Len() {
|
||||
return false
|
||||
}
|
||||
if grow0.SizeSum()+pc.outputLevel.files.SizeSum() >= maxExpandedBytes {
|
||||
return false
|
||||
}
|
||||
sm1, la1 := manifest.KeyRange(pc.cmp, grow0.Iter())
|
||||
// We need to include the outputLevel iter because without it, in a multiLevel scenario,
|
||||
// sm1 and la1 could shift the output level keyspace when pc.outputLevel.files is set to grow1.
|
||||
sm1, la1 := manifest.KeyRange(pc.cmp, grow0.Iter(), pc.outputLevel.files.Iter())
|
||||
grow1 := pc.version.Overlaps(pc.outputLevel.level, pc.cmp, sm1.UserKey,
|
||||
la1.UserKey, la1.IsExclusiveSentinel())
|
||||
grow1, isCompacting = expandToAtomicUnit(pc.cmp, grow1, false /* disableIsCompacting */)
|
||||
@@ -385,11 +428,19 @@ func (pc *pickedCompaction) grow(sm, la InternalKey, maxExpandedBytes uint64) bo
|
||||
if grow1.Len() != pc.outputLevel.files.Len() {
|
||||
return false
|
||||
}
|
||||
pc.startLevel.files = grow0
|
||||
startLevel.files = grow0
|
||||
pc.outputLevel.files = grow1
|
||||
return true
|
||||
}
|
||||
|
||||
// initMultiLevelCompaction returns true if it initiated a multilevel input
|
||||
// compaction. This currently never inits a multiLevel compaction.
|
||||
func (pc *pickedCompaction) initMultiLevelCompaction(
|
||||
opts *Options, vers *version, levelMaxBytes [7]int64, diskAvailBytes uint64,
|
||||
) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// expandToAtomicUnit expands the provided level slice within its level both
|
||||
// forwards and backwards to its "atomic compaction unit" boundaries, if
|
||||
// necessary.
|
||||
@@ -453,7 +504,7 @@ func expandToAtomicUnit(
|
||||
iter := start.Clone()
|
||||
iter.Prev()
|
||||
for cur, prev := start.Current(), iter.Current(); prev != nil; cur, prev = start.Prev(), iter.Prev() {
|
||||
if cur.Compacting {
|
||||
if cur.IsCompacting() {
|
||||
isCompacting = true
|
||||
}
|
||||
if cmp(prev.Largest.UserKey, cur.Smallest.UserKey) < 0 {
|
||||
@@ -471,7 +522,7 @@ func expandToAtomicUnit(
|
||||
iter = end.Clone()
|
||||
iter.Next()
|
||||
for cur, next := end.Current(), iter.Current(); next != nil; cur, next = end.Next(), iter.Next() {
|
||||
if cur.Compacting {
|
||||
if cur.IsCompacting() {
|
||||
isCompacting = true
|
||||
}
|
||||
if cmp(cur.Largest.UserKey, next.Smallest.UserKey) < 0 {
|
||||
@@ -487,7 +538,8 @@ func expandToAtomicUnit(
|
||||
}
|
||||
})
|
||||
inputIter := inputs.Iter()
|
||||
isCompacting = !disableIsCompacting && (isCompacting || inputIter.First().Compacting || inputIter.Last().Compacting)
|
||||
isCompacting = !disableIsCompacting &&
|
||||
(isCompacting || inputIter.First().IsCompacting() || inputIter.Last().IsCompacting())
|
||||
return inputs, isCompacting
|
||||
}
|
||||
|
||||
@@ -525,11 +577,11 @@ type candidateLevelInfo struct {
|
||||
|
||||
// compensatedSize returns f's file size, inflated according to compaction
|
||||
// priorities.
|
||||
func compensatedSize(f *fileMetadata) uint64 {
|
||||
func compensatedSize(f *fileMetadata, pointTombstoneWeight float64) uint64 {
|
||||
sz := f.Size
|
||||
// Add in the estimate of disk space that may be reclaimed by compacting
|
||||
// the file's tombstones.
|
||||
sz += f.Stats.PointDeletionsBytesEstimate
|
||||
sz += uint64(float64(f.Stats.PointDeletionsBytesEstimate) * pointTombstoneWeight)
|
||||
sz += f.Stats.RangeDeletionsBytesEstimate
|
||||
return sz
|
||||
}
|
||||
@@ -539,7 +591,9 @@ func compensatedSize(f *fileMetadata) uint64 {
|
||||
// a *uint64. Compensated sizes may change once a table's stats are loaded
|
||||
// asynchronously, so its values are marked as cacheable only if a file's
|
||||
// stats have been loaded.
|
||||
type compensatedSizeAnnotator struct{}
|
||||
type compensatedSizeAnnotator struct {
|
||||
pointTombstoneWeight float64
|
||||
}
|
||||
|
||||
var _ manifest.Annotator = compensatedSizeAnnotator{}
|
||||
|
||||
@@ -556,8 +610,8 @@ func (a compensatedSizeAnnotator) Accumulate(
|
||||
f *fileMetadata, dst interface{},
|
||||
) (v interface{}, cacheOK bool) {
|
||||
vptr := dst.(*uint64)
|
||||
*vptr = *vptr + compensatedSize(f)
|
||||
return vptr, f.Stats.Valid
|
||||
*vptr = *vptr + compensatedSize(f, a.pointTombstoneWeight)
|
||||
return vptr, f.StatsValidLocked()
|
||||
}
|
||||
|
||||
func (a compensatedSizeAnnotator) Merge(src interface{}, dst interface{}) interface{} {
|
||||
@@ -571,10 +625,10 @@ func (a compensatedSizeAnnotator) Merge(src interface{}, dst interface{}) interf
|
||||
// iterator. Note that this function is linear in the files available to the
|
||||
// iterator. Use the compensatedSizeAnnotator if querying the total
|
||||
// compensated size of a level.
|
||||
func totalCompensatedSize(iter manifest.LevelIterator) uint64 {
|
||||
func totalCompensatedSize(iter manifest.LevelIterator, pointTombstoneWeight float64) uint64 {
|
||||
var sz uint64
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
sz += compensatedSize(f)
|
||||
sz += compensatedSize(f, pointTombstoneWeight)
|
||||
}
|
||||
return sz
|
||||
}
|
||||
@@ -732,11 +786,11 @@ func (p *compactionPickerByScore) initLevelMaxBytes(inProgressCompactions []comp
|
||||
}
|
||||
|
||||
dbSize += p.levelSizes[0]
|
||||
bottomLevelSize := dbSize - dbSize/levelMultiplier
|
||||
bottomLevelSize := dbSize - dbSize/int64(p.opts.Experimental.LevelMultiplier)
|
||||
|
||||
curLevelSize := bottomLevelSize
|
||||
for level := numLevels - 2; level >= firstNonEmptyLevel; level-- {
|
||||
curLevelSize = int64(float64(curLevelSize) / levelMultiplier)
|
||||
curLevelSize = int64(float64(curLevelSize) / float64(p.opts.Experimental.LevelMultiplier))
|
||||
}
|
||||
|
||||
// Compute base level (where L0 data is compacted to).
|
||||
@@ -744,7 +798,7 @@ func (p *compactionPickerByScore) initLevelMaxBytes(inProgressCompactions []comp
|
||||
p.baseLevel = firstNonEmptyLevel
|
||||
for p.baseLevel > 1 && curLevelSize > baseBytesMax {
|
||||
p.baseLevel--
|
||||
curLevelSize = int64(float64(curLevelSize) / levelMultiplier)
|
||||
curLevelSize = int64(float64(curLevelSize) / float64(p.opts.Experimental.LevelMultiplier))
|
||||
}
|
||||
|
||||
smoothedLevelMultiplier := 1.0
|
||||
@@ -772,7 +826,9 @@ func (p *compactionPickerByScore) initLevelMaxBytes(inProgressCompactions []comp
|
||||
}
|
||||
}
|
||||
|
||||
func calculateSizeAdjust(inProgressCompactions []compactionInfo) [numLevels]int64 {
|
||||
func calculateSizeAdjust(
|
||||
inProgressCompactions []compactionInfo, pointTombstoneWeight float64,
|
||||
) [numLevels]int64 {
|
||||
// Compute a size adjustment for each level based on the in-progress
|
||||
// compactions. We subtract the compensated size of start level inputs.
|
||||
// Since compensated file sizes may be compensated because they reclaim
|
||||
@@ -785,7 +841,7 @@ func calculateSizeAdjust(inProgressCompactions []compactionInfo) [numLevels]int6
|
||||
|
||||
for _, input := range c.inputs {
|
||||
real := int64(input.files.SizeSum())
|
||||
compensated := int64(totalCompensatedSize(input.files.Iter()))
|
||||
compensated := int64(totalCompensatedSize(input.files.Iter(), pointTombstoneWeight))
|
||||
|
||||
if input.level != c.outputLevel {
|
||||
sizeAdjust[input.level] -= compensated
|
||||
@@ -812,7 +868,10 @@ func (p *compactionPickerByScore) calculateScores(
|
||||
}
|
||||
scores[0] = p.calculateL0Score(inProgressCompactions)
|
||||
|
||||
sizeAdjust := calculateSizeAdjust(inProgressCompactions)
|
||||
sizeAdjust := calculateSizeAdjust(
|
||||
inProgressCompactions,
|
||||
p.opts.Experimental.PointTombstoneWeight,
|
||||
)
|
||||
for level := 1; level < numLevels; level++ {
|
||||
levelSize := int64(levelCompensatedSize(p.vers.Levels[level])) + sizeAdjust[level]
|
||||
scores[level].score = float64(levelSize) / float64(p.levelMaxBytes[level])
|
||||
@@ -936,10 +995,10 @@ func (p *compactionPickerByScore) pickFile(
|
||||
outputFile = outputIter.Next()
|
||||
}
|
||||
|
||||
compacting := f.Compacting
|
||||
compacting := f.IsCompacting()
|
||||
for outputFile != nil && base.InternalCompare(cmp, outputFile.Smallest, f.Largest) < 0 {
|
||||
overlappingBytes += outputFile.Size
|
||||
compacting = compacting || outputFile.Compacting
|
||||
compacting = compacting || outputFile.IsCompacting()
|
||||
|
||||
// For files in the bottommost level of the LSM, the
|
||||
// Stats.RangeDeletionsBytesEstimate field is set to the estimate
|
||||
@@ -969,8 +1028,9 @@ func (p *compactionPickerByScore) pickFile(
|
||||
continue
|
||||
}
|
||||
|
||||
scaledRatio := overlappingBytes * 1024 / compensatedSize(f)
|
||||
if scaledRatio < smallestRatio && !f.Compacting {
|
||||
compSz := compensatedSize(f, p.opts.Experimental.PointTombstoneWeight)
|
||||
scaledRatio := overlappingBytes * 1024 / compSz
|
||||
if scaledRatio < smallestRatio && !f.IsCompacting() {
|
||||
smallestRatio = scaledRatio
|
||||
file = startIter.Take()
|
||||
}
|
||||
@@ -1031,7 +1091,9 @@ func (p *compactionPickerByScore) pickAuto(env compactionEnv) (pc *pickedCompact
|
||||
}
|
||||
fmt.Fprintf(&buf, " %sL%d: %5.1f %5.1f %8s %8s",
|
||||
marker, info.level, info.score, info.origScore,
|
||||
humanize.Int64(int64(totalCompensatedSize(p.vers.Levels[info.level].Iter()))),
|
||||
humanize.Int64(int64(totalCompensatedSize(
|
||||
p.vers.Levels[info.level].Iter(), p.opts.Experimental.PointTombstoneWeight,
|
||||
))),
|
||||
humanize.Int64(p.levelMaxBytes[info.level]),
|
||||
)
|
||||
|
||||
@@ -1092,7 +1154,7 @@ func (p *compactionPickerByScore) pickAuto(env compactionEnv) (pc *pickedCompact
|
||||
continue
|
||||
}
|
||||
|
||||
pc := pickAutoLPositive(env, p.opts, p.vers, *info, p.baseLevel, p.diskAvailBytes)
|
||||
pc := pickAutoLPositive(env, p.opts, p.vers, *info, p.baseLevel, p.diskAvailBytes, p.levelMaxBytes)
|
||||
// Fail-safe to protect against compacting the same sstable concurrently.
|
||||
if pc != nil && !inputRangeAlreadyCompacting(env, pc) {
|
||||
pc.score = info.score
|
||||
@@ -1172,10 +1234,10 @@ func (a elisionOnlyAnnotator) Zero(interface{}) interface{} {
|
||||
}
|
||||
|
||||
func (a elisionOnlyAnnotator) Accumulate(f *fileMetadata, dst interface{}) (interface{}, bool) {
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
return dst, true
|
||||
}
|
||||
if !f.Stats.Valid {
|
||||
if !f.StatsValidLocked() {
|
||||
return dst, false
|
||||
}
|
||||
// Bottommost files are large and not worthwhile to compact just
|
||||
@@ -1187,8 +1249,13 @@ func (a elisionOnlyAnnotator) Accumulate(f *fileMetadata, dst interface{}) (inte
|
||||
// which may be collapsed. Ideally, we would have 'obsolete keys'
|
||||
// statistics that would include tombstones, the keys that are
|
||||
// dropped by tombstones and duplicated user keys. See #847.
|
||||
//
|
||||
// Note that tables that contain exclusively range keys (i.e. no point keys,
|
||||
// `NumEntries` and `RangeDeletionsBytesEstimate` are both zero) are excluded
|
||||
// from elision-only compactions.
|
||||
// TODO(travers): Consider an alternative heuristic for elision of range-keys.
|
||||
if f.Stats.RangeDeletionsBytesEstimate*10 < f.Size &&
|
||||
f.Stats.NumDeletions*10 < f.Stats.NumEntries {
|
||||
f.Stats.NumDeletions*10 <= f.Stats.NumEntries {
|
||||
return dst, true
|
||||
}
|
||||
if dst == nil {
|
||||
@@ -1266,7 +1333,7 @@ func (p *compactionPickerByScore) pickElisionOnlyCompaction(
|
||||
return nil
|
||||
}
|
||||
candidate := v.(*fileMetadata)
|
||||
if candidate.Compacting || candidate.LargestSeqNum >= env.earliestSnapshotSeqNum {
|
||||
if candidate.IsCompacting() || candidate.LargestSeqNum >= env.earliestSnapshotSeqNum {
|
||||
return nil
|
||||
}
|
||||
lf := p.vers.Levels[numLevels-1].Find(p.opts.Comparer.Compare, candidate)
|
||||
@@ -1304,7 +1371,7 @@ func (p *compactionPickerByScore) pickRewriteCompaction(env compactionEnv) (pc *
|
||||
continue
|
||||
}
|
||||
candidate := v.(*fileMetadata)
|
||||
if candidate.Compacting {
|
||||
if candidate.IsCompacting() {
|
||||
// Try the next level.
|
||||
continue
|
||||
}
|
||||
@@ -1364,6 +1431,7 @@ func pickAutoLPositive(
|
||||
cInfo candidateLevelInfo,
|
||||
baseLevel int,
|
||||
diskAvailBytes func() uint64,
|
||||
levelMaxBytes [7]int64,
|
||||
) (pc *pickedCompaction) {
|
||||
if cInfo.level == 0 {
|
||||
panic("pebble: pickAutoLPositive called for L0")
|
||||
@@ -1385,9 +1453,15 @@ func pickAutoLPositive(
|
||||
}
|
||||
}
|
||||
|
||||
if !pc.setupInputs(opts, diskAvailBytes()) {
|
||||
if !pc.setupInputs(opts, diskAvailBytes(), pc.startLevel) {
|
||||
return nil
|
||||
}
|
||||
if opts.Experimental.MultiLevelCompaction &&
|
||||
pc.initMultiLevelCompaction(opts, vers, levelMaxBytes, diskAvailBytes()) {
|
||||
if !pc.setupInputs(opts, diskAvailBytes(), pc.extraLevels[len(pc.extraLevels)-1]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return pc
|
||||
}
|
||||
|
||||
@@ -1410,7 +1484,7 @@ func pickL0(
|
||||
}
|
||||
if lcf != nil {
|
||||
pc = newPickedCompactionFromL0(lcf, opts, vers, baseLevel, true)
|
||||
pc.setupInputs(opts, diskAvailBytes())
|
||||
pc.setupInputs(opts, diskAvailBytes(), pc.startLevel)
|
||||
if pc.startLevel.files.Empty() {
|
||||
opts.Logger.Fatalf("empty compaction chosen")
|
||||
}
|
||||
@@ -1428,7 +1502,7 @@ func pickL0(
|
||||
}
|
||||
if lcf != nil {
|
||||
pc = newPickedCompactionFromL0(lcf, opts, vers, 0, false)
|
||||
if !pc.setupInputs(opts, diskAvailBytes()) {
|
||||
if !pc.setupInputs(opts, diskAvailBytes(), pc.startLevel) {
|
||||
return nil
|
||||
}
|
||||
if pc.startLevel.files.Empty() {
|
||||
@@ -1474,12 +1548,16 @@ func (p *compactionPickerByScore) pickManual(
|
||||
if conflictsWithInProgress(manual, outputLevel, env.inProgressCompactions, p.opts.Comparer.Compare) {
|
||||
return nil, true
|
||||
}
|
||||
pc = pickManualHelper(p.opts, manual, p.vers, p.baseLevel, p.diskAvailBytes)
|
||||
pc = pickManualHelper(p.opts, manual, p.vers, p.baseLevel, p.diskAvailBytes, p.levelMaxBytes)
|
||||
if pc == nil {
|
||||
return nil, false
|
||||
}
|
||||
if pc.outputLevel.level != outputLevel {
|
||||
panic("pebble: compaction picked unexpected output level")
|
||||
if len(pc.extraLevels) > 0 {
|
||||
// multilevel compactions relax this invariant
|
||||
} else {
|
||||
panic("pebble: compaction picked unexpected output level")
|
||||
}
|
||||
}
|
||||
// Fail-safe to protect against compacting the same sstable concurrently.
|
||||
if inputRangeAlreadyCompacting(env, pc) {
|
||||
@@ -1494,6 +1572,7 @@ func pickManualHelper(
|
||||
vers *version,
|
||||
baseLevel int,
|
||||
diskAvailBytes func() uint64,
|
||||
levelMaxBytes [7]int64,
|
||||
) (pc *pickedCompaction) {
|
||||
pc = newPickedCompaction(opts, vers, manual.level, defaultOutputLevel(manual.level, baseLevel), baseLevel)
|
||||
manual.outputLevel = pc.outputLevel.level
|
||||
@@ -1503,9 +1582,15 @@ func pickManualHelper(
|
||||
// Nothing to do
|
||||
return nil
|
||||
}
|
||||
if !pc.setupInputs(opts, diskAvailBytes()) {
|
||||
if !pc.setupInputs(opts, diskAvailBytes(), pc.startLevel) {
|
||||
return nil
|
||||
}
|
||||
if opts.Experimental.MultiLevelCompaction && pc.startLevel.level > 0 &&
|
||||
pc.initMultiLevelCompaction(opts, vers, levelMaxBytes, diskAvailBytes()) {
|
||||
if !pc.setupInputs(opts, diskAvailBytes(), pc.extraLevels[len(pc.extraLevels)-1]) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return pc
|
||||
}
|
||||
|
||||
@@ -1554,7 +1639,7 @@ func pickReadTriggeredCompactionHelper(
|
||||
pc = newPickedCompaction(p.opts, p.vers, rc.level, defaultOutputLevel(rc.level, p.baseLevel), p.baseLevel)
|
||||
|
||||
pc.startLevel.files = overlapSlice
|
||||
if !pc.setupInputs(p.opts, p.diskAvailBytes()) {
|
||||
if !pc.setupInputs(p.opts, p.diskAvailBytes(), pc.startLevel) {
|
||||
return nil
|
||||
}
|
||||
if inputRangeAlreadyCompacting(env, pc) {
|
||||
@@ -1588,7 +1673,7 @@ func inputRangeAlreadyCompacting(env compactionEnv, pc *pickedCompaction) bool {
|
||||
for _, cl := range pc.inputs {
|
||||
iter := cl.files.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
411
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
411
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
"github.com/cockroachdb/pebble/vfs"
|
||||
"github.com/cockroachdb/pebble/vfs/atomicfs"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -140,22 +141,12 @@ type Writer interface {
|
||||
// It is safe to modify the contents of the arguments after Set returns.
|
||||
Set(key, value []byte, o *WriteOptions) error
|
||||
|
||||
// Experimental returns the experimental write API.
|
||||
Experimental() ExperimentalWriter
|
||||
}
|
||||
|
||||
// ExperimentalWriter provides access to experimental features of a Batch.
|
||||
type ExperimentalWriter interface {
|
||||
Writer
|
||||
|
||||
// RangeKeySet sets a range key mapping the key range [start, end) at the MVCC
|
||||
// timestamp suffix to value. The suffix is optional. If any portion of the key
|
||||
// range [start, end) is already set by a range key with the same suffix value,
|
||||
// RangeKeySet overrides it.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeySet returns.
|
||||
//
|
||||
// WARNING: This is an experimental feature with limited functionality.
|
||||
RangeKeySet(start, end, suffix, value []byte, opts *WriteOptions) error
|
||||
|
||||
// RangeKeyUnset removes a range key mapping the key range [start, end) at the
|
||||
@@ -166,8 +157,6 @@ type ExperimentalWriter interface {
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyUnset
|
||||
// returns.
|
||||
//
|
||||
// WARNING: This is an experimental feature with limited functionality.
|
||||
RangeKeyUnset(start, end, suffix []byte, opts *WriteOptions) error
|
||||
|
||||
// RangeKeyDelete deletes all of the range keys in the range [start,end)
|
||||
@@ -177,11 +166,43 @@ type ExperimentalWriter interface {
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyDelete
|
||||
// returns.
|
||||
//
|
||||
// WARNING: This is an experimental feature with limited functionality.
|
||||
RangeKeyDelete(start, end []byte, opts *WriteOptions) error
|
||||
}
|
||||
|
||||
// CPUWorkHandle represents a handle used by the CPUWorkPermissionGranter API.
|
||||
type CPUWorkHandle interface {
|
||||
// Permitted indicates whether Pebble can use additional CPU resources.
|
||||
Permitted() bool
|
||||
}
|
||||
|
||||
// CPUWorkPermissionGranter is used to request permission to opportunistically
|
||||
// use additional CPUs to speed up internal background work.
|
||||
type CPUWorkPermissionGranter interface {
|
||||
// GetPermission returns a handle regardless of whether permission is granted
|
||||
// or not. In the latter case, the handle is only useful for recording
|
||||
// the CPU time actually spent on this calling goroutine.
|
||||
GetPermission(time.Duration) CPUWorkHandle
|
||||
// CPUWorkDone must be called regardless of whether CPUWorkHandle.Permitted
|
||||
// returns true or false.
|
||||
CPUWorkDone(CPUWorkHandle)
|
||||
}
|
||||
|
||||
// Use a default implementation for the CPU work granter to avoid excessive nil
|
||||
// checks in the code.
|
||||
type defaultCPUWorkHandle struct{}
|
||||
|
||||
func (d defaultCPUWorkHandle) Permitted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type defaultCPUWorkGranter struct{}
|
||||
|
||||
func (d defaultCPUWorkGranter) GetPermission(_ time.Duration) CPUWorkHandle {
|
||||
return defaultCPUWorkHandle{}
|
||||
}
|
||||
|
||||
func (d defaultCPUWorkGranter) CPUWorkDone(_ CPUWorkHandle) {}
|
||||
|
||||
// DB provides a concurrent, persistent ordered key/value store.
|
||||
//
|
||||
// A DB's basic operations (Get, Set, Delete) should be self-explanatory. Get
|
||||
@@ -222,16 +243,6 @@ type DB struct {
|
||||
memTableCount int64
|
||||
memTableReserved int64 // number of bytes reserved in the cache for memtables
|
||||
|
||||
// bytesFlushed is the number of bytes flushed in the current flush. This
|
||||
// must be read/written atomically since it is accessed by both the flush
|
||||
// and compaction routines.
|
||||
bytesFlushed uint64
|
||||
|
||||
// bytesCompacted is the number of bytes compacted in the current compaction.
|
||||
// This is used as a dummy variable to increment during compaction, and the
|
||||
// value is not used anywhere.
|
||||
bytesCompacted uint64
|
||||
|
||||
// The size of the current log file (i.e. db.mu.log.queue[len(queue)-1].
|
||||
logSize uint64
|
||||
|
||||
@@ -281,9 +292,7 @@ type DB struct {
|
||||
closed *atomic.Value
|
||||
closedCh chan struct{}
|
||||
|
||||
compactionLimiter limiter
|
||||
flushLimiter limiter
|
||||
deletionLimiter limiter
|
||||
deletionLimiter limiter
|
||||
|
||||
// Async deletion jobs spawned by cleaners increment this WaitGroup, and
|
||||
// call Done when completed. Once `d.mu.cleaning` is false, the db.Close()
|
||||
@@ -355,7 +364,10 @@ type DB struct {
|
||||
// (i.e. makeRoomForWrite).
|
||||
*record.LogWriter
|
||||
// Can be nil.
|
||||
metrics *record.LogWriterMetrics
|
||||
metrics struct {
|
||||
fsyncLatency prometheus.Histogram
|
||||
record.LogWriterMetrics
|
||||
}
|
||||
}
|
||||
|
||||
mem struct {
|
||||
@@ -461,11 +473,6 @@ type DB struct {
|
||||
}
|
||||
}
|
||||
|
||||
// rangeKeys is a temporary field so that Pebble can provide a non-durable
|
||||
// implementation of range keys in advance of the real implementation.
|
||||
// TODO(jackson): Remove this.
|
||||
rangeKeys *RangeKeysArena
|
||||
|
||||
// Normally equal to time.Now() but may be overridden in tests.
|
||||
timeNow func() time.Time
|
||||
}
|
||||
@@ -542,15 +549,13 @@ func (d *DB) getInternal(key []byte, b *Batch, s *Snapshot) ([]byte, io.Closer,
|
||||
}
|
||||
|
||||
i := &buf.dbi
|
||||
pointIter := base.WrapIterWithStats(get)
|
||||
pointIter := get
|
||||
*i = Iterator{
|
||||
getIterAlloc: buf,
|
||||
cmp: d.cmp,
|
||||
equal: d.equal,
|
||||
iter: pointIter,
|
||||
pointIter: pointIter,
|
||||
merge: d.merge,
|
||||
split: d.split,
|
||||
comparer: *d.opts.Comparer,
|
||||
readState: readState,
|
||||
keyBuf: buf.keyBuf,
|
||||
}
|
||||
@@ -658,21 +663,16 @@ func (d *DB) LogData(data []byte, opts *WriteOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Experimental returns the experimental write API.
|
||||
func (d *DB) Experimental() ExperimentalWriter {
|
||||
return experimentalDB{d}
|
||||
}
|
||||
|
||||
type experimentalDB struct {
|
||||
*DB
|
||||
}
|
||||
|
||||
// RangeKeySet implements the ExperimentalWriter interface.
|
||||
func (e experimentalDB) RangeKeySet(start, end, suffix, value []byte, opts *WriteOptions) error {
|
||||
b := newBatch(e.DB)
|
||||
eb := b.Experimental()
|
||||
_ = eb.RangeKeySet(start, end, suffix, value, opts)
|
||||
if err := e.Apply(b, opts); err != nil {
|
||||
// RangeKeySet sets a range key mapping the key range [start, end) at the MVCC
|
||||
// timestamp suffix to value. The suffix is optional. If any portion of the key
|
||||
// range [start, end) is already set by a range key with the same suffix value,
|
||||
// RangeKeySet overrides it.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeySet returns.
|
||||
func (d *DB) RangeKeySet(start, end, suffix, value []byte, opts *WriteOptions) error {
|
||||
b := newBatch(d)
|
||||
_ = b.RangeKeySet(start, end, suffix, value, opts)
|
||||
if err := d.Apply(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
// Only release the batch on success.
|
||||
@@ -680,12 +680,18 @@ func (e experimentalDB) RangeKeySet(start, end, suffix, value []byte, opts *Writ
|
||||
return nil
|
||||
}
|
||||
|
||||
// RangeKeyUnset implements the ExperimentalWriter interface.
|
||||
func (e experimentalDB) RangeKeyUnset(start, end, suffix []byte, opts *WriteOptions) error {
|
||||
b := newBatch(e.DB)
|
||||
eb := b.Experimental()
|
||||
_ = eb.RangeKeyUnset(start, end, suffix, opts)
|
||||
if err := e.Apply(b, opts); err != nil {
|
||||
// RangeKeyUnset removes a range key mapping the key range [start, end) at the
|
||||
// MVCC timestamp suffix. The suffix may be omitted to remove an unsuffixed
|
||||
// range key. RangeKeyUnset only removes portions of range keys that fall within
|
||||
// the [start, end) key span, and only range keys with suffixes that exactly
|
||||
// match the unset suffix.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyUnset
|
||||
// returns.
|
||||
func (d *DB) RangeKeyUnset(start, end, suffix []byte, opts *WriteOptions) error {
|
||||
b := newBatch(d)
|
||||
_ = b.RangeKeyUnset(start, end, suffix, opts)
|
||||
if err := d.Apply(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
// Only release the batch on success.
|
||||
@@ -693,12 +699,17 @@ func (e experimentalDB) RangeKeyUnset(start, end, suffix []byte, opts *WriteOpti
|
||||
return nil
|
||||
}
|
||||
|
||||
// RangeKeyDelete implements the ExperimentalWriter interface.
|
||||
func (e experimentalDB) RangeKeyDelete(start, end []byte, opts *WriteOptions) error {
|
||||
b := newBatch(e.DB)
|
||||
eb := b.Experimental()
|
||||
_ = eb.RangeKeyDelete(start, end, opts)
|
||||
if err := e.Apply(b, opts); err != nil {
|
||||
// RangeKeyDelete deletes all of the range keys in the range [start,end)
|
||||
// (inclusive on start, exclusive on end). It does not delete point keys (for
|
||||
// that use DeleteRange). RangeKeyDelete removes all range keys within the
|
||||
// bounds, including those with or without suffixes.
|
||||
//
|
||||
// It is safe to modify the contents of the arguments after RangeKeyDelete
|
||||
// returns.
|
||||
func (d *DB) RangeKeyDelete(start, end []byte, opts *WriteOptions) error {
|
||||
b := newBatch(d)
|
||||
_ = b.RangeKeyDelete(start, end, opts)
|
||||
if err := d.Apply(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
// Only release the batch on success.
|
||||
@@ -735,9 +746,6 @@ func (d *DB) Apply(batch *Batch, opts *WriteOptions) error {
|
||||
if d.split == nil {
|
||||
return errNoSplit
|
||||
}
|
||||
if d.opts.Experimental.RangeKeys == nil {
|
||||
panic("pebble: range keys require the Experimental.RangeKeys option")
|
||||
}
|
||||
if d.FormatMajorVersion() < FormatRangeKeys {
|
||||
panic(fmt.Sprintf(
|
||||
"pebble: range keys require at least format major version %d (current: %d)",
|
||||
@@ -785,9 +793,18 @@ func (d *DB) commitApply(b *Batch, mem *memTable) error {
|
||||
// If the batch contains range tombstones and the database is configured
|
||||
// to flush range deletions, schedule a delayed flush so that disk space
|
||||
// may be reclaimed without additional writes or an explicit flush.
|
||||
if b.countRangeDels > 0 && d.opts.Experimental.DeleteRangeFlushDelay > 0 {
|
||||
if b.countRangeDels > 0 && d.opts.FlushDelayDeleteRange > 0 {
|
||||
d.mu.Lock()
|
||||
d.maybeScheduleDelayedFlush(mem)
|
||||
d.maybeScheduleDelayedFlush(mem, d.opts.FlushDelayDeleteRange)
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
// If the batch contains range keys and the database is configured to flush
|
||||
// range keys, schedule a delayed flush so that the range keys are cleared
|
||||
// from the memtable.
|
||||
if b.countRangeKeys > 0 && d.opts.FlushDelayRangeKey > 0 {
|
||||
d.mu.Lock()
|
||||
d.maybeScheduleDelayedFlush(mem, d.opts.FlushDelayRangeKey)
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -881,9 +898,6 @@ func (d *DB) newIterInternal(batch *Batch, s *Snapshot, o *IterOptions) *Iterato
|
||||
panic(err)
|
||||
}
|
||||
if o.rangeKeys() {
|
||||
if d.opts.Experimental.RangeKeys == nil {
|
||||
panic("pebble: range keys require the Experimental.RangeKeys option")
|
||||
}
|
||||
if d.FormatMajorVersion() < FormatRangeKeys {
|
||||
panic(fmt.Sprintf(
|
||||
"pebble: range keys require at least format major version %d (current: %d)",
|
||||
@@ -921,24 +935,28 @@ func (d *DB) newIterInternal(batch *Batch, s *Snapshot, o *IterOptions) *Iterato
|
||||
dbi := &buf.dbi
|
||||
*dbi = Iterator{
|
||||
alloc: buf,
|
||||
cmp: d.cmp,
|
||||
equal: d.equal,
|
||||
merge: d.merge,
|
||||
split: d.split,
|
||||
comparer: *d.opts.Comparer,
|
||||
readState: readState,
|
||||
keyBuf: buf.keyBuf,
|
||||
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
|
||||
boundsBuf: buf.boundsBuf,
|
||||
batch: batch,
|
||||
newIters: d.newIters,
|
||||
newIterRangeKey: d.tableNewRangeKeyIter,
|
||||
seqNum: seqNum,
|
||||
db: d,
|
||||
}
|
||||
if o != nil {
|
||||
dbi.opts = *o
|
||||
dbi.saveBounds(o.LowerBound, o.UpperBound)
|
||||
}
|
||||
dbi.opts.logger = d.opts.Logger
|
||||
if d.opts.private.disableLazyCombinedIteration {
|
||||
dbi.opts.disableLazyCombinedIteration = true
|
||||
}
|
||||
if batch != nil {
|
||||
dbi.batchSeqNum = dbi.batch.nextSeqNum()
|
||||
}
|
||||
return finishInitializingIter(buf)
|
||||
}
|
||||
|
||||
@@ -963,45 +981,95 @@ func finishInitializingIter(buf *iterAlloc) *Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the point iterator. This function either returns a pointer to
|
||||
// dbi.merging (if points are enabled) or an emptyIter. If this is called
|
||||
// during a SetOptions call and this Iterator has already initialized
|
||||
// dbi.merging, constructPointIter returns the existing, unmodified point
|
||||
// iterator which is stored in dbi.pointIter.
|
||||
dbi.iter = constructPointIter(dbi, dbi.batch, memtables, buf)
|
||||
if dbi.opts.pointKeys() {
|
||||
// Construct the point iterator, initializing dbi.pointIter to point to
|
||||
// dbi.merging. If this is called during a SetOptions call and this
|
||||
// Iterator has already initialized dbi.merging, constructPointIter is a
|
||||
// noop and an initialized pointIter already exists in dbi.pointIter.
|
||||
dbi.constructPointIter(memtables, buf)
|
||||
dbi.iter = dbi.pointIter
|
||||
} else {
|
||||
dbi.iter = emptyIter
|
||||
}
|
||||
|
||||
// If range keys are enabled, construct the range key iterator stack too.
|
||||
if dbi.opts.rangeKeys() {
|
||||
if dbi.rangeKey == nil {
|
||||
dbi.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
dbi.rangeKey.keys.cmp = dbi.cmp
|
||||
dbi.rangeKey.rangeKeyIter = dbi.db.newRangeKeyIter(dbi.rangeKey, dbi.seqNum, dbi.batch, dbi.readState, &dbi.opts)
|
||||
dbi.rangeKeyMasking.init(dbi, dbi.comparer.Compare, dbi.comparer.Split)
|
||||
|
||||
// When iterating over both point and range keys, don't create the
|
||||
// range-key iterator stack immediately if we can avoid it. This
|
||||
// optimization takes advantage of the expected sparseness of range
|
||||
// keys, and configures the point-key iterator to dynamically switch to
|
||||
// combined iteration when it observes a file containing range keys.
|
||||
//
|
||||
// Lazy combined iteration is not possible if a batch or a memtable
|
||||
// contains any range keys.
|
||||
useLazyCombinedIteration := dbi.rangeKey == nil &&
|
||||
dbi.opts.KeyTypes == IterKeyTypePointsAndRanges &&
|
||||
(dbi.batch == nil || dbi.batch.countRangeKeys == 0) &&
|
||||
!dbi.opts.disableLazyCombinedIteration
|
||||
if useLazyCombinedIteration {
|
||||
// The user requested combined iteration, and there's no indexed
|
||||
// batch currently containing range keys that would prevent lazy
|
||||
// combined iteration. Check the memtables to see if they contain
|
||||
// any range keys.
|
||||
for i := range memtables {
|
||||
if memtables[i].containsRangeKeys() {
|
||||
useLazyCombinedIteration = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the point iterator (currently dbi.iter) with an interleaving
|
||||
// iterator that interleaves range keys pulled from
|
||||
// dbi.rangeKey.rangeKeyIter.
|
||||
if useLazyCombinedIteration {
|
||||
dbi.lazyCombinedIter = lazyCombinedIter{
|
||||
parent: dbi,
|
||||
pointIter: dbi.pointIter,
|
||||
combinedIterState: combinedIterState{
|
||||
initialized: false,
|
||||
},
|
||||
}
|
||||
dbi.iter = &dbi.lazyCombinedIter
|
||||
} else {
|
||||
dbi.lazyCombinedIter.combinedIterState = combinedIterState{
|
||||
initialized: true,
|
||||
}
|
||||
if dbi.rangeKey == nil {
|
||||
dbi.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
dbi.rangeKey.init(dbi.comparer.Compare, dbi.comparer.Split, &dbi.opts)
|
||||
dbi.constructRangeKeyIter()
|
||||
} else {
|
||||
dbi.rangeKey.iterConfig.SetBounds(dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
}
|
||||
|
||||
// Wrap the point iterator (currently dbi.iter) with an interleaving
|
||||
// iterator that interleaves range keys pulled from
|
||||
// dbi.rangeKey.rangeKeyIter.
|
||||
//
|
||||
// NB: The interleaving iterator is always reinitialized, even if
|
||||
// dbi already had an initialized range key iterator, in case the point
|
||||
// iterator changed or the range key masking suffix changed.
|
||||
dbi.rangeKey.iiter.Init(&dbi.comparer, dbi.iter, dbi.rangeKey.rangeKeyIter,
|
||||
&dbi.rangeKeyMasking, dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
dbi.iter = &dbi.rangeKey.iiter
|
||||
}
|
||||
} else {
|
||||
// !dbi.opts.rangeKeys()
|
||||
//
|
||||
// NB: The interleaving iterator is always reinitialized, even if
|
||||
// dbi already had an initialized range key iterator, in case the point
|
||||
// iterator changed or the range key masking suffix changed.
|
||||
dbi.rangeKey.iter.Init(dbi.cmp, dbi.iter, dbi.rangeKey.rangeKeyIter, keyspan.Hooks{
|
||||
SpanChanged: dbi.rangeKeySpanChanged,
|
||||
SkipPoint: dbi.rangeKeySkipPoint,
|
||||
}, dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
dbi.iter = &dbi.rangeKey.iter
|
||||
// Reset the combined iterator state. The initialized=true ensures the
|
||||
// iterator doesn't unnecessarily try to switch to combined iteration.
|
||||
dbi.lazyCombinedIter.combinedIterState = combinedIterState{initialized: true}
|
||||
}
|
||||
return dbi
|
||||
}
|
||||
|
||||
func constructPointIter(
|
||||
dbi *Iterator, batch *Batch, memtables flushableList, buf *iterAlloc,
|
||||
) internalIteratorWithStats {
|
||||
if !dbi.opts.pointKeys() {
|
||||
return emptyIter
|
||||
func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
if i.pointIter != nil {
|
||||
// Already have one.
|
||||
return
|
||||
}
|
||||
if dbi.pointIter != nil {
|
||||
return dbi.pointIter
|
||||
internalOpts := internalIterOpts{stats: &i.stats.InternalStats}
|
||||
if i.opts.RangeKeyMasking.Filter != nil {
|
||||
internalOpts.boundLimitedFilter = &i.rangeKeyMasking
|
||||
}
|
||||
|
||||
// Merging levels and levels from iterAlloc.
|
||||
@@ -1013,12 +1081,12 @@ func constructPointIter(
|
||||
// should improve the performance.
|
||||
numMergingLevels := 0
|
||||
numLevelIters := 0
|
||||
if batch != nil {
|
||||
if i.batch != nil {
|
||||
numMergingLevels++
|
||||
}
|
||||
numMergingLevels += len(memtables)
|
||||
|
||||
current := dbi.readState.current
|
||||
current := i.readState.current
|
||||
numMergingLevels += len(current.L0SublevelFiles)
|
||||
numLevelIters += len(current.L0SublevelFiles)
|
||||
for level := 1; level < len(current.Levels); level++ {
|
||||
@@ -1037,19 +1105,39 @@ func constructPointIter(
|
||||
}
|
||||
|
||||
// Top-level is the batch, if any.
|
||||
if batch != nil {
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: base.WrapIterWithStats(batch.newInternalIter(&dbi.opts)),
|
||||
rangeDelIter: batch.newRangeDelIter(&dbi.opts),
|
||||
})
|
||||
if i.batch != nil {
|
||||
if i.batch.index == nil {
|
||||
// This isn't an indexed batch. Include an error iterator so that
|
||||
// the resulting iterator correctly surfaces ErrIndexed.
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: newErrorIter(ErrNotIndexed),
|
||||
rangeDelIter: newErrorKeyspanIter(ErrNotIndexed),
|
||||
})
|
||||
} else {
|
||||
i.batch.initInternalIter(&i.opts, &i.batchPointIter)
|
||||
i.batch.initRangeDelIter(&i.opts, &i.batchRangeDelIter, i.batchSeqNum)
|
||||
// Only include the batch's rangedel iterator if it's non-empty.
|
||||
// This requires some subtle logic in the case a rangedel is later
|
||||
// written to the batch and the view of the batch is refreshed
|
||||
// during a call to SetOptions—in this case, we need to reconstruct
|
||||
// the point iterator to add the batch rangedel iterator.
|
||||
var rangeDelIter keyspan.FragmentIterator
|
||||
if i.batchRangeDelIter.Count() > 0 {
|
||||
rangeDelIter = &i.batchRangeDelIter
|
||||
}
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: &i.batchPointIter,
|
||||
rangeDelIter: rangeDelIter,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Next are the memtables.
|
||||
for i := len(memtables) - 1; i >= 0; i-- {
|
||||
mem := memtables[i]
|
||||
for j := len(memtables) - 1; j >= 0; j-- {
|
||||
mem := memtables[j]
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: base.WrapIterWithStats(mem.newIter(&dbi.opts)),
|
||||
rangeDelIter: mem.newRangeDelIter(&dbi.opts),
|
||||
iter: mem.newIter(&i.opts),
|
||||
rangeDelIter: mem.newRangeDelIter(&i.opts),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1058,16 +1146,13 @@ func constructPointIter(
|
||||
levelsIndex := len(levels)
|
||||
mlevels = mlevels[:numMergingLevels]
|
||||
levels = levels[:numLevelIters]
|
||||
|
||||
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
||||
li := &levels[levelsIndex]
|
||||
|
||||
li.init(dbi.opts, dbi.cmp, dbi.split, dbi.newIters, files, level, nil)
|
||||
li.init(i.opts, i.comparer.Compare, i.comparer.Split, i.newIters, files, level, internalOpts)
|
||||
li.initRangeDel(&mlevels[mlevelsIndex].rangeDelIter)
|
||||
li.initSmallestLargestUserKey(&mlevels[mlevelsIndex].smallestUserKey,
|
||||
&mlevels[mlevelsIndex].largestUserKey,
|
||||
&mlevels[mlevelsIndex].isLargestUserKeyRangeDelSentinel)
|
||||
li.initIsSyntheticIterBoundsKey(&mlevels[mlevelsIndex].isSyntheticIterBoundsKey)
|
||||
li.initBoundaryContext(&mlevels[mlevelsIndex].levelIterBoundaryContext)
|
||||
li.initCombinedIterState(&i.lazyCombinedIter.combinedIterState)
|
||||
mlevels[mlevelsIndex].iter = li
|
||||
|
||||
levelsIndex++
|
||||
@@ -1087,11 +1172,13 @@ func constructPointIter(
|
||||
}
|
||||
addLevelIterForFiles(current.Levels[level].Iter(), manifest.Level(level))
|
||||
}
|
||||
buf.merging.init(&dbi.opts, dbi.cmp, dbi.split, mlevels...)
|
||||
buf.merging.snapshot = dbi.seqNum
|
||||
buf.merging.init(&i.opts, &i.stats.InternalStats, i.comparer.Compare, i.comparer.Split, mlevels...)
|
||||
buf.merging.snapshot = i.seqNum
|
||||
buf.merging.batchSnapshot = i.batchSeqNum
|
||||
buf.merging.elideRangeTombstones = true
|
||||
dbi.pointIter = &buf.merging
|
||||
return &buf.merging
|
||||
buf.merging.combinedIterState = &i.lazyCombinedIter.combinedIterState
|
||||
i.pointIter = &buf.merging
|
||||
i.merging = &buf.merging
|
||||
}
|
||||
|
||||
// NewBatch returns a new empty write-only batch. Any reads on the batch will
|
||||
@@ -1150,6 +1237,18 @@ func (d *DB) NewSnapshot() *Snapshot {
|
||||
// or to call Close concurrently with any other DB method. It is not valid
|
||||
// to call any of a DB's methods after the DB has been closed.
|
||||
func (d *DB) Close() error {
|
||||
// Lock the commit pipeline for the duration of Close. This prevents a race
|
||||
// with makeRoomForWrite. Rotating the WAL in makeRoomForWrite requires
|
||||
// dropping d.mu several times for I/O. If Close only holds d.mu, an
|
||||
// in-progress WAL rotation may re-acquire d.mu only once the database is
|
||||
// closed.
|
||||
//
|
||||
// Additionally, locking the commit pipeline makes it more likely that
|
||||
// (illegal) concurrent writes will observe d.closed.Load() != nil, creating
|
||||
// more understable panics if the database is improperly used concurrently
|
||||
// during Close.
|
||||
d.commit.mu.Lock()
|
||||
defer d.commit.mu.Unlock()
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if err := d.closed.Load(); err != nil {
|
||||
@@ -1239,10 +1338,22 @@ func (d *DB) Close() error {
|
||||
d.compactionSchedulers.Wait()
|
||||
d.mu.Lock()
|
||||
|
||||
// As a sanity check, ensure that there are no zombie tables. A non-zero count
|
||||
// hints at a reference count leak.
|
||||
if ztbls := len(d.mu.versions.zombieTables); ztbls > 0 {
|
||||
err = firstError(err, errors.Errorf("non-zero zombie file count: %d", ztbls))
|
||||
}
|
||||
|
||||
// If the options include a closer to 'close' the filesystem, close it.
|
||||
if d.opts.private.fsCloser != nil {
|
||||
d.opts.private.fsCloser.Close()
|
||||
}
|
||||
|
||||
// Return an error if the user failed to close all open snapshots.
|
||||
if v := d.mu.snapshots.count(); v > 0 {
|
||||
err = firstError(err, errors.Errorf("leaked snapshots: %d open snapshots on DB %p", v, d))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1426,36 +1537,18 @@ func (d *DB) AsyncFlush() (<-chan struct{}, error) {
|
||||
return flushed, nil
|
||||
}
|
||||
|
||||
// InternalIntervalMetrics returns the InternalIntervalMetrics and resets for
|
||||
// the next interval (which is until the next call to this method).
|
||||
func (d *DB) InternalIntervalMetrics() *InternalIntervalMetrics {
|
||||
m := &InternalIntervalMetrics{}
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.mu.log.metrics != nil {
|
||||
m.LogWriter.WriteThroughput = d.mu.log.metrics.WriteThroughput
|
||||
m.LogWriter.PendingBufferUtilization =
|
||||
d.mu.log.metrics.PendingBufferLen.Mean() / record.CapAllocatedBlocks
|
||||
m.LogWriter.SyncQueueUtilization = d.mu.log.metrics.SyncQueueLen.Mean() / record.SyncConcurrency
|
||||
m.LogWriter.SyncLatencyMicros = d.mu.log.metrics.SyncLatencyMicros
|
||||
d.mu.log.metrics = nil
|
||||
}
|
||||
m.Flush.WriteThroughput = d.mu.compact.flushWriteThroughput
|
||||
d.mu.compact.flushWriteThroughput = ThroughputMetric{}
|
||||
return m
|
||||
}
|
||||
|
||||
// Metrics returns metrics about the database.
|
||||
func (d *DB) Metrics() *Metrics {
|
||||
metrics := &Metrics{}
|
||||
recycledLogsCount, recycledLogSize := d.logRecycler.stats()
|
||||
|
||||
d.mu.Lock()
|
||||
vers := d.mu.versions.currentVersion()
|
||||
*metrics = d.mu.versions.metrics
|
||||
metrics.Compact.EstimatedDebt = d.mu.versions.picker.estimatedCompactionDebt(0)
|
||||
metrics.Compact.InProgressBytes = atomic.LoadInt64(&d.mu.versions.atomic.atomicInProgressBytes)
|
||||
metrics.Compact.NumInProgress = int64(d.mu.compact.compactingCount)
|
||||
metrics.Compact.MarkedFiles = d.mu.versions.currentVersion().Stats.MarkedForCompaction
|
||||
metrics.Compact.MarkedFiles = vers.Stats.MarkedForCompaction
|
||||
for _, m := range d.mu.mem.queue {
|
||||
metrics.MemTable.Size += m.totalBytes()
|
||||
}
|
||||
@@ -1499,9 +1592,18 @@ func (d *DB) Metrics() *Metrics {
|
||||
}
|
||||
metrics.private.optionsFileSize = d.optionsFileSize
|
||||
|
||||
metrics.Keys.RangeKeySetsCount = countRangeKeySetFragments(vers)
|
||||
|
||||
d.mu.versions.logLock()
|
||||
metrics.private.manifestFileSize = uint64(d.mu.versions.manifest.Size())
|
||||
d.mu.versions.logUnlock()
|
||||
|
||||
metrics.LogWriter.FsyncLatency = d.mu.log.metrics.fsyncLatency
|
||||
if err := metrics.LogWriter.Merge(&d.mu.log.metrics.LogWriterMetrics); err != nil {
|
||||
d.opts.Logger.Infof("metrics error: %s", err)
|
||||
}
|
||||
metrics.Flush.WriteThroughput = d.mu.compact.flushWriteThroughput
|
||||
|
||||
d.mu.Unlock()
|
||||
|
||||
metrics.BlockCache = d.opts.Cache.Metrics()
|
||||
@@ -1791,12 +1893,8 @@ func (d *DB) makeRoomForWrite(b *Batch) error {
|
||||
err = d.mu.log.LogWriter.Close()
|
||||
metrics := d.mu.log.LogWriter.Metrics()
|
||||
d.mu.Lock()
|
||||
if d.mu.log.metrics == nil {
|
||||
d.mu.log.metrics = metrics
|
||||
} else {
|
||||
if err := d.mu.log.metrics.Merge(metrics); err != nil {
|
||||
d.opts.Logger.Infof("metrics error: %s", err)
|
||||
}
|
||||
if err := d.mu.log.metrics.Merge(metrics); err != nil {
|
||||
d.opts.Logger.Infof("metrics error: %s", err)
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
@@ -1848,6 +1946,7 @@ func (d *DB) makeRoomForWrite(b *Batch) error {
|
||||
newLogFile.Close()
|
||||
} else if err == nil {
|
||||
newLogFile = vfs.NewSyncingFile(newLogFile, vfs.SyncingFileOptions{
|
||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||
BytesPerSync: d.opts.WALBytesPerSync,
|
||||
PreallocateSize: d.walPreallocateSize(),
|
||||
})
|
||||
@@ -1883,8 +1982,10 @@ func (d *DB) makeRoomForWrite(b *Batch) error {
|
||||
|
||||
if !d.opts.DisableWAL {
|
||||
d.mu.log.queue = append(d.mu.log.queue, fileInfo{fileNum: newLogNum, fileSize: newLogSize})
|
||||
d.mu.log.LogWriter = record.NewLogWriter(newLogFile, newLogNum)
|
||||
d.mu.log.LogWriter.SetMinSyncInterval(d.opts.WALMinSyncInterval)
|
||||
d.mu.log.LogWriter = record.NewLogWriter(newLogFile, newLogNum, record.LogWriterConfig{
|
||||
WALFsyncLatency: d.mu.log.metrics.fsyncLatency,
|
||||
WALMinSyncInterval: d.opts.WALMinSyncInterval,
|
||||
})
|
||||
}
|
||||
|
||||
immMem := d.mu.mem.mutable
|
||||
@@ -1946,7 +2047,7 @@ func (d *DB) makeRoomForWrite(b *Batch) error {
|
||||
var entry *flushableEntry
|
||||
d.mu.mem.mutable, entry = d.newMemTable(newLogNum, logSeqNum)
|
||||
d.mu.mem.queue = append(d.mu.mem.queue, entry)
|
||||
d.updateReadStateLocked(nil, nil)
|
||||
d.updateReadStateLocked(nil)
|
||||
if immMem.writerUnref() {
|
||||
d.maybeScheduleFlush()
|
||||
}
|
||||
|
||||
52
vendor/github.com/cockroachdb/pebble/error_iter.go
generated
vendored
52
vendor/github.com/cockroachdb/pebble/error_iter.go
generated
vendored
@@ -14,40 +14,40 @@ type errorIter struct {
|
||||
}
|
||||
|
||||
// errorIter implements the base.InternalIterator interface.
|
||||
var _ internalIteratorWithStats = (*errorIter)(nil)
|
||||
var _ internalIterator = (*errorIter)(nil)
|
||||
|
||||
func newErrorIter(err error) *errorIter {
|
||||
return &errorIter{err: err}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return nil, nil
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) First() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) First() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Last() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Last() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Next() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Next() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Prev() (*InternalKey, []byte) {
|
||||
return nil, nil
|
||||
func (c *errorIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) Error() error {
|
||||
@@ -63,8 +63,6 @@ func (c *errorIter) String() string {
|
||||
}
|
||||
|
||||
func (c *errorIter) SetBounds(lower, upper []byte) {}
|
||||
func (c *errorIter) Stats() InternalIteratorStats { return InternalIteratorStats{} }
|
||||
func (c *errorIter) ResetStats() {}
|
||||
|
||||
type errorKeyspanIter struct {
|
||||
err error
|
||||
@@ -77,12 +75,12 @@ func newErrorKeyspanIter(err error) *errorKeyspanIter {
|
||||
return &errorKeyspanIter{err: err}
|
||||
}
|
||||
|
||||
func (*errorKeyspanIter) SeekGE(key []byte) keyspan.Span { return keyspan.Span{} }
|
||||
func (*errorKeyspanIter) SeekLT(key []byte) keyspan.Span { return keyspan.Span{} }
|
||||
func (*errorKeyspanIter) First() keyspan.Span { return keyspan.Span{} }
|
||||
func (*errorKeyspanIter) Last() keyspan.Span { return keyspan.Span{} }
|
||||
func (*errorKeyspanIter) Next() keyspan.Span { return keyspan.Span{} }
|
||||
func (*errorKeyspanIter) Prev() keyspan.Span { return keyspan.Span{} }
|
||||
func (i *errorKeyspanIter) Error() error { return i.err }
|
||||
func (i *errorKeyspanIter) Close() error { return i.err }
|
||||
func (*errorKeyspanIter) String() string { return "error" }
|
||||
func (*errorKeyspanIter) SeekGE(key []byte) *keyspan.Span { return nil }
|
||||
func (*errorKeyspanIter) SeekLT(key []byte) *keyspan.Span { return nil }
|
||||
func (*errorKeyspanIter) First() *keyspan.Span { return nil }
|
||||
func (*errorKeyspanIter) Last() *keyspan.Span { return nil }
|
||||
func (*errorKeyspanIter) Next() *keyspan.Span { return nil }
|
||||
func (*errorKeyspanIter) Prev() *keyspan.Span { return nil }
|
||||
func (i *errorKeyspanIter) Error() error { return i.err }
|
||||
func (i *errorKeyspanIter) Close() error { return i.err }
|
||||
func (*errorKeyspanIter) String() string { return "error" }
|
||||
|
||||
496
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
496
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
@@ -5,41 +5,113 @@
|
||||
package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
)
|
||||
|
||||
// NewExternalIter takes an input set of sstable files which may overlap
|
||||
// arbitrarily and returns an Iterator over the merged contents of the sstables.
|
||||
// ExternalIterOption provide an interface to specify open-time options to
|
||||
// NewExternalIter.
|
||||
type ExternalIterOption interface {
|
||||
// iterApply is called on the iterator during opening in order to set internal
|
||||
// parameters.
|
||||
iterApply(*Iterator)
|
||||
// readerOptions returns any reader options added by this iter option.
|
||||
readerOptions() []sstable.ReaderOption
|
||||
}
|
||||
|
||||
type externalIterReaderOptions struct {
|
||||
opts []sstable.ReaderOption
|
||||
}
|
||||
|
||||
func (e *externalIterReaderOptions) iterApply(iterator *Iterator) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
func (e *externalIterReaderOptions) readerOptions() []sstable.ReaderOption {
|
||||
return e.opts
|
||||
}
|
||||
|
||||
// ExternalIterReaderOptions returns an ExternalIterOption that specifies
|
||||
// sstable.ReaderOptions to be applied on sstable readers in NewExternalIter.
|
||||
func ExternalIterReaderOptions(opts ...sstable.ReaderOption) ExternalIterOption {
|
||||
return &externalIterReaderOptions{opts: opts}
|
||||
}
|
||||
|
||||
// ExternalIterForwardOnly is an ExternalIterOption that specifies this iterator
|
||||
// will only be used for forward positioning operations (First, SeekGE, Next).
|
||||
// This could enable optimizations that take advantage of this invariant.
|
||||
// Behaviour when a reverse positioning operation is done on an iterator
|
||||
// opened with this option is unpredictable, though in most cases it should.
|
||||
type ExternalIterForwardOnly struct{}
|
||||
|
||||
func (e ExternalIterForwardOnly) iterApply(iter *Iterator) {
|
||||
iter.forwardOnly = true
|
||||
}
|
||||
|
||||
func (e ExternalIterForwardOnly) readerOptions() []sstable.ReaderOption {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewExternalIter takes an input 2d array of sstable files which may overlap
|
||||
// across subarrays but not within a subarray (at least as far as points are
|
||||
// concerned; range keys are allowed to overlap arbitrarily even within a
|
||||
// subarray), and returns an Iterator over the merged contents of the sstables.
|
||||
// Input sstables may contain point keys, range keys, range deletions, etc. The
|
||||
// input files slice must be sorted in reverse chronological ordering. A key in
|
||||
// a file at a lower index will shadow a key with an identical user key
|
||||
// contained within a file at a higher index.
|
||||
// input files slice must be sorted in reverse chronological ordering. A key in a
|
||||
// file at a lower index subarray will shadow a key with an identical user key
|
||||
// contained within a file at a higher index subarray. Each subarray must be
|
||||
// sorted in internal key order, where lower index files contain keys that sort
|
||||
// left of files with higher indexes.
|
||||
//
|
||||
// Input sstables must only contain keys with the zero sequence number.
|
||||
//
|
||||
// Iterators constructed through NewExternalIter do not support all iterator
|
||||
// options, including block-property and table filters. NewExternalIter errors
|
||||
// if an incompatible option is set.
|
||||
func NewExternalIter(
|
||||
o *Options,
|
||||
iterOpts *IterOptions,
|
||||
files []sstable.ReadableFile,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
files [][]sstable.ReadableFile,
|
||||
extraOpts ...ExternalIterOption,
|
||||
) (it *Iterator, err error) {
|
||||
var readers []*sstable.Reader
|
||||
|
||||
// Ensure we close all the opened readers if we error out.
|
||||
closeReaders := func() {
|
||||
for i := range readers {
|
||||
_ = readers[i].Close()
|
||||
if iterOpts != nil {
|
||||
if err := validateExternalIterOpts(iterOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var readers [][]*sstable.Reader
|
||||
|
||||
// Ensure we close all the opened readers if we error out.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
closeReaders()
|
||||
for i := range readers {
|
||||
for j := range readers[i] {
|
||||
_ = readers[i][j].Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
readers, err = openExternalTables(o, files, o.MakeReaderOptions(), extraReaderOpts...)
|
||||
seqNumOffset := 0
|
||||
var extraReaderOpts []sstable.ReaderOption
|
||||
for i := range extraOpts {
|
||||
extraReaderOpts = append(extraReaderOpts, extraOpts[i].readerOptions()...)
|
||||
}
|
||||
for _, levelFiles := range files {
|
||||
seqNumOffset += len(levelFiles)
|
||||
}
|
||||
for _, levelFiles := range files {
|
||||
var subReaders []*sstable.Reader
|
||||
seqNumOffset -= len(levelFiles)
|
||||
subReaders, err = openExternalTables(o, levelFiles, seqNumOffset, o.MakeReaderOptions(), extraReaderOpts...)
|
||||
readers = append(readers, subReaders)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -48,16 +120,17 @@ func NewExternalIter(
|
||||
dbi := &buf.dbi
|
||||
*dbi = Iterator{
|
||||
alloc: buf,
|
||||
cmp: o.Comparer.Compare,
|
||||
equal: o.equal(),
|
||||
merge: o.Merger.Merge,
|
||||
split: o.Comparer.Split,
|
||||
comparer: *o.Comparer,
|
||||
readState: nil,
|
||||
keyBuf: buf.keyBuf,
|
||||
prefixOrFullSeekKey: buf.prefixOrFullSeekKey,
|
||||
boundsBuf: buf.boundsBuf,
|
||||
batch: nil,
|
||||
newIters: func(f *manifest.FileMetadata, opts *IterOptions, bytesIterated *uint64) (internalIterator, keyspan.FragmentIterator, error) {
|
||||
// Add the readers to the Iterator so that Close closes them, and
|
||||
// SetOptions can re-construct iterators from them.
|
||||
externalReaders: readers,
|
||||
newIters: func(f *manifest.FileMetadata, opts *IterOptions, internalOpts internalIterOpts) (internalIterator, keyspan.FragmentIterator, error) {
|
||||
// NB: External iterators are currently constructed without any
|
||||
// `levelIters`. newIters should never be called. When we support
|
||||
// organizing multiple non-overlapping files into a single level
|
||||
@@ -72,92 +145,170 @@ func NewExternalIter(
|
||||
dbi.opts = *iterOpts
|
||||
dbi.saveBounds(iterOpts.LowerBound, iterOpts.UpperBound)
|
||||
}
|
||||
for i := range extraOpts {
|
||||
extraOpts[i].iterApply(dbi)
|
||||
}
|
||||
finishInitializingExternal(dbi)
|
||||
return dbi, nil
|
||||
}
|
||||
|
||||
func validateExternalIterOpts(iterOpts *IterOptions) error {
|
||||
switch {
|
||||
case iterOpts.TableFilter != nil:
|
||||
return errors.Errorf("pebble: external iterator: TableFilter unsupported")
|
||||
case iterOpts.PointKeyFilters != nil:
|
||||
return errors.Errorf("pebble: external iterator: PointKeyFilters unsupported")
|
||||
case iterOpts.RangeKeyFilters != nil:
|
||||
return errors.Errorf("pebble: external iterator: RangeKeyFilters unsupported")
|
||||
case iterOpts.OnlyReadGuaranteedDurable:
|
||||
return errors.Errorf("pebble: external iterator: OnlyReadGuaranteedDurable unsupported")
|
||||
case iterOpts.UseL6Filters:
|
||||
return errors.Errorf("pebble: external iterator: UseL6Filters unsupported")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createExternalPointIter(it *Iterator) (internalIterator, error) {
|
||||
// TODO(jackson): In some instances we could generate fewer levels by using
|
||||
// L0Sublevels code to organize nonoverlapping files into the same level.
|
||||
// This would allow us to use levelIters and keep a smaller set of data and
|
||||
// files in-memory. However, it would also require us to identify the bounds
|
||||
// of all the files upfront.
|
||||
|
||||
// Ensure we close all iters if error out early.
|
||||
mlevels := buf.mlevels[:0]
|
||||
var rangeKeyIters []keyspan.FragmentIterator
|
||||
defer func() {
|
||||
if err != nil {
|
||||
for i := range rangeKeyIters {
|
||||
_ = rangeKeyIters[i].Close()
|
||||
}
|
||||
for i := range mlevels {
|
||||
if mlevels[i].iter != nil {
|
||||
_ = mlevels[i].iter.Close()
|
||||
}
|
||||
if mlevels[i].rangeDelIter != nil {
|
||||
_ = mlevels[i].rangeDelIter.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
if iterOpts.pointKeys() {
|
||||
if len(files) > cap(mlevels) {
|
||||
mlevels = make([]mergingIterLevel, 0, len(files))
|
||||
}
|
||||
if !it.opts.pointKeys() {
|
||||
return emptyIter, nil
|
||||
} else if it.pointIter != nil {
|
||||
return it.pointIter, nil
|
||||
}
|
||||
mlevels := it.alloc.mlevels[:0]
|
||||
|
||||
if len(it.externalReaders) > cap(mlevels) {
|
||||
mlevels = make([]mergingIterLevel, 0, len(it.externalReaders))
|
||||
}
|
||||
for _, readers := range it.externalReaders {
|
||||
var combinedIters []internalIterator
|
||||
for _, r := range readers {
|
||||
pointIter, err := r.NewIter(dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
var (
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
pointIter internalIterator
|
||||
err error
|
||||
)
|
||||
pointIter, err = r.NewIterWithBlockPropertyFilters(
|
||||
it.opts.LowerBound,
|
||||
it.opts.UpperBound,
|
||||
nil, /* BlockPropertiesFilterer */
|
||||
false, /* useFilterBlock */
|
||||
&it.stats.InternalStats,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rangeDelIter, err := r.NewRawRangeDelIter()
|
||||
rangeDelIter, err = r.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
_ = pointIter.Close()
|
||||
return nil, err
|
||||
}
|
||||
if rangeDelIter == nil && pointIter != nil && it.forwardOnly {
|
||||
// TODO(bilal): Consider implementing range key pausing in
|
||||
// simpleLevelIter so we can reduce mergingIterLevels even more by
|
||||
// sending all sstable iterators to combinedIters, not just those
|
||||
// corresponding to sstables without range deletes.
|
||||
combinedIters = append(combinedIters, pointIter)
|
||||
continue
|
||||
}
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: base.WrapIterWithStats(pointIter),
|
||||
iter: pointIter,
|
||||
rangeDelIter: rangeDelIter,
|
||||
})
|
||||
}
|
||||
}
|
||||
buf.merging.init(&dbi.opts, dbi.cmp, dbi.split, mlevels...)
|
||||
buf.merging.snapshot = base.InternalKeySeqNumMax
|
||||
buf.merging.elideRangeTombstones = true
|
||||
dbi.pointIter = &buf.merging
|
||||
dbi.iter = dbi.pointIter
|
||||
|
||||
if dbi.opts.rangeKeys() {
|
||||
for _, r := range readers {
|
||||
rki, err := r.NewRawRangeKeyIter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(combinedIters) == 1 {
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: combinedIters[0],
|
||||
})
|
||||
} else if len(combinedIters) > 1 {
|
||||
sli := &simpleLevelIter{
|
||||
cmp: it.cmp,
|
||||
iters: combinedIters,
|
||||
}
|
||||
if rki != nil {
|
||||
rangeKeyIters = append(rangeKeyIters, rki)
|
||||
sli.init(it.opts)
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: sli,
|
||||
rangeDelIter: nil,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(mlevels) == 1 && mlevels[0].rangeDelIter == nil {
|
||||
// Set closePointIterOnce to true. This is because we're bypassing the
|
||||
// merging iter, which turns Close()s on it idempotent for any child
|
||||
// iterators. The outer Iterator could call Close() on a point iter twice,
|
||||
// which sstable iterators do not support (as they release themselves to
|
||||
// a pool).
|
||||
it.closePointIterOnce = true
|
||||
return mlevels[0].iter, nil
|
||||
}
|
||||
|
||||
it.alloc.merging.init(&it.opts, &it.stats.InternalStats, it.comparer.Compare, it.comparer.Split, mlevels...)
|
||||
it.alloc.merging.snapshot = base.InternalKeySeqNumMax
|
||||
it.alloc.merging.elideRangeTombstones = true
|
||||
return &it.alloc.merging, nil
|
||||
}
|
||||
|
||||
func finishInitializingExternal(it *Iterator) {
|
||||
pointIter, err := createExternalPointIter(it)
|
||||
if err != nil {
|
||||
it.pointIter = &errorIter{err: err}
|
||||
} else {
|
||||
it.pointIter = pointIter
|
||||
}
|
||||
it.iter = it.pointIter
|
||||
|
||||
if it.opts.rangeKeys() {
|
||||
it.rangeKeyMasking.init(it, it.comparer.Compare, it.comparer.Split)
|
||||
var rangeKeyIters []keyspan.FragmentIterator
|
||||
if it.rangeKey == nil {
|
||||
// We could take advantage of the lack of overlaps in range keys within
|
||||
// each slice in it.externalReaders, and generate keyspan.LevelIters
|
||||
// out of those. However, since range keys are expected to be sparse to
|
||||
// begin with, the performance gain might not be significant enough to
|
||||
// warrant it.
|
||||
//
|
||||
// TODO(bilal): Explore adding a simpleRangeKeyLevelIter that does not
|
||||
// operate on FileMetadatas (similar to simpleLevelIter), and implements
|
||||
// this optimization.
|
||||
for _, readers := range it.externalReaders {
|
||||
for _, r := range readers {
|
||||
if rki, err := r.NewRawRangeKeyIter(); err != nil {
|
||||
rangeKeyIters = append(rangeKeyIters, &errorKeyspanIter{err: err})
|
||||
} else if rki != nil {
|
||||
rangeKeyIters = append(rangeKeyIters, rki)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(rangeKeyIters) > 0 {
|
||||
it.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
it.rangeKey.init(it.comparer.Compare, it.comparer.Split, &it.opts)
|
||||
it.rangeKey.rangeKeyIter = it.rangeKey.iterConfig.Init(
|
||||
&it.comparer,
|
||||
base.InternalKeySeqNumMax,
|
||||
it.opts.LowerBound, it.opts.UpperBound,
|
||||
&it.hasPrefix, &it.prefixOrFullSeekKey,
|
||||
)
|
||||
for i := range rangeKeyIters {
|
||||
it.rangeKey.iterConfig.AddLevel(rangeKeyIters[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbi.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
dbi.rangeKey.rangeKeyIter = rangekey.InitUserIteration(
|
||||
o.Comparer.Compare,
|
||||
base.InternalKeySeqNumMax,
|
||||
&dbi.rangeKey.alloc.merging,
|
||||
&dbi.rangeKey.alloc.defraging,
|
||||
rangeKeyIters...,
|
||||
)
|
||||
|
||||
dbi.rangeKey.iter.Init(dbi.cmp, &buf.merging, dbi.rangeKey.rangeKeyIter, keyspan.Hooks{
|
||||
SpanChanged: dbi.rangeKeySpanChanged,
|
||||
SkipPoint: dbi.rangeKeySkipPoint,
|
||||
}, dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
dbi.iter = &dbi.rangeKey.iter
|
||||
if it.rangeKey != nil {
|
||||
it.rangeKey.iiter.Init(&it.comparer, it.iter, it.rangeKey.rangeKeyIter, &it.rangeKeyMasking,
|
||||
it.opts.LowerBound, it.opts.UpperBound)
|
||||
it.iter = &it.rangeKey.iiter
|
||||
}
|
||||
}
|
||||
|
||||
// Close all the opened sstable.Readers when the Iterator is closed.
|
||||
dbi.closeHook = closeReaders
|
||||
return dbi, nil
|
||||
}
|
||||
|
||||
func openExternalTables(
|
||||
o *Options,
|
||||
files []sstable.ReadableFile,
|
||||
seqNumOffset int,
|
||||
readerOpts sstable.ReaderOptions,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
) (readers []*sstable.Reader, err error) {
|
||||
@@ -169,8 +320,193 @@ func openExternalTables(
|
||||
}
|
||||
// Use the index of the file in files as the sequence number for all of
|
||||
// its keys.
|
||||
r.Properties.GlobalSeqNum = uint64(len(files) - i)
|
||||
r.Properties.GlobalSeqNum = uint64(len(files) - i + seqNumOffset)
|
||||
readers = append(readers, r)
|
||||
}
|
||||
return readers, err
|
||||
}
|
||||
|
||||
// simpleLevelIter is similar to a levelIter in that it merges the points
|
||||
// from multiple point iterators that are non-overlapping in the key ranges
|
||||
// they return. It is only expected to support forward iteration and forward
|
||||
// regular seeking; reverse iteration and prefix seeking is not supported.
|
||||
// Intended to be a low-overhead, non-FileMetadata dependent option for
|
||||
// NewExternalIter. To optimize seeking and forward iteration, it maintains
|
||||
// two slices of child iterators; one of all iterators, and a subset of it that
|
||||
// contains just the iterators that contain point keys within the current
|
||||
// bounds.
|
||||
//
|
||||
// Note that this levelIter does not support pausing at file boundaries
|
||||
// in case of range tombstones in this file that could apply to points outside
|
||||
// of this file (and outside of this level). This is sufficient for optimizing
|
||||
// the main use cases of NewExternalIter, however for completeness it would make
|
||||
// sense to build this pausing functionality in.
|
||||
type simpleLevelIter struct {
|
||||
cmp Compare
|
||||
err error
|
||||
lowerBound []byte
|
||||
iters []internalIterator
|
||||
filtered []internalIterator
|
||||
firstKeys [][]byte
|
||||
firstKeysBuf []byte
|
||||
currentIdx int
|
||||
}
|
||||
|
||||
var _ internalIterator = &simpleLevelIter{}
|
||||
|
||||
// init initializes this simpleLevelIter.
|
||||
func (s *simpleLevelIter) init(opts IterOptions) {
|
||||
s.currentIdx = 0
|
||||
s.lowerBound = opts.LowerBound
|
||||
s.resetFilteredIters()
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) resetFilteredIters() {
|
||||
s.filtered = s.filtered[:0]
|
||||
s.firstKeys = s.firstKeys[:0]
|
||||
s.firstKeysBuf = s.firstKeysBuf[:0]
|
||||
s.err = nil
|
||||
for i := range s.iters {
|
||||
var iterKey *base.InternalKey
|
||||
if s.lowerBound != nil {
|
||||
iterKey, _ = s.iters[i].SeekGE(s.lowerBound, base.SeekGEFlagsNone)
|
||||
} else {
|
||||
iterKey, _ = s.iters[i].First()
|
||||
}
|
||||
if iterKey != nil {
|
||||
s.filtered = append(s.filtered, s.iters[i])
|
||||
bufStart := len(s.firstKeysBuf)
|
||||
s.firstKeysBuf = append(s.firstKeysBuf, iterKey.UserKey...)
|
||||
s.firstKeys = append(s.firstKeys, s.firstKeysBuf[bufStart:bufStart+len(iterKey.UserKey)])
|
||||
} else if err := s.iters[i].Error(); err != nil {
|
||||
s.err = err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
if s.err != nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
// Find the first file that is entirely >= key. The file before that could
|
||||
// contain the key we're looking for.
|
||||
n := sort.Search(len(s.firstKeys), func(i int) bool {
|
||||
return s.cmp(key, s.firstKeys[i]) <= 0
|
||||
})
|
||||
if n > 0 {
|
||||
s.currentIdx = n - 1
|
||||
} else {
|
||||
s.currentIdx = n
|
||||
}
|
||||
if s.currentIdx < len(s.filtered) {
|
||||
if iterKey, val := s.filtered[s.currentIdx].SeekGE(key, flags); iterKey != nil {
|
||||
return iterKey, val
|
||||
}
|
||||
if err := s.filtered[s.currentIdx].Error(); err != nil {
|
||||
s.err = err
|
||||
}
|
||||
s.currentIdx++
|
||||
}
|
||||
return s.skipEmptyFileForward(key, flags)
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) skipEmptyFileForward(
|
||||
seekKey []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
var iterKey *base.InternalKey
|
||||
var val base.LazyValue
|
||||
for s.currentIdx >= 0 && s.currentIdx < len(s.filtered) && s.err == nil {
|
||||
if seekKey != nil {
|
||||
iterKey, val = s.filtered[s.currentIdx].SeekGE(seekKey, flags)
|
||||
} else if s.lowerBound != nil {
|
||||
iterKey, val = s.filtered[s.currentIdx].SeekGE(s.lowerBound, flags)
|
||||
} else {
|
||||
iterKey, val = s.filtered[s.currentIdx].First()
|
||||
}
|
||||
if iterKey != nil {
|
||||
return iterKey, val
|
||||
}
|
||||
if err := s.filtered[s.currentIdx].Error(); err != nil {
|
||||
s.err = err
|
||||
}
|
||||
s.currentIdx++
|
||||
}
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) First() (*base.InternalKey, base.LazyValue) {
|
||||
if s.err != nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
s.currentIdx = 0
|
||||
return s.skipEmptyFileForward(nil /* seekKey */, base.SeekGEFlagsNone)
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) Last() (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) Next() (*base.InternalKey, base.LazyValue) {
|
||||
if s.err != nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if s.currentIdx < 0 || s.currentIdx >= len(s.filtered) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if iterKey, val := s.filtered[s.currentIdx].Next(); iterKey != nil {
|
||||
return iterKey, val
|
||||
}
|
||||
s.currentIdx++
|
||||
return s.skipEmptyFileForward(nil /* seekKey */, base.SeekGEFlagsNone)
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) Prev() (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) Error() error {
|
||||
if s.currentIdx >= 0 && s.currentIdx < len(s.filtered) {
|
||||
s.err = firstError(s.err, s.filtered[s.currentIdx].Error())
|
||||
}
|
||||
return s.err
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) Close() error {
|
||||
var err error
|
||||
for i := range s.iters {
|
||||
err = firstError(err, s.iters[i].Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) SetBounds(lower, upper []byte) {
|
||||
s.currentIdx = -1
|
||||
s.lowerBound = lower
|
||||
for i := range s.iters {
|
||||
s.iters[i].SetBounds(lower, upper)
|
||||
}
|
||||
s.resetFilteredIters()
|
||||
}
|
||||
|
||||
func (s *simpleLevelIter) String() string {
|
||||
if s.currentIdx < 0 || s.currentIdx >= len(s.filtered) {
|
||||
return "simpleLevelIter: current=<nil>"
|
||||
}
|
||||
return fmt.Sprintf("simpleLevelIter: current=%s", s.filtered[s.currentIdx])
|
||||
}
|
||||
|
||||
var _ internalIterator = &simpleLevelIter{}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/flush_external.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/flush_external.go
generated
vendored
@@ -85,7 +85,7 @@ func flushExternalTable(untypedDB interface{}, path string, originalMeta *fileMe
|
||||
}
|
||||
return err
|
||||
}
|
||||
d.updateReadStateLocked(d.opts.DebugCheck, nil)
|
||||
d.updateReadStateLocked(d.opts.DebugCheck)
|
||||
d.updateTableStatsLocked(ve.NewFiles)
|
||||
d.deleteObsoleteFiles(jobID, true /* waitForOngoing */)
|
||||
d.maybeScheduleCompaction()
|
||||
|
||||
9
vendor/github.com/cockroachdb/pebble/flushable.go
generated
vendored
9
vendor/github.com/cockroachdb/pebble/flushable.go
generated
vendored
@@ -7,6 +7,7 @@ package pebble
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
)
|
||||
@@ -17,6 +18,7 @@ type flushable interface {
|
||||
newFlushIter(o *IterOptions, bytesFlushed *uint64) internalIterator
|
||||
newRangeDelIter(o *IterOptions) keyspan.FragmentIterator
|
||||
newRangeKeyIter(o *IterOptions) keyspan.FragmentIterator
|
||||
containsRangeKeys() bool
|
||||
// inuseBytes returns the number of inuse bytes by the flushable.
|
||||
inuseBytes() uint64
|
||||
// totalBytes returns the total number of bytes allocated by the flushable.
|
||||
@@ -36,9 +38,10 @@ type flushableEntry struct {
|
||||
// flushForced indicates whether a flush was forced on this memtable (either
|
||||
// manual, or due to ingestion). Protected by DB.mu.
|
||||
flushForced bool
|
||||
// delayedFlushForced indicates whether a timer has been set to force a flush
|
||||
// on this memtable at some point in the future. Protected by DB.mu
|
||||
delayedFlushForced bool
|
||||
// delayedFlushForcedAt indicates whether a timer has been set to force a
|
||||
// flush on this memtable at some point in the future. Protected by DB.mu.
|
||||
// Holds the timestamp of when the flush will be issued.
|
||||
delayedFlushForcedAt time.Time
|
||||
// logNum corresponds to the WAL that contains the records present in the
|
||||
// receiver.
|
||||
logNum FileNum
|
||||
|
||||
261
vendor/github.com/cockroachdb/pebble/format_major_version.go
generated
vendored
261
vendor/github.com/cockroachdb/pebble/format_major_version.go
generated
vendored
@@ -40,6 +40,8 @@ func (v FormatMajorVersion) String() string {
|
||||
}
|
||||
|
||||
const (
|
||||
// 21.2 versions.
|
||||
|
||||
// FormatDefault leaves the format version unspecified. The
|
||||
// FormatDefault constant may be ratcheted upwards over time.
|
||||
FormatDefault FormatMajorVersion = iota
|
||||
@@ -68,6 +70,9 @@ const (
|
||||
// kind, base.InternalKeyKindSetWithDelete. Previous Pebble versions will be
|
||||
// unable to open this database.
|
||||
FormatSetWithDelete
|
||||
|
||||
// 22.1 versions.
|
||||
|
||||
// FormatBlockPropertyCollector is a format major version that introduces
|
||||
// BlockPropertyCollectors.
|
||||
FormatBlockPropertyCollector
|
||||
@@ -77,21 +82,46 @@ const (
|
||||
// (without holding mutexes) until the scan of the LSM is complete and the
|
||||
// manifest has been rotated.
|
||||
FormatSplitUserKeysMarked
|
||||
// FormatMarkedCompacted is a format major version that guarantees that all
|
||||
// files explicitly marked for compaction in the manifest have been
|
||||
// compacted. Combined with the FormatSplitUserKeysMarked format major
|
||||
// version, this version guarantees that there are no user keys split across
|
||||
// multiple files within a level L1+. Ratcheting to this format version will
|
||||
// block (without holding mutexes) until all necessary compactions for files
|
||||
// marked for compaction are complete.
|
||||
FormatMarkedCompacted
|
||||
|
||||
// 22.2 versions.
|
||||
|
||||
// FormatSplitUserKeysMarkedCompacted is a format major version that
|
||||
// guarantees that all files explicitly marked for compaction in the manifest
|
||||
// have been compacted. Combined with the FormatSplitUserKeysMarked format
|
||||
// major version, this version guarantees that there are no user keys split
|
||||
// across multiple files within a level L1+. Ratcheting to this format version
|
||||
// will block (without holding mutexes) until all necessary compactions for
|
||||
// files marked for compaction are complete.
|
||||
FormatSplitUserKeysMarkedCompacted
|
||||
// FormatRangeKeys is a format major version that introduces range keys.
|
||||
FormatRangeKeys
|
||||
// FormatMinTableFormatPebblev1 is a format major version that guarantees that
|
||||
// tables created by or ingested into the DB at or above this format major
|
||||
// version will have a table format version of at least Pebblev1 (Block
|
||||
// Properties).
|
||||
FormatMinTableFormatPebblev1
|
||||
// FormatPrePebblev1Marked is a format major version that guarantees that all
|
||||
// sstables with a table format version pre-Pebblev1 (i.e. those that are
|
||||
// guaranteed to not contain block properties) are marked for compaction in
|
||||
// the manifest. Ratcheting to FormatPrePebblev1Marked will block (without
|
||||
// holding mutexes) until the scan of the LSM is complete and the manifest has
|
||||
// been rotated.
|
||||
FormatPrePebblev1Marked
|
||||
|
||||
// 23.1 versions.
|
||||
|
||||
// FormatPrePebblev1MarkedCompacted is a format major version that
|
||||
// guarantees that all sstables explicitly marked for compaction in the
|
||||
// manifest have been compacted. Ratcheting to this format version will block
|
||||
// (without holding mutexes) until all necessary compactions for files marked
|
||||
// for compaction are complete.
|
||||
FormatPrePebblev1MarkedCompacted
|
||||
|
||||
// FormatNewest always contains the most recent format major version.
|
||||
// NB: When adding new versions, the MaxTableFormat method should also be
|
||||
// updated to return the maximum allowable version for the new
|
||||
// FormatMajorVersion.
|
||||
FormatNewest FormatMajorVersion = FormatRangeKeys
|
||||
FormatNewest FormatMajorVersion = FormatPrePebblev1MarkedCompacted
|
||||
)
|
||||
|
||||
// MaxTableFormat returns the maximum sstable.TableFormat that can be used at
|
||||
@@ -101,15 +131,34 @@ func (v FormatMajorVersion) MaxTableFormat() sstable.TableFormat {
|
||||
case FormatDefault, FormatMostCompatible, formatVersionedManifestMarker,
|
||||
FormatVersioned, FormatSetWithDelete:
|
||||
return sstable.TableFormatRocksDBv2
|
||||
case FormatBlockPropertyCollector, FormatSplitUserKeysMarked, FormatMarkedCompacted:
|
||||
case FormatBlockPropertyCollector, FormatSplitUserKeysMarked,
|
||||
FormatSplitUserKeysMarkedCompacted:
|
||||
return sstable.TableFormatPebblev1
|
||||
case FormatRangeKeys:
|
||||
case FormatRangeKeys, FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
||||
FormatPrePebblev1MarkedCompacted:
|
||||
return sstable.TableFormatPebblev2
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||
}
|
||||
}
|
||||
|
||||
// MinTableFormat returns the minimum sstable.TableFormat that can be used at
|
||||
// this FormatMajorVersion.
|
||||
func (v FormatMajorVersion) MinTableFormat() sstable.TableFormat {
|
||||
switch v {
|
||||
case FormatDefault, FormatMostCompatible, formatVersionedManifestMarker,
|
||||
FormatVersioned, FormatSetWithDelete, FormatBlockPropertyCollector,
|
||||
FormatSplitUserKeysMarked, FormatSplitUserKeysMarkedCompacted,
|
||||
FormatRangeKeys:
|
||||
return sstable.TableFormatLevelDB
|
||||
case FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
||||
FormatPrePebblev1MarkedCompacted:
|
||||
return sstable.TableFormatPebblev1
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||
}
|
||||
}
|
||||
|
||||
// formatMajorVersionMigrations defines the migrations from one format
|
||||
// major version to the next. Each migration is defined as a closure
|
||||
// which will be invoked on the database before the new format major
|
||||
@@ -189,23 +238,43 @@ var formatMajorVersionMigrations = map[FormatMajorVersion]func(*DB) error{
|
||||
FormatSplitUserKeysMarked: func(d *DB) error {
|
||||
// Mark any unmarked files with split-user keys. Note all format major
|
||||
// versions migrations are invoked with DB.mu locked.
|
||||
if err := d.markFilesWithSplitUserKeysLocked(); err != nil {
|
||||
if err := d.markFilesLocked(markFilesWithSplitUserKeys(d.opts.Comparer.Equal)); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.finalizeFormatVersUpgrade(FormatSplitUserKeysMarked)
|
||||
},
|
||||
FormatMarkedCompacted: func(d *DB) error {
|
||||
FormatSplitUserKeysMarkedCompacted: func(d *DB) error {
|
||||
// Before finalizing the format major version, rewrite any sstables
|
||||
// still marked for compaction. Note all format major versions
|
||||
// migrations are invoked with DB.mu locked.
|
||||
if err := d.compactMarkedFilesLocked(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.finalizeFormatVersUpgrade(FormatMarkedCompacted)
|
||||
return d.finalizeFormatVersUpgrade(FormatSplitUserKeysMarkedCompacted)
|
||||
},
|
||||
FormatRangeKeys: func(d *DB) error {
|
||||
return d.finalizeFormatVersUpgrade(FormatRangeKeys)
|
||||
},
|
||||
FormatMinTableFormatPebblev1: func(d *DB) error {
|
||||
return d.finalizeFormatVersUpgrade(FormatMinTableFormatPebblev1)
|
||||
},
|
||||
FormatPrePebblev1Marked: func(d *DB) error {
|
||||
// Mark any unmarked files that contain only table properties. Note all
|
||||
// format major versions migrations are invoked with DB.mu locked.
|
||||
if err := d.markFilesLocked(markFilesPrePebblev1(d.tableCache)); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.finalizeFormatVersUpgrade(FormatPrePebblev1Marked)
|
||||
},
|
||||
FormatPrePebblev1MarkedCompacted: func(d *DB) error {
|
||||
// Before finalizing the format major version, rewrite any sstables
|
||||
// still marked for compaction. Note all format major versions
|
||||
// migrations are invoked with DB.mu locked.
|
||||
if err := d.compactMarkedFilesLocked(); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.finalizeFormatVersUpgrade(FormatPrePebblev1MarkedCompacted)
|
||||
},
|
||||
}
|
||||
|
||||
const formatVersionMarkerName = `format-version`
|
||||
@@ -316,7 +385,11 @@ func (d *DB) finalizeFormatVersUpgrade(formatVers FormatMajorVersion) error {
|
||||
// compactMarkedFilesLocked performs a migration that schedules rewrite
|
||||
// compactions to compact away any sstables marked for compaction.
|
||||
// compactMarkedFilesLocked is run while ratcheting the database's format major
|
||||
// version to FormatMarkedCompacted.
|
||||
// version to FormatSplitUserKeysMarkedCompacted.
|
||||
//
|
||||
// Note that while this method is called with the DB.mu held, and will not
|
||||
// return until all marked files have been compacted, the mutex is dropped while
|
||||
// waiting for compactions to complete (or for slots to free up).
|
||||
func (d *DB) compactMarkedFilesLocked() error {
|
||||
curr := d.mu.versions.currentVersion()
|
||||
for curr.Stats.MarkedForCompaction > 0 {
|
||||
@@ -339,44 +412,41 @@ func (d *DB) compactMarkedFilesLocked() error {
|
||||
if err := d.closed.Load(); err != nil {
|
||||
return err.(error)
|
||||
}
|
||||
// NB: Waiting on this condition variable drops d.mu while blocked.
|
||||
d.mu.compact.cond.Wait()
|
||||
|
||||
// Some flush or compaction was scheduled or completed. Loop again to
|
||||
// check again for files that must be compacted. The next iteration may
|
||||
// find same file again, but that's okay. It'll eventually succeed in
|
||||
// scheduling the compaction and eventually be woken by its completion.
|
||||
// Some flush or compaction may have scheduled or completed while we waited
|
||||
// for the manifest lock in maybeScheduleCompactionPicker. Get the latest
|
||||
// Version before waiting on a compaction.
|
||||
curr = d.mu.versions.currentVersion()
|
||||
|
||||
// Only wait on compactions if there are files still marked for compaction.
|
||||
// NB: Waiting on this condition variable drops d.mu while blocked.
|
||||
if curr.Stats.MarkedForCompaction > 0 {
|
||||
if d.mu.compact.compactingCount == 0 {
|
||||
panic("expected a compaction of marked files in progress")
|
||||
}
|
||||
d.mu.compact.cond.Wait()
|
||||
// Refresh the current version again.
|
||||
curr = d.mu.versions.currentVersion()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// markFilesWithSplitUserKeysLocked scans the LSM's levels 1 through 6 for
|
||||
// adjacent files that contain the same user key. Such arrangements of files
|
||||
// were permitted in RocksDB and in Pebble up to SHA a860bbad.
|
||||
// markFilesWithSplitUserKeysLocked durably marks such files for compaction.
|
||||
func (d *DB) markFilesWithSplitUserKeysLocked() error {
|
||||
jobID := d.mu.nextJobID
|
||||
d.mu.nextJobID++
|
||||
|
||||
// Files with split user keys are expected to be rare and performing key
|
||||
// comparisons for every file within the LSM is expensive, so drop the
|
||||
// database lock while scanning the file metadata.
|
||||
//
|
||||
// After scanning, if we found files to mark, we'll re-acquire the mutex
|
||||
// before marking them, since MarkedForCompaction and the version's
|
||||
// Stats.MarkedForCompaction are protected by d.mu.
|
||||
var files [numLevels][]*fileMetadata
|
||||
var foundSplitUserKey bool
|
||||
func() {
|
||||
equal := d.opts.equal()
|
||||
vers := d.mu.versions.currentVersion()
|
||||
// Note the unusual locking: unlock, defer Lock().
|
||||
d.mu.Unlock()
|
||||
defer d.mu.Lock()
|
||||
// findFilesFunc scans the LSM for files, returning true if at least one
|
||||
// file was found. The returned array contains the matched files, if any, per
|
||||
// level.
|
||||
type findFilesFunc func(v *version) (found bool, files [numLevels][]*fileMetadata, _ error)
|
||||
|
||||
// markFilesWithSplitUserKeys scans the LSM's levels 1 through 6 for adjacent
|
||||
// files that contain the same user key. Such arrangements of files were
|
||||
// permitted in RocksDB and in Pebble up to SHA a860bbad.
|
||||
var markFilesWithSplitUserKeys = func(equal Equal) findFilesFunc {
|
||||
return func(v *version) (found bool, files [numLevels][]*fileMetadata, _ error) {
|
||||
// Files with split user keys are expected to be rare and performing key
|
||||
// comparisons for every file within the LSM is expensive, so drop the
|
||||
// database lock while scanning the file metadata.
|
||||
for l := numLevels - 1; l > 0; l-- {
|
||||
iter := vers.Levels[l].Iter()
|
||||
iter := v.Levels[l].Iter()
|
||||
var prevFile *fileMetadata
|
||||
var prevUserKey []byte
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
@@ -384,7 +454,7 @@ func (d *DB) markFilesWithSplitUserKeysLocked() error {
|
||||
// NB: We may append a file twice, once as prevFile and once
|
||||
// as f. That's okay, and handled below.
|
||||
files[l] = append(files[l], prevFile, f)
|
||||
foundSplitUserKey = true
|
||||
found = true
|
||||
}
|
||||
if f.Largest.IsExclusiveSentinel() {
|
||||
prevUserKey = nil
|
||||
@@ -395,41 +465,102 @@ func (d *DB) markFilesWithSplitUserKeysLocked() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// markFilesPrePebblev1 scans the LSM for files that do not support block
|
||||
// properties (i.e. a table format version pre-Pebblev1).
|
||||
var markFilesPrePebblev1 = func(tc *tableCacheContainer) findFilesFunc {
|
||||
return func(v *version) (found bool, files [numLevels][]*fileMetadata, err error) {
|
||||
for l := numLevels - 1; l > 0; l-- {
|
||||
iter := v.Levels[l].Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
err = tc.withReader(f, func(r *sstable.Reader) error {
|
||||
tf, err := r.TableFormat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tf < sstable.TableFormatPebblev1 {
|
||||
found = true
|
||||
files[l] = append(files[l], f)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// markFilesLock durably marks the files that match the given findFilesFunc for
|
||||
// compaction.
|
||||
func (d *DB) markFilesLocked(findFn findFilesFunc) error {
|
||||
jobID := d.mu.nextJobID
|
||||
d.mu.nextJobID++
|
||||
|
||||
vers := d.mu.versions.currentVersion()
|
||||
var (
|
||||
found bool
|
||||
files [numLevels][]*fileMetadata
|
||||
err error
|
||||
)
|
||||
func() {
|
||||
// Disable file deletions. Some migrations that mark files may need to
|
||||
// read the files themselves. The LSM may be changing while the
|
||||
// migration is determining which files to mark. That's ok, because
|
||||
// after re-acquiring the mutex and acquiring the manifest lock, we'll
|
||||
// discard any sstables that have been removed from the LSM. However, we
|
||||
// also need to make sure that the files we try to open during the scan
|
||||
// are not removed before we read them. This is preferable over
|
||||
// gracefully handling nonexistent files because some environments (eg,
|
||||
// Windows) error if you attempt to remove an open file.
|
||||
d.disableFileDeletions()
|
||||
defer d.enableFileDeletions()
|
||||
|
||||
// Note the unusual locking: unlock, defer Lock(). The scan of the files in
|
||||
// the version does not need to block other operations that require the
|
||||
// DB.mu. Drop it for the scan, before re-acquiring it.
|
||||
d.mu.Unlock()
|
||||
defer d.mu.Lock()
|
||||
found, files, err = findFn(vers)
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The database lock has been acquired again by the defer within the above
|
||||
// anonymous function.
|
||||
if !foundSplitUserKey {
|
||||
if !found {
|
||||
// Nothing to do.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lock the manifest for a coherent view of the LSM.
|
||||
// After scanning, if we found files to mark, we fetch the current state of
|
||||
// the LSM (which may have changed) and set MarkedForCompaction on the files,
|
||||
// and update the version's Stats.MarkedForCompaction count, which are both
|
||||
// protected by d.mu.
|
||||
|
||||
// Lock the manifest for a coherent view of the LSM. The database lock has
|
||||
// been re-acquired by the defer within the above anonymous function.
|
||||
d.mu.versions.logLock()
|
||||
vers := d.mu.versions.currentVersion()
|
||||
vers = d.mu.versions.currentVersion()
|
||||
for l, filesToMark := range files {
|
||||
if len(filesToMark) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, f := range filesToMark {
|
||||
if f.MarkedForCompaction {
|
||||
// Ignore files to be marked that have already been compacted or marked.
|
||||
if f.CompactionState == manifest.CompactionStateCompacted ||
|
||||
f.MarkedForCompaction {
|
||||
continue
|
||||
}
|
||||
|
||||
// We need to determine if the file still exists in the current
|
||||
// version of the LSM. Note that we're making the assumption that if
|
||||
// f still exists within the LSM, it's within the same level. That's
|
||||
// okay, because only move compactions may move files between levels
|
||||
// and move compactions only apply to compactions with a single file
|
||||
// in the start level, but we already know these files have
|
||||
// multi-file atomic compaction units.
|
||||
if m := vers.Levels[l].Find(d.cmp, f); m != nil {
|
||||
// It's still there. Mark it for compaction and update the
|
||||
// version's stats.
|
||||
f.MarkedForCompaction = true
|
||||
vers.Stats.MarkedForCompaction++
|
||||
}
|
||||
// Else, mark the file for compaction in this version.
|
||||
vers.Stats.MarkedForCompaction++
|
||||
f.MarkedForCompaction = true
|
||||
}
|
||||
// The compaction picker uses the markedForCompactionAnnotator to
|
||||
// quickly find files marked for compaction, or to quickly determine
|
||||
|
||||
70
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
70
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
@@ -25,7 +25,7 @@ type getIter struct {
|
||||
key []byte
|
||||
iter internalIterator
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
tombstone keyspan.Span
|
||||
tombstone *keyspan.Span
|
||||
levelIter levelIter
|
||||
level int
|
||||
batch *Batch
|
||||
@@ -33,7 +33,7 @@ type getIter struct {
|
||||
l0 []manifest.LevelSlice
|
||||
version *version
|
||||
iterKey *InternalKey
|
||||
iterValue []byte
|
||||
iterValue base.LazyValue
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -47,29 +47,29 @@ func (g *getIter) String() string {
|
||||
return fmt.Sprintf("len(l0)=%d, len(mem)=%d, level=%d", len(g.l0), len(g.mem), g.level)
|
||||
}
|
||||
|
||||
func (g *getIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (g *getIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (g *getIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) First() (*InternalKey, []byte) {
|
||||
func (g *getIter) First() (*InternalKey, base.LazyValue) {
|
||||
return g.Next()
|
||||
}
|
||||
|
||||
func (g *getIter) Last() (*InternalKey, []byte) {
|
||||
func (g *getIter) Last() (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: Last unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
func (g *getIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if g.iter != nil {
|
||||
g.iterKey, g.iterValue = g.iter.Next()
|
||||
}
|
||||
@@ -82,26 +82,26 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
// key. Every call to levelIter.Next() potentially switches to a new
|
||||
// table and thus reinitializes rangeDelIter.
|
||||
if g.rangeDelIter != nil {
|
||||
g.tombstone = keyspan.Get(g.cmp, g.rangeDelIter, g.key, g.snapshot)
|
||||
g.tombstone = keyspan.Get(g.cmp, g.rangeDelIter, g.key)
|
||||
if g.err = g.rangeDelIter.Close(); g.err != nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
g.rangeDelIter = nil
|
||||
}
|
||||
|
||||
if g.iterKey != nil {
|
||||
key := g.iterKey
|
||||
if g.tombstone.Covers(key.SeqNum()) {
|
||||
if g.tombstone != nil && g.tombstone.CoversAt(g.snapshot, key.SeqNum()) {
|
||||
// We have a range tombstone covering this key. Rather than return a
|
||||
// point or range deletion here, we return false and close our
|
||||
// internal iterator which will make Valid() return false,
|
||||
// effectively stopping iteration.
|
||||
g.err = g.iter.Close()
|
||||
g.iter = nil
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if g.equal(g.key, key.UserKey) {
|
||||
if !key.Visible(g.snapshot) {
|
||||
if !key.Visible(g.snapshot, base.InternalKeySeqNumMax) {
|
||||
g.iterKey, g.iterValue = g.iter.Next()
|
||||
continue
|
||||
}
|
||||
@@ -113,23 +113,33 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
g.err = g.iter.Close()
|
||||
g.iter = nil
|
||||
if g.err != nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an iterator from the batch.
|
||||
if g.batch != nil {
|
||||
if g.batch.index == nil {
|
||||
g.err = ErrNotIndexed
|
||||
g.iterKey, g.iterValue = nil, base.LazyValue{}
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
g.iter = g.batch.newInternalIter(nil)
|
||||
g.rangeDelIter = g.batch.newRangeDelIter(nil)
|
||||
g.rangeDelIter = g.batch.newRangeDelIter(
|
||||
nil,
|
||||
// Get always reads the entirety of the batch's history, so no
|
||||
// batch keys should be filtered.
|
||||
base.InternalKeySeqNumMax,
|
||||
)
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, base.SeekGEFlagsNone)
|
||||
g.batch = nil
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, false /* trySeekUsingNext */)
|
||||
continue
|
||||
}
|
||||
|
||||
// If we have a tombstone from a previous level it is guaranteed to delete
|
||||
// keys in lower levels.
|
||||
if !g.tombstone.Empty() {
|
||||
return nil, nil
|
||||
if g.tombstone != nil && g.tombstone.VisibleAt(g.snapshot) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Create iterators from memtables from newest to oldest.
|
||||
@@ -138,7 +148,7 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
g.iter = m.newIter(nil)
|
||||
g.rangeDelIter = m.newRangeDelIter(nil)
|
||||
g.mem = g.mem[:n-1]
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, false /* trySeekUsingNext */)
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, base.SeekGEFlagsNone)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -149,17 +159,17 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
g.l0 = g.l0[:n-1]
|
||||
iterOpts := IterOptions{logger: g.logger}
|
||||
g.levelIter.init(iterOpts, g.cmp, nil /* split */, g.newIters,
|
||||
files, manifest.L0Sublevel(n), nil)
|
||||
files, manifest.L0Sublevel(n), internalIterOpts{})
|
||||
g.levelIter.initRangeDel(&g.rangeDelIter)
|
||||
g.iter = &g.levelIter
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, false /* trySeekUsingNext */)
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, base.SeekGEFlagsNone)
|
||||
continue
|
||||
}
|
||||
g.level++
|
||||
}
|
||||
|
||||
if g.level >= numLevels {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if g.version.Levels[g.level].Empty() {
|
||||
g.level++
|
||||
@@ -168,26 +178,18 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
|
||||
iterOpts := IterOptions{logger: g.logger}
|
||||
g.levelIter.init(iterOpts, g.cmp, nil /* split */, g.newIters,
|
||||
g.version.Levels[g.level].Iter(), manifest.Level(g.level), nil)
|
||||
g.version.Levels[g.level].Iter(), manifest.Level(g.level), internalIterOpts{})
|
||||
g.levelIter.initRangeDel(&g.rangeDelIter)
|
||||
g.level++
|
||||
g.iter = &g.levelIter
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, false /* trySeekUsingNext */)
|
||||
g.iterKey, g.iterValue = g.iter.SeekGE(g.key, base.SeekGEFlagsNone)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *getIter) Prev() (*InternalKey, []byte) {
|
||||
func (g *getIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: Prev unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) Key() *InternalKey {
|
||||
return g.iterKey
|
||||
}
|
||||
|
||||
func (g *getIter) Value() []byte {
|
||||
return g.iterValue
|
||||
}
|
||||
|
||||
func (g *getIter) Valid() bool {
|
||||
return g.iterKey != nil && g.err == nil
|
||||
}
|
||||
|
||||
27
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
27
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
@@ -69,10 +69,10 @@ func ingestLoad1(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tf > fmv.MaxTableFormat() {
|
||||
if tf < fmv.MinTableFormat() || tf > fmv.MaxTableFormat() {
|
||||
return nil, errors.Newf(
|
||||
"pebble: table with format %s unsupported at DB format major version %d, %s",
|
||||
tf, fmv, fmv.MaxTableFormat(),
|
||||
"pebble: table format %s is not within range supported at DB format major version %d, (%s,%s)",
|
||||
tf, fmv, fmv.MinTableFormat(), fmv.MaxTableFormat(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func ingestLoad1(
|
||||
if iter != nil {
|
||||
defer iter.Close()
|
||||
var smallest InternalKey
|
||||
if s := iter.First(); s.Valid() {
|
||||
if s := iter.First(); s != nil {
|
||||
key := s.SmallestKey()
|
||||
if err := ingestValidateKey(opts, &key); err != nil {
|
||||
return nil, err
|
||||
@@ -136,7 +136,7 @@ func ingestLoad1(
|
||||
if err := iter.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s := iter.Last(); s.Valid() {
|
||||
if s := iter.Last(); s != nil {
|
||||
k := s.SmallestKey()
|
||||
if err := ingestValidateKey(opts, &k); err != nil {
|
||||
return nil, err
|
||||
@@ -155,7 +155,7 @@ func ingestLoad1(
|
||||
if iter != nil {
|
||||
defer iter.Close()
|
||||
var smallest InternalKey
|
||||
if s := iter.First(); s.Valid() {
|
||||
if s := iter.First(); s != nil {
|
||||
key := s.SmallestKey()
|
||||
if err := ingestValidateKey(opts, &key); err != nil {
|
||||
return nil, err
|
||||
@@ -165,7 +165,7 @@ func ingestLoad1(
|
||||
if err := iter.Error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s := iter.Last(); s.Valid() {
|
||||
if s := iter.Last(); s != nil {
|
||||
k := s.SmallestKey()
|
||||
if err := ingestValidateKey(opts, &k); err != nil {
|
||||
return nil, err
|
||||
@@ -271,7 +271,8 @@ func ingestLink(
|
||||
fs := syncingFS{
|
||||
FS: opts.FS,
|
||||
syncOpts: vfs.SyncingFileOptions{
|
||||
BytesPerSync: opts.BytesPerSync,
|
||||
NoSyncOnClose: opts.NoSyncOnClose,
|
||||
BytesPerSync: opts.BytesPerSync,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -397,7 +398,7 @@ func overlapWithIterator(
|
||||
// means boundary < L and hence is similar to 1).
|
||||
// 4) boundary == L and L is sentinel,
|
||||
// we'll always overlap since for any values of i,j ranges [i, k) and [j, k) always overlap.
|
||||
key, _ := iter.SeekGE(meta.Smallest.UserKey, false /* trySeekUsingNext */)
|
||||
key, _ := iter.SeekGE(meta.Smallest.UserKey, base.SeekGEFlagsNone)
|
||||
if key != nil {
|
||||
c := sstableKeyCompare(cmp, *key, meta.Largest)
|
||||
if c <= 0 {
|
||||
@@ -411,10 +412,10 @@ func overlapWithIterator(
|
||||
}
|
||||
rangeDelItr := *rangeDelIter
|
||||
rangeDel := rangeDelItr.SeekLT(meta.Smallest.UserKey)
|
||||
if !rangeDel.Valid() {
|
||||
if rangeDel == nil {
|
||||
rangeDel = rangeDelItr.Next()
|
||||
}
|
||||
for ; rangeDel.Valid(); rangeDel = rangeDelItr.Next() {
|
||||
for ; rangeDel != nil; rangeDel = rangeDelItr.Next() {
|
||||
key := rangeDel.SmallestKey()
|
||||
c := sstableKeyCompare(cmp, key, meta.Largest)
|
||||
if c > 0 {
|
||||
@@ -510,7 +511,7 @@ func ingestTargetLevel(
|
||||
continue
|
||||
}
|
||||
|
||||
iter, rangeDelIter, err := newIters(iter.Current(), nil, nil)
|
||||
iter, rangeDelIter, err := newIters(iter.Current(), nil, internalIterOpts{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -848,7 +849,7 @@ func (d *DB) ingestApply(
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.updateReadStateLocked(d.opts.DebugCheck, nil)
|
||||
d.updateReadStateLocked(d.opts.DebugCheck)
|
||||
d.updateTableStatsLocked(ve.NewFiles)
|
||||
d.deleteObsoleteFiles(jobID, false /* waitForOngoing */)
|
||||
// The ingestion may have pushed a level over the threshold for compaction,
|
||||
|
||||
4
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
@@ -33,4 +33,6 @@ type InternalKey = base.InternalKey
|
||||
|
||||
type internalIterator = base.InternalIterator
|
||||
|
||||
type internalIteratorWithStats = base.InternalIteratorWithStats
|
||||
// ErrCorruption is a marker to indicate that data in a file (WAL, MANIFEST,
|
||||
// sstable) isn't in the expected format.
|
||||
var ErrCorruption = base.ErrCorruption
|
||||
|
||||
24
vendor/github.com/cockroachdb/pebble/internal/arenaskl/flush_iterator.go
generated
vendored
24
vendor/github.com/cockroachdb/pebble/internal/arenaskl/flush_iterator.go
generated
vendored
@@ -34,17 +34,21 @@ func (it *flushIterator) String() string {
|
||||
return "memtable"
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekGE(key []byte, tryNextUsingSeek bool) (*base.InternalKey, []byte) {
|
||||
func (it *flushIterator) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekLT(key []byte) (*base.InternalKey, []byte) {
|
||||
func (it *flushIterator) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
@@ -52,10 +56,10 @@ func (it *flushIterator) SeekLT(key []byte) (*base.InternalKey, []byte) {
|
||||
// if the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that First only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound.
|
||||
func (it *flushIterator) First() (*base.InternalKey, []byte) {
|
||||
func (it *flushIterator) First() (*base.InternalKey, base.LazyValue) {
|
||||
key, val := it.Iterator.First()
|
||||
if key == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
*it.bytesIterated += uint64(it.nd.allocSize)
|
||||
return key, val
|
||||
@@ -65,16 +69,16 @@ func (it *flushIterator) First() (*base.InternalKey, []byte) {
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
// Note: flushIterator.Next mirrors the implementation of Iterator.Next
|
||||
// due to performance. Keep the two in sync.
|
||||
func (it *flushIterator) Next() (*base.InternalKey, []byte) {
|
||||
func (it *flushIterator) Next() (*base.InternalKey, base.LazyValue) {
|
||||
it.nd = it.list.getNext(it.nd, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
*it.bytesIterated += uint64(it.nd.allocSize)
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
func (it *flushIterator) Prev() (*base.InternalKey, []byte) {
|
||||
func (it *flushIterator) Prev() (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: Prev unimplemented")
|
||||
}
|
||||
|
||||
62
vendor/github.com/cockroachdb/pebble/internal/arenaskl/iterator.go
generated
vendored
62
vendor/github.com/cockroachdb/pebble/internal/arenaskl/iterator.go
generated
vendored
@@ -78,11 +78,11 @@ func (it *Iterator) Error() error {
|
||||
// pointing at a valid entry, and (nil, nil) otherwise. Note that SeekGE only
|
||||
// checks the upper bound. It is up to the caller to ensure that key is greater
|
||||
// than or equal to the lower bound.
|
||||
func (it *Iterator) SeekGE(key []byte, trySeekUsingNext bool) (*base.InternalKey, []byte) {
|
||||
if trySeekUsingNext {
|
||||
func (it *Iterator) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKey, base.LazyValue) {
|
||||
if flags.TrySeekUsingNext() {
|
||||
if it.nd == it.list.tail {
|
||||
// Iterator is done.
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
less := it.list.cmp(it.key.UserKey, key) < 0
|
||||
// Arbitrary constant. By measuring the seek cost as a function of the
|
||||
@@ -94,24 +94,24 @@ func (it *Iterator) SeekGE(key []byte, trySeekUsingNext bool) (*base.InternalKey
|
||||
k, _ := it.Next()
|
||||
if k == nil {
|
||||
// Iterator is done.
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
less = it.list.cmp(it.key.UserKey, key) < 0
|
||||
}
|
||||
if !less {
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
}
|
||||
_, it.nd, _ = it.seekForBaseSplice(key)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// SeekPrefixGE moves the iterator to the first entry whose key is greater than
|
||||
@@ -119,94 +119,94 @@ func (it *Iterator) SeekGE(key []byte, trySeekUsingNext bool) (*base.InternalKey
|
||||
// provided so that an arenaskl.Iterator implements the
|
||||
// internal/base.InternalIterator interface.
|
||||
func (it *Iterator) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return it.SeekGE(key, trySeekUsingNext)
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return it.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
// SeekLT moves the iterator to the last entry whose key is less than the given
|
||||
// key. Returns the key and value if the iterator is pointing at a valid entry,
|
||||
// and (nil, nil) otherwise. Note that SeekLT only checks the lower bound. It
|
||||
// is up to the caller to ensure that key is less than the upper bound.
|
||||
func (it *Iterator) SeekLT(key []byte) (*base.InternalKey, []byte) {
|
||||
func (it *Iterator) SeekLT(key []byte, flags base.SeekLTFlags) (*base.InternalKey, base.LazyValue) {
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// the upper-bound.
|
||||
it.nd, _, _ = it.seekForBaseSplice(key)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// First seeks position at the first entry in list. Returns the key and value
|
||||
// if the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that First only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound (e.g. via a call to SeekGE(lower)).
|
||||
func (it *Iterator) First() (*base.InternalKey, []byte) {
|
||||
func (it *Iterator) First() (*base.InternalKey, base.LazyValue) {
|
||||
it.nd = it.list.getNext(it.list.head, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// Last seeks position at the last entry in list. Returns the key and value if
|
||||
// the iterator is pointing at a valid entry, and (nil, nil) otherwise. Note
|
||||
// that Last only checks the lower bound. It is up to the caller to ensure that
|
||||
// key is less than the upper bound (e.g. via a call to SeekLT(upper)).
|
||||
func (it *Iterator) Last() (*base.InternalKey, []byte) {
|
||||
func (it *Iterator) Last() (*base.InternalKey, base.LazyValue) {
|
||||
it.nd = it.list.getPrev(it.list.tail, 0)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// Next advances to the next position. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
// Note: flushIterator.Next mirrors the implementation of Iterator.Next
|
||||
// due to performance. Keep the two in sync.
|
||||
func (it *Iterator) Next() (*base.InternalKey, []byte) {
|
||||
func (it *Iterator) Next() (*base.InternalKey, base.LazyValue) {
|
||||
it.nd = it.list.getNext(it.nd, 0)
|
||||
if it.nd == it.list.tail {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.upper != nil && it.list.cmp(it.upper, it.key.UserKey) <= 0 {
|
||||
it.nd = it.list.tail
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// Prev moves to the previous position. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise.
|
||||
func (it *Iterator) Prev() (*base.InternalKey, []byte) {
|
||||
func (it *Iterator) Prev() (*base.InternalKey, base.LazyValue) {
|
||||
it.nd = it.list.getPrev(it.nd, 0)
|
||||
if it.nd == it.list.head {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
it.decodeKey()
|
||||
if it.lower != nil && it.list.cmp(it.lower, it.key.UserKey) > 0 {
|
||||
it.nd = it.list.head
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &it.key, it.value()
|
||||
return &it.key, base.MakeInPlaceValue(it.value())
|
||||
}
|
||||
|
||||
// value returns the value at the current position.
|
||||
|
||||
38
vendor/github.com/cockroachdb/pebble/internal/base/comparer.go
generated
vendored
38
vendor/github.com/cockroachdb/pebble/internal/base/comparer.go
generated
vendored
@@ -66,6 +66,23 @@ type Separator func(dst, a, b []byte) []byte
|
||||
// key must be valid to pass to Compare.
|
||||
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.
|
||||
// ImmediateSuccessor must return a prefix key k such that:
|
||||
//
|
||||
// Split(k) == len(k) and Compare(k, a) > 0
|
||||
//
|
||||
// and there exists no representable k2 such that:
|
||||
//
|
||||
// Split(k2) == len(k2) and Compare(k2, a) > 0 and Compare(k2, k) < 0
|
||||
//
|
||||
// As an example, an implementation built on the natural byte ordering using
|
||||
// bytes.Compare could append a `\0` to `a`.
|
||||
//
|
||||
// The dst parameter may be used to store the returned key, though it is valid
|
||||
// to pass nil. The returned key must be valid to pass to Compare.
|
||||
type ImmediateSuccessor func(dst, a []byte) []byte
|
||||
|
||||
// Split returns the length of the prefix of the user key that corresponds to
|
||||
// the key portion of an MVCC encoding scheme to enable the use of prefix bloom
|
||||
// filters.
|
||||
@@ -104,14 +121,15 @@ 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
|
||||
Compare Compare
|
||||
Equal Equal
|
||||
AbbreviatedKey AbbreviatedKey
|
||||
FormatKey FormatKey
|
||||
FormatValue FormatValue
|
||||
Separator Separator
|
||||
Split Split
|
||||
Successor Successor
|
||||
ImmediateSuccessor ImmediateSuccessor
|
||||
|
||||
// Name is the name of the comparer.
|
||||
//
|
||||
@@ -193,6 +211,10 @@ var DefaultComparer = &Comparer{
|
||||
return append(dst, a...)
|
||||
},
|
||||
|
||||
ImmediateSuccessor: func(dst, a []byte) (ret []byte) {
|
||||
return append(append(dst, a...), 0x00)
|
||||
},
|
||||
|
||||
// This name is part of the C++ Level-DB implementation's default file
|
||||
// format, and should not be changed.
|
||||
Name: "leveldb.BytewiseComparator",
|
||||
|
||||
28
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
28
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
@@ -71,6 +71,10 @@ const (
|
||||
// seqNum.
|
||||
InternalKeyKindMax InternalKeyKind = 21
|
||||
|
||||
// InternalKeyZeroSeqnumMaxTrailer is the largest trailer with a
|
||||
// zero sequence number.
|
||||
InternalKeyZeroSeqnumMaxTrailer = uint64(InternalKeyKindInvalid)
|
||||
|
||||
// A marker for an invalid key.
|
||||
InternalKeyKindInvalid InternalKeyKind = 255
|
||||
|
||||
@@ -329,14 +333,28 @@ func (k InternalKey) SeqNum() uint64 {
|
||||
|
||||
// Visible returns true if the key is visible at the specified snapshot
|
||||
// sequence number.
|
||||
func (k InternalKey) Visible(snapshot uint64) bool {
|
||||
return Visible(k.SeqNum(), snapshot)
|
||||
func (k InternalKey) Visible(snapshot, batchSnapshot uint64) bool {
|
||||
return Visible(k.SeqNum(), snapshot, batchSnapshot)
|
||||
}
|
||||
|
||||
// Visible returns true if a key with the provided sequence number is visible at
|
||||
// the specified snapshot sequence number.
|
||||
func Visible(seqNum uint64, snapshot uint64) bool {
|
||||
return seqNum < snapshot || (seqNum&InternalKeySeqNumBatch) != 0
|
||||
// the specified snapshot sequence numbers.
|
||||
func Visible(seqNum uint64, snapshot, batchSnapshot uint64) bool {
|
||||
// There are two snapshot sequence numbers, one for committed keys and one
|
||||
// for batch keys. If a seqNum is less than `snapshot`, then seqNum
|
||||
// corresponds to a committed key that is visible. If seqNum has its batch
|
||||
// bit set, then seqNum corresponds to an uncommitted batch key. Its
|
||||
// visible if its snapshot is less than batchSnapshot.
|
||||
//
|
||||
// There's one complication. The maximal sequence number
|
||||
// (`InternalKeySeqNumMax`) is used across Pebble for exclusive sentinel
|
||||
// keys and other purposes. The maximal sequence number has its batch bit
|
||||
// set, but it can never be < `batchSnapshot`, since there is no expressible
|
||||
// larger snapshot. We dictate that the maximal sequence number is always
|
||||
// visible.
|
||||
return seqNum < snapshot ||
|
||||
((seqNum&InternalKeySeqNumBatch) != 0 && seqNum < batchSnapshot) ||
|
||||
seqNum == InternalKeySeqNumMax
|
||||
}
|
||||
|
||||
// SetKind sets the kind component of the key.
|
||||
|
||||
252
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
252
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
@@ -85,33 +85,19 @@ import "fmt"
|
||||
// them through the Error method. All of the absolute positioning methods
|
||||
// reset any accumulated error before positioning. Relative positioning
|
||||
// methods return without advancing if the iterator has accumulated an error.
|
||||
//
|
||||
// nilv == shorthand for LazyValue{}, which represents a nil value.
|
||||
type InternalIterator interface {
|
||||
// SeekGE moves the iterator to the first key/value pair whose key is greater
|
||||
// than or equal to the given key. Returns the key and value if the iterator
|
||||
// is pointing at a valid entry, and (nil, nil) otherwise. Note that SeekGE
|
||||
// is pointing at a valid entry, and (nil, nilv) otherwise. Note that SeekGE
|
||||
// only checks the upper bound. It is up to the caller to ensure that key
|
||||
// is greater than or equal to the lower bound.
|
||||
//
|
||||
// trySeekUsingNext is a performance optimization that callers can use to
|
||||
// indicate that they have not done any action to move this iterator beyond
|
||||
// the first key that would be found if this iterator were to honestly do
|
||||
// the intended seek. For example, say the caller did a
|
||||
// SeekGE(k1...), followed by SeekGE(k2...) where k1 <= k2, without any
|
||||
// intermediate positioning calls. The caller can safely specify true for this
|
||||
// parameter in the second call. As another example, say the caller did do one
|
||||
// call to Next between the two Seek calls, and k1 < k2. Again, the caller can
|
||||
// safely specify a true value for this parameter. Note that a false value is
|
||||
// always safe. The callee is free to ignore the true value if its
|
||||
// implementation does not permit this optimization.
|
||||
// - We make the caller do this determination since a string comparison of
|
||||
// k1, k2 is not necessarily cheap, and there may be many iterators in
|
||||
// the iterator stack. Doing it once at the root of the iterator stack
|
||||
// is cheaper.
|
||||
SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte)
|
||||
SeekGE(key []byte, flags SeekGEFlags) (*InternalKey, LazyValue)
|
||||
|
||||
// SeekPrefixGE moves the iterator to the first key/value pair whose key is
|
||||
// greater than or equal to the given key. Returns the key and value if the
|
||||
// iterator is pointing at a valid entry, and (nil, nil) otherwise. Note that
|
||||
// iterator is pointing at a valid entry, and (nil, nilv) otherwise. Note that
|
||||
// SeekPrefixGE only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound.
|
||||
//
|
||||
@@ -120,7 +106,7 @@ type InternalIterator interface {
|
||||
// function must be supplied to the Comparer for the DB. The supplied prefix
|
||||
// will be the prefix of the given key returned by that Split function. If
|
||||
// the iterator is able to determine that no key with the prefix exists, it
|
||||
// can return (nil,nil). Unlike SeekGE, this is not an indication that
|
||||
// can return (nil,nilv). Unlike SeekGE, this is not an indication that
|
||||
// iteration is exhausted.
|
||||
//
|
||||
// Note that the iterator may return keys not matching the prefix. It is up
|
||||
@@ -132,78 +118,53 @@ type InternalIterator interface {
|
||||
// not supporting reverse iteration in prefix iteration mode until a
|
||||
// different positioning routine (SeekGE, SeekLT, First or Last) switches the
|
||||
// iterator out of prefix iteration.
|
||||
//
|
||||
// trySeekUsingNext is a performance optimization that callers can use to
|
||||
// indicate that they have not done any action to move this iterator
|
||||
// beyond the first key that would be found if this iterator were to
|
||||
// honestly do the indicated seek. For example, say the caller did a
|
||||
// SeekPrefixGE(k1...), followed by SeekPrefixGE(k2...) where k1 <= k2,
|
||||
// without any intermediate positioning calls. The caller can safely
|
||||
// specify true for this parameter in the second call. As another example,
|
||||
// say the caller did do one call to Next between the two SeekPrefixGE
|
||||
// calls, and k1 < k2. Again, the caller can safely specify a true value
|
||||
// for this parameter. Note that a false value is always safe. The callee
|
||||
// is free to ignore the true value if its implementation does not permit
|
||||
// this optimization.
|
||||
// - We make the caller do this determination since a string comparison of
|
||||
// k1, k2 is not necessarily cheap, and there may be many iterators in
|
||||
// the iterator stack. Doing it once at the root of the iterator stack
|
||||
// is cheaper.
|
||||
// - This optimization could also be applied to SeekLT (where it would be
|
||||
// trySeekUsingPrev). We currently only do it for SeekPrefixGE and SeekGE
|
||||
// because this is where this optimization helps the performance of
|
||||
// CockroachDB. The SeekLT cases in CockroachDB are typically accompanied
|
||||
// with bounds that change between seek calls, and is optimized inside
|
||||
// certain iterator implementations, like singleLevelIterator, without any
|
||||
// extra parameter passing (though the same amortization of string
|
||||
// comparisons could be done to improve that optimization, by making the
|
||||
// root of the iterator stack do it).
|
||||
SeekPrefixGE(prefix, key []byte, trySeekUsingNext bool) (*InternalKey, []byte)
|
||||
SeekPrefixGE(prefix, key []byte, flags SeekGEFlags) (*InternalKey, LazyValue)
|
||||
|
||||
// SeekLT moves the iterator to the last key/value pair whose key is less
|
||||
// than the given key. Returns the key and value if the iterator is pointing
|
||||
// at a valid entry, and (nil, nil) otherwise. Note that SeekLT only checks
|
||||
// at a valid entry, and (nil, nilv) otherwise. Note that SeekLT only checks
|
||||
// the lower bound. It is up to the caller to ensure that key is less than
|
||||
// the upper bound.
|
||||
SeekLT(key []byte) (*InternalKey, []byte)
|
||||
SeekLT(key []byte, flags SeekLTFlags) (*InternalKey, LazyValue)
|
||||
|
||||
// First moves the iterator the the first key/value pair. Returns the key and
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nil)
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nilv)
|
||||
// otherwise. Note that First only checks the upper bound. It is up to the
|
||||
// caller to ensure that First() is not called when there is a lower bound,
|
||||
// and instead call SeekGE(lower).
|
||||
First() (*InternalKey, []byte)
|
||||
First() (*InternalKey, LazyValue)
|
||||
|
||||
// Last moves the iterator the the last key/value pair. Returns the key and
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nil)
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nilv)
|
||||
// otherwise. Note that Last only checks the lower bound. It is up to the
|
||||
// caller to ensure that Last() is not called when there is an upper bound,
|
||||
// and instead call SeekLT(upper).
|
||||
Last() (*InternalKey, []byte)
|
||||
Last() (*InternalKey, LazyValue)
|
||||
|
||||
// Next moves the iterator to the next key/value pair. Returns the key and
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nil)
|
||||
// value if the iterator is pointing at a valid entry, and (nil, nilv)
|
||||
// otherwise. Note that Next only checks the upper bound. It is up to the
|
||||
// caller to ensure that key is greater than or equal to the lower bound.
|
||||
//
|
||||
// It is valid to call Next when the iterator is positioned before the first
|
||||
// key/value pair due to either a prior call to SeekLT or Prev which returned
|
||||
// (nil, nil). It is not allowed to call Next when the previous call to SeekGE,
|
||||
// SeekPrefixGE or Next returned (nil, nil).
|
||||
Next() (*InternalKey, []byte)
|
||||
// (nil, nilv). It is not allowed to call Next when the previous call to SeekGE,
|
||||
// SeekPrefixGE or Next returned (nil, nilv).
|
||||
Next() (*InternalKey, LazyValue)
|
||||
|
||||
// Prev moves the iterator to the previous key/value pair. Returns the key
|
||||
// and value if the iterator is pointing at a valid entry, and (nil, nil)
|
||||
// and value if the iterator is pointing at a valid entry, and (nil, nilv)
|
||||
// otherwise. Note that Prev only checks the lower bound. It is up to the
|
||||
// caller to ensure that key is less than the upper bound.
|
||||
//
|
||||
// It is valid to call Prev when the iterator is positioned after the last
|
||||
// key/value pair due to either a prior call to SeekGE or Next which returned
|
||||
// (nil, nil). It is not allowed to call Prev when the previous call to SeekLT
|
||||
// or Prev returned (nil, nil).
|
||||
Prev() (*InternalKey, []byte)
|
||||
// (nil, nilv). It is not allowed to call Prev when the previous call to SeekLT
|
||||
// or Prev returned (nil, nilv).
|
||||
Prev() (*InternalKey, LazyValue)
|
||||
|
||||
// Error returns any accumulated error.
|
||||
// Error returns any accumulated error. It may not include errors returned
|
||||
// to the client when calling LazyValue.Value().
|
||||
Error() error
|
||||
|
||||
// Close closes the iterator and returns any accumulated error. Exhausting
|
||||
@@ -225,11 +186,137 @@ type InternalIterator interface {
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// InternalIteratorWithStats extends InternalIterator to expose stats.
|
||||
type InternalIteratorWithStats interface {
|
||||
InternalIterator
|
||||
Stats() InternalIteratorStats
|
||||
ResetStats()
|
||||
// SeekGEFlags holds flags that may configure the behavior of a forward seek.
|
||||
// Not all flags are relevant to all iterators.
|
||||
type SeekGEFlags uint8
|
||||
|
||||
const (
|
||||
seekGEFlagTrySeekUsingNext uint8 = iota
|
||||
seekGEFlagRelativeSeek
|
||||
seekGEFlagBatchJustRefreshed
|
||||
)
|
||||
|
||||
// SeekGEFlagsNone is the default value of SeekGEFlags, with all flags disabled.
|
||||
const SeekGEFlagsNone = SeekGEFlags(0)
|
||||
|
||||
// TrySeekUsingNext indicates whether a performance optimization was enabled
|
||||
// by a caller, indicating the caller has not done any action to move this
|
||||
// iterator beyond the first key that would be found if this iterator were to
|
||||
// honestly do the intended seek. For example, say the caller did a
|
||||
// SeekGE(k1...), followed by SeekGE(k2...) where k1 <= k2, without any
|
||||
// intermediate positioning calls. The caller can safely specify true for this
|
||||
// parameter in the second call. As another example, say the caller did do one
|
||||
// call to Next between the two Seek calls, and k1 < k2. Again, the caller can
|
||||
// safely specify a true value for this parameter. Note that a false value is
|
||||
// always safe. The callee is free to ignore the true value if its
|
||||
// implementation does not permit this optimization.
|
||||
//
|
||||
// We make the caller do this determination since a string comparison of k1, k2
|
||||
// is not necessarily cheap, and there may be many iterators in the iterator
|
||||
// stack. Doing it once at the root of the iterator stack is cheaper.
|
||||
//
|
||||
// This optimization could also be applied to SeekLT (where it would be
|
||||
// trySeekUsingPrev). We currently only do it for SeekPrefixGE and SeekGE
|
||||
// because this is where this optimization helps the performance of CockroachDB.
|
||||
// The SeekLT cases in CockroachDB are typically accompanied with bounds that
|
||||
// change between seek calls, and is optimized inside certain iterator
|
||||
// implementations, like singleLevelIterator, without any extra parameter
|
||||
// passing (though the same amortization of string comparisons could be done to
|
||||
// improve that optimization, by making the root of the iterator stack do it).
|
||||
func (s SeekGEFlags) TrySeekUsingNext() bool { return (s & (1 << seekGEFlagTrySeekUsingNext)) != 0 }
|
||||
|
||||
// RelativeSeek is set when in the course of a forward positioning operation, a
|
||||
// higher-level iterator seeks a lower-level iterator to a larger key than the
|
||||
// one at the current iterator position.
|
||||
//
|
||||
// Concretely, this occurs when the merging iterator observes a range deletion
|
||||
// covering the key at a level's current position, and the merging iterator
|
||||
// seeks the level to the range deletion's end key. During lazy-combined
|
||||
// iteration, this flag signals to the level iterator that the seek is NOT an
|
||||
// absolute-positioning operation from the perspective of the pebble.Iterator,
|
||||
// and the level iterator must look for range keys in tables between the current
|
||||
// iterator position and the new seeked position.
|
||||
func (s SeekGEFlags) RelativeSeek() bool { return (s & (1 << seekGEFlagRelativeSeek)) != 0 }
|
||||
|
||||
// BatchJustRefreshed is set by Seek[Prefix]GE when an iterator's view of an
|
||||
// indexed batch was just refreshed. It serves as a signal to the batch iterator
|
||||
// to ignore the TrySeekUsingNext optimization, because the external knowledge
|
||||
// imparted by the TrySeekUsingNext flag does not apply to the batch iterator's
|
||||
// position. See (pebble.Iterator).batchJustRefreshed.
|
||||
func (s SeekGEFlags) BatchJustRefreshed() bool { return (s & (1 << seekGEFlagBatchJustRefreshed)) != 0 }
|
||||
|
||||
// EnableTrySeekUsingNext returns the provided flags with the
|
||||
// try-seek-using-next optimization enabled. See TrySeekUsingNext for an
|
||||
// explanation of this optimization.
|
||||
func (s SeekGEFlags) EnableTrySeekUsingNext() SeekGEFlags {
|
||||
return s | (1 << seekGEFlagTrySeekUsingNext)
|
||||
}
|
||||
|
||||
// DisableTrySeekUsingNext returns the provided flags with the
|
||||
// try-seek-using-next optimization disabled.
|
||||
func (s SeekGEFlags) DisableTrySeekUsingNext() SeekGEFlags {
|
||||
return s &^ (1 << seekGEFlagTrySeekUsingNext)
|
||||
}
|
||||
|
||||
// EnableRelativeSeek returns the provided flags with the relative-seek flag
|
||||
// enabled. See RelativeSeek for an explanation of this flag's use.
|
||||
func (s SeekGEFlags) EnableRelativeSeek() SeekGEFlags {
|
||||
return s | (1 << seekGEFlagRelativeSeek)
|
||||
}
|
||||
|
||||
// DisableRelativeSeek returns the provided flags with the relative-seek flag
|
||||
// disabled.
|
||||
func (s SeekGEFlags) DisableRelativeSeek() SeekGEFlags {
|
||||
return s &^ (1 << seekGEFlagRelativeSeek)
|
||||
}
|
||||
|
||||
// EnableBatchJustRefreshed returns the provided flags with the
|
||||
// batch-just-refreshed bit set. See BatchJustRefreshed for an explanation of
|
||||
// this flag.
|
||||
func (s SeekGEFlags) EnableBatchJustRefreshed() SeekGEFlags {
|
||||
return s | (1 << seekGEFlagBatchJustRefreshed)
|
||||
}
|
||||
|
||||
// DisableBatchJustRefreshed returns the provided flags with the
|
||||
// batch-just-refreshed bit unset.
|
||||
func (s SeekGEFlags) DisableBatchJustRefreshed() SeekGEFlags {
|
||||
return s &^ (1 << seekGEFlagBatchJustRefreshed)
|
||||
}
|
||||
|
||||
// SeekLTFlags holds flags that may configure the behavior of a reverse seek.
|
||||
// Not all flags are relevant to all iterators.
|
||||
type SeekLTFlags uint8
|
||||
|
||||
const (
|
||||
seekLTFlagRelativeSeek uint8 = iota
|
||||
)
|
||||
|
||||
// SeekLTFlagsNone is the default value of SeekLTFlags, with all flags disabled.
|
||||
const SeekLTFlagsNone = SeekLTFlags(0)
|
||||
|
||||
// RelativeSeek is set when in the course of a reverse positioning operation, a
|
||||
// higher-level iterator seeks a lower-level iterator to a smaller key than the
|
||||
// one at the current iterator position.
|
||||
//
|
||||
// Concretely, this occurs when the merging iterator observes a range deletion
|
||||
// covering the key at a level's current position, and the merging iterator
|
||||
// seeks the level to the range deletion's start key. During lazy-combined
|
||||
// iteration, this flag signals to the level iterator that the seek is NOT an
|
||||
// absolute-positioning operation from the perspective of the pebble.Iterator,
|
||||
// and the level iterator must look for range keys in tables between the current
|
||||
// iterator position and the new seeked position.
|
||||
func (s SeekLTFlags) RelativeSeek() bool { return s&(1<<seekLTFlagRelativeSeek) != 0 }
|
||||
|
||||
// EnableRelativeSeek returns the provided flags with the relative-seek flag
|
||||
// enabled. See RelativeSeek for an explanation of this flag's use.
|
||||
func (s SeekLTFlags) EnableRelativeSeek() SeekLTFlags {
|
||||
return s | (1 << seekLTFlagRelativeSeek)
|
||||
}
|
||||
|
||||
// DisableRelativeSeek returns the provided flags with the relative-seek flag
|
||||
// disabled.
|
||||
func (s SeekLTFlags) DisableRelativeSeek() SeekLTFlags {
|
||||
return s &^ (1 << seekLTFlagRelativeSeek)
|
||||
}
|
||||
|
||||
// InternalIteratorStats contains miscellaneous stats produced by
|
||||
@@ -238,8 +325,8 @@ type InternalIteratorWithStats interface {
|
||||
// are aggregated as one goes up the InternalIterator tree.
|
||||
type InternalIteratorStats struct {
|
||||
// Bytes in the loaded blocks. If the block was compressed, this is the
|
||||
// compressed bytes. Currently, only the second-level index and data blocks
|
||||
// containing points are included.
|
||||
// compressed bytes. Currently, only the index blocks, data blocks
|
||||
// containing points, and filter blocks are included.
|
||||
BlockBytes uint64
|
||||
// Subset of BlockBytes that were in the block cache.
|
||||
BlockBytesInCache uint64
|
||||
@@ -271,30 +358,3 @@ func (s *InternalIteratorStats) Merge(from InternalIteratorStats) {
|
||||
s.PointCount += from.PointCount
|
||||
s.PointsCoveredByRangeTombstones += from.PointsCoveredByRangeTombstones
|
||||
}
|
||||
|
||||
type internalIteratorWithEmptyStats struct {
|
||||
InternalIterator
|
||||
}
|
||||
|
||||
var _ InternalIteratorWithStats = internalIteratorWithEmptyStats{}
|
||||
|
||||
// Stats implements InternalIteratorWithStats.
|
||||
func (i internalIteratorWithEmptyStats) Stats() InternalIteratorStats {
|
||||
return InternalIteratorStats{}
|
||||
}
|
||||
|
||||
// ResetStats implements InternalIteratorWithStats.
|
||||
func (i internalIteratorWithEmptyStats) ResetStats() {}
|
||||
|
||||
// WrapIterWithStats ensures that either iter implements the stats methods or
|
||||
// wraps it, such that the return value implements InternalIteratorWithStats.
|
||||
func WrapIterWithStats(iter InternalIterator) InternalIteratorWithStats {
|
||||
if iter == nil {
|
||||
return nil
|
||||
}
|
||||
i, ok := iter.(InternalIteratorWithStats)
|
||||
if ok {
|
||||
return i
|
||||
}
|
||||
return internalIteratorWithEmptyStats{InternalIterator: iter}
|
||||
}
|
||||
|
||||
256
vendor/github.com/cockroachdb/pebble/internal/base/lazy_value.go
generated
vendored
Normal file
256
vendor/github.com/cockroachdb/pebble/internal/base/lazy_value.go
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2022 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 "github.com/cockroachdb/pebble/internal/invariants"
|
||||
|
||||
// A value can have user-defined attributes that are a function of the value
|
||||
// byte slice. For now, we only support "short attributes", which can be
|
||||
// encoded in 3 bits. We will likely extend this to "long attributes" later
|
||||
// for values that are even more expensive to access than those in value
|
||||
// blocks in the same sstable.
|
||||
//
|
||||
// When a sstable writer chooses not to store a value together with the key,
|
||||
// it can call the ShortAttributeExtractor to extract the attribute and store
|
||||
// it together with the key. This allows for cheap retrieval of
|
||||
// AttributeAndLen on the read-path, without doing a more expensive retrieval
|
||||
// of the value.
|
||||
//
|
||||
// In general, the extraction code may want to also look at the key to decide
|
||||
// how to treat the value. Our current needs can be satisfied by configuring a
|
||||
// keyspan for which the ShortAttributeExtractor should be used, so we do not
|
||||
// expose the key to the extractor.
|
||||
//
|
||||
// Write path performance: The ShortAttributeExtractor func cannot be inlined,
|
||||
// so we will pay the cost of this function call. However, we will only pay
|
||||
// this when (a) the value is not being stored together with the key, and (b)
|
||||
// the key-value pair is being initially written to the DB, or a compaction is
|
||||
// transitioning the key-value pair from being stored together to being stored
|
||||
// separately.
|
||||
|
||||
// ShortAttribute encodes a user-specified attribute of the value.
|
||||
type ShortAttribute uint8
|
||||
|
||||
// MaxShortAttribute is the maximum value of the short attribute (3 bits).
|
||||
const MaxShortAttribute = 7
|
||||
|
||||
// ShortAttributeExtractor is an extractor that given the value, will return
|
||||
// the ShortAttribute.
|
||||
type ShortAttributeExtractor func(value []byte) (ShortAttribute, error)
|
||||
|
||||
// AttributeAndLen represents the pair of value length and the short
|
||||
// attribute.
|
||||
type AttributeAndLen struct {
|
||||
ValueLen int32
|
||||
ShortAttribute ShortAttribute
|
||||
}
|
||||
|
||||
// LazyValue represents a value that may not already have been extracted.
|
||||
// Currently, it can represent either an in-place value (stored with the key)
|
||||
// or a value stored in the value section. However, the interface is general
|
||||
// enough to support values that are stored in separate files.
|
||||
//
|
||||
// LazyValue is used in the InternalIterator interface, such that all
|
||||
// positioning calls return (*InternalKey, LazyValue). It is also exposed via
|
||||
// the public Iterator for callers that need to remember a recent but not
|
||||
// necessarily latest LazyValue, in case they need the actual value in the
|
||||
// future. An example is a caller that is iterating in reverse and looking for
|
||||
// the latest MVCC version for a key -- it cannot identify the latest MVCC
|
||||
// version without stepping to the previous key-value pair e.g.
|
||||
// storage.pebbleMVCCScanner in CockroachDB.
|
||||
//
|
||||
// Performance note: It is important for this struct to not exceed a sizeof 32
|
||||
// bytes, for optimizing the common case of the in-place value. Prior to
|
||||
// introducing LazyValue, we were passing around a []byte which is 24 bytes.
|
||||
// Passing a 40 byte or larger struct causes performance to drop by 75% on
|
||||
// some benchmarks that do tight iteration loops.
|
||||
//
|
||||
// Memory management:
|
||||
// This is subtle, but important for performance.
|
||||
//
|
||||
// - A LazyValue returned by an InternalIterator or Iterator is unstable in
|
||||
// that repositioning the iterator will invalidate the memory inside it. A
|
||||
// caller wishing to maintain that LazyValue needs to call
|
||||
// LazyValue.Clone(). Note that this does not fetch the value if it is not
|
||||
// in-place. Additionally, Clone() *must* only be called if
|
||||
// LazyValue.Value() has *not* been called, since (a) it only makes a
|
||||
// promise about stability of LazyValue and not the underlying value, (b) if
|
||||
// LazyValue.Value() has been called already, the caller has the value
|
||||
// []byte slice so it is unnecessary to fiddle around with LazyValue.
|
||||
//
|
||||
// - A user of an iterator that calls LazyValue.Value() wants as much as
|
||||
// possible for the returned value []byte to point to iterator owned memory.
|
||||
// - [P1] The underlying iterator that owns that memory also needs a promise
|
||||
// from that user that at any time there is at most one value []byte slice
|
||||
// that the caller is expecting it to maintain. Otherwise, the underlying
|
||||
// iterator has to maintain multiple such []byte slices which results in
|
||||
// more complicated and inefficient code.
|
||||
// - [P2] The underlying iterator, in order to make the promise that it is
|
||||
// maintaining the one value []byte slice, also needs a way to know when
|
||||
// it is relieved of that promise. One way it is relieved of that promise
|
||||
// is by being told that it is being repositioned. Typically, the owner of
|
||||
// the value []byte slice is a sstable iterator, and it will know that it
|
||||
// is relieved of the promise when it is repositioned. However, consider
|
||||
// the case where the caller has used LazyValue.Clone() and repositioned
|
||||
// the iterator (which is actually a tree of iterators). In this case the
|
||||
// underlying sstable iterator may not even be open. LazyValue.Value()
|
||||
// will still work (at a higher cost), but since the sstable iterator is
|
||||
// not open, it does not have a mechanism to know when the retrieved value
|
||||
// is no longer in use. We refer to this situation as "not satisfying P2".
|
||||
// To handle this situation, the LazyValue.Value() method accepts a caller
|
||||
// owned buffer, that the callee will use if needed. The callee explicitly
|
||||
// tells the caller whether the []byte slice for the value is now owned by
|
||||
// the caller. This will be true if the callee attempted to use buf and
|
||||
// either successfully used it or allocated a new []byte slice.
|
||||
//
|
||||
// To ground the above in reality, we consider three examples of callers of
|
||||
// LazyValue.Value():
|
||||
// - Iterator: it calls LazyValue.Value for its own use when merging values.
|
||||
// When merging during reverse iteration, it may have cloned the LazyValue.
|
||||
// In this case it calls LazyValue.Value() on the cloned value, merges it,
|
||||
// and then calls LazyValue.Value() on the current iterator position and
|
||||
// merges it. So it is honoring P1.
|
||||
// - Iterator on behalf of Iterator clients: The Iterator.Value() method
|
||||
// needs to call LazyValue.Value(). The client of Iterator is satisfying P1
|
||||
// because of the inherent Iterator interface constraint, i.e., it is calling
|
||||
// Iterator.Value() on the current Iterator position. It is possible that
|
||||
// the Iterator has cloned this LazyValue (for the reverse iteration case),
|
||||
// which the client is unaware of, so the underlying sstable iterator may
|
||||
// not be able to satisfy P2. This is ok because Iterator will call
|
||||
// LazyValue.Value with its (reusable) owned buffer.
|
||||
// - CockroachDB's pebbleMVCCScanner: This will use LazyValues from Iterator
|
||||
// since during reverse iteration in order to find the highest version that
|
||||
// satisfies a read it needs to clone the LazyValue, step back the iterator
|
||||
// and then decide whether it needs the value from the previously cloned
|
||||
// LazyValue. The pebbleMVCCScanner will satisfy P1. The P2 story is
|
||||
// similar to the previous case in that it will call LazyValue.Value with
|
||||
// its (reusable) owned buffer.
|
||||
//
|
||||
// Corollary: callers that directly use InternalIterator can know that they
|
||||
// have done nothing to interfere with promise P2 can pass in a nil buf and be
|
||||
// sure that it will not trigger an allocation.
|
||||
//
|
||||
// Repeated calling of LazyValue.Value:
|
||||
// This is ok as long as the caller continues to satisfy P1. The previously
|
||||
// fetched value will be remembered inside LazyValue to avoid fetching again.
|
||||
// So if the caller's buffer is used the first time the value was fetched, it
|
||||
// is still in use.
|
||||
//
|
||||
// LazyValue fields are visible outside the package for use in
|
||||
// InternalIterator implementations and in Iterator, but not meant for direct
|
||||
// use by users of Pebble.
|
||||
type LazyValue struct {
|
||||
// ValueOrHandle represents a value, or a handle to be passed to ValueFetcher.
|
||||
// - Fetcher == nil: ValueOrHandle is a value.
|
||||
// - Fetcher != nil: ValueOrHandle is a handle and Fetcher.Attribute is
|
||||
// initialized.
|
||||
// The ValueOrHandle exposed by InternalIterator or Iterator may not be stable
|
||||
// if the iterator is stepped. To make it stable, make a copy using Clone.
|
||||
ValueOrHandle []byte
|
||||
// Fetcher provides support for fetching an actually lazy value.
|
||||
Fetcher *LazyFetcher
|
||||
}
|
||||
|
||||
// LazyFetcher supports fetching a lazy value.
|
||||
type LazyFetcher struct {
|
||||
// ValueFetcher, given a handle, returns the value. It is acceptable to call
|
||||
// the ValueFetcher as long as the DB is open. However, one should assume
|
||||
// there is a fast-path when the iterator tree has not moved off the sstable
|
||||
// iterator that initially provided this LazyValue. Hence, to utilize this
|
||||
// fast-path the caller should try to decide whether it needs the value or
|
||||
// not as soon as possible, with minimal possible stepping of the iterator.
|
||||
//
|
||||
// buf will be used if the fetcher cannot satisfy P2 (see earlier comment).
|
||||
// If the fetcher attempted to use buf *and* len(buf) was insufficient, it
|
||||
// will allocate a new slice for the value. In either case it will set
|
||||
// callerOwned to true.
|
||||
ValueFetcher func(handle []byte, buf []byte) (val []byte, callerOwned bool, err error)
|
||||
// Attribute includes the short attribute and value length.
|
||||
Attribute AttributeAndLen
|
||||
fetched bool
|
||||
value []byte
|
||||
callerOwned bool
|
||||
err error
|
||||
}
|
||||
|
||||
// Value returns the underlying value.
|
||||
func (lv *LazyValue) Value(buf []byte) (val []byte, callerOwned bool, err error) {
|
||||
if lv.Fetcher == nil {
|
||||
return lv.ValueOrHandle, false, nil
|
||||
}
|
||||
// Do the rest of the work in a separate method to attempt mid-stack
|
||||
// inlining of Value(). Unfortunately, this still does not inline since the
|
||||
// cost of 85 exceeds the budget of 80.
|
||||
//
|
||||
// TODO(sumeer): Packing the return values into a struct{[]byte error bool}
|
||||
// causes it to be below the budget. Consider this if we need to recover
|
||||
// more performance. I suspect that inlining this only matters in
|
||||
// micro-benchmarks, and in actual use cases in CockroachDB it will not
|
||||
// matter because there is substantial work done with a fetched value.
|
||||
return lv.fetchValue(buf)
|
||||
}
|
||||
|
||||
// INVARIANT: lv.Fetcher != nil
|
||||
func (lv *LazyValue) fetchValue(buf []byte) (val []byte, callerOwned bool, err error) {
|
||||
f := lv.Fetcher
|
||||
if !f.fetched {
|
||||
f.fetched = true
|
||||
f.value, f.callerOwned, f.err = lv.Fetcher.ValueFetcher(lv.ValueOrHandle, buf)
|
||||
}
|
||||
return f.value, f.callerOwned, f.err
|
||||
}
|
||||
|
||||
// InPlaceValue returns the value under the assumption that it is in-place.
|
||||
// This is for Pebble-internal code.
|
||||
func (lv *LazyValue) InPlaceValue() []byte {
|
||||
if invariants.Enabled && lv.Fetcher != nil {
|
||||
panic("value must be in-place")
|
||||
}
|
||||
return lv.ValueOrHandle
|
||||
}
|
||||
|
||||
// Len returns the length of the value.
|
||||
func (lv *LazyValue) Len() int {
|
||||
if lv.Fetcher == nil {
|
||||
return len(lv.ValueOrHandle)
|
||||
}
|
||||
return int(lv.Fetcher.Attribute.ValueLen)
|
||||
}
|
||||
|
||||
// Clone creates a stable copy of the LazyValue, by appending bytes to buf.
|
||||
// REQUIRES: LazyValue.Value() has not been called.
|
||||
// Ensure that this is only called before LazyValue.Value is called. We don't
|
||||
// actually care if it was in-place but we don't want confusing things about whether
|
||||
// we need to clone the value inside the fetcher.
|
||||
func (lv *LazyValue) Clone(buf []byte) (LazyValue, []byte) {
|
||||
if invariants.Enabled && lv.Fetcher != nil && lv.Fetcher.fetched {
|
||||
panic("value has already been fetched")
|
||||
}
|
||||
// INVARIANT: LazyFetcher.value is nil, so there is nothing to copy there.
|
||||
vLen := len(lv.ValueOrHandle)
|
||||
lvCopy := LazyValue{Fetcher: lv.Fetcher}
|
||||
if vLen == 0 {
|
||||
return lvCopy, buf
|
||||
}
|
||||
bufLen := len(buf)
|
||||
buf = append(buf, lv.ValueOrHandle...)
|
||||
lvCopy.ValueOrHandle = buf[bufLen : bufLen+vLen]
|
||||
return lvCopy, buf
|
||||
}
|
||||
|
||||
// MakeInPlaceValue constructs an in-place value.
|
||||
func MakeInPlaceValue(val []byte) LazyValue {
|
||||
return LazyValue{ValueOrHandle: val}
|
||||
}
|
||||
|
||||
// TODO(sumeer): test that callers are adhering to promise P1. In the sstable
|
||||
// iterators, when invariants are enabled, create a testingFetcher struct that
|
||||
// contains the sstable name and the []byte backing for the last fetched
|
||||
// value. The sstable iterator, instead of returning an in-place value, will
|
||||
// return a copy of the key in ValueOrHandle, and a ValueFetcher implemented
|
||||
// by testingFetcher.valueFetcher. Each time this method is called, it will
|
||||
// mangle the last fetched value []byte slice, allocate a new one, open the
|
||||
// sstable, seek to the key, and copy the value into this new byte slice and
|
||||
// return.
|
||||
13
vendor/github.com/cockroachdb/pebble/internal/base/metrics.go
generated
vendored
13
vendor/github.com/cockroachdb/pebble/internal/base/metrics.go
generated
vendored
@@ -29,6 +29,13 @@ func (tm *ThroughputMetric) Merge(x ThroughputMetric) {
|
||||
tm.IdleDuration += x.IdleDuration
|
||||
}
|
||||
|
||||
// Subtract subtracts the information from another ThroughputMetric
|
||||
func (tm *ThroughputMetric) Subtract(x ThroughputMetric) {
|
||||
tm.Bytes -= x.Bytes
|
||||
tm.WorkDuration -= x.WorkDuration
|
||||
tm.IdleDuration -= x.IdleDuration
|
||||
}
|
||||
|
||||
// PeakRate returns the approximate peak rate if there was no idling.
|
||||
func (tm *ThroughputMetric) PeakRate() int64 {
|
||||
if tm.Bytes == 0 {
|
||||
@@ -67,6 +74,12 @@ func (gsm *GaugeSampleMetric) Merge(x GaugeSampleMetric) {
|
||||
gsm.count += x.count
|
||||
}
|
||||
|
||||
// Subtract subtracts the information from another gauge metric.
|
||||
func (gsm *GaugeSampleMetric) Subtract(x GaugeSampleMetric) {
|
||||
gsm.sampleSum -= x.sampleSum
|
||||
gsm.count -= x.count
|
||||
}
|
||||
|
||||
// Mean returns the mean value.
|
||||
func (gsm *GaugeSampleMetric) Mean() float64 {
|
||||
if gsm.count == 0 {
|
||||
|
||||
26
vendor/github.com/cockroachdb/pebble/internal/batchskl/iterator.go
generated
vendored
26
vendor/github.com/cockroachdb/pebble/internal/batchskl/iterator.go
generated
vendored
@@ -47,7 +47,31 @@ func (it *Iterator) Close() error {
|
||||
// entry and false otherwise. Note that SeekGE only checks the upper bound. It
|
||||
// is up to the caller to ensure that key is greater than or equal to the lower
|
||||
// bound.
|
||||
func (it *Iterator) SeekGE(key []byte) *base.InternalKey {
|
||||
func (it *Iterator) SeekGE(key []byte, flags base.SeekGEFlags) *base.InternalKey {
|
||||
if flags.TrySeekUsingNext() {
|
||||
if it.nd == it.list.tail {
|
||||
// Iterator is done.
|
||||
return nil
|
||||
}
|
||||
less := it.list.cmp(it.key.UserKey, key) < 0
|
||||
// Arbitrary constant. By measuring the seek cost as a function of the
|
||||
// number of elements in the skip list, and fitting to a model, we
|
||||
// could adjust the number of nexts based on the current size of the
|
||||
// skip list.
|
||||
const numNexts = 5
|
||||
for i := 0; less && i < numNexts; i++ {
|
||||
k := it.Next()
|
||||
if k == nil {
|
||||
// Iterator is done.
|
||||
return nil
|
||||
}
|
||||
less = it.list.cmp(k.UserKey, key) < 0
|
||||
}
|
||||
if !less {
|
||||
return &it.key
|
||||
}
|
||||
}
|
||||
|
||||
_, it.nd = it.seekForBaseSplice(key, it.list.abbreviatedKey(key))
|
||||
if it.nd == it.list.tail {
|
||||
return nil
|
||||
|
||||
265
vendor/github.com/cockroachdb/pebble/internal/keyspan/bounded.go
generated
vendored
Normal file
265
vendor/github.com/cockroachdb/pebble/internal/keyspan/bounded.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright 2022 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 keyspan
|
||||
|
||||
import "github.com/cockroachdb/pebble/internal/base"
|
||||
|
||||
// TODO(jackson): Consider removing this type and adding bounds enforcement
|
||||
// directly to the MergingIter. This type is probably too lightweight to warrant
|
||||
// its own type, but for now we implement it separately for expediency.
|
||||
|
||||
// boundedIterPos records the position of the BoundedIter relative to the
|
||||
// underlying iterator's position. It's used to avoid Next/Prev-ing the iterator
|
||||
// if there can't possibly be another span within bounds, because the current
|
||||
// span overlaps the bound.
|
||||
//
|
||||
// Imagine bounds [a,c) and an iterator that seeks to a span [b,d). The span
|
||||
// [b,d) overlaps some portion of the iterator bounds, so the iterator must
|
||||
// return it. If the iterator is subsequently Nexted, Next can tell that the
|
||||
// iterator is exhausted without advancing the underlying iterator because the
|
||||
// current span's end bound of d is ≥ the upper bound of c. In this case, the
|
||||
// bounded iterator returns nil and records i.pos as posAtUpperLimit to remember
|
||||
// that the underlying iterator position does not match the current BoundedIter
|
||||
// position.
|
||||
type boundedIterPos int8
|
||||
|
||||
const (
|
||||
posAtLowerLimit boundedIterPos = -1
|
||||
posAtIterSpan boundedIterPos = 0
|
||||
posAtUpperLimit boundedIterPos = +1
|
||||
)
|
||||
|
||||
// BoundedIter implements FragmentIterator and enforces bounds.
|
||||
//
|
||||
// Like the point InternalIterator interface, the bounded iterator's forward
|
||||
// positioning routines (SeekGE, First, and Next) only check the upper bound.
|
||||
// The reverse positioning routines (SeekLT, Last, and Prev) only check the
|
||||
// lower bound. It is up to the caller to ensure that the forward positioning
|
||||
// routines respect the lower bound and the reverse positioning routines respect
|
||||
// the upper bound (i.e. calling SeekGE instead of First if there is a lower
|
||||
// bound, and SeekLT instead of Last if there is an upper bound).
|
||||
//
|
||||
// When the hasPrefix parameter indicates that the iterator is in prefix
|
||||
// iteration mode, BoundedIter elides any spans that do not overlap with the
|
||||
// prefix's keyspace. In prefix iteration mode, reverse iteration is disallowed,
|
||||
// except for an initial SeekLT with a seek key greater than or equal to the
|
||||
// prefix. In prefix iteration mode, the first seek must position the iterator
|
||||
// at or immediately before the first fragment covering a key greater than or
|
||||
// equal to the prefix.
|
||||
type BoundedIter struct {
|
||||
iter FragmentIterator
|
||||
iterSpan *Span
|
||||
cmp base.Compare
|
||||
split base.Split
|
||||
lower []byte
|
||||
upper []byte
|
||||
hasPrefix *bool
|
||||
prefix *[]byte
|
||||
pos boundedIterPos
|
||||
}
|
||||
|
||||
// Init initializes the bounded iterator.
|
||||
//
|
||||
// In addition to the iterator bounds, Init takes pointers to a boolean
|
||||
// indicating whether the iterator is in prefix iteration mode and the prefix
|
||||
// key if it is. This is used to exclude spans that are outside the iteration
|
||||
// prefix.
|
||||
func (i *BoundedIter) Init(
|
||||
cmp base.Compare,
|
||||
split base.Split,
|
||||
iter FragmentIterator,
|
||||
lower, upper []byte,
|
||||
hasPrefix *bool,
|
||||
prefix *[]byte,
|
||||
) {
|
||||
*i = BoundedIter{
|
||||
iter: iter,
|
||||
cmp: cmp,
|
||||
split: split,
|
||||
lower: lower,
|
||||
upper: upper,
|
||||
hasPrefix: hasPrefix,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
var _ FragmentIterator = (*BoundedIter)(nil)
|
||||
|
||||
// Seek calls.
|
||||
//
|
||||
// Seek calls check iterator bounds in the direction of the seek. Additionally,
|
||||
// if the iterator is in prefix iteration mode, seek calls check both start and
|
||||
// end bounds against the prefix's bounds. We check both bounds for defense in
|
||||
// depth. This optimization has been a source of various bugs due to various
|
||||
// other prefix iteration optimizations that can result in seek keys that don't
|
||||
// respect the prefix bounds.
|
||||
|
||||
// SeekGE implements FragmentIterator.
|
||||
func (i *BoundedIter) SeekGE(key []byte) *Span {
|
||||
s := i.iter.SeekGE(key)
|
||||
s = i.checkPrefixSpanStart(s)
|
||||
s = i.checkPrefixSpanEnd(s)
|
||||
return i.checkForwardBound(s)
|
||||
}
|
||||
|
||||
// SeekLT implements FragmentIterator.
|
||||
func (i *BoundedIter) SeekLT(key []byte) *Span {
|
||||
s := i.iter.SeekLT(key)
|
||||
s = i.checkPrefixSpanStart(s)
|
||||
s = i.checkPrefixSpanEnd(s)
|
||||
return i.checkBackwardBound(s)
|
||||
}
|
||||
|
||||
// First implements FragmentIterator.
|
||||
func (i *BoundedIter) First() *Span {
|
||||
s := i.iter.First()
|
||||
s = i.checkPrefixSpanStart(s)
|
||||
return i.checkForwardBound(s)
|
||||
}
|
||||
|
||||
// Last implements FragmentIterator.
|
||||
func (i *BoundedIter) Last() *Span {
|
||||
s := i.iter.Last()
|
||||
s = i.checkPrefixSpanEnd(s)
|
||||
return i.checkBackwardBound(s)
|
||||
}
|
||||
|
||||
// Next implements FragmentIterator.
|
||||
func (i *BoundedIter) Next() *Span {
|
||||
switch i.pos {
|
||||
case posAtLowerLimit:
|
||||
// The BoundedIter had previously returned nil, because it knew from
|
||||
// i.iterSpan's bounds that there was no previous span. To Next, we only
|
||||
// need to return the current iter span and reset i.pos to reflect that
|
||||
// we're no longer positioned at the limit.
|
||||
i.pos = posAtIterSpan
|
||||
return i.iterSpan
|
||||
case posAtIterSpan:
|
||||
// If the span at the underlying iterator position extends to or beyond the
|
||||
// upper bound, we can avoid advancing because the next span is necessarily
|
||||
// out of bounds.
|
||||
if i.iterSpan != nil && i.upper != nil && i.cmp(i.iterSpan.End, i.upper) >= 0 {
|
||||
i.pos = posAtUpperLimit
|
||||
return nil
|
||||
}
|
||||
// Similarly, if the span extends to the next prefix and we're in prefix
|
||||
// iteration mode, we can avoid advancing.
|
||||
if i.iterSpan != nil && *i.hasPrefix {
|
||||
ei := i.split(i.iterSpan.End)
|
||||
if i.cmp(i.iterSpan.End[:ei], *i.prefix) > 0 {
|
||||
i.pos = posAtUpperLimit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return i.checkForwardBound(i.checkPrefixSpanStart(i.iter.Next()))
|
||||
case posAtUpperLimit:
|
||||
// Already exhausted.
|
||||
return nil
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// Prev implements FragmentIterator.
|
||||
func (i *BoundedIter) Prev() *Span {
|
||||
switch i.pos {
|
||||
case posAtLowerLimit:
|
||||
// Already exhausted.
|
||||
return nil
|
||||
case posAtIterSpan:
|
||||
// If the span at the underlying iterator position extends to or beyond
|
||||
// the lower bound, we can avoid advancing because the previous span is
|
||||
// necessarily out of bounds.
|
||||
if i.iterSpan != nil && i.lower != nil && i.cmp(i.iterSpan.Start, i.lower) <= 0 {
|
||||
i.pos = posAtLowerLimit
|
||||
return nil
|
||||
}
|
||||
// Similarly, if the span extends to or beyond the current prefix and
|
||||
// we're in prefix iteration mode, we can avoid advancing.
|
||||
if i.iterSpan != nil && *i.hasPrefix {
|
||||
si := i.split(i.iterSpan.Start)
|
||||
if i.cmp(i.iterSpan.Start[:si], *i.prefix) < 0 {
|
||||
i.pos = posAtLowerLimit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return i.checkBackwardBound(i.checkPrefixSpanEnd(i.iter.Prev()))
|
||||
case posAtUpperLimit:
|
||||
// The BoundedIter had previously returned nil, because it knew from
|
||||
// i.iterSpan's bounds that there was no next span. To Prev, we only
|
||||
// need to return the current iter span and reset i.pos to reflect that
|
||||
// we're no longer positioned at the limit.
|
||||
i.pos = posAtIterSpan
|
||||
return i.iterSpan
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements FragmentIterator.
|
||||
func (i *BoundedIter) Error() error {
|
||||
return i.iter.Error()
|
||||
}
|
||||
|
||||
// Close implements FragmentIterator.
|
||||
func (i *BoundedIter) Close() error {
|
||||
return i.iter.Close()
|
||||
}
|
||||
|
||||
// SetBounds modifies the FragmentIterator's bounds.
|
||||
func (i *BoundedIter) SetBounds(lower, upper []byte) {
|
||||
i.lower, i.upper = lower, upper
|
||||
}
|
||||
|
||||
func (i *BoundedIter) checkPrefixSpanStart(span *Span) *Span {
|
||||
// Compare to the prefix's bounds, if in prefix iteration mode.
|
||||
if span != nil && *i.hasPrefix {
|
||||
si := i.split(span.Start)
|
||||
if i.cmp(span.Start[:si], *i.prefix) > 0 {
|
||||
// This span starts at a prefix that sorts after our current prefix.
|
||||
span = nil
|
||||
}
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
// checkForwardBound enforces the upper bound, returning nil if the provided
|
||||
// span is wholly outside the upper bound. It also updates i.pos and i.iterSpan
|
||||
// to reflect the new iterator position.
|
||||
func (i *BoundedIter) checkForwardBound(span *Span) *Span {
|
||||
// Compare to the upper bound.
|
||||
if span != nil && i.upper != nil && i.cmp(span.Start, i.upper) >= 0 {
|
||||
span = nil
|
||||
}
|
||||
i.iterSpan = span
|
||||
if i.pos != posAtIterSpan {
|
||||
i.pos = posAtIterSpan
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
func (i *BoundedIter) checkPrefixSpanEnd(span *Span) *Span {
|
||||
// Compare to the prefix's bounds, if in prefix iteration mode.
|
||||
if span != nil && *i.hasPrefix && i.cmp(span.End, *i.prefix) <= 0 {
|
||||
// This span ends before the current prefix.
|
||||
span = nil
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
// checkBackward enforces the lower bound, returning nil if the provided span is
|
||||
// wholly outside the lower bound. It also updates i.pos and i.iterSpan to
|
||||
// reflect the new iterator position.
|
||||
func (i *BoundedIter) checkBackwardBound(span *Span) *Span {
|
||||
// Compare to the lower bound.
|
||||
if span != nil && i.lower != nil && i.cmp(span.End, i.lower) <= 0 {
|
||||
span = nil
|
||||
}
|
||||
i.iterSpan = span
|
||||
if i.pos != posAtIterSpan {
|
||||
i.pos = posAtIterSpan
|
||||
}
|
||||
return span
|
||||
}
|
||||
178
vendor/github.com/cockroachdb/pebble/internal/keyspan/defragment.go
generated
vendored
178
vendor/github.com/cockroachdb/pebble/internal/keyspan/defragment.go
generated
vendored
@@ -18,15 +18,33 @@ const bufferReuseMaxCapacity = 10 << 10 // 10 KB
|
||||
|
||||
// DefragmentMethod configures the defragmentation performed by the
|
||||
// DefragmentingIter.
|
||||
type DefragmentMethod func(base.Compare, Span, Span) bool
|
||||
type DefragmentMethod interface {
|
||||
// ShouldDefragment takes two abutting spans and returns whether the two
|
||||
// spans should be combined into a single, defragmented Span.
|
||||
ShouldDefragment(equal base.Equal, left, right *Span) bool
|
||||
}
|
||||
|
||||
// The DefragmentMethodFunc type is an adapter to allow the use of ordinary
|
||||
// functions as DefragmentMethods. If f is a function with the appropriate
|
||||
// signature, DefragmentMethodFunc(f) is a DefragmentMethod that calls f.
|
||||
type DefragmentMethodFunc func(equal base.Equal, left, right *Span) bool
|
||||
|
||||
// ShouldDefragment calls f(equal, left, right).
|
||||
func (f DefragmentMethodFunc) ShouldDefragment(equal base.Equal, left, right *Span) bool {
|
||||
return f(equal, left, right)
|
||||
}
|
||||
|
||||
// DefragmentInternal configures a DefragmentingIter to defragment spans
|
||||
// only if they have identical keys.
|
||||
// only if they have identical keys. It requires spans' keys to be sorted in
|
||||
// trailer descending order.
|
||||
//
|
||||
// This defragmenting method is intended for use in compactions that may see
|
||||
// internal range keys fragments that may now be joined, because the state that
|
||||
// required their fragmentation has been dropped.
|
||||
var DefragmentInternal DefragmentMethod = func(cmp base.Compare, a, b Span) bool {
|
||||
var DefragmentInternal DefragmentMethod = DefragmentMethodFunc(func(equal base.Equal, a, b *Span) bool {
|
||||
if a.KeysOrder != ByTrailerDesc || b.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span keys unexpectedly not in trailer descending order")
|
||||
}
|
||||
if len(a.Keys) != len(b.Keys) {
|
||||
return false
|
||||
}
|
||||
@@ -34,7 +52,7 @@ var DefragmentInternal DefragmentMethod = func(cmp base.Compare, a, b Span) bool
|
||||
if a.Keys[i].Trailer != b.Keys[i].Trailer {
|
||||
return false
|
||||
}
|
||||
if cmp(a.Keys[i].Suffix, b.Keys[i].Suffix) != 0 {
|
||||
if !equal(a.Keys[i].Suffix, b.Keys[i].Suffix) {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(a.Keys[i].Value, b.Keys[i].Value) {
|
||||
@@ -42,7 +60,7 @@ var DefragmentInternal DefragmentMethod = func(cmp base.Compare, a, b Span) bool
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// DefragmentReducer merges the current and next Key slices, returning a new Key
|
||||
// slice.
|
||||
@@ -100,9 +118,10 @@ const (
|
||||
// defragments in the iteration direction, ensuring it's found a whole
|
||||
// defragmented span.
|
||||
type DefragmentingIter struct {
|
||||
cmp base.Compare
|
||||
comparer *base.Comparer
|
||||
equal base.Equal
|
||||
iter FragmentIterator
|
||||
iterSpan Span
|
||||
iterSpan *Span
|
||||
iterPos iterPos
|
||||
|
||||
// curr holds the span at the current iterator position. currBuf is a buffer
|
||||
@@ -119,10 +138,10 @@ type DefragmentingIter struct {
|
||||
keysBuf []Key
|
||||
keyBuf []byte
|
||||
|
||||
// equal is a comparison function for two spans. equal is called when two
|
||||
// method is a comparison function for two spans. method is called when two
|
||||
// spans are abutting to determine whether they may be defragmented.
|
||||
// equal does not itself check for adjacency for the two spans.
|
||||
equal DefragmentMethod
|
||||
// method does not itself check for adjacency for the two spans.
|
||||
method DefragmentMethod
|
||||
|
||||
// reduce is the reducer function used to collect Keys across all spans that
|
||||
// constitute a defragmented span.
|
||||
@@ -135,13 +154,14 @@ var _ FragmentIterator = (*DefragmentingIter)(nil)
|
||||
// Init initializes the defragmenting iter using the provided defragment
|
||||
// method.
|
||||
func (i *DefragmentingIter) Init(
|
||||
cmp base.Compare, iter FragmentIterator, equal DefragmentMethod, reducer DefragmentReducer,
|
||||
comparer *base.Comparer, iter FragmentIterator, equal DefragmentMethod, reducer DefragmentReducer,
|
||||
) {
|
||||
*i = DefragmentingIter{
|
||||
cmp: cmp,
|
||||
iter: iter,
|
||||
equal: equal,
|
||||
reduce: reducer,
|
||||
comparer: comparer,
|
||||
equal: comparer.Equal,
|
||||
iter: iter,
|
||||
method: equal,
|
||||
reduce: reducer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,16 +177,19 @@ func (i *DefragmentingIter) Close() error {
|
||||
|
||||
// SeekGE seeks the iterator to the first span with a start key greater than or
|
||||
// equal to key and returns it.
|
||||
func (i *DefragmentingIter) SeekGE(key []byte) Span {
|
||||
func (i *DefragmentingIter) SeekGE(key []byte) *Span {
|
||||
i.iterSpan = i.iter.SeekGE(key)
|
||||
if !i.iterSpan.Valid() {
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
} else if i.iterSpan.Empty() {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
// Save the current span and peek backwards.
|
||||
i.saveCurrent(i.iterSpan)
|
||||
i.saveCurrent()
|
||||
i.iterSpan = i.iter.Prev()
|
||||
if i.cmp(i.curr.Start, i.iterSpan.End) == 0 && i.checkEqual(i.iterSpan, i.curr) {
|
||||
if i.iterSpan != nil && i.equal(i.curr.Start, i.iterSpan.End) && i.checkEqual(i.iterSpan, &i.curr) {
|
||||
// A continuation. The span we originally landed on and defragmented
|
||||
// backwards has a true Start key < key. To obey the FragmentIterator
|
||||
// contract, we must not return this defragmented span. Defragment
|
||||
@@ -187,8 +210,15 @@ func (i *DefragmentingIter) SeekGE(key []byte) Span {
|
||||
|
||||
// SeekLT seeks the iterator to the last span with a start key less than
|
||||
// key and returns it.
|
||||
func (i *DefragmentingIter) SeekLT(key []byte) Span {
|
||||
func (i *DefragmentingIter) SeekLT(key []byte) *Span {
|
||||
i.iterSpan = i.iter.SeekLT(key)
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
} else if i.iterSpan.Empty() {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
// Defragment forward to find the end of the defragmented span.
|
||||
i.defragmentForward()
|
||||
if i.iterPos == iterPosNext {
|
||||
@@ -200,19 +230,27 @@ func (i *DefragmentingIter) SeekLT(key []byte) Span {
|
||||
}
|
||||
|
||||
// First seeks the iterator to the first span and returns it.
|
||||
func (i *DefragmentingIter) First() Span {
|
||||
func (i *DefragmentingIter) First() *Span {
|
||||
i.iterSpan = i.iter.First()
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
return i.defragmentForward()
|
||||
}
|
||||
|
||||
// Last seeks the iterator to the last span and returns it.
|
||||
func (i *DefragmentingIter) Last() Span {
|
||||
func (i *DefragmentingIter) Last() *Span {
|
||||
i.iterSpan = i.iter.Last()
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
return i.defragmentBackward()
|
||||
}
|
||||
|
||||
// Next advances to the next span and returns it.
|
||||
func (i *DefragmentingIter) Next() Span {
|
||||
func (i *DefragmentingIter) Next() *Span {
|
||||
switch i.iterPos {
|
||||
case iterPosPrev:
|
||||
// Switching directions; The iterator is currently positioned over the
|
||||
@@ -228,7 +266,7 @@ func (i *DefragmentingIter) Next() Span {
|
||||
// Next once to move onto y, defragment forward to land on the first z
|
||||
// position.
|
||||
i.iterSpan = i.iter.Next()
|
||||
if !i.iterSpan.Valid() {
|
||||
if invariants.Enabled && i.iterSpan == nil {
|
||||
panic("pebble: invariant violation: no next span while switching directions")
|
||||
}
|
||||
// We're now positioned on the first span that was defragmented into the
|
||||
@@ -236,6 +274,10 @@ func (i *DefragmentingIter) Next() Span {
|
||||
// position's constitutent fragments. In the above example, this would
|
||||
// land on the first 'z'.
|
||||
i.defragmentForward()
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now that we're positioned over the first of the next set of
|
||||
// fragments, defragment forward.
|
||||
@@ -243,14 +285,21 @@ func (i *DefragmentingIter) Next() Span {
|
||||
case iterPosCurr:
|
||||
// iterPosCurr is only used when the iter is exhausted or when the iterator
|
||||
// is at an empty span.
|
||||
if invariants.Enabled && i.iterSpan.Valid() && !i.iterSpan.Empty() {
|
||||
if invariants.Enabled && i.iterSpan != nil && !i.iterSpan.Empty() {
|
||||
panic("pebble: invariant violation: iterPosCurr with valid iterSpan")
|
||||
}
|
||||
|
||||
i.iterSpan = i.iter.Next()
|
||||
if i.iterSpan == nil {
|
||||
return nil
|
||||
}
|
||||
return i.defragmentForward()
|
||||
case iterPosNext:
|
||||
// Already at the next span.
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
return i.defragmentForward()
|
||||
default:
|
||||
panic("unreachable")
|
||||
@@ -258,19 +307,26 @@ func (i *DefragmentingIter) Next() Span {
|
||||
}
|
||||
|
||||
// Prev steps back to the previous span and returns it.
|
||||
func (i *DefragmentingIter) Prev() Span {
|
||||
func (i *DefragmentingIter) Prev() *Span {
|
||||
switch i.iterPos {
|
||||
case iterPosPrev:
|
||||
// Already at the previous span.
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
return i.defragmentBackward()
|
||||
case iterPosCurr:
|
||||
// iterPosCurr is only used when the iter is exhausted or when the iterator
|
||||
// is at an empty span.
|
||||
if invariants.Enabled && i.iterSpan.Valid() && !i.iterSpan.Empty() {
|
||||
if invariants.Enabled && i.iterSpan != nil && !i.iterSpan.Empty() {
|
||||
panic("pebble: invariant violation: iterPosCurr with valid iterSpan")
|
||||
}
|
||||
|
||||
i.iterSpan = i.iter.Prev()
|
||||
if i.iterSpan == nil {
|
||||
return nil
|
||||
}
|
||||
return i.defragmentBackward()
|
||||
case iterPosNext:
|
||||
// Switching directions; The iterator is currently positioned over the
|
||||
@@ -286,7 +342,7 @@ func (i *DefragmentingIter) Prev() Span {
|
||||
// Prev once to move onto y, defragment backward to land on the last x
|
||||
// position.
|
||||
i.iterSpan = i.iter.Prev()
|
||||
if !i.iterSpan.Valid() {
|
||||
if invariants.Enabled && i.iterSpan == nil {
|
||||
panic("pebble: invariant violation: no previous span while switching directions")
|
||||
}
|
||||
// We're now positioned on the last span that was defragmented into the
|
||||
@@ -297,6 +353,10 @@ func (i *DefragmentingIter) Prev() Span {
|
||||
|
||||
// Now that we're positioned over the last of the prev set of
|
||||
// fragments, defragment backward.
|
||||
if i.iterSpan == nil {
|
||||
i.iterPos = iterPosCurr
|
||||
return nil
|
||||
}
|
||||
return i.defragmentBackward()
|
||||
default:
|
||||
panic("unreachable")
|
||||
@@ -306,17 +366,14 @@ func (i *DefragmentingIter) Prev() Span {
|
||||
// checkEqual checks the two spans for logical equivalence. It uses the passed-in
|
||||
// DefragmentMethod and ensures both spans are NOT empty; not defragmenting empty
|
||||
// spans is an optimization that lets us load fewer sstable blocks.
|
||||
func (i *DefragmentingIter) checkEqual(left, right Span) bool {
|
||||
return i.equal(i.cmp, i.iterSpan, i.curr) && !(left.Empty() && right.Empty())
|
||||
func (i *DefragmentingIter) checkEqual(left, right *Span) bool {
|
||||
return (!left.Empty() && !right.Empty()) && i.method.ShouldDefragment(i.equal, i.iterSpan, &i.curr)
|
||||
}
|
||||
|
||||
// defragmentForward defragments spans in the forward direction, starting from
|
||||
// i.iter's current position.
|
||||
func (i *DefragmentingIter) defragmentForward() Span {
|
||||
if !i.iterSpan.Valid() {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
// i.iter's current position. The span at the current position must be non-nil,
|
||||
// but may be Empty().
|
||||
func (i *DefragmentingIter) defragmentForward() *Span {
|
||||
if i.iterSpan.Empty() {
|
||||
// An empty span will never be equal to another span; see checkEqual for
|
||||
// why. To avoid loading non-empty range keys further ahead by calling Next,
|
||||
@@ -324,16 +381,16 @@ func (i *DefragmentingIter) defragmentForward() Span {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
i.saveCurrent(i.iterSpan)
|
||||
i.saveCurrent()
|
||||
|
||||
i.iterPos = iterPosNext
|
||||
i.iterSpan = i.iter.Next()
|
||||
for i.iterSpan.Valid() {
|
||||
if i.cmp(i.curr.End, i.iterSpan.Start) != 0 {
|
||||
for i.iterSpan != nil {
|
||||
if !i.equal(i.curr.End, i.iterSpan.Start) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
if !i.checkEqual(i.iterSpan, i.curr) {
|
||||
if !i.checkEqual(i.iterSpan, &i.curr) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
@@ -343,16 +400,13 @@ func (i *DefragmentingIter) defragmentForward() Span {
|
||||
i.iterSpan = i.iter.Next()
|
||||
}
|
||||
i.curr.Keys = i.keysBuf
|
||||
return i.curr
|
||||
return &i.curr
|
||||
}
|
||||
|
||||
// defragmentBackward defragments spans in the backward direction, starting from
|
||||
// i.iter's current position.
|
||||
func (i *DefragmentingIter) defragmentBackward() Span {
|
||||
if !i.iterSpan.Valid() {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
// i.iter's current position. The span at the current position must be non-nil,
|
||||
// but may be Empty().
|
||||
func (i *DefragmentingIter) defragmentBackward() *Span {
|
||||
if i.iterSpan.Empty() {
|
||||
// An empty span will never be equal to another span; see checkEqual for
|
||||
// why. To avoid loading non-empty range keys further ahead by calling Next,
|
||||
@@ -360,16 +414,16 @@ func (i *DefragmentingIter) defragmentBackward() Span {
|
||||
i.iterPos = iterPosCurr
|
||||
return i.iterSpan
|
||||
}
|
||||
i.saveCurrent(i.iterSpan)
|
||||
i.saveCurrent()
|
||||
|
||||
i.iterPos = iterPosPrev
|
||||
i.iterSpan = i.iter.Prev()
|
||||
for i.iterSpan.Valid() {
|
||||
if i.cmp(i.curr.Start, i.iterSpan.End) != 0 {
|
||||
for i.iterSpan != nil {
|
||||
if !i.equal(i.curr.Start, i.iterSpan.End) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
if !i.checkEqual(i.iterSpan, i.curr) {
|
||||
if !i.checkEqual(i.iterSpan, &i.curr) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
@@ -379,10 +433,10 @@ func (i *DefragmentingIter) defragmentBackward() Span {
|
||||
i.iterSpan = i.iter.Prev()
|
||||
}
|
||||
i.curr.Keys = i.keysBuf
|
||||
return i.curr
|
||||
return &i.curr
|
||||
}
|
||||
|
||||
func (i *DefragmentingIter) saveCurrent(span Span) {
|
||||
func (i *DefragmentingIter) saveCurrent() {
|
||||
i.currBuf = i.currBuf[:0]
|
||||
i.keysBuf = i.keysBuf[:0]
|
||||
i.keyBuf = i.keyBuf[:0]
|
||||
@@ -392,15 +446,19 @@ func (i *DefragmentingIter) saveCurrent(span Span) {
|
||||
if cap(i.keyBuf) > bufferReuseMaxCapacity {
|
||||
i.keyBuf = nil
|
||||
}
|
||||
i.curr = Span{
|
||||
Start: i.saveBytes(span.Start),
|
||||
End: i.saveBytes(span.End),
|
||||
if i.iterSpan == nil {
|
||||
return
|
||||
}
|
||||
for j := range span.Keys {
|
||||
i.curr = Span{
|
||||
Start: i.saveBytes(i.iterSpan.Start),
|
||||
End: i.saveBytes(i.iterSpan.End),
|
||||
KeysOrder: i.iterSpan.KeysOrder,
|
||||
}
|
||||
for j := range i.iterSpan.Keys {
|
||||
i.keysBuf = append(i.keysBuf, Key{
|
||||
Trailer: span.Keys[j].Trailer,
|
||||
Suffix: i.saveBytes(span.Keys[j].Suffix),
|
||||
Value: i.saveBytes(span.Keys[j].Value),
|
||||
Trailer: i.iterSpan.Keys[j].Trailer,
|
||||
Suffix: i.saveBytes(i.iterSpan.Keys[j].Suffix),
|
||||
Value: i.saveBytes(i.iterSpan.Keys[j].Value),
|
||||
})
|
||||
}
|
||||
i.curr.Keys = i.keysBuf
|
||||
|
||||
24
vendor/github.com/cockroachdb/pebble/internal/keyspan/filter.go
generated
vendored
24
vendor/github.com/cockroachdb/pebble/internal/keyspan/filter.go
generated
vendored
@@ -10,7 +10,7 @@ package keyspan
|
||||
// the output Span, for example, to elice certain keys, or update the Span's
|
||||
// bounds if so desired. The output Span's Keys slice may be reused to reduce
|
||||
// allocations.
|
||||
type FilterFunc func(in Span, out *Span) (keep bool)
|
||||
type FilterFunc func(in *Span, out *Span) (keep bool)
|
||||
|
||||
// filteringIter is a FragmentIterator that uses a FilterFunc to select which
|
||||
// Spans from the input iterator are returned in the output.
|
||||
@@ -37,32 +37,32 @@ func Filter(iter FragmentIterator, filter FilterFunc) FragmentIterator {
|
||||
}
|
||||
|
||||
// SeekGE implements FragmentIterator.
|
||||
func (i *filteringIter) SeekGE(key []byte) Span {
|
||||
func (i *filteringIter) SeekGE(key []byte) *Span {
|
||||
return i.filter(i.iter.SeekGE(key), +1)
|
||||
}
|
||||
|
||||
// SeekLT implements FragmentIterator.
|
||||
func (i *filteringIter) SeekLT(key []byte) Span {
|
||||
func (i *filteringIter) SeekLT(key []byte) *Span {
|
||||
return i.filter(i.iter.SeekLT(key), -1)
|
||||
}
|
||||
|
||||
// First implements FragmentIterator.
|
||||
func (i *filteringIter) First() Span {
|
||||
func (i *filteringIter) First() *Span {
|
||||
return i.filter(i.iter.First(), +1)
|
||||
}
|
||||
|
||||
// Last implements FragmentIterator.
|
||||
func (i *filteringIter) Last() Span {
|
||||
func (i *filteringIter) Last() *Span {
|
||||
return i.filter(i.iter.Last(), -1)
|
||||
}
|
||||
|
||||
// Next implements FragmentIterator.
|
||||
func (i *filteringIter) Next() Span {
|
||||
func (i *filteringIter) Next() *Span {
|
||||
return i.filter(i.iter.Next(), +1)
|
||||
}
|
||||
|
||||
// Prev implements FragmentIterator.
|
||||
func (i *filteringIter) Prev() Span {
|
||||
func (i *filteringIter) Prev() *Span {
|
||||
return i.filter(i.iter.Prev(), -1)
|
||||
}
|
||||
|
||||
@@ -80,18 +80,18 @@ func (i *filteringIter) Close() error {
|
||||
// given Span. If the current Span is to be skipped, the iterator continues
|
||||
// iterating in the given direction until it lands on a Span that should be
|
||||
// returned, or the iterator becomes invalid.
|
||||
func (i *filteringIter) filter(span Span, dir int8) Span {
|
||||
func (i *filteringIter) filter(span *Span, dir int8) *Span {
|
||||
if i.filterFn == nil {
|
||||
return span
|
||||
}
|
||||
for i.Error() == nil && span.Valid() {
|
||||
for i.Error() == nil && span != nil {
|
||||
if keep := i.filterFn(span, &i.span); keep {
|
||||
return i.span
|
||||
return &i.span
|
||||
}
|
||||
if dir == +1 {
|
||||
span = i.Next()
|
||||
span = i.iter.Next()
|
||||
} else {
|
||||
span = i.Prev()
|
||||
span = i.iter.Prev()
|
||||
}
|
||||
}
|
||||
return span
|
||||
|
||||
6
vendor/github.com/cockroachdb/pebble/internal/keyspan/fragmenter.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/internal/keyspan/fragmenter.go
generated
vendored
@@ -183,9 +183,13 @@ func (f *Fragmenter) checkInvariants(buf []Span) {
|
||||
// deletion and range key blocks, so these keys are stable. Because of this key
|
||||
// stability, typically callers only need to perform a shallow clone of the Span
|
||||
// before Add-ing it to the fragmenter.
|
||||
//
|
||||
// Add requires the provided span's keys are sorted in Trailer descending order.
|
||||
func (f *Fragmenter) Add(s Span) {
|
||||
if f.finished {
|
||||
panic("pebble: span fragmenter already finished")
|
||||
} else if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span keys unexpectedly not in trailer descending order")
|
||||
}
|
||||
if f.flushedKey != nil {
|
||||
switch c := f.Cmp(s.Start, f.flushedKey); {
|
||||
@@ -248,7 +252,7 @@ func (f *Fragmenter) Covers(key base.InternalKey, snapshot uint64) bool {
|
||||
// NB: A range deletion tombstone does not delete a point operation
|
||||
// at the same sequence number, and broadly a span is not considered
|
||||
// to cover a point operation at the same sequence number.
|
||||
if s.Visible(snapshot).Covers(seqNum) {
|
||||
if s.CoversAt(snapshot, seqNum) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
16
vendor/github.com/cockroachdb/pebble/internal/keyspan/get.go
generated
vendored
16
vendor/github.com/cockroachdb/pebble/internal/keyspan/get.go
generated
vendored
@@ -11,7 +11,7 @@ import "github.com/cockroachdb/pebble/internal/base"
|
||||
// parameter controls the visibility of spans (only spans older than the
|
||||
// snapshot sequence number are visible). The iterator must contain
|
||||
// fragmented spans: no span may overlap another.
|
||||
func Get(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64) Span {
|
||||
func Get(cmp base.Compare, iter FragmentIterator, key []byte) *Span {
|
||||
// NB: We use SeekLT in order to land on the proper span for a search
|
||||
// key that resides in the middle of a span. Consider the scenario:
|
||||
//
|
||||
@@ -25,15 +25,15 @@ func Get(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64) S
|
||||
// happens for the search key `e`. In that case SeekLT will land us
|
||||
// on the span [a,e) and we'll have to move forward.
|
||||
iterSpan := iter.SeekLT(key)
|
||||
if !iterSpan.Valid() {
|
||||
if iterSpan == nil {
|
||||
iterSpan = iter.Next()
|
||||
if !iterSpan.Valid() {
|
||||
if iterSpan == nil {
|
||||
// The iterator is empty.
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if cmp(key, iterSpan.Start) < 0 {
|
||||
// The search key lies before the first span.
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ func Get(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64) S
|
||||
// once to potentially land on a key with a start key exactly equal to
|
||||
// key. (See the comment at the beginning of this function.)
|
||||
iterSpan = iter.Next()
|
||||
if !iterSpan.Valid() || cmp(key, iterSpan.Start) < 0 {
|
||||
if iterSpan == nil || cmp(key, iterSpan.Start) < 0 {
|
||||
// We've run out of spans or we've moved on to a span which
|
||||
// starts after our search key.
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return iterSpan.Visible(snapshot)
|
||||
return iterSpan
|
||||
}
|
||||
|
||||
697
vendor/github.com/cockroachdb/pebble/internal/keyspan/interleaving_iter.go
generated
vendored
697
vendor/github.com/cockroachdb/pebble/internal/keyspan/interleaving_iter.go
generated
vendored
File diff suppressed because it is too large
Load Diff
40
vendor/github.com/cockroachdb/pebble/internal/keyspan/internal_iter_shim.go
generated
vendored
40
vendor/github.com/cockroachdb/pebble/internal/keyspan/internal_iter_shim.go
generated
vendored
@@ -16,7 +16,7 @@ import "github.com/cockroachdb/pebble/internal/base"
|
||||
// keyspan.FragmentIterator with point keys.
|
||||
type InternalIteratorShim struct {
|
||||
miter MergingIter
|
||||
span Span
|
||||
span *Span
|
||||
iterKey base.InternalKey
|
||||
}
|
||||
|
||||
@@ -31,62 +31,64 @@ func (i *InternalIteratorShim) Init(cmp base.Compare, iters ...FragmentIterator)
|
||||
|
||||
// Span returns the span containing the full set of keys over the key span at
|
||||
// the current iterator position.
|
||||
func (i *InternalIteratorShim) Span() Span {
|
||||
func (i *InternalIteratorShim) Span() *Span {
|
||||
return i.span
|
||||
}
|
||||
|
||||
// SeekGE implements (base.InternalIterator).SeekGE.
|
||||
func (i *InternalIteratorShim) SeekGE(
|
||||
key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements (base.InternalIterator).SeekPrefixGE.
|
||||
func (i *InternalIteratorShim) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements (base.InternalIterator).SeekLT.
|
||||
func (i *InternalIteratorShim) SeekLT(key []byte) (*base.InternalKey, []byte) {
|
||||
func (i *InternalIteratorShim) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// First implements (base.InternalIterator).First.
|
||||
func (i *InternalIteratorShim) First() (*base.InternalKey, []byte) {
|
||||
func (i *InternalIteratorShim) First() (*base.InternalKey, base.LazyValue) {
|
||||
i.span = i.miter.First()
|
||||
for i.span.Valid() && i.span.Empty() {
|
||||
for i.span != nil && i.span.Empty() {
|
||||
i.span = i.miter.Next()
|
||||
}
|
||||
if !i.span.Valid() {
|
||||
return nil, nil
|
||||
if i.span == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.iterKey = base.InternalKey{UserKey: i.span.Start, Trailer: i.span.Keys[0].Trailer}
|
||||
return &i.iterKey, i.span.End
|
||||
return &i.iterKey, base.MakeInPlaceValue(i.span.End)
|
||||
}
|
||||
|
||||
// Last implements (base.InternalIterator).Last.
|
||||
func (i *InternalIteratorShim) Last() (*base.InternalKey, []byte) {
|
||||
func (i *InternalIteratorShim) Last() (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Next implements (base.InternalIterator).Next.
|
||||
func (i *InternalIteratorShim) Next() (*base.InternalKey, []byte) {
|
||||
func (i *InternalIteratorShim) Next() (*base.InternalKey, base.LazyValue) {
|
||||
i.span = i.miter.Next()
|
||||
for i.span.Valid() && i.span.Empty() {
|
||||
for i.span != nil && i.span.Empty() {
|
||||
i.span = i.miter.Next()
|
||||
}
|
||||
if !i.span.Valid() {
|
||||
return nil, nil
|
||||
if i.span == nil {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.iterKey = base.InternalKey{UserKey: i.span.Start, Trailer: i.span.Keys[0].Trailer}
|
||||
return &i.iterKey, i.span.End
|
||||
return &i.iterKey, base.MakeInPlaceValue(i.span.End)
|
||||
}
|
||||
|
||||
// Prev implements (base.InternalIterator).Prev.
|
||||
func (i *InternalIteratorShim) Prev() (*base.InternalKey, []byte) {
|
||||
func (i *InternalIteratorShim) Prev() (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
|
||||
66
vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go
generated
vendored
66
vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go
generated
vendored
@@ -20,17 +20,17 @@ import (
|
||||
type FragmentIterator interface {
|
||||
// SeekGE moves the iterator to the first span whose start key is greater
|
||||
// than or equal to the given key.
|
||||
SeekGE(key []byte) Span
|
||||
SeekGE(key []byte) *Span
|
||||
|
||||
// SeekLT moves the iterator to the last span whose start key is less than
|
||||
// the given key.
|
||||
SeekLT(key []byte) Span
|
||||
SeekLT(key []byte) *Span
|
||||
|
||||
// First moves the iterator to the first span.
|
||||
First() Span
|
||||
First() *Span
|
||||
|
||||
// Last moves the iterator to the last span.
|
||||
Last() Span
|
||||
Last() *Span
|
||||
|
||||
// Next moves the iterator to the next span.
|
||||
//
|
||||
@@ -38,7 +38,7 @@ type FragmentIterator interface {
|
||||
// key/value pair due to either a prior call to SeekLT or Prev which
|
||||
// returned an invalid span. It is not allowed to call Next when the
|
||||
// previous call to SeekGE, SeekPrefixGE or Next returned an invalid span.
|
||||
Next() Span
|
||||
Next() *Span
|
||||
|
||||
// Prev moves the iterator to the previous span.
|
||||
//
|
||||
@@ -46,7 +46,7 @@ type FragmentIterator interface {
|
||||
// key/value pair due to either a prior call to SeekGE or Next which
|
||||
// returned an invalid span. It is not allowed to call Prev when the
|
||||
// previous call to SeekLT or Prev returned an invalid span.
|
||||
Prev() Span
|
||||
Prev() *Span
|
||||
|
||||
// Error returns any accumulated error.
|
||||
Error() error
|
||||
@@ -81,7 +81,19 @@ var _ FragmentIterator = (*Iter)(nil)
|
||||
|
||||
// NewIter returns a new iterator over a set of fragmented spans.
|
||||
func NewIter(cmp base.Compare, spans []Span) *Iter {
|
||||
return &Iter{
|
||||
i := &Iter{}
|
||||
i.Init(cmp, spans)
|
||||
return i
|
||||
}
|
||||
|
||||
// Count returns the number of spans contained by Iter.
|
||||
func (i *Iter) Count() int {
|
||||
return len(i.spans)
|
||||
}
|
||||
|
||||
// Init initializes an Iter with the provided spans.
|
||||
func (i *Iter) Init(cmp base.Compare, spans []Span) {
|
||||
*i = Iter{
|
||||
cmp: cmp,
|
||||
spans: spans,
|
||||
index: -1,
|
||||
@@ -89,7 +101,7 @@ func NewIter(cmp base.Compare, spans []Span) *Iter {
|
||||
}
|
||||
|
||||
// SeekGE implements FragmentIterator.SeekGE.
|
||||
func (i *Iter) SeekGE(key []byte) Span {
|
||||
func (i *Iter) SeekGE(key []byte) *Span {
|
||||
// NB: manually inlined sort.Search is ~5% faster.
|
||||
//
|
||||
// Define f(-1) == false and f(n) == true.
|
||||
@@ -108,13 +120,13 @@ func (i *Iter) SeekGE(key []byte) Span {
|
||||
// i.index == upper, f(i.index-1) == false, and f(upper) (= f(i.index)) ==
|
||||
// true => answer is i.index.
|
||||
if i.index >= len(i.spans) {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// SeekLT implements FragmentIterator.SeekLT.
|
||||
func (i *Iter) SeekLT(key []byte) Span {
|
||||
func (i *Iter) SeekLT(key []byte) *Span {
|
||||
// NB: manually inlined sort.Search is ~5% faster.
|
||||
//
|
||||
// Define f(-1) == false and f(n) == true.
|
||||
@@ -137,51 +149,51 @@ func (i *Iter) SeekLT(key []byte) Span {
|
||||
// the largest whose key is < the key sought.
|
||||
i.index--
|
||||
if i.index < 0 {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// First implements FragmentIterator.First.
|
||||
func (i *Iter) First() Span {
|
||||
func (i *Iter) First() *Span {
|
||||
if len(i.spans) == 0 {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
i.index = 0
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// Last implements FragmentIterator.Last.
|
||||
func (i *Iter) Last() Span {
|
||||
func (i *Iter) Last() *Span {
|
||||
if len(i.spans) == 0 {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
i.index = len(i.spans) - 1
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// Next implements FragmentIterator.Next.
|
||||
func (i *Iter) Next() Span {
|
||||
func (i *Iter) Next() *Span {
|
||||
if i.index >= len(i.spans) {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
i.index++
|
||||
if i.index >= len(i.spans) {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// Prev implements FragmentIterator.Prev.
|
||||
func (i *Iter) Prev() Span {
|
||||
func (i *Iter) Prev() *Span {
|
||||
if i.index < 0 {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
i.index--
|
||||
if i.index < 0 {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
return i.spans[i.index]
|
||||
return &i.spans[i.index]
|
||||
}
|
||||
|
||||
// Error implements FragmentIterator.Error.
|
||||
|
||||
208
vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go
generated
vendored
208
vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go
generated
vendored
@@ -40,8 +40,8 @@ type LevelIter struct {
|
||||
// key spaces. This is an optimization to avoid unnecessarily loading files
|
||||
// in cases where range keys are sparse and rare. dir is set by every
|
||||
// positioning operation, straddleDir is set to dir whenever a straddling
|
||||
// Span is synthesized, and straddle is Valid() whenever the last positioning
|
||||
// operation returned a synthesized straddle span.
|
||||
// Span is synthesized and the last positioning operation returned a
|
||||
// synthesized straddle span.
|
||||
//
|
||||
// Note that when a straddle span is initialized, iterFile is modified to
|
||||
// point to the next file in the straddleDir direction. A change of direction
|
||||
@@ -53,7 +53,7 @@ type LevelIter struct {
|
||||
// The iter for the current file. It is nil under any of the following conditions:
|
||||
// - files.Current() == nil
|
||||
// - err != nil
|
||||
// - straddle.Valid(), in which case iterFile is not nil and points to the
|
||||
// - straddleDir != 0, in which case iterFile is not nil and points to the
|
||||
// next file (in the straddleDir direction).
|
||||
// - some other constraint, like the bounds in opts, caused the file at index to not
|
||||
// be relevant to the iteration.
|
||||
@@ -178,7 +178,7 @@ func (l *LevelIter) loadFile(file *manifest.FileMetadata, dir int) loadFileRetur
|
||||
return noFileLoaded
|
||||
}
|
||||
if indicator != fileAlreadyLoaded {
|
||||
l.iter, l.err = l.newIter(l.files.Current(), &l.tableOpts)
|
||||
l.iter, l.err = l.newIter(file, &l.tableOpts)
|
||||
indicator = newFileLoaded
|
||||
}
|
||||
if l.err != nil {
|
||||
@@ -188,123 +188,149 @@ func (l *LevelIter) loadFile(file *manifest.FileMetadata, dir int) loadFileRetur
|
||||
}
|
||||
|
||||
// SeekGE implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) SeekGE(key []byte) Span {
|
||||
func (l *LevelIter) SeekGE(key []byte) *Span {
|
||||
l.dir = +1
|
||||
l.straddle = Span{}
|
||||
l.straddleDir = 0
|
||||
l.err = nil // clear cached iteration error
|
||||
|
||||
f := l.findFileGE(key)
|
||||
if f != nil && l.keyType == manifest.KeyTypeRange && l.cmp(key, f.SmallestRangeKey.UserKey) < 0 {
|
||||
// Return a straddling key instead of loading the file.
|
||||
l.iterFile = f
|
||||
l.iter = nil
|
||||
l.straddleDir = +1
|
||||
// The synthetic span that we are creating starts at the seeked key. This
|
||||
// is an optimization as it prevents us from loading the adjacent file's
|
||||
// bounds, at the expense of this iterator appearing "inconsistent" to its
|
||||
// callers i.e.:
|
||||
//
|
||||
// SeekGE(bb) -> {bb-c, empty}
|
||||
// Next() -> {c-d, RANGEKEYSET}
|
||||
// Prev() -> {a-c, empty}
|
||||
//
|
||||
// Seeing as the inconsistency will only be around empty spans, which are
|
||||
// expected to be elided by one of the higher-level iterators (either
|
||||
// top-level Iterator or the defragmenting iter), the entire iterator should
|
||||
// still appear consistent to the user.
|
||||
l.straddle = Span{
|
||||
Start: key,
|
||||
End: f.SmallestRangeKey.UserKey,
|
||||
Keys: nil,
|
||||
prevFile := l.files.Prev()
|
||||
if prevFile != nil {
|
||||
// We could unconditionally return an empty span between the seek key and
|
||||
// f.SmallestRangeKey, however if this span is to the left of all range
|
||||
// keys on this level, it could lead to inconsistent behaviour in relative
|
||||
// positioning operations. Consider this example, with a b-c range key:
|
||||
//
|
||||
// SeekGE(a) -> a-b:{}
|
||||
// Next() -> b-c{(#5,RANGEKEYSET,@4,foo)}
|
||||
// Prev() -> nil
|
||||
//
|
||||
// Iterators higher up in the iterator stack rely on this sort of relative
|
||||
// positioning consistency.
|
||||
//
|
||||
// TODO(bilal): Investigate ways to be able to return straddle spans in
|
||||
// cases similar to the above, while still retaining correctness.
|
||||
l.files.Next()
|
||||
// Return a straddling key instead of loading the file.
|
||||
l.iterFile = f
|
||||
if err := l.Close(); err != nil {
|
||||
return nil
|
||||
}
|
||||
l.straddleDir = +1
|
||||
l.straddle = Span{
|
||||
Start: prevFile.LargestRangeKey.UserKey,
|
||||
End: f.SmallestRangeKey.UserKey,
|
||||
Keys: nil,
|
||||
}
|
||||
return &l.straddle
|
||||
}
|
||||
return l.straddle
|
||||
}
|
||||
loadFileIndicator := l.loadFile(f, +1)
|
||||
if loadFileIndicator == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if span := l.iter.SeekGE(key); span.Valid() {
|
||||
if span := l.iter.SeekGE(key); span != nil {
|
||||
return span
|
||||
}
|
||||
return l.skipEmptyFileForward()
|
||||
}
|
||||
|
||||
// SeekLT implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) SeekLT(key []byte) Span {
|
||||
func (l *LevelIter) SeekLT(key []byte) *Span {
|
||||
l.dir = -1
|
||||
l.straddle = Span{}
|
||||
l.straddleDir = 0
|
||||
l.err = nil // clear cached iteration error
|
||||
|
||||
f := l.findFileLT(key)
|
||||
if f != nil && l.keyType == manifest.KeyTypeRange && l.cmp(f.LargestRangeKey.UserKey, key) < 0 {
|
||||
// Return a straddling key instead of loading the file.
|
||||
l.iterFile = f
|
||||
l.iter = nil
|
||||
l.straddleDir = -1
|
||||
// The synthetic span that we are creating ends at the seeked key. This
|
||||
// is an optimization as it prevents us from loading the adjacent file's
|
||||
// bounds, at the expense of this iterator appearing "inconsistent" to its
|
||||
// callers i.e.:
|
||||
//
|
||||
// SeekLT(dd) -> {d-dd, empty}
|
||||
// Prev() -> {c-d, RANGEKEYSET}
|
||||
// Next() -> {d-e, empty}
|
||||
//
|
||||
// Seeing as the inconsistency will only be around empty spans, which are
|
||||
// expected to be elided by one of the higher-level iterators (either
|
||||
// top-level Iterator or the defragmenting iter), the entire iterator should
|
||||
// still appear consistent to the user.
|
||||
l.straddle = Span{
|
||||
Start: f.LargestRangeKey.UserKey,
|
||||
End: key,
|
||||
Keys: nil,
|
||||
nextFile := l.files.Next()
|
||||
if nextFile != nil {
|
||||
// We could unconditionally return an empty span between f.LargestRangeKey
|
||||
// and the seek key, however if this span is to the right of all range keys
|
||||
// on this level, it could lead to inconsistent behaviour in relative
|
||||
// positioning operations. Consider this example, with a b-c range key:
|
||||
//
|
||||
// SeekLT(d) -> c-d:{}
|
||||
// Prev() -> b-c{(#5,RANGEKEYSET,@4,foo)}
|
||||
// Next() -> nil
|
||||
//
|
||||
// Iterators higher up in the iterator stack rely on this sort of relative
|
||||
// positioning consistency.
|
||||
//
|
||||
// TODO(bilal): Investigate ways to be able to return straddle spans in
|
||||
// cases similar to the above, while still retaining correctness.
|
||||
l.files.Prev()
|
||||
// Return a straddling key instead of loading the file.
|
||||
l.iterFile = f
|
||||
if err := l.Close(); err != nil {
|
||||
return nil
|
||||
}
|
||||
l.straddleDir = -1
|
||||
l.straddle = Span{
|
||||
Start: f.LargestRangeKey.UserKey,
|
||||
End: nextFile.SmallestRangeKey.UserKey,
|
||||
Keys: nil,
|
||||
}
|
||||
return &l.straddle
|
||||
}
|
||||
return l.straddle
|
||||
}
|
||||
if l.loadFile(l.findFileLT(key), -1) == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if span := l.iter.SeekLT(key); span.Valid() {
|
||||
if span := l.iter.SeekLT(key); span != nil {
|
||||
return span
|
||||
}
|
||||
return l.skipEmptyFileBackward()
|
||||
}
|
||||
|
||||
// First implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) First() Span {
|
||||
func (l *LevelIter) First() *Span {
|
||||
l.dir = +1
|
||||
l.straddle = Span{}
|
||||
l.straddleDir = 0
|
||||
l.err = nil // clear cached iteration error
|
||||
|
||||
if l.loadFile(l.files.First(), +1) == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if span := l.iter.First(); span.Valid() {
|
||||
if span := l.iter.First(); span != nil {
|
||||
return span
|
||||
}
|
||||
return l.skipEmptyFileForward()
|
||||
}
|
||||
|
||||
// Last implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) Last() Span {
|
||||
func (l *LevelIter) Last() *Span {
|
||||
l.dir = -1
|
||||
l.straddle = Span{}
|
||||
l.straddleDir = 0
|
||||
l.err = nil // clear cached iteration error
|
||||
|
||||
if l.loadFile(l.files.Last(), -1) == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if span := l.iter.Last(); span.Valid() {
|
||||
if span := l.iter.Last(); span != nil {
|
||||
return span
|
||||
}
|
||||
return l.skipEmptyFileBackward()
|
||||
}
|
||||
|
||||
// Next implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) Next() Span {
|
||||
l.dir = +1
|
||||
if l.err != nil || (l.iter == nil && l.iterFile == nil) {
|
||||
return Span{}
|
||||
func (l *LevelIter) Next() *Span {
|
||||
if l.err != nil || (l.iter == nil && l.iterFile == nil && l.dir > 0) {
|
||||
return nil
|
||||
}
|
||||
if l.iter == nil && l.iterFile == nil {
|
||||
// l.dir <= 0
|
||||
return l.First()
|
||||
}
|
||||
l.dir = +1
|
||||
|
||||
if l.iter != nil {
|
||||
if span := l.iter.Next(); span.Valid() {
|
||||
if span := l.iter.Next(); span != nil {
|
||||
return span
|
||||
}
|
||||
}
|
||||
@@ -312,22 +338,26 @@ func (l *LevelIter) Next() Span {
|
||||
}
|
||||
|
||||
// Prev implements keyspan.FragmentIterator.
|
||||
func (l *LevelIter) Prev() Span {
|
||||
l.dir = -1
|
||||
if l.err != nil || (l.iter == nil && l.iterFile == nil) {
|
||||
return Span{}
|
||||
func (l *LevelIter) Prev() *Span {
|
||||
if l.err != nil || (l.iter == nil && l.iterFile == nil && l.dir < 0) {
|
||||
return nil
|
||||
}
|
||||
if l.iter == nil && l.iterFile == nil {
|
||||
// l.dir >= 0
|
||||
return l.Last()
|
||||
}
|
||||
l.dir = -1
|
||||
|
||||
if l.iter != nil {
|
||||
if span := l.iter.Prev(); span.Valid() {
|
||||
if span := l.iter.Prev(); span != nil {
|
||||
return span
|
||||
}
|
||||
}
|
||||
return l.skipEmptyFileBackward()
|
||||
}
|
||||
|
||||
func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
if !l.straddle.Valid() && l.keyType == manifest.KeyTypeRange &&
|
||||
func (l *LevelIter) skipEmptyFileForward() *Span {
|
||||
if l.straddleDir == 0 && l.keyType == manifest.KeyTypeRange &&
|
||||
l.iterFile != nil && l.iter != nil {
|
||||
// We were at a file that had spans. Check if the next file that has
|
||||
// spans is not directly adjacent to the current file i.e. there is a
|
||||
@@ -337,7 +367,7 @@ func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
// Straddle spans are not created in rangedel mode.
|
||||
if err := l.Close(); err != nil {
|
||||
l.err = err
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
startKey := l.iterFile.LargestRangeKey.UserKey
|
||||
// Resetting l.iterFile without loading the file into l.iter is okay and
|
||||
@@ -345,7 +375,7 @@ func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
// which it should be due to the Close() call above.
|
||||
l.iterFile = l.files.Next()
|
||||
if l.iterFile == nil {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
endKey := l.iterFile.SmallestRangeKey.UserKey
|
||||
if l.cmp(startKey, endKey) < 0 {
|
||||
@@ -355,17 +385,18 @@ func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
Start: startKey,
|
||||
End: endKey,
|
||||
}
|
||||
l.straddleDir = l.dir
|
||||
return l.straddle
|
||||
l.straddleDir = +1
|
||||
return &l.straddle
|
||||
}
|
||||
} else if l.straddle.Valid() && l.straddleDir < 0 {
|
||||
} else if l.straddleDir < 0 {
|
||||
// We were at a straddle key, but are now changing directions. l.iterFile
|
||||
// was already moved backward by skipEmptyFileBackward, so advance it
|
||||
// forward.
|
||||
l.iterFile = l.files.Next()
|
||||
}
|
||||
l.straddle = Span{}
|
||||
var span Span
|
||||
l.straddleDir = 0
|
||||
var span *Span
|
||||
for span.Empty() {
|
||||
fileToLoad := l.iterFile
|
||||
if l.keyType == manifest.KeyTypePoint {
|
||||
@@ -374,7 +405,7 @@ func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
fileToLoad = l.files.Next()
|
||||
}
|
||||
if l.loadFile(fileToLoad, +1) == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
span = l.iter.First()
|
||||
// In rangedel mode, we can expect to get empty files that we'd need to
|
||||
@@ -386,18 +417,18 @@ func (l *LevelIter) skipEmptyFileForward() Span {
|
||||
return span
|
||||
}
|
||||
|
||||
func (l *LevelIter) skipEmptyFileBackward() Span {
|
||||
func (l *LevelIter) skipEmptyFileBackward() *Span {
|
||||
// We were at a file that had spans. Check if the previous file that has
|
||||
// spans is not directly adjacent to the current file i.e. there is a
|
||||
// gap in the span keyspace between the two files. In that case, synthesize
|
||||
// a "straddle span" in l.straddle and return that.
|
||||
//
|
||||
// Straddle spans are not created in rangedel mode.
|
||||
if !l.straddle.Valid() && l.keyType == manifest.KeyTypeRange &&
|
||||
if l.straddleDir == 0 && l.keyType == manifest.KeyTypeRange &&
|
||||
l.iterFile != nil && l.iter != nil {
|
||||
if err := l.Close(); err != nil {
|
||||
l.err = err
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
endKey := l.iterFile.SmallestRangeKey.UserKey
|
||||
// Resetting l.iterFile without loading the file into l.iter is okay and
|
||||
@@ -405,7 +436,7 @@ func (l *LevelIter) skipEmptyFileBackward() Span {
|
||||
// which it should be due to the Close() call above.
|
||||
l.iterFile = l.files.Prev()
|
||||
if l.iterFile == nil {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
startKey := l.iterFile.LargestRangeKey.UserKey
|
||||
if l.cmp(startKey, endKey) < 0 {
|
||||
@@ -415,24 +446,25 @@ func (l *LevelIter) skipEmptyFileBackward() Span {
|
||||
Start: startKey,
|
||||
End: endKey,
|
||||
}
|
||||
l.straddleDir = l.dir
|
||||
return l.straddle
|
||||
l.straddleDir = -1
|
||||
return &l.straddle
|
||||
}
|
||||
} else if l.straddle.Valid() && l.straddleDir > 0 {
|
||||
} else if l.straddleDir > 0 {
|
||||
// We were at a straddle key, but are now changing directions. l.iterFile
|
||||
// was already advanced forward by skipEmptyFileForward, so move it
|
||||
// backward.
|
||||
l.iterFile = l.files.Prev()
|
||||
}
|
||||
l.straddle = Span{}
|
||||
var span Span
|
||||
l.straddleDir = 0
|
||||
var span *Span
|
||||
for span.Empty() {
|
||||
fileToLoad := l.iterFile
|
||||
if l.keyType == manifest.KeyTypePoint {
|
||||
fileToLoad = l.files.Prev()
|
||||
}
|
||||
if l.loadFile(fileToLoad, -1) == noFileLoaded {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
span = l.iter.Last()
|
||||
// In rangedel mode, we can expect to get empty files that we'd need to
|
||||
|
||||
263
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
263
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
)
|
||||
|
||||
// TODO(jackson): Consider implementing an optimization to seek lower levels
|
||||
@@ -20,27 +21,47 @@ import (
|
||||
// seeks would require introducing key comparisons to switchTo{Min,Max}Heap
|
||||
// where there currently are none.
|
||||
|
||||
// Transform defines a transform function to be applied to a Span. A Transform
|
||||
// takes a Span as input and writes the transformed Span to the provided output
|
||||
// *Span pointer. The output Span's Keys slice may be reused by Transform to
|
||||
// reduce allocations.
|
||||
type Transform func(cmp base.Compare, in Span, out *Span) error
|
||||
// Transformer defines a transformation to be applied to a Span.
|
||||
type Transformer interface {
|
||||
// Transform takes a Span as input and writes the transformed Span to the
|
||||
// provided output *Span pointer. The output Span's Keys slice may be reused
|
||||
// by Transform to reduce allocations.
|
||||
Transform(cmp base.Compare, in Span, out *Span) error
|
||||
}
|
||||
|
||||
func noopTransform(_ base.Compare, s Span, dst *Span) error {
|
||||
// The TransformerFunc type is an adapter to allow the use of ordinary functions
|
||||
// as Transformers. If f is a function with the appropriate signature,
|
||||
// TransformerFunc(f) is a Transformer that calls f.
|
||||
type TransformerFunc func(base.Compare, Span, *Span) error
|
||||
|
||||
// Transform calls f(cmp, in, out).
|
||||
func (tf TransformerFunc) Transform(cmp base.Compare, in Span, out *Span) error {
|
||||
return tf(cmp, in, out)
|
||||
}
|
||||
|
||||
var noopTransform Transformer = TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
||||
dst.Start, dst.End = s.Start, s.End
|
||||
dst.Keys = append(dst.Keys[:0], s.Keys...)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
|
||||
// visibleTransform filters keys that are invisible at the provided snapshot
|
||||
// sequence number.
|
||||
func visibleTransform(snapshot uint64) Transform {
|
||||
return func(_ base.Compare, s Span, dst *Span) error {
|
||||
s = s.Visible(snapshot)
|
||||
func visibleTransform(snapshot uint64) Transformer {
|
||||
return TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
||||
dst.Start, dst.End = s.Start, s.End
|
||||
dst.Keys = append(dst.Keys[:0], s.Keys...)
|
||||
dst.Keys = dst.Keys[:0]
|
||||
for _, k := range s.Keys {
|
||||
// NB: The InternalKeySeqNumMax value is used for the batch snapshot
|
||||
// because a batch's visible span keys are filtered when they're
|
||||
// fragmented. There's no requirement to enforce visibility at
|
||||
// iteration time.
|
||||
if base.Visible(k.SeqNum(), snapshot, base.InternalKeySeqNumMax) {
|
||||
dst.Keys = append(dst.Keys, k)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// MergingIter merges spans across levels of the LSM, exposing an iterator over
|
||||
@@ -51,6 +72,10 @@ func visibleTransform(snapshot uint64) Transform {
|
||||
// overlapping keys are surfaced in a single Span. Key spans from one child
|
||||
// iterator may overlap key spans from another child iterator arbitrarily.
|
||||
//
|
||||
// The spans combined by MergingIter will return spans with keys sorted by
|
||||
// trailer descending. If the MergingIter is configured with a Transformer, it's
|
||||
// permitted to modify the ordering of the spans' keys returned by MergingIter.
|
||||
//
|
||||
// Algorithm
|
||||
//
|
||||
// The merging iterator wraps child iterators, merging and fragmenting spans
|
||||
@@ -193,17 +218,17 @@ type MergingIter struct {
|
||||
// buf is a buffer used to save [start, end) boundary keys.
|
||||
buf []byte
|
||||
// keys holds all of the keys across all levels that overlap the key span
|
||||
// [start, end), sorted by sequence number and kind descending. This slice
|
||||
// is reconstituted in synthesizeKeys from each mergingIterLevel's keys
|
||||
// every time the [start, end) bounds change.
|
||||
// [start, end), sorted by Trailer descending. This slice is reconstituted
|
||||
// in synthesizeKeys from each mergingIterLevel's keys every time the
|
||||
// [start, end) bounds change.
|
||||
//
|
||||
// Each element points into a child iterator's memory, so the keys may not
|
||||
// be directly modified.
|
||||
keys keysBySeqNumKind
|
||||
// transform defines a function to be applied to a span before it's yielded
|
||||
// to the user. A transform may filter individual keys contained within the
|
||||
// span.
|
||||
transform Transform
|
||||
// transformer defines a transformation to be applied to a span before it's
|
||||
// yielded to the user. Transforming may filter individual keys contained
|
||||
// within the span.
|
||||
transformer Transformer
|
||||
// span holds the iterator's current span. This span is used as the
|
||||
// destination for transforms. Every tranformed span overwrites the
|
||||
// previous.
|
||||
@@ -211,6 +236,17 @@ type MergingIter struct {
|
||||
|
||||
err error
|
||||
dir int8
|
||||
|
||||
// alloc preallocates mergingIterLevel and mergingIterItems for use by the
|
||||
// merging iterator. As long as the merging iterator is used with
|
||||
// manifest.NumLevels+3 and fewer fragment iterators, the merging iterator
|
||||
// will not need to allocate upon initialization. The value NumLevels+3
|
||||
// mirrors the preallocated levels in iterAlloc used for point iterators.
|
||||
// Invariant: cap(levels) == cap(items)
|
||||
alloc struct {
|
||||
levels [manifest.NumLevels + 3]mergingIterLevel
|
||||
items [manifest.NumLevels + 3]mergingIterItem
|
||||
}
|
||||
}
|
||||
|
||||
// MergingIter implements the FragmentIterator interface.
|
||||
@@ -225,45 +261,82 @@ type mergingIterLevel struct {
|
||||
|
||||
func (l *mergingIterLevel) next() {
|
||||
if l.heapKey.kind == boundKindFragmentStart {
|
||||
l.heapKey = endBound(l.heapKey.span)
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentEnd,
|
||||
key: l.heapKey.span.End,
|
||||
span: l.heapKey.span,
|
||||
}
|
||||
return
|
||||
}
|
||||
l.heapKey = startBound(l.iter.Next())
|
||||
if s := l.iter.Next(); s == nil {
|
||||
l.heapKey = boundKey{kind: boundKindInvalid}
|
||||
} else {
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentStart,
|
||||
key: s.Start,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *mergingIterLevel) prev() {
|
||||
if l.heapKey.kind == boundKindFragmentEnd {
|
||||
l.heapKey = startBound(l.heapKey.span)
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentStart,
|
||||
key: l.heapKey.span.Start,
|
||||
span: l.heapKey.span,
|
||||
}
|
||||
return
|
||||
}
|
||||
l.heapKey = endBound(l.iter.Prev())
|
||||
if s := l.iter.Prev(); s == nil {
|
||||
l.heapKey = boundKey{kind: boundKindInvalid}
|
||||
} else {
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentEnd,
|
||||
key: s.End,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the merging iterator with the provided fragment iterators.
|
||||
func (m *MergingIter) Init(cmp base.Compare, transform Transform, iters ...FragmentIterator) {
|
||||
func (m *MergingIter) Init(cmp base.Compare, transformer Transformer, iters ...FragmentIterator) {
|
||||
levels, items := m.levels, m.heap.items
|
||||
|
||||
*m = MergingIter{
|
||||
heap: mergingIterHeap{cmp: cmp},
|
||||
transform: transform,
|
||||
heap: mergingIterHeap{cmp: cmp},
|
||||
transformer: transformer,
|
||||
}
|
||||
// Invariant: cap(levels) == cap(items)
|
||||
if cap(levels) < len(iters) {
|
||||
m.levels = make([]mergingIterLevel, len(iters))
|
||||
m.heap.items = make([]mergingIterItem, 0, len(iters))
|
||||
} else {
|
||||
|
||||
// Invariant: cap(levels) >= cap(items)
|
||||
// Invariant: cap(alloc.levels) == cap(alloc.items)
|
||||
if len(iters) <= len(m.alloc.levels) {
|
||||
// The slices allocated on the MergingIter struct are large enough.
|
||||
m.levels = m.alloc.levels[:len(iters)]
|
||||
m.heap.items = m.alloc.items[:0]
|
||||
} else if len(iters) <= cap(levels) {
|
||||
// The existing heap-allocated slices are large enough, so reuse them.
|
||||
m.levels = levels[:len(iters)]
|
||||
m.heap.items = items[:0]
|
||||
} else {
|
||||
// Heap allocate new slices.
|
||||
m.levels = make([]mergingIterLevel, len(iters))
|
||||
m.heap.items = make([]mergingIterItem, 0, len(iters))
|
||||
}
|
||||
for i := range m.levels {
|
||||
m.levels[i] = mergingIterLevel{}
|
||||
m.levels[i].iter = iters[i]
|
||||
m.levels[i] = mergingIterLevel{iter: iters[i]}
|
||||
}
|
||||
}
|
||||
|
||||
// AddLevel adds a new level to the bottom of the merging iterator. AddLevel
|
||||
// must be called after Init and before any other method.
|
||||
func (m *MergingIter) AddLevel(iter FragmentIterator) {
|
||||
m.levels = append(m.levels, mergingIterLevel{iter: iter})
|
||||
}
|
||||
|
||||
// SeekGE moves the iterator to the first span with a start key greater than or
|
||||
// equal to key.
|
||||
func (m *MergingIter) SeekGE(key []byte) Span {
|
||||
func (m *MergingIter) SeekGE(key []byte) *Span {
|
||||
m.invalidate() // clear state about current position
|
||||
for i := range m.levels {
|
||||
l := &m.levels[i]
|
||||
@@ -277,24 +350,35 @@ func (m *MergingIter) SeekGE(key []byte) Span {
|
||||
// Otherwise we use the start boundary of the next span which
|
||||
// necessarily has a start ≥ key.
|
||||
s := l.iter.SeekLT(key)
|
||||
if s.Valid() && m.cmp(s.End, key) >= 0 {
|
||||
if s != nil && m.cmp(s.End, key) >= 0 {
|
||||
// s.End ≥ key
|
||||
// We need to use this span's end bound.
|
||||
l.heapKey = endBound(s)
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentEnd,
|
||||
key: s.End,
|
||||
span: s,
|
||||
}
|
||||
continue
|
||||
}
|
||||
// s.End < key
|
||||
// The span `s` ends before key. Next to the first span with a Start ≥
|
||||
// key, and use that.
|
||||
s = l.iter.Next()
|
||||
l.heapKey = startBound(s)
|
||||
if s = l.iter.Next(); s == nil {
|
||||
l.heapKey = boundKey{kind: boundKindInvalid}
|
||||
} else {
|
||||
l.heapKey = boundKey{
|
||||
kind: boundKindFragmentStart,
|
||||
key: s.Start,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
m.initMinHeap()
|
||||
return m.findNextFragmentSet()
|
||||
}
|
||||
|
||||
// SeekLT moves the iterator to the last span with a start key less than key.
|
||||
func (m *MergingIter) SeekLT(key []byte) Span {
|
||||
func (m *MergingIter) SeekLT(key []byte) *Span {
|
||||
// TODO(jackson): Evaluate whether there's an implementation of SeekLT
|
||||
// independent of SeekGE that is more efficient. It's tricky, because the
|
||||
// span we should return might straddle `key` itself.
|
||||
@@ -314,42 +398,56 @@ func (m *MergingIter) SeekLT(key []byte) Span {
|
||||
// Start user key < key: [b,l)#1. This requires examining bounds both < 'c'
|
||||
// (the 'b' of [b,m)#1's start key) and bounds ≥ 'c' (the 'l' of ([a,l)#2's
|
||||
// end key).
|
||||
if s := m.SeekGE(key); !s.Valid() && m.err != nil {
|
||||
return Span{}
|
||||
if s := m.SeekGE(key); s == nil && m.err != nil {
|
||||
return nil
|
||||
}
|
||||
// Prev to the previous span.
|
||||
return m.Prev()
|
||||
}
|
||||
|
||||
// First seeks the iterator to the first span.
|
||||
func (m *MergingIter) First() Span {
|
||||
func (m *MergingIter) First() *Span {
|
||||
m.invalidate() // clear state about current position
|
||||
for i := range m.levels {
|
||||
l := &m.levels[i]
|
||||
l.heapKey = startBound(l.iter.First())
|
||||
if s := m.levels[i].iter.First(); s == nil {
|
||||
m.levels[i].heapKey = boundKey{kind: boundKindInvalid}
|
||||
} else {
|
||||
m.levels[i].heapKey = boundKey{
|
||||
kind: boundKindFragmentStart,
|
||||
key: s.Start,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
m.initMinHeap()
|
||||
return m.findNextFragmentSet()
|
||||
}
|
||||
|
||||
// Last seeks the iterator to the last span.
|
||||
func (m *MergingIter) Last() Span {
|
||||
func (m *MergingIter) Last() *Span {
|
||||
m.invalidate() // clear state about current position
|
||||
for i := range m.levels {
|
||||
l := &m.levels[i]
|
||||
l.heapKey = endBound(l.iter.Last())
|
||||
if s := m.levels[i].iter.Last(); s == nil {
|
||||
m.levels[i].heapKey = boundKey{kind: boundKindInvalid}
|
||||
} else {
|
||||
m.levels[i].heapKey = boundKey{
|
||||
kind: boundKindFragmentEnd,
|
||||
key: s.End,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
m.initMaxHeap()
|
||||
return m.findPrevFragmentSet()
|
||||
}
|
||||
|
||||
// Next advances the iterator to the next span.
|
||||
func (m *MergingIter) Next() Span {
|
||||
func (m *MergingIter) Next() *Span {
|
||||
if m.err != nil {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if m.dir == +1 && (m.end == nil || m.start == nil) {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if m.dir != +1 {
|
||||
m.switchToMinHeap()
|
||||
@@ -358,12 +456,12 @@ func (m *MergingIter) Next() Span {
|
||||
}
|
||||
|
||||
// Prev advances the iterator to the previous span.
|
||||
func (m *MergingIter) Prev() Span {
|
||||
func (m *MergingIter) Prev() *Span {
|
||||
if m.err != nil {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if m.dir == -1 && (m.end == nil || m.start == nil) {
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
if m.dir != -1 {
|
||||
m.switchToMaxHeap()
|
||||
@@ -533,7 +631,7 @@ func (m *MergingIter) cmp(a, b []byte) int {
|
||||
return m.heap.cmp(a, b)
|
||||
}
|
||||
|
||||
func (m *MergingIter) findNextFragmentSet() Span {
|
||||
func (m *MergingIter) findNextFragmentSet() *Span {
|
||||
// Each iteration of this loop considers a new merged span between unique
|
||||
// user keys. An iteration may find that there exists no overlap for a given
|
||||
// span, (eg, if the spans [a,b), [d, e) exist within level iterators, the
|
||||
@@ -601,16 +699,16 @@ func (m *MergingIter) findNextFragmentSet() Span {
|
||||
// we elide empty spans created by the mergingIter itself that don't overlap
|
||||
// with any child iterator returned spans (i.e. empty spans that bridge two
|
||||
// distinct child-iterator-defined spans).
|
||||
if found, s := m.synthesizeKeys(+1); found && s.Valid() {
|
||||
if found, s := m.synthesizeKeys(+1); found && s != nil {
|
||||
return s
|
||||
}
|
||||
}
|
||||
// Exhausted.
|
||||
m.clear()
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MergingIter) findPrevFragmentSet() Span {
|
||||
func (m *MergingIter) findPrevFragmentSet() *Span {
|
||||
// Each iteration of this loop considers a new merged span between unique
|
||||
// user keys. An iteration may find that there exists no overlap for a given
|
||||
// span, (eg, if the spans [a,b), [d, e) exist within level iterators, the
|
||||
@@ -677,13 +775,13 @@ func (m *MergingIter) findPrevFragmentSet() Span {
|
||||
// we elide empty spans created by the mergingIter itself that don't overlap
|
||||
// with any child iterator returned spans (i.e. empty spans that bridge two
|
||||
// distinct child-iterator-defined spans).
|
||||
if found, s := m.synthesizeKeys(-1); found && s.Valid() {
|
||||
if found, s := m.synthesizeKeys(-1); found && s != nil {
|
||||
return s
|
||||
}
|
||||
}
|
||||
// Exhausted.
|
||||
m.clear()
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MergingIter) heapRoot() []byte {
|
||||
@@ -703,7 +801,7 @@ func (m *MergingIter) heapRoot() []byte {
|
||||
//
|
||||
// The boolean return value, `found`, is true if the returned span overlaps
|
||||
// with a span returned by a child iterator.
|
||||
func (m *MergingIter) synthesizeKeys(dir int8) (bool, Span) {
|
||||
func (m *MergingIter) synthesizeKeys(dir int8) (bool, *Span) {
|
||||
if invariants.Enabled {
|
||||
if m.cmp(m.start, m.end) >= 0 {
|
||||
panic(fmt.Sprintf("pebble: invariant violation: span start ≥ end: %s >= %s", m.start, m.end))
|
||||
@@ -719,19 +817,26 @@ func (m *MergingIter) synthesizeKeys(dir int8) (bool, Span) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
// TODO(jackson): We should be able to remove this sort and instead
|
||||
// guarantee that we'll return keys in the order of the levels they're from.
|
||||
// With careful iterator construction, this would guarantee that they're
|
||||
// sorted by trailer descending for the range key iteration use case.
|
||||
sort.Sort(&m.keys)
|
||||
|
||||
// Apply the configured transform. See VisibleTransform.
|
||||
// Apply the configured transform. See visibleTransform.
|
||||
s := Span{
|
||||
Start: m.start,
|
||||
End: m.end,
|
||||
Keys: m.keys,
|
||||
Start: m.start,
|
||||
End: m.end,
|
||||
Keys: m.keys,
|
||||
KeysOrder: ByTrailerDesc,
|
||||
}
|
||||
if err := m.transform(m.cmp, s, &m.span); err != nil {
|
||||
// NB: m.heap.cmp is a base.Compare, whereas m.cmp is a method on
|
||||
// MergingIter.
|
||||
if err := m.transformer.Transform(m.heap.cmp, s, &m.span); err != nil {
|
||||
m.err = err
|
||||
return false, Span{}
|
||||
return false, nil
|
||||
}
|
||||
return found, m.span
|
||||
return found, &m.span
|
||||
}
|
||||
|
||||
func (m *MergingIter) invalidate() {
|
||||
@@ -885,28 +990,6 @@ func (h *mergingIterHeap) down(i0, n int) bool {
|
||||
return i > i0
|
||||
}
|
||||
|
||||
func startBound(s Span) boundKey {
|
||||
if !s.Valid() {
|
||||
return boundKey{kind: boundKindInvalid}
|
||||
}
|
||||
return boundKey{
|
||||
kind: boundKindFragmentStart,
|
||||
key: s.Start,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
|
||||
func endBound(s Span) boundKey {
|
||||
if !s.Valid() {
|
||||
return boundKey{kind: boundKindInvalid}
|
||||
}
|
||||
return boundKey{
|
||||
kind: boundKindFragmentEnd,
|
||||
key: s.End,
|
||||
span: s,
|
||||
}
|
||||
}
|
||||
|
||||
type boundKind int8
|
||||
|
||||
const (
|
||||
@@ -922,7 +1005,7 @@ type boundKey struct {
|
||||
//
|
||||
// If kind is boundKindFragmentStart, then key is span.Start. If kind is
|
||||
// boundKindFragmentEnd, then key is span.End.
|
||||
span Span
|
||||
span *Span
|
||||
}
|
||||
|
||||
func (k boundKey) valid() bool {
|
||||
|
||||
40
vendor/github.com/cockroachdb/pebble/internal/keyspan/seek.go
generated
vendored
40
vendor/github.com/cockroachdb/pebble/internal/keyspan/seek.go
generated
vendored
@@ -6,13 +6,9 @@ package keyspan
|
||||
|
||||
import "github.com/cockroachdb/pebble/internal/base"
|
||||
|
||||
// TODO(jackson): Move snapshot visiblity outside of SeekGE/SeekLE and into the
|
||||
// callsite. This should simplify the code some more.
|
||||
|
||||
// SeekGE seeks to the span that contains the target key or the first span past
|
||||
// the target key. The snapshot parameter controls the visibility of keys (only
|
||||
// spans older than the snapshot sequence number are visible).
|
||||
func SeekGE(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64) Span {
|
||||
// the target key.
|
||||
func SeekGE(cmp base.Compare, iter FragmentIterator, key []byte) *Span {
|
||||
// NB: We use SeekLT in order to land on the proper span for a search
|
||||
// key that resides in the middle of a span. Consider the scenario:
|
||||
//
|
||||
@@ -29,27 +25,17 @@ func SeekGE(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64
|
||||
|
||||
// Invariant: key > iterSpan.Start
|
||||
|
||||
if !iterSpan.Valid() || cmp(key, iterSpan.End) >= 0 {
|
||||
if iterSpan == nil || cmp(key, iterSpan.End) >= 0 {
|
||||
// The current span lies entirely before the search key, or the iterator
|
||||
// is exhausted. Advance the iterator to the next span which is
|
||||
// guaranteed to lie at or past the search key.
|
||||
iterSpan = iter.Next()
|
||||
}
|
||||
// The iterator is positioned at a valid iterSpan which is the first
|
||||
// iterator position that satisfies the requirement that it contains or is
|
||||
// past the target key. It may not be visible based on the snapshot. So now
|
||||
// we only need to move forward and return the first span with visible keys.
|
||||
iterSpan = iterSpan.Visible(snapshot)
|
||||
for iterSpan.Valid() && iterSpan.Empty() {
|
||||
iterSpan = iter.Next().Visible(snapshot)
|
||||
}
|
||||
return iterSpan
|
||||
}
|
||||
|
||||
// SeekLE seeks to the span that contains or is before the target key. The
|
||||
// snapshot parameter controls the visibility of keys (only spans older than the
|
||||
// snapshot sequence number are visible).
|
||||
func SeekLE(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64) Span {
|
||||
// SeekLE seeks to the span that contains or is before the target key.
|
||||
func SeekLE(cmp base.Compare, iter FragmentIterator, key []byte) *Span {
|
||||
// NB: We use SeekLT in order to land on the proper span for a search
|
||||
// key that resides in the middle of a span. Consider the scenario:
|
||||
//
|
||||
@@ -64,13 +50,13 @@ func SeekLE(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64
|
||||
// and we'll have to move forward.
|
||||
iterSpan := iter.SeekLT(key)
|
||||
|
||||
if !iterSpan.Valid() {
|
||||
if iterSpan == nil {
|
||||
// Advance the iterator once to see if the next span has a start key
|
||||
// equal to key.
|
||||
iterSpan = iter.Next()
|
||||
if !iterSpan.Valid() || cmp(key, iterSpan.Start) < 0 {
|
||||
if iterSpan == nil || cmp(key, iterSpan.Start) < 0 {
|
||||
// The iterator is exhausted or we've hit the next span.
|
||||
return Span{}
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// Invariant: key > iterSpan.Start
|
||||
@@ -79,20 +65,12 @@ func SeekLE(cmp base.Compare, iter FragmentIterator, key []byte, snapshot uint64
|
||||
// the next span contains the search key. If it doesn't, we'll backup
|
||||
// and return to our earlier candidate.
|
||||
iterSpan = iter.Next()
|
||||
if !iterSpan.Valid() || cmp(key, iterSpan.Start) < 0 {
|
||||
if iterSpan == nil || cmp(key, iterSpan.Start) < 0 {
|
||||
// The next span is past our search key or there is no next span. Go
|
||||
// back.
|
||||
iterSpan = iter.Prev()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're positioned at a span that contains or is before the search key. It
|
||||
// may not be visible based on the snapshot. So now we only need to move
|
||||
// backward and return the first span with visible keys.
|
||||
iterSpan = iterSpan.Visible(snapshot)
|
||||
for iterSpan.Valid() && iterSpan.Empty() {
|
||||
iterSpan = iter.Prev().Visible(snapshot)
|
||||
}
|
||||
return iterSpan
|
||||
}
|
||||
|
||||
233
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
233
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package keyspan // import "github.com/cockroachdb/pebble/internal/keyspan"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -30,16 +31,30 @@ type Span struct {
|
||||
// non-nil, or both nil if representing an invalid Span.
|
||||
Start, End []byte
|
||||
// Keys holds the set of keys applied over the [Start, End) user key range.
|
||||
// Keys is sorted by (SeqNum, Kind) descending. If SeqNum and Kind are
|
||||
// equal, the order of Keys is undefined. Keys may be empty, even if Start
|
||||
// and End are non-nil.
|
||||
// Keys is sorted by (SeqNum, Kind) descending, unless otherwise specified
|
||||
// by the context. If SeqNum and Kind are equal, the order of Keys is
|
||||
// undefined. Keys may be empty, even if Start and End are non-nil.
|
||||
//
|
||||
// Keys are a decoded representation of the internal keys stored in batches
|
||||
// or sstable blocks. A single internal key in a range key block may produce
|
||||
// several decoded Keys.
|
||||
Keys []Key
|
||||
Keys []Key
|
||||
KeysOrder KeysOrder
|
||||
}
|
||||
|
||||
// KeysOrder describes the ordering of Keys within a Span.
|
||||
type KeysOrder int8
|
||||
|
||||
const (
|
||||
// ByTrailerDesc indicates a Span's keys are sorted by Trailer descending.
|
||||
// This is the default ordering, and the ordering used during physical
|
||||
// storage.
|
||||
ByTrailerDesc KeysOrder = iota
|
||||
// BySuffixAsc indicates a Span's keys are sorted by Suffix ascending. This
|
||||
// ordering is used during user iteration of range keys.
|
||||
BySuffixAsc
|
||||
)
|
||||
|
||||
// Key represents a single key applied over a span of user keys. A Key is
|
||||
// contained by a Span which specifies the span of user keys over which the Key
|
||||
// is applied.
|
||||
@@ -65,8 +80,17 @@ func (k Key) Kind() base.InternalKeyKind {
|
||||
return base.InternalKeyKind(k.Trailer & 0xff)
|
||||
}
|
||||
|
||||
// Equal returns true if this Key is equal to the given key. Two keys are said
|
||||
// to be equal if the two Keys have equal trailers, suffix and value. Suffix
|
||||
// comparison uses the provided base.Compare func. Value comparison is bytewise.
|
||||
func (k Key) Equal(equal base.Equal, b Key) bool {
|
||||
return k.Trailer == b.Trailer &&
|
||||
equal(k.Suffix, b.Suffix) &&
|
||||
bytes.Equal(k.Value, b.Value)
|
||||
}
|
||||
|
||||
// Valid returns true if the span is defined.
|
||||
func (s Span) Valid() bool {
|
||||
func (s *Span) Valid() bool {
|
||||
return s.Start != nil && s.End != nil
|
||||
}
|
||||
|
||||
@@ -75,15 +99,18 @@ func (s Span) Valid() bool {
|
||||
//
|
||||
// An Empty span may be produced by Visible, or be produced by iterators in
|
||||
// order to surface the gaps between keys.
|
||||
func (s Span) Empty() bool {
|
||||
return len(s.Keys) == 0
|
||||
func (s *Span) Empty() bool {
|
||||
return s == nil || len(s.Keys) == 0
|
||||
}
|
||||
|
||||
// SmallestKey returns the smallest internal key defined by the span's keys.
|
||||
// It panics if the span contains no keys.
|
||||
func (s Span) SmallestKey() base.InternalKey {
|
||||
// It requires the Span's keys be in ByTrailerDesc order. It panics if the span
|
||||
// contains no keys or its keys are sorted in a different order.
|
||||
func (s *Span) SmallestKey() base.InternalKey {
|
||||
if len(s.Keys) == 0 {
|
||||
panic("pebble: Span contains no keys")
|
||||
} else if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
// The first key has the highest (sequence number,kind) tuple.
|
||||
return base.InternalKey{
|
||||
@@ -98,10 +125,13 @@ func (s Span) SmallestKey() base.InternalKey {
|
||||
// with the maximal sequence number, ensuring all InternalKeys with the same
|
||||
// user key sort after the sentinel key.
|
||||
//
|
||||
// It panics if the span contains no keys.
|
||||
func (s Span) LargestKey() base.InternalKey {
|
||||
// It requires the Span's keys be in ByTrailerDesc order. It panics if the span
|
||||
// contains no keys or its keys are sorted in a different order.
|
||||
func (s *Span) LargestKey() base.InternalKey {
|
||||
if len(s.Keys) == 0 {
|
||||
panic("pebble: Span contains no keys")
|
||||
} else if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
// The last key has the lowest (sequence number,kind) tuple.
|
||||
kind := s.Keys[len(s.Keys)-1].Kind()
|
||||
@@ -109,43 +139,144 @@ func (s Span) LargestKey() base.InternalKey {
|
||||
}
|
||||
|
||||
// SmallestSeqNum returns the smallest sequence number of a key contained within
|
||||
// the span. It panics if the span contains no keys.
|
||||
func (s Span) SmallestSeqNum() uint64 {
|
||||
// the span. It requires the Span's keys be in ByTrailerDesc order. It panics if
|
||||
// the span contains no keys or its keys are sorted in a different order.
|
||||
func (s *Span) SmallestSeqNum() uint64 {
|
||||
if len(s.Keys) == 0 {
|
||||
panic("pebble: Span contains no keys")
|
||||
} else if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
|
||||
return s.Keys[len(s.Keys)-1].SeqNum()
|
||||
}
|
||||
|
||||
// LargestSeqNum returns the largest sequence number of a key contained within
|
||||
// the span. It panics if the span contains no keys.
|
||||
func (s Span) LargestSeqNum() uint64 {
|
||||
// the span. It requires the Span's keys be in ByTrailerDesc order. It panics if
|
||||
// the span contains no keys or its keys are sorted in a different order.
|
||||
func (s *Span) LargestSeqNum() uint64 {
|
||||
if len(s.Keys) == 0 {
|
||||
panic("pebble: Span contains no keys")
|
||||
} else if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
return s.Keys[0].SeqNum()
|
||||
}
|
||||
|
||||
// TODO(jackson): Replace most of the calls to Visible with more targeted calls
|
||||
// that avoid the need to construct a new Span.
|
||||
|
||||
// Visible returns a span with the subset of keys visible at the provided
|
||||
// sequence number.
|
||||
func (s Span) Visible(seqNum uint64) Span {
|
||||
// sequence number. It requires the Span's keys be in ByTrailerDesc order. It
|
||||
// panics if the span's keys are sorted in a different order.
|
||||
//
|
||||
// Visible may incur an allocation, so callers should prefer targeted,
|
||||
// non-allocating methods when possible.
|
||||
func (s Span) Visible(snapshot uint64) Span {
|
||||
if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
|
||||
ret := Span{Start: s.Start, End: s.End}
|
||||
if len(s.Keys) == 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Keys from indexed batches may force an allocation. The Keys slice is
|
||||
// ordered by sequence number, so ordinarily we can return the trailing
|
||||
// subslice containing keys with sequence numbers less than `seqNum`.
|
||||
//
|
||||
// However, batch keys are special. Only visible batch keys are included
|
||||
// when an Iterator's batch spans are fragmented. They must always be
|
||||
// visible.
|
||||
//
|
||||
// Batch keys can create a sandwich of visible batch keys at the beginning
|
||||
// of the slice and visible committed keys at the end of the slice, forcing
|
||||
// us to allocate a new slice and copy the contents.
|
||||
//
|
||||
// Care is taking to only incur an allocation only when batch keys and
|
||||
// visible keys actually sandwich non-visible keys.
|
||||
|
||||
// lastBatchIdx and lastNonVisibleIdx are set to the last index of a batch
|
||||
// key and a non-visible key respectively.
|
||||
lastBatchIdx := -1
|
||||
lastNonVisibleIdx := -1
|
||||
for i := range s.Keys {
|
||||
if base.Visible(s.Keys[i].SeqNum(), seqNum) {
|
||||
ret.Keys = s.Keys[i:]
|
||||
break
|
||||
if seqNum := s.Keys[i].SeqNum(); seqNum&base.InternalKeySeqNumBatch != 0 {
|
||||
// Batch key. Always visible.
|
||||
lastBatchIdx = i
|
||||
} else if seqNum >= snapshot {
|
||||
// This key is not visible.
|
||||
lastNonVisibleIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
// In the following comments: b = batch, h = hidden, v = visible (committed).
|
||||
switch {
|
||||
case lastNonVisibleIdx == -1:
|
||||
// All keys are visible.
|
||||
//
|
||||
// [b b b], [v v v] and [b b b v v v]
|
||||
ret.Keys = s.Keys
|
||||
case lastBatchIdx == -1:
|
||||
// There are no batch keys, so we can return the continuous subslice
|
||||
// starting after the last non-visible Key.
|
||||
//
|
||||
// h h h [v v v]
|
||||
ret.Keys = s.Keys[lastNonVisibleIdx+1:]
|
||||
case lastNonVisibleIdx == len(s.Keys)-1:
|
||||
// While we have a batch key and non-visible keys, there are no
|
||||
// committed visible keys. The 'sandwich' is missing the bottom layer,
|
||||
// so we can return the continuous sublice at the beginning.
|
||||
//
|
||||
// [b b b] h h h
|
||||
ret.Keys = s.Keys[0 : lastBatchIdx+1]
|
||||
default:
|
||||
// This is the problematic sandwich case. Allocate a new slice, copying
|
||||
// the batch keys and the visible keys into it.
|
||||
//
|
||||
// [b b b] h h h [v v v]
|
||||
ret.Keys = make([]Key, (lastBatchIdx+1)+(len(s.Keys)-lastNonVisibleIdx-1))
|
||||
copy(ret.Keys, s.Keys[:lastBatchIdx+1])
|
||||
copy(ret.Keys[lastBatchIdx+1:], s.Keys[lastNonVisibleIdx+1:])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// VisibleAt returns true if the span contains a key visible at the provided
|
||||
// snapshot. Keys with sequence numbers with the batch bit set are treated as
|
||||
// always visible.
|
||||
//
|
||||
// VisibleAt requires the Span's keys be in ByTrailerDesc order. It panics if
|
||||
// the span's keys are sorted in a different order.
|
||||
func (s *Span) VisibleAt(snapshot uint64) bool {
|
||||
if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
if len(s.Keys) == 0 {
|
||||
return false
|
||||
} else if first := s.Keys[0].SeqNum(); first&base.InternalKeySeqNumBatch != 0 {
|
||||
// Only visible batch keys are included when an Iterator's batch spans
|
||||
// are fragmented. They must always be visible.
|
||||
return true
|
||||
} else {
|
||||
// Otherwise we check the last key. Since keys are ordered decreasing in
|
||||
// sequence number, the last key has the lowest sequence number of any
|
||||
// of the span's keys. If any of the keys are visible, the last key must
|
||||
// be visible. Or put differently: if the last key is not visible, then
|
||||
// no key is visible.
|
||||
return s.Keys[len(s.Keys)-1].SeqNum() < snapshot
|
||||
}
|
||||
}
|
||||
|
||||
// ShallowClone returns the span with a Keys slice owned by the span itself.
|
||||
// None of the key byte slices are cloned (see Span.DeepClone).
|
||||
func (s Span) ShallowClone() Span {
|
||||
func (s *Span) ShallowClone() Span {
|
||||
c := Span{
|
||||
Start: s.Start,
|
||||
End: s.End,
|
||||
Keys: make([]Key, len(s.Keys)),
|
||||
Start: s.Start,
|
||||
End: s.End,
|
||||
Keys: make([]Key, len(s.Keys)),
|
||||
KeysOrder: s.KeysOrder,
|
||||
}
|
||||
copy(c.Keys, s.Keys)
|
||||
return c
|
||||
@@ -154,11 +285,12 @@ func (s Span) ShallowClone() Span {
|
||||
// DeepClone clones the span, creating copies of all contained slices. DeepClone
|
||||
// is intended for non-production code paths like tests, the level checker, etc
|
||||
// because it is allocation heavy.
|
||||
func (s Span) DeepClone() Span {
|
||||
func (s *Span) DeepClone() Span {
|
||||
c := Span{
|
||||
Start: make([]byte, len(s.Start)),
|
||||
End: make([]byte, len(s.End)),
|
||||
Keys: make([]Key, len(s.Keys)),
|
||||
Start: make([]byte, len(s.Start)),
|
||||
End: make([]byte, len(s.End)),
|
||||
Keys: make([]Key, len(s.Keys)),
|
||||
KeysOrder: s.KeysOrder,
|
||||
}
|
||||
copy(c.Start, s.Start)
|
||||
copy(c.End, s.End)
|
||||
@@ -177,15 +309,48 @@ func (s Span) DeepClone() Span {
|
||||
}
|
||||
|
||||
// Contains returns true if the specified key resides within the span's bounds.
|
||||
func (s Span) Contains(cmp base.Compare, key []byte) bool {
|
||||
func (s *Span) Contains(cmp base.Compare, key []byte) bool {
|
||||
return cmp(s.Start, key) <= 0 && cmp(key, s.End) < 0
|
||||
}
|
||||
|
||||
// Covers returns true if the span covers keys at seqNum.
|
||||
//
|
||||
// Covers requires the Span's keys be in ByTrailerDesc order. It panics if the
|
||||
// span's keys are sorted in a different order.
|
||||
func (s Span) Covers(seqNum uint64) bool {
|
||||
if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
return !s.Empty() && s.Keys[0].SeqNum() > seqNum
|
||||
}
|
||||
|
||||
// CoversAt returns true if the span contains a key that is visible at the
|
||||
// provided snapshot sequence number, and that key's sequence number is higher
|
||||
// than seqNum.
|
||||
//
|
||||
// Keys with sequence numbers with the batch bit set are treated as always
|
||||
// visible.
|
||||
//
|
||||
// CoversAt requires the Span's keys be in ByTrailerDesc order. It panics if the
|
||||
// span's keys are sorted in a different order.
|
||||
func (s *Span) CoversAt(snapshot, seqNum uint64) bool {
|
||||
if s.KeysOrder != ByTrailerDesc {
|
||||
panic("pebble: span's keys unexpectedly not in trailer order")
|
||||
}
|
||||
// NB: A key is visible at `snapshot` if its sequence number is strictly
|
||||
// less than `snapshot`. See base.Visible.
|
||||
for i := range s.Keys {
|
||||
if kseq := s.Keys[i].SeqNum(); kseq&base.InternalKeySeqNumBatch != 0 {
|
||||
// Only visible batch keys are included when an Iterator's batch spans
|
||||
// are fragmented. They must always be visible.
|
||||
return kseq > seqNum
|
||||
} else if kseq < snapshot {
|
||||
return kseq > seqNum
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns a string representation of the span.
|
||||
func (s Span) String() string {
|
||||
return fmt.Sprint(prettySpan{Span: s, formatKey: base.DefaultFormatter})
|
||||
@@ -223,10 +388,12 @@ func (s prettySpan) Format(fs fmt.State, c rune) {
|
||||
fmt.Fprintf(fs, "}")
|
||||
}
|
||||
|
||||
// SortKeys sorts a keys slice by trailer.
|
||||
func SortKeys(keys []Key) {
|
||||
sorted := keysBySeqNumKind(keys)
|
||||
sort.Sort(&sorted)
|
||||
// SortKeysByTrailer sorts a keys slice by trailer.
|
||||
func SortKeysByTrailer(keys *[]Key) {
|
||||
// NB: keys is a pointer to a slice instead of a slice to avoid `sorted`
|
||||
// escaping to the heap.
|
||||
sorted := (*keysBySeqNumKind)(keys)
|
||||
sort.Sort(sorted)
|
||||
}
|
||||
|
||||
// ParseSpan parses the string representation of a Span. It's intended for
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/internal/keyspan/truncate.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/internal/keyspan/truncate.go
generated
vendored
@@ -12,7 +12,7 @@ import "github.com/cockroachdb/pebble/internal/base"
|
||||
func Truncate(
|
||||
cmp base.Compare, iter FragmentIterator, lower, upper []byte, start, end *base.InternalKey,
|
||||
) FragmentIterator {
|
||||
return Filter(iter, func(in Span, out *Span) (keep bool) {
|
||||
return Filter(iter, func(in *Span, out *Span) (keep bool) {
|
||||
out.Start, out.End = in.Start, in.End
|
||||
out.Keys = append(out.Keys[:0], in.Keys...)
|
||||
|
||||
|
||||
32
vendor/github.com/cockroachdb/pebble/internal/manifest/l0_sublevels.go
generated
vendored
32
vendor/github.com/cockroachdb/pebble/internal/manifest/l0_sublevels.go
generated
vendored
@@ -714,7 +714,17 @@ func (s *L0Sublevels) InitCompactingFileInfo(inProgress []L0Compaction) {
|
||||
|
||||
iter := s.levelMetadata.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
if !f.Compacting {
|
||||
if invariants.Enabled {
|
||||
if !bytes.Equal(s.orderedIntervals[f.minIntervalIndex].startKey.key, f.Smallest.UserKey) {
|
||||
panic(fmt.Sprintf("f.minIntervalIndex in FileMetadata out of sync with intervals in L0Sublevels: %s != %s",
|
||||
s.formatKey(s.orderedIntervals[f.minIntervalIndex].startKey.key), s.formatKey(f.Smallest.UserKey)))
|
||||
}
|
||||
if !bytes.Equal(s.orderedIntervals[f.maxIntervalIndex+1].startKey.key, f.Largest.UserKey) {
|
||||
panic(fmt.Sprintf("f.maxIntervalIndex in FileMetadata out of sync with intervals in L0Sublevels: %s != %s",
|
||||
s.formatKey(s.orderedIntervals[f.maxIntervalIndex+1].startKey.key), s.formatKey(f.Smallest.UserKey)))
|
||||
}
|
||||
}
|
||||
if !f.IsCompacting() {
|
||||
continue
|
||||
}
|
||||
for i := f.minIntervalIndex; i <= f.maxIntervalIndex; i++ {
|
||||
@@ -795,7 +805,7 @@ func (s *L0Sublevels) describe(verbose bool) string {
|
||||
}
|
||||
sumIntervals += intervals
|
||||
totalBytes += f.Size
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
numCompactingFiles++
|
||||
}
|
||||
}
|
||||
@@ -1042,7 +1052,7 @@ func (s *L0Sublevels) checkCompaction(c *L0CompactionFiles) error {
|
||||
fmt.Fprintf(&buf, "bug %t, seed interval: %d: level %d, sl index %d, f.index %d, min %d, max %d, pre-min %d, pre-max %d, f.min %d, f.max %d, filenum: %d, isCompacting: %t\n%s\n",
|
||||
c.isIntraL0, c.seedInterval, level, index, f.L0Index, min, max, c.preExtensionMinInterval, c.preExtensionMaxInterval,
|
||||
f.minIntervalIndex, f.maxIntervalIndex,
|
||||
f.FileNum, f.Compacting, s)
|
||||
f.FileNum, f.IsCompacting(), s)
|
||||
fmt.Fprintf(&buf, "files included:\n")
|
||||
for _, f := range c.Files {
|
||||
fmt.Fprintf(&buf, "filenum: %d, sl: %d, index: %d, [%d, %d]\n",
|
||||
@@ -1386,7 +1396,7 @@ func (s *L0Sublevels) PickBaseCompaction(
|
||||
return nil, errors.New("no seed file found in sublevel intervals")
|
||||
}
|
||||
consideredIntervals.markBits(f.minIntervalIndex, f.maxIntervalIndex+1)
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
if f.IsIntraL0Compacting {
|
||||
// If we're picking a base compaction and we came across a
|
||||
// seed file candidate that's being intra-L0 compacted, skip
|
||||
@@ -1418,7 +1428,7 @@ func (s *L0Sublevels) PickBaseCompaction(
|
||||
if cmp > 0 || (cmp == 0 && !s.orderedIntervals[c.maxIntervalIndex+1].startKey.isLargest) {
|
||||
break
|
||||
}
|
||||
baseCompacting = baseCompacting || m.Compacting
|
||||
baseCompacting = baseCompacting || m.IsCompacting()
|
||||
}
|
||||
if baseCompacting {
|
||||
continue
|
||||
@@ -1539,7 +1549,7 @@ func (s *L0Sublevels) extendFiles(
|
||||
if f.minIntervalIndex > cFiles.maxIntervalIndex {
|
||||
break
|
||||
}
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
return false
|
||||
}
|
||||
// Skip over files that are newer than earliestUnflushedSeqNum. This is
|
||||
@@ -1590,7 +1600,7 @@ func (s *L0Sublevels) PickIntraL0Compaction(
|
||||
stackDepthReduction := scoredInterval.score
|
||||
for i := len(interval.files) - 1; i >= 0; i-- {
|
||||
f = interval.files[i]
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
break
|
||||
}
|
||||
consideredIntervals.markBits(f.minIntervalIndex, f.maxIntervalIndex+1)
|
||||
@@ -1614,7 +1624,7 @@ func (s *L0Sublevels) PickIntraL0Compaction(
|
||||
if f == nil {
|
||||
return nil, errors.New("no seed file found in sublevel intervals")
|
||||
}
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
// This file could be in a concurrent intra-L0 or base compaction.
|
||||
// Try another interval.
|
||||
continue
|
||||
@@ -1666,7 +1676,7 @@ func (s *L0Sublevels) intraL0CompactionUsingSeed(
|
||||
for ; slIndex >= 0; slIndex-- {
|
||||
f2 := interval.files[slIndex]
|
||||
sl := f2.SubLevel
|
||||
if f2.Compacting {
|
||||
if f2.IsCompacting() {
|
||||
break
|
||||
}
|
||||
c.seedIntervalStackDepthReduction++
|
||||
@@ -1936,7 +1946,7 @@ func (s *L0Sublevels) extendCandidateToRectangle(
|
||||
candidateHasAlreadyPickedFiles := false
|
||||
for index = firstIndex; index <= lastIndex; index++ {
|
||||
f := files[index]
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
if nonCompactingFirst != -1 {
|
||||
last := index - 1
|
||||
// Prioritize runs of consecutive non-compacting files that
|
||||
@@ -1988,7 +1998,7 @@ func (s *L0Sublevels) extendCandidateToRectangle(
|
||||
}
|
||||
for index := candidateNonCompactingFirst; index <= candidateNonCompactingLast; index++ {
|
||||
f := files[index]
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
// TODO(bilal): Do a logger.Fatalf instead of a panic, for
|
||||
// cleaner unwinding and error messages.
|
||||
panic(fmt.Sprintf("expected %s to not be compacting", f.FileNum))
|
||||
|
||||
30
vendor/github.com/cockroachdb/pebble/internal/manifest/level_metadata.go
generated
vendored
30
vendor/github.com/cockroachdb/pebble/internal/manifest/level_metadata.go
generated
vendored
@@ -481,6 +481,21 @@ func (i *LevelIterator) SeekGE(cmp Compare, userKey []byte) *FileMetadata {
|
||||
meta := i.seek(func(m *FileMetadata) bool {
|
||||
return cmp(m.Largest.UserKey, userKey) >= 0
|
||||
})
|
||||
for meta != nil {
|
||||
switch i.filter {
|
||||
case KeyTypePointAndRange:
|
||||
return meta
|
||||
case KeyTypePoint:
|
||||
if meta.HasPointKeys && cmp(meta.LargestPointKey.UserKey, userKey) >= 0 {
|
||||
return meta
|
||||
}
|
||||
case KeyTypeRange:
|
||||
if meta.HasRangeKeys && cmp(meta.LargestRangeKey.UserKey, userKey) >= 0 {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
meta = i.Next()
|
||||
}
|
||||
return i.filteredNextFile(meta)
|
||||
}
|
||||
|
||||
@@ -497,6 +512,21 @@ func (i *LevelIterator) SeekLT(cmp Compare, userKey []byte) *FileMetadata {
|
||||
return cmp(m.Smallest.UserKey, userKey) >= 0
|
||||
})
|
||||
meta := i.Prev()
|
||||
for meta != nil {
|
||||
switch i.filter {
|
||||
case KeyTypePointAndRange:
|
||||
return meta
|
||||
case KeyTypePoint:
|
||||
if meta.HasPointKeys && cmp(meta.SmallestPointKey.UserKey, userKey) < 0 {
|
||||
return meta
|
||||
}
|
||||
case KeyTypeRange:
|
||||
if meta.HasRangeKeys && cmp(meta.SmallestRangeKey.UserKey, userKey) < 0 {
|
||||
return meta
|
||||
}
|
||||
}
|
||||
meta = i.Prev()
|
||||
}
|
||||
return i.filteredPrevFile(meta)
|
||||
}
|
||||
|
||||
|
||||
130
vendor/github.com/cockroachdb/pebble/internal/manifest/version.go
generated
vendored
130
vendor/github.com/cockroachdb/pebble/internal/manifest/version.go
generated
vendored
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/vfs"
|
||||
)
|
||||
|
||||
@@ -43,15 +44,12 @@ type TableInfo struct {
|
||||
|
||||
// TableStats contains statistics on a table used for compaction heuristics.
|
||||
type TableStats struct {
|
||||
// Valid true if stats have been loaded for the table. The rest of the
|
||||
// structure is populated only if true.
|
||||
Valid bool
|
||||
// The total number of entries in the table.
|
||||
NumEntries uint64
|
||||
// The number of point and range deletion entries in the table.
|
||||
NumDeletions uint64
|
||||
// NumRangeKeys is the total number of range keys in the table.
|
||||
NumRangeKeys uint64
|
||||
// NumRangeKeySets is the total number of range key sets in the table.
|
||||
NumRangeKeySets uint64
|
||||
// Estimate of the total disk space that may be dropped by this table's
|
||||
// point deletions by compacting them.
|
||||
PointDeletionsBytesEstimate uint64
|
||||
@@ -77,6 +75,48 @@ const (
|
||||
boundTypeRangeKey
|
||||
)
|
||||
|
||||
// CompactionState is the compaction state of a file.
|
||||
//
|
||||
// The following shows the valid state transitions:
|
||||
//
|
||||
// NotCompacting --> Compacting --> Compacted
|
||||
// ^ |
|
||||
// | |
|
||||
// +-------<-------+
|
||||
//
|
||||
// Input files to a compaction transition to Compacting when a compaction is
|
||||
// picked. A file that has finished compacting typically transitions into the
|
||||
// Compacted state, at which point it is effectively obsolete ("zombied") and
|
||||
// will eventually be removed from the LSM. A file that has been move-compacted
|
||||
// will transition from Compacting back into the NotCompacting state, signaling
|
||||
// that the file may be selected for a subsequent compaction. A failed
|
||||
// compaction will result in all input tables transitioning from Compacting to
|
||||
// NotCompacting.
|
||||
//
|
||||
// This state is in-memory only. It is not persisted to the manifest.
|
||||
type CompactionState uint8
|
||||
|
||||
// CompactionStates.
|
||||
const (
|
||||
CompactionStateNotCompacting CompactionState = iota
|
||||
CompactionStateCompacting
|
||||
CompactionStateCompacted
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (s CompactionState) String() string {
|
||||
switch s {
|
||||
case CompactionStateNotCompacting:
|
||||
return "NotCompacting"
|
||||
case CompactionStateCompacting:
|
||||
return "Compacting"
|
||||
case CompactionStateCompacted:
|
||||
return "Compacted"
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unknown compaction state %d", s))
|
||||
}
|
||||
}
|
||||
|
||||
// FileMetadata holds the metadata for an on-disk table.
|
||||
type FileMetadata struct {
|
||||
// Atomic contains fields which are accessed atomically. Go allocations
|
||||
@@ -90,6 +130,10 @@ type FileMetadata struct {
|
||||
// in pebble.Iterator after every after every positioning operation
|
||||
// that returns a user key (eg. Next, Prev, SeekGE, SeekLT, etc).
|
||||
AllowedSeeks int64
|
||||
|
||||
// statsValid is 1 if stats have been loaded for the table. The
|
||||
// TableStats structure is populated only if valid is 1.
|
||||
statsValid uint32
|
||||
}
|
||||
|
||||
// InitAllowedSeeks is the inital value of allowed seeks. This is used
|
||||
@@ -146,12 +190,11 @@ type FileMetadata struct {
|
||||
// pick L0 compactions. Only accurate for the most recent Version.
|
||||
//
|
||||
// IsIntraL0Compacting is set to True if this file is part of an intra-L0
|
||||
// compaction. When it's true, Compacting must also be true. If Compacting
|
||||
// is true and IsIntraL0Compacting is false for an L0 file, the file must
|
||||
// be part of a compaction to Lbase.
|
||||
// compaction. When it's true, IsCompacting must also return true. If
|
||||
// Compacting is true and IsIntraL0Compacting is false for an L0 file, the
|
||||
// file must be part of a compaction to Lbase.
|
||||
IsIntraL0Compacting bool
|
||||
// True if the file is actively being compacted. Protected by DB.mu.
|
||||
Compacting bool
|
||||
CompactionState CompactionState
|
||||
// True if compaction of this file has been explicitly requested.
|
||||
// Previously, RocksDB and earlier versions of Pebble allowed this
|
||||
// flag to be set by a user table property collector. Some earlier
|
||||
@@ -169,9 +212,11 @@ type FileMetadata struct {
|
||||
//
|
||||
// Protected by DB.mu.
|
||||
MarkedForCompaction bool
|
||||
// HasPointKeys and HasRangeKeys track whether the table contains point and
|
||||
// range keys, respectively.
|
||||
// HasPointKeys tracks whether the table contains point keys (including
|
||||
// RANGEDELs). If a table contains only range deletions, HasPointsKeys is
|
||||
// still true.
|
||||
HasPointKeys bool
|
||||
// HasRangeKeys tracks whether the table contains any range keys.
|
||||
HasRangeKeys bool
|
||||
// smallestSet and largestSet track whether the overall bounds have been set.
|
||||
boundsSet bool
|
||||
@@ -181,6 +226,59 @@ type FileMetadata struct {
|
||||
boundTypeSmallest, boundTypeLargest boundType
|
||||
}
|
||||
|
||||
// SetCompactionState transitions this file's compaction state to the given
|
||||
// state. Protected by DB.mu.
|
||||
func (m *FileMetadata) SetCompactionState(to CompactionState) {
|
||||
if invariants.Enabled {
|
||||
transitionErr := func() error {
|
||||
return errors.Newf("pebble: invalid compaction state transition: %s -> %s", m.CompactionState, to)
|
||||
}
|
||||
switch m.CompactionState {
|
||||
case CompactionStateNotCompacting:
|
||||
if to != CompactionStateCompacting {
|
||||
panic(transitionErr())
|
||||
}
|
||||
case CompactionStateCompacting:
|
||||
if to != CompactionStateCompacted && to != CompactionStateNotCompacting {
|
||||
panic(transitionErr())
|
||||
}
|
||||
case CompactionStateCompacted:
|
||||
panic(transitionErr())
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unknown compaction state: %d", m.CompactionState))
|
||||
}
|
||||
}
|
||||
m.CompactionState = to
|
||||
}
|
||||
|
||||
// IsCompacting returns true if this file's compaction state is
|
||||
// CompactionStateCompacting. Protected by DB.mu.
|
||||
func (m *FileMetadata) IsCompacting() bool {
|
||||
return m.CompactionState == CompactionStateCompacting
|
||||
}
|
||||
|
||||
// StatsValid returns true if the table stats have been populated. If StatValid
|
||||
// returns true, the Stats field may be read (with or without holding the
|
||||
// database mutex).
|
||||
func (m *FileMetadata) StatsValid() bool {
|
||||
return atomic.LoadUint32(&m.Atomic.statsValid) == 1
|
||||
}
|
||||
|
||||
// StatsValidLocked returns true if the table stats have been populated.
|
||||
// StatsValidLocked requires DB.mu is held when it's invoked, and it avoids the
|
||||
// overhead of an atomic load. This is possible because table stats validity is
|
||||
// only set while DB.mu is held.
|
||||
func (m *FileMetadata) StatsValidLocked() bool {
|
||||
return m.Atomic.statsValid == 1
|
||||
}
|
||||
|
||||
// StatsMarkValid marks the TableStats as valid. The caller must hold DB.mu
|
||||
// while populating TableStats and calling StatsMarkValud. Once stats are
|
||||
// populated, they must not be mutated.
|
||||
func (m *FileMetadata) StatsMarkValid() {
|
||||
atomic.StoreUint32(&m.Atomic.statsValid, 1)
|
||||
}
|
||||
|
||||
// ExtendPointKeyBounds attempts to extend the lower and upper point key bounds
|
||||
// and overall table bounds with the given smallest and largest keys. The
|
||||
// smallest and largest bounds may not be extended if the table already has a
|
||||
@@ -654,6 +752,11 @@ type Version struct {
|
||||
|
||||
Levels [NumLevels]LevelMetadata
|
||||
|
||||
// RangeKeyLevels holds a subset of the same files as Levels that contain range
|
||||
// keys (i.e. fileMeta.HasRangeKeys == true). The memory amplification of this
|
||||
// duplication should be minimal, as range keys are expected to be rare.
|
||||
RangeKeyLevels [NumLevels]LevelMetadata
|
||||
|
||||
// The callback to invoke when the last reference to a version is
|
||||
// removed. Will be called with list.mu held.
|
||||
Deleted func(obsolete []*FileMetadata)
|
||||
@@ -787,6 +890,9 @@ func (v *Version) unrefFiles() []*FileMetadata {
|
||||
for _, lm := range v.Levels {
|
||||
obsolete = append(obsolete, lm.release()...)
|
||||
}
|
||||
for _, lm := range v.RangeKeyLevels {
|
||||
obsolete = append(obsolete, lm.release()...)
|
||||
}
|
||||
return obsolete
|
||||
}
|
||||
|
||||
|
||||
22
vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go
generated
vendored
22
vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go
generated
vendored
@@ -648,6 +648,11 @@ func (b *BulkVersionEdit) Apply(
|
||||
} else {
|
||||
v.Levels[level] = curr.Levels[level].clone()
|
||||
}
|
||||
if curr == nil || curr.RangeKeyLevels[level].tree.root == nil {
|
||||
v.RangeKeyLevels[level] = makeLevelMetadata(cmp, level, nil /* files */)
|
||||
} else {
|
||||
v.RangeKeyLevels[level] = curr.RangeKeyLevels[level].clone()
|
||||
}
|
||||
|
||||
if len(b.Added[level]) == 0 && len(b.Deleted[level]) == 0 {
|
||||
// There are no edits on this level.
|
||||
@@ -667,6 +672,7 @@ func (b *BulkVersionEdit) Apply(
|
||||
|
||||
// Some edits on this level.
|
||||
lm := &v.Levels[level]
|
||||
lmRange := &v.RangeKeyLevels[level]
|
||||
addedFiles := b.Added[level]
|
||||
deletedMap := b.Deleted[level]
|
||||
if n := v.Levels[level].Len() + len(addedFiles); n == 0 {
|
||||
@@ -688,6 +694,16 @@ func (b *BulkVersionEdit) Apply(
|
||||
err := errors.Errorf("pebble: internal error: file L%d.%s obsolete during B-Tree removal", level, f.FileNum)
|
||||
return nil, nil, err
|
||||
}
|
||||
if f.HasRangeKeys {
|
||||
if obsolete := v.RangeKeyLevels[level].tree.delete(f); obsolete {
|
||||
// Deleting a file from the B-Tree may decrement its
|
||||
// reference count. However, because we cloned the
|
||||
// previous level's B-Tree, this should never result in a
|
||||
// file's reference count dropping to zero.
|
||||
err := errors.Errorf("pebble: internal error: file L%d.%s obsolete during range-key B-Tree removal", level, f.FileNum)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sm, la *FileMetadata
|
||||
@@ -714,6 +730,12 @@ func (b *BulkVersionEdit) Apply(
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "pebble")
|
||||
}
|
||||
if f.HasRangeKeys {
|
||||
err = lmRange.tree.insert(f)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "pebble")
|
||||
}
|
||||
}
|
||||
removeZombie(f.FileNum)
|
||||
// Track the keys with the smallest and largest keys, so that we can
|
||||
// check consistency of the modified span.
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/internal/rangedel/rangedel.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/internal/rangedel/rangedel.go
generated
vendored
@@ -13,7 +13,7 @@ import (
|
||||
// closure with the encoded internal keys that represent the Span's state. The
|
||||
// keys and values passed to emit are only valid until the closure returns. If
|
||||
// emit returns an error, Encode stops and returns the error.
|
||||
func Encode(s keyspan.Span, emit func(k base.InternalKey, v []byte) error) error {
|
||||
func Encode(s *keyspan.Span, emit func(k base.InternalKey, v []byte) error) error {
|
||||
for _, k := range s.Keys {
|
||||
if k.Kind() != base.InternalKeyKindRangeDelete {
|
||||
return base.CorruptionErrorf("pebble: rangedel.Encode cannot encode %s key", k.Kind())
|
||||
|
||||
270
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
270
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
@@ -11,117 +11,162 @@ import (
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
)
|
||||
|
||||
// InitUserIteration initializes the range key iterator stack for user
|
||||
// iteration. The resulting fragment iterator applies range key semantics,
|
||||
// defragments spans according to their user-observable state and removes all
|
||||
// Keys other than RangeKeySets describing the current state of range keys.
|
||||
// UserIteratorConfig holds state for constructing the range key iterator stack
|
||||
// for user iteration.
|
||||
type UserIteratorConfig struct {
|
||||
snapshot uint64
|
||||
comparer *base.Comparer
|
||||
miter keyspan.MergingIter
|
||||
biter keyspan.BoundedIter
|
||||
diter keyspan.DefragmentingIter
|
||||
liters [manifest.NumLevels]keyspan.LevelIter
|
||||
litersUsed int
|
||||
sortBuf keysBySuffix
|
||||
}
|
||||
|
||||
// Init initializes the range key iterator stack for user iteration. The
|
||||
// resulting fragment iterator applies range key semantics, defragments spans
|
||||
// according to their user-observable state and removes all Keys other than
|
||||
// RangeKeySets describing the current state of range keys. The resulting spans
|
||||
// contain Keys sorted by Suffix.
|
||||
//
|
||||
// The snapshot sequence number parameter determines which keys are visible. Any
|
||||
// keys not visible at the provided snapshot are ignored.
|
||||
func InitUserIteration(
|
||||
cmp base.Compare,
|
||||
func (ui *UserIteratorConfig) Init(
|
||||
comparer *base.Comparer,
|
||||
snapshot uint64,
|
||||
miter *keyspan.MergingIter,
|
||||
diter *keyspan.DefragmentingIter,
|
||||
levelIters ...keyspan.FragmentIterator,
|
||||
lower, upper []byte,
|
||||
hasPrefix *bool,
|
||||
prefix *[]byte,
|
||||
iters ...keyspan.FragmentIterator,
|
||||
) keyspan.FragmentIterator {
|
||||
miter.Init(cmp, userIterationTransform(snapshot), levelIters...)
|
||||
diter.Init(cmp, miter, userIterationDefragmenter(), keyspan.StaticDefragmentReducer)
|
||||
return diter
|
||||
ui.snapshot = snapshot
|
||||
ui.comparer = comparer
|
||||
ui.miter.Init(comparer.Compare, ui, iters...)
|
||||
ui.biter.Init(comparer.Compare, comparer.Split, &ui.miter, lower, upper, hasPrefix, prefix)
|
||||
ui.diter.Init(comparer, &ui.biter, ui, keyspan.StaticDefragmentReducer)
|
||||
ui.litersUsed = 0
|
||||
return &ui.diter
|
||||
}
|
||||
|
||||
// userIterationTransform returns a keyspan.Transform for use with a
|
||||
// keyspan.MergingIter that transforms spans by resolving range keys at the
|
||||
// AddLevel adds a new level to the bottom of the iterator stack. AddLevel
|
||||
// must be called after Init and before any other method on the iterator.
|
||||
func (ui *UserIteratorConfig) AddLevel(iter keyspan.FragmentIterator) {
|
||||
ui.miter.AddLevel(iter)
|
||||
}
|
||||
|
||||
// NewLevelIter returns a pointer to a newly allocated or reused
|
||||
// keyspan.LevelIter. The caller is responsible for calling Init() on this
|
||||
// instance.
|
||||
func (ui *UserIteratorConfig) NewLevelIter() *keyspan.LevelIter {
|
||||
if ui.litersUsed >= len(ui.liters) {
|
||||
return &keyspan.LevelIter{}
|
||||
}
|
||||
ui.litersUsed++
|
||||
return &ui.liters[ui.litersUsed-1]
|
||||
}
|
||||
|
||||
// SetBounds propagates bounds to the iterator stack. The fragment iterator
|
||||
// interface ordinarily doesn't enforce bounds, so this is exposed as an
|
||||
// explicit method on the user iterator config.
|
||||
func (ui *UserIteratorConfig) SetBounds(lower, upper []byte) {
|
||||
ui.biter.SetBounds(lower, upper)
|
||||
}
|
||||
|
||||
// Transform implements the keyspan.Transformer interface for use with a
|
||||
// keyspan.MergingIter. It transforms spans by resolving range keys at the
|
||||
// provided snapshot sequence number. Shadowing of keys is resolved (eg, removal
|
||||
// of unset keys, removal of keys overwritten by a set at the same suffix, etc)
|
||||
// and then non-RangeKeySet keys are removed. The resulting transformed spans
|
||||
// only contain RangeKeySets describing the state visible at the provided
|
||||
// sequence number.
|
||||
func userIterationTransform(snapshot uint64) keyspan.Transform {
|
||||
return func(cmp base.Compare, s keyspan.Span, dst *keyspan.Span) error {
|
||||
// Apply shadowing of keys.
|
||||
if err := Coalesce(cmp, s.Visible(snapshot), dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// During user iteration over range keys, unsets and deletes don't
|
||||
// matter. Remove them. This step helps logical defragmentation during
|
||||
// iteration.
|
||||
keys := dst.Keys
|
||||
dst.Keys = dst.Keys[:0]
|
||||
for i := range keys {
|
||||
switch keys[i].Kind() {
|
||||
case base.InternalKeyKindRangeKeySet:
|
||||
dst.Keys = append(dst.Keys, keys[i])
|
||||
case base.InternalKeyKindRangeKeyUnset:
|
||||
// Skip.
|
||||
continue
|
||||
case base.InternalKeyKindRangeKeyDelete:
|
||||
// Skip.
|
||||
continue
|
||||
default:
|
||||
return base.CorruptionErrorf("pebble: unrecognized range key kind %s", keys[i].Kind())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// sequence number, and hold their Keys sorted by Suffix.
|
||||
func (ui *UserIteratorConfig) Transform(cmp base.Compare, s keyspan.Span, dst *keyspan.Span) error {
|
||||
// Apply shadowing of keys.
|
||||
dst.Start = s.Start
|
||||
dst.End = s.End
|
||||
ui.sortBuf = keysBySuffix{
|
||||
cmp: cmp,
|
||||
keys: dst.Keys[:0],
|
||||
}
|
||||
if err := coalesce(&ui.sortBuf, s.Visible(ui.snapshot).Keys, &dst.Keys); err != nil {
|
||||
return err
|
||||
}
|
||||
// During user iteration over range keys, unsets and deletes don't
|
||||
// matter. Remove them. This step helps logical defragmentation during
|
||||
// iteration.
|
||||
keys := dst.Keys
|
||||
dst.Keys = dst.Keys[:0]
|
||||
for i := range keys {
|
||||
switch keys[i].Kind() {
|
||||
case base.InternalKeyKindRangeKeySet:
|
||||
if invariants.Enabled && len(dst.Keys) > 0 && cmp(dst.Keys[len(dst.Keys)-1].Suffix, keys[i].Suffix) > 0 {
|
||||
panic("pebble: keys unexpectedly not in ascending suffix order")
|
||||
}
|
||||
dst.Keys = append(dst.Keys, keys[i])
|
||||
case base.InternalKeyKindRangeKeyUnset:
|
||||
if invariants.Enabled && len(dst.Keys) > 0 && cmp(dst.Keys[len(dst.Keys)-1].Suffix, keys[i].Suffix) > 0 {
|
||||
panic("pebble: keys unexpectedly not in ascending suffix order")
|
||||
}
|
||||
// Skip.
|
||||
continue
|
||||
case base.InternalKeyKindRangeKeyDelete:
|
||||
// Skip.
|
||||
continue
|
||||
default:
|
||||
return base.CorruptionErrorf("pebble: unrecognized range key kind %s", keys[i].Kind())
|
||||
}
|
||||
}
|
||||
// coalesce results in dst.Keys being sorted by Suffix.
|
||||
dst.KeysOrder = keyspan.BySuffixAsc
|
||||
return nil
|
||||
}
|
||||
|
||||
// userIterationDefragmenter constructs a DefragmentMethod that configures a
|
||||
// ShouldDefragment implements the DefragmentMethod interface and configures a
|
||||
// DefragmentingIter to defragment spans of range keys if their user-visible
|
||||
// state is identical. This defragmenting method assumes the provided spans have
|
||||
// already been transformed through UserIterationTransform, so all RangeKeySets
|
||||
// are user-visible sets. This defragmenter checks for equality between set
|
||||
// suffixes and values (ignoring sequence numbers). It's intended for use during
|
||||
// user iteration, when the wrapped keyspan iterator is merging spans across all
|
||||
// levels of the LSM.
|
||||
//
|
||||
// The returned defragmenting method is stateful, and must not be used on
|
||||
// multiple DefragmentingIters concurrently.
|
||||
func userIterationDefragmenter() keyspan.DefragmentMethod {
|
||||
var bufA keysBySuffix
|
||||
var bufB keysBySuffix
|
||||
return func(cmp base.Compare, a, b keyspan.Span) bool {
|
||||
// UserIterationDefragmenter must only be used on spans that have
|
||||
// transformed by UserIterationTransform. The transform applies
|
||||
// shadowing and removes all keys besides the resulting Sets. Since
|
||||
// shadowing has been applied, each Set must set a unique suffix. If the
|
||||
// two spans are equivalent, they must have the same number of range key
|
||||
// sets.
|
||||
if len(a.Keys) != len(b.Keys) {
|
||||
return false
|
||||
}
|
||||
// already been transformed through (UserIterationConfig).Transform, so all
|
||||
// RangeKeySets are user-visible sets and are already in Suffix order. This
|
||||
// defragmenter checks for equality between set suffixes and values (ignoring
|
||||
// sequence numbers). It's intended for use during user iteration, when the
|
||||
// wrapped keyspan iterator is merging spans across all levels of the LSM.
|
||||
func (ui *UserIteratorConfig) ShouldDefragment(equal base.Equal, a, b *keyspan.Span) bool {
|
||||
// This implementation must only be used on spans that have transformed by
|
||||
// ui.Transform. The transform applies shadowing, removes all keys besides
|
||||
// the resulting Sets and sorts the keys by suffix. Since shadowing has been
|
||||
// applied, each Set must set a unique suffix. If the two spans are
|
||||
// equivalent, they must have the same number of range key sets.
|
||||
if len(a.Keys) != len(b.Keys) || len(a.Keys) == 0 {
|
||||
return false
|
||||
}
|
||||
if a.KeysOrder != keyspan.BySuffixAsc || b.KeysOrder != keyspan.BySuffixAsc {
|
||||
panic("pebble: range key span's keys unexpectedly not in ascending suffix order")
|
||||
}
|
||||
|
||||
// The keys in both spans must be all sets. In order to be equivalent
|
||||
// during user iteration, they must set identical suffix-value mappings.
|
||||
// They do not need to have the same sequence number.
|
||||
//
|
||||
// Currently, the Keys are sorted by Trailer (as per the keyspan.Span
|
||||
// and keyspan.FragmentIterator interfaces). Collect all the keys in
|
||||
// both spans into buffers and sort them by suffix for comparison.
|
||||
bufA.cmp = cmp
|
||||
bufA.keys = append(bufA.keys[:0], a.Keys...)
|
||||
sort.Sort(bufA)
|
||||
bufB.cmp = cmp
|
||||
bufB.keys = append(bufB.keys[:0], b.Keys...)
|
||||
sort.Sort(bufB)
|
||||
|
||||
for i := range bufA.keys {
|
||||
if bufA.keys[i].Kind() != base.InternalKeyKindRangeKeySet ||
|
||||
bufB.keys[i].Kind() != base.InternalKeyKindRangeKeySet {
|
||||
ret := true
|
||||
for i := range a.Keys {
|
||||
if invariants.Enabled {
|
||||
if a.Keys[i].Kind() != base.InternalKeyKindRangeKeySet ||
|
||||
b.Keys[i].Kind() != base.InternalKeyKindRangeKeySet {
|
||||
panic("pebble: unexpected non-RangeKeySet during defragmentation")
|
||||
}
|
||||
if cmp(bufA.keys[i].Suffix, bufB.keys[i].Suffix) != 0 {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(bufA.keys[i].Value, bufB.keys[i].Value) {
|
||||
return false
|
||||
if i > 0 && (ui.comparer.Compare(a.Keys[i].Suffix, a.Keys[i-1].Suffix) < 0 ||
|
||||
ui.comparer.Compare(b.Keys[i].Suffix, b.Keys[i-1].Suffix) < 0) {
|
||||
panic("pebble: range keys not ordered by suffix during defragmentation")
|
||||
}
|
||||
}
|
||||
return true
|
||||
if !equal(a.Keys[i].Suffix, b.Keys[i].Suffix) {
|
||||
ret = false
|
||||
break
|
||||
}
|
||||
if !bytes.Equal(a.Keys[i].Value, b.Keys[i].Value) {
|
||||
ret = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Coalesce imposes range key semantics and coalesces range keys with the same
|
||||
@@ -157,18 +202,28 @@ func userIterationDefragmenter() keyspan.DefragmentMethod {
|
||||
// keys do not affect one another. Ingested sstables are expected to be
|
||||
// consistent with respect to the set/unset suffixes: A given suffix should be
|
||||
// set or unset but not both.
|
||||
func Coalesce(cmp base.Compare, span keyspan.Span, dst *keyspan.Span) error {
|
||||
//
|
||||
// The resulting dst Keys slice is sorted by Trailer.
|
||||
func Coalesce(cmp base.Compare, keys []keyspan.Key, dst *[]keyspan.Key) error {
|
||||
// TODO(jackson): Currently, Coalesce doesn't actually perform the sequence
|
||||
// number promotion described in the comment above.
|
||||
|
||||
keysBySuffix := keysBySuffix{
|
||||
cmp: cmp,
|
||||
keys: dst.Keys[:0],
|
||||
keys: (*dst)[:0],
|
||||
}
|
||||
if err := coalesce(&keysBySuffix, keys, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
// coalesce left the keys in *dst sorted by suffix. Re-sort them by trailer.
|
||||
keyspan.SortKeysByTrailer(dst)
|
||||
return nil
|
||||
}
|
||||
|
||||
func coalesce(keysBySuffix *keysBySuffix, keys []keyspan.Key, dst *[]keyspan.Key) error {
|
||||
var deleted bool
|
||||
for i := 0; i < len(span.Keys) && !deleted; i++ {
|
||||
k := span.Keys[i]
|
||||
if invariants.Enabled && i > 0 && k.Trailer > span.Keys[i-1].Trailer {
|
||||
for i := 0; i < len(keys) && !deleted; i++ {
|
||||
k := keys[i]
|
||||
if invariants.Enabled && i > 0 && k.Trailer > keys[i-1].Trailer {
|
||||
panic("pebble: invariant violation: span keys unordered")
|
||||
}
|
||||
|
||||
@@ -210,26 +265,13 @@ func Coalesce(cmp base.Compare, span keyspan.Span, dst *keyspan.Span) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the span with the (potentially reduced) keys slice, and re-sort it
|
||||
// by Trailer.
|
||||
*dst = keyspan.Span{
|
||||
Start: span.Start,
|
||||
End: span.End,
|
||||
Keys: keysBySuffix.keys,
|
||||
}
|
||||
keyspan.SortKeys(dst.Keys)
|
||||
// Update the span with the (potentially reduced) keys slice.
|
||||
// NB: We don't re-sort by Trailer. The exported Coalesce function however
|
||||
// will.
|
||||
*dst = keysBySuffix.keys
|
||||
return nil
|
||||
}
|
||||
|
||||
// SortBySuffix sorts the provided keys by suffix.
|
||||
func SortBySuffix(cmp base.Compare, keys []keyspan.Key) {
|
||||
bySuffix := keysBySuffix{
|
||||
cmp: cmp,
|
||||
keys: keys,
|
||||
}
|
||||
sort.Sort(bySuffix)
|
||||
}
|
||||
|
||||
type keysBySuffix struct {
|
||||
cmp base.Compare
|
||||
keys []keyspan.Key
|
||||
@@ -238,7 +280,7 @@ type keysBySuffix struct {
|
||||
// get searches for suffix among the first n keys in keys. If the suffix is
|
||||
// found, it returns the index of the item with the suffix. If the suffix is not
|
||||
// found, it returns n.
|
||||
func (s keysBySuffix) get(n int, suffix []byte) (i int) {
|
||||
func (s *keysBySuffix) get(n int, suffix []byte) (i int) {
|
||||
// Binary search for the suffix to see if there's an existing key with the
|
||||
// suffix. Only binary search among the first n items. get is called while
|
||||
// appending new keys with suffixes that may sort before existing keys.
|
||||
@@ -254,6 +296,6 @@ func (s keysBySuffix) get(n int, suffix []byte) (i int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (s keysBySuffix) Len() int { return len(s.keys) }
|
||||
func (s keysBySuffix) Less(i, j int) bool { return s.cmp(s.keys[i].Suffix, s.keys[j].Suffix) < 0 }
|
||||
func (s keysBySuffix) Swap(i, j int) { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] }
|
||||
func (s *keysBySuffix) Len() int { return len(s.keys) }
|
||||
func (s *keysBySuffix) Less(i, j int) bool { return s.cmp(s.keys[i].Suffix, s.keys[j].Suffix) < 0 }
|
||||
func (s *keysBySuffix) Swap(i, j int) { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] }
|
||||
|
||||
6
vendor/github.com/cockroachdb/pebble/internal/rangekey/rangekey.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/internal/rangekey/rangekey.go
generated
vendored
@@ -61,7 +61,7 @@ import (
|
||||
// closure with the encoded internal keys that represent the Span's state. The
|
||||
// keys and values passed to emit are only valid until the closure returns.
|
||||
// If emit returns an error, Encode stops and returns the error.
|
||||
func Encode(s keyspan.Span, emit func(k base.InternalKey, v []byte) error) error {
|
||||
func Encode(s *keyspan.Span, emit func(k base.InternalKey, v []byte) error) error {
|
||||
enc := Encoder{Emit: emit}
|
||||
return enc.Encode(s)
|
||||
}
|
||||
@@ -82,7 +82,7 @@ type Encoder struct {
|
||||
//
|
||||
// The encoded key-value pair passed to Emit is only valid until the closure
|
||||
// completes.
|
||||
func (e *Encoder) Encode(s keyspan.Span) error {
|
||||
func (e *Encoder) Encode(s *keyspan.Span) error {
|
||||
if s.Empty() {
|
||||
return nil
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func (e *Encoder) Encode(s keyspan.Span) error {
|
||||
|
||||
// flush constructs internal keys for accumulated key state, and emits the
|
||||
// internal keys.
|
||||
func (e *Encoder) flush(s keyspan.Span, seqNum uint64, del bool) error {
|
||||
func (e *Encoder) flush(s *keyspan.Span, seqNum uint64, del bool) error {
|
||||
if len(e.sets) > 0 {
|
||||
ik := base.MakeInternalKey(s.Start, seqNum, base.InternalKeyKindRangeKeySet)
|
||||
l := EncodedSetValueLen(s.End, e.sets)
|
||||
|
||||
121
vendor/github.com/cockroachdb/pebble/internal/testkeys/strconv.go
generated
vendored
Normal file
121
vendor/github.com/cockroachdb/pebble/internal/testkeys/strconv.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright 2013 The Perkeep Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testkeys
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
)
|
||||
|
||||
// parseUintBytes is like strconv.ParseUint, but using a []byte. Use of this
|
||||
// function avoids an allocation when parsing an integer out of a []byte.
|
||||
//
|
||||
// This function is copied from go4.org/strconv.
|
||||
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
|
||||
var cutoff, maxVal uint64
|
||||
|
||||
if bitSize == 0 {
|
||||
bitSize = int(strconv.IntSize)
|
||||
}
|
||||
|
||||
s0 := s
|
||||
switch {
|
||||
case len(s) < 1:
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
|
||||
case 2 <= base && base <= 36:
|
||||
// valid base; nothing to do
|
||||
|
||||
case base == 0:
|
||||
// Look for octal, hex prefix.
|
||||
switch {
|
||||
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
|
||||
base = 16
|
||||
s = s[2:]
|
||||
if len(s) < 1 {
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
case s[0] == '0':
|
||||
base = 8
|
||||
default:
|
||||
base = 10
|
||||
}
|
||||
|
||||
default:
|
||||
err = errors.New("invalid base " + strconv.Itoa(base))
|
||||
goto Error
|
||||
}
|
||||
|
||||
n = 0
|
||||
cutoff = cutoff64(base)
|
||||
maxVal = 1<<uint(bitSize) - 1
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
var v byte
|
||||
d := s[i]
|
||||
switch {
|
||||
case '0' <= d && d <= '9':
|
||||
v = d - '0'
|
||||
case 'a' <= d && d <= 'z':
|
||||
v = d - 'a' + 10
|
||||
case 'A' <= d && d <= 'Z':
|
||||
v = d - 'A' + 10
|
||||
default:
|
||||
n = 0
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
if int(v) >= base {
|
||||
n = 0
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
|
||||
if n >= cutoff {
|
||||
// n*base overflows
|
||||
n = 1<<64 - 1
|
||||
err = strconv.ErrRange
|
||||
goto Error
|
||||
}
|
||||
n *= uint64(base)
|
||||
|
||||
n1 := n + uint64(v)
|
||||
if n1 < n || n1 > maxVal {
|
||||
// n+v overflows
|
||||
n = 1<<64 - 1
|
||||
err = strconv.ErrRange
|
||||
goto Error
|
||||
}
|
||||
n = n1
|
||||
}
|
||||
|
||||
return n, nil
|
||||
|
||||
Error:
|
||||
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
|
||||
}
|
||||
|
||||
// Return the first number n such that n*base >= 1<<64.
|
||||
func cutoff64(base int) uint64 {
|
||||
if base < 2 {
|
||||
return 0
|
||||
}
|
||||
return (1<<64-1)/uint64(base) + 1
|
||||
}
|
||||
349
vendor/github.com/cockroachdb/pebble/internal/testkeys/testkeys.go
generated
vendored
Normal file
349
vendor/github.com/cockroachdb/pebble/internal/testkeys/testkeys.go
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
// 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 testkeys provides facilities for generating and comparing
|
||||
// human-readable test keys for use in tests and benchmarks. This package
|
||||
// provides a single Comparer implementation that compares all keys generated
|
||||
// by this package.
|
||||
//
|
||||
// Keys generated by this package may optionally have a 'suffix' encoding an
|
||||
// MVCC timestamp. This suffix is of the form "@<integer>". Comparisons on the
|
||||
// suffix are performed using integer value, not the byte representation.
|
||||
package testkeys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
)
|
||||
|
||||
const alpha = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
const suffixDelim = '@'
|
||||
|
||||
// MaxSuffixLen is the maximum length of a suffix generated by this package.
|
||||
var MaxSuffixLen = 1 + len(fmt.Sprintf("%d", math.MaxInt64))
|
||||
|
||||
// Comparer is the comparer for test keys generated by this package.
|
||||
var Comparer *base.Comparer = &base.Comparer{
|
||||
Compare: compare,
|
||||
Equal: func(a, b []byte) bool { return compare(a, b) == 0 },
|
||||
AbbreviatedKey: func(k []byte) uint64 {
|
||||
return base.DefaultComparer.AbbreviatedKey(k[:split(k)])
|
||||
},
|
||||
FormatKey: base.DefaultFormatter,
|
||||
Separator: func(dst, a, b []byte) []byte {
|
||||
ai := split(a)
|
||||
if ai == len(a) {
|
||||
return append(dst, a...)
|
||||
}
|
||||
bi := split(b)
|
||||
if bi == len(b) {
|
||||
return append(dst, a...)
|
||||
}
|
||||
|
||||
// If the keys are the same just return a.
|
||||
if bytes.Equal(a[:ai], b[:bi]) {
|
||||
return append(dst, a...)
|
||||
}
|
||||
n := len(dst)
|
||||
dst = base.DefaultComparer.Separator(dst, a[:ai], b[:bi])
|
||||
// Did it pick a separator different than a[:ai] -- if not we can't do better than a.
|
||||
buf := dst[n:]
|
||||
if bytes.Equal(a[:ai], buf) {
|
||||
return append(dst[:n], a...)
|
||||
}
|
||||
// The separator is > a[:ai], so return it
|
||||
return dst
|
||||
},
|
||||
Successor: func(dst, a []byte) []byte {
|
||||
ai := split(a)
|
||||
if ai == len(a) {
|
||||
return append(dst, a...)
|
||||
}
|
||||
n := len(dst)
|
||||
dst = base.DefaultComparer.Successor(dst, a[:ai])
|
||||
// Did it pick a successor different than a[:ai] -- if not we can't do better than a.
|
||||
buf := dst[n:]
|
||||
if bytes.Equal(a[:ai], buf) {
|
||||
return append(dst[:n], a...)
|
||||
}
|
||||
// The successor is > a[:ai], so return it.
|
||||
return dst
|
||||
},
|
||||
ImmediateSuccessor: func(dst, a []byte) []byte {
|
||||
// TODO(jackson): Consider changing this Comparer to only support
|
||||
// representable prefix keys containing characters a-z.
|
||||
ai := split(a)
|
||||
if ai != len(a) {
|
||||
panic("pebble: ImmediateSuccessor invoked with a non-prefix key")
|
||||
}
|
||||
return append(append(dst, a...), 0x00)
|
||||
},
|
||||
Split: split,
|
||||
Name: "pebble.internal.testkeys",
|
||||
}
|
||||
|
||||
func compare(a, b []byte) int {
|
||||
ai, bi := split(a), split(b)
|
||||
if v := bytes.Compare(a[:ai], b[:bi]); v != 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
if len(a[ai:]) == 0 {
|
||||
if len(b[bi:]) == 0 {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
} else if len(b[bi:]) == 0 {
|
||||
return +1
|
||||
}
|
||||
return compareTimestamps(a[ai:], b[bi:])
|
||||
}
|
||||
|
||||
func split(a []byte) int {
|
||||
i := bytes.LastIndexByte(a, suffixDelim)
|
||||
if i >= 0 {
|
||||
return i
|
||||
}
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func compareTimestamps(a, b []byte) int {
|
||||
ai, err := parseUintBytes(bytes.TrimPrefix(a, []byte{suffixDelim}), 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid test mvcc timestamp %q", a))
|
||||
}
|
||||
bi, err := parseUintBytes(bytes.TrimPrefix(b, []byte{suffixDelim}), 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid test mvcc timestamp %q", b))
|
||||
}
|
||||
switch {
|
||||
case ai < bi:
|
||||
return +1
|
||||
case ai > bi:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Keyspace describes a finite keyspace of unsuffixed test keys.
|
||||
type Keyspace interface {
|
||||
// Count returns the number of keys that exist within this keyspace.
|
||||
Count() int
|
||||
|
||||
// MaxLen returns the maximum length, in bytes, of a key within this
|
||||
// keyspace. This is only guaranteed to return an upper bound.
|
||||
MaxLen() int
|
||||
|
||||
// Slice returns the sub-keyspace from index i, inclusive, to index j,
|
||||
// exclusive. The receiver is unmodified.
|
||||
Slice(i, j int) Keyspace
|
||||
|
||||
// EveryN returns a key space that includes 1 key for every N keys in the
|
||||
// original keyspace. The receiver is unmodified.
|
||||
EveryN(n int) Keyspace
|
||||
|
||||
key(buf []byte, i int) int
|
||||
}
|
||||
|
||||
// Divvy divides the provided keyspace into N equal portions, containing
|
||||
// disjoint keys evenly distributed across the keyspace.
|
||||
func Divvy(ks Keyspace, n int) []Keyspace {
|
||||
ret := make([]Keyspace, n)
|
||||
for i := 0; i < n; i++ {
|
||||
ret[i] = ks.Slice(i, ks.Count()).EveryN(n)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Alpha constructs a keyspace consisting of all keys containing characters a-z,
|
||||
// with at most `maxLength` characters.
|
||||
func Alpha(maxLength int) Keyspace {
|
||||
return alphabet{
|
||||
alphabet: []byte(alpha),
|
||||
maxLength: maxLength,
|
||||
increment: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// KeyAt returns the i-th key within the keyspace with a suffix encoding the
|
||||
// timestamp t.
|
||||
func KeyAt(k Keyspace, i int, t int) []byte {
|
||||
b := make([]byte, k.MaxLen()+MaxSuffixLen)
|
||||
return b[:WriteKeyAt(b, k, i, t)]
|
||||
}
|
||||
|
||||
// WriteKeyAt writes the i-th key within the keyspace to the buffer dst, with a
|
||||
// suffix encoding the timestamp t suffix. It returns the number of bytes
|
||||
// written.
|
||||
func WriteKeyAt(dst []byte, k Keyspace, i int, t int) int {
|
||||
n := WriteKey(dst, k, i)
|
||||
n += WriteSuffix(dst[n:], t)
|
||||
return n
|
||||
}
|
||||
|
||||
// Suffix returns the test keys suffix representation of timestamp t.
|
||||
func Suffix(t int) []byte {
|
||||
b := make([]byte, MaxSuffixLen)
|
||||
return b[:WriteSuffix(b, t)]
|
||||
}
|
||||
|
||||
// SuffixLen returns the exact length of the given suffix when encoded.
|
||||
func SuffixLen(t int) int {
|
||||
// Begin at 1 for the '@' delimiter, 1 for a single digit.
|
||||
n := 2
|
||||
t /= 10
|
||||
for t > 0 {
|
||||
t /= 10
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseSuffix returns the integer representation of the encoded suffix.
|
||||
func ParseSuffix(s []byte) (int, error) {
|
||||
return strconv.Atoi(strings.TrimPrefix(string(s), string(suffixDelim)))
|
||||
}
|
||||
|
||||
// WriteSuffix writes the test keys suffix representation of timestamp t to dst,
|
||||
// returning the number of bytes written.
|
||||
func WriteSuffix(dst []byte, t int) int {
|
||||
dst[0] = suffixDelim
|
||||
n := 1
|
||||
n += len(strconv.AppendInt(dst[n:n], int64(t), 10))
|
||||
return n
|
||||
}
|
||||
|
||||
// Key returns the i-th unsuffixed key within the keyspace.
|
||||
func Key(k Keyspace, i int) []byte {
|
||||
b := make([]byte, k.MaxLen())
|
||||
return b[:k.key(b, i)]
|
||||
}
|
||||
|
||||
// WriteKey writes the i-th unsuffixed key within the keyspace to the buffer dst. It
|
||||
// returns the number of bytes written.
|
||||
func WriteKey(dst []byte, k Keyspace, i int) int {
|
||||
return k.key(dst, i)
|
||||
}
|
||||
|
||||
type alphabet struct {
|
||||
alphabet []byte
|
||||
maxLength int
|
||||
headSkip int
|
||||
tailSkip int
|
||||
increment int
|
||||
}
|
||||
|
||||
func (a alphabet) Count() int {
|
||||
// Calculate the total number of keys, ignoring the increment.
|
||||
total := (keyCount(len(a.alphabet), a.maxLength) - a.headSkip - a.tailSkip)
|
||||
|
||||
// The increment dictates that we take every N keys, where N = a.increment.
|
||||
// Consider a total containing the 5 keys:
|
||||
// a b c d e
|
||||
// ^ ^ ^
|
||||
// If the increment is 2, this keyspace includes 'a', 'c' and 'e'. After
|
||||
// dividing by the increment, there may be remainder. If there is, there's
|
||||
// one additional key in the alphabet.
|
||||
count := total / a.increment
|
||||
if total%a.increment > 0 {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (a alphabet) MaxLen() int {
|
||||
return a.maxLength
|
||||
}
|
||||
|
||||
func (a alphabet) Slice(i, j int) Keyspace {
|
||||
s := a
|
||||
s.headSkip += i
|
||||
s.tailSkip += a.Count() - j
|
||||
return s
|
||||
}
|
||||
|
||||
func (a alphabet) EveryN(n int) Keyspace {
|
||||
s := a
|
||||
s.increment *= n
|
||||
return s
|
||||
}
|
||||
|
||||
func keyCount(n, l int) int {
|
||||
// The number of representable keys in the keyspace is a function of the
|
||||
// length of the alphabet n and the max key length l. Consider how the
|
||||
// number of representable keys grows as l increases:
|
||||
//
|
||||
// l = 1: n
|
||||
// l = 2: n + n^2
|
||||
// l = 3: n + n^2 + n^3
|
||||
// ...
|
||||
// Σ i=(1...l) n^i = n*(n^l - 1)/(n-1)
|
||||
return (n * (int(math.Pow(float64(n), float64(l))) - 1)) / (n - 1)
|
||||
}
|
||||
|
||||
func (a alphabet) key(buf []byte, idx int) int {
|
||||
// This function generates keys of length 1..maxKeyLength, pulling
|
||||
// characters from the alphabet. The idx determines which key to generate,
|
||||
// generating the i-th lexicographically next key.
|
||||
//
|
||||
// The index to use is advanced by `headSkip`, allowing a keyspace to encode
|
||||
// a subregion of the keyspace.
|
||||
//
|
||||
// Eg, alphabet = `ab`, maxKeyLength = 3:
|
||||
//
|
||||
// aaa aab aba abb baa bab bba bbb
|
||||
// aa ab ba bb
|
||||
// a b
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
||||
//
|
||||
return generateAlphabetKey(buf, a.alphabet, (idx*a.increment)+a.headSkip,
|
||||
keyCount(len(a.alphabet), a.maxLength))
|
||||
}
|
||||
|
||||
func generateAlphabetKey(buf, alphabet []byte, i, keyCount int) int {
|
||||
if keyCount == 0 || i > keyCount || i < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Of the keyCount keys in the generative keyspace, how many are there
|
||||
// starting with a particular character?
|
||||
keysPerCharacter := keyCount / len(alphabet)
|
||||
|
||||
// Find the character that the key at index i starts with and set it.
|
||||
characterIdx := i / keysPerCharacter
|
||||
buf[0] = alphabet[characterIdx]
|
||||
|
||||
// Consider characterIdx = 0, pointing to 'a'.
|
||||
//
|
||||
// aaa aab aba abb baa bab bba bbb
|
||||
// aa ab ba bb
|
||||
// a b
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
|
||||
// \_________________________/
|
||||
// |keysPerCharacter| keys
|
||||
//
|
||||
// In our recursive call, we reduce the problem to:
|
||||
//
|
||||
// aaa aab aba abb
|
||||
// aa ab
|
||||
// 0 1 2 3 4 5
|
||||
// \________________________/
|
||||
// |keysPerCharacter-1| keys
|
||||
//
|
||||
// In the subproblem, there are keysPerCharacter-1 keys (eliminating the
|
||||
// just 'a' key, plus any keys beginning with any other character).
|
||||
//
|
||||
// The index i is also offset, reduced by the count of keys beginning with
|
||||
// characters earlier in the alphabet (keysPerCharacter*characterIdx) and
|
||||
// the key consisting of just the 'a' (-1).
|
||||
i = i - keysPerCharacter*characterIdx - 1
|
||||
return 1 + generateAlphabetKey(buf[1:], alphabet, i, keysPerCharacter-1)
|
||||
}
|
||||
1090
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
1090
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
File diff suppressed because it is too large
Load Diff
41
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
41
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
@@ -47,13 +47,13 @@ import (
|
||||
|
||||
// The per-level structure used by simpleMergingIter.
|
||||
type simpleMergingIterLevel struct {
|
||||
iter internalIterator
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
smallestUserKey []byte
|
||||
iter internalIterator
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
levelIterBoundaryContext
|
||||
|
||||
iterKey *InternalKey
|
||||
iterValue []byte
|
||||
tombstone keyspan.Span
|
||||
iterValue base.LazyValue
|
||||
tombstone *keyspan.Span
|
||||
}
|
||||
|
||||
type simpleMergingIter struct {
|
||||
@@ -117,7 +117,7 @@ func (m *simpleMergingIter) positionRangeDels() {
|
||||
if l.rangeDelIter == nil {
|
||||
continue
|
||||
}
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey, m.snapshot)
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (m *simpleMergingIter) step() bool {
|
||||
item := &m.heap.items[0]
|
||||
l := &m.levels[item.index]
|
||||
// Sentinels are not relevant for this point checking.
|
||||
if !item.key.IsExclusiveSentinel() && item.key.Visible(m.snapshot) {
|
||||
if !item.key.IsExclusiveSentinel() && item.key.Visible(m.snapshot, base.InternalKeySeqNumMax) {
|
||||
m.numPoints++
|
||||
keyChanged := m.heap.cmp(item.key.UserKey, m.lastKey.UserKey) != 0
|
||||
if !keyChanged {
|
||||
@@ -157,6 +157,11 @@ func (m *simpleMergingIter) step() bool {
|
||||
}
|
||||
m.valueMerger = nil
|
||||
}
|
||||
itemValue, _, err := item.value.Value(nil)
|
||||
if err != nil {
|
||||
m.err = err
|
||||
return false
|
||||
}
|
||||
if m.valueMerger != nil {
|
||||
// Ongoing series of MERGE records.
|
||||
switch item.key.Kind() {
|
||||
@@ -168,7 +173,7 @@ func (m *simpleMergingIter) step() bool {
|
||||
}
|
||||
m.valueMerger = nil
|
||||
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
|
||||
m.err = m.valueMerger.MergeOlder(item.value)
|
||||
m.err = m.valueMerger.MergeOlder(itemValue)
|
||||
if m.err == nil {
|
||||
var closer io.Closer
|
||||
_, closer, m.err = m.valueMerger.Finish(true /* includesBase */)
|
||||
@@ -178,7 +183,7 @@ func (m *simpleMergingIter) step() bool {
|
||||
}
|
||||
m.valueMerger = nil
|
||||
case InternalKeyKindMerge:
|
||||
m.err = m.valueMerger.MergeOlder(item.value)
|
||||
m.err = m.valueMerger.MergeOlder(itemValue)
|
||||
default:
|
||||
m.err = errors.Errorf("pebble: invalid internal key kind %s in %s",
|
||||
item.key.Pretty(m.formatKey),
|
||||
@@ -187,7 +192,7 @@ func (m *simpleMergingIter) step() bool {
|
||||
}
|
||||
} else if item.key.Kind() == InternalKeyKindMerge && m.err == nil {
|
||||
// New series of MERGE records.
|
||||
m.valueMerger, m.err = m.merge(item.key.UserKey, item.value)
|
||||
m.valueMerger, m.err = m.merge(item.key.UserKey, itemValue)
|
||||
}
|
||||
if m.err != nil {
|
||||
m.err = errors.Wrapf(m.err, "merge processing error on key %s in %s",
|
||||
@@ -207,7 +212,7 @@ func (m *simpleMergingIter) step() bool {
|
||||
}
|
||||
if (lvl.smallestUserKey == nil || m.heap.cmp(lvl.smallestUserKey, item.key.UserKey) <= 0) &&
|
||||
lvl.tombstone.Contains(m.heap.cmp, item.key.UserKey) {
|
||||
if lvl.tombstone.Covers(item.key.SeqNum()) {
|
||||
if lvl.tombstone.CoversAt(m.snapshot, item.key.SeqNum()) {
|
||||
m.err = errors.Errorf("tombstone %s in %s deletes key %s in %s",
|
||||
lvl.tombstone.Pretty(m.formatKey), lvl.iter, item.key.Pretty(m.formatKey),
|
||||
l.iter)
|
||||
@@ -373,7 +378,7 @@ func checkRangeTombstones(c *checkConfig) error {
|
||||
lf := files.Take()
|
||||
atomicUnit, _ := expandToAtomicUnit(c.cmp, lf.Slice(), true /* disableIsCompacting */)
|
||||
lower, upper := manifest.KeyRange(c.cmp, atomicUnit.Iter())
|
||||
iterToClose, iter, err := c.newIters(lf.FileMetadata, nil, nil)
|
||||
iterToClose, iter, err := c.newIters(lf.FileMetadata, nil, internalIterOpts{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -452,8 +457,8 @@ func addTombstonesFromIter(
|
||||
}()
|
||||
|
||||
var prevTombstone keyspan.Span
|
||||
for t := iter.First(); t.Valid(); t = iter.Next() {
|
||||
t = t.Visible(seqNum)
|
||||
for tomb := iter.First(); tomb != nil; tomb = iter.Next() {
|
||||
t := tomb.Visible(seqNum)
|
||||
if t.Empty() {
|
||||
continue
|
||||
}
|
||||
@@ -634,9 +639,9 @@ func checkLevelsInternal(c *checkConfig) (err error) {
|
||||
iterOpts := IterOptions{logger: c.logger}
|
||||
li := &levelIter{}
|
||||
li.init(iterOpts, c.cmp, nil /* split */, c.newIters, manifestIter,
|
||||
manifest.L0Sublevel(sublevel), nil)
|
||||
manifest.L0Sublevel(sublevel), internalIterOpts{})
|
||||
li.initRangeDel(&mlevelAlloc[0].rangeDelIter)
|
||||
li.initSmallestLargestUserKey(&mlevelAlloc[0].smallestUserKey, nil, nil)
|
||||
li.initBoundaryContext(&mlevelAlloc[0].levelIterBoundaryContext)
|
||||
mlevelAlloc[0].iter = li
|
||||
mlevelAlloc = mlevelAlloc[1:]
|
||||
}
|
||||
@@ -648,9 +653,9 @@ func checkLevelsInternal(c *checkConfig) (err error) {
|
||||
iterOpts := IterOptions{logger: c.logger}
|
||||
li := &levelIter{}
|
||||
li.init(iterOpts, c.cmp, nil /* split */, c.newIters,
|
||||
current.Levels[level].Iter(), manifest.Level(level), nil)
|
||||
current.Levels[level].Iter(), manifest.Level(level), internalIterOpts{})
|
||||
li.initRangeDel(&mlevelAlloc[0].rangeDelIter)
|
||||
li.initSmallestLargestUserKey(&mlevelAlloc[0].smallestUserKey, nil, nil)
|
||||
li.initBoundaryContext(&mlevelAlloc[0].levelIterBoundaryContext)
|
||||
mlevelAlloc[0].iter = li
|
||||
mlevelAlloc = mlevelAlloc[1:]
|
||||
}
|
||||
|
||||
656
vendor/github.com/cockroachdb/pebble/level_iter.go
generated
vendored
656
vendor/github.com/cockroachdb/pebble/level_iter.go
generated
vendored
@@ -7,20 +7,30 @@ package pebble
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
)
|
||||
|
||||
// tableNewIters creates a new point and range-del iterator for the given file
|
||||
// number. If bytesIterated is specified, it is incremented as the given file is
|
||||
// iterated through.
|
||||
type tableNewIters func(
|
||||
file *manifest.FileMetadata, opts *IterOptions, bytesIterated *uint64,
|
||||
file *manifest.FileMetadata,
|
||||
opts *IterOptions,
|
||||
internalOpts internalIterOpts,
|
||||
) (internalIterator, keyspan.FragmentIterator, error)
|
||||
|
||||
type internalIterOpts struct {
|
||||
bytesIterated *uint64
|
||||
stats *base.InternalIteratorStats
|
||||
boundLimitedFilter sstable.BoundLimitedBlockPropertyFilter
|
||||
}
|
||||
|
||||
// levelIter provides a merged view of the sstables in a level.
|
||||
//
|
||||
// levelIter is used during compaction and as part of the Iterator
|
||||
@@ -62,6 +72,16 @@ type levelIter struct {
|
||||
// (i.e. arrived at with Prev), or largest (arrived at with Next).
|
||||
smallestBoundary *InternalKey
|
||||
largestBoundary *InternalKey
|
||||
// combinedIterState may be set when a levelIter is used during user
|
||||
// iteration. Although levelIter only iterates over point keys, it's also
|
||||
// responsible for lazily constructing the combined range & point iterator
|
||||
// when it observes a file containing range keys. If the combined iter
|
||||
// state's initialized field is true, the iterator is already using combined
|
||||
// iterator, OR the iterator is not configured to use combined iteration. If
|
||||
// it's false, the levelIter must set the `triggered` and `key` fields when
|
||||
// the levelIter passes over a file containing range keys. See the
|
||||
// lazyCombinedIter for more details.
|
||||
combinedIterState *combinedIterState
|
||||
// A synthetic boundary key to return when SeekPrefixGE finds an sstable
|
||||
// which doesn't contain the search key, but which does contain range
|
||||
// tombstones.
|
||||
@@ -71,9 +91,19 @@ type levelIter struct {
|
||||
// - err != nil
|
||||
// - some other constraint, like the bounds in opts, caused the file at index to not
|
||||
// be relevant to the iteration.
|
||||
iter internalIteratorWithStats
|
||||
iter internalIterator
|
||||
iterFile *fileMetadata
|
||||
newIters tableNewIters
|
||||
// filteredIter is an optional interface that may be implemented by internal
|
||||
// iterators that perform filtering of keys. When a new file's iterator is
|
||||
// opened, it's tested to see if it implements filteredIter. If it does,
|
||||
// it's stored here to allow the level iterator to recognize when keys were
|
||||
// omitted from iteration results due to filtering. This is important when a
|
||||
// file contains range deletions that may delete keys from other files. The
|
||||
// levelIter must not advance to the next file until the mergingIter has
|
||||
// advanced beyond the file's bounds. See
|
||||
// levelIterBoundaryContext.isIgnorableBoundaryKey.
|
||||
filteredIter filteredIter
|
||||
newIters tableNewIters
|
||||
// When rangeDelIterPtr != nil, the caller requires that *rangeDelIterPtr must
|
||||
// point to a range del iterator corresponding to the current file. When this
|
||||
// iterator returns nil, *rangeDelIterPtr should also be set to nil. Whenever
|
||||
@@ -87,10 +117,8 @@ type levelIter struct {
|
||||
rangeDelIterCopy keyspan.FragmentIterator
|
||||
files manifest.LevelIterator
|
||||
err error
|
||||
// stats accumulates the stats of iters that have been closed.
|
||||
stats InternalIteratorStats
|
||||
|
||||
// Pointer into this level's entry in `mergingIterLevel::smallestUserKey,largestUserKey`.
|
||||
// Pointer into this level's entry in `mergingIterLevel::levelIterBoundaryContext`.
|
||||
// We populate it with the corresponding bounds for the currently opened file. It is used for
|
||||
// two purposes (described for forward iteration. The explanation for backward iteration is
|
||||
// similar.)
|
||||
@@ -136,17 +164,11 @@ type levelIter struct {
|
||||
// - `*Boundary` is only populated when the iterator is positioned exactly on the sentinel key.
|
||||
// - `*Boundary` can hold either the lower- or upper-bound, depending on the iterator direction.
|
||||
// - `*Boundary` is not exposed to the next higher-level iterator, i.e., `mergingIter`.
|
||||
smallestUserKey, largestUserKey *[]byte
|
||||
isLargestUserKeyRangeDelSentinel *bool
|
||||
boundaryContext *levelIterBoundaryContext
|
||||
|
||||
// Set to true iff the key returned by this iterator is a synthetic key
|
||||
// derived from the iterator bounds. This is used to prevent the
|
||||
// mergingIter from being stuck at such a synthetic key if it becomes the
|
||||
// top element of the heap.
|
||||
isSyntheticIterBoundsKey *bool
|
||||
|
||||
// bytesIterated keeps track of the number of bytes iterated during compaction.
|
||||
bytesIterated *uint64
|
||||
// internalOpts holds the internal iterator options to pass to the table
|
||||
// cache when constructing new table iterators.
|
||||
internalOpts internalIterOpts
|
||||
|
||||
// Disable invariant checks even if they are otherwise enabled. Used by tests
|
||||
// which construct "impossible" situations (e.g. seeking to a key before the
|
||||
@@ -154,6 +176,29 @@ type levelIter struct {
|
||||
disableInvariants bool
|
||||
}
|
||||
|
||||
// filteredIter is an additional interface implemented by iterators that may
|
||||
// skip over point keys during iteration. The sstable.Iterator implements this
|
||||
// interface.
|
||||
type filteredIter interface {
|
||||
// MaybeFilteredKeys may be called when an iterator is exhausted, indicating
|
||||
// whether or not the iterator's last positioning method may have skipped
|
||||
// any keys due to low-level filters.
|
||||
//
|
||||
// When an iterator is configured to use block-property filters, the
|
||||
// low-level iterator may skip over blocks or whole sstables of keys.
|
||||
// Implementations that implement skipping must implement this interface.
|
||||
// Higher-level iterators require it to preserve invariants (eg, a levelIter
|
||||
// used in a mergingIter must keep the file's range-del iterator open until
|
||||
// the mergingIter has moved past the file's bounds, even if all of the
|
||||
// file's point keys were filtered).
|
||||
//
|
||||
// MaybeFilteredKeys may always return false positives, that is it may
|
||||
// return true when no keys were filtered. It should only be called when the
|
||||
// iterator is exhausted. It must never return false negatives when the
|
||||
// iterator is exhausted.
|
||||
MaybeFilteredKeys() bool
|
||||
}
|
||||
|
||||
// levelIter implements the base.InternalIterator interface.
|
||||
var _ base.InternalIterator = (*levelIter)(nil)
|
||||
|
||||
@@ -169,7 +214,7 @@ func newLevelIter(
|
||||
bytesIterated *uint64,
|
||||
) *levelIter {
|
||||
l := &levelIter{}
|
||||
l.init(opts, cmp, split, newIters, files, level, bytesIterated)
|
||||
l.init(opts, cmp, split, newIters, files, level, internalIterOpts{bytesIterated: bytesIterated})
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -180,7 +225,7 @@ func (l *levelIter) init(
|
||||
newIters tableNewIters,
|
||||
files manifest.LevelIterator,
|
||||
level manifest.Level,
|
||||
bytesIterated *uint64,
|
||||
internalOpts internalIterOpts,
|
||||
) {
|
||||
l.err = nil
|
||||
l.level = level
|
||||
@@ -195,51 +240,272 @@ func (l *levelIter) init(
|
||||
l.split = split
|
||||
l.iterFile = nil
|
||||
l.newIters = newIters
|
||||
l.files = files.Filter(manifest.KeyTypePoint)
|
||||
l.bytesIterated = bytesIterated
|
||||
l.files = files
|
||||
l.internalOpts = internalOpts
|
||||
}
|
||||
|
||||
func (l *levelIter) initRangeDel(rangeDelIter *keyspan.FragmentIterator) {
|
||||
l.rangeDelIterPtr = rangeDelIter
|
||||
}
|
||||
|
||||
func (l *levelIter) initSmallestLargestUserKey(
|
||||
smallestUserKey, largestUserKey *[]byte, isLargestUserKeyRangeDelSentinel *bool,
|
||||
) {
|
||||
l.smallestUserKey = smallestUserKey
|
||||
l.largestUserKey = largestUserKey
|
||||
l.isLargestUserKeyRangeDelSentinel = isLargestUserKeyRangeDelSentinel
|
||||
func (l *levelIter) initBoundaryContext(context *levelIterBoundaryContext) {
|
||||
l.boundaryContext = context
|
||||
}
|
||||
|
||||
func (l *levelIter) initIsSyntheticIterBoundsKey(isSyntheticIterBoundsKey *bool) {
|
||||
l.isSyntheticIterBoundsKey = isSyntheticIterBoundsKey
|
||||
func (l *levelIter) initCombinedIterState(state *combinedIterState) {
|
||||
l.combinedIterState = state
|
||||
}
|
||||
|
||||
func (l *levelIter) findFileGE(key []byte) *fileMetadata {
|
||||
// Find the earliest file whose largest key is >= ikey.
|
||||
func (l *levelIter) maybeTriggerCombinedIteration(file *fileMetadata, dir int) {
|
||||
// If we encounter a file that contains range keys, we may need to
|
||||
// trigger a switch to combined range-key and point-key iteration,
|
||||
// if the *pebble.Iterator is configured for it. This switch is done
|
||||
// lazily because range keys are intended to be rare, and
|
||||
// constructing the range-key iterator substantially adds to the
|
||||
// cost of iterator construction and seeking.
|
||||
//
|
||||
// If the earliest file has its largest key == ikey and that largest key is a
|
||||
// range deletion sentinel, we know that we manufactured this sentinel to convert
|
||||
// the exclusive range deletion end key into an inclusive key (reminder: [start, end)#seqnum
|
||||
// is the form of a range deletion sentinel which can contribute a largest key = end#sentinel).
|
||||
// In this case we don't return this as the earliest file since there is nothing actually
|
||||
// equal to key in it.
|
||||
// If l.combinedIterState.initialized is already true, either the
|
||||
// iterator is already using combined iteration or the iterator is not
|
||||
// configured to observe range keys. Either way, there's nothing to do.
|
||||
// If false, trigger the switch to combined iteration, using the the
|
||||
// file's bounds to seek the range-key iterator appropriately.
|
||||
//
|
||||
// Additionally, this prevents loading untruncated range deletions from a table which can't
|
||||
// possibly contain the target key and is required for correctness by mergingIter.SeekGE
|
||||
// (see the comment in that function).
|
||||
// We only need to trigger combined iteration if the file contains
|
||||
// RangeKeySets: if there are only Unsets and Dels, the user will observe no
|
||||
// range keys regardless. If this file has table stats available, they'll
|
||||
// tell us whether the file has any RangeKeySets. Otherwise, we must
|
||||
// fallback to assuming it does if HasRangeKeys=true.
|
||||
if file != nil && file.HasRangeKeys && l.combinedIterState != nil && !l.combinedIterState.initialized &&
|
||||
(l.upper == nil || l.cmp(file.SmallestRangeKey.UserKey, l.upper) < 0) &&
|
||||
(l.lower == nil || l.cmp(file.LargestRangeKey.UserKey, l.lower) > 0) &&
|
||||
(!file.StatsValid() || file.Stats.NumRangeKeySets > 0) {
|
||||
// The file contains range keys, and we're not using combined iteration yet.
|
||||
// Trigger a switch to combined iteration. It's possible that a switch has
|
||||
// already been triggered if multiple levels encounter files containing
|
||||
// range keys while executing a single mergingIter operation. In this case,
|
||||
// we need to compare the existing key recorded to l.combinedIterState.key,
|
||||
// adjusting it if our key is smaller (forward iteration) or larger
|
||||
// (backward iteration) than the existing key.
|
||||
//
|
||||
// These key comparisons are only required during a single high-level
|
||||
// iterator operation. When the high-level iter op completes,
|
||||
// iinitialized will be true, and future calls to this function will be
|
||||
// no-ops.
|
||||
switch dir {
|
||||
case +1:
|
||||
if !l.combinedIterState.triggered {
|
||||
l.combinedIterState.triggered = true
|
||||
l.combinedIterState.key = file.SmallestRangeKey.UserKey
|
||||
} else if l.cmp(l.combinedIterState.key, file.SmallestRangeKey.UserKey) > 0 {
|
||||
l.combinedIterState.key = file.SmallestRangeKey.UserKey
|
||||
}
|
||||
case -1:
|
||||
if !l.combinedIterState.triggered {
|
||||
l.combinedIterState.triggered = true
|
||||
l.combinedIterState.key = file.LargestRangeKey.UserKey
|
||||
} else if l.cmp(l.combinedIterState.key, file.LargestRangeKey.UserKey) < 0 {
|
||||
l.combinedIterState.key = file.LargestRangeKey.UserKey
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := l.files.SeekGE(l.cmp, key)
|
||||
for m != nil && m.LargestPointKey.IsExclusiveSentinel() &&
|
||||
l.cmp(m.LargestPointKey.UserKey, key) == 0 {
|
||||
m = l.files.Next()
|
||||
func (l *levelIter) findFileGE(key []byte, flags base.SeekGEFlags) *fileMetadata {
|
||||
// Find the earliest file whose largest key is >= key.
|
||||
|
||||
if invariants.Enabled && flags.TrySeekUsingNext() && disableSeekOpt(key, uintptr(unsafe.Pointer(l))) {
|
||||
flags = flags.DisableTrySeekUsingNext()
|
||||
}
|
||||
|
||||
// Ordinarily we seek the LevelIterator using SeekGE. In some instances, we
|
||||
// Next instead. In other instances, we try Next-ing first, falling back to
|
||||
// seek:
|
||||
// a) flags.TrySeekUsingNext(): The top-level Iterator knows we're seeking
|
||||
// to a key later than the current iterator position. We don't know how
|
||||
// much later the seek key is, so it's possible there are many sstables
|
||||
// between the current position and the seek key. However in most real-
|
||||
// world use cases, the seek key is likely to be nearby. Rather than
|
||||
// performing a log(N) seek through the file metadata, we next a few
|
||||
// times from from our existing location. If we don't find a file whose
|
||||
// largest is >= key within a few nexts, we fall back to seeking.
|
||||
//
|
||||
// Note that in this case, the file returned by findFileGE may be
|
||||
// different than the file returned by a raw binary search (eg, when
|
||||
// TrySeekUsingNext=false). This is possible because the most recent
|
||||
// positioning operation may have already determined that previous
|
||||
// files' keys that are ≥ key are all deleted. This information is
|
||||
// encoded within the iterator's current iterator position and is
|
||||
// unavailable to a fresh binary search.
|
||||
//
|
||||
// b) flags.RelativeSeek(): The merging iterator decided to re-seek this
|
||||
// level according to a range tombstone. When lazy combined iteration
|
||||
// is enabled, the level iterator is responsible for watching for
|
||||
// files containing range keys and triggering the switch to combined
|
||||
// iteration when such a file is observed. If a range deletion was
|
||||
// observed in a higher level causing the merging iterator to seek the
|
||||
// level to the range deletion's end key, we need to check whether all
|
||||
// of the files between the old position and the new position contain
|
||||
// any range keys.
|
||||
//
|
||||
// In this scenario, we don't seek the LevelIterator and instead we
|
||||
// Next it, one file at a time, checking each for range keys. The
|
||||
// merging iterator sets this flag to inform us that we're moving
|
||||
// forward relative to the existing position and that we must examine
|
||||
// each intermediate sstable's metadata for lazy-combined iteration.
|
||||
// In this case, we only Next and never Seek. We set nextsUntilSeek=-1
|
||||
// to signal this intention.
|
||||
//
|
||||
// NB: At most one of flags.RelativeSeek() and flags.TrySeekUsingNext() may
|
||||
// be set, because the merging iterator re-seeks relative seeks with
|
||||
// explicitly only the RelativeSeek flag set.
|
||||
var nextsUntilSeek int
|
||||
var nextInsteadOfSeek bool
|
||||
if flags.TrySeekUsingNext() {
|
||||
nextInsteadOfSeek = true
|
||||
nextsUntilSeek = 4 // arbitrary
|
||||
}
|
||||
if flags.RelativeSeek() && l.combinedIterState != nil && !l.combinedIterState.initialized {
|
||||
nextInsteadOfSeek = true
|
||||
nextsUntilSeek = -1
|
||||
}
|
||||
|
||||
var m *fileMetadata
|
||||
if nextInsteadOfSeek {
|
||||
m = l.iterFile
|
||||
} else {
|
||||
m = l.files.SeekGE(l.cmp, key)
|
||||
}
|
||||
// The below loop has a bit of an unusual organization. There are several
|
||||
// conditions under which we need to Next to a later file. If none of those
|
||||
// conditions are met, the file in `m` is okay to return. The loop body is
|
||||
// structured with a series of if statements, each of which may continue the
|
||||
// loop to the next file. If none of the statements are met, the end of the
|
||||
// loop body is a break.
|
||||
for m != nil {
|
||||
if m.HasRangeKeys {
|
||||
l.maybeTriggerCombinedIteration(m, +1)
|
||||
|
||||
// Some files may only contain range keys, which we can skip.
|
||||
// NB: HasPointKeys=true if the file contains any points or range
|
||||
// deletions (which delete points).
|
||||
if !m.HasPointKeys {
|
||||
m = l.files.Next()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// This file has point keys.
|
||||
//
|
||||
// However, there are a couple reasons why `m` may not be positioned ≥
|
||||
// `key` yet:
|
||||
//
|
||||
// 1. If SeekGE(key) landed on a file containing range keys, the file
|
||||
// may contain range keys ≥ `key` but no point keys ≥ `key`.
|
||||
// 2. When nexting instead of seeking, we must check to see whether
|
||||
// we've nexted sufficiently far, or we need to next again.
|
||||
//
|
||||
// If the file does not contain point keys ≥ `key`, next to continue
|
||||
// looking for a file that does.
|
||||
if (m.HasRangeKeys || nextInsteadOfSeek) && l.cmp(m.LargestPointKey.UserKey, key) < 0 {
|
||||
// If nextInsteadOfSeek is set and nextsUntilSeek is non-negative,
|
||||
// the iterator has been nexting hoping to discover the relevant
|
||||
// file without seeking. It's exhausted the allotted nextsUntilSeek
|
||||
// and should seek to the sought key.
|
||||
if nextInsteadOfSeek && nextsUntilSeek == 0 {
|
||||
nextInsteadOfSeek = false
|
||||
m = l.files.SeekGE(l.cmp, key)
|
||||
continue
|
||||
} else if nextsUntilSeek > 0 {
|
||||
nextsUntilSeek--
|
||||
}
|
||||
m = l.files.Next()
|
||||
continue
|
||||
}
|
||||
|
||||
// This file has a point key bound ≥ `key`. But the largest point key
|
||||
// bound may still be a range deletion sentinel, which is exclusive. In
|
||||
// this case, the file doesn't actually contain any point keys equal to
|
||||
// `key`. We next to keep searching for a file that actually contains
|
||||
// point keys ≥ key.
|
||||
//
|
||||
// Additionally, this prevents loading untruncated range deletions from
|
||||
// a table which can't possibly contain the target key and is required
|
||||
// for correctness by mergingIter.SeekGE (see the comment in that
|
||||
// function).
|
||||
if m.LargestPointKey.IsExclusiveSentinel() && l.cmp(m.LargestPointKey.UserKey, key) == 0 {
|
||||
m = l.files.Next()
|
||||
continue
|
||||
}
|
||||
|
||||
// This file contains point keys ≥ `key`. Break and return it.
|
||||
break
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (l *levelIter) findFileLT(key []byte) *fileMetadata {
|
||||
func (l *levelIter) findFileLT(key []byte, flags base.SeekLTFlags) *fileMetadata {
|
||||
// Find the last file whose smallest key is < ikey.
|
||||
return l.files.SeekLT(l.cmp, key)
|
||||
|
||||
// Ordinarily we seek the LevelIterator using SeekLT.
|
||||
//
|
||||
// When lazy combined iteration is enabled, there's a complication. The
|
||||
// level iterator is responsible for watching for files containing range
|
||||
// keys and triggering the switch to combined iteration when such a file is
|
||||
// observed. If a range deletion was observed in a higher level causing the
|
||||
// merging iterator to seek the level to the range deletion's start key, we
|
||||
// need to check whether all of the files between the old position and the
|
||||
// new position contain any range keys.
|
||||
//
|
||||
// In this scenario, we don't seek the LevelIterator and instead we Prev it,
|
||||
// one file at a time, checking each for range keys.
|
||||
prevInsteadOfSeek := flags.RelativeSeek() && l.combinedIterState != nil && !l.combinedIterState.initialized
|
||||
|
||||
var m *fileMetadata
|
||||
if prevInsteadOfSeek {
|
||||
m = l.iterFile
|
||||
} else {
|
||||
m = l.files.SeekLT(l.cmp, key)
|
||||
}
|
||||
// The below loop has a bit of an unusual organization. There are several
|
||||
// conditions under which we need to Prev to a previous file. If none of
|
||||
// those conditions are met, the file in `m` is okay to return. The loop
|
||||
// body is structured with a series of if statements, each of which may
|
||||
// continue the loop to the previous file. If none of the statements are
|
||||
// met, the end of the loop body is a break.
|
||||
for m != nil {
|
||||
if m.HasRangeKeys {
|
||||
l.maybeTriggerCombinedIteration(m, -1)
|
||||
|
||||
// Some files may only contain range keys, which we can skip.
|
||||
// NB: HasPointKeys=true if the file contains any points or range
|
||||
// deletions (which delete points).
|
||||
if !m.HasPointKeys {
|
||||
m = l.files.Prev()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// This file has point keys.
|
||||
//
|
||||
// However, there are a couple reasons why `m` may not be positioned <
|
||||
// `key` yet:
|
||||
//
|
||||
// 1. If SeekLT(key) landed on a file containing range keys, the file
|
||||
// may contain range keys < `key` but no point keys < `key`.
|
||||
// 2. When preving instead of seeking, we must check to see whether
|
||||
// we've preved sufficiently far, or we need to prev again.
|
||||
//
|
||||
// If the file does not contain point keys < `key`, prev to continue
|
||||
// looking for a file that does.
|
||||
if (m.HasRangeKeys || prevInsteadOfSeek) && l.cmp(m.SmallestPointKey.UserKey, key) >= 0 {
|
||||
m = l.files.Prev()
|
||||
continue
|
||||
}
|
||||
|
||||
// This file contains point keys < `key`. Break and return it.
|
||||
break
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Init the iteration bounds for the current table. Returns -1 if the table
|
||||
@@ -248,11 +514,11 @@ func (l *levelIter) findFileLT(key []byte) *fileMetadata {
|
||||
func (l *levelIter) initTableBounds(f *fileMetadata) int {
|
||||
l.tableOpts.LowerBound = l.lower
|
||||
if l.tableOpts.LowerBound != nil {
|
||||
if l.cmp(f.Largest.UserKey, l.tableOpts.LowerBound) < 0 {
|
||||
if l.cmp(f.LargestPointKey.UserKey, l.tableOpts.LowerBound) < 0 {
|
||||
// The largest key in the sstable is smaller than the lower bound.
|
||||
return -1
|
||||
}
|
||||
if l.cmp(l.tableOpts.LowerBound, f.Smallest.UserKey) <= 0 {
|
||||
if l.cmp(l.tableOpts.LowerBound, f.SmallestPointKey.UserKey) <= 0 {
|
||||
// The lower bound is smaller or equal to the smallest key in the
|
||||
// table. Iteration within the table does not need to check the lower
|
||||
// bound.
|
||||
@@ -261,15 +527,16 @@ func (l *levelIter) initTableBounds(f *fileMetadata) int {
|
||||
}
|
||||
l.tableOpts.UpperBound = l.upper
|
||||
if l.tableOpts.UpperBound != nil {
|
||||
if l.cmp(f.Smallest.UserKey, l.tableOpts.UpperBound) >= 0 {
|
||||
if l.cmp(f.SmallestPointKey.UserKey, l.tableOpts.UpperBound) >= 0 {
|
||||
// The smallest key in the sstable is greater than or equal to the upper
|
||||
// bound.
|
||||
return 1
|
||||
}
|
||||
if l.cmp(l.tableOpts.UpperBound, f.Largest.UserKey) > 0 {
|
||||
if l.cmp(l.tableOpts.UpperBound, f.LargestPointKey.UserKey) > 0 {
|
||||
// The upper bound is greater than the largest key in the
|
||||
// table. Iteration within the table does not need to check the upper
|
||||
// bound. NB: tableOpts.UpperBound is exclusive and f.Largest is inclusive.
|
||||
// bound. NB: tableOpts.UpperBound is exclusive and f.LargestPointKey is
|
||||
// inclusive.
|
||||
l.tableOpts.UpperBound = nil
|
||||
}
|
||||
}
|
||||
@@ -287,8 +554,9 @@ const (
|
||||
func (l *levelIter) loadFile(file *fileMetadata, dir int) loadFileReturnIndicator {
|
||||
l.smallestBoundary = nil
|
||||
l.largestBoundary = nil
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
if l.iterFile == file {
|
||||
if l.err != nil {
|
||||
@@ -301,6 +569,16 @@ func (l *levelIter) loadFile(file *fileMetadata, dir int) loadFileReturnIndicato
|
||||
if l.rangeDelIterPtr != nil {
|
||||
*l.rangeDelIterPtr = l.rangeDelIterCopy
|
||||
}
|
||||
|
||||
// There are a few reasons we might not have triggered combined
|
||||
// iteration yet, even though we already had `file` open.
|
||||
// 1. If the bounds changed, we might have previously avoided
|
||||
// switching to combined iteration because the bounds excluded
|
||||
// the range keys contained in this file.
|
||||
// 2. If an existing iterator was reconfigured to iterate over range
|
||||
// keys (eg, using SetOptions), then we wouldn't have triggered
|
||||
// the switch to combined iteration yet.
|
||||
l.maybeTriggerCombinedIteration(file, dir)
|
||||
return fileAlreadyLoaded
|
||||
}
|
||||
// We were already at file, but don't have an iterator, probably because the file was
|
||||
@@ -322,6 +600,18 @@ func (l *levelIter) loadFile(file *fileMetadata, dir int) loadFileReturnIndicato
|
||||
return noFileLoaded
|
||||
}
|
||||
|
||||
l.maybeTriggerCombinedIteration(file, dir)
|
||||
if !file.HasPointKeys {
|
||||
switch dir {
|
||||
case +1:
|
||||
file = l.files.Next()
|
||||
continue
|
||||
case -1:
|
||||
file = l.files.Prev()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
switch l.initTableBounds(file) {
|
||||
case -1:
|
||||
// The largest key in the sstable is smaller than the lower bound.
|
||||
@@ -342,25 +632,30 @@ func (l *levelIter) loadFile(file *fileMetadata, dir int) loadFileReturnIndicato
|
||||
|
||||
var rangeDelIter keyspan.FragmentIterator
|
||||
var iter internalIterator
|
||||
iter, rangeDelIter, l.err = l.newIters(l.files.Current(), &l.tableOpts, l.bytesIterated)
|
||||
l.iter = base.WrapIterWithStats(iter)
|
||||
iter, rangeDelIter, l.err = l.newIters(l.files.Current(), &l.tableOpts, l.internalOpts)
|
||||
l.iter = iter
|
||||
if l.err != nil {
|
||||
return noFileLoaded
|
||||
}
|
||||
if rangeDelIter != nil {
|
||||
if fi, ok := iter.(filteredIter); ok {
|
||||
l.filteredIter = fi
|
||||
} else {
|
||||
l.filteredIter = nil
|
||||
}
|
||||
} else {
|
||||
l.filteredIter = nil
|
||||
}
|
||||
if l.rangeDelIterPtr != nil {
|
||||
*l.rangeDelIterPtr = rangeDelIter
|
||||
l.rangeDelIterCopy = rangeDelIter
|
||||
} else if rangeDelIter != nil {
|
||||
rangeDelIter.Close()
|
||||
}
|
||||
if l.smallestUserKey != nil {
|
||||
*l.smallestUserKey = file.Smallest.UserKey
|
||||
}
|
||||
if l.largestUserKey != nil {
|
||||
*l.largestUserKey = file.Largest.UserKey
|
||||
}
|
||||
if l.isLargestUserKeyRangeDelSentinel != nil {
|
||||
*l.isLargestUserKeyRangeDelSentinel = file.Largest.IsExclusiveSentinel()
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.smallestUserKey = file.Smallest.UserKey
|
||||
l.boundaryContext.largestUserKey = file.Largest.UserKey
|
||||
l.boundaryContext.isLargestUserKeyRangeDelSentinel = file.Largest.IsExclusiveSentinel()
|
||||
}
|
||||
return newFileLoaded
|
||||
}
|
||||
@@ -368,7 +663,7 @@ func (l *levelIter) loadFile(file *fileMetadata, dir int) loadFileReturnIndicato
|
||||
|
||||
// In race builds we verify that the keys returned by levelIter lie within
|
||||
// [lower,upper).
|
||||
func (l *levelIter) verify(key *InternalKey, val []byte) (*InternalKey, []byte) {
|
||||
func (l *levelIter) verify(key *InternalKey, val base.LazyValue) (*InternalKey, base.LazyValue) {
|
||||
// Note that invariants.Enabled is a compile time constant, which means the
|
||||
// block of code will be compiled out of normal builds making this method
|
||||
// eligible for inlining. Do not change this to use a variable.
|
||||
@@ -386,116 +681,123 @@ func (l *levelIter) verify(key *InternalKey, val []byte) (*InternalKey, []byte)
|
||||
return key, val
|
||||
}
|
||||
|
||||
func (l *levelIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.LowerBound.
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key), +1)
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key, flags), +1)
|
||||
if loadFileIndicator == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if loadFileIndicator == newFileLoaded {
|
||||
// File changed, so l.iter has changed, and that iterator is not
|
||||
// positioned appropriately.
|
||||
trySeekUsingNext = false
|
||||
flags = flags.DisableTrySeekUsingNext()
|
||||
}
|
||||
if ikey, val := l.iter.SeekGE(key, trySeekUsingNext); ikey != nil {
|
||||
if ikey, val := l.iter.SeekGE(key, flags); ikey != nil {
|
||||
return l.verify(ikey, val)
|
||||
}
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
|
||||
func (l *levelIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.LowerBound.
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key), +1)
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key, flags), +1)
|
||||
if loadFileIndicator == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if loadFileIndicator == newFileLoaded {
|
||||
// File changed, so l.iter has changed, and that iterator is not
|
||||
// positioned appropriately.
|
||||
trySeekUsingNext = false
|
||||
flags = flags.DisableTrySeekUsingNext()
|
||||
}
|
||||
if key, val := l.iter.SeekPrefixGE(prefix, key, trySeekUsingNext); key != nil {
|
||||
if key, val := l.iter.SeekPrefixGE(prefix, key, flags); key != nil {
|
||||
return l.verify(key, val)
|
||||
}
|
||||
// When SeekPrefixGE returns nil, we have not necessarily reached the end of
|
||||
// the sstable. All we know is that a key with prefix does not exist in the
|
||||
// current sstable. We do know that the key lies within the bounds of the
|
||||
// table as findFileGE found the table where key <= meta.Largest. We treat
|
||||
// this case the same as SeekGE where an upper-bound resides within the
|
||||
// sstable and generate a synthetic boundary key.
|
||||
// table as findFileGE found the table where key <= meta.Largest. We return
|
||||
// the table's bound with isIgnorableBoundaryKey set.
|
||||
if l.rangeDelIterPtr != nil && *l.rangeDelIterPtr != nil {
|
||||
if l.tableOpts.UpperBound != nil {
|
||||
l.syntheticBoundary.UserKey = l.tableOpts.UpperBound
|
||||
l.syntheticBoundary.Trailer = InternalKeyRangeDeleteSentinel
|
||||
l.largestBoundary = &l.syntheticBoundary
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = true
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = true
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
return l.verify(l.largestBoundary, nil)
|
||||
return l.verify(l.largestBoundary, base.LazyValue{})
|
||||
}
|
||||
l.syntheticBoundary = l.iterFile.Largest
|
||||
l.syntheticBoundary.SetKind(InternalKeyKindRangeDelete)
|
||||
l.largestBoundary = &l.syntheticBoundary
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
// Return the file's largest bound, ensuring this file stays open until
|
||||
// the mergingIter advances beyond the file's bounds. We set
|
||||
// isIgnorableBoundaryKey to signal that the actual key returned should
|
||||
// be ignored, and does not represent a real key in the database.
|
||||
l.largestBoundary = &l.iterFile.LargestPointKey
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = true
|
||||
}
|
||||
return l.verify(l.largestBoundary, nil)
|
||||
return l.verify(l.largestBoundary, base.LazyValue{})
|
||||
}
|
||||
// It is possible that we are here because bloom filter matching failed.
|
||||
// In that case it is likely that all keys matching the prefix are wholly
|
||||
// It is possible that we are here because bloom filter matching failed. In
|
||||
// that case it is likely that all keys matching the prefix are wholly
|
||||
// within the current file and cannot be in the subsequent file. In that
|
||||
// case we don't want to go to the next file, since loading and seeking in
|
||||
// there has some cost. Additionally, for sparse key spaces, loading the
|
||||
// next file will defeat the optimization for the next SeekPrefixGE that
|
||||
// is called with trySeekUsingNext=true, since for sparse key spaces it is
|
||||
// next file will defeat the optimization for the next SeekPrefixGE that is
|
||||
// called with flags.TrySeekUsingNext(), since for sparse key spaces it is
|
||||
// likely that the next key will also be contained in the current file.
|
||||
if n := l.split(l.iterFile.Largest.UserKey); l.cmp(prefix, l.iterFile.Largest.UserKey[:n]) < 0 {
|
||||
return nil, nil
|
||||
if n := l.split(l.iterFile.LargestPointKey.UserKey); l.cmp(prefix, l.iterFile.LargestPointKey.UserKey[:n]) < 0 {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
|
||||
func (l *levelIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.UpperBound.
|
||||
if l.loadFile(l.findFileLT(key), -1) == noFileLoaded {
|
||||
return nil, nil
|
||||
if l.loadFile(l.findFileLT(key, flags), -1) == noFileLoaded {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if key, val := l.iter.SeekLT(key); key != nil {
|
||||
if key, val := l.iter.SeekLT(key, flags); key != nil {
|
||||
return l.verify(key, val)
|
||||
}
|
||||
return l.verify(l.skipEmptyFileBackward())
|
||||
}
|
||||
|
||||
func (l *levelIter) First() (*InternalKey, []byte) {
|
||||
func (l *levelIter) First() (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
// NB: the top-level Iterator will call SeekGE if IterOptions.LowerBound is
|
||||
// set.
|
||||
if l.loadFile(l.files.First(), +1) == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if key, val := l.iter.First(); key != nil {
|
||||
return l.verify(key, val)
|
||||
@@ -503,16 +805,17 @@ func (l *levelIter) First() (*InternalKey, []byte) {
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
|
||||
func (l *levelIter) Last() (*InternalKey, []byte) {
|
||||
func (l *levelIter) Last() (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
// NB: the top-level Iterator will call SeekLT if IterOptions.UpperBound is
|
||||
// set.
|
||||
if l.loadFile(l.files.Last(), -1) == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if key, val := l.iter.Last(); key != nil {
|
||||
return l.verify(key, val)
|
||||
@@ -520,12 +823,13 @@ func (l *levelIter) Last() (*InternalKey, []byte) {
|
||||
return l.verify(l.skipEmptyFileBackward())
|
||||
}
|
||||
|
||||
func (l *levelIter) Next() (*InternalKey, []byte) {
|
||||
func (l *levelIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if l.err != nil || l.iter == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -539,7 +843,7 @@ func (l *levelIter) Next() (*InternalKey, []byte) {
|
||||
if l.rangeDelIterPtr != nil {
|
||||
*l.rangeDelIterPtr = nil
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
// We're stepping past the boundary key, so now we can load the next file.
|
||||
if l.loadFile(l.files.Next(), +1) != noFileLoaded {
|
||||
@@ -548,7 +852,7 @@ func (l *levelIter) Next() (*InternalKey, []byte) {
|
||||
}
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
|
||||
default:
|
||||
// Reset the smallest boundary since we're moving away from it.
|
||||
@@ -560,12 +864,13 @@ func (l *levelIter) Next() (*InternalKey, []byte) {
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
|
||||
func (l *levelIter) Prev() (*InternalKey, []byte) {
|
||||
func (l *levelIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if l.err != nil || l.iter == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = false
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -579,7 +884,7 @@ func (l *levelIter) Prev() (*InternalKey, []byte) {
|
||||
if l.rangeDelIterPtr != nil {
|
||||
*l.rangeDelIterPtr = nil
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
// We're stepping past the boundary key, so now we can load the prev file.
|
||||
if l.loadFile(l.files.Prev(), -1) != noFileLoaded {
|
||||
@@ -588,7 +893,7 @@ func (l *levelIter) Prev() (*InternalKey, []byte) {
|
||||
}
|
||||
return l.verify(l.skipEmptyFileBackward())
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
|
||||
default:
|
||||
// Reset the largest boundary since we're moving away from it.
|
||||
@@ -600,9 +905,9 @@ func (l *levelIter) Prev() (*InternalKey, []byte) {
|
||||
return l.verify(l.skipEmptyFileBackward())
|
||||
}
|
||||
|
||||
func (l *levelIter) skipEmptyFileForward() (*InternalKey, []byte) {
|
||||
func (l *levelIter) skipEmptyFileForward() (*InternalKey, base.LazyValue) {
|
||||
var key *InternalKey
|
||||
var val []byte
|
||||
var val base.LazyValue
|
||||
// The first iteration of this loop starts with an already exhausted
|
||||
// l.iter. The reason for the exhaustion is either that we iterated to the
|
||||
// end of the sstable, or our iteration was terminated early due to the
|
||||
@@ -636,35 +941,61 @@ func (l *levelIter) skipEmptyFileForward() (*InternalKey, []byte) {
|
||||
l.syntheticBoundary.UserKey = l.tableOpts.UpperBound
|
||||
l.syntheticBoundary.Trailer = InternalKeyRangeDeleteSentinel
|
||||
l.largestBoundary = &l.syntheticBoundary
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = true
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = true
|
||||
}
|
||||
return l.largestBoundary, nil
|
||||
return l.largestBoundary, base.LazyValue{}
|
||||
}
|
||||
// Else there are no range deletions in this sstable. This
|
||||
// helps with performance when many levels are populated with
|
||||
// sstables and most don't have any actual keys within the
|
||||
// bounds.
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
// If the boundary is a range deletion tombstone, return that key.
|
||||
if l.iterFile.Largest.Kind() == InternalKeyKindRangeDelete {
|
||||
if l.iterFile.LargestPointKey.Kind() == InternalKeyKindRangeDelete {
|
||||
l.largestBoundary = &l.iterFile.LargestPointKey
|
||||
return l.largestBoundary, base.LazyValue{}
|
||||
}
|
||||
// If the last point iterator positioning op might've skipped keys,
|
||||
// it's possible the file's range deletions are still relevant to
|
||||
// other levels. Return the largest boundary as a special ignorable
|
||||
// marker to avoid advancing to the next file.
|
||||
//
|
||||
// The sstable iterator cannot guarantee that keys were skipped. A
|
||||
// SeekGE that lands on a index separator k only knows that the
|
||||
// block at the index entry contains keys ≤ k. We can't know whether
|
||||
// there were actually keys between the seek key and the index
|
||||
// separator key. If the block is then excluded due to block
|
||||
// property filters, the iterator does not know whether keys were
|
||||
// actually skipped by the block's exclusion.
|
||||
//
|
||||
// Since MaybeFilteredKeys cannot guarantee that keys were skipped,
|
||||
// it's possible l.iterFile.Largest was already returned. Returning
|
||||
// l.iterFile.Largest again is a violation of the strict
|
||||
// monotonicity normally provided. The mergingIter's heap can
|
||||
// tolerate this repeat key and in this case will keep the level at
|
||||
// the top of the heap and immediately skip the entry, advancing to
|
||||
// the next file.
|
||||
if *l.rangeDelIterPtr != nil && l.filteredIter != nil &&
|
||||
l.filteredIter.MaybeFilteredKeys() {
|
||||
l.largestBoundary = &l.iterFile.Largest
|
||||
return l.largestBoundary, nil
|
||||
l.boundaryContext.isIgnorableBoundaryKey = true
|
||||
return l.largestBoundary, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
// Current file was exhausted. Move to the next file.
|
||||
if l.loadFile(l.files.Next(), +1) == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
return key, val
|
||||
}
|
||||
|
||||
func (l *levelIter) skipEmptyFileBackward() (*InternalKey, []byte) {
|
||||
func (l *levelIter) skipEmptyFileBackward() (*InternalKey, base.LazyValue) {
|
||||
var key *InternalKey
|
||||
var val []byte
|
||||
var val base.LazyValue
|
||||
// The first iteration of this loop starts with an already exhausted
|
||||
// l.iter. The reason for the exhaustion is either that we iterated to the
|
||||
// end of the sstable, or our iteration was terminated early due to the
|
||||
@@ -697,27 +1028,52 @@ func (l *levelIter) skipEmptyFileBackward() (*InternalKey, []byte) {
|
||||
l.syntheticBoundary.UserKey = l.tableOpts.LowerBound
|
||||
l.syntheticBoundary.Trailer = InternalKeyRangeDeleteSentinel
|
||||
l.smallestBoundary = &l.syntheticBoundary
|
||||
if l.isSyntheticIterBoundsKey != nil {
|
||||
*l.isSyntheticIterBoundsKey = true
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = true
|
||||
}
|
||||
return l.smallestBoundary, nil
|
||||
return l.smallestBoundary, base.LazyValue{}
|
||||
}
|
||||
// Else there are no range deletions in this sstable. This
|
||||
// helps with performance when many levels are populated with
|
||||
// sstables and most don't have any actual keys within the
|
||||
// bounds.
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
// If the boundary is a range deletion tombstone, return that key.
|
||||
if l.iterFile.Smallest.Kind() == InternalKeyKindRangeDelete {
|
||||
if l.iterFile.SmallestPointKey.Kind() == InternalKeyKindRangeDelete {
|
||||
l.smallestBoundary = &l.iterFile.SmallestPointKey
|
||||
return l.smallestBoundary, base.LazyValue{}
|
||||
}
|
||||
// If the last point iterator positioning op skipped keys, it's
|
||||
// possible the file's range deletions are still relevant to other
|
||||
// levels. Return the smallest boundary as a special ignorable key
|
||||
// to avoid advancing to the next file.
|
||||
//
|
||||
// The sstable iterator cannot guarantee that keys were skipped. A
|
||||
// SeekGE that lands on a index separator k only knows that the
|
||||
// block at the index entry contains keys ≤ k. We can't know whether
|
||||
// there were actually keys between the seek key and the index
|
||||
// separator key. If the block is then excluded due to block
|
||||
// property filters, the iterator does not know whether keys were
|
||||
// actually skipped by the block's exclusion.
|
||||
//
|
||||
// Since MaybeFilteredKeys cannot guarantee that keys were skipped,
|
||||
// it's possible l.iterFile.Smallest was already returned. Returning
|
||||
// l.iterFile.Smallest again is a violation of the strict
|
||||
// monotonicity normally provided. The mergingIter's heap can
|
||||
// tolerate this repeat key and in this case will keep the level at
|
||||
// the top of the heap and immediately skip the entry, advancing to
|
||||
// the next file.
|
||||
if *l.rangeDelIterPtr != nil && l.filteredIter != nil && l.filteredIter.MaybeFilteredKeys() {
|
||||
l.smallestBoundary = &l.iterFile.Smallest
|
||||
return l.smallestBoundary, nil
|
||||
l.boundaryContext.isIgnorableBoundaryKey = true
|
||||
return l.smallestBoundary, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
// Current file was exhausted. Move to the previous file.
|
||||
if l.loadFile(l.files.Prev(), -1) == noFileLoaded {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
return key, val
|
||||
@@ -732,7 +1088,6 @@ func (l *levelIter) Error() error {
|
||||
|
||||
func (l *levelIter) Close() error {
|
||||
if l.iter != nil {
|
||||
l.stats.Merge(l.iter.Stats())
|
||||
l.err = l.iter.Close()
|
||||
l.iter = nil
|
||||
}
|
||||
@@ -773,21 +1128,4 @@ func (l *levelIter) String() string {
|
||||
return fmt.Sprintf("%s: fileNum=<nil>", l.level)
|
||||
}
|
||||
|
||||
var _ internalIteratorWithStats = &levelIter{}
|
||||
|
||||
// Stats implements InternalIteratorWithStats.
|
||||
func (l *levelIter) Stats() base.InternalIteratorStats {
|
||||
stats := l.stats
|
||||
if l.iter != nil {
|
||||
stats.Merge(l.iter.Stats())
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
// ResetStats implements InternalIteratorWithStats.
|
||||
func (l *levelIter) ResetStats() {
|
||||
l.stats = base.InternalIteratorStats{}
|
||||
if l.iter != nil {
|
||||
l.iter.ResetStats()
|
||||
}
|
||||
}
|
||||
var _ internalIterator = &levelIter{}
|
||||
|
||||
10
vendor/github.com/cockroachdb/pebble/mem_table.go
generated
vendored
10
vendor/github.com/cockroachdb/pebble/mem_table.go
generated
vendored
@@ -257,6 +257,10 @@ func (m *memTable) newRangeKeyIter(*IterOptions) keyspan.FragmentIterator {
|
||||
return keyspan.NewIter(m.cmp, rangeKeys)
|
||||
}
|
||||
|
||||
func (m *memTable) containsRangeKeys() bool {
|
||||
return atomic.LoadUint32(&m.rangeKeys.atomicCount) > 0
|
||||
}
|
||||
|
||||
func (m *memTable) availBytes() uint32 {
|
||||
a := m.skl.Arena()
|
||||
if atomic.LoadInt32(&m.writerRefs) == 1 {
|
||||
@@ -332,7 +336,7 @@ func (f *keySpanFrags) get(
|
||||
it := skl.NewIter(nil, nil)
|
||||
var keysDst []keyspan.Key
|
||||
for key, val := it.First(); key != nil; key, val = it.Next() {
|
||||
s, err := constructSpan(*key, val, keysDst)
|
||||
s, err := constructSpan(*key, val.InPlaceValue(), keysDst)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -348,7 +352,7 @@ func (f *keySpanFrags) get(
|
||||
// invalidated whenever a key of the same kind is added to a memTable, and
|
||||
// populated when empty when a span iterator of that key kind is created.
|
||||
type keySpanCache struct {
|
||||
count uint32
|
||||
atomicCount uint32
|
||||
frags unsafe.Pointer
|
||||
cmp Compare
|
||||
formatKey base.FormatKey
|
||||
@@ -359,7 +363,7 @@ type keySpanCache struct {
|
||||
// Invalidate the current set of cached spans, indicating the number of
|
||||
// spans that were added.
|
||||
func (c *keySpanCache) invalidate(count uint32) {
|
||||
newCount := atomic.AddUint32(&c.count, count)
|
||||
newCount := atomic.AddUint32(&c.atomicCount, count)
|
||||
var frags *keySpanFrags
|
||||
|
||||
for {
|
||||
|
||||
296
vendor/github.com/cockroachdb/pebble/merging_iter.go
generated
vendored
296
vendor/github.com/cockroachdb/pebble/merging_iter.go
generated
vendored
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type mergingIterLevel struct {
|
||||
iter internalIteratorWithStats
|
||||
iter internalIterator
|
||||
// rangeDelIter is set to the range-deletion iterator for the level. When
|
||||
// configured with a levelIter, this pointer changes as sstable boundaries
|
||||
// are crossed. See levelIter.initRangeDel and the Range Deletions comment
|
||||
@@ -24,22 +24,41 @@ type mergingIterLevel struct {
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
// iterKey and iterValue cache the current key and value iter are pointed at.
|
||||
iterKey *InternalKey
|
||||
iterValue []byte
|
||||
iterValue base.LazyValue
|
||||
|
||||
// smallestUserKey, largestUserKey, isLargestUserKeyRangeDelSentinel are set using the sstable
|
||||
// boundary keys when using levelIter. See levelIter comment and the Range Deletions comment
|
||||
// below.
|
||||
smallestUserKey, largestUserKey []byte
|
||||
isLargestUserKeyRangeDelSentinel bool
|
||||
isSyntheticIterBoundsKey bool
|
||||
// levelIterBoundaryContext's fields are set when using levelIter, in order
|
||||
// to surface sstable boundary keys and file-level context. See levelIter
|
||||
// comment and the Range Deletions comment below.
|
||||
levelIterBoundaryContext
|
||||
|
||||
// tombstone caches the tombstone rangeDelIter is currently pointed at. If
|
||||
// tombstone.Valid() is false, there are no further tombstones within the
|
||||
// tombstone is nil, there are no further tombstones within the
|
||||
// current sstable in the current iterator direction. The cached tombstone is
|
||||
// only valid for the levels in the range [0,heap[0].index]. This avoids
|
||||
// positioning tombstones at lower levels which cannot possibly shadow the
|
||||
// current key.
|
||||
tombstone keyspan.Span
|
||||
tombstone *keyspan.Span
|
||||
}
|
||||
|
||||
type levelIterBoundaryContext struct {
|
||||
// smallestUserKey and largestUserKey are populated with the smallest and
|
||||
// largest boundaries of the current file.
|
||||
smallestUserKey, largestUserKey []byte
|
||||
// isLargestUserKeyRangeDelSentinel is set to true when a file's largest
|
||||
// boundary is an exclusive range deletion sentinel. If true, the file does
|
||||
// not contain any keys with the provided user key, and the largestUserKey
|
||||
// bound is exclusive.
|
||||
isLargestUserKeyRangeDelSentinel bool
|
||||
// isSyntheticIterBoundsKey is set to true iff the key returned by the level
|
||||
// iterator is a synthetic key derived from the iterator bounds. This is
|
||||
// used to prevent the mergingIter from being stuck at such a synthetic key
|
||||
// if it becomes the top element of the heap.
|
||||
isSyntheticIterBoundsKey bool
|
||||
// isIgnorableBoundaryKey is set to true iff the key returned by the level
|
||||
// iterator is a file boundary key that should be ignored. This is used to
|
||||
// keep a levelIter file's range deletion iterator open as long as other
|
||||
// levels within the merging iterator require it.
|
||||
isIgnorableBoundaryKey bool
|
||||
}
|
||||
|
||||
// mergingIter provides a merged view of multiple iterators from different
|
||||
@@ -211,17 +230,20 @@ type mergingIterLevel struct {
|
||||
// scenarios and have each step display the current state (i.e. the current
|
||||
// heap and range-del iterator positioning).
|
||||
type mergingIter struct {
|
||||
logger Logger
|
||||
split Split
|
||||
dir int
|
||||
snapshot uint64
|
||||
levels []mergingIterLevel
|
||||
heap mergingIterHeap
|
||||
err error
|
||||
prefix []byte
|
||||
lower []byte
|
||||
upper []byte
|
||||
stats InternalIteratorStats
|
||||
logger Logger
|
||||
split Split
|
||||
dir int
|
||||
snapshot uint64
|
||||
batchSnapshot uint64
|
||||
levels []mergingIterLevel
|
||||
heap mergingIterHeap
|
||||
err error
|
||||
prefix []byte
|
||||
lower []byte
|
||||
upper []byte
|
||||
stats *InternalIteratorStats
|
||||
|
||||
combinedIterState *combinedIterState
|
||||
|
||||
// Elide range tombstones from being returned during iteration. Set to true
|
||||
// when mergingIter is a child of Iterator and the mergingIter is processing
|
||||
@@ -243,19 +265,27 @@ var _ base.InternalIterator = (*mergingIter)(nil)
|
||||
//
|
||||
// None of the iters may be nil.
|
||||
func newMergingIter(
|
||||
logger Logger, cmp Compare, split Split, iters ...internalIterator,
|
||||
logger Logger,
|
||||
stats *base.InternalIteratorStats,
|
||||
cmp Compare,
|
||||
split Split,
|
||||
iters ...internalIterator,
|
||||
) *mergingIter {
|
||||
m := &mergingIter{}
|
||||
levels := make([]mergingIterLevel, len(iters))
|
||||
for i := range levels {
|
||||
levels[i].iter = base.WrapIterWithStats(iters[i])
|
||||
levels[i].iter = iters[i]
|
||||
}
|
||||
m.init(&IterOptions{logger: logger}, cmp, split, levels...)
|
||||
m.init(&IterOptions{logger: logger}, stats, cmp, split, levels...)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *mergingIter) init(
|
||||
opts *IterOptions, cmp Compare, split Split, levels ...mergingIterLevel,
|
||||
opts *IterOptions,
|
||||
stats *base.InternalIteratorStats,
|
||||
cmp Compare,
|
||||
split Split,
|
||||
levels ...mergingIterLevel,
|
||||
) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.logger = opts.getLogger()
|
||||
@@ -264,9 +294,11 @@ func (m *mergingIter) init(
|
||||
m.upper = opts.UpperBound
|
||||
}
|
||||
m.snapshot = InternalKeySeqNumMax
|
||||
m.batchSnapshot = InternalKeySeqNumMax
|
||||
m.levels = levels
|
||||
m.heap.cmp = cmp
|
||||
m.split = split
|
||||
m.stats = stats
|
||||
if cap(m.heap.items) < len(levels) {
|
||||
m.heap.items = make([]mergingIterItem, 0, len(levels))
|
||||
} else {
|
||||
@@ -317,7 +349,7 @@ func (m *mergingIter) initMinRangeDelIters(oldTopLevel int) {
|
||||
if l.rangeDelIter == nil {
|
||||
continue
|
||||
}
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey, m.snapshot)
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,14 +376,14 @@ func (m *mergingIter) initMaxRangeDelIters(oldTopLevel int) {
|
||||
if l.rangeDelIter == nil {
|
||||
continue
|
||||
}
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, l.rangeDelIter, item.key.UserKey, m.snapshot)
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, l.rangeDelIter, item.key.UserKey)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mergingIter) switchToMinHeap() {
|
||||
if m.heap.len() == 0 {
|
||||
if m.lower != nil {
|
||||
m.SeekGE(m.lower, false /* trySeekUsingNext */)
|
||||
m.SeekGE(m.lower, base.SeekGEFlagsNone)
|
||||
} else {
|
||||
m.First()
|
||||
}
|
||||
@@ -404,7 +436,7 @@ func (m *mergingIter) switchToMinHeap() {
|
||||
l.iterKey.IsExclusiveSentinel() &&
|
||||
m.heap.cmp(l.iterKey.UserKey, m.lower) <= 0) {
|
||||
if m.lower != nil {
|
||||
l.iterKey, l.iterValue = l.iter.SeekGE(m.lower, false /* trySeekUsingNext */)
|
||||
l.iterKey, l.iterValue = l.iter.SeekGE(m.lower, base.SeekGEFlagsNone)
|
||||
} else {
|
||||
l.iterKey, l.iterValue = l.iter.First()
|
||||
}
|
||||
@@ -425,7 +457,7 @@ func (m *mergingIter) switchToMinHeap() {
|
||||
// levelIter's invariants. See the example in the for loop above.
|
||||
if m.lower != nil && cur.isSyntheticIterBoundsKey && cur.iterKey.IsExclusiveSentinel() &&
|
||||
m.heap.cmp(cur.iterKey.UserKey, m.lower) <= 0 {
|
||||
cur.iterKey, cur.iterValue = cur.iter.SeekGE(m.lower, false /* trySeekUsingNext */)
|
||||
cur.iterKey, cur.iterValue = cur.iter.SeekGE(m.lower, base.SeekGEFlagsNone)
|
||||
} else {
|
||||
cur.iterKey, cur.iterValue = cur.iter.Next()
|
||||
}
|
||||
@@ -435,7 +467,7 @@ func (m *mergingIter) switchToMinHeap() {
|
||||
func (m *mergingIter) switchToMaxHeap() {
|
||||
if m.heap.len() == 0 {
|
||||
if m.upper != nil {
|
||||
m.SeekLT(m.upper)
|
||||
m.SeekLT(m.upper, base.SeekLTFlagsNone)
|
||||
} else {
|
||||
m.Last()
|
||||
}
|
||||
@@ -486,7 +518,7 @@ func (m *mergingIter) switchToMaxHeap() {
|
||||
if l.iterKey == nil || (m.upper != nil && l.isSyntheticIterBoundsKey &&
|
||||
l.iterKey.IsExclusiveSentinel() && m.heap.cmp(l.iterKey.UserKey, m.upper) >= 0) {
|
||||
if m.upper != nil {
|
||||
l.iterKey, l.iterValue = l.iter.SeekLT(m.upper)
|
||||
l.iterKey, l.iterValue = l.iter.SeekLT(m.upper, base.SeekLTFlagsNone)
|
||||
} else {
|
||||
l.iterKey, l.iterValue = l.iter.Last()
|
||||
}
|
||||
@@ -508,7 +540,7 @@ func (m *mergingIter) switchToMaxHeap() {
|
||||
// above.
|
||||
if m.upper != nil && cur.isSyntheticIterBoundsKey && cur.iterKey.IsExclusiveSentinel() &&
|
||||
m.heap.cmp(cur.iterKey.UserKey, m.upper) >= 0 {
|
||||
cur.iterKey, cur.iterValue = cur.iter.SeekLT(m.upper)
|
||||
cur.iterKey, cur.iterValue = cur.iter.SeekLT(m.upper, base.SeekLTFlagsNone)
|
||||
} else {
|
||||
cur.iterKey, cur.iterValue = cur.iter.Prev()
|
||||
}
|
||||
@@ -555,8 +587,8 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
|
||||
// entry.
|
||||
for level := 0; level <= item.index; level++ {
|
||||
l := &m.levels[level]
|
||||
if l.rangeDelIter == nil || !l.tombstone.Valid() {
|
||||
// If l.tombstone.Valid() is false, there are no further tombstones
|
||||
if l.rangeDelIter == nil || l.tombstone == nil {
|
||||
// If l.tombstone is nil, there are no further tombstones
|
||||
// in the current sstable in the current (forward) iteration
|
||||
// direction.
|
||||
continue
|
||||
@@ -570,9 +602,9 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
|
||||
// levelIter in the future cannot contain item.key). Also, it is possible that we
|
||||
// will encounter parts of the range delete that should be ignored -- we handle that
|
||||
// below.
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey, m.snapshot)
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, l.rangeDelIter, item.key.UserKey)
|
||||
}
|
||||
if !l.tombstone.Valid() {
|
||||
if l.tombstone == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -595,7 +627,7 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
|
||||
//
|
||||
// For a tombstone at the same level as the key, the file bounds are trivially satisfied.
|
||||
if (l.smallestUserKey == nil || m.heap.cmp(l.smallestUserKey, item.key.UserKey) <= 0) &&
|
||||
l.tombstone.Contains(m.heap.cmp, item.key.UserKey) && !l.tombstone.Empty() {
|
||||
l.tombstone.VisibleAt(m.snapshot) && l.tombstone.Contains(m.heap.cmp, item.key.UserKey) {
|
||||
if level < item.index {
|
||||
// We could also do m.seekGE(..., level + 1). The levels from
|
||||
// [level + 1, item.index) are already after item.key so seeking them may be
|
||||
@@ -625,11 +657,17 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
|
||||
}
|
||||
// This seek is not directly due to a SeekGE call, so we don't
|
||||
// know enough about the underlying iterator positions, and so
|
||||
// we set trySeekUsingNext=false.
|
||||
m.seekGE(seekKey, item.index, false /* trySeekUsingNext */)
|
||||
// we keep the try-seek-using-next optimization disabled.
|
||||
//
|
||||
// Additionally, we set the relative-seek flag. This is
|
||||
// important when iterating with lazy combined iteration. If
|
||||
// there's a range key between this level's current file and the
|
||||
// file the seek will land on, we need to detect it in order to
|
||||
// trigger construction of the combined iterator.
|
||||
m.seekGE(seekKey, item.index, base.SeekGEFlagsNone.EnableRelativeSeek())
|
||||
return true
|
||||
}
|
||||
if l.tombstone.Covers(item.key.SeqNum()) {
|
||||
if l.tombstone.CoversAt(m.snapshot, item.key.SeqNum()) {
|
||||
m.nextEntry(item)
|
||||
return true
|
||||
}
|
||||
@@ -639,37 +677,65 @@ func (m *mergingIter) isNextEntryDeleted(item *mergingIterItem) bool {
|
||||
}
|
||||
|
||||
// Starting from the current entry, finds the first (next) entry that can be returned.
|
||||
func (m *mergingIter) findNextEntry() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) findNextEntry() (*InternalKey, base.LazyValue) {
|
||||
var reseeked bool
|
||||
for m.heap.len() > 0 && m.err == nil {
|
||||
item := &m.heap.items[0]
|
||||
if m.levels[item.index].isSyntheticIterBoundsKey {
|
||||
break
|
||||
}
|
||||
// For prefix iteration, stop if we've already exceeded the iterator's
|
||||
// current prefix. There are two cases where we perform this check.
|
||||
//
|
||||
// 1. We already re-seeked the iterator due to a range tombstone. We
|
||||
// could amortize the cost of this comparison, by doing it only after
|
||||
// we have iterated in this for loop a few times. But unless we find
|
||||
// a performance benefit to that, we do the simple thing and compare
|
||||
// each time. Note that isNextEntryDeleted already did at least 4 key
|
||||
// comparisons in order to return true, and additionally at least one
|
||||
// heap comparison to step to the next entry.
|
||||
//
|
||||
// Note that we cannot move this comparison into the
|
||||
// isNextEntryDeleted branch. Once isNextEntryDeleted determines a
|
||||
// key is deleted and seeks the level's iterator, item.key's memory
|
||||
// is potentially invalid. If the iterator is now exhausted, item.key
|
||||
// may be garbage.
|
||||
//
|
||||
// 2. If the heap root is an ignorable boundary key, Next-ing the
|
||||
// iterator will close the associated file and open the next file in
|
||||
// the level. We don't want to do this unnecessarily because the
|
||||
// 'exhausted' file may only appear exhausted because it failed a
|
||||
// bloom filter check. Avoiding Next-ing the file has a performance
|
||||
// benefit of unnecessarily advancing to the next file, but it's also
|
||||
// necessary for correctness of some TrySeekUsingNext optimizations
|
||||
// deeper in the iterator stack:
|
||||
//
|
||||
// There may exist valid keys between our current prefix (m.prefix)
|
||||
// and the ignorable boundary key (item.key) that were not returned
|
||||
// due to the bloom filter test. A subsequent SeekPrefixGE call with
|
||||
// a seek key in the range (m.prefix - item.key) will have the
|
||||
// TrySeekUsingNext flag enabled. The levelIter relies on having not
|
||||
// skipped these keys in order to avoid more expensive full seeks.
|
||||
if m.prefix != nil && (reseeked || m.levels[item.index].isIgnorableBoundaryKey) {
|
||||
if n := m.split(item.key.UserKey); !bytes.Equal(m.prefix, item.key.UserKey[:n]) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
m.addItemStats(item)
|
||||
if m.isNextEntryDeleted(item) {
|
||||
m.stats.PointsCoveredByRangeTombstones++
|
||||
// For prefix iteration, stop if we are past the prefix. We could
|
||||
// amortize the cost of this comparison, by doing it only after we
|
||||
// have iterated in this for loop a few times. But unless we find
|
||||
// a performance benefit to that, we do the simple thing and
|
||||
// compare each time. Note that isNextEntryDeleted already did at
|
||||
// least 4 key comparisons in order to return true, and
|
||||
// additionally at least one heap comparison to step to the next
|
||||
// entry.
|
||||
if m.prefix != nil {
|
||||
if n := m.split(item.key.UserKey); !bytes.Equal(m.prefix, item.key.UserKey[:n]) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
reseeked = true
|
||||
continue
|
||||
}
|
||||
if item.key.Visible(m.snapshot) &&
|
||||
if item.key.Visible(m.snapshot, m.batchSnapshot) &&
|
||||
(!m.levels[item.index].isIgnorableBoundaryKey) &&
|
||||
(item.key.Kind() != InternalKeyKindRangeDelete || !m.elideRangeTombstones) {
|
||||
return &item.key, item.value
|
||||
}
|
||||
m.nextEntry(item)
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Steps to the prev entry. item is the current top item in the heap.
|
||||
@@ -713,8 +779,8 @@ func (m *mergingIter) isPrevEntryDeleted(item *mergingIterItem) bool {
|
||||
// entry.
|
||||
for level := 0; level <= item.index; level++ {
|
||||
l := &m.levels[level]
|
||||
if l.rangeDelIter == nil || !l.tombstone.Valid() {
|
||||
// If l.tombstone.Valid() is false, there are no further tombstones
|
||||
if l.rangeDelIter == nil || l.tombstone == nil {
|
||||
// If l.tombstone is nil, there are no further tombstones
|
||||
// in the current sstable in the current (reverse) iteration
|
||||
// direction.
|
||||
continue
|
||||
@@ -728,9 +794,9 @@ func (m *mergingIter) isPrevEntryDeleted(item *mergingIterItem) bool {
|
||||
// levelIter in the future cannot contain item.key). Also, it is it is possible that we
|
||||
// will encounter parts of the range delete that should be ignored -- we handle that
|
||||
// below.
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, l.rangeDelIter, item.key.UserKey, m.snapshot)
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, l.rangeDelIter, item.key.UserKey)
|
||||
}
|
||||
if !l.tombstone.Valid() {
|
||||
if l.tombstone == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -767,7 +833,7 @@ func (m *mergingIter) isPrevEntryDeleted(item *mergingIterItem) bool {
|
||||
cmpResult := m.heap.cmp(l.largestUserKey, item.key.UserKey)
|
||||
withinLargestSSTableBound = cmpResult > 0 || (cmpResult == 0 && !l.isLargestUserKeyRangeDelSentinel)
|
||||
}
|
||||
if withinLargestSSTableBound && l.tombstone.Contains(m.heap.cmp, item.key.UserKey) && !l.tombstone.Empty() {
|
||||
if withinLargestSSTableBound && l.tombstone.Contains(m.heap.cmp, item.key.UserKey) && l.tombstone.VisibleAt(m.snapshot) {
|
||||
if level < item.index {
|
||||
// We could also do m.seekLT(..., level + 1). The levels from
|
||||
// [level + 1, item.index) are already before item.key so seeking them may be
|
||||
@@ -787,10 +853,15 @@ func (m *mergingIter) isPrevEntryDeleted(item *mergingIterItem) bool {
|
||||
if l.smallestUserKey != nil && m.heap.cmp(l.smallestUserKey, seekKey) > 0 {
|
||||
seekKey = l.smallestUserKey
|
||||
}
|
||||
m.seekLT(seekKey, item.index)
|
||||
// We set the relative-seek flag. This is important when
|
||||
// iterating with lazy combined iteration. If there's a range
|
||||
// key between this level's current file and the file the seek
|
||||
// will land on, we need to detect it in order to trigger
|
||||
// construction of the combined iterator.
|
||||
m.seekLT(seekKey, item.index, base.SeekLTFlagsNone.EnableRelativeSeek())
|
||||
return true
|
||||
}
|
||||
if l.tombstone.Covers(item.key.SeqNum()) {
|
||||
if l.tombstone.CoversAt(m.snapshot, item.key.SeqNum()) {
|
||||
m.prevEntry(item)
|
||||
return true
|
||||
}
|
||||
@@ -800,7 +871,7 @@ func (m *mergingIter) isPrevEntryDeleted(item *mergingIterItem) bool {
|
||||
}
|
||||
|
||||
// Starting from the current entry, finds the first (prev) entry that can be returned.
|
||||
func (m *mergingIter) findPrevEntry() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) findPrevEntry() (*InternalKey, base.LazyValue) {
|
||||
for m.heap.len() > 0 && m.err == nil {
|
||||
item := &m.heap.items[0]
|
||||
if m.levels[item.index].isSyntheticIterBoundsKey {
|
||||
@@ -811,17 +882,18 @@ func (m *mergingIter) findPrevEntry() (*InternalKey, []byte) {
|
||||
m.stats.PointsCoveredByRangeTombstones++
|
||||
continue
|
||||
}
|
||||
if item.key.Visible(m.snapshot) &&
|
||||
if item.key.Visible(m.snapshot, m.batchSnapshot) &&
|
||||
(!m.levels[item.index].isIgnorableBoundaryKey) &&
|
||||
(item.key.Kind() != InternalKeyKindRangeDelete || !m.elideRangeTombstones) {
|
||||
return &item.key, item.value
|
||||
}
|
||||
m.prevEntry(item)
|
||||
}
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Seeks levels >= level to >= key. Additionally uses range tombstones to extend the seeks.
|
||||
func (m *mergingIter) seekGE(key []byte, level int, trySeekUsingNext bool) {
|
||||
func (m *mergingIter) seekGE(key []byte, level int, flags base.SeekGEFlags) {
|
||||
// When seeking, we can use tombstones to adjust the key we seek to on each
|
||||
// level. Consider the series of range tombstones:
|
||||
//
|
||||
@@ -850,12 +922,19 @@ func (m *mergingIter) seekGE(key []byte, level int, trySeekUsingNext bool) {
|
||||
|
||||
l := &m.levels[level]
|
||||
if m.prefix != nil {
|
||||
l.iterKey, l.iterValue = l.iter.SeekPrefixGE(m.prefix, key, trySeekUsingNext)
|
||||
l.iterKey, l.iterValue = l.iter.SeekPrefixGE(m.prefix, key, flags)
|
||||
} else {
|
||||
l.iterKey, l.iterValue = l.iter.SeekGE(key, trySeekUsingNext)
|
||||
l.iterKey, l.iterValue = l.iter.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
if rangeDelIter := l.rangeDelIter; rangeDelIter != nil {
|
||||
// If this level contains overlapping range tombstones, alter the seek
|
||||
// key accordingly. Caveat: If we're performing lazy-combined iteration,
|
||||
// we cannot alter the seek key: Range tombstones don't delete range
|
||||
// keys, and there might exist live range keys within the range
|
||||
// tombstone's span that need to be observed to trigger a switch to
|
||||
// combined iteration.
|
||||
if rangeDelIter := l.rangeDelIter; rangeDelIter != nil &&
|
||||
(m.combinedIterState == nil || m.combinedIterState.initialized) {
|
||||
// The level has a range-del iterator. Find the tombstone containing
|
||||
// the search key.
|
||||
//
|
||||
@@ -871,8 +950,8 @@ func (m *mergingIter) seekGE(key []byte, level int, trySeekUsingNext bool) {
|
||||
// so we can have a sstable with bounds [c#8, i#InternalRangeDelSentinel], and the
|
||||
// tombstone is [b, k)#8 and the seek key is i: levelIter.SeekGE(i) will move past
|
||||
// this sstable since it realizes the largest key is a InternalRangeDelSentinel.
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, rangeDelIter, key, m.snapshot)
|
||||
if !l.tombstone.Empty() && l.tombstone.Contains(m.heap.cmp, key) &&
|
||||
l.tombstone = keyspan.SeekGE(m.heap.cmp, rangeDelIter, key)
|
||||
if l.tombstone != nil && l.tombstone.VisibleAt(m.snapshot) && l.tombstone.Contains(m.heap.cmp, key) &&
|
||||
(l.smallestUserKey == nil || m.heap.cmp(l.smallestUserKey, key) <= 0) {
|
||||
// NB: Based on the comment above l.largestUserKey >= key, and based on the
|
||||
// containment condition tombstone.End > key, so the assignment to key results
|
||||
@@ -904,10 +983,10 @@ func (m *mergingIter) String() string {
|
||||
// SeekGE implements base.InternalIterator.SeekGE. Note that SeekGE only checks
|
||||
// the upper bound. It is up to the caller to ensure that key is greater than
|
||||
// or equal to the lower bound.
|
||||
func (m *mergingIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (m *mergingIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = nil
|
||||
m.seekGE(key, 0 /* start level */, trySeekUsingNext)
|
||||
m.seekGE(key, 0 /* start level */, flags)
|
||||
return m.findNextEntry()
|
||||
}
|
||||
|
||||
@@ -915,16 +994,16 @@ func (m *mergingIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, [
|
||||
// SeekPrefixGE only checks the upper bound. It is up to the caller to ensure
|
||||
// that key is greater than or equal to the lower bound.
|
||||
func (m *mergingIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = prefix
|
||||
m.seekGE(key, 0 /* start level */, trySeekUsingNext)
|
||||
m.seekGE(key, 0 /* start level */, flags)
|
||||
return m.findNextEntry()
|
||||
}
|
||||
|
||||
// Seeks levels >= level to < key. Additionally uses range tombstones to extend the seeks.
|
||||
func (m *mergingIter) seekLT(key []byte, level int) {
|
||||
func (m *mergingIter) seekLT(key []byte, level int, flags base.SeekLTFlags) {
|
||||
// See the comment in seekGE regarding using tombstones to adjust the seek
|
||||
// target per level.
|
||||
m.prefix = nil
|
||||
@@ -934,9 +1013,16 @@ func (m *mergingIter) seekLT(key []byte, level int) {
|
||||
}
|
||||
|
||||
l := &m.levels[level]
|
||||
l.iterKey, l.iterValue = l.iter.SeekLT(key)
|
||||
l.iterKey, l.iterValue = l.iter.SeekLT(key, flags)
|
||||
|
||||
if rangeDelIter := l.rangeDelIter; rangeDelIter != nil {
|
||||
// If this level contains overlapping range tombstones, alter the seek
|
||||
// key accordingly. Caveat: If we're performing lazy-combined iteration,
|
||||
// we cannot alter the seek key: Range tombstones don't delete range
|
||||
// keys, and there might exist live range keys within the range
|
||||
// tombstone's span that need to be observed to trigger a switch to
|
||||
// combined iteration.
|
||||
if rangeDelIter := l.rangeDelIter; rangeDelIter != nil &&
|
||||
(m.combinedIterState == nil || m.combinedIterState.initialized) {
|
||||
// The level has a range-del iterator. Find the tombstone containing
|
||||
// the search key.
|
||||
//
|
||||
@@ -957,8 +1043,9 @@ func (m *mergingIter) seekLT(key []byte, level int) {
|
||||
withinLargestSSTableBound = cmpResult > 0 || (cmpResult == 0 && !l.isLargestUserKeyRangeDelSentinel)
|
||||
}
|
||||
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, rangeDelIter, key, m.snapshot)
|
||||
if !l.tombstone.Empty() && l.tombstone.Contains(m.heap.cmp, key) && withinLargestSSTableBound {
|
||||
l.tombstone = keyspan.SeekLE(m.heap.cmp, rangeDelIter, key)
|
||||
if l.tombstone != nil && l.tombstone.VisibleAt(m.snapshot) &&
|
||||
l.tombstone.Contains(m.heap.cmp, key) && withinLargestSSTableBound {
|
||||
// NB: Based on the comment above l.smallestUserKey <= key, and based
|
||||
// on the containment condition tombstone.Start.UserKey <= key, so the
|
||||
// assignment to key results in a monotonically non-increasing key
|
||||
@@ -986,17 +1073,17 @@ func (m *mergingIter) seekLT(key []byte, level int) {
|
||||
// SeekLT implements base.InternalIterator.SeekLT. Note that SeekLT only checks
|
||||
// the lower bound. It is up to the caller to ensure that key is less than the
|
||||
// upper bound.
|
||||
func (m *mergingIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (m *mergingIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = nil
|
||||
m.seekLT(key, 0 /* start level */)
|
||||
m.seekLT(key, 0 /* start level */, flags)
|
||||
return m.findPrevEntry()
|
||||
}
|
||||
|
||||
// First implements base.InternalIterator.First. Note that First only checks
|
||||
// the upper bound. It is up to the caller to ensure that key is greater than
|
||||
// or equal to the lower bound (e.g. via a call to SeekGE(lower)).
|
||||
func (m *mergingIter) First() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) First() (*InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = nil
|
||||
m.heap.items = m.heap.items[:0]
|
||||
@@ -1011,7 +1098,7 @@ func (m *mergingIter) First() (*InternalKey, []byte) {
|
||||
// Last implements base.InternalIterator.Last. Note that Last only checks the
|
||||
// lower bound. It is up to the caller to ensure that key is less than the
|
||||
// upper bound (e.g. via a call to SeekLT(upper))
|
||||
func (m *mergingIter) Last() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) Last() (*InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = nil
|
||||
for i := range m.levels {
|
||||
@@ -1022,9 +1109,9 @@ func (m *mergingIter) Last() (*InternalKey, []byte) {
|
||||
return m.findPrevEntry()
|
||||
}
|
||||
|
||||
func (m *mergingIter) Next() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if m.err != nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
if m.dir != 1 {
|
||||
@@ -1033,29 +1120,29 @@ func (m *mergingIter) Next() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
if m.heap.len() == 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
m.nextEntry(&m.heap.items[0])
|
||||
return m.findNextEntry()
|
||||
}
|
||||
|
||||
func (m *mergingIter) Prev() (*InternalKey, []byte) {
|
||||
func (m *mergingIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if m.err != nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
if m.dir != -1 {
|
||||
if m.prefix != nil {
|
||||
m.err = errors.New("pebble: unsupported reverse prefix iteration")
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
m.switchToMaxHeap()
|
||||
return m.findPrevEntry()
|
||||
}
|
||||
|
||||
if m.heap.len() == 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
m.prevEntry(&m.heap.items[0])
|
||||
@@ -1125,24 +1212,7 @@ func (m *mergingIter) ForEachLevelIter(fn func(li *levelIter) bool) {
|
||||
func (m *mergingIter) addItemStats(item *mergingIterItem) {
|
||||
m.stats.PointCount++
|
||||
m.stats.KeyBytes += uint64(len(item.key.UserKey))
|
||||
m.stats.ValueBytes += uint64(len(item.value))
|
||||
m.stats.ValueBytes += uint64(len(item.value.ValueOrHandle))
|
||||
}
|
||||
|
||||
var _ internalIteratorWithStats = &mergingIter{}
|
||||
|
||||
// Stats implements InternalIteratorWithStats.
|
||||
func (m *mergingIter) Stats() base.InternalIteratorStats {
|
||||
stats := m.stats
|
||||
for i := range m.levels {
|
||||
stats.Merge(m.levels[i].iter.Stats())
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
// ResetStats implements InternalIteratorWithStats.
|
||||
func (m *mergingIter) ResetStats() {
|
||||
m.stats = InternalIteratorStats{}
|
||||
for i := range m.levels {
|
||||
m.levels[i].iter.ResetStats()
|
||||
}
|
||||
}
|
||||
var _ internalIterator = &mergingIter{}
|
||||
|
||||
4
vendor/github.com/cockroachdb/pebble/merging_iter_heap.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/merging_iter_heap.go
generated
vendored
@@ -4,10 +4,12 @@
|
||||
|
||||
package pebble
|
||||
|
||||
import "github.com/cockroachdb/pebble/internal/base"
|
||||
|
||||
type mergingIterItem struct {
|
||||
index int
|
||||
key InternalKey
|
||||
value []byte
|
||||
value base.LazyValue
|
||||
}
|
||||
|
||||
type mergingIterHeap struct {
|
||||
|
||||
70
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
70
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
@@ -6,13 +6,15 @@ package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/cache"
|
||||
"github.com/cockroachdb/pebble/internal/humanize"
|
||||
"github.com/cockroachdb/pebble/record"
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
"github.com/cockroachdb/redact"
|
||||
"github.com/codahale/hdrhistogram"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// CacheMetrics holds metrics for the block and table cache.
|
||||
@@ -140,6 +142,7 @@ type Metrics struct {
|
||||
MoveCount int64
|
||||
ReadCount int64
|
||||
RewriteCount int64
|
||||
MultiLevelCount int64
|
||||
// An estimate of the number of bytes that need to be compacted for the LSM
|
||||
// to reach a stable state.
|
||||
EstimatedDebt uint64
|
||||
@@ -157,7 +160,8 @@ type Metrics struct {
|
||||
|
||||
Flush struct {
|
||||
// The total number of flushes.
|
||||
Count int64
|
||||
Count int64
|
||||
WriteThroughput ThroughputMetric
|
||||
}
|
||||
|
||||
Filter FilterMetrics
|
||||
@@ -177,6 +181,11 @@ type Metrics struct {
|
||||
ZombieCount int64
|
||||
}
|
||||
|
||||
Keys struct {
|
||||
// The approximate count of internal range key set keys in the database.
|
||||
RangeKeySetsCount uint64
|
||||
}
|
||||
|
||||
Snapshots struct {
|
||||
// The number of currently open snapshots.
|
||||
Count int
|
||||
@@ -221,12 +230,26 @@ type Metrics struct {
|
||||
BytesWritten uint64
|
||||
}
|
||||
|
||||
LogWriter struct {
|
||||
FsyncLatency prometheus.Histogram
|
||||
record.LogWriterMetrics
|
||||
}
|
||||
|
||||
private struct {
|
||||
optionsFileSize uint64
|
||||
manifestFileSize uint64
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// FsyncLatencyBuckets are prometheus histogram buckets suitable for a histogram
|
||||
// that records latencies for fsyncs.
|
||||
FsyncLatencyBuckets = append(
|
||||
prometheus.LinearBuckets(0.0, float64(time.Microsecond*100), 50),
|
||||
prometheus.ExponentialBucketsRange(float64(time.Millisecond*5), float64(10*time.Second), 50)...,
|
||||
)
|
||||
)
|
||||
|
||||
// DiskSpaceUsage returns the total disk space used by the database in bytes,
|
||||
// including live and obsolete files.
|
||||
func (m *Metrics) DiskSpaceUsage() uint64 {
|
||||
@@ -386,13 +409,14 @@ func (m *Metrics) SafeFormat(w redact.SafePrinter, _ rune) {
|
||||
humanize.IEC.Int64(m.Compact.InProgressBytes),
|
||||
redact.Safe(m.Compact.NumInProgress),
|
||||
redact.SafeString(""))
|
||||
w.Printf(" ctype %9d %7d %7d %7d %7d %7d (default, delete, elision, move, read, rewrite)\n",
|
||||
w.Printf(" ctype %9d %7d %7d %7d %7d %7d %7d (default, delete, elision, move, read, rewrite, multi-level)\n",
|
||||
redact.Safe(m.Compact.DefaultCount),
|
||||
redact.Safe(m.Compact.DeleteOnlyCount),
|
||||
redact.Safe(m.Compact.ElisionOnlyCount),
|
||||
redact.Safe(m.Compact.MoveCount),
|
||||
redact.Safe(m.Compact.ReadCount),
|
||||
redact.Safe(m.Compact.RewriteCount))
|
||||
redact.Safe(m.Compact.RewriteCount),
|
||||
redact.Safe(m.Compact.MultiLevelCount))
|
||||
w.Printf(" memtbl %9d %7s\n",
|
||||
redact.Safe(m.MemTable.Count),
|
||||
humanize.IEC.Uint64(m.MemTable.Size))
|
||||
@@ -422,41 +446,3 @@ func hitRate(hits, misses int64) float64 {
|
||||
}
|
||||
return 100 * float64(hits) / float64(sum)
|
||||
}
|
||||
|
||||
// InternalIntervalMetrics exposes metrics about internal subsystems, that can
|
||||
// be useful for deep observability purposes, and for higher-level admission
|
||||
// control systems that are trying to estimate the capacity of the DB. These
|
||||
// are experimental and subject to change, since they expose internal
|
||||
// implementation details, so do not rely on these without discussion with the
|
||||
// Pebble team.
|
||||
// These represent the metrics over the interval of time from the last call to
|
||||
// retrieve these metrics. These are not cumulative, unlike Metrics. The main
|
||||
// challenge in making these cumulative is the hdrhistogram.Histogram, which
|
||||
// does not have the ability to subtract a histogram from a preceding metric
|
||||
// retrieval.
|
||||
type InternalIntervalMetrics struct {
|
||||
// LogWriter metrics.
|
||||
LogWriter struct {
|
||||
// WriteThroughput is the WAL throughput.
|
||||
WriteThroughput ThroughputMetric
|
||||
// PendingBufferUtilization is the utilization of the WAL writer's
|
||||
// finite-sized pending blocks buffer. It provides an additional signal
|
||||
// regarding how close to "full" the WAL writer is. The value is in the
|
||||
// interval [0,1].
|
||||
PendingBufferUtilization float64
|
||||
// SyncQueueUtilization is the utilization of the WAL writer's
|
||||
// finite-sized queue of work that is waiting to sync. The value is in the
|
||||
// interval [0,1].
|
||||
SyncQueueUtilization float64
|
||||
// SyncLatencyMicros is a distribution of the fsync latency observed by
|
||||
// the WAL writer. It can be nil if there were no fsyncs.
|
||||
SyncLatencyMicros *hdrhistogram.Histogram
|
||||
}
|
||||
// Flush loop metrics.
|
||||
Flush struct {
|
||||
// WriteThroughput is the flushing throughput.
|
||||
WriteThroughput ThroughputMetric
|
||||
}
|
||||
// NB: the LogWriter throughput and the Flush throughput are not directly
|
||||
// comparable because the former does not compress, unlike the latter.
|
||||
}
|
||||
|
||||
64
vendor/github.com/cockroachdb/pebble/open.go
generated
vendored
64
vendor/github.com/cockroachdb/pebble/open.go
generated
vendored
@@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -24,6 +23,7 @@ import (
|
||||
"github.com/cockroachdb/pebble/internal/rate"
|
||||
"github.com/cockroachdb/pebble/record"
|
||||
"github.com/cockroachdb/pebble/vfs"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -75,7 +75,6 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
merge: opts.Merger.Merge,
|
||||
split: opts.Comparer.Split,
|
||||
abbreviatedKey: opts.Comparer.AbbreviatedKey,
|
||||
rangeKeys: opts.Experimental.RangeKeys,
|
||||
largeBatchThreshold: (opts.MemTableSize - int(memTableEmptySize)) / 2,
|
||||
logRecycler: logRecycler{limit: opts.MemTableStopWritesThreshold + 1},
|
||||
closed: new(atomic.Value),
|
||||
@@ -125,12 +124,6 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
apply: d.commitApply,
|
||||
write: d.commitWrite,
|
||||
})
|
||||
d.compactionLimiter = rate.NewLimiter(
|
||||
rate.Limit(d.opts.private.minCompactionRate),
|
||||
d.opts.private.minCompactionRate)
|
||||
d.flushLimiter = rate.NewLimiter(
|
||||
rate.Limit(d.opts.private.minFlushRate),
|
||||
d.opts.private.minFlushRate)
|
||||
d.deletionLimiter = rate.NewLimiter(
|
||||
rate.Limit(d.opts.Experimental.MinDeletionRate),
|
||||
d.opts.Experimental.MinDeletionRate)
|
||||
@@ -161,6 +154,24 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we close resources if we error out early. If the database is
|
||||
// successfully opened, the named return value `db` will be set to `d`.
|
||||
defer func() {
|
||||
if db != nil {
|
||||
// The database was successfully opened.
|
||||
return
|
||||
}
|
||||
if d.dataDir != nil {
|
||||
d.dataDir.Close()
|
||||
}
|
||||
if d.walDirname != d.dirname && d.walDir != nil {
|
||||
d.walDir.Close()
|
||||
}
|
||||
if d.mu.formatVers.marker != nil {
|
||||
d.mu.formatVers.marker.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Open the database and WAL directories first in order to check for their
|
||||
// existence.
|
||||
var err error
|
||||
@@ -220,6 +231,15 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
// Find the currently active manifest, if there is one.
|
||||
manifestMarker, manifestFileNum, exists, err := findCurrentManifest(d.mu.formatVers.vers, opts.FS, dirname)
|
||||
setCurrent := setCurrentFunc(d.mu.formatVers.vers, manifestMarker, opts.FS, dirname, d.dataDir)
|
||||
defer func() {
|
||||
// Ensure we close the manifest marker if we error out for any reason.
|
||||
// If the database is successfully opened, the *versionSet will take
|
||||
// ownership over the manifest marker, ensuring it's closed when the DB
|
||||
// is closed.
|
||||
if db == nil {
|
||||
manifestMarker.Close()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "pebble: database %q", dirname)
|
||||
} else if !exists && !d.opts.ReadOnly && !d.opts.ErrorIfNotExists {
|
||||
@@ -395,14 +415,22 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
d.mu.mem.queue[len(d.mu.mem.queue)-1].logNum = newLogNum
|
||||
|
||||
logFile = vfs.NewSyncingFile(logFile, vfs.SyncingFileOptions{
|
||||
NoSyncOnClose: d.opts.NoSyncOnClose,
|
||||
BytesPerSync: d.opts.WALBytesPerSync,
|
||||
PreallocateSize: d.walPreallocateSize(),
|
||||
})
|
||||
d.mu.log.LogWriter = record.NewLogWriter(logFile, newLogNum)
|
||||
d.mu.log.LogWriter.SetMinSyncInterval(d.opts.WALMinSyncInterval)
|
||||
d.mu.log.metrics.fsyncLatency = prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Buckets: FsyncLatencyBuckets,
|
||||
})
|
||||
|
||||
logWriterConfig := record.LogWriterConfig{
|
||||
WALMinSyncInterval: d.opts.WALMinSyncInterval,
|
||||
WALFsyncLatency: d.mu.log.metrics.fsyncLatency,
|
||||
}
|
||||
d.mu.log.LogWriter = record.NewLogWriter(logFile, newLogNum, logWriterConfig)
|
||||
d.mu.versions.metrics.WAL.Files++
|
||||
}
|
||||
d.updateReadStateLocked(d.opts.DebugCheck, nil)
|
||||
d.updateReadStateLocked(d.opts.DebugCheck)
|
||||
|
||||
if !d.opts.ReadOnly {
|
||||
// Write the current options to disk.
|
||||
@@ -517,7 +545,7 @@ func GetVersion(dir string, fs vfs.FS) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
data, err := ioutil.ReadAll(f)
|
||||
data, err := io.ReadAll(f)
|
||||
f.Close()
|
||||
|
||||
if err != nil {
|
||||
@@ -679,14 +707,8 @@ func (d *DB) replayWAL(
|
||||
// mem is nil here.
|
||||
if !d.opts.ReadOnly {
|
||||
c := newFlush(d.opts, d.mu.versions.currentVersion(),
|
||||
1 /* base level */, toFlush, &d.atomic.bytesFlushed)
|
||||
newVE, _, err := d.runCompaction(jobID, c, nilPacer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// TODO(jackson): Remove the below call to applyFlushedRangeKeys once
|
||||
// flushes actually persist range keys to sstables.
|
||||
err = d.applyFlushedRangeKeys(toFlush)
|
||||
1 /* base level */, toFlush)
|
||||
newVE, _, err := d.runCompaction(jobID, c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -705,7 +727,7 @@ func checkOptions(opts *Options, path string) (strictWALTail bool, err error) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
data, err := io.ReadAll(f)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
219
vendor/github.com/cockroachdb/pebble/options.go
generated
vendored
219
vendor/github.com/cockroachdb/pebble/options.go
generated
vendored
@@ -23,7 +23,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
cacheDefaultSize = 8 << 20 // 8 MB
|
||||
cacheDefaultSize = 8 << 20 // 8 MB
|
||||
defaultLevelMultiplier = 10
|
||||
)
|
||||
|
||||
// Compression exports the base.Compression type.
|
||||
@@ -163,11 +164,15 @@ type IterOptions struct {
|
||||
// existing is not low or if we just expect a one-time Seek (where loading the
|
||||
// data block directly is better).
|
||||
UseL6Filters bool
|
||||
|
||||
// Internal options.
|
||||
|
||||
logger Logger
|
||||
// Level corresponding to this file. Only passed in if constructed by a
|
||||
// levelIter.
|
||||
level manifest.Level
|
||||
// disableLazyCombinedIteration is an internal testing option.
|
||||
disableLazyCombinedIteration bool
|
||||
|
||||
// NB: If adding new Options, you must account for them in iterator
|
||||
// construction and Iterator.SetOptions.
|
||||
@@ -212,9 +217,9 @@ func (o *IterOptions) getLogger() Logger {
|
||||
|
||||
// RangeKeyMasking configures automatic hiding of point keys by range keys. A
|
||||
// non-nil Suffix enables range-key masking. When enabled, range keys with
|
||||
// suffixes ≤ Suffix behave as masks. All point keys that are contained within a
|
||||
// masking range key's bounds and have suffixes less than the range key's suffix
|
||||
// are automatically skipped.
|
||||
// suffixes ≥ Suffix behave as masks. All point keys that are contained within a
|
||||
// masking range key's bounds and have suffixes greater than the range key's
|
||||
// suffix are automatically skipped.
|
||||
//
|
||||
// Specifically, when configured with a RangeKeyMasking.Suffix _s_, and there
|
||||
// exists a range key with suffix _r_ covering a point key with suffix _p_, and
|
||||
@@ -227,12 +232,58 @@ func (o *IterOptions) getLogger() Logger {
|
||||
// range keys with IterKeyTypePointsAndRanges.
|
||||
type RangeKeyMasking struct {
|
||||
// Suffix configures which range keys may mask point keys. Only range keys
|
||||
// that are defined at suffixes less than or equal to Suffix will mask point
|
||||
// keys.
|
||||
// that are defined at suffixes greater than or equal to Suffix will mask
|
||||
// point keys.
|
||||
Suffix []byte
|
||||
// Filter is an optional field that may be used to improve performance of
|
||||
// range-key masking through a block-property filter defined over key
|
||||
// suffixes. If non-nil, Filter is called by Pebble to construct a
|
||||
// block-property filter mask at iterator creation. The filter is used to
|
||||
// skip whole point-key blocks containing point keys with suffixes greater
|
||||
// than a covering range-key's suffix.
|
||||
//
|
||||
// To use this functionality, the caller must create and configure (through
|
||||
// Options.BlockPropertyCollectors) a block-property collector that records
|
||||
// the maxmimum suffix contained within a block. The caller then must write
|
||||
// and provide a BlockPropertyFilterMask implementation on that same
|
||||
// property. See the BlockPropertyFilterMask type for more information.
|
||||
Filter func() BlockPropertyFilterMask
|
||||
}
|
||||
|
||||
// TODO(jackson): Add fields necessary for constructing and updating block
|
||||
// property collectors.
|
||||
// BlockPropertyFilterMask extends the BlockPropertyFilter interface for use
|
||||
// with range-key masking. Unlike an ordinary block property filter, a
|
||||
// BlockPropertyFilterMask's filtering criteria is allowed to change when Pebble
|
||||
// invokes its SetSuffix method.
|
||||
//
|
||||
// When a Pebble iterator steps into a range key's bounds and the range key has
|
||||
// a suffix greater than or equal to RangeKeyMasking.Suffix, the range key acts
|
||||
// as a mask. The masking range key hides all point keys that fall within the
|
||||
// range key's bounds and have suffixes > the range key's suffix. Without a
|
||||
// filter mask configured, Pebble performs this hiding by stepping through point
|
||||
// keys and comparing suffixes. If large numbers of point keys are masked, this
|
||||
// requires Pebble to load, iterate through and discard a large number of
|
||||
// sstable blocks containing masked point keys.
|
||||
//
|
||||
// If a block-property collector and a filter mask are configured, Pebble may
|
||||
// skip loading some point-key blocks altogether. If a block's keys are known to
|
||||
// all fall within the bounds of the masking range key and the block was
|
||||
// annotated by a block-property collector with the maximal suffix, Pebble can
|
||||
// ask the filter mask to compare the property to the current masking range
|
||||
// key's suffix. If the mask reports no intersection, the block may be skipped.
|
||||
//
|
||||
// If unsuffixed and suffixed keys are written to the database, care must be
|
||||
// taken to avoid unintentionally masking un-suffixed keys located in the same
|
||||
// block as suffixed keys. One solution is to interpret unsuffixed keys as
|
||||
// containing the maximal suffix value, ensuring that blocks containing
|
||||
// unsuffixed keys are always loaded.
|
||||
type BlockPropertyFilterMask interface {
|
||||
BlockPropertyFilter
|
||||
|
||||
// SetSuffix configures the mask with the suffix of a range key. The filter
|
||||
// should return false from Intersects whenever it's provided with a
|
||||
// property encoding a block's minimum suffix that's greater (according to
|
||||
// Compare) than the provided suffix.
|
||||
SetSuffix(suffix []byte) error
|
||||
}
|
||||
|
||||
// WriteOptions hold the optional per-query parameters for Set and Delete
|
||||
@@ -429,12 +480,6 @@ type Options struct {
|
||||
// concurrency slots as determined by the two options is chosen.
|
||||
CompactionDebtConcurrency int
|
||||
|
||||
// DeleteRangeFlushDelay configures how long the database should wait
|
||||
// before forcing a flush of a memtable that contains a range
|
||||
// deletion. Disk space cannot be reclaimed until the range deletion
|
||||
// is flushed. No automatic flush occurs if zero.
|
||||
DeleteRangeFlushDelay time.Duration
|
||||
|
||||
// MinDeletionRate is the minimum number of bytes per second that would
|
||||
// be deleted. Deletion pacing is used to slow down deletions when
|
||||
// compactions finish up or readers close, and newly-obsolete files need
|
||||
@@ -446,12 +491,6 @@ type Options struct {
|
||||
// deletion pacing, which is also the default.
|
||||
MinDeletionRate int
|
||||
|
||||
// RangeKeys enables the experimental use of range keys, stored in an
|
||||
// in-memory nondurable arena. This option is only intended to be
|
||||
// temporary, to allow the Pebble metamorphic tests to reuse the
|
||||
// range-key arena across DB restarts.
|
||||
RangeKeys *RangeKeysArena
|
||||
|
||||
// ReadCompactionRate controls the frequency of read triggered
|
||||
// compactions by adjusting `AllowedSeeks` in manifest.FileMetadata:
|
||||
//
|
||||
@@ -511,12 +550,38 @@ type Options struct {
|
||||
// By default, this value is false.
|
||||
ValidateOnIngest bool
|
||||
|
||||
// LevelMultiplier configures the size multiplier used to determine the
|
||||
// desired size of each level of the LSM. Defaults to 10.
|
||||
LevelMultiplier int
|
||||
|
||||
// MultiLevelCompaction allows the compaction of SSTs from more than two
|
||||
// levels iff a conventional two level compaction will quickly trigger a
|
||||
// compaction in the output level.
|
||||
MultiLevelCompaction bool
|
||||
|
||||
// MaxWriterConcurrency is used to indicate the maximum number of
|
||||
// compression workers the compression queue is allowed to use. If
|
||||
// MaxWriterConcurrency > 0, then the Writer will use parallelism, to
|
||||
// compress and write blocks to disk. Otherwise, the writer will
|
||||
// compress and write blocks to disk synchronously.
|
||||
MaxWriterConcurrency int
|
||||
|
||||
// ForceWriterParallelism is used to force parallelism in the sstable
|
||||
// Writer for the metamorphic tests. Even with the MaxWriterConcurrency
|
||||
// option set, we only enable parallelism in the sstable Writer if there
|
||||
// is enough CPU available, and this option bypasses that.
|
||||
ForceWriterParallelism bool
|
||||
|
||||
// CPUWorkPermissionGranter should be set if Pebble should be given the
|
||||
// ability to optionally schedule additional CPU. See the documentation
|
||||
// for CPUWorkPermissionGranter for more details.
|
||||
CPUWorkPermissionGranter CPUWorkPermissionGranter
|
||||
|
||||
// PointTombstoneWeight is a float in the range [0, +inf) used to weight the
|
||||
// point tombstone heuristics during compaction picking.
|
||||
//
|
||||
// The default value is 1, which results in no scaling of point tombstones.
|
||||
PointTombstoneWeight float64
|
||||
}
|
||||
|
||||
// Filters is a map from filter policy name to filter policy. It is used for
|
||||
@@ -525,6 +590,18 @@ type Options struct {
|
||||
// map during normal usage of a DB.
|
||||
Filters map[string]FilterPolicy
|
||||
|
||||
// FlushDelayDeleteRange configures how long the database should wait before
|
||||
// forcing a flush of a memtable that contains a range deletion. Disk space
|
||||
// cannot be reclaimed until the range deletion is flushed. No automatic
|
||||
// flush occurs if zero.
|
||||
FlushDelayDeleteRange time.Duration
|
||||
|
||||
// FlushDelayRangeKey configures how long the database should wait before
|
||||
// forcing a flush of a memtable that contains a range key. Range keys in
|
||||
// the memtable prevent lazy combined iteration, so it's desirable to flush
|
||||
// range keys promptly. No automatic flush occurs if zero.
|
||||
FlushDelayRangeKey time.Duration
|
||||
|
||||
// FlushSplitBytes denotes the target number of bytes per sublevel in
|
||||
// each flush split interval (i.e. range between two flush split keys)
|
||||
// in L0 sstables. When set to zero, only a single sstable is generated
|
||||
@@ -622,13 +699,20 @@ type Options struct {
|
||||
// - when L0 read-amplification passes the L0CompactionConcurrency threshold
|
||||
// - for automatic background compactions
|
||||
// - when a manual compaction for a level is split and parallelized
|
||||
MaxConcurrentCompactions int
|
||||
// MaxConcurrentCompactions must be greater than 0.
|
||||
MaxConcurrentCompactions func() int
|
||||
|
||||
// DisableAutomaticCompactions dictates whether automatic compactions are
|
||||
// scheduled or not. The default is false (enabled). This option is only used
|
||||
// externally when running a manual compaction, and internally for tests.
|
||||
DisableAutomaticCompactions bool
|
||||
|
||||
// NoSyncOnClose decides whether the Pebble instance will enforce a
|
||||
// close-time synchronization (e.g., fdatasync() or sync_file_range())
|
||||
// on files it writes to. Setting this to true removes the guarantee for a
|
||||
// sync on close. Some implementations can still issue a non-blocking sync.
|
||||
NoSyncOnClose bool
|
||||
|
||||
// NumPrevManifest is the number of non-current or older manifests which
|
||||
// we want to keep around for debugging purposes. By default, we're going
|
||||
// to keep one older manifest.
|
||||
@@ -699,24 +783,9 @@ type Options struct {
|
||||
// RocksDB 6.2.1.
|
||||
strictWALTail bool
|
||||
|
||||
// TODO(peter): A private option to enable flush/compaction pacing. Only used
|
||||
// by tests. Compaction/flush pacing is disabled until we fix the impact on
|
||||
// throughput.
|
||||
enablePacing bool
|
||||
|
||||
// A private option to disable stats collection.
|
||||
disableTableStats bool
|
||||
|
||||
// minCompactionRate sets the minimum rate at which compactions occur. The
|
||||
// default is 4 MB/s. Currently disabled as this option has no effect while
|
||||
// private.enablePacing is false.
|
||||
minCompactionRate int
|
||||
|
||||
// minFlushRate sets the minimum rate at which the MemTables are flushed. The
|
||||
// default is 1 MB/s. Currently disabled as this option has no effect while
|
||||
// private.enablePacing is false.
|
||||
minFlushRate int
|
||||
|
||||
// fsCloser holds a closer that should be invoked after a DB using these
|
||||
// Options is closed. This is used to automatically stop the
|
||||
// long-running goroutine associated with the disk-health-checking FS.
|
||||
@@ -726,6 +795,13 @@ type Options struct {
|
||||
// against the FS are made after the DB is closed, the FS may leak a
|
||||
// goroutine indefinitely.
|
||||
fsCloser io.Closer
|
||||
|
||||
// disableLazyCombinedIteration is a private option used by the
|
||||
// metamorphic tests to test equivalence between lazy-combined iteration
|
||||
// and constructing the range-key iterator upfront. It's a private
|
||||
// option to avoid littering the public interface with options that we
|
||||
// do not want to allow users to actually configure.
|
||||
disableLazyCombinedIteration bool
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,14 +907,8 @@ func (o *Options) EnsureDefaults() *Options {
|
||||
o.Merger = DefaultMerger
|
||||
}
|
||||
o.private.strictWALTail = true
|
||||
if o.private.minCompactionRate == 0 {
|
||||
o.private.minCompactionRate = 4 << 20 // 4 MB/s
|
||||
}
|
||||
if o.private.minFlushRate == 0 {
|
||||
o.private.minFlushRate = 1 << 20 // 1 MB/s
|
||||
}
|
||||
if o.MaxConcurrentCompactions <= 0 {
|
||||
o.MaxConcurrentCompactions = 1
|
||||
if o.MaxConcurrentCompactions == nil {
|
||||
o.MaxConcurrentCompactions = func() int { return 1 }
|
||||
}
|
||||
if o.NumPrevManifest <= 0 {
|
||||
o.NumPrevManifest = 1
|
||||
@@ -860,6 +930,9 @@ func (o *Options) EnsureDefaults() *Options {
|
||||
if o.FlushSplitBytes <= 0 {
|
||||
o.FlushSplitBytes = 2 * o.Levels[0].TargetFileSize
|
||||
}
|
||||
if o.Experimental.LevelMultiplier <= 0 {
|
||||
o.Experimental.LevelMultiplier = defaultLevelMultiplier
|
||||
}
|
||||
if o.Experimental.ReadCompactionRate == 0 {
|
||||
o.Experimental.ReadCompactionRate = 16000
|
||||
}
|
||||
@@ -869,6 +942,12 @@ func (o *Options) EnsureDefaults() *Options {
|
||||
if o.Experimental.TableCacheShards <= 0 {
|
||||
o.Experimental.TableCacheShards = runtime.GOMAXPROCS(0)
|
||||
}
|
||||
if o.Experimental.CPUWorkPermissionGranter == nil {
|
||||
o.Experimental.CPUWorkPermissionGranter = defaultCPUWorkGranter{}
|
||||
}
|
||||
if o.Experimental.PointTombstoneWeight == 0 {
|
||||
o.Experimental.PointTombstoneWeight = 1
|
||||
}
|
||||
|
||||
o.initMaps()
|
||||
return o
|
||||
@@ -943,8 +1022,9 @@ func (o *Options) String() string {
|
||||
fmt.Fprintf(&buf, " cleaner=%s\n", o.Cleaner)
|
||||
fmt.Fprintf(&buf, " compaction_debt_concurrency=%d\n", o.Experimental.CompactionDebtConcurrency)
|
||||
fmt.Fprintf(&buf, " comparer=%s\n", o.Comparer.Name)
|
||||
fmt.Fprintf(&buf, " delete_range_flush_delay=%s\n", o.Experimental.DeleteRangeFlushDelay)
|
||||
fmt.Fprintf(&buf, " disable_wal=%t\n", o.DisableWAL)
|
||||
fmt.Fprintf(&buf, " flush_delay_delete_range=%s\n", o.FlushDelayDeleteRange)
|
||||
fmt.Fprintf(&buf, " flush_delay_range_key=%s\n", o.FlushDelayRangeKey)
|
||||
fmt.Fprintf(&buf, " flush_split_bytes=%d\n", o.FlushSplitBytes)
|
||||
fmt.Fprintf(&buf, " format_major_version=%d\n", o.FormatMajorVersion)
|
||||
fmt.Fprintf(&buf, " l0_compaction_concurrency=%d\n", o.Experimental.L0CompactionConcurrency)
|
||||
@@ -952,15 +1032,17 @@ func (o *Options) String() string {
|
||||
fmt.Fprintf(&buf, " l0_compaction_threshold=%d\n", o.L0CompactionThreshold)
|
||||
fmt.Fprintf(&buf, " l0_stop_writes_threshold=%d\n", o.L0StopWritesThreshold)
|
||||
fmt.Fprintf(&buf, " lbase_max_bytes=%d\n", o.LBaseMaxBytes)
|
||||
fmt.Fprintf(&buf, " max_concurrent_compactions=%d\n", o.MaxConcurrentCompactions)
|
||||
if o.Experimental.LevelMultiplier != defaultLevelMultiplier {
|
||||
fmt.Fprintf(&buf, " level_multiplier=%d\n", o.Experimental.LevelMultiplier)
|
||||
}
|
||||
fmt.Fprintf(&buf, " max_concurrent_compactions=%d\n", o.MaxConcurrentCompactions())
|
||||
fmt.Fprintf(&buf, " max_manifest_file_size=%d\n", o.MaxManifestFileSize)
|
||||
fmt.Fprintf(&buf, " max_open_files=%d\n", o.MaxOpenFiles)
|
||||
fmt.Fprintf(&buf, " mem_table_size=%d\n", o.MemTableSize)
|
||||
fmt.Fprintf(&buf, " mem_table_stop_writes_threshold=%d\n", o.MemTableStopWritesThreshold)
|
||||
fmt.Fprintf(&buf, " min_compaction_rate=%d\n", o.private.minCompactionRate)
|
||||
fmt.Fprintf(&buf, " min_deletion_rate=%d\n", o.Experimental.MinDeletionRate)
|
||||
fmt.Fprintf(&buf, " min_flush_rate=%d\n", o.private.minFlushRate)
|
||||
fmt.Fprintf(&buf, " merger=%s\n", o.Merger.Name)
|
||||
fmt.Fprintf(&buf, " point_tombstone_weight=%f\n", o.Experimental.PointTombstoneWeight)
|
||||
fmt.Fprintf(&buf, " read_compaction_rate=%d\n", o.Experimental.ReadCompactionRate)
|
||||
fmt.Fprintf(&buf, " read_sampling_multiplier=%d\n", o.Experimental.ReadSamplingMultiplier)
|
||||
fmt.Fprintf(&buf, " strict_wal_tail=%t\n", o.private.strictWALTail)
|
||||
@@ -979,6 +1061,16 @@ func (o *Options) String() string {
|
||||
fmt.Fprintf(&buf, " wal_dir=%s\n", o.WALDir)
|
||||
fmt.Fprintf(&buf, " wal_bytes_per_sync=%d\n", o.WALBytesPerSync)
|
||||
fmt.Fprintf(&buf, " max_writer_concurrency=%d\n", o.Experimental.MaxWriterConcurrency)
|
||||
fmt.Fprintf(&buf, " force_writer_parallelism=%t\n", o.Experimental.ForceWriterParallelism)
|
||||
|
||||
// Private options.
|
||||
if o.private.disableLazyCombinedIteration {
|
||||
// This option is only encoded if true, because we do not want it to
|
||||
// appear in production serialized Options files, since it's a
|
||||
// testing-only option. It's only serialized when true, which still
|
||||
// ensures that the metamorphic tests may propagate it to subprocesses.
|
||||
fmt.Fprintln(&buf, " disable_lazy_combined_iteration=true")
|
||||
}
|
||||
|
||||
for i := range o.Levels {
|
||||
l := &o.Levels[i]
|
||||
@@ -1117,9 +1209,17 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
||||
case "compaction_debt_concurrency":
|
||||
o.Experimental.CompactionDebtConcurrency, err = strconv.Atoi(value)
|
||||
case "delete_range_flush_delay":
|
||||
o.Experimental.DeleteRangeFlushDelay, err = time.ParseDuration(value)
|
||||
// NB: This is a deprecated serialization of the
|
||||
// `flush_delay_delete_range`.
|
||||
o.FlushDelayDeleteRange, err = time.ParseDuration(value)
|
||||
case "disable_wal":
|
||||
o.DisableWAL, err = strconv.ParseBool(value)
|
||||
case "disable_lazy_combined_iteration":
|
||||
o.private.disableLazyCombinedIteration, err = strconv.ParseBool(value)
|
||||
case "flush_delay_delete_range":
|
||||
o.FlushDelayDeleteRange, err = time.ParseDuration(value)
|
||||
case "flush_delay_range_key":
|
||||
o.FlushDelayRangeKey, err = time.ParseDuration(value)
|
||||
case "flush_split_bytes":
|
||||
o.FlushSplitBytes, err = strconv.ParseInt(value, 10, 64)
|
||||
case "format_major_version":
|
||||
@@ -1147,8 +1247,16 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
||||
// Do nothing; option existed in older versions of pebble.
|
||||
case "lbase_max_bytes":
|
||||
o.LBaseMaxBytes, err = strconv.ParseInt(value, 10, 64)
|
||||
case "level_multiplier":
|
||||
o.Experimental.LevelMultiplier, err = strconv.Atoi(value)
|
||||
case "max_concurrent_compactions":
|
||||
o.MaxConcurrentCompactions, err = strconv.Atoi(value)
|
||||
var concurrentCompactions int
|
||||
concurrentCompactions, err = strconv.Atoi(value)
|
||||
if concurrentCompactions <= 0 {
|
||||
err = errors.New("max_concurrent_compactions cannot be <= 0")
|
||||
} else {
|
||||
o.MaxConcurrentCompactions = func() int { return concurrentCompactions }
|
||||
}
|
||||
case "max_manifest_file_size":
|
||||
o.MaxManifestFileSize, err = strconv.ParseInt(value, 10, 64)
|
||||
case "max_open_files":
|
||||
@@ -1158,11 +1266,15 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
||||
case "mem_table_stop_writes_threshold":
|
||||
o.MemTableStopWritesThreshold, err = strconv.Atoi(value)
|
||||
case "min_compaction_rate":
|
||||
o.private.minCompactionRate, err = strconv.Atoi(value)
|
||||
// Do nothing; option existed in older versions of pebble, and
|
||||
// may be meaningful again eventually.
|
||||
case "min_deletion_rate":
|
||||
o.Experimental.MinDeletionRate, err = strconv.Atoi(value)
|
||||
case "min_flush_rate":
|
||||
o.private.minFlushRate, err = strconv.Atoi(value)
|
||||
// Do nothing; option existed in older versions of pebble, and
|
||||
// may be meaningful again eventually.
|
||||
case "point_tombstone_weight":
|
||||
o.Experimental.PointTombstoneWeight, err = strconv.ParseFloat(value, 64)
|
||||
case "strict_wal_tail":
|
||||
o.private.strictWALTail, err = strconv.ParseBool(value)
|
||||
case "merger":
|
||||
@@ -1199,6 +1311,8 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
||||
o.WALBytesPerSync, err = strconv.Atoi(value)
|
||||
case "max_writer_concurrency":
|
||||
o.Experimental.MaxWriterConcurrency, err = strconv.Atoi(value)
|
||||
case "force_writer_parallelism":
|
||||
o.Experimental.ForceWriterParallelism, err = strconv.ParseBool(value)
|
||||
default:
|
||||
if hooks != nil && hooks.SkipUnknown != nil && hooks.SkipUnknown(section+"."+key, value) {
|
||||
return nil
|
||||
@@ -1383,6 +1497,5 @@ func (o *Options) MakeWriterOptions(level int, format sstable.TableFormat) sstab
|
||||
writerOpts.FilterPolicy = levelOpts.FilterPolicy
|
||||
writerOpts.FilterType = levelOpts.FilterType
|
||||
writerOpts.IndexBlockSize = levelOpts.IndexBlockSize
|
||||
writerOpts.Parallelism = o.Experimental.MaxWriterConcurrency > 0
|
||||
return writerOpts
|
||||
}
|
||||
|
||||
215
vendor/github.com/cockroachdb/pebble/pacer.go
generated
vendored
215
vendor/github.com/cockroachdb/pebble/pacer.go
generated
vendored
@@ -26,221 +26,6 @@ type pacer interface {
|
||||
maybeThrottle(bytesIterated uint64) error
|
||||
}
|
||||
|
||||
// compactionInternalPacer contains fields and methods common to compactionPacer
|
||||
// and flushPacer.
|
||||
type compactionInternalPacer struct {
|
||||
limiter limiter
|
||||
|
||||
iterCount uint64
|
||||
prevBytesIterated uint64
|
||||
refreshBytesThreshold uint64
|
||||
slowdownThreshold uint64
|
||||
}
|
||||
|
||||
// limit applies rate limiting if the current byte level is below the configured
|
||||
// threshold.
|
||||
func (p *compactionInternalPacer) limit(amount, currentLevel uint64) error {
|
||||
if currentLevel <= p.slowdownThreshold {
|
||||
burst := p.limiter.Burst()
|
||||
for amount > uint64(burst) {
|
||||
d := p.limiter.DelayN(time.Now(), burst)
|
||||
if d == rate.InfDuration {
|
||||
return errors.Errorf("pacing failed")
|
||||
}
|
||||
time.Sleep(d)
|
||||
amount -= uint64(burst)
|
||||
}
|
||||
d := p.limiter.DelayN(time.Now(), int(amount))
|
||||
if d == rate.InfDuration {
|
||||
return errors.Errorf("pacing failed")
|
||||
}
|
||||
time.Sleep(d)
|
||||
} else {
|
||||
burst := p.limiter.Burst()
|
||||
for amount > uint64(burst) {
|
||||
p.limiter.AllowN(time.Now(), burst)
|
||||
amount -= uint64(burst)
|
||||
}
|
||||
p.limiter.AllowN(time.Now(), int(amount))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// compactionPacerInfo contains information necessary for compaction pacing.
|
||||
type compactionPacerInfo struct {
|
||||
// slowdownThreshold is the low watermark for compaction debt. If compaction debt is
|
||||
// below this threshold, we slow down compactions. If compaction debt is above this
|
||||
// threshold, we let compactions continue as fast as possible. We want to keep
|
||||
// compaction speed as slow as possible to match the speed of flushes. This threshold
|
||||
// is set so that a single flush cannot contribute enough compaction debt to overshoot
|
||||
// the threshold.
|
||||
slowdownThreshold uint64
|
||||
totalCompactionDebt uint64
|
||||
// totalDirtyBytes is the number of dirty bytes in memtables. The compaction
|
||||
// pacer can monitor changes to this value to determine if user writes have
|
||||
// stopped.
|
||||
totalDirtyBytes uint64
|
||||
}
|
||||
|
||||
// compactionPacerEnv defines the environment in which the compaction rate limiter
|
||||
// is applied.
|
||||
type compactionPacerEnv struct {
|
||||
limiter limiter
|
||||
memTableSize uint64
|
||||
|
||||
getInfo func() compactionPacerInfo
|
||||
}
|
||||
|
||||
// compactionPacer rate limits compactions depending on compaction debt. The rate
|
||||
// limiter is applied at a rate that keeps compaction debt at a steady level. If
|
||||
// compaction debt increases at a rate that is faster than the system can handle,
|
||||
// no rate limit is applied.
|
||||
type compactionPacer struct {
|
||||
compactionInternalPacer
|
||||
env compactionPacerEnv
|
||||
totalCompactionDebt uint64
|
||||
totalDirtyBytes uint64
|
||||
}
|
||||
|
||||
func newCompactionPacer(env compactionPacerEnv) *compactionPacer {
|
||||
return &compactionPacer{
|
||||
env: env,
|
||||
compactionInternalPacer: compactionInternalPacer{
|
||||
limiter: env.limiter,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// maybeThrottle slows down compactions to match memtable flush rate. The DB
|
||||
// provides a compaction debt estimate and a slowdown threshold. We subtract the
|
||||
// compaction debt estimate by the bytes iterated in the current compaction. If
|
||||
// the new compaction debt estimate is below the threshold, the rate limiter is
|
||||
// applied. If the new compaction debt is above the threshold, the rate limiter
|
||||
// is not applied.
|
||||
func (p *compactionPacer) maybeThrottle(bytesIterated uint64) error {
|
||||
if bytesIterated == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recalculate total compaction debt and the slowdown threshold only once
|
||||
// every 1000 iterations or when the refresh threshold is hit since it
|
||||
// requires grabbing DB.mu which is expensive.
|
||||
if p.iterCount == 0 || bytesIterated > p.refreshBytesThreshold {
|
||||
pacerInfo := p.env.getInfo()
|
||||
p.slowdownThreshold = pacerInfo.slowdownThreshold
|
||||
p.totalCompactionDebt = pacerInfo.totalCompactionDebt
|
||||
p.refreshBytesThreshold = bytesIterated + (p.env.memTableSize * 5 / 100)
|
||||
p.iterCount = 1000
|
||||
if p.totalDirtyBytes == pacerInfo.totalDirtyBytes {
|
||||
// The total dirty bytes in the memtables have not changed since the
|
||||
// previous call: user writes have completely stopped. Allow the
|
||||
// compaction to proceed as fast as possible until the next
|
||||
// recalculation. We adjust the recalculation threshold so that we can be
|
||||
// nimble in the face of new user writes.
|
||||
p.totalCompactionDebt += p.slowdownThreshold
|
||||
p.iterCount = 100
|
||||
}
|
||||
p.totalDirtyBytes = pacerInfo.totalDirtyBytes
|
||||
}
|
||||
p.iterCount--
|
||||
|
||||
var curCompactionDebt uint64
|
||||
if p.totalCompactionDebt > bytesIterated {
|
||||
curCompactionDebt = p.totalCompactionDebt - bytesIterated
|
||||
}
|
||||
|
||||
compactAmount := bytesIterated - p.prevBytesIterated
|
||||
p.prevBytesIterated = bytesIterated
|
||||
|
||||
// We slow down compactions when the compaction debt falls below the slowdown
|
||||
// threshold, which is set dynamically based on the number of non-empty levels.
|
||||
// This will only occur if compactions can keep up with the pace of flushes. If
|
||||
// bytes are flushed faster than how fast compactions can occur, compactions
|
||||
// proceed at maximum (unthrottled) speed.
|
||||
return p.limit(compactAmount, curCompactionDebt)
|
||||
}
|
||||
|
||||
// flushPacerInfo contains information necessary for compaction pacing.
|
||||
type flushPacerInfo struct {
|
||||
inuseBytes uint64
|
||||
}
|
||||
|
||||
// flushPacerEnv defines the environment in which the compaction rate limiter is
|
||||
// applied.
|
||||
type flushPacerEnv struct {
|
||||
limiter limiter
|
||||
memTableSize uint64
|
||||
|
||||
getInfo func() flushPacerInfo
|
||||
}
|
||||
|
||||
// flushPacer rate limits memtable flushing to match the speed of incoming user
|
||||
// writes. If user writes come in faster than the memtable can be flushed, no
|
||||
// rate limit is applied.
|
||||
type flushPacer struct {
|
||||
compactionInternalPacer
|
||||
env flushPacerEnv
|
||||
inuseBytes uint64
|
||||
adjustedInuseBytes uint64
|
||||
}
|
||||
|
||||
func newFlushPacer(env flushPacerEnv) *flushPacer {
|
||||
return &flushPacer{
|
||||
env: env,
|
||||
compactionInternalPacer: compactionInternalPacer{
|
||||
limiter: env.limiter,
|
||||
slowdownThreshold: env.memTableSize * 105 / 100,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// maybeThrottle slows down memtable flushing to match user write rate. The DB
|
||||
// provides the total number of bytes in all the memtables. We subtract this total
|
||||
// by the number of bytes flushed in the current flush to get a "dirty byte" count.
|
||||
// If the dirty byte count is below the watermark (105% memtable size), the rate
|
||||
// limiter is applied. If the dirty byte count is above the watermark, the rate
|
||||
// limiter is not applied.
|
||||
func (p *flushPacer) maybeThrottle(bytesIterated uint64) error {
|
||||
if bytesIterated == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Recalculate inuse memtable bytes only once every 1000 iterations or when
|
||||
// the refresh threshold is hit since getting the inuse memtable byte count
|
||||
// requires grabbing DB.mu which is expensive.
|
||||
if p.iterCount == 0 || bytesIterated > p.refreshBytesThreshold {
|
||||
pacerInfo := p.env.getInfo()
|
||||
p.iterCount = 1000
|
||||
p.refreshBytesThreshold = bytesIterated + (p.env.memTableSize * 5 / 100)
|
||||
p.adjustedInuseBytes = pacerInfo.inuseBytes
|
||||
if p.inuseBytes == pacerInfo.inuseBytes {
|
||||
// The inuse bytes in the memtables have not changed since the previous
|
||||
// call: user writes have completely stopped. Allow the flush to proceed
|
||||
// as fast as possible until the next recalculation. We adjust the
|
||||
// recalculation threshold so that we can be nimble in the face of new
|
||||
// user writes.
|
||||
p.adjustedInuseBytes += p.slowdownThreshold
|
||||
p.iterCount = 100
|
||||
}
|
||||
p.inuseBytes = pacerInfo.inuseBytes
|
||||
}
|
||||
p.iterCount--
|
||||
|
||||
// dirtyBytes is the inuse number of bytes in the memtables minus the number of
|
||||
// bytes flushed. It represents unflushed bytes in all the memtables, even the
|
||||
// ones which aren't being flushed such as the mutable memtable.
|
||||
dirtyBytes := p.adjustedInuseBytes - bytesIterated
|
||||
flushAmount := bytesIterated - p.prevBytesIterated
|
||||
p.prevBytesIterated = bytesIterated
|
||||
|
||||
// We slow down memtable flushing when the dirty bytes indicator falls
|
||||
// below the low watermark, which is 105% memtable size. This will only
|
||||
// occur if memtable flushing can keep up with the pace of incoming
|
||||
// writes. If writes come in faster than how fast the memtable can flush,
|
||||
// flushing proceeds at maximum (unthrottled) speed.
|
||||
return p.limit(flushAmount, dirtyBytes)
|
||||
}
|
||||
|
||||
// deletionPacerInfo contains any info from the db necessary to make deletion
|
||||
// pacing decisions.
|
||||
type deletionPacerInfo struct {
|
||||
|
||||
757
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
757
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
@@ -5,136 +5,665 @@
|
||||
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/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
)
|
||||
|
||||
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
|
||||
// constructRangeKeyIter constructs the range-key iterator stack, populating
|
||||
// i.rangeKey.rangeKeyIter with the resulting iterator.
|
||||
func (i *Iterator) constructRangeKeyIter() {
|
||||
i.rangeKey.rangeKeyIter = i.rangeKey.iterConfig.Init(
|
||||
&i.comparer, i.seqNum, i.opts.LowerBound, i.opts.UpperBound,
|
||||
&i.hasPrefix, &i.prefixOrFullSeekKey)
|
||||
|
||||
// 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)
|
||||
if i.batch != nil {
|
||||
if i.batch.index == nil {
|
||||
i.rangeKey.iterConfig.AddLevel(newErrorKeyspanIter(ErrNotIndexed))
|
||||
} else {
|
||||
// Only include the batch's range key iterator if it has any keys.
|
||||
// NB: This can force reconstruction of the rangekey iterator stack
|
||||
// in SetOptions if subsequently range keys are added. See
|
||||
// SetOptions.
|
||||
if i.batch.countRangeKeys > 0 {
|
||||
i.batch.initRangeKeyIter(&i.opts, &i.batchRangeKeyIter, i.batchSeqNum)
|
||||
i.rangeKey.iterConfig.AddLevel(&i.batchRangeKeyIter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next are the flushables: memtables and large batches.
|
||||
for i := len(readState.memtables) - 1; i >= 0; i-- {
|
||||
mem := readState.memtables[i]
|
||||
for j := len(i.readState.memtables) - 1; j >= 0; j-- {
|
||||
mem := i.readState.memtables[j]
|
||||
// We only need to read from memtables which contain sequence numbers older
|
||||
// than seqNum.
|
||||
if logSeqNum := mem.logSeqNum; logSeqNum >= seqNum {
|
||||
if logSeqNum := mem.logSeqNum; logSeqNum >= i.seqNum {
|
||||
continue
|
||||
}
|
||||
if rki := mem.newRangeKeyIter(opts); rki != nil {
|
||||
iters = append(iters, rki)
|
||||
if rki := mem.newRangeKeyIter(&i.opts); rki != nil {
|
||||
i.rangeKey.iterConfig.AddLevel(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))
|
||||
current := i.readState.current
|
||||
// Next are the file levels: L0 sub-levels followed by lower levels.
|
||||
//
|
||||
// Add file-specific iterators for L0 files containing range keys. This is less
|
||||
// efficient than using levelIters for sublevels of L0 files containing
|
||||
// range keys, but range keys are expected to be sparse anyway, reducing the
|
||||
// cost benefit of maintaining a separate L0Sublevels instance for range key
|
||||
// files and then using it here.
|
||||
//
|
||||
// NB: We iterate L0's files in reverse order. They're sorted by
|
||||
// LargestSeqNum ascending, and we need to add them to the merging iterator
|
||||
// in LargestSeqNum descending to preserve the merging iterator's invariants
|
||||
// around Key Trailer order.
|
||||
iter := current.RangeKeyLevels[0].Iter()
|
||||
for f := iter.Last(); f != nil; f = iter.Prev() {
|
||||
spanIterOpts := &keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
||||
spanIter, err := i.newIterRangeKey(f, spanIterOpts)
|
||||
if err != nil {
|
||||
i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
||||
continue
|
||||
}
|
||||
i.rangeKey.iterConfig.AddLevel(spanIter)
|
||||
}
|
||||
|
||||
// Add level iterators for the non-empty non-L0 levels.
|
||||
for level := 1; level < len(current.RangeKeyLevels); level++ {
|
||||
if current.RangeKeyLevels[level].Empty() {
|
||||
continue
|
||||
}
|
||||
li := i.rangeKey.iterConfig.NewLevelIter()
|
||||
spanIterOpts := keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
||||
li.Init(spanIterOpts, i.cmp, i.newIterRangeKey, current.RangeKeyLevels[level].Iter(),
|
||||
manifest.Level(level), i.opts.logger, manifest.KeyTypeRange)
|
||||
i.rangeKey.iterConfig.AddLevel(li)
|
||||
}
|
||||
it.rangeKeyIter = rangekey.InitUserIteration(
|
||||
d.cmp, seqNum, &it.alloc.merging, &it.alloc.defraging, iters...,
|
||||
)
|
||||
return it.rangeKeyIter
|
||||
}
|
||||
|
||||
// Range key masking
|
||||
//
|
||||
// Pebble iterators may be configured such that range keys with suffixes mask
|
||||
// point keys with lower suffixes. The intended use is implementing a MVCC
|
||||
// delete range operation using range keys, when suffixes are MVCC timestamps.
|
||||
//
|
||||
// To enable masking, the user populates the IterOptions's RangeKeyMasking
|
||||
// field. The Suffix field configures which range keys act as masks. The
|
||||
// intended use is to hold a MVCC read timestamp. When implementing a MVCC
|
||||
// delete range operation, only range keys that are visible at the read
|
||||
// timestamp should be visible. If a range key has a suffix ≤
|
||||
// RangeKeyMasking.Suffix, it acts as a mask.
|
||||
//
|
||||
// Range key masking is facilitated by the keyspan.InterleavingIter. The
|
||||
// interleaving iterator interleaves range keys and point keys during combined
|
||||
// iteration. During user iteration, the interleaving iterator is configured
|
||||
// with a keyspan.SpanMask, implemented by the rangeKeyMasking struct below.
|
||||
// The SpanMask interface defines two methods: SpanChanged and SkipPoint.
|
||||
//
|
||||
// SpanChanged is used to keep the current mask up-to-date. Whenever the point
|
||||
// iterator has stepped into or out of the bounds of a range key, the
|
||||
// interleaving iterator invokes SpanChanged passing the current covering range
|
||||
// key. The below rangeKeyMasking implementation scans the range keys looking
|
||||
// for the range key with the largest suffix that's still ≤ the suffix supplied
|
||||
// to IterOptions.RangeKeyMasking.Suffix (the "read timestamp"). If it finds a
|
||||
// range key that meets the condition, the range key should act as a mask. The
|
||||
// span and the relevant range key's suffix are saved.
|
||||
//
|
||||
// The above ensures that `rangeKeyMasking.maskActiveSuffix` always contains the
|
||||
// current masking suffix such that any point keys with lower suffixes should be
|
||||
// skipped.
|
||||
//
|
||||
// There are two ways in which masked point keys are skipped.
|
||||
//
|
||||
// 1. Interleaving iterator SkipPoint
|
||||
//
|
||||
// Whenever the interleaving iterator encounters a point key that falls within
|
||||
// the bounds of a range key, it invokes SkipPoint. The interleaving iterator
|
||||
// guarantees that the SpanChanged method described above has already been
|
||||
// invoked with the covering range key. The below rangeKeyMasking implementation
|
||||
// of SkipPoint splits the key into prefix and suffix, compares the suffix to
|
||||
// the `maskActiveSuffix` updated by SpanChanged and returns true if
|
||||
// suffix(point) < maskActiveSuffix.
|
||||
//
|
||||
// The SkipPoint logic is sufficient to ensure that the Pebble iterator filters
|
||||
// out all masked point keys. However, it requires the iterator read each masked
|
||||
// point key. For broad range keys that mask many points, this may be expensive.
|
||||
//
|
||||
// 2. Block property filter
|
||||
//
|
||||
// For more efficient handling of braad range keys that mask many points, the
|
||||
// IterOptions.RangeKeyMasking field has an optional Filter option. This Filter
|
||||
// field takes a superset of the block-property filter interface, adding a
|
||||
// method to dynamically configure the filter's filtering criteria.
|
||||
//
|
||||
// To make use of the Filter option, the user is required to define and
|
||||
// configure a block-property collector that collects a property containing at
|
||||
// least the maximum suffix of a key within a block.
|
||||
//
|
||||
// When the SpanChanged method described above is invoked, rangeKeyMasking also
|
||||
// reconfigures the user-provided filter. It invokes a SetSuffix method,
|
||||
// providing the `maskActiveSuffix`, requesting that from now on the
|
||||
// block-property filter return Intersects()=false for any properties indicating
|
||||
// that a block contains exclusively keys with suffixes greater than the
|
||||
// provided suffix.
|
||||
//
|
||||
// Note that unlike other block-property filters, the filter used for masking
|
||||
// must not apply across the entire keyspace. It must only filter blocks that
|
||||
// lie within the bounds of the range key that set the mask suffix. To
|
||||
// accommodate this, rangeKeyMasking implements a special interface:
|
||||
// sstable.BoundLimitedBlockPropertyFilter. This interface extends the block
|
||||
// property filter interface with two new methods: KeyIsWithinLowerBound and
|
||||
// KeyIsWithinUpperBound. The rangeKeyMasking type wraps the user-provided block
|
||||
// property filter, implementing these two methods and overriding Intersects to
|
||||
// always return true if there is no active mask.
|
||||
//
|
||||
// The logic to ensure that a mask block-property filter is only applied within
|
||||
// the bounds of the masking range key is subtle. The interleaving iterator
|
||||
// guarantees that it never invokes SpanChanged until the point iterator is
|
||||
// positioned within the range key. During forward iteration, this guarantees
|
||||
// that any block that a sstable reader might attempt to load contains only keys
|
||||
// greater than or equal to the range key's lower bound. During backward
|
||||
// iteration, it provides the analagous guarantee on the range key's upper
|
||||
// bound.
|
||||
//
|
||||
// The above ensures that an sstable reader only needs to verify that a block
|
||||
// that it skips meets the opposite bound. This is where the
|
||||
// KeyIsWithinLowerBound and KeyIsWithinUpperBound methods are used. When an
|
||||
// sstable iterator is configured with a BoundLimitedBlockPropertyFilter, it
|
||||
// checks for intersection with the block-property filter before every block
|
||||
// load, like ordinary block-property filters. However, if the bound-limited
|
||||
// block property filter indicates that it does NOT intersect, the filter's
|
||||
// relevant KeyIsWithin{Lower,Upper}Bound method is queried, using a block
|
||||
// index separator as the bound. If the method indicates that the provided index
|
||||
// separator does not fall within the range key bounds, the no-intersection
|
||||
// result is ignored, and the block is read.
|
||||
|
||||
type rangeKeyMasking struct {
|
||||
cmp base.Compare
|
||||
split base.Split
|
||||
filter BlockPropertyFilterMask
|
||||
// maskActiveSuffix holds the suffix of a range key currently acting as a
|
||||
// mask, hiding point keys with suffixes greater than it. maskActiveSuffix
|
||||
// is only ever non-nil if IterOptions.RangeKeyMasking.Suffix is non-nil.
|
||||
// maskActiveSuffix is updated whenever the iterator passes over a new range
|
||||
// key. The maskActiveSuffix should only be used if maskSpan is non-nil.
|
||||
//
|
||||
// See SpanChanged.
|
||||
maskActiveSuffix []byte
|
||||
// maskSpan holds the span from which the active mask suffix was extracted.
|
||||
// The span is used for bounds comparisons, to ensure that a range-key mask
|
||||
// is not applied beyond the bounds of the range key.
|
||||
maskSpan *keyspan.Span
|
||||
parent *Iterator
|
||||
}
|
||||
|
||||
func (m *rangeKeyMasking) init(parent *Iterator, cmp base.Compare, split base.Split) {
|
||||
m.cmp = cmp
|
||||
m.split = split
|
||||
if parent.opts.RangeKeyMasking.Filter != nil {
|
||||
m.filter = parent.opts.RangeKeyMasking.Filter()
|
||||
}
|
||||
m.parent = parent
|
||||
}
|
||||
|
||||
// SpanChanged implements the keyspan.SpanMask interface, used during range key
|
||||
// iteration.
|
||||
func (m *rangeKeyMasking) SpanChanged(s *keyspan.Span) {
|
||||
if s == nil && m.maskSpan == nil {
|
||||
return
|
||||
}
|
||||
m.maskSpan = nil
|
||||
m.maskActiveSuffix = m.maskActiveSuffix[:0]
|
||||
|
||||
// Find the smallest suffix of a range key contained within the Span,
|
||||
// excluding suffixes less than m.opts.RangeKeyMasking.Suffix.
|
||||
if s != nil {
|
||||
m.parent.rangeKey.stale = true
|
||||
if m.parent.opts.RangeKeyMasking.Suffix != nil {
|
||||
for j := range s.Keys {
|
||||
if s.Keys[j].Suffix == nil {
|
||||
continue
|
||||
}
|
||||
if m.cmp(s.Keys[j].Suffix, m.parent.opts.RangeKeyMasking.Suffix) < 0 {
|
||||
continue
|
||||
}
|
||||
if len(m.maskActiveSuffix) == 0 || m.cmp(m.maskActiveSuffix, s.Keys[j].Suffix) > 0 {
|
||||
m.maskSpan = s
|
||||
m.maskActiveSuffix = append(m.maskActiveSuffix[:0], s.Keys[j].Suffix...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m.maskSpan != nil && m.parent.opts.RangeKeyMasking.Filter != nil {
|
||||
// Update the block-property filter to filter point keys with suffixes
|
||||
// greater than m.maskActiveSuffix.
|
||||
err := m.filter.SetSuffix(m.maskActiveSuffix)
|
||||
if err != nil {
|
||||
m.parent.err = err
|
||||
}
|
||||
}
|
||||
// If no span is active, we leave the inner block-property filter configured
|
||||
// with its existing suffix. That's okay, because Intersects calls are first
|
||||
// evaluated by iteratorRangeKeyState.Intersects, which considers all blocks
|
||||
// as intersecting if there's no active mask.
|
||||
}
|
||||
|
||||
// SkipPoint implements the keyspan.SpanMask interface, used during range key
|
||||
// iteration. Whenever a point key is covered by a non-empty Span, the
|
||||
// interleaving iterator invokes SkipPoint. This function is responsible for
|
||||
// performing range key masking.
|
||||
//
|
||||
// If a non-nil IterOptions.RangeKeyMasking.Suffix is set, range key masking is
|
||||
// enabled. Masking hides point keys, transparently skipping over the keys.
|
||||
// Whether or not a point key is masked is determined by comparing the point
|
||||
// key's suffix, the overlapping span's keys' suffixes, and the user-configured
|
||||
// IterOption's RangeKeyMasking.Suffix. When configured with a masking threshold
|
||||
// _t_, and there exists a span with suffix _r_ covering a point key with suffix
|
||||
// _p_, and
|
||||
//
|
||||
// _t_ ≤ _r_ < _p_
|
||||
//
|
||||
// then the point key is elided. Consider the following rendering, where using
|
||||
// integer suffixes with higher integers sort before suffixes with lower
|
||||
// integers, (for example @7 ≤ @6 < @5):
|
||||
//
|
||||
// ^
|
||||
// @9 | •―――――――――――――――○ [e,m)@9
|
||||
// s 8 | • l@8
|
||||
// u 7 |------------------------------------ @7 RangeKeyMasking.Suffix
|
||||
// f 6 | [h,q)@6 •―――――――――――――――――○ (threshold)
|
||||
// f 5 | • h@5
|
||||
// f 4 | • n@4
|
||||
// i 3 | •―――――――――――○ [f,l)@3
|
||||
// x 2 | • b@2
|
||||
// 1 |
|
||||
// 0 |___________________________________
|
||||
// a b c d e f g h i j k l m n o p q
|
||||
//
|
||||
// An iterator scanning the entire keyspace with the masking threshold set to @7
|
||||
// will observe point keys b@2 and l@8. The span keys [h,q)@6 and [f,l)@3 serve
|
||||
// as masks, because cmp(@6,@7) ≥ 0 and cmp(@3,@7) ≥ 0. The span key [e,m)@9
|
||||
// does not serve as a mask, because cmp(@9,@7) < 0.
|
||||
//
|
||||
// Although point l@8 falls within the user key bounds of [e,m)@9, [e,m)@9 is
|
||||
// non-masking due to its suffix. The point key l@8 also falls within the user
|
||||
// key bounds of [h,q)@6, but since cmp(@6,@8) ≥ 0, l@8 is unmasked.
|
||||
//
|
||||
// Invariant: The userKey is within the user key bounds of the span most
|
||||
// recently provided to `SpanChanged`.
|
||||
func (m *rangeKeyMasking) SkipPoint(userKey []byte) bool {
|
||||
if m.maskSpan == nil {
|
||||
// No range key is currently acting as a mask, so don't skip.
|
||||
return false
|
||||
}
|
||||
// Range key masking is enabled and the current span includes a range key
|
||||
// that is being used as a mask. (NB: SpanChanged already verified that the
|
||||
// range key's suffix is ≥ RangeKeyMasking.Suffix).
|
||||
//
|
||||
// This point key falls within the bounds of the range key (guaranteed by
|
||||
// the InterleavingIter). Skip the point key if the range key's suffix is
|
||||
// greater than the point key's suffix.
|
||||
pointSuffix := userKey[m.split(userKey):]
|
||||
return len(pointSuffix) > 0 && m.cmp(m.maskActiveSuffix, pointSuffix) < 0
|
||||
}
|
||||
|
||||
// The iteratorRangeKeyState type implements the sstable package's
|
||||
// BoundLimitedBlockPropertyFilter interface in order to use block property
|
||||
// filters for range key masking. The iteratorRangeKeyState implementation wraps
|
||||
// the block-property filter provided in Options.RangeKeyMasking.Filter.
|
||||
//
|
||||
// Using a block-property filter for range-key masking requires limiting the
|
||||
// filter's effect to the bounds of the range key currently acting as a mask.
|
||||
// Consider the range key [a,m)@10, and an iterator positioned just before the
|
||||
// below block, bounded by index separators `c` and `z`:
|
||||
//
|
||||
// c z
|
||||
// x | c@9 c@5 c@1 d@7 e@4 y@4 | ...
|
||||
// iter pos
|
||||
//
|
||||
// The next block cannot be skipped, despite the range key suffix @10 is greater
|
||||
// than all the block's keys' suffixes, because it contains a key (y@4) outside
|
||||
// the bounds of the range key.
|
||||
//
|
||||
// This extended BoundLimitedBlockPropertyFilter interface adds two new methods,
|
||||
// KeyIsWithinLowerBound and KeyIsWithinUpperBound, for testing whether a
|
||||
// particular block is within bounds.
|
||||
//
|
||||
// The iteratorRangeKeyState implements these new methods by first checking if
|
||||
// the iterator is currently positioned within a range key. If not, the provided
|
||||
// key is considered out-of-bounds. If the iterator is positioned within a range
|
||||
// key, it compares the corresponding range key bound.
|
||||
var _ sstable.BoundLimitedBlockPropertyFilter = (*rangeKeyMasking)(nil)
|
||||
|
||||
// Name implements the limitedBlockPropertyFilter interface defined in the
|
||||
// sstable package by passing through to the user-defined block property filter.
|
||||
func (m *rangeKeyMasking) Name() string {
|
||||
return m.filter.Name()
|
||||
}
|
||||
|
||||
// Intersects implements the limitedBlockPropertyFilter interface defined in the
|
||||
// sstable package by passing the intersection decision to the user-provided
|
||||
// block property filter only if a range key is covering the current iterator
|
||||
// position.
|
||||
func (m *rangeKeyMasking) Intersects(prop []byte) (bool, error) {
|
||||
if m.maskSpan == nil {
|
||||
// No span is actively masking.
|
||||
return true, nil
|
||||
}
|
||||
return m.filter.Intersects(prop)
|
||||
}
|
||||
|
||||
// KeyIsWithinLowerBound implements the limitedBlockPropertyFilter interface
|
||||
// defined in the sstable package. It's used to restrict the masking block
|
||||
// property filter to only applying within the bounds of the active range key.
|
||||
func (m *rangeKeyMasking) KeyIsWithinLowerBound(ik *InternalKey) bool {
|
||||
// Invariant: m.maskSpan != nil
|
||||
//
|
||||
// The provided `ik` is an inclusive lower bound of the block we're
|
||||
// considering skipping.
|
||||
return m.cmp(m.maskSpan.Start, ik.UserKey) <= 0
|
||||
}
|
||||
|
||||
// KeyIsWithinUpperBound implements the limitedBlockPropertyFilter interface
|
||||
// defined in the sstable package. It's used to restrict the masking block
|
||||
// property filter to only applying within the bounds of the active range key.
|
||||
func (m *rangeKeyMasking) KeyIsWithinUpperBound(ik *InternalKey) bool {
|
||||
// Invariant: m.maskSpan != nil
|
||||
//
|
||||
// The provided `ik` is an *inclusive* upper bound of the block we're
|
||||
// considering skipping, so the range key's end must be strictly greater
|
||||
// than the block bound for the block to be within bounds.
|
||||
return m.cmp(m.maskSpan.End, ik.UserKey) > 0
|
||||
}
|
||||
|
||||
// lazyCombinedIter implements the internalIterator interface, wrapping a
|
||||
// pointIter. It requires the pointIter's the levelIters be configured with
|
||||
// pointers to its combinedIterState. When the levelIter observes a file
|
||||
// containing a range key, the lazyCombinedIter constructs the combined
|
||||
// range+point key iterator stack and switches to it.
|
||||
type lazyCombinedIter struct {
|
||||
// parent holds a pointer to the root *pebble.Iterator containing this
|
||||
// iterator. It's used to mutate the internalIterator in use when switching
|
||||
// to combined iteration.
|
||||
parent *Iterator
|
||||
pointIter internalIterator
|
||||
combinedIterState combinedIterState
|
||||
}
|
||||
|
||||
// combinedIterState encapsulates the current state of combined iteration.
|
||||
// Various low-level iterators (mergingIter, leveliter) hold pointers to the
|
||||
// *pebble.Iterator's combinedIterState. This allows them to check whether or
|
||||
// not they must monitor for files containing range keys (!initialized), or not.
|
||||
//
|
||||
// When !initialized, low-level iterators watch for files containing range keys.
|
||||
// When one is discovered, they set triggered=true and key to the smallest
|
||||
// (forward direction) or largest (reverse direction) range key that's been
|
||||
// observed.
|
||||
type combinedIterState struct {
|
||||
// key holds the smallest (forward direction) or largest (backward
|
||||
// direction) user key from a range key bound discovered during the iterator
|
||||
// operation that triggered the switch to combined iteration.
|
||||
//
|
||||
// Slices stored here must be stable. This is possible because callers pass
|
||||
// a Smallest/Largest bound from a fileMetadata, which are immutable. A key
|
||||
// slice's bytes must not be overwritten.
|
||||
key []byte
|
||||
triggered bool
|
||||
initialized bool
|
||||
}
|
||||
|
||||
// Assert that *lazyCombinedIter implements internalIterator.
|
||||
var _ internalIterator = (*lazyCombinedIter)(nil)
|
||||
|
||||
// initCombinedIteration is invoked after a pointIter positioning operation
|
||||
// resulted in i.combinedIterState.triggered=true.
|
||||
//
|
||||
// The `dir` parameter is `+1` or `-1` indicating forward iteration or backward
|
||||
// iteration respectively.
|
||||
//
|
||||
// The `pointKey` and `pointValue` parameters provide the new point key-value
|
||||
// pair that the iterator was just positioned to. The combined iterator should
|
||||
// be seeded with this point key-value pair and return the smaller (forward
|
||||
// iteration) or largest (backward iteration) of the two.
|
||||
//
|
||||
// The `seekKey` parameter is non-nil only if the iterator operation that
|
||||
// triggered the switch to combined iteration was a SeekGE, SeekPrefixGE or
|
||||
// SeekLT. It provides the seek key supplied and is used to seek the range-key
|
||||
// iterator using the same key. This is necessary for SeekGE/SeekPrefixGE
|
||||
// operations that land in the middle of a range key and must truncate to the
|
||||
// user-provided seek key.
|
||||
func (i *lazyCombinedIter) initCombinedIteration(
|
||||
dir int8, pointKey *InternalKey, pointValue base.LazyValue, seekKey []byte,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
// Invariant: i.parent.rangeKey is nil.
|
||||
// Invariant: !i.combinedIterState.initialized.
|
||||
if invariants.Enabled {
|
||||
if i.combinedIterState.initialized {
|
||||
panic("pebble: combined iterator already initialized")
|
||||
}
|
||||
if i.parent.rangeKey != nil {
|
||||
panic("pebble: iterator already has a range-key iterator stack")
|
||||
}
|
||||
}
|
||||
|
||||
// We need to determine the key to seek the range key iterator to. If
|
||||
// seekKey is not nil, the user-initiated operation that triggered the
|
||||
// switch to combined iteration was itself a seek, and we can use that key.
|
||||
// Otherwise, a First/Last or relative positioning operation triggered the
|
||||
// switch to combined iteration.
|
||||
//
|
||||
// The levelIter that observed a file containing range keys populated
|
||||
// combinedIterState.key with the smallest (forward) or largest (backward)
|
||||
// range key it observed. If multiple levelIters observed files with range
|
||||
// keys during the same operation on the mergingIter, combinedIterState.key
|
||||
// is the smallest [during forward iteration; largest in reverse iteration]
|
||||
// such key.
|
||||
if seekKey == nil {
|
||||
// Use the levelIter-populated key.
|
||||
seekKey = i.combinedIterState.key
|
||||
|
||||
// We may need to adjust the levelIter-populated seek key to the
|
||||
// surfaced point key. If the key observed is beyond [in the iteration
|
||||
// direction] the current point key, there may still exist a range key
|
||||
// at an earlier key. Consider the following example:
|
||||
//
|
||||
// L5: 000003:[bar.DEL.5, foo.RANGEKEYSET.9]
|
||||
// L6: 000001:[bar.SET.2] 000002:[bax.RANGEKEYSET.8]
|
||||
//
|
||||
// A call to First() seeks the levels to files L5.000003 and L6.000001.
|
||||
// The L5 levelIter observes that L5.000003 contains the range key with
|
||||
// start key `foo`, and triggers a switch to combined iteration, setting
|
||||
// `combinedIterState.key` = `foo`.
|
||||
//
|
||||
// The L6 levelIter did not observe the true first range key
|
||||
// (bax.RANGEKEYSET.8), because it appears in a later sstable. When the
|
||||
// combined iterator is initialized, the range key iterator must be
|
||||
// seeked to a key that will find `bax`. To accomplish this, we seek the
|
||||
// key instead to `bar`. It is guaranteed that no range key exists
|
||||
// earlier than `bar`, otherwise a levelIter would've observed it and
|
||||
// set `combinedIterState.key` to its start key.
|
||||
if pointKey != nil {
|
||||
if dir == +1 && i.parent.cmp(i.combinedIterState.key, pointKey.UserKey) > 0 {
|
||||
seekKey = pointKey.UserKey
|
||||
} else if dir == -1 && i.parent.cmp(seekKey, pointKey.UserKey) < 0 {
|
||||
seekKey = pointKey.UserKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i.parent.hasPrefix {
|
||||
si := i.parent.comparer.Split(seekKey)
|
||||
if i.parent.cmp(seekKey[:si], i.parent.prefixOrFullSeekKey) > 0 {
|
||||
// The earliest possible range key has a start key with a prefix
|
||||
// greater than the current iteration prefix. There's no need to
|
||||
// switch to combined iteration, because there are not any range
|
||||
// keys within the bounds of the prefix. Additionally, using a seek
|
||||
// key that is outside the scope of the prefix can violate
|
||||
// invariants within the range key iterator stack. Optimizations
|
||||
// that exit early due to exhausting the prefix may result in
|
||||
// `seekKey` being larger than the next range key's start key.
|
||||
//
|
||||
// See the testdata/rangekeys test case associated with #1893.
|
||||
i.combinedIterState = combinedIterState{initialized: false}
|
||||
return pointKey, pointValue
|
||||
}
|
||||
}
|
||||
|
||||
// An operation on the point iterator observed a file containing range keys,
|
||||
// so we must switch to combined interleaving iteration. First, construct
|
||||
// the range key iterator stack. It must not exist, otherwise we'd already
|
||||
// be performing combined iteration.
|
||||
i.parent.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
i.parent.rangeKey.init(i.parent.comparer.Compare, i.parent.comparer.Split, &i.parent.opts)
|
||||
i.parent.constructRangeKeyIter()
|
||||
|
||||
// Initialize the Iterator's interleaving iterator.
|
||||
i.parent.rangeKey.iiter.Init(
|
||||
&i.parent.comparer, i.parent.pointIter, i.parent.rangeKey.rangeKeyIter,
|
||||
&i.parent.rangeKeyMasking, i.parent.opts.LowerBound, i.parent.opts.UpperBound)
|
||||
|
||||
// Set the parent's primary iterator to point to the combined, interleaving
|
||||
// iterator that's now initialized with our current state.
|
||||
i.parent.iter = &i.parent.rangeKey.iiter
|
||||
i.combinedIterState.initialized = true
|
||||
i.combinedIterState.key = nil
|
||||
|
||||
// All future iterator operations will go directly through the combined
|
||||
// iterator.
|
||||
//
|
||||
// Initialize the interleaving iterator. We pass the point key-value pair so
|
||||
// that the interleaving iterator knows where the point iterator is
|
||||
// positioned. Additionally, we pass the seek key to which the range-key
|
||||
// iterator should be seeked in order to initialize its position.
|
||||
//
|
||||
// In the forward direction (invert for backwards), the seek key is a key
|
||||
// guaranteed to find the smallest range key that's greater than the last
|
||||
// key the iterator returned. The range key may be less than pointKey, in
|
||||
// which case the range key will be interleaved next instead of the point
|
||||
// key.
|
||||
if dir == +1 {
|
||||
var prefix []byte
|
||||
if i.parent.hasPrefix {
|
||||
prefix = i.parent.prefixOrFullSeekKey
|
||||
}
|
||||
return i.parent.rangeKey.iiter.InitSeekGE(prefix, seekKey, pointKey, pointValue)
|
||||
}
|
||||
return i.parent.rangeKey.iiter.InitSeekLT(seekKey, pointKey, pointValue)
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekGE(key, flags)
|
||||
}
|
||||
k, v := i.pointIter.SeekGE(key, flags)
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(+1, k, v, key)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekPrefixGE(prefix, key, flags)
|
||||
}
|
||||
k, v := i.pointIter.SeekPrefixGE(prefix, key, flags)
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(+1, k, v, key)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekLT(key, flags)
|
||||
}
|
||||
k, v := i.pointIter.SeekLT(key, flags)
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(-1, k, v, key)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) First() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.First()
|
||||
}
|
||||
k, v := i.pointIter.First()
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(+1, k, v, nil)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Last() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Last()
|
||||
}
|
||||
k, v := i.pointIter.Last()
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(-1, k, v, nil)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Next()
|
||||
}
|
||||
k, v := i.pointIter.Next()
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(+1, k, v, nil)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Prev()
|
||||
}
|
||||
k, v := i.pointIter.Prev()
|
||||
if i.combinedIterState.triggered {
|
||||
return i.initCombinedIteration(-1, k, v, nil)
|
||||
}
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Error() error {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Error()
|
||||
}
|
||||
return i.pointIter.Error()
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Close() error {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Close()
|
||||
}
|
||||
return i.pointIter.Close()
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) SetBounds(lower, upper []byte) {
|
||||
if i.combinedIterState.initialized {
|
||||
i.parent.rangeKey.iiter.SetBounds(lower, upper)
|
||||
return
|
||||
}
|
||||
i.pointIter.SetBounds(lower, upper)
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) String() string {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.String()
|
||||
}
|
||||
return i.pointIter.String()
|
||||
}
|
||||
|
||||
7
vendor/github.com/cockroachdb/pebble/read_state.go
generated
vendored
7
vendor/github.com/cockroachdb/pebble/read_state.go
generated
vendored
@@ -80,7 +80,7 @@ func (d *DB) loadReadState() *readState {
|
||||
// list of memtables. Requires DB.mu is held. If checker is not nil, it is
|
||||
// called after installing the new readState. If atomicFunc is not nil, it is
|
||||
// executed atomically with the transition to the new read state.
|
||||
func (d *DB) updateReadStateLocked(checker func(*DB) error, atomicFunc func()) {
|
||||
func (d *DB) updateReadStateLocked(checker func(*DB) error) {
|
||||
s := &readState{
|
||||
db: d,
|
||||
refcnt: 1,
|
||||
@@ -95,11 +95,6 @@ func (d *DB) updateReadStateLocked(checker func(*DB) error, atomicFunc func()) {
|
||||
d.readState.Lock()
|
||||
old := d.readState.val
|
||||
d.readState.val = s
|
||||
// TODO(jackson): Remove atomicFunc. It's temporary to allow us to copy
|
||||
// range keys from flushed memtables into the global arena atomically.
|
||||
if atomicFunc != nil {
|
||||
atomicFunc()
|
||||
}
|
||||
d.readState.Unlock()
|
||||
if checker != nil {
|
||||
if err := checker(d); err != nil {
|
||||
|
||||
51
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
51
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/crc"
|
||||
"github.com/codahale/hdrhistogram"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var walSyncLabels = pprof.Labels("pebble", "wal-sync")
|
||||
@@ -281,6 +281,7 @@ type LogWriter struct {
|
||||
err error
|
||||
// minSyncInterval is the minimum duration between syncs.
|
||||
minSyncInterval durationFunc
|
||||
fsyncLatency prometheus.Histogram
|
||||
pending []*block
|
||||
syncQ syncQueue
|
||||
metrics *LogWriterMetrics
|
||||
@@ -292,12 +293,18 @@ type LogWriter struct {
|
||||
afterFunc func(d time.Duration, f func()) syncTimer
|
||||
}
|
||||
|
||||
// LogWriterConfig is a struct used for configuring new LogWriters
|
||||
type LogWriterConfig struct {
|
||||
WALMinSyncInterval durationFunc
|
||||
WALFsyncLatency prometheus.Histogram
|
||||
}
|
||||
|
||||
// CapAllocatedBlocks is the maximum number of blocks allocated by the
|
||||
// LogWriter.
|
||||
const CapAllocatedBlocks = 16
|
||||
|
||||
// NewLogWriter returns a new LogWriter.
|
||||
func NewLogWriter(w io.Writer, logNum base.FileNum) *LogWriter {
|
||||
func NewLogWriter(w io.Writer, logNum base.FileNum, logWriterConfig LogWriterConfig) *LogWriter {
|
||||
c, _ := w.(io.Closer)
|
||||
s, _ := w.(syncer)
|
||||
r := &LogWriter{
|
||||
@@ -321,25 +328,17 @@ func NewLogWriter(w io.Writer, logNum base.FileNum) *LogWriter {
|
||||
r.flusher.closed = make(chan struct{})
|
||||
r.flusher.pending = make([]*block, 0, cap(r.free.blocks))
|
||||
r.flusher.metrics = &LogWriterMetrics{}
|
||||
// Histogram with max value of 30s. We are not trying to detect anomalies
|
||||
// with this, and normally latencies range from 0.5ms to 25ms.
|
||||
r.flusher.metrics.SyncLatencyMicros = hdrhistogram.New(
|
||||
0, (time.Second * 30).Microseconds(), 2)
|
||||
|
||||
f := &r.flusher
|
||||
f.minSyncInterval = logWriterConfig.WALMinSyncInterval
|
||||
f.fsyncLatency = logWriterConfig.WALFsyncLatency
|
||||
|
||||
go func() {
|
||||
pprof.Do(context.Background(), walSyncLabels, r.flushLoop)
|
||||
}()
|
||||
return r
|
||||
}
|
||||
|
||||
// SetMinSyncInterval sets the closure to invoke for retrieving the minimum
|
||||
// sync duration between syncs.
|
||||
func (w *LogWriter) SetMinSyncInterval(minSyncInterval durationFunc) {
|
||||
f := &w.flusher
|
||||
f.Lock()
|
||||
f.minSyncInterval = minSyncInterval
|
||||
f.Unlock()
|
||||
}
|
||||
|
||||
func (w *LogWriter) flushLoop(context.Context) {
|
||||
f := &w.flusher
|
||||
f.Lock()
|
||||
@@ -452,8 +451,8 @@ func (w *LogWriter) flushLoop(context.Context) {
|
||||
f.Unlock()
|
||||
synced, syncLatency, bytesWritten, err := w.flushPending(data, pending, head, tail)
|
||||
f.Lock()
|
||||
if synced {
|
||||
f.metrics.SyncLatencyMicros.RecordValue(syncLatency.Microseconds())
|
||||
if synced && f.fsyncLatency != nil {
|
||||
f.fsyncLatency.Observe(float64(syncLatency))
|
||||
}
|
||||
f.err = err
|
||||
if f.err != nil {
|
||||
@@ -607,7 +606,9 @@ func (w *LogWriter) Close() error {
|
||||
syncLatency, err = w.syncWithLatency()
|
||||
}
|
||||
f.Lock()
|
||||
f.metrics.SyncLatencyMicros.RecordValue(syncLatency.Microseconds())
|
||||
if f.fsyncLatency != nil {
|
||||
f.fsyncLatency.Observe(float64(syncLatency))
|
||||
}
|
||||
f.Unlock()
|
||||
|
||||
if w.c != nil {
|
||||
@@ -730,10 +731,9 @@ func (w *LogWriter) Metrics() *LogWriterMetrics {
|
||||
|
||||
// LogWriterMetrics contains misc metrics for the log writer.
|
||||
type LogWriterMetrics struct {
|
||||
WriteThroughput base.ThroughputMetric
|
||||
PendingBufferLen base.GaugeSampleMetric
|
||||
SyncQueueLen base.GaugeSampleMetric
|
||||
SyncLatencyMicros *hdrhistogram.Histogram
|
||||
WriteThroughput base.ThroughputMetric
|
||||
PendingBufferLen base.GaugeSampleMetric
|
||||
SyncQueueLen base.GaugeSampleMetric
|
||||
}
|
||||
|
||||
// Merge merges metrics from x. Requires that x is non-nil.
|
||||
@@ -741,12 +741,5 @@ func (m *LogWriterMetrics) Merge(x *LogWriterMetrics) error {
|
||||
m.WriteThroughput.Merge(x.WriteThroughput)
|
||||
m.PendingBufferLen.Merge(x.PendingBufferLen)
|
||||
m.SyncQueueLen.Merge(x.SyncQueueLen)
|
||||
dropped := m.SyncLatencyMicros.Merge(x.SyncLatencyMicros)
|
||||
if dropped > 0 {
|
||||
// This should never happen since we use a consistent min, max when
|
||||
// creating these histograms, and out-of-range is the only reason for the
|
||||
// merge to drop samples.
|
||||
return errors.Errorf("sync latency histogram merge dropped %d samples", dropped)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/record/record.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/record/record.go
generated
vendored
@@ -32,7 +32,7 @@
|
||||
// r.Recover()
|
||||
// continue
|
||||
// }
|
||||
// s, err := ioutil.ReadAll(rec)
|
||||
// s, err := io.ReadAll(rec)
|
||||
// if err != nil {
|
||||
// log.Printf("recovering from %v", err)
|
||||
// r.Recover()
|
||||
|
||||
142
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
142
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
@@ -534,7 +534,7 @@ func (i *blockIter) cacheEntry() {
|
||||
|
||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []byte) {
|
||||
func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
|
||||
ikey := base.MakeSearchKey(key)
|
||||
@@ -629,25 +629,25 @@ func (i *blockIter) SeekGE(key []byte, trySeekUsingNext bool) (*InternalKey, []b
|
||||
// Iterate from that restart point to somewhere >= the key sought.
|
||||
for ; i.valid(); i.Next() {
|
||||
if base.InternalCompare(i.cmp, i.ikey, ikey) >= 0 {
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
|
||||
// pebble package.
|
||||
func (i *blockIter) SeekPrefixGE(
|
||||
prefix, key []byte, trySeekUsingNext bool,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
// This should never be called as prefix iteration is handled by sstable.Iterator.
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
i.clearCache()
|
||||
|
||||
ikey := base.MakeSearchKey(key)
|
||||
@@ -742,7 +742,7 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
// sought.
|
||||
i.offset = -1
|
||||
i.nextOffset = 0
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Iterate from that restart point to somewhere >= the key sought, then back
|
||||
@@ -761,7 +761,7 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
// loop will execute at least once with this if-block not being true, so
|
||||
// the key we are backing up to is the last one this loop cached.
|
||||
i.Prev()
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
if i.nextOffset >= targetOffset {
|
||||
@@ -777,30 +777,30 @@ func (i *blockIter) SeekLT(key []byte) (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) First() (*InternalKey, []byte) {
|
||||
func (i *blockIter) First() (*InternalKey, base.LazyValue) {
|
||||
i.offset = 0
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.clearCache()
|
||||
i.readEntry()
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Last implements internalIterator.Last, as documented in the pebble package.
|
||||
func (i *blockIter) Last() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Last() (*InternalKey, base.LazyValue) {
|
||||
// Seek forward from the last restart point.
|
||||
i.offset = int32(binary.LittleEndian.Uint32(i.data[i.restarts+4*(i.numRestarts-1):]))
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
i.readEntry()
|
||||
@@ -813,12 +813,12 @@ func (i *blockIter) Last() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Next implements internalIterator.Next, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if len(i.cachedBuf) > 0 {
|
||||
// We're switching from reverse iteration to forward iteration. We need to
|
||||
// populate i.fullKey with the current key we're positioned at so that
|
||||
@@ -836,7 +836,7 @@ func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
|
||||
i.offset = i.nextOffset
|
||||
if !i.valid() {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
i.readEntry()
|
||||
// Manually inlined version of i.decodeInternalKey(i.key).
|
||||
@@ -850,12 +850,12 @@ func (i *blockIter) Next() (*InternalKey, []byte) {
|
||||
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
||||
i.ikey.UserKey = nil
|
||||
}
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Prev implements internalIterator.Prev, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if n := len(i.cached) - 1; n >= 0 {
|
||||
i.nextOffset = i.offset
|
||||
e := &i.cached[n]
|
||||
@@ -874,14 +874,14 @@ func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
i.ikey.UserKey = nil
|
||||
}
|
||||
i.cached = i.cached[:n]
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
i.clearCache()
|
||||
if i.offset <= 0 {
|
||||
i.offset = -1
|
||||
i.nextOffset = 0
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
targetOffset := i.offset
|
||||
@@ -921,7 +921,7 @@ func (i *blockIter) Prev() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
i.decodeInternalKey(i.key)
|
||||
return &i.ikey, i.val
|
||||
return &i.ikey, base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Key implements internalIterator.Key, as documented in the pebble package.
|
||||
@@ -929,10 +929,8 @@ func (i *blockIter) Key() *InternalKey {
|
||||
return &i.ikey
|
||||
}
|
||||
|
||||
// Value implements internalIterator.Value, as documented in the pebble
|
||||
// package.
|
||||
func (i *blockIter) Value() []byte {
|
||||
return i.val
|
||||
func (i *blockIter) value() base.LazyValue {
|
||||
return base.MakeInPlaceValue(i.val)
|
||||
}
|
||||
|
||||
// Error implements internalIterator.Error, as documented in the pebble
|
||||
@@ -987,6 +985,10 @@ type fragmentBlockIter struct {
|
||||
closeHook func(i keyspan.FragmentIterator) error
|
||||
}
|
||||
|
||||
func (i *fragmentBlockIter) resetForReuse() fragmentBlockIter {
|
||||
return fragmentBlockIter{blockIter: i.blockIter.resetForReuse()}
|
||||
}
|
||||
|
||||
func (i *fragmentBlockIter) decodeSpanKeys(k *InternalKey, internalValue []byte) {
|
||||
// TODO(jackson): The use of i.span.Keys to accumulate keys across multiple
|
||||
// calls to Decode is too confusing and subtle. Refactor to make it
|
||||
@@ -1017,10 +1019,10 @@ func (i *fragmentBlockIter) decodeSpanKeys(k *InternalKey, internalValue []byte)
|
||||
//
|
||||
// gatherForward iterates forward, re-combining the fragmented internal keys to
|
||||
// reconstruct a keyspan.Span that holds all the keys defined over the span.
|
||||
func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte) keyspan.Span {
|
||||
func (i *fragmentBlockIter) gatherForward(k *InternalKey, lazyValue base.LazyValue) *keyspan.Span {
|
||||
i.span = keyspan.Span{}
|
||||
if k == nil || !i.blockIter.valid() {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
i.err = nil
|
||||
// Use the i.keyBuf array to back the Keys slice to prevent an allocation
|
||||
@@ -1028,20 +1030,22 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
i.span.Keys = i.keyBuf[:0]
|
||||
|
||||
// Decode the span's end key and individual keys from the value.
|
||||
internalValue := lazyValue.InPlaceValue()
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
prevEnd := i.span.End
|
||||
|
||||
// There might exist additional internal keys with identical bounds encoded
|
||||
// within the block. Iterate forward, accumulating all the keys with
|
||||
// identical bounds to s.
|
||||
k, internalValue = i.blockIter.Next()
|
||||
k, lazyValue = i.blockIter.Next()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
for k != nil && i.blockIter.cmp(k.UserKey, i.span.Start) == 0 {
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
|
||||
// Since k indicates an equal start key, the encoded end key must
|
||||
@@ -1051,12 +1055,13 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
if i.blockIter.cmp(prevEnd, i.span.End) != 0 {
|
||||
i.err = base.CorruptionErrorf("pebble: corrupt keyspan fragmentation")
|
||||
i.span = keyspan.Span{}
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
k, internalValue = i.blockIter.Next()
|
||||
k, lazyValue = i.blockIter.Next()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
}
|
||||
// i.blockIter is positioned over the first internal key for the next span.
|
||||
return i.span
|
||||
return &i.span
|
||||
}
|
||||
|
||||
// gatherBackward gathers internal keys with identical bounds. Keys defined over
|
||||
@@ -1067,10 +1072,10 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
//
|
||||
// gatherBackward iterates backwards, re-combining the fragmented internal keys
|
||||
// to reconstruct a keyspan.Span that holds all the keys defined over the span.
|
||||
func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte) keyspan.Span {
|
||||
func (i *fragmentBlockIter) gatherBackward(k *InternalKey, lazyValue base.LazyValue) *keyspan.Span {
|
||||
i.span = keyspan.Span{}
|
||||
if k == nil || !i.blockIter.valid() {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
i.err = nil
|
||||
// Use the i.keyBuf array to back the Keys slice to prevent an allocation
|
||||
@@ -1078,20 +1083,22 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
i.span.Keys = i.keyBuf[:0]
|
||||
|
||||
// Decode the span's end key and individual keys from the value.
|
||||
internalValue := lazyValue.InPlaceValue()
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
prevEnd := i.span.End
|
||||
|
||||
// There might exist additional internal keys with identical bounds encoded
|
||||
// within the block. Iterate backward, accumulating all the keys with
|
||||
// identical bounds to s.
|
||||
k, internalValue = i.blockIter.Prev()
|
||||
k, lazyValue = i.blockIter.Prev()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
for k != nil && i.blockIter.cmp(k.UserKey, i.span.Start) == 0 {
|
||||
i.decodeSpanKeys(k, internalValue)
|
||||
if i.err != nil {
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
|
||||
// Since k indicates an equal start key, the encoded end key must
|
||||
@@ -1101,27 +1108,26 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
if i.blockIter.cmp(prevEnd, i.span.End) != 0 {
|
||||
i.err = base.CorruptionErrorf("pebble: corrupt keyspan fragmentation")
|
||||
i.span = keyspan.Span{}
|
||||
return i.span
|
||||
return nil
|
||||
}
|
||||
k, internalValue = i.blockIter.Prev()
|
||||
k, lazyValue = i.blockIter.Prev()
|
||||
internalValue = lazyValue.InPlaceValue()
|
||||
}
|
||||
// i.blockIter is positioned over the last internal key for the previous
|
||||
// span.
|
||||
|
||||
// Backwards iteration encounters internal keys in the wrong order.
|
||||
keyspan.SortKeys(i.span.Keys)
|
||||
keyspan.SortKeysByTrailer(&i.span.Keys)
|
||||
|
||||
return i.span
|
||||
return &i.span
|
||||
}
|
||||
|
||||
// Error implements (base.InternalIterator).Error, as documented in the
|
||||
// internal/base package.
|
||||
// Error implements (keyspan.FragmentIterator).Error.
|
||||
func (i *fragmentBlockIter) Error() error {
|
||||
return i.err
|
||||
}
|
||||
|
||||
// Close implements (base.InternalIterator).Close, as documented in the
|
||||
// internal/base package.
|
||||
// Close implements (keyspan.FragmentIterator).Close.
|
||||
func (i *fragmentBlockIter) Close() error {
|
||||
var err error
|
||||
if i.closeHook != nil {
|
||||
@@ -1131,23 +1137,20 @@ func (i *fragmentBlockIter) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// First implements (base.InternalIterator).First, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) First() keyspan.Span {
|
||||
// First implements (keyspan.FragmentIterator).First
|
||||
func (i *fragmentBlockIter) First() *keyspan.Span {
|
||||
i.dir = +1
|
||||
return i.gatherForward(i.blockIter.First())
|
||||
}
|
||||
|
||||
// Last implements (base.InternalIterator).Last, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Last() keyspan.Span {
|
||||
// Last implements (keyspan.FragmentIterator).Last.
|
||||
func (i *fragmentBlockIter) Last() *keyspan.Span {
|
||||
i.dir = -1
|
||||
return i.gatherBackward(i.blockIter.Last())
|
||||
}
|
||||
|
||||
// Next implements (base.InternalIterator).Next, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Next() keyspan.Span {
|
||||
// Next implements (keyspan.FragmentIterator).Next.
|
||||
func (i *fragmentBlockIter) Next() *keyspan.Span {
|
||||
switch {
|
||||
case i.dir == -1 && !i.span.Valid():
|
||||
// Switching directions.
|
||||
@@ -1176,12 +1179,12 @@ func (i *fragmentBlockIter) Next() keyspan.Span {
|
||||
}
|
||||
i.dir = +1
|
||||
}
|
||||
return i.gatherForward(&i.blockIter.ikey, i.blockIter.val)
|
||||
// We know that this blockIter has in-place values.
|
||||
return i.gatherForward(&i.blockIter.ikey, base.MakeInPlaceValue(i.blockIter.val))
|
||||
}
|
||||
|
||||
// Prev implements (base.InternalIterator).Prev, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) Prev() keyspan.Span {
|
||||
// Prev implements (keyspan.FragmentIterator).Prev.
|
||||
func (i *fragmentBlockIter) Prev() *keyspan.Span {
|
||||
switch {
|
||||
case i.dir == +1 && !i.span.Valid():
|
||||
// Switching directions.
|
||||
@@ -1210,21 +1213,20 @@ func (i *fragmentBlockIter) Prev() keyspan.Span {
|
||||
}
|
||||
i.dir = -1
|
||||
}
|
||||
return i.gatherBackward(&i.blockIter.ikey, i.blockIter.val)
|
||||
// We know that this blockIter has in-place values.
|
||||
return i.gatherBackward(&i.blockIter.ikey, base.MakeInPlaceValue(i.blockIter.val))
|
||||
}
|
||||
|
||||
// SeekGE implements (base.InternalIterator).SeekGE, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) SeekGE(k []byte) keyspan.Span {
|
||||
// SeekGE implements (keyspan.FragmentIterator).SeekGE.
|
||||
func (i *fragmentBlockIter) SeekGE(k []byte) *keyspan.Span {
|
||||
i.dir = +1
|
||||
return i.gatherForward(i.blockIter.SeekGE(k, false))
|
||||
return i.gatherForward(i.blockIter.SeekGE(k, base.SeekGEFlags(0)))
|
||||
}
|
||||
|
||||
// SeekLT implements (base.InternalIterator).SeekLT, as documented in the
|
||||
// internal/base package.
|
||||
func (i *fragmentBlockIter) SeekLT(k []byte) keyspan.Span {
|
||||
// SeekLT implements (keyspan.FragmentIterator).SeekLT.
|
||||
func (i *fragmentBlockIter) SeekLT(k []byte) *keyspan.Span {
|
||||
i.dir = -1
|
||||
return i.gatherBackward(i.blockIter.SeekLT(k))
|
||||
return i.gatherBackward(i.blockIter.SeekLT(k, base.SeekLTFlagsNone))
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
|
||||
279
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
279
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
@@ -46,6 +46,40 @@ import (
|
||||
// identical behavior to table properties by returning the nil slice from
|
||||
// FinishDataBlock and FinishIndexBlock, and interpret them as the universal
|
||||
// set in BlockPropertyFilter, and return a non-universal set in FinishTable.
|
||||
//
|
||||
// Block property filtering is nondeterministic because the separation of keys
|
||||
// into blocks is nondeterministic. Clients use block-property filters to
|
||||
// implement efficient application of a filter F that applies to key-value pairs
|
||||
// (abbreviated as kv-filter). Consider correctness defined as surfacing exactly
|
||||
// the same key-value pairs that would be surfaced if one applied the filter F
|
||||
// above normal iteration. With this correctness definition, block property
|
||||
// filtering may introduce two kinds of errors:
|
||||
//
|
||||
// a) Block property filtering that uses a kv-filter may produce additional
|
||||
// key-value pairs that don't satisfy the filter because of the separation
|
||||
// of keys into blocks. Clients may remove these extra key-value pairs by
|
||||
// re-applying the kv filter while reading results back from Pebble.
|
||||
//
|
||||
// b) Block property filtering may surface deleted key-value pairs if the
|
||||
// kv filter is not a strict function of the key's user key. A block
|
||||
// containing k.DEL may be filtered, while a block containing the deleted
|
||||
// key k.SET may not be filtered, if the kv filter applies to one but not
|
||||
// the other.
|
||||
//
|
||||
// This error may be avoided trivially by using a kv filter that is a pure
|
||||
// function of the user key. A filter that examines values or key kinds
|
||||
// requires care to ensure F(k.SET, <value>) = F(k.DEL) = F(k.SINGLEDEL).
|
||||
//
|
||||
// The combination of range deletions and filtering by table-level properties
|
||||
// add another opportunity for deleted point keys to be surfaced. The pebble
|
||||
// Iterator stack takes care to correctly apply filtered tables' range deletions
|
||||
// to lower tables, preventing this form of nondeterministic error.
|
||||
//
|
||||
// In addition to the non-determinism discussed in (b), which limits the use
|
||||
// of properties over values, we now have support for values that are not
|
||||
// stored together with the key, and may not even be retrieved during
|
||||
// compactions. If Pebble is configured with such value separation, block
|
||||
// properties must only apply to the key, and will be provided a nil value.
|
||||
|
||||
// BlockPropertyCollector is used when writing a sstable.
|
||||
// - All calls to Add are included in the next FinishDataBlock, after which
|
||||
@@ -112,6 +146,57 @@ type SuffixReplaceableBlockCollector interface {
|
||||
// be thread-safe.
|
||||
type BlockPropertyFilter = base.BlockPropertyFilter
|
||||
|
||||
// BoundLimitedBlockPropertyFilter implements the block-property filter but
|
||||
// imposes an additional constraint on its usage, requiring that only blocks
|
||||
// containing exclusively keys between its lower and upper bounds may be
|
||||
// filtered. The bounds may be change during iteration, so the filter doesn't
|
||||
// expose the bounds, instead implementing KeyIsWithin[Lower,Upper]Bound methods
|
||||
// for performing bound comparisons.
|
||||
//
|
||||
// To be used, a BoundLimitedBlockPropertyFilter must be supplied directly
|
||||
// through NewBlockPropertiesFilterer's dedicated parameter. If supplied through
|
||||
// the ordinary slice of block property filters, this filter's bounds will be
|
||||
// ignored.
|
||||
//
|
||||
// The current [lower,upper) bounds of the filter are unknown, because they may
|
||||
// be changing. During forward iteration the lower bound is externally
|
||||
// guaranteed, meaning Intersects only returns false if the sstable iterator is
|
||||
// already known to be positioned at a key ≥ lower. The sstable iterator is then
|
||||
// only responsible for ensuring filtered blocks also meet the upper bound, and
|
||||
// should only allow a block to be filtered if all its keys are < upper. The
|
||||
// sstable iterator may invoke KeyIsWithinUpperBound(key) to perform this check,
|
||||
// where key is an inclusive upper bound on the block's keys.
|
||||
//
|
||||
// During backward iteration the upper bound is externally guaranteed, and
|
||||
// Intersects only returns false if the sstable iterator is already known to be
|
||||
// positioned at a key < upper. The sstable iterator is responsible for ensuring
|
||||
// filtered blocks also meet the lower bound, enforcing that a block is only
|
||||
// filtered if all its keys are ≥ lower. This check is made through passing the
|
||||
// block's inclusive lower bound to KeyIsWithinLowerBound.
|
||||
//
|
||||
// Implementations may become active or inactive through implementing Intersects
|
||||
// to return true whenever the filter is disabled.
|
||||
//
|
||||
// Usage of BoundLimitedBlockPropertyFilter is subtle, and Pebble consumers
|
||||
// should not implement this interface directly. This interface is an internal
|
||||
// detail in the implementation of block-property range-key masking.
|
||||
type BoundLimitedBlockPropertyFilter interface {
|
||||
BlockPropertyFilter
|
||||
|
||||
// KeyIsWithinLowerBound tests whether the provided internal key falls
|
||||
// within the current lower bound of the filter. A true return value
|
||||
// indicates that the filter may be used to filter blocks that exclusively
|
||||
// contain keys ≥ `key`, so long as the blocks' keys also satisfy the upper
|
||||
// bound.
|
||||
KeyIsWithinLowerBound(key *InternalKey) bool
|
||||
// KeyIsWithinUpperBound tests whether the provided internal key falls
|
||||
// within the current upper bound of the filter. A true return value
|
||||
// indicates that the filter may be used to filter blocks that exclusively
|
||||
// contain keys ≤ `key`, so long as the blocks' keys also satisfy the lower
|
||||
// bound.
|
||||
KeyIsWithinUpperBound(key *InternalKey) bool
|
||||
}
|
||||
|
||||
// BlockIntervalCollector is a helper implementation of BlockPropertyCollector
|
||||
// for users who want to represent a set of the form [lower,upper) where both
|
||||
// lower and upper are uint64, and lower <= upper.
|
||||
@@ -326,32 +411,44 @@ func (w *suffixReplacementBlockCollectorWrapper) UpdateKeySuffixes(
|
||||
return w.BlockIntervalCollector.points.(SuffixReplaceableBlockCollector).UpdateKeySuffixes(oldProp, from, to)
|
||||
}
|
||||
|
||||
// blockIntervalFilter is an implementation of BlockPropertyFilter when the
|
||||
// BlockIntervalFilter is an implementation of BlockPropertyFilter when the
|
||||
// corresponding collector is a BlockIntervalCollector. That is, the set is of
|
||||
// the form [lower, upper).
|
||||
type blockIntervalFilter struct {
|
||||
type BlockIntervalFilter struct {
|
||||
name string
|
||||
filterInterval interval
|
||||
}
|
||||
|
||||
var _ BlockPropertyFilter = (*BlockIntervalFilter)(nil)
|
||||
|
||||
// NewBlockIntervalFilter constructs a BlockPropertyFilter that filters blocks
|
||||
// based on an interval property collected by BlockIntervalCollector and the
|
||||
// given [lower, upper) bounds. The given name specifies the
|
||||
// BlockIntervalCollector's properties to read.
|
||||
func NewBlockIntervalFilter(name string, lower uint64, upper uint64) BlockPropertyFilter {
|
||||
return &blockIntervalFilter{
|
||||
func NewBlockIntervalFilter(name string, lower uint64, upper uint64) *BlockIntervalFilter {
|
||||
b := new(BlockIntervalFilter)
|
||||
b.Init(name, lower, upper)
|
||||
return b
|
||||
}
|
||||
|
||||
// Init initializes (or re-initializes, clearing previous state) an existing
|
||||
// BLockPropertyFilter to filter blocks based on an interval property collected
|
||||
// by BlockIntervalCollector and the given [lower, upper) bounds. The given name
|
||||
// specifies the BlockIntervalCollector's properties to read.
|
||||
func (b *BlockIntervalFilter) Init(name string, lower, upper uint64) {
|
||||
*b = BlockIntervalFilter{
|
||||
name: name,
|
||||
filterInterval: interval{lower: lower, upper: upper},
|
||||
}
|
||||
}
|
||||
|
||||
// Name implements the BlockPropertyFilter interface.
|
||||
func (b *blockIntervalFilter) Name() string {
|
||||
func (b *BlockIntervalFilter) Name() string {
|
||||
return b.name
|
||||
}
|
||||
|
||||
// Intersects implements the BlockPropertyFilter interface.
|
||||
func (b *blockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
func (b *BlockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
var i interval
|
||||
if err := i.decode(prop); err != nil {
|
||||
return false, err
|
||||
@@ -359,6 +456,14 @@ func (b *blockIntervalFilter) Intersects(prop []byte) (bool, error) {
|
||||
return i.intersects(b.filterInterval), nil
|
||||
}
|
||||
|
||||
// SetInterval adjusts the [lower, upper) bounds used by the filter. It is not
|
||||
// generally safe to alter the filter while it's in use, except as part of the
|
||||
// implementation of BlockPropertyFilterMask.SetSuffix used for range-key
|
||||
// masking.
|
||||
func (b *BlockIntervalFilter) SetInterval(lower, upper uint64) {
|
||||
b.filterInterval = interval{lower: lower, upper: upper}
|
||||
}
|
||||
|
||||
// When encoding block properties for each block, we cannot afford to encode
|
||||
// the name. Instead, the name is mapped to a shortID, in the scope of that
|
||||
// sstable, and the shortID is encoded. Since we use a uint8, there is a limit
|
||||
@@ -454,6 +559,25 @@ type BlockPropertiesFilterer struct {
|
||||
// has two filters, corresponding to shortIDs 2, 0, this would be:
|
||||
// len(shortIDToFiltersIndex)==3, 0=>1, 1=>-1, 2=>0.
|
||||
shortIDToFiltersIndex []int
|
||||
|
||||
// boundLimitedFilter, if non-nil, holds a single block-property filter with
|
||||
// additional constraints on its filtering. A boundLimitedFilter may only
|
||||
// filter blocks that are wholly contained within its bounds. During forward
|
||||
// iteration the lower bound (and during backward iteration the upper bound)
|
||||
// must be externally guaranteed, with Intersects only returning false if
|
||||
// that bound is met. The opposite bound is verified during iteration by the
|
||||
// sstable iterator.
|
||||
//
|
||||
// boundLimitedFilter is permitted to be defined on a property (`Name()`)
|
||||
// for which another filter exists in filters. In this case both filters
|
||||
// will be consulted, and either filter may exclude block(s). Only a single
|
||||
// bound-limited block-property filter may be set.
|
||||
//
|
||||
// The boundLimitedShortID field contains the shortID of the filter's
|
||||
// property within the sstable. It's set to -1 if the property was not
|
||||
// collected when the table was built.
|
||||
boundLimitedFilter BoundLimitedBlockPropertyFilter
|
||||
boundLimitedShortID int
|
||||
}
|
||||
|
||||
var blockPropertiesFiltererPool = sync.Pool{
|
||||
@@ -464,9 +588,16 @@ var blockPropertiesFiltererPool = sync.Pool{
|
||||
|
||||
// NewBlockPropertiesFilterer returns a partially initialized filterer. To complete
|
||||
// initialization, call IntersectsUserPropsAndFinishInit.
|
||||
func NewBlockPropertiesFilterer(filters []BlockPropertyFilter) *BlockPropertiesFilterer {
|
||||
func NewBlockPropertiesFilterer(
|
||||
filters []BlockPropertyFilter, limited BoundLimitedBlockPropertyFilter,
|
||||
) *BlockPropertiesFilterer {
|
||||
filterer := blockPropertiesFiltererPool.Get().(*BlockPropertiesFilterer)
|
||||
*filterer = BlockPropertiesFilterer{filters: filters}
|
||||
*filterer = BlockPropertiesFilterer{
|
||||
filters: filters,
|
||||
shortIDToFiltersIndex: filterer.shortIDToFiltersIndex[:0],
|
||||
boundLimitedFilter: limited,
|
||||
boundLimitedShortID: -1,
|
||||
}
|
||||
return filterer
|
||||
}
|
||||
|
||||
@@ -518,12 +649,75 @@ func (f *BlockPropertiesFilterer) IntersectsUserPropsAndFinishInit(
|
||||
}
|
||||
f.shortIDToFiltersIndex[shortID] = i
|
||||
}
|
||||
if f.boundLimitedFilter == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// There's a bound-limited filter. Find its shortID. It's possible that
|
||||
// there's an existing filter in f.filters on the same property. That's
|
||||
// okay. Both filters will be consulted whenever a relevant prop is decoded.
|
||||
props, ok := userProperties[f.boundLimitedFilter.Name()]
|
||||
if !ok {
|
||||
// The collector was not used when writing this file, so it's
|
||||
// intersecting. We leave f.boundLimitedShortID=-1, so the filter will
|
||||
// be unused within this file.
|
||||
return true, nil
|
||||
}
|
||||
byteProps := []byte(props)
|
||||
if len(byteProps) < 1 {
|
||||
return false, base.CorruptionErrorf(
|
||||
"block properties for %s is corrupted", f.boundLimitedFilter.Name())
|
||||
}
|
||||
f.boundLimitedShortID = int(byteProps[0])
|
||||
|
||||
// We don't check for table-level intersection for the bound-limited filter.
|
||||
// The bound-limited filter is treated as vacuously intersecting.
|
||||
//
|
||||
// NB: If a block-property filter needs to be toggled inactive/active, it
|
||||
// should be implemented within the Intersects implementation.
|
||||
//
|
||||
// TODO(jackson): We could filter at the table-level by threading the table
|
||||
// smallest and largest bounds here.
|
||||
|
||||
// The bound-limited filter isn't included in shortIDToFiltersIndex.
|
||||
//
|
||||
// When determining intersection, we decode props only up to the shortID
|
||||
// len(shortIDToFiltersIndex). If f.limitedShortID is greater than any of
|
||||
// the existing filters' shortIDs, we need to grow shortIDToFiltersIndex.
|
||||
// Growing the index with -1s ensures we're able to consult the index
|
||||
// without length checks.
|
||||
if n := len(f.shortIDToFiltersIndex); n <= f.boundLimitedShortID {
|
||||
if cap(f.shortIDToFiltersIndex) <= f.boundLimitedShortID {
|
||||
index := make([]int, f.boundLimitedShortID+1)
|
||||
copy(index, f.shortIDToFiltersIndex)
|
||||
f.shortIDToFiltersIndex = index
|
||||
} else {
|
||||
f.shortIDToFiltersIndex = f.shortIDToFiltersIndex[:f.boundLimitedShortID+1]
|
||||
}
|
||||
for j := n; j <= f.boundLimitedShortID; j++ {
|
||||
f.shortIDToFiltersIndex[j] = -1
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersects(props []byte) (bool, error) {
|
||||
type intersectsResult int8
|
||||
|
||||
const (
|
||||
blockIntersects intersectsResult = iota
|
||||
blockExcluded
|
||||
// blockMaybeExcluded is returned by BlockPropertiesFilterer.intersects when
|
||||
// no filters unconditionally exclude the block, but the bound-limited block
|
||||
// property filter will exclude it if the block's bounds fall within the
|
||||
// filter's current bounds. See the reader's
|
||||
// {single,two}LevelIterator.resolveMaybeExcluded methods.
|
||||
blockMaybeExcluded
|
||||
)
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersects(props []byte) (ret intersectsResult, err error) {
|
||||
i := 0
|
||||
decoder := blockPropertiesDecoder{props: props}
|
||||
ret = blockIntersects
|
||||
for i < len(f.shortIDToFiltersIndex) {
|
||||
var id int
|
||||
var prop []byte
|
||||
@@ -532,43 +726,68 @@ func (f *BlockPropertiesFilterer) intersects(props []byte) (bool, error) {
|
||||
var err error
|
||||
shortID, prop, err = decoder.next()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return ret, err
|
||||
}
|
||||
id = int(shortID)
|
||||
} else {
|
||||
id = math.MaxUint8 + 1
|
||||
}
|
||||
for i < len(f.shortIDToFiltersIndex) && id > i {
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
// There is a filter for this id, but the property for this id
|
||||
// is not encoded for this block.
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !intersects {
|
||||
return false, nil
|
||||
}
|
||||
// The property for this id is not encoded for this block, but there
|
||||
// may still be a filter for this id.
|
||||
if intersects, err := f.intersectsFilter(i, nil); err != nil {
|
||||
return ret, err
|
||||
} else if intersects == blockExcluded {
|
||||
return blockExcluded, nil
|
||||
} else if intersects == blockMaybeExcluded {
|
||||
ret = blockMaybeExcluded
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(f.shortIDToFiltersIndex) {
|
||||
return true, nil
|
||||
return ret, nil
|
||||
}
|
||||
// INVARIANT: id <= i. And since i is always incremented by 1, id==i.
|
||||
if id != i {
|
||||
panic(fmt.Sprintf("%d != %d", id, i))
|
||||
}
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(prop)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !intersects {
|
||||
return false, nil
|
||||
}
|
||||
if intersects, err := f.intersectsFilter(i, prop); err != nil {
|
||||
return ret, err
|
||||
} else if intersects == blockExcluded {
|
||||
return blockExcluded, nil
|
||||
} else if intersects == blockMaybeExcluded {
|
||||
ret = blockMaybeExcluded
|
||||
}
|
||||
i++
|
||||
}
|
||||
return true, nil
|
||||
// ret == blockIntersects || ret == blockMaybeExcluded
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (f *BlockPropertiesFilterer) intersectsFilter(i int, prop []byte) (intersectsResult, error) {
|
||||
if f.shortIDToFiltersIndex[i] >= 0 {
|
||||
intersects, err := f.filters[f.shortIDToFiltersIndex[i]].Intersects(prop)
|
||||
if err != nil {
|
||||
return blockIntersects, err
|
||||
}
|
||||
if !intersects {
|
||||
return blockExcluded, nil
|
||||
}
|
||||
}
|
||||
if i == f.boundLimitedShortID {
|
||||
// The bound-limited filter uses this id.
|
||||
//
|
||||
// The bound-limited filter only applies within a keyspan interval. We
|
||||
// expect the Intersects call to be cheaper than bounds checks. If
|
||||
// Intersects determines that there is no intersection, we return
|
||||
// `blockMaybeExcluded` if no other bpf unconditionally excludes the
|
||||
// block.
|
||||
intersects, err := f.boundLimitedFilter.Intersects(prop)
|
||||
if err != nil {
|
||||
return blockIntersects, err
|
||||
} else if !intersects {
|
||||
return blockMaybeExcluded, nil
|
||||
}
|
||||
}
|
||||
return blockIntersects, nil
|
||||
}
|
||||
|
||||
117
vendor/github.com/cockroachdb/pebble/sstable/block_property_test_utils.go
generated
vendored
Normal file
117
vendor/github.com/cockroachdb/pebble/sstable/block_property_test_utils.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2022 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 sstable
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/testkeys"
|
||||
)
|
||||
|
||||
// Code in this file contains utils for testing. It implements interval block
|
||||
// property collectors and filters on the suffixes of keys in the format used
|
||||
// by the testkeys package (eg, 'key@5').
|
||||
|
||||
const testKeysBlockPropertyName = `pebble.internal.testkeys.suffixes`
|
||||
|
||||
// NewTestKeysBlockPropertyCollector constructs a sstable property collector
|
||||
// over testkey suffixes.
|
||||
func NewTestKeysBlockPropertyCollector() BlockPropertyCollector {
|
||||
return NewBlockIntervalCollector(
|
||||
testKeysBlockPropertyName,
|
||||
&testKeysSuffixIntervalCollector{},
|
||||
nil)
|
||||
}
|
||||
|
||||
// NewTestKeysBlockPropertyFilter constructs a new block-property filter that excludes
|
||||
// blocks containing exclusively suffixed keys where all the suffixes fall
|
||||
// outside of the range [filterMin, filterMax).
|
||||
//
|
||||
// The filter only filters based on data derived from the key. The iteration
|
||||
// results of this block property filter are deterministic for unsuffixed keys
|
||||
// and keys with suffixes within the range [filterMin, filterMax). For keys with
|
||||
// suffixes outside the range, iteration is nondeterministic.
|
||||
func NewTestKeysBlockPropertyFilter(filterMin, filterMax uint64) *BlockIntervalFilter {
|
||||
return NewBlockIntervalFilter(testKeysBlockPropertyName, filterMin, filterMax)
|
||||
}
|
||||
|
||||
// NewTestKeysMaskingFilter constructs a TestKeysMaskingFilter that implements
|
||||
// pebble.BlockPropertyFilterMask for efficient range-key masking using the
|
||||
// testkeys block property filter. The masking filter wraps a block interval
|
||||
// filter, and modifies the configured interval when Pebble requests it.
|
||||
func NewTestKeysMaskingFilter() TestKeysMaskingFilter {
|
||||
return TestKeysMaskingFilter{BlockIntervalFilter: NewTestKeysBlockPropertyFilter(0, math.MaxUint64)}
|
||||
}
|
||||
|
||||
// TestKeysMaskingFilter implements BlockPropertyFilterMask and may be used to mask
|
||||
// point keys with the testkeys-style suffixes (eg, @4) that are masked by range
|
||||
// keys with testkeys-style suffixes.
|
||||
type TestKeysMaskingFilter struct {
|
||||
*BlockIntervalFilter
|
||||
}
|
||||
|
||||
// SetSuffix implements pebble.BlockPropertyFilterMask.
|
||||
func (f TestKeysMaskingFilter) SetSuffix(suffix []byte) error {
|
||||
ts, err := testkeys.ParseSuffix(suffix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.BlockIntervalFilter.SetInterval(uint64(ts), math.MaxUint64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Intersects implements the BlockPropertyFilter interface.
|
||||
func (f TestKeysMaskingFilter) Intersects(prop []byte) (bool, error) {
|
||||
return f.BlockIntervalFilter.Intersects(prop)
|
||||
}
|
||||
|
||||
var _ DataBlockIntervalCollector = (*testKeysSuffixIntervalCollector)(nil)
|
||||
|
||||
// testKeysSuffixIntervalCollector maintains an interval over the timestamps in
|
||||
// MVCC-like suffixes for keys (e.g. foo@123).
|
||||
type testKeysSuffixIntervalCollector struct {
|
||||
initialized bool
|
||||
lower, upper uint64
|
||||
}
|
||||
|
||||
// Add implements DataBlockIntervalCollector by adding the timestamp(s) in the
|
||||
// suffix(es) of this record to the current interval.
|
||||
//
|
||||
// Note that range sets and unsets may have multiple suffixes. Range key deletes
|
||||
// do not have a suffix. All other point keys have a single suffix.
|
||||
func (c *testKeysSuffixIntervalCollector) Add(key base.InternalKey, value []byte) error {
|
||||
i := testkeys.Comparer.Split(key.UserKey)
|
||||
if i == len(key.UserKey) {
|
||||
c.initialized = true
|
||||
c.lower, c.upper = 0, math.MaxUint64
|
||||
return nil
|
||||
}
|
||||
ts, err := testkeys.ParseSuffix(key.UserKey[i:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uts := uint64(ts)
|
||||
if !c.initialized {
|
||||
c.lower, c.upper = uts, uts+1
|
||||
c.initialized = true
|
||||
return nil
|
||||
}
|
||||
if uts < c.lower {
|
||||
c.lower = uts
|
||||
}
|
||||
if uts >= c.upper {
|
||||
c.upper = uts + 1
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FinishDataBlock implements DataBlockIntervalCollector.
|
||||
func (c *testKeysSuffixIntervalCollector) FinishDataBlock() (lower, upper uint64, err error) {
|
||||
l, u := c.lower, c.upper
|
||||
c.lower, c.upper = 0, 0
|
||||
c.initialized = false
|
||||
return l, u, nil
|
||||
}
|
||||
6
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
6
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
@@ -9,6 +9,12 @@ import "github.com/cockroachdb/pebble/internal/base"
|
||||
// InternalKeyKind exports the base.InternalKeyKind type.
|
||||
type InternalKeyKind = base.InternalKeyKind
|
||||
|
||||
// SeekGEFlags exports base.SeekGEFlags.
|
||||
type SeekGEFlags = base.SeekGEFlags
|
||||
|
||||
// SeekLTFlags exports base.SeekLTFlags.
|
||||
type SeekLTFlags = base.SeekLTFlags
|
||||
|
||||
// These constants are part of the file format, and should not be changed.
|
||||
const (
|
||||
InternalKeyKindDelete = base.InternalKeyKindDelete
|
||||
|
||||
16
vendor/github.com/cockroachdb/pebble/sstable/raw_block.go
generated
vendored
16
vendor/github.com/cockroachdb/pebble/sstable/raw_block.go
generated
vendored
@@ -33,7 +33,8 @@ func (w *rawBlockWriter) add(key InternalKey, value []byte) {
|
||||
// keys are stored in "raw" format (i.e. not as internal keys). Note that there
|
||||
// is significant similarity between this code and the code in blockIter. Yet
|
||||
// reducing duplication is difficult due to the blockIter being performance
|
||||
// critical.
|
||||
// critical. rawBlockIter must only be used for blocks where the value is
|
||||
// stored together with the key.
|
||||
type rawBlockIter struct {
|
||||
cmp Compare
|
||||
offset int32
|
||||
@@ -148,19 +149,6 @@ func (i *rawBlockIter) SeekGE(key []byte) bool {
|
||||
return i.Valid()
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements internalIterator.SeekPrefixGE, as documented in the
|
||||
// pebble package.
|
||||
func (i *rawBlockIter) SeekPrefixGE(key []byte) bool {
|
||||
// This should never be called as prefix iteration is never used with raw blocks.
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements internalIterator.SeekLT, as documented in the pebble
|
||||
// package.
|
||||
func (i *rawBlockIter) SeekLT(key []byte) bool {
|
||||
panic("pebble/table: SeekLT unimplemented")
|
||||
}
|
||||
|
||||
// First implements internalIterator.First, as documented in the pebble
|
||||
// package.
|
||||
func (i *rawBlockIter) First() bool {
|
||||
|
||||
1142
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
1142
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
File diff suppressed because it is too large
Load Diff
78
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
78
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
@@ -9,13 +9,16 @@ import (
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||
)
|
||||
|
||||
// RewriteKeySuffixes copies the content of the passed SSTable bytes to a new
|
||||
// sstable, written to `out`, in which the suffix `from` has is replaced with
|
||||
// `to` in every key. The input sstable must consist of only Sets and every key
|
||||
// must have `from` as its suffix as determined by the Split function of the
|
||||
// Comparer in the passed WriterOptions.
|
||||
// `to` in every key. The input sstable must consist of only Sets or RangeKeySets
|
||||
// and every key must have `from` as its suffix as determined by the Split
|
||||
// function of the Comparer in the passed WriterOptions. Range deletes must not
|
||||
// exist in this sstable, as they will be ignored.
|
||||
//
|
||||
// Data blocks are rewritten in parallel by `concurrency` workers and then
|
||||
// assembled into a final SST. Filters are copied from the original SST without
|
||||
@@ -73,12 +76,14 @@ func rewriteKeySuffixesInBlocks(
|
||||
return nil, errors.Wrap(err, "rewriting data blocks")
|
||||
}
|
||||
|
||||
// Copy over the range key block and replace suffixes in it if it exists.
|
||||
if err := rewriteRangeKeyBlockToWriter(r, w, from, to); err != nil {
|
||||
return nil, errors.Wrap(err, "rewriting range key blocks")
|
||||
}
|
||||
|
||||
// Copy over the filter block if it exists (rewriteDataBlocksToWriter will
|
||||
// already have ensured this is valid if it exists).
|
||||
if w.filter != nil {
|
||||
if l.Filter.Length == 0 {
|
||||
return nil, errors.New("input table has no filter")
|
||||
}
|
||||
if w.filter != nil && l.Filter.Length > 0 {
|
||||
filterBlock, _, err := readBlockBuf(r, l.Filter, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "reading filter")
|
||||
@@ -173,7 +178,8 @@ func rewriteBlocks(
|
||||
copy(scratch.UserKey, key.UserKey[:si])
|
||||
copy(scratch.UserKey[si:], to)
|
||||
|
||||
bw.add(scratch, val)
|
||||
v := val.InPlaceValue()
|
||||
bw.add(scratch, v)
|
||||
if output[i].start.UserKey == nil {
|
||||
keyAlloc, output[i].start = cloneKeyWithBuf(scratch, keyAlloc)
|
||||
}
|
||||
@@ -205,6 +211,10 @@ func rewriteDataBlocksToWriter(
|
||||
split Split,
|
||||
concurrency int,
|
||||
) error {
|
||||
if r.Properties.NumEntries == 0 {
|
||||
// No point keys.
|
||||
return nil
|
||||
}
|
||||
blocks := make([]blockWithSpan, len(data))
|
||||
|
||||
if w.filter != nil {
|
||||
@@ -313,8 +323,47 @@ func rewriteDataBlocksToWriter(
|
||||
w.props.NumEntries = r.Properties.NumEntries
|
||||
w.props.RawKeySize = r.Properties.RawKeySize
|
||||
w.props.RawValueSize = r.Properties.RawValueSize
|
||||
w.meta.SmallestPoint, w.meta.LargestPoint = blocks[0].start, blocks[len(blocks)-1].end
|
||||
w.meta.HasPointKeys = true
|
||||
w.meta.SetSmallestPointKey(blocks[0].start)
|
||||
w.meta.SetLargestPointKey(blocks[len(blocks)-1].end)
|
||||
return nil
|
||||
}
|
||||
|
||||
func rewriteRangeKeyBlockToWriter(r *Reader, w *Writer, from, to []byte) error {
|
||||
iter, err := r.NewRawRangeKeyIter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if iter == nil {
|
||||
// No range keys.
|
||||
return nil
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for s := iter.First(); s != nil; s = iter.Next() {
|
||||
if !s.Valid() {
|
||||
break
|
||||
}
|
||||
for i := range s.Keys {
|
||||
if s.Keys[i].Kind() != base.InternalKeyKindRangeKeySet {
|
||||
return errBadKind
|
||||
}
|
||||
if !bytes.Equal(s.Keys[i].Suffix, from) {
|
||||
return errors.Errorf("key has suffix %q, expected %q", s.Keys[i].Suffix, from)
|
||||
}
|
||||
s.Keys[i].Suffix = to
|
||||
}
|
||||
|
||||
err := rangekey.Encode(s, func(k base.InternalKey, v []byte) error {
|
||||
// Calling AddRangeKey instead of addRangeKeySpan bypasses the fragmenter.
|
||||
// This is okay because the raw fragments off of `iter` are already
|
||||
// fragmented, and suffix replacement should not affect fragmentation.
|
||||
return w.AddRangeKey(k, v)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -362,11 +411,18 @@ func RewriteKeySuffixesViaWriter(
|
||||
scratch.UserKey = append(scratch.UserKey, to...)
|
||||
scratch.Trailer = k.Trailer
|
||||
|
||||
if w.addPoint(scratch, v); err != nil {
|
||||
val, _, err := v.Value(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if w.addPoint(scratch, val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k, v = i.Next()
|
||||
}
|
||||
if err := rewriteRangeKeyBlockToWriter(r, w, from, to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
8
vendor/github.com/cockroachdb/pebble/sstable/write_queue.go
generated
vendored
8
vendor/github.com/cockroachdb/pebble/sstable/write_queue.go
generated
vendored
@@ -17,8 +17,6 @@ type writeTask struct {
|
||||
// currIndexBlock is the index block on which indexBlock.add must be called.
|
||||
currIndexBlock *indexBlockBuf
|
||||
indexEntrySep InternalKey
|
||||
// inflightSize is used to decrement Writer.coordination.sizeEstimate.inflightSize.
|
||||
inflightSize int
|
||||
// inflightIndexEntrySize is used to decrement Writer.indexBlock.sizeEstimate.inflightSize.
|
||||
indexInflightSize int
|
||||
// If the index block is finished, then we set the finishedIndexProps here.
|
||||
@@ -69,11 +67,6 @@ func (w *writeQueue) performWrite(task *writeTask) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the size estimates after writing the data block to disk.
|
||||
w.writer.coordination.sizeEstimate.dataBlockWritten(
|
||||
w.writer.meta.Size, task.inflightSize, int(bh.Length),
|
||||
)
|
||||
|
||||
bhp = BlockHandleWithProperties{BlockHandle: bh, Props: task.buf.dataBlockProps}
|
||||
if err = w.writer.addIndexEntry(
|
||||
task.indexEntrySep, bhp, task.buf.tmp[:], task.flushableIndexBlock, task.currIndexBlock,
|
||||
@@ -114,7 +107,6 @@ func (w *writeQueue) runWorker() {
|
||||
w.wg.Done()
|
||||
}
|
||||
|
||||
//lint:ignore U1000 - Will be used in a future pr.
|
||||
func (w *writeQueue) add(task *writeTask) {
|
||||
w.tasks <- task
|
||||
}
|
||||
|
||||
142
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
142
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
@@ -221,7 +221,9 @@ type coordinationState struct {
|
||||
|
||||
func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
|
||||
c.parallelismEnabled = parallelismEnabled
|
||||
c.sizeEstimate.useMutex = parallelismEnabled
|
||||
// useMutex is false regardless of parallelismEnabled, because we do not do
|
||||
// parallel compression yet.
|
||||
c.sizeEstimate.useMutex = false
|
||||
|
||||
// writeQueueSize determines the size of the write queue, or the number
|
||||
// of items which can be added to the queue without blocking. By default, we
|
||||
@@ -234,6 +236,47 @@ func (c *coordinationState) init(parallelismEnabled bool, writer *Writer) {
|
||||
c.writeQueue = newWriteQueue(writeQueueSize, writer)
|
||||
}
|
||||
|
||||
// sizeEstimate is a general purpose helper for estimating two kinds of sizes:
|
||||
// A. The compressed sstable size, which is useful for deciding when to start
|
||||
// a new sstable during flushes or compactions. In practice, we use this in
|
||||
// estimating the data size (excluding the index).
|
||||
// B. The size of index blocks to decide when to start a new index block.
|
||||
//
|
||||
// There are some terminology peculiarities which are due to the origin of
|
||||
// sizeEstimate for use case A with parallel compression enabled (for which
|
||||
// the code has not been merged). Specifically this relates to the terms
|
||||
// "written" and "compressed".
|
||||
// - The notion of "written" for case A is sufficiently defined by saying that
|
||||
// the data block is compressed. Waiting for the actual data block write to
|
||||
// happen can result in unnecessary estimation, when we already know how big
|
||||
// it will be in compressed form. Additionally, with the forthcoming value
|
||||
// blocks containing older MVCC values, these compressed block will be held
|
||||
// in-memory until late in the sstable writing, and we do want to accurately
|
||||
// account for them without waiting for the actual write.
|
||||
// For case B, "written" means that the index entry has been fully
|
||||
// generated, and has been added to the uncompressed block buffer for that
|
||||
// index block. It does not include actually writing a potentially
|
||||
// compressed index block.
|
||||
// - The notion of "compressed" is to differentiate between a "inflight" size
|
||||
// and the actual size, and is handled via computing a compression ratio
|
||||
// observed so far (defaults to 1).
|
||||
// For case A, this is actual data block compression, so the "inflight" size
|
||||
// is uncompressed blocks (that are no longer being written to) and the
|
||||
// "compressed" size is after they have been compressed.
|
||||
// For case B the inflight size is for a key-value pair in the index for
|
||||
// which the value size (the encoded size of the BlockHandleWithProperties)
|
||||
// is not accurately known, while the compressed size is the size of that
|
||||
// entry when it has been added to the (in-progress) index ssblock.
|
||||
//
|
||||
// Usage: To update state, one can optionally provide an inflight write value
|
||||
// using addInflight (used for case B). When something is "written" the state
|
||||
// can be updated using either writtenWithDelta or writtenWithTotal, which
|
||||
// provide the actual delta size or the total size (latter must be
|
||||
// monotonically non-decreasing). If there were no calls to addInflight, there
|
||||
// isn't any real estimation happening here. So case A does not do any real
|
||||
// estimation. However, when we introduce parallel compression, there will be
|
||||
// estimation in that the client goroutine will call addInFlight and the
|
||||
// compression goroutines will call writtenWithDelta.
|
||||
type sizeEstimate struct {
|
||||
// emptySize is the size when there is no inflight data, and numEntries is 0.
|
||||
// emptySize is constant once set.
|
||||
@@ -300,18 +343,22 @@ func (s *sizeEstimate) addInflight(size int) {
|
||||
s.inflightSize += uint64(size)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) written(newTotalSize uint64, inflightSize int, finalEntrySize int) {
|
||||
s.inflightSize -= uint64(inflightSize)
|
||||
func (s *sizeEstimate) writtenWithTotal(newTotalSize uint64, inflightSize int) {
|
||||
finalEntrySize := int(newTotalSize - s.totalSize)
|
||||
s.writtenWithDelta(finalEntrySize, inflightSize)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) writtenWithDelta(finalEntrySize int, inflightSize int) {
|
||||
if inflightSize > 0 {
|
||||
// This entry was previously inflight, so we should decrement inflight
|
||||
// entries.
|
||||
// entries and update the "compression" stats for future estimation.
|
||||
s.numInflightEntries--
|
||||
s.inflightSize -= uint64(inflightSize)
|
||||
s.uncompressedSize += uint64(inflightSize)
|
||||
s.compressedSize += uint64(finalEntrySize)
|
||||
}
|
||||
s.numWrittenEntries++
|
||||
s.totalSize = newTotalSize
|
||||
|
||||
s.uncompressedSize += uint64(inflightSize)
|
||||
s.compressedSize += uint64(finalEntrySize)
|
||||
s.totalSize += uint64(finalEntrySize)
|
||||
}
|
||||
|
||||
func (s *sizeEstimate) clear() {
|
||||
@@ -368,7 +415,6 @@ func (i *indexBlockBuf) shouldFlush(
|
||||
defer i.size.mu.Unlock()
|
||||
}
|
||||
|
||||
// nEntries := i.size.estimate.numWrittenEntries + i.size.estimate.numInflightEntries
|
||||
nEntries := i.size.estimate.numTotalEntries()
|
||||
return shouldFlush(
|
||||
sep, valueLen, i.restartInterval, int(i.size.estimate.size()),
|
||||
@@ -382,10 +428,7 @@ func (i *indexBlockBuf) add(key InternalKey, value []byte, inflightSize int) {
|
||||
i.size.mu.Lock()
|
||||
defer i.size.mu.Unlock()
|
||||
}
|
||||
// Since, we're not compressing index entries when adding them to index blocks,
|
||||
// we assume that the size of entry written to the index block is equal to the
|
||||
// size of the inflight entry, giving us a compression ratio of 1.
|
||||
i.size.estimate.written(uint64(size), inflightSize, inflightSize)
|
||||
i.size.estimate.writtenWithTotal(uint64(size), inflightSize)
|
||||
}
|
||||
|
||||
func (i *indexBlockBuf) finish() []byte {
|
||||
@@ -424,11 +467,12 @@ func (i *indexBlockBuf) estimatedSize() uint64 {
|
||||
return i.size.estimate.size()
|
||||
}
|
||||
|
||||
// sizeEstimate is used for sstable size estimation. sizeEstimate can be accessed by
|
||||
// the Writer client, writeQueue, compressionQueue goroutines. Fields should only be
|
||||
// read/updated through the functions defined on the *sizeEstimate type.
|
||||
// sizeEstimate is used for sstable size estimation. sizeEstimate can be
|
||||
// accessed by the Writer client and compressionQueue goroutines. Fields
|
||||
// should only be read/updated through the functions defined on the
|
||||
// *sizeEstimate type.
|
||||
type dataBlockEstimates struct {
|
||||
// If we don't do block compression, block writes in parallel, then we don't need to take
|
||||
// If we don't do block compression in parallel, then we don't need to take
|
||||
// the performance hit of synchronizing using this mutex.
|
||||
useMutex bool
|
||||
mu sync.Mutex
|
||||
@@ -436,18 +480,16 @@ type dataBlockEstimates struct {
|
||||
estimate sizeEstimate
|
||||
}
|
||||
|
||||
// newTotalSize is the new w.meta.Size. inflightSize is the uncompressed block size estimate which
|
||||
// was previously added to sizeEstimate.inflightSize. writtenSize is the compressed size of the block
|
||||
// which was written to disk.
|
||||
func (d *dataBlockEstimates) dataBlockWritten(
|
||||
newTotalSize uint64, inflightSize int, writtenSize int,
|
||||
) {
|
||||
// inflightSize is the uncompressed block size estimate which has been
|
||||
// previously provided to addInflightDataBlock(). If addInflightDataBlock()
|
||||
// has not been called, this must be set to 0. compressedSize is the
|
||||
// compressed size of the block.
|
||||
func (d *dataBlockEstimates) dataBlockCompressed(compressedSize int, inflightSize int) {
|
||||
if d.useMutex {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
}
|
||||
|
||||
d.estimate.written(newTotalSize, inflightSize, writtenSize)
|
||||
d.estimate.writtenWithDelta(compressedSize+blockTrailerLen, inflightSize)
|
||||
}
|
||||
|
||||
// size is an estimated size of datablock data which has been written to disk.
|
||||
@@ -456,18 +498,19 @@ func (d *dataBlockEstimates) size() uint64 {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
}
|
||||
|
||||
// Use invariants to make sure that the size estimation works as expected
|
||||
// when parallelism is disabled.
|
||||
// If there is no parallel compression, there should not be any inflight bytes.
|
||||
if invariants.Enabled && !d.useMutex {
|
||||
if d.estimate.inflightSize != 0 {
|
||||
panic("unexpected inflight entry in data block size estimation")
|
||||
}
|
||||
}
|
||||
|
||||
return d.estimate.size()
|
||||
}
|
||||
|
||||
// Avoid linter unused error.
|
||||
var _ = (&dataBlockEstimates{}).addInflightDataBlock
|
||||
|
||||
// NB: unused since no parallel compression.
|
||||
func (d *dataBlockEstimates) addInflightDataBlock(size int) {
|
||||
if d.useMutex {
|
||||
d.mu.Lock()
|
||||
@@ -609,9 +652,10 @@ type indexBlockAndBlockProperties struct {
|
||||
block []byte
|
||||
}
|
||||
|
||||
// Set sets the value for the given key. The sequence number is set to
|
||||
// 0. Intended for use to externally construct an sstable before ingestion into
|
||||
// a DB.
|
||||
// Set sets the value for the given key. The sequence number is set to 0.
|
||||
// Intended for use to externally construct an sstable before ingestion into a
|
||||
// DB. For a given Writer, the keys passed to Set must be in strictly increasing
|
||||
// order.
|
||||
//
|
||||
// TODO(peter): untested
|
||||
func (w *Writer) Set(key, value []byte) error {
|
||||
@@ -708,8 +752,8 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
||||
// 3.5% faster on BenchmarkWriter on go1.13. Remove if go1.14 or future
|
||||
// versions show this to not be a performance win.
|
||||
x := w.compare(largestPointKey.UserKey, key.UserKey)
|
||||
if x > 0 || (x == 0 && largestPointKey.Trailer < key.Trailer) {
|
||||
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
||||
if x > 0 || (x == 0 && largestPointKey.Trailer <= key.Trailer) {
|
||||
w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
largestPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
return w.err
|
||||
}
|
||||
@@ -790,7 +834,7 @@ func (w *Writer) addTombstone(key InternalKey, value []byte) error {
|
||||
return w.err
|
||||
}
|
||||
if prevKey.SeqNum() <= key.SeqNum() {
|
||||
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
||||
w.err = errors.Errorf("pebble: keys must be added in strictly increasing order: %s, %s",
|
||||
prevKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||
return w.err
|
||||
}
|
||||
@@ -938,7 +982,9 @@ func (w *Writer) addRangeKeySpan(span keyspan.Span) error {
|
||||
func (w *Writer) coalesceSpans(span keyspan.Span) {
|
||||
// This method is the emit function of the Fragmenter, so span.Keys is only
|
||||
// owned by this span and it's safe to mutate.
|
||||
err := rangekey.Coalesce(w.compare, span, &w.rangeKeyCoalesced)
|
||||
w.rangeKeyCoalesced.Start = span.Start
|
||||
w.rangeKeyCoalesced.End = span.End
|
||||
err := rangekey.Coalesce(w.compare, span.Keys, &w.rangeKeyCoalesced.Keys)
|
||||
if err != nil {
|
||||
w.err = errors.Newf("sstable: could not coalesce span: %s", err)
|
||||
return
|
||||
@@ -946,7 +992,7 @@ func (w *Writer) coalesceSpans(span keyspan.Span) {
|
||||
|
||||
// NB: The span only contains range keys and is internally consistent (eg,
|
||||
// no duplicate suffixes, no additional keys after a RANGEKEYDEL).
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(w.rangeKeyCoalesced))
|
||||
w.err = firstError(w.err, w.rangeKeyEncoder.Encode(&w.rangeKeyCoalesced))
|
||||
}
|
||||
|
||||
func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
|
||||
@@ -1080,19 +1126,16 @@ func (w *Writer) maybeAddToFilter(key []byte) {
|
||||
}
|
||||
|
||||
func (w *Writer) flush(key InternalKey) error {
|
||||
estimatedUncompressedSize := w.dataBlockBuf.dataBlock.estimatedSize()
|
||||
w.coordination.sizeEstimate.addInflightDataBlock(estimatedUncompressedSize)
|
||||
|
||||
var err error
|
||||
|
||||
// We're finishing a data block.
|
||||
err = w.finishDataBlockProps(w.dataBlockBuf)
|
||||
err := w.finishDataBlockProps(w.dataBlockBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.dataBlockBuf.finish()
|
||||
w.dataBlockBuf.compressAndChecksum(w.compression)
|
||||
// Since dataBlockEstimates.addInflightDataBlock was never called, the
|
||||
// inflightSize is set to 0.
|
||||
w.coordination.sizeEstimate.dataBlockCompressed(len(w.dataBlockBuf.compressed), 0)
|
||||
|
||||
// Determine if the index block should be flushed. Since we're accessing the
|
||||
// dataBlockBuf.dataBlock.curKey here, we have to make sure that once we start
|
||||
@@ -1139,7 +1182,6 @@ func (w *Writer) flush(key InternalKey) error {
|
||||
writeTask.compressionDone <- true
|
||||
writeTask.buf = w.dataBlockBuf
|
||||
writeTask.indexEntrySep = sep
|
||||
writeTask.inflightSize = estimatedUncompressedSize
|
||||
writeTask.currIndexBlock = w.indexBlock
|
||||
writeTask.indexInflightSize = sep.Size() + encodedBHPEstimatedSize
|
||||
writeTask.finishedIndexProps = indexProps
|
||||
@@ -1813,14 +1855,6 @@ func (w *Writer) Close() (err error) {
|
||||
// EstimatedSize returns the estimated size of the sstable being written if a
|
||||
// call to Finish() was made without adding additional keys.
|
||||
func (w *Writer) EstimatedSize() uint64 {
|
||||
if invariants.Enabled && !w.coordination.parallelismEnabled {
|
||||
// The w.meta.Size should only be accessed from the writeQueue goroutine
|
||||
// if parallelism is enabled, but since it isn't we break that invariant
|
||||
// here.
|
||||
if w.coordination.sizeEstimate.size() != w.meta.Size {
|
||||
panic("sstable size estimation sans parallelism is incorrect")
|
||||
}
|
||||
}
|
||||
return w.coordination.sizeEstimate.size() +
|
||||
uint64(w.dataBlockBuf.dataBlock.estimatedSize()) +
|
||||
w.indexBlock.estimatedSize()
|
||||
@@ -1838,7 +1872,7 @@ func (w *Writer) Metadata() (*WriterMetadata, error) {
|
||||
// WriterOption provide an interface to do work on Writer while it is being
|
||||
// opened.
|
||||
type WriterOption interface {
|
||||
// writerAPply is called on the writer during opening in order to set
|
||||
// writerApply is called on the writer during opening in order to set
|
||||
// internal parameters.
|
||||
writerApply(*Writer)
|
||||
}
|
||||
|
||||
141
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
141
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
@@ -28,6 +28,25 @@ import (
|
||||
var emptyIter = &errorIter{err: nil}
|
||||
var emptyKeyspanIter = &errorKeyspanIter{err: nil}
|
||||
|
||||
// filteredAll is a singleton internalIterator implementation used when an
|
||||
// sstable does contain point keys, but all the keys are filtered by the active
|
||||
// PointKeyFilters set in the iterator's IterOptions.
|
||||
//
|
||||
// filteredAll implements filteredIter, ensuring the level iterator recognizes
|
||||
// when it may need to return file boundaries to keep the rangeDelIter open
|
||||
// during mergingIter operation.
|
||||
var filteredAll = &filteredAllKeysIter{errorIter: errorIter{err: nil}}
|
||||
|
||||
var _ filteredIter = filteredAll
|
||||
|
||||
type filteredAllKeysIter struct {
|
||||
errorIter
|
||||
}
|
||||
|
||||
func (s *filteredAllKeysIter) MaybeFilteredKeys() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var tableCacheLabels = pprof.Labels("pebble", "table-cache")
|
||||
|
||||
// tableCacheOpts contains the db specific fields
|
||||
@@ -114,9 +133,9 @@ func (c *tableCacheContainer) close() error {
|
||||
}
|
||||
|
||||
func (c *tableCacheContainer) newIters(
|
||||
file *manifest.FileMetadata, opts *IterOptions, bytesIterated *uint64,
|
||||
file *manifest.FileMetadata, opts *IterOptions, internalOpts internalIterOpts,
|
||||
) (internalIterator, keyspan.FragmentIterator, error) {
|
||||
return c.tableCache.getShard(file.FileNum).newIters(file, opts, bytesIterated, &c.dbOpts)
|
||||
return c.tableCache.getShard(file.FileNum).newIters(file, opts, internalOpts, &c.dbOpts)
|
||||
}
|
||||
|
||||
func (c *tableCacheContainer) newRangeKeyIter(
|
||||
@@ -280,8 +299,9 @@ type tableCacheShard struct {
|
||||
sizeCold int
|
||||
sizeTest int
|
||||
}
|
||||
releasing sync.WaitGroup
|
||||
releasingCh chan *tableCacheValue
|
||||
releasing sync.WaitGroup
|
||||
releasingCh chan *tableCacheValue
|
||||
releaseLoopExit sync.WaitGroup
|
||||
}
|
||||
|
||||
func (c *tableCacheShard) init(size int) {
|
||||
@@ -290,6 +310,7 @@ func (c *tableCacheShard) init(size int) {
|
||||
c.mu.nodes = make(map[tableCacheKey]*tableCacheNode)
|
||||
c.mu.coldTarget = size
|
||||
c.releasingCh = make(chan *tableCacheValue, 100)
|
||||
c.releaseLoopExit.Add(1)
|
||||
go c.releaseLoop()
|
||||
|
||||
if invariants.RaceEnabled {
|
||||
@@ -299,6 +320,7 @@ func (c *tableCacheShard) init(size int) {
|
||||
|
||||
func (c *tableCacheShard) releaseLoop() {
|
||||
pprof.Do(context.Background(), tableCacheLabels, func(context.Context) {
|
||||
defer c.releaseLoopExit.Done()
|
||||
for v := range c.releasingCh {
|
||||
v.release(c)
|
||||
}
|
||||
@@ -312,14 +334,15 @@ func (c *tableCacheShard) checkAndIntersectFilters(
|
||||
v *tableCacheValue,
|
||||
tableFilter func(userProps map[string]string) bool,
|
||||
blockPropertyFilters []BlockPropertyFilter,
|
||||
boundLimitedFilter sstable.BoundLimitedBlockPropertyFilter,
|
||||
) (ok bool, filterer *sstable.BlockPropertiesFilterer, err error) {
|
||||
if tableFilter != nil &&
|
||||
!tableFilter(v.reader.Properties.UserProperties) {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
if len(blockPropertyFilters) > 0 {
|
||||
filterer = sstable.NewBlockPropertiesFilterer(blockPropertyFilters)
|
||||
if boundLimitedFilter != nil || len(blockPropertyFilters) > 0 {
|
||||
filterer = sstable.NewBlockPropertiesFilterer(blockPropertyFilters, boundLimitedFilter)
|
||||
intersects, err :=
|
||||
filterer.IntersectsUserPropsAndFinishInit(v.reader.Properties.UserProperties)
|
||||
if err != nil {
|
||||
@@ -333,7 +356,10 @@ func (c *tableCacheShard) checkAndIntersectFilters(
|
||||
}
|
||||
|
||||
func (c *tableCacheShard) newIters(
|
||||
file *manifest.FileMetadata, opts *IterOptions, bytesIterated *uint64, dbOpts *tableCacheOpts,
|
||||
file *manifest.FileMetadata,
|
||||
opts *IterOptions,
|
||||
internalOpts internalIterOpts,
|
||||
dbOpts *tableCacheOpts,
|
||||
) (internalIterator, keyspan.FragmentIterator, error) {
|
||||
// Calling findNode gives us the responsibility of decrementing v's
|
||||
// refCount. If opening the underlying table resulted in error, then we
|
||||
@@ -350,17 +376,36 @@ func (c *tableCacheShard) newIters(
|
||||
var filterer *sstable.BlockPropertiesFilterer
|
||||
var err error
|
||||
if opts != nil {
|
||||
ok, filterer, err = c.checkAndIntersectFilters(v, opts.TableFilter, opts.PointKeyFilters)
|
||||
ok, filterer, err = c.checkAndIntersectFilters(v, opts.TableFilter,
|
||||
opts.PointKeyFilters, internalOpts.boundLimitedFilter)
|
||||
}
|
||||
if err != nil {
|
||||
c.unrefValue(v)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// NB: range-del iterator does not maintain a reference to the table, nor
|
||||
// does it need to read from it after creation.
|
||||
rangeDelIter, err := v.reader.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
c.unrefValue(v)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
c.unrefValue(v)
|
||||
// Return the empty iterator. This iterator has no mutable state, so
|
||||
// Return an empty iterator. This iterator has no mutable state, so
|
||||
// using a singleton is fine.
|
||||
return emptyIter, nil, err
|
||||
// NB: We still return the potentially non-empty rangeDelIter. This
|
||||
// ensures the iterator observes the file's range deletions even if the
|
||||
// block property filters exclude all the file's point keys. The range
|
||||
// deletions may still delete keys lower in the LSM in files that DO
|
||||
// match the active filters.
|
||||
//
|
||||
// The point iterator returned must implement the filteredIter
|
||||
// interface, so that the level iterator surfaces file boundaries when
|
||||
// range deletions are present.
|
||||
return filteredAll, rangeDelIter, err
|
||||
}
|
||||
|
||||
var iter sstable.Iterator
|
||||
@@ -368,20 +413,22 @@ func (c *tableCacheShard) newIters(
|
||||
if opts != nil {
|
||||
useFilter = manifest.LevelToInt(opts.level) != 6 || opts.UseL6Filters
|
||||
}
|
||||
if bytesIterated != nil {
|
||||
iter, err = v.reader.NewCompactionIter(bytesIterated)
|
||||
if internalOpts.bytesIterated != nil {
|
||||
iter, err = v.reader.NewCompactionIter(internalOpts.bytesIterated)
|
||||
} else {
|
||||
iter, err = v.reader.NewIterWithBlockPropertyFilters(
|
||||
opts.GetLowerBound(), opts.GetUpperBound(), filterer, useFilter)
|
||||
opts.GetLowerBound(), opts.GetUpperBound(), filterer, useFilter, internalOpts.stats)
|
||||
}
|
||||
if err != nil {
|
||||
if rangeDelIter != nil {
|
||||
_ = rangeDelIter.Close()
|
||||
}
|
||||
c.unrefValue(v)
|
||||
return nil, nil, err
|
||||
}
|
||||
// NB: v.closeHook takes responsibility for calling unrefValue(v) here.
|
||||
iter.SetCloseHook(func(i sstable.Iterator) error {
|
||||
return v.closeHook(i)
|
||||
})
|
||||
// NB: v.closeHook takes responsibility for calling unrefValue(v) here. Take
|
||||
// care to avoid introduceingan allocation here by adding a closure.
|
||||
iter.SetCloseHook(v.closeHook)
|
||||
|
||||
atomic.AddInt32(&c.atomic.iterCount, 1)
|
||||
atomic.AddInt32(dbOpts.atomic.iterCount, 1)
|
||||
@@ -390,19 +437,7 @@ func (c *tableCacheShard) newIters(
|
||||
c.mu.iters[iter] = debug.Stack()
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// NB: range-del iterator does not maintain a reference to the table, nor
|
||||
// does it need to read from it after creation.
|
||||
rangeDelIter, err := v.reader.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
_ = iter.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
if rangeDelIter != nil {
|
||||
return iter, rangeDelIter, nil
|
||||
}
|
||||
// NB: Translate a nil range-del iterator into a nil interface.
|
||||
return iter, nil, nil
|
||||
return iter, rangeDelIter, nil
|
||||
}
|
||||
|
||||
func (c *tableCacheShard) newRangeKeyIter(
|
||||
@@ -421,8 +456,13 @@ func (c *tableCacheShard) newRangeKeyIter(
|
||||
|
||||
ok := true
|
||||
var err error
|
||||
if opts != nil {
|
||||
ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters)
|
||||
// Don't filter a table's range keys if the file contains RANGEKEYDELs.
|
||||
// The RANGEKEYDELs may delete range keys in other levels. Skipping the
|
||||
// file's range key blocks may surface deleted range keys below. This is
|
||||
// done here, rather than deferring to the block-property collector in order
|
||||
// to maintain parity with point keys and the treatment of RANGEDELs.
|
||||
if opts != nil && v.reader.Properties.NumRangeKeyDels == 0 {
|
||||
ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters, nil)
|
||||
}
|
||||
if err != nil {
|
||||
c.unrefValue(v)
|
||||
@@ -435,27 +475,14 @@ func (c *tableCacheShard) newRangeKeyIter(
|
||||
return emptyKeyspanIter, err
|
||||
}
|
||||
|
||||
var iter sstable.FragmentIterator
|
||||
// TODO(bilal): We are currently passing through the raw blockIter for range
|
||||
// keys. This iter does not support bounds (eg. SetBounds will panic).
|
||||
// Any future users of the iter returned by this function need to make any
|
||||
// bounds-specific optimizations themselves.
|
||||
var iter keyspan.FragmentIterator
|
||||
iter, err = v.reader.NewRawRangeKeyIter()
|
||||
if err != nil || iter == nil {
|
||||
c.unrefValue(v)
|
||||
return nil, err
|
||||
}
|
||||
// NB: v.closeHook takes responsibility for calling unrefValue(v) here.
|
||||
iter.SetCloseHook(func(i keyspan.FragmentIterator) error {
|
||||
return v.closeHook(i)
|
||||
})
|
||||
// iter is a block iter that holds the entire value of the block in memory.
|
||||
// No need to hold onto a ref of the cache value.
|
||||
c.unrefValue(v)
|
||||
|
||||
atomic.AddInt32(&c.atomic.iterCount, 1)
|
||||
atomic.AddInt32(dbOpts.atomic.iterCount, 1)
|
||||
if invariants.RaceEnabled {
|
||||
c.mu.Lock()
|
||||
c.mu.iters[iter] = debug.Stack()
|
||||
c.mu.Unlock()
|
||||
if err != nil || iter == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return iter, nil
|
||||
@@ -607,7 +634,7 @@ func (c *tableCacheShard) findNode(meta *fileMetadata, dbOpts *tableCacheOpts) *
|
||||
}
|
||||
// Cache the closure invoked when an iterator is closed. This avoids an
|
||||
// allocation on every call to newIters.
|
||||
v.closeHook = func(i io.Closer) error {
|
||||
v.closeHook = func(i sstable.Iterator) error {
|
||||
if invariants.RaceEnabled {
|
||||
c.mu.Lock()
|
||||
delete(c.mu.iters, i)
|
||||
@@ -829,15 +856,19 @@ func (c *tableCacheShard) Close() error {
|
||||
// complete. This behavior is used by iterator leak tests. Leaking the
|
||||
// goroutine for these tests is less bad not closing the iterator which
|
||||
// triggers other warnings about block cache handles not being released.
|
||||
if err == nil {
|
||||
close(c.releasingCh)
|
||||
if err != nil {
|
||||
c.releasing.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
close(c.releasingCh)
|
||||
c.releasing.Wait()
|
||||
c.releaseLoopExit.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
type tableCacheValue struct {
|
||||
closeHook func(i io.Closer) error
|
||||
closeHook func(i sstable.Iterator) error
|
||||
reader *sstable.Reader
|
||||
filename string
|
||||
err error
|
||||
|
||||
479
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
479
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
@@ -52,7 +53,7 @@ func (d *DB) maybeCollectTableStatsLocked() {
|
||||
func (d *DB) updateTableStatsLocked(newFiles []manifest.NewFileEntry) {
|
||||
var needStats bool
|
||||
for _, nf := range newFiles {
|
||||
if !nf.Meta.Stats.Valid {
|
||||
if !nf.Meta.StatsValidLocked() {
|
||||
needStats = true
|
||||
break
|
||||
}
|
||||
@@ -125,7 +126,8 @@ func (d *DB) collectTableStats() bool {
|
||||
maybeCompact := false
|
||||
for _, c := range collected {
|
||||
c.fileMetadata.Stats = c.TableStats
|
||||
maybeCompact = maybeCompact || c.fileMetadata.Stats.RangeDeletionsBytesEstimate > 0
|
||||
maybeCompact = maybeCompact || c.TableStats.RangeDeletionsBytesEstimate > 0
|
||||
c.fileMetadata.StatsMarkValid()
|
||||
}
|
||||
d.mu.tableStats.cond.Broadcast()
|
||||
d.maybeCollectTableStatsLocked()
|
||||
@@ -173,7 +175,7 @@ func (d *DB) loadNewFileStats(
|
||||
// collectTableStats updates f.Stats for active files, and we
|
||||
// ensure only one goroutine runs it at a time through
|
||||
// d.mu.tableStats.loading.
|
||||
if nf.Meta.Stats.Valid {
|
||||
if nf.Meta.StatsValidLocked() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -215,9 +217,9 @@ func (d *DB) scanReadStateTableStats(
|
||||
// NB: We're not holding d.mu which protects f.Stats, but only the
|
||||
// active stats collection job updates f.Stats for active files,
|
||||
// and we ensure only one goroutine runs it at a time through
|
||||
// d.mu.tableStats.loading. This makes it safe to read
|
||||
// f.Stats.Valid despite not holding d.mu.
|
||||
if f.Stats.Valid {
|
||||
// d.mu.tableStats.loading. This makes it safe to read validity
|
||||
// through f.Stats.ValidLocked despite not holding d.mu.
|
||||
if f.StatsValidLocked() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -261,7 +263,7 @@ func (d *DB) loadTableStats(
|
||||
return
|
||||
}
|
||||
}
|
||||
if r.Properties.NumRangeDeletions > 0 {
|
||||
if r.Properties.NumRangeDeletions > 0 || r.Properties.NumRangeKeyDels > 0 {
|
||||
if compactionHints, err = d.loadTableRangeDelStats(r, v, level, meta, &stats); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -269,13 +271,12 @@ func (d *DB) loadTableStats(
|
||||
// TODO(travers): Once we have real-world data, consider collecting
|
||||
// additional stats that may provide improved heuristics for compaction
|
||||
// picking.
|
||||
stats.NumRangeKeys = r.Properties.NumRangeKeys()
|
||||
stats.NumRangeKeySets = r.Properties.NumRangeKeySets
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
return stats, nil, err
|
||||
}
|
||||
stats.Valid = true
|
||||
return stats, compactionHints, nil
|
||||
}
|
||||
|
||||
@@ -299,68 +300,85 @@ func (d *DB) loadTablePointKeyStats(
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadTableRangeDelStats calculates the range deletion statistics for the given
|
||||
// table.
|
||||
// loadTableRangeDelStats calculates the range deletion and range key deletion
|
||||
// statistics for the given table.
|
||||
func (d *DB) loadTableRangeDelStats(
|
||||
r *sstable.Reader, v *version, level int, meta *fileMetadata, stats *manifest.TableStats,
|
||||
) ([]deleteCompactionHint, error) {
|
||||
var compactionHints []deleteCompactionHint
|
||||
// We iterate over the defragmented range tombstones, which ensures
|
||||
// we don't double count ranges deleted at different sequence numbers.
|
||||
// Also, merging abutting tombstones reduces the number of calls to
|
||||
// estimateSizeBeneath which is costly, and improves the accuracy of
|
||||
// our overall estimate.
|
||||
rangeDelIter, err := r.NewRawRangeDelIter()
|
||||
iter, err := newCombinedDeletionKeyspanIter(d.opts.Comparer, r, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rangeDelIter.Close()
|
||||
// Truncate tombstones to the containing file's bounds if necessary.
|
||||
// See docs/range_deletions.md for why this is necessary.
|
||||
rangeDelIter = keyspan.Truncate(
|
||||
d.cmp, rangeDelIter, meta.Smallest.UserKey, meta.Largest.UserKey, nil, nil)
|
||||
err = foreachDefragmentedTombstone(rangeDelIter, d.cmp,
|
||||
func(startUserKey, endUserKey []byte, smallestSeqNum, largestSeqNum uint64) error {
|
||||
// If the file is in the last level of the LSM, there is no
|
||||
// data beneath it. The fact that there is still a range
|
||||
// tombstone in a bottommost file suggests that an open
|
||||
// snapshot kept the tombstone around. Estimate disk usage
|
||||
// within the file itself.
|
||||
if level == numLevels-1 {
|
||||
size, err := r.EstimateDiskUsage(startUserKey, endUserKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats.RangeDeletionsBytesEstimate += size
|
||||
return nil
|
||||
defer iter.Close()
|
||||
var compactionHints []deleteCompactionHint
|
||||
// We iterate over the defragmented range tombstones and range key deletions,
|
||||
// which ensures we don't double count ranges deleted at different sequence
|
||||
// numbers. Also, merging abutting tombstones reduces the number of calls to
|
||||
// estimateReclaimedSizeBeneath which is costly, and improves the accuracy of
|
||||
// our overall estimate.
|
||||
for s := iter.First(); s != nil; s = iter.Next() {
|
||||
start, end := s.Start, s.End
|
||||
// We only need to consider deletion size estimates for tables that contain
|
||||
// point keys.
|
||||
var hasPoints bool
|
||||
for _, k := range s.Keys {
|
||||
if k.Kind() == base.InternalKeyKindRangeDelete {
|
||||
hasPoints = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
estimate, hintSeqNum, err := d.estimateSizeBeneath(v, level, meta, startUserKey, endUserKey)
|
||||
// If the file is in the last level of the LSM, there is no data beneath
|
||||
// it. The fact that there is still a range tombstone in a bottommost file
|
||||
// suggests that an open snapshot kept the tombstone around. Estimate disk
|
||||
// usage within the file itself.
|
||||
// NOTE: If the span `s` wholly contains a table containing range keys,
|
||||
// the returned size estimate will be slightly inflated by the range key
|
||||
// block. However, in practice, range keys are expected to be rare, and
|
||||
// the size of the range key block relative to the overall size of the
|
||||
// table is expected to be small.
|
||||
if hasPoints && level == numLevels-1 {
|
||||
size, err := r.EstimateDiskUsage(start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
stats.RangeDeletionsBytesEstimate += estimate
|
||||
stats.RangeDeletionsBytesEstimate += size
|
||||
|
||||
// If any files were completely contained with the range,
|
||||
// hintSeqNum is the smallest sequence number contained in any
|
||||
// such file.
|
||||
if hintSeqNum == math.MaxUint64 {
|
||||
return nil
|
||||
}
|
||||
hint := deleteCompactionHint{
|
||||
start: make([]byte, len(startUserKey)),
|
||||
end: make([]byte, len(endUserKey)),
|
||||
tombstoneFile: meta,
|
||||
tombstoneLevel: level,
|
||||
tombstoneLargestSeqNum: largestSeqNum,
|
||||
tombstoneSmallestSeqNum: smallestSeqNum,
|
||||
fileSmallestSeqNum: hintSeqNum,
|
||||
}
|
||||
copy(hint.start, startUserKey)
|
||||
copy(hint.end, endUserKey)
|
||||
compactionHints = append(compactionHints, hint)
|
||||
return nil
|
||||
})
|
||||
// As the file is in the bottommost level, there is no need to collect a
|
||||
// deletion hint.
|
||||
continue
|
||||
}
|
||||
|
||||
// While the size estimates for point keys should only be updated if this
|
||||
// span contains a range del, the sequence numbers are required for the
|
||||
// hint. Unconditionally descend, but conditionally update the estimates.
|
||||
hintType := compactionHintFromKeys(s.Keys)
|
||||
estimate, hintSeqNum, err := d.estimateReclaimedSizeBeneath(v, level, start, end, hintType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats.RangeDeletionsBytesEstimate += estimate
|
||||
|
||||
// If any files were completely contained with the range,
|
||||
// hintSeqNum is the smallest sequence number contained in any
|
||||
// such file.
|
||||
if hintSeqNum == math.MaxUint64 {
|
||||
continue
|
||||
}
|
||||
hint := deleteCompactionHint{
|
||||
hintType: hintType,
|
||||
start: make([]byte, len(start)),
|
||||
end: make([]byte, len(end)),
|
||||
tombstoneFile: meta,
|
||||
tombstoneLevel: level,
|
||||
tombstoneLargestSeqNum: s.LargestSeqNum(),
|
||||
tombstoneSmallestSeqNum: s.SmallestSeqNum(),
|
||||
fileSmallestSeqNum: hintSeqNum,
|
||||
}
|
||||
copy(hint.start, start)
|
||||
copy(hint.end, end)
|
||||
compactionHints = append(compactionHints, hint)
|
||||
}
|
||||
return compactionHints, err
|
||||
}
|
||||
|
||||
@@ -410,8 +428,8 @@ func (d *DB) averageEntrySizeBeneath(
|
||||
return avgKeySize, avgValueSize, err
|
||||
}
|
||||
|
||||
func (d *DB) estimateSizeBeneath(
|
||||
v *version, level int, meta *fileMetadata, start, end []byte,
|
||||
func (d *DB) estimateReclaimedSizeBeneath(
|
||||
v *version, level int, start, end []byte, hintType deleteCompactionHintType,
|
||||
) (estimate uint64, hintSeqNum uint64, err error) {
|
||||
// Find all files in lower levels that overlap with the deleted range
|
||||
// [start, end).
|
||||
@@ -430,13 +448,58 @@ func (d *DB) estimateSizeBeneath(
|
||||
startCmp := d.cmp(start, file.Smallest.UserKey)
|
||||
endCmp := d.cmp(file.Largest.UserKey, end)
|
||||
if startCmp <= 0 && (endCmp < 0 || endCmp == 0 && file.Largest.IsExclusiveSentinel()) {
|
||||
// The range fully contains the file, so skip looking it up in
|
||||
// table cache/looking at its indexes and add the full file size.
|
||||
estimate += file.Size
|
||||
if hintSeqNum > file.SmallestSeqNum {
|
||||
// The range fully contains the file, so skip looking it up in table
|
||||
// cache/looking at its indexes and add the full file size. Whether the
|
||||
// disk estimate and hint seqnums are updated depends on a) the type of
|
||||
// hint that requested the estimate and b) the keys contained in this
|
||||
// current file.
|
||||
var updateEstimates, updateHints bool
|
||||
switch hintType {
|
||||
case deleteCompactionHintTypePointKeyOnly:
|
||||
// The range deletion byte estimates should only be updated if this
|
||||
// table contains point keys. This ends up being an overestimate in
|
||||
// the case that table also has range keys, but such keys are expected
|
||||
// to contribute a negligible amount of the table's overall size,
|
||||
// relative to point keys.
|
||||
if file.HasPointKeys {
|
||||
updateEstimates = true
|
||||
}
|
||||
// As the initiating span contained only range dels, hints can only be
|
||||
// updated if this table does _not_ contain range keys.
|
||||
if !file.HasRangeKeys {
|
||||
updateHints = true
|
||||
}
|
||||
case deleteCompactionHintTypeRangeKeyOnly:
|
||||
// The initiating span contained only range key dels. The estimates
|
||||
// apply only to point keys, and are therefore not updated.
|
||||
updateEstimates = false
|
||||
// As the initiating span contained only range key dels, hints can
|
||||
// only be updated if this table does _not_ contain point keys.
|
||||
if !file.HasPointKeys {
|
||||
updateHints = true
|
||||
}
|
||||
case deleteCompactionHintTypePointAndRangeKey:
|
||||
// Always update the estimates and hints, as this hint type can drop a
|
||||
// file, irrespective of the mixture of keys. Similar to above, the
|
||||
// range del bytes estimates is an overestimate.
|
||||
updateEstimates, updateHints = true, true
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unknown hint type %s", hintType))
|
||||
}
|
||||
if updateEstimates {
|
||||
estimate += file.Size
|
||||
}
|
||||
if updateHints && hintSeqNum > file.SmallestSeqNum {
|
||||
hintSeqNum = file.SmallestSeqNum
|
||||
}
|
||||
} else if d.cmp(file.Smallest.UserKey, end) <= 0 && d.cmp(start, file.Largest.UserKey) <= 0 {
|
||||
// Partial overlap.
|
||||
if hintType == deleteCompactionHintTypeRangeKeyOnly {
|
||||
// If the hint that generated this overlap contains only range keys,
|
||||
// there is no need to calculate disk usage, as the reclaimable space
|
||||
// is expected to be minimal relative to point keys.
|
||||
continue
|
||||
}
|
||||
var size uint64
|
||||
err := d.tableCache.withReader(file, func(r *sstable.Reader) (err error) {
|
||||
size, err = r.EstimateDiskUsage(start, end)
|
||||
@@ -452,60 +515,19 @@ func (d *DB) estimateSizeBeneath(
|
||||
return estimate, hintSeqNum, nil
|
||||
}
|
||||
|
||||
func foreachDefragmentedTombstone(
|
||||
rangeDelIter keyspan.FragmentIterator,
|
||||
cmp base.Compare,
|
||||
fn func([]byte, []byte, uint64, uint64) error,
|
||||
) error {
|
||||
// Use an equals func that will always merge abutting spans.
|
||||
equal := func(_ base.Compare, _, _ keyspan.Span) bool { return true }
|
||||
// Reduce keys by maintaining a slice of length two, corresponding to the
|
||||
// largest and smallest keys in the defragmented span. This maintains the
|
||||
// contract that the emitted slice is sorted by (SeqNum, Kind) descending.
|
||||
reducer := func(current, incoming []keyspan.Key) []keyspan.Key {
|
||||
if len(current) == 0 && len(incoming) == 0 {
|
||||
// While this should never occur in practice, a defensive return is used
|
||||
// here to preserve correctness.
|
||||
return current
|
||||
}
|
||||
// Determine the smallest / largest keys across the two slices. As `current`
|
||||
// and `incoming` are both sorted by (seqnum, kind) descending, only the
|
||||
// first and last element of each slice need to be consulted.
|
||||
var largest, smallest keyspan.Key
|
||||
var largestSet, smallestSet bool
|
||||
for _, keys := range [2][]keyspan.Key{current, incoming} {
|
||||
if len(keys) == 0 {
|
||||
continue
|
||||
}
|
||||
first, last := keys[0], keys[len(keys)-1]
|
||||
// Update largest.
|
||||
if !largestSet || first.Trailer > largest.Trailer {
|
||||
largest, largestSet = first, true
|
||||
}
|
||||
// Update smallest.
|
||||
if !smallestSet || last.Trailer < smallest.Trailer {
|
||||
smallest, smallestSet = last, true
|
||||
}
|
||||
}
|
||||
return append(current[:0], largest, smallest)
|
||||
}
|
||||
iter := keyspan.DefragmentingIter{}
|
||||
iter.Init(cmp, rangeDelIter, equal, reducer)
|
||||
for s := iter.First(); s.Valid(); s = iter.Next() {
|
||||
if err := fn(s.Start, s.End, s.SmallestSeqNum(), s.LargestSeqNum()); err != nil {
|
||||
_ = iter.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
return iter.Close()
|
||||
}
|
||||
|
||||
func maybeSetStatsFromProperties(meta *fileMetadata, props *sstable.Properties) bool {
|
||||
// If a table has range deletions, we can't calculate the
|
||||
// RangeDeletionsBytesEstimate statistic and can't populate table stats
|
||||
// from just the properties. The table stats collector goroutine will
|
||||
// populate the stats.
|
||||
if props.NumRangeDeletions != 0 {
|
||||
// If a table contains range deletions or range key deletions, we defer the
|
||||
// stats collection. There are two main reasons for this:
|
||||
//
|
||||
// 1. Estimating the potential for reclaimed space due to a range deletion
|
||||
// tombstone requires scanning the LSM - a potentially expensive operation
|
||||
// that should be deferred.
|
||||
// 2. Range deletions and / or range key deletions present an opportunity to
|
||||
// compute "deletion hints", which also requires a scan of the LSM to
|
||||
// compute tables that would be eligible for deletion.
|
||||
//
|
||||
// These two tasks are deferred to the table stats collector goroutine.
|
||||
if props.NumRangeDeletions != 0 || props.NumRangeKeyDels != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -528,14 +550,12 @@ func maybeSetStatsFromProperties(meta *fileMetadata, props *sstable.Properties)
|
||||
pointEstimate = pointDeletionsBytesEstimate(props, avgKeySize, avgValSize)
|
||||
}
|
||||
|
||||
meta.Stats = manifest.TableStats{
|
||||
Valid: true,
|
||||
NumEntries: props.NumEntries,
|
||||
NumDeletions: props.NumDeletions,
|
||||
NumRangeKeys: props.NumRangeKeys(),
|
||||
PointDeletionsBytesEstimate: pointEstimate,
|
||||
RangeDeletionsBytesEstimate: 0,
|
||||
}
|
||||
meta.Stats.NumEntries = props.NumEntries
|
||||
meta.Stats.NumDeletions = props.NumDeletions
|
||||
meta.Stats.NumRangeKeySets = props.NumRangeKeySets
|
||||
meta.Stats.PointDeletionsBytesEstimate = pointEstimate
|
||||
meta.Stats.RangeDeletionsBytesEstimate = 0
|
||||
meta.StatsMarkValid()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -581,3 +601,208 @@ func estimateEntrySizes(
|
||||
avgValSize = props.RawValueSize * fileSizePerEntry / uncompressedSum
|
||||
return avgKeySize, avgValSize
|
||||
}
|
||||
|
||||
// newCombinedDeletionKeyspanIter returns a keyspan.FragmentIterator that
|
||||
// returns "ranged deletion" spans for a single table, providing a combined view
|
||||
// of both range deletion and range key deletion spans. The
|
||||
// tableRangedDeletionIter is intended for use in the specific case of computing
|
||||
// the statistics and deleteCompactionHints for a single table.
|
||||
//
|
||||
// As an example, consider the following set of spans from the range deletion
|
||||
// and range key blocks of a table:
|
||||
//
|
||||
// |---------| |---------| |-------| RANGEKEYDELs
|
||||
// |-----------|-------------| |-----| RANGEDELs
|
||||
// __________________________________________________________
|
||||
// a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
//
|
||||
// The tableRangedDeletionIter produces the following set of output spans, where
|
||||
// '1' indicates a span containing only range deletions, '2' is a span
|
||||
// containing only range key deletions, and '3' is a span containing a mixture
|
||||
// of both range deletions and range key deletions.
|
||||
//
|
||||
// 1 3 1 3 2 1 3 2
|
||||
// |-----|---------|-----|---|-----| |---|-|-----|
|
||||
// __________________________________________________________
|
||||
// a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
//
|
||||
// Algorithm.
|
||||
//
|
||||
// The iterator first defragments the range deletion and range key blocks
|
||||
// separately. During this defragmentation, the range key block is also filtered
|
||||
// so that keys other than range key deletes are ignored. The range delete and
|
||||
// range key delete keyspaces are then merged.
|
||||
//
|
||||
// Note that the only fragmentation introduced by merging is from where a range
|
||||
// del span overlaps with a range key del span. Within the bounds of any overlap
|
||||
// there is guaranteed to be no further fragmentation, as the constituent spans
|
||||
// have already been defragmented. To the left and right of any overlap, the
|
||||
// same reasoning applies. For example,
|
||||
//
|
||||
// |--------| |-------| RANGEKEYDEL
|
||||
// |---------------------------| RANGEDEL
|
||||
// |----1---|----3---|----1----|---2---| Merged, fragmented spans.
|
||||
// __________________________________________________________
|
||||
// a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
//
|
||||
// Any fragmented abutting spans produced by the merging iter will be of
|
||||
// differing types (i.e. a transition from a span with homogenous key kinds to a
|
||||
// heterogeneous span, or a transition from a span with exclusively range dels
|
||||
// to a span with exclusively range key dels). Therefore, further
|
||||
// defragmentation is not required.
|
||||
//
|
||||
// Each span returned by the tableRangeDeletionIter will have at most four keys,
|
||||
// corresponding to the largest and smallest sequence numbers encountered across
|
||||
// the range deletes and range keys deletes that comprised the merged spans.
|
||||
func newCombinedDeletionKeyspanIter(
|
||||
comparer *base.Comparer, r *sstable.Reader, m *fileMetadata,
|
||||
) (keyspan.FragmentIterator, error) {
|
||||
// The range del iter and range key iter are each wrapped in their own
|
||||
// defragmenting iter. For each iter, abutting spans can always be merged.
|
||||
var equal = keyspan.DefragmentMethodFunc(func(_ base.Equal, a, b *keyspan.Span) bool { return true })
|
||||
// Reduce keys by maintaining a slice of at most length two, corresponding to
|
||||
// the largest and smallest keys in the defragmented span. This maintains the
|
||||
// contract that the emitted slice is sorted by (SeqNum, Kind) descending.
|
||||
reducer := func(current, incoming []keyspan.Key) []keyspan.Key {
|
||||
if len(current) == 0 && len(incoming) == 0 {
|
||||
// While this should never occur in practice, a defensive return is used
|
||||
// here to preserve correctness.
|
||||
return current
|
||||
}
|
||||
var largest, smallest keyspan.Key
|
||||
var set bool
|
||||
for _, keys := range [2][]keyspan.Key{current, incoming} {
|
||||
if len(keys) == 0 {
|
||||
continue
|
||||
}
|
||||
first, last := keys[0], keys[len(keys)-1]
|
||||
if !set {
|
||||
largest, smallest = first, last
|
||||
set = true
|
||||
continue
|
||||
}
|
||||
if first.Trailer > largest.Trailer {
|
||||
largest = first
|
||||
}
|
||||
if last.Trailer < smallest.Trailer {
|
||||
smallest = last
|
||||
}
|
||||
}
|
||||
if largest.Equal(comparer.Equal, smallest) {
|
||||
current = append(current[:0], largest)
|
||||
} else {
|
||||
current = append(current[:0], largest, smallest)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// The separate iters for the range dels and range keys are wrapped in a
|
||||
// merging iter to join the keyspaces into a single keyspace. The separate
|
||||
// iters are only added if the particular key kind is present.
|
||||
mIter := &keyspan.MergingIter{}
|
||||
var transform = keyspan.TransformerFunc(func(cmp base.Compare, in keyspan.Span, out *keyspan.Span) error {
|
||||
if in.KeysOrder != keyspan.ByTrailerDesc {
|
||||
panic("pebble: combined deletion iter encountered keys in non-trailer descending order")
|
||||
}
|
||||
out.Start, out.End = in.Start, in.End
|
||||
out.Keys = append(out.Keys[:0], in.Keys...)
|
||||
out.KeysOrder = keyspan.ByTrailerDesc
|
||||
// NB: The order of by-trailer descending may have been violated,
|
||||
// because we've layered rangekey and rangedel iterators from the same
|
||||
// sstable into the same keyspan.MergingIter. The MergingIter will
|
||||
// return the keys in the order that the child iterators were provided.
|
||||
// Sort the keys to ensure they're sorted by trailer descending.
|
||||
keyspan.SortKeysByTrailer(&out.Keys)
|
||||
return nil
|
||||
})
|
||||
mIter.Init(comparer.Compare, transform)
|
||||
|
||||
iter, err := r.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iter != nil {
|
||||
dIter := &keyspan.DefragmentingIter{}
|
||||
dIter.Init(comparer, iter, equal, reducer)
|
||||
iter = dIter
|
||||
// Truncate tombstones to the containing file's bounds if necessary.
|
||||
// See docs/range_deletions.md for why this is necessary.
|
||||
iter = keyspan.Truncate(
|
||||
comparer.Compare, iter, m.Smallest.UserKey, m.Largest.UserKey, nil, nil,
|
||||
)
|
||||
mIter.AddLevel(iter)
|
||||
}
|
||||
|
||||
iter, err = r.NewRawRangeKeyIter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if iter != nil {
|
||||
// Wrap the range key iterator in a filter that elides keys other than range
|
||||
// key deletions.
|
||||
iter = keyspan.Filter(iter, func(in *keyspan.Span, out *keyspan.Span) (keep bool) {
|
||||
out.Start, out.End = in.Start, in.End
|
||||
out.Keys = out.Keys[:0]
|
||||
for _, k := range in.Keys {
|
||||
if k.Kind() != base.InternalKeyKindRangeKeyDelete {
|
||||
continue
|
||||
}
|
||||
out.Keys = append(out.Keys, k)
|
||||
}
|
||||
return len(out.Keys) > 0
|
||||
})
|
||||
dIter := &keyspan.DefragmentingIter{}
|
||||
dIter.Init(comparer, iter, equal, reducer)
|
||||
iter = dIter
|
||||
mIter.AddLevel(iter)
|
||||
}
|
||||
|
||||
return mIter, nil
|
||||
}
|
||||
|
||||
// rangeKeySetsAnnotator implements manifest.Annotator, annotating B-Tree nodes
|
||||
// with the sum of the files' counts of range key fragments. Its annotation type
|
||||
// is a *uint64. The count of range key sets may change once a table's stats are
|
||||
// loaded asynchronously, so its values are marked as cacheable only if a file's
|
||||
// stats have been loaded.
|
||||
type rangeKeySetsAnnotator struct{}
|
||||
|
||||
var _ manifest.Annotator = rangeKeySetsAnnotator{}
|
||||
|
||||
func (a rangeKeySetsAnnotator) Zero(dst interface{}) interface{} {
|
||||
if dst == nil {
|
||||
return new(uint64)
|
||||
}
|
||||
v := dst.(*uint64)
|
||||
*v = 0
|
||||
return v
|
||||
}
|
||||
|
||||
func (a rangeKeySetsAnnotator) Accumulate(
|
||||
f *fileMetadata, dst interface{},
|
||||
) (v interface{}, cacheOK bool) {
|
||||
vptr := dst.(*uint64)
|
||||
*vptr = *vptr + f.Stats.NumRangeKeySets
|
||||
return vptr, f.StatsValidLocked()
|
||||
}
|
||||
|
||||
func (a rangeKeySetsAnnotator) Merge(src interface{}, dst interface{}) interface{} {
|
||||
srcV := src.(*uint64)
|
||||
dstV := dst.(*uint64)
|
||||
*dstV = *dstV + *srcV
|
||||
return dstV
|
||||
}
|
||||
|
||||
// countRangeKeySetFragments counts the number of RANGEKEYSET keys across all
|
||||
// files of the LSM. It only counts keys in files for which table stats have
|
||||
// been loaded. It uses a b-tree annotator to cache intermediate values between
|
||||
// calculations when possible.
|
||||
func countRangeKeySetFragments(v *version) (count uint64) {
|
||||
for l := 0; l < numLevels; l++ {
|
||||
if v.RangeKeyLevels[l].Empty() {
|
||||
continue
|
||||
}
|
||||
count += *v.RangeKeyLevels[l].Annotation(rangeKeySetsAnnotator{}).(*uint64)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
107
vendor/github.com/cockroachdb/pebble/version_set.go
generated
vendored
107
vendor/github.com/cockroachdb/pebble/version_set.go
generated
vendored
@@ -110,6 +110,9 @@ type versionSet struct {
|
||||
|
||||
writing bool
|
||||
writerCond sync.Cond
|
||||
// State for deciding when to write a snapshot. Protected by mu.
|
||||
lastSnapshotFileCount int64
|
||||
editsSinceLastSnapshotFileCount int64
|
||||
}
|
||||
|
||||
func (vs *versionSet) init(
|
||||
@@ -391,13 +394,76 @@ func (vs *versionSet) logAndApply(
|
||||
currentVersion := vs.currentVersion()
|
||||
var newVersion *version
|
||||
|
||||
// Generate a new manifest if we don't currently have one, or the current one
|
||||
// is too large.
|
||||
// Generate a new manifest if we don't currently have one, or forceRotation
|
||||
// is true, or the current one is too large.
|
||||
//
|
||||
// For largeness, we do not exclusively use MaxManifestFileSize size
|
||||
// threshold since we have had incidents where due to either large keys or
|
||||
// large numbers of files, each edit results in a snapshot + write of the
|
||||
// edit. This slows the system down since each flush or compaction is
|
||||
// writing a new manifest snapshot. The primary goal of the size-based
|
||||
// rollover logic is to ensure that when reopening a DB, the number of edits
|
||||
// that need to be replayed on top of the snapshot is "sane". Rolling over
|
||||
// to a new manifest after each edit is not relevant to that goal.
|
||||
//
|
||||
// Consider the following cases:
|
||||
// - The number of live files F in the DB is roughly stable: after writing
|
||||
// the snapshot (with F files), say we require that there be enough edits
|
||||
// such that the cumulative number of files in those edits, E, be greater
|
||||
// than F. This will ensure that the total amount of time in logAndApply
|
||||
// that is spent in snapshot writing is ~50%.
|
||||
//
|
||||
// - The number of live files F in the DB is shrinking drastically, say from
|
||||
// F to F/10: This can happen for various reasons, like wide range
|
||||
// tombstones, or large numbers of smaller than usual files that are being
|
||||
// merged together into larger files. And say the new files generated
|
||||
// during this shrinkage is insignificant compared to F/10, and so for
|
||||
// this example we will assume it is effectively 0. After this shrinking,
|
||||
// E = 0.9F, and so if we used the previous snapshot file count, F, as the
|
||||
// threshold that needs to be exceeded, we will further delay the snapshot
|
||||
// writing. Which means on DB reopen we will need to replay 0.9F edits to
|
||||
// get to a version with 0.1F files. It would be better to create a new
|
||||
// snapshot when E exceeds the number of files in the current version.
|
||||
//
|
||||
// - The number of live files F in the DB is growing via perfect ingests
|
||||
// into L6: Say we wrote the snapshot when there were F files and now we
|
||||
// have 10F files, so E = 9F. We will further delay writing a new
|
||||
// snapshot. This case can be critiqued as contrived, but we consider it
|
||||
// nonetheless.
|
||||
//
|
||||
// The logic below uses the min of the last snapshot file count and the file
|
||||
// count in the current version.
|
||||
editCount := int64(len(ve.DeletedFiles) + len(ve.NewFiles))
|
||||
vs.editsSinceLastSnapshotFileCount += editCount
|
||||
sizeExceeded := vs.manifest.Size() >= vs.opts.MaxManifestFileSize
|
||||
requireRotation := forceRotation || vs.manifest == nil
|
||||
computeNextSnapshotFileCount := func() int64 {
|
||||
var count int64
|
||||
for i := range vs.metrics.Levels {
|
||||
count += vs.metrics.Levels[i].NumFiles
|
||||
}
|
||||
return count
|
||||
}
|
||||
var nextSnapshotFileCount int64
|
||||
if sizeExceeded && !requireRotation {
|
||||
if vs.editsSinceLastSnapshotFileCount > vs.lastSnapshotFileCount {
|
||||
requireRotation = true
|
||||
} else {
|
||||
nextSnapshotFileCount = computeNextSnapshotFileCount()
|
||||
if vs.editsSinceLastSnapshotFileCount > nextSnapshotFileCount {
|
||||
requireRotation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var newManifestFileNum FileNum
|
||||
var prevManifestFileSize uint64
|
||||
if forceRotation || vs.manifest == nil || vs.manifest.Size() >= vs.opts.MaxManifestFileSize {
|
||||
if requireRotation {
|
||||
newManifestFileNum = vs.getNextFileNum()
|
||||
prevManifestFileSize = uint64(vs.manifest.Size())
|
||||
if nextSnapshotFileCount == 0 {
|
||||
// Haven't computed it, or happens to be 0.
|
||||
nextSnapshotFileCount = computeNextSnapshotFileCount()
|
||||
}
|
||||
}
|
||||
|
||||
// Grab certain values before releasing vs.mu, in case createManifest() needs
|
||||
@@ -418,7 +484,7 @@ func (vs *versionSet) logAndApply(
|
||||
var err error
|
||||
newVersion, zombies, err = bve.Apply(currentVersion, vs.cmp, vs.opts.Comparer.FormatKey, vs.opts.FlushSplitBytes, vs.opts.Experimental.ReadCompactionRate)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST apply failed")
|
||||
}
|
||||
|
||||
if newManifestFileNum != 0 {
|
||||
@@ -429,13 +495,13 @@ func (vs *versionSet) logAndApply(
|
||||
FileNum: newManifestFileNum,
|
||||
Err: err,
|
||||
})
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST create failed")
|
||||
}
|
||||
}
|
||||
|
||||
w, err := vs.manifest.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST next record write failed")
|
||||
}
|
||||
// NB: Any error from this point on is considered fatal as we don't now if
|
||||
// the MANIFEST write occurred or not. Trying to determine that is
|
||||
@@ -443,22 +509,18 @@ func (vs *versionSet) logAndApply(
|
||||
// database is open. In particular, that mechanism generates a new MANIFEST
|
||||
// and ensures it is synced.
|
||||
if err := ve.Encode(w); err != nil {
|
||||
vs.opts.Logger.Fatalf("MANIFEST write failed: %v", err)
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST write failed")
|
||||
}
|
||||
if err := vs.manifest.Flush(); err != nil {
|
||||
vs.opts.Logger.Fatalf("MANIFEST flush failed: %v", err)
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST flush failed")
|
||||
}
|
||||
if err := vs.manifestFile.Sync(); err != nil {
|
||||
vs.opts.Logger.Fatalf("MANIFEST sync failed: %v", err)
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST sync failed")
|
||||
}
|
||||
if newManifestFileNum != 0 {
|
||||
// NB: setCurrent is responsible for syncing the data directory.
|
||||
if err := vs.setCurrent(newManifestFileNum); err != nil {
|
||||
vs.opts.Logger.Fatalf("MANIFEST set current failed: %v", err)
|
||||
return err
|
||||
return errors.Wrap(err, "MANIFEST set current failed")
|
||||
}
|
||||
vs.opts.EventListener.ManifestCreated(ManifestCreateInfo{
|
||||
JobID: jobID,
|
||||
@@ -468,9 +530,21 @@ func (vs *versionSet) logAndApply(
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
// Any error encountered during any of the operations in the previous
|
||||
// closure are considered fatal. Treating such errors as fatal is preferred
|
||||
// to attempting to unwind various file and b-tree reference counts, and
|
||||
// re-generating L0 sublevel metadata. This may change in the future, if
|
||||
// certain manifest / WAL operations become retryable. For more context, see
|
||||
// #1159 and #1792.
|
||||
vs.opts.Logger.Fatalf("%s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if requireRotation {
|
||||
// Successfully rotated.
|
||||
vs.lastSnapshotFileCount = nextSnapshotFileCount
|
||||
vs.editsSinceLastSnapshotFileCount = editCount
|
||||
}
|
||||
// Now that DB.mu is held again, initialize compacting file info in
|
||||
// L0Sublevels.
|
||||
inProgress := inProgressCompactions()
|
||||
@@ -527,7 +601,7 @@ func (vs *versionSet) logAndApply(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vs *versionSet) incrementCompactions(kind compactionKind) {
|
||||
func (vs *versionSet) incrementCompactions(kind compactionKind, extraLevels []*compactionLevel) {
|
||||
switch kind {
|
||||
case compactionKindDefault:
|
||||
vs.metrics.Compact.Count++
|
||||
@@ -556,6 +630,9 @@ func (vs *versionSet) incrementCompactions(kind compactionKind) {
|
||||
vs.metrics.Compact.Count++
|
||||
vs.metrics.Compact.RewriteCount++
|
||||
}
|
||||
if len(extraLevels) > 0 {
|
||||
vs.metrics.Compact.MultiLevelCount++
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *versionSet) incrementCompactionBytes(numBytes int64) {
|
||||
|
||||
4
vendor/github.com/cockroachdb/pebble/vfs/clone.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/vfs/clone.go
generated
vendored
@@ -5,7 +5,7 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/cockroachdb/errors/oserror"
|
||||
@@ -94,7 +94,7 @@ func Clone(srcFS, dstFS FS, srcPath, dstPath string, opts ...CloneOption) (bool,
|
||||
return true, nil
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(srcFile)
|
||||
data, err := io.ReadAll(srcFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/vfs/disk_usage_linux.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/vfs/disk_usage_linux.go
generated
vendored
@@ -35,7 +35,7 @@ func (defaultFS) GetDiskUsage(path string) (DiskUsage, error) {
|
||||
// [2] https://man7.org/linux/man-pages/man3/statvfs.3.html
|
||||
freeBytes := uint64(stat.Frsize) * uint64(stat.Bfree)
|
||||
availBytes := uint64(stat.Frsize) * uint64(stat.Bavail)
|
||||
totalBytes := uint64(stat.Bsize) * uint64(stat.Blocks)
|
||||
totalBytes := uint64(stat.Frsize) * uint64(stat.Blocks)
|
||||
return DiskUsage{
|
||||
AvailBytes: availBytes,
|
||||
TotalBytes: totalBytes,
|
||||
|
||||
26
vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go
generated
vendored
26
vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go
generated
vendored
@@ -78,10 +78,23 @@ type MemFS struct {
|
||||
|
||||
strict bool
|
||||
ignoreSyncs bool
|
||||
// Windows has peculiar semantics with respect to hard links and deleting
|
||||
// open files. In tests meant to exercise this behavior, this flag can be
|
||||
// set to error if removing an open file.
|
||||
windowsSemantics bool
|
||||
}
|
||||
|
||||
var _ FS = &MemFS{}
|
||||
|
||||
// UseWindowsSemantics configures whether the MemFS implements Windows-style
|
||||
// semantics, in particular with respect to whether any of an open file's links
|
||||
// may be removed. Windows semantics default to off.
|
||||
func (y *MemFS) UseWindowsSemantics(windowsSemantics bool) {
|
||||
y.mu.Lock()
|
||||
defer y.mu.Unlock()
|
||||
y.windowsSemantics = windowsSemantics
|
||||
}
|
||||
|
||||
// String dumps the contents of the MemFS.
|
||||
func (y *MemFS) String() string {
|
||||
y.mu.Lock()
|
||||
@@ -303,11 +316,14 @@ func (y *MemFS) Remove(fullname string) error {
|
||||
if !ok {
|
||||
return oserror.ErrNotExist
|
||||
}
|
||||
// Disallow removal of open files/directories which implements Windows
|
||||
// semantics. This ensures that we don't regress in the ordering of
|
||||
// operations and try to remove a file while it is still open.
|
||||
if n := atomic.LoadInt32(&child.refs); n > 0 {
|
||||
return oserror.ErrInvalid
|
||||
if y.windowsSemantics {
|
||||
// Disallow removal of open files/directories which implements
|
||||
// Windows semantics. This ensures that we don't regress in the
|
||||
// ordering of operations and try to remove a file while it is
|
||||
// still open.
|
||||
if n := atomic.LoadInt32(&child.refs); n > 0 {
|
||||
return oserror.ErrInvalid
|
||||
}
|
||||
}
|
||||
if len(child.children) > 0 {
|
||||
return errNotEmpty
|
||||
|
||||
18
vendor/github.com/cockroachdb/pebble/vfs/syncing_file.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/vfs/syncing_file.go
generated
vendored
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
// SyncingFileOptions holds the options for a syncingFile.
|
||||
type SyncingFileOptions struct {
|
||||
NoSyncOnClose bool
|
||||
BytesPerSync int
|
||||
PreallocateSize int
|
||||
}
|
||||
@@ -20,6 +21,8 @@ type syncingFile struct {
|
||||
File
|
||||
fd uintptr
|
||||
useSyncRange bool
|
||||
closing bool
|
||||
noSyncOnClose bool
|
||||
bytesPerSync int64
|
||||
preallocateSize int64
|
||||
atomic struct {
|
||||
@@ -46,6 +49,7 @@ type syncingFile struct {
|
||||
func NewSyncingFile(f File, opts SyncingFileOptions) File {
|
||||
s := &syncingFile{
|
||||
File: f,
|
||||
noSyncOnClose: bool(opts.NoSyncOnClose),
|
||||
bytesPerSync: int64(opts.BytesPerSync),
|
||||
preallocateSize: int64(opts.PreallocateSize),
|
||||
}
|
||||
@@ -172,12 +176,16 @@ func (f *syncingFile) maybeSync() error {
|
||||
}
|
||||
|
||||
func (f *syncingFile) Close() error {
|
||||
// Sync any data that has been written but not yet synced. Note that if
|
||||
// SyncFileRange was used, atomic.syncOffset will be less than
|
||||
// Sync any data that has been written but not yet synced unless the file
|
||||
// has noSyncOnClose option explicitly set.
|
||||
// Note that if SyncFileRange was used, atomic.syncOffset will be less than
|
||||
// atomic.offset. See syncingFile.syncToRange.
|
||||
if atomic.LoadInt64(&f.atomic.offset) > atomic.LoadInt64(&f.atomic.syncOffset) {
|
||||
if err := f.Sync(); err != nil {
|
||||
return errors.WithStack(err)
|
||||
f.closing = true
|
||||
if !f.noSyncOnClose || f.useSyncRange {
|
||||
if atomic.LoadInt64(&f.atomic.offset) > atomic.LoadInt64(&f.atomic.syncOffset) {
|
||||
if err := f.Sync(); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.WithStack(f.File.Close())
|
||||
|
||||
10
vendor/github.com/cockroachdb/pebble/vfs/syncing_file_linux.go
generated
vendored
10
vendor/github.com/cockroachdb/pebble/vfs/syncing_file_linux.go
generated
vendored
@@ -76,6 +76,14 @@ func (f *syncingFile) syncToRange(offset int64) error {
|
||||
// waitAfter = 0x4
|
||||
)
|
||||
|
||||
// The flags for the sync_file_range system call. Unless the file has
|
||||
// noSyncOnClose explicitly set and it is being closed, the waitBefore
|
||||
// flag will be set which may block the call.
|
||||
flags := write
|
||||
if !f.noSyncOnClose || !f.closing {
|
||||
flags |= waitBefore
|
||||
}
|
||||
|
||||
// Note that syncToRange is only called with an offset that is guaranteed to
|
||||
// be less than atomic.offset (i.e. the write offset). This implies the
|
||||
// syncingFile.Close will Sync the rest of the data, as well as the file's
|
||||
@@ -91,7 +99,7 @@ func (f *syncingFile) syncToRange(offset int64) error {
|
||||
// data accumulates, impacting other I/O operations.
|
||||
var err error
|
||||
f.timeDiskOp(func() {
|
||||
err = syscall.SyncFileRange(int(f.fd), 0, offset, write|waitBefore)
|
||||
err = syscall.SyncFileRange(int(f.fd), 0, offset, flags)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user