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
|
||||
|
||||
12
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
12
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
@@ -21,7 +21,7 @@ all:
|
||||
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
|
||||
@@ -46,14 +46,17 @@ crossversion-meta:
|
||||
${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:
|
||||
@@ -80,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
|
||||
|
||||
@@ -90,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
|
||||
|
||||
186
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
186
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
@@ -855,16 +855,37 @@ func (b *Batch) NewIter(o *IterOptions) *Iterator {
|
||||
// contents of the batch.
|
||||
func (b *Batch) newInternalIter(o *IterOptions) *batchIter {
|
||||
iter := &batchIter{}
|
||||
b.initInternalIter(o, iter, b.nextSeqNum())
|
||||
b.initInternalIter(o, iter)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (b *Batch) initInternalIter(o *IterOptions, iter *batchIter, batchSnapshot uint64) {
|
||||
func (b *Batch) initInternalIter(o *IterOptions, iter *batchIter) {
|
||||
*iter = batchIter{
|
||||
cmp: b.cmp,
|
||||
batch: b,
|
||||
iter: b.index.NewIter(o.GetLowerBound(), o.GetUpperBound()),
|
||||
snapshot: batchSnapshot,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,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.
|
||||
@@ -1009,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
|
||||
}
|
||||
@@ -1244,90 +1265,89 @@ func (i *batchIter) String() string {
|
||||
return "batch"
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []byte) {
|
||||
// Ignore trySeekUsingNext since the batch may have changed, so using Next
|
||||
// would be incorrect.
|
||||
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()
|
||||
}
|
||||
|
||||
i.err = nil // clear cached iteration error
|
||||
ikey := i.iter.SeekGE(key)
|
||||
ikey := i.iter.SeekGE(key, flags)
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.err = nil // clear cached iteration error
|
||||
return i.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
func (i *batchIter) SeekLT(key []byte, flags base.SeekLTFlags) (*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)
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
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()
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
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()
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Next() (*InternalKey, []byte) {
|
||||
func (i *batchIter) Next() (*InternalKey, base.LazyValue) {
|
||||
ikey := i.iter.Next()
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Next()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
return ikey, base.MakeInPlaceValue(i.value())
|
||||
}
|
||||
|
||||
func (i *batchIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *batchIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
ikey := i.iter.Prev()
|
||||
for ikey != nil && ikey.SeqNum() >= i.snapshot {
|
||||
ikey = i.iter.Prev()
|
||||
}
|
||||
if ikey == nil {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return ikey, i.Value()
|
||||
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 {
|
||||
@@ -1348,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
|
||||
}
|
||||
@@ -1642,34 +1658,38 @@ func (i *flushableBatchIter) String() string {
|
||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||
// 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, []byte) {
|
||||
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, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*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, flags base.SeekLTFlags) (*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 {
|
||||
@@ -1677,80 +1697,80 @@ func (i *flushableBatchIter) SeekLT(key []byte, flags base.SeekLTFlags) (*Intern
|
||||
})
|
||||
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 {
|
||||
@@ -1760,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
|
||||
@@ -1784,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 {
|
||||
@@ -1823,27 +1839,27 @@ func (i *flushFlushableBatchIter) String() string {
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, []byte) {
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (i *flushFlushableBatchIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, []byte) {
|
||||
) (*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()
|
||||
@@ -1852,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")
|
||||
}
|
||||
|
||||
|
||||
249
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
249
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
@@ -47,7 +47,7 @@ func expandedCompactionByteSizeLimit(opts *Options, level int, availBytes uint64
|
||||
// compactions to half of available disk space. Note that this will not
|
||||
// prevent compaction picking from pursuing compactions that are larger
|
||||
// than this threshold before expansion.
|
||||
diskMax := (availBytes / 2) / uint64(opts.MaxConcurrentCompactions)
|
||||
diskMax := (availBytes / 2) / uint64(opts.MaxConcurrentCompactions())
|
||||
if v > diskMax {
|
||||
v = diskMax
|
||||
}
|
||||
@@ -342,7 +342,7 @@ func rangeKeyCompactionTransform(
|
||||
usedLen := 0
|
||||
for i >= 0 {
|
||||
start := j
|
||||
for j < len(s.Keys) && !base.Visible(s.Keys[j].SeqNum(), snapshots[i]) {
|
||||
for j < len(s.Keys) && !base.Visible(s.Keys[j].SeqNum(), snapshots[i], base.InternalKeySeqNumMax) {
|
||||
// Include j in current partition.
|
||||
j++
|
||||
}
|
||||
@@ -379,9 +379,11 @@ type compaction struct {
|
||||
kind compactionKind
|
||||
cmp Compare
|
||||
equal Equal
|
||||
comparer *base.Comparer
|
||||
formatKey base.FormatKey
|
||||
logger Logger
|
||||
version *version
|
||||
stats base.InternalIteratorStats
|
||||
|
||||
score float64
|
||||
|
||||
@@ -462,7 +464,11 @@ type compaction struct {
|
||||
// grandparent and lower levels. See setupInuseKeyRanges() for the
|
||||
// construction. Used by elideTombstone() and elideRangeTombstone() to
|
||||
// determine if keys affected by a tombstone possibly exist at a lower level.
|
||||
inuseKeyRanges []manifest.UserKeyRange
|
||||
inuseKeyRanges []manifest.UserKeyRange
|
||||
// inuseEntireRange is set if the above inuse key ranges wholly contain the
|
||||
// compaction's key range. This allows compactions in higher levels to often
|
||||
// elide key comparisons.
|
||||
inuseEntireRange bool
|
||||
elideTombstoneIndex int
|
||||
|
||||
// allowedZeroSeqNum is true if seqnums can be zeroed if there are no
|
||||
@@ -511,6 +517,7 @@ func newCompaction(pc *pickedCompaction, opts *Options) *compaction {
|
||||
kind: compactionKindDefault,
|
||||
cmp: pc.cmp,
|
||||
equal: opts.equal(),
|
||||
comparer: opts.Comparer,
|
||||
formatKey: opts.Comparer.FormatKey,
|
||||
score: pc.score,
|
||||
inputs: pc.inputs,
|
||||
@@ -554,6 +561,7 @@ func newDeleteOnlyCompaction(opts *Options, cur *version, inputs []compactionLev
|
||||
kind: compactionKindDeleteOnly,
|
||||
cmp: opts.Comparer.Compare,
|
||||
equal: opts.equal(),
|
||||
comparer: opts.Comparer,
|
||||
formatKey: opts.Comparer.FormatKey,
|
||||
logger: opts.Logger,
|
||||
version: cur,
|
||||
@@ -641,6 +649,7 @@ func newFlush(opts *Options, cur *version, baseLevel int, flushing flushableList
|
||||
kind: compactionKindFlush,
|
||||
cmp: opts.Comparer.Compare,
|
||||
equal: opts.equal(),
|
||||
comparer: opts.Comparer,
|
||||
formatKey: opts.Comparer.FormatKey,
|
||||
logger: opts.Logger,
|
||||
version: cur,
|
||||
@@ -736,9 +745,19 @@ func (c *compaction) setupInuseKeyRanges() {
|
||||
if c.outputLevel.level == 0 {
|
||||
level = 0
|
||||
}
|
||||
// calculateInuseKeyRanges will return a series of sorted spans. Overlapping
|
||||
// or abutting spans have already been merged.
|
||||
c.inuseKeyRanges = calculateInuseKeyRanges(
|
||||
c.version, c.cmp, level, numLevels-1, c.smallest.UserKey, c.largest.UserKey,
|
||||
)
|
||||
// Check if there's a single in-use span that encompasses the entire key
|
||||
// range of the compaction. This is an optimization to avoid key comparisons
|
||||
// against inuseKeyRanges during the compaction when every key within the
|
||||
// compaction overlaps with an in-use span.
|
||||
if len(c.inuseKeyRanges) > 0 {
|
||||
c.inuseEntireRange = c.cmp(c.inuseKeyRanges[0].Start, c.smallest.UserKey) <= 0 &&
|
||||
c.cmp(c.inuseKeyRanges[0].End, c.largest.UserKey) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
func calculateInuseKeyRanges(
|
||||
@@ -935,7 +954,7 @@ func (c *compaction) allowZeroSeqNum() bool {
|
||||
// key. The keys in multiple invocations to elideTombstone must be supplied in
|
||||
// order.
|
||||
func (c *compaction) elideTombstone(key []byte) bool {
|
||||
if len(c.flushing) != 0 {
|
||||
if c.inuseEntireRange || len(c.flushing) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1010,12 +1029,12 @@ func (c *compaction) newInputIter(
|
||||
iter := f.newFlushIter(nil, &c.bytesIterated)
|
||||
if rangeDelIter := f.newRangeDelIter(nil); rangeDelIter != nil {
|
||||
c.rangeDelIter.Init(c.cmp, rangeDelIter)
|
||||
iter = newMergingIter(c.logger, c.cmp, nil, iter, &c.rangeDelIter)
|
||||
iter = newMergingIter(c.logger, &c.stats, c.cmp, nil, iter, &c.rangeDelIter)
|
||||
}
|
||||
if rangeKeyIter := f.newRangeKeyIter(nil); rangeKeyIter != nil {
|
||||
mi := &keyspan.MergingIter{}
|
||||
mi.Init(c.cmp, rangeKeyCompactionTransform(snapshots, c.elideRangeKey), rangeKeyIter)
|
||||
c.rangeKeyInterleaving.Init(c.cmp, base.WrapIterWithStats(iter), mi, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
c.rangeKeyInterleaving.Init(c.comparer, iter, mi, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
iter = &c.rangeKeyInterleaving
|
||||
}
|
||||
return iter, nil
|
||||
@@ -1038,11 +1057,11 @@ func (c *compaction) newInputIter(
|
||||
c.rangeDelIter.Init(c.cmp, rangeDelIters...)
|
||||
iters = append(iters, &c.rangeDelIter)
|
||||
}
|
||||
var iter base.InternalIteratorWithStats = newMergingIter(c.logger, c.cmp, nil, iters...)
|
||||
var iter internalIterator = newMergingIter(c.logger, &c.stats, c.cmp, nil, iters...)
|
||||
if len(rangeKeyIters) > 0 {
|
||||
mi := &keyspan.MergingIter{}
|
||||
mi.Init(c.cmp, rangeKeyCompactionTransform(snapshots, c.elideRangeKey), rangeKeyIters...)
|
||||
c.rangeKeyInterleaving.Init(c.cmp, base.WrapIterWithStats(iter), mi, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
c.rangeKeyInterleaving.Init(c.comparer, iter, mi, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
iter = &c.rangeKeyInterleaving
|
||||
}
|
||||
return iter, nil
|
||||
@@ -1112,9 +1131,10 @@ func (c *compaction) newInputIter(
|
||||
// internal iterator interface). The resulting merged rangedel iterator is
|
||||
// then included with the point levels in a single mergingIter.
|
||||
newRangeDelIter := func(
|
||||
f *manifest.FileMetadata, slice manifest.LevelSlice, _ *IterOptions, bytesIterated *uint64,
|
||||
f manifest.LevelFile, _ *IterOptions, bytesIterated *uint64,
|
||||
) (keyspan.FragmentIterator, error) {
|
||||
iter, rangeDelIter, err := newIters(f, nil /* iter options */, &c.bytesIterated)
|
||||
iter, rangeDelIter, err := newIters(f.FileMetadata, nil, /* iter options */
|
||||
internalIterOpts{bytesIterated: &c.bytesIterated})
|
||||
if err == nil {
|
||||
// TODO(peter): It is mildly wasteful to open the point iterator only to
|
||||
// immediately close it. One way to solve this would be to add new
|
||||
@@ -1135,15 +1155,43 @@ func (c *compaction) newInputIter(
|
||||
rangeDelIter = noCloseIter{rangeDelIter}
|
||||
|
||||
// Truncate the range tombstones returned by the iterator to the
|
||||
// upper bound of the atomic compaction unit. Note that we need do
|
||||
// this truncation at read time in order to handle sstables
|
||||
// generated by RocksDB and earlier versions of Pebble which do not
|
||||
// truncate range tombstones to atomic compaction unit boundaries at
|
||||
// write time.
|
||||
// upper bound of the atomic compaction unit of the file. We want to
|
||||
// truncate the range tombstone to the bounds of the file, but files
|
||||
// with split user keys pose an obstacle: The file's largest bound
|
||||
// is inclusive whereas the range tombstone's end is exclusive.
|
||||
//
|
||||
// Consider the example:
|
||||
//
|
||||
// 000001:[b-f#200] range del [c,k)
|
||||
// 000002:[f#190-g#inf] range del [c,k)
|
||||
// 000003:[g#500-i#3]
|
||||
//
|
||||
// Files 000001 and 000002 contain the untruncated range tombstones
|
||||
// [c,k). While the keyspace covered by 000003 was at one point
|
||||
// deleted by the tombstone [c,k), the tombstone may have already
|
||||
// been compacted away and the file does not contain an untruncated
|
||||
// range tombstone. We want to bound 000001's tombstone to the file
|
||||
// bounds, but it's not possible to encode a range tombstone with an
|
||||
// end boundary within a user key (eg, between sequence numbers
|
||||
// f#200 and f#190). Instead, we expand 000001 to its atomic
|
||||
// compaction unit (000001 and 000002) and truncate the tombstone to
|
||||
// g#inf.
|
||||
//
|
||||
// NB: We must not use the atomic compaction unit of the entire
|
||||
// compaction, because the [c,k) tombstone contained in the file
|
||||
// 000001 ≥ g. If 000001, 000002 and 000003 are all included in the
|
||||
// same compaction, the compaction's atomic compaction unit includes
|
||||
// 000003. However 000003's keys must not be covered by 000001's
|
||||
// untruncated range tombstone.
|
||||
//
|
||||
// Note that we need do this truncation at read time in order to
|
||||
// handle sstables generated by RocksDB and earlier versions of
|
||||
// Pebble which do not truncate range tombstones to atomic
|
||||
// compaction unit boundaries at write time.
|
||||
//
|
||||
// The current Pebble compaction logic DOES truncate tombstones to
|
||||
// atomic unit boundaries at compaction time too.
|
||||
atomicUnit, _ := expandToAtomicUnit(c.cmp, slice, true /* disableIsCompacting */)
|
||||
atomicUnit, _ := expandToAtomicUnit(c.cmp, f.Slice(), true /* disableIsCompacting */)
|
||||
lowerBound, upperBound := manifest.KeyRange(c.cmp, atomicUnit.Iter())
|
||||
// Range deletion tombstones are often written to sstables
|
||||
// untruncated on the end key side. However, they are still only
|
||||
@@ -1166,20 +1214,54 @@ func (c *compaction) newInputIter(
|
||||
addItersForLevel := func(level *compactionLevel, l manifest.Level) error {
|
||||
iters = append(iters, newLevelIter(iterOpts, c.cmp, nil /* split */, newIters,
|
||||
level.files.Iter(), l, &c.bytesIterated))
|
||||
// Create a wrapping closure to turn newRangeDelIter into a
|
||||
// keyspan.TableNewSpanIter, and return a LevelIter that lazily creates
|
||||
// rangedel iterators. This is safe now that range deletions are truncated
|
||||
// at file bounds; the merging iterator no longer needs to see all range
|
||||
// deletes for correctness.
|
||||
wrapper := func(file *manifest.FileMetadata, iterOptions *keyspan.SpanIterOptions) (keyspan.FragmentIterator, error) {
|
||||
return newRangeDelIter(file, level.files, nil, &c.bytesIterated)
|
||||
// TODO(jackson): Use keyspan.LevelIter to avoid loading all the range
|
||||
// deletions into memory upfront. (See #2015, which reverted this.)
|
||||
// There will be no user keys that are split between sstables
|
||||
// within a level in Cockroach 23.1, which unblocks this optimization.
|
||||
|
||||
// Add the range deletion iterator for each file as an independent level
|
||||
// in mergingIter, as opposed to making a levelIter out of those. This
|
||||
// is safer as levelIter expects all keys coming from underlying
|
||||
// iterators to be in order. Due to compaction / tombstone writing
|
||||
// logic in finishOutput(), it is possible for range tombstones to not
|
||||
// be strictly ordered across all files in one level.
|
||||
//
|
||||
// Consider this example from the metamorphic tests (also repeated in
|
||||
// finishOutput()), consisting of three L3 files with their bounds
|
||||
// specified in square brackets next to the file name:
|
||||
//
|
||||
// ./000240.sst [tmgc#391,MERGE-tmgc#391,MERGE]
|
||||
// tmgc#391,MERGE [786e627a]
|
||||
// tmgc-udkatvs#331,RANGEDEL
|
||||
//
|
||||
// ./000241.sst [tmgc#384,MERGE-tmgc#384,MERGE]
|
||||
// tmgc#384,MERGE [666c7070]
|
||||
// tmgc-tvsalezade#383,RANGEDEL
|
||||
// tmgc-tvsalezade#331,RANGEDEL
|
||||
//
|
||||
// ./000242.sst [tmgc#383,RANGEDEL-tvsalezade#72057594037927935,RANGEDEL]
|
||||
// tmgc-tvsalezade#383,RANGEDEL
|
||||
// tmgc#375,SET [72646c78766965616c72776865676e79]
|
||||
// tmgc-tvsalezade#356,RANGEDEL
|
||||
//
|
||||
// Here, the range tombstone in 000240.sst falls "after" one in
|
||||
// 000241.sst, despite 000240.sst being ordered "before" 000241.sst for
|
||||
// levelIter's purposes. While each file is still consistent before its
|
||||
// bounds, it's safer to have all rangedel iterators be visible to
|
||||
// mergingIter.
|
||||
iter := level.files.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
rangeDelIter, err := newRangeDelIter(iter.Take(), nil, &c.bytesIterated)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "pebble: could not open table %s", errors.Safe(f.FileNum))
|
||||
}
|
||||
if rangeDelIter != emptyKeyspanIter {
|
||||
rangeDelIters = append(rangeDelIters, rangeDelIter)
|
||||
}
|
||||
}
|
||||
li := &keyspan.LevelIter{}
|
||||
li.Init(keyspan.SpanIterOptions{}, c.cmp, wrapper, level.files.Iter(), l, c.logger, manifest.KeyTypePoint)
|
||||
rangeDelIters = append(rangeDelIters, li)
|
||||
|
||||
// Check if this level has any range keys.
|
||||
hasRangeKeys := false
|
||||
iter := level.files.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
if f.HasRangeKeys {
|
||||
hasRangeKeys = true
|
||||
@@ -1245,13 +1327,13 @@ func (c *compaction) newInputIter(
|
||||
c.rangeDelIter.Init(c.cmp, rangeDelIters...)
|
||||
iters = append(iters, &c.rangeDelIter)
|
||||
}
|
||||
pointKeyIter := newMergingIter(c.logger, c.cmp, nil, iters...)
|
||||
pointKeyIter := newMergingIter(c.logger, &c.stats, c.cmp, nil, iters...)
|
||||
if len(rangeKeyIters) > 0 {
|
||||
mi := &keyspan.MergingIter{}
|
||||
mi.Init(c.cmp, rangeKeyCompactionTransform(snapshots, c.elideRangeKey), rangeKeyIters...)
|
||||
di := &keyspan.DefragmentingIter{}
|
||||
di.Init(c.cmp, mi, keyspan.DefragmentInternal, keyspan.StaticDefragmentReducer)
|
||||
c.rangeKeyInterleaving.Init(c.cmp, pointKeyIter, di, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
di.Init(c.comparer, mi, keyspan.DefragmentInternal, keyspan.StaticDefragmentReducer)
|
||||
c.rangeKeyInterleaving.Init(c.comparer, pointKeyIter, di, nil /* hooks */, nil /* lowerBound */, nil /* upperBound */)
|
||||
return &c.rangeKeyInterleaving, nil
|
||||
}
|
||||
|
||||
@@ -1306,10 +1388,10 @@ func (d *DB) addInProgressCompaction(c *compaction) {
|
||||
for _, cl := range c.inputs {
|
||||
iter := cl.files.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
d.opts.Logger.Fatalf("L%d->L%d: %s already being compacted", c.startLevel.level, c.outputLevel.level, f.FileNum)
|
||||
}
|
||||
f.Compacting = true
|
||||
f.SetCompactionState(manifest.CompactionStateCompacting)
|
||||
if c.startLevel != nil && c.outputLevel != nil && c.startLevel.level == 0 {
|
||||
if c.outputLevel.level == 0 {
|
||||
f.IsIntraL0Compacting = true
|
||||
@@ -1357,18 +1439,33 @@ func (d *DB) addInProgressCompaction(c *compaction) {
|
||||
}
|
||||
}
|
||||
|
||||
// Removes compaction markers from files in a compaction.
|
||||
// Removes compaction markers from files in a compaction. The rollback parameter
|
||||
// indicates whether the compaction state should be rolled back to its original
|
||||
// state in the case of an unsuccessful compaction.
|
||||
//
|
||||
// DB.mu must be held when calling this method. All writes to the manifest
|
||||
// for this compaction should have completed by this point.
|
||||
func (d *DB) removeInProgressCompaction(c *compaction) {
|
||||
// DB.mu must be held when calling this method. All writes to the manifest for
|
||||
// this compaction should have completed by this point.
|
||||
func (d *DB) removeInProgressCompaction(c *compaction, rollback bool) {
|
||||
for _, cl := range c.inputs {
|
||||
iter := cl.files.Iter()
|
||||
for f := iter.First(); f != nil; f = iter.Next() {
|
||||
if !f.Compacting {
|
||||
if !f.IsCompacting() {
|
||||
d.opts.Logger.Fatalf("L%d->L%d: %s not being compacted", c.startLevel.level, c.outputLevel.level, f.FileNum)
|
||||
}
|
||||
f.Compacting = false
|
||||
if !rollback {
|
||||
// On success all compactions other than move-compactions transition the
|
||||
// file into the Compacted state. Move-compacted files become eligible
|
||||
// for compaction again and transition back to NotCompacting.
|
||||
if c.kind != compactionKindMove {
|
||||
f.SetCompactionState(manifest.CompactionStateCompacted)
|
||||
} else {
|
||||
f.SetCompactionState(manifest.CompactionStateNotCompacting)
|
||||
}
|
||||
} else {
|
||||
// Else, on rollback, all input files unconditionally transition back to
|
||||
// NotCompacting.
|
||||
f.SetCompactionState(manifest.CompactionStateNotCompacting)
|
||||
}
|
||||
f.IsIntraL0Compacting = false
|
||||
}
|
||||
}
|
||||
@@ -1453,7 +1550,7 @@ func (d *DB) passedFlushThreshold() bool {
|
||||
return size >= minFlushSize
|
||||
}
|
||||
|
||||
func (d *DB) maybeScheduleDelayedFlush(tbl *memTable) {
|
||||
func (d *DB) maybeScheduleDelayedFlush(tbl *memTable, dur time.Duration) {
|
||||
var mem *flushableEntry
|
||||
for _, m := range d.mu.mem.queue {
|
||||
if m.flushable == tbl {
|
||||
@@ -1461,12 +1558,17 @@ func (d *DB) maybeScheduleDelayedFlush(tbl *memTable) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mem == nil || mem.flushForced || mem.delayedFlushForced {
|
||||
if mem == nil || mem.flushForced {
|
||||
return
|
||||
}
|
||||
mem.delayedFlushForced = true
|
||||
deadline := d.timeNow().Add(dur)
|
||||
if !mem.delayedFlushForcedAt.IsZero() && deadline.After(mem.delayedFlushForcedAt) {
|
||||
// Already scheduled to flush sooner than within `dur`.
|
||||
return
|
||||
}
|
||||
mem.delayedFlushForcedAt = deadline
|
||||
go func() {
|
||||
timer := time.NewTimer(d.opts.Experimental.DeleteRangeFlushDelay)
|
||||
timer := time.NewTimer(dur)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
@@ -1481,11 +1583,11 @@ func (d *DB) maybeScheduleDelayedFlush(tbl *memTable) {
|
||||
defer d.mu.Unlock()
|
||||
|
||||
// NB: The timer may fire concurrently with a call to Close. If a
|
||||
// Close call beat us to acquiring d.mu, d.closed is 1, and it's
|
||||
// too late to flush anything. Otherwise, the Close call will
|
||||
// block on locking d.mu until we've finished scheduling the flush
|
||||
// and set `d.mu.compact.flushing` to true. Close will wait for
|
||||
// the current flush to complete.
|
||||
// Close call beat us to acquiring d.mu, d.closed holds ErrClosed,
|
||||
// and it's too late to flush anything. Otherwise, the Close call
|
||||
// will block on locking d.mu until we've finished scheduling the
|
||||
// flush and set `d.mu.compact.flushing` to true. Close will wait
|
||||
// for the current flush to complete.
|
||||
if d.closed.Load() != nil {
|
||||
return
|
||||
}
|
||||
@@ -1609,7 +1711,7 @@ func (d *DB) flush1() (bytesFlushed uint64, err error) {
|
||||
|
||||
bytesFlushed = c.bytesIterated
|
||||
d.maybeUpdateDeleteCompactionHints(c)
|
||||
d.removeInProgressCompaction(c)
|
||||
d.removeInProgressCompaction(c, err != nil)
|
||||
d.mu.versions.incrementCompactions(c.kind, c.extraLevels)
|
||||
d.mu.versions.incrementCompactionBytes(-c.bytesWritten)
|
||||
|
||||
@@ -1683,7 +1785,8 @@ func (d *DB) maybeScheduleCompactionPicker(
|
||||
if d.closed.Load() != nil || d.opts.ReadOnly {
|
||||
return
|
||||
}
|
||||
if d.mu.compact.compactingCount >= d.opts.MaxConcurrentCompactions {
|
||||
maxConcurrentCompactions := d.opts.MaxConcurrentCompactions()
|
||||
if d.mu.compact.compactingCount >= maxConcurrentCompactions {
|
||||
if len(d.mu.compact.manual) > 0 {
|
||||
// Inability to run head blocks later manual compactions.
|
||||
d.mu.compact.manual[0].retries++
|
||||
@@ -1712,7 +1815,7 @@ func (d *DB) maybeScheduleCompactionPicker(
|
||||
// Check for delete-only compactions first, because they're expected to be
|
||||
// cheap and reduce future compaction work.
|
||||
if len(d.mu.compact.deletionHints) > 0 &&
|
||||
d.mu.compact.compactingCount < d.opts.MaxConcurrentCompactions &&
|
||||
d.mu.compact.compactingCount < maxConcurrentCompactions &&
|
||||
!d.opts.DisableAutomaticCompactions {
|
||||
v := d.mu.versions.currentVersion()
|
||||
snapshots := d.mu.snapshots.toSlice()
|
||||
@@ -1727,7 +1830,7 @@ func (d *DB) maybeScheduleCompactionPicker(
|
||||
}
|
||||
}
|
||||
|
||||
for len(d.mu.compact.manual) > 0 && d.mu.compact.compactingCount < d.opts.MaxConcurrentCompactions {
|
||||
for len(d.mu.compact.manual) > 0 && d.mu.compact.compactingCount < maxConcurrentCompactions {
|
||||
manual := d.mu.compact.manual[0]
|
||||
env.inProgressCompactions = d.getInProgressCompactionInfoLocked(nil)
|
||||
pc, retryLater := d.mu.versions.picker.pickManual(env, manual)
|
||||
@@ -1748,7 +1851,7 @@ func (d *DB) maybeScheduleCompactionPicker(
|
||||
}
|
||||
}
|
||||
|
||||
for !d.opts.DisableAutomaticCompactions && d.mu.compact.compactingCount < d.opts.MaxConcurrentCompactions {
|
||||
for !d.opts.DisableAutomaticCompactions && d.mu.compact.compactingCount < maxConcurrentCompactions {
|
||||
env.inProgressCompactions = d.getInProgressCompactionInfoLocked(nil)
|
||||
env.readCompactionEnv = readCompactionEnv{
|
||||
readCompactions: &d.mu.compact.readCompactions,
|
||||
@@ -2012,7 +2115,7 @@ func checkDeleteCompactionHints(
|
||||
overlaps := v.Overlaps(l, cmp, h.start, h.end, true /* exclusiveEnd */)
|
||||
iter := overlaps.Iter()
|
||||
for m := iter.First(); m != nil; m = iter.Next() {
|
||||
if m.Compacting || !h.canDelete(cmp, m, snapshots) || files[m] {
|
||||
if m.IsCompacting() || !h.canDelete(cmp, m, snapshots) || files[m] {
|
||||
continue
|
||||
}
|
||||
if files == nil {
|
||||
@@ -2098,7 +2201,7 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) {
|
||||
}
|
||||
|
||||
d.maybeUpdateDeleteCompactionHints(c)
|
||||
d.removeInProgressCompaction(c)
|
||||
d.removeInProgressCompaction(c, err != nil)
|
||||
d.mu.versions.incrementCompactions(c.kind, c.extraLevels)
|
||||
d.mu.versions.incrementCompactionBytes(-c.bytesWritten)
|
||||
|
||||
@@ -2276,10 +2379,10 @@ func (d *DB) runCompaction(
|
||||
// must be careful, because the byte slice returned by UnsafeKey
|
||||
// points directly into the Writer's block buffer.
|
||||
var prevPointKey sstable.PreviousPointKeyOpt
|
||||
var additionalCPUProcs int
|
||||
var cpuWorkHandle CPUWorkHandle
|
||||
defer func() {
|
||||
if additionalCPUProcs > 0 {
|
||||
d.opts.Experimental.CPUWorkPermissionGranter.ReturnProcs(additionalCPUProcs)
|
||||
if cpuWorkHandle != nil {
|
||||
d.opts.Experimental.CPUWorkPermissionGranter.CPUWorkDone(cpuWorkHandle)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -2318,12 +2421,15 @@ func (d *DB) runCompaction(
|
||||
filenames = append(filenames, filename)
|
||||
cacheOpts := private.SSTableCacheOpts(d.cacheID, fileNum).(sstable.WriterOption)
|
||||
internalTableOpt := private.SSTableInternalTableOpt.(sstable.WriterOption)
|
||||
if d.opts.Experimental.CPUWorkPermissionGranter != nil {
|
||||
additionalCPUProcs = d.opts.Experimental.CPUWorkPermissionGranter.TryGetProcs(1)
|
||||
}
|
||||
|
||||
const MaxFileWriteAdditionalCPUTime = time.Millisecond * 100
|
||||
cpuWorkHandle = d.opts.Experimental.CPUWorkPermissionGranter.GetPermission(
|
||||
MaxFileWriteAdditionalCPUTime,
|
||||
)
|
||||
writerOpts.Parallelism =
|
||||
d.opts.Experimental.MaxWriterConcurrency > 0 &&
|
||||
(additionalCPUProcs > 0 || d.opts.Experimental.ForceWriterParallelism)
|
||||
(cpuWorkHandle.Permitted() || d.opts.Experimental.ForceWriterParallelism)
|
||||
|
||||
tw = sstable.NewWriter(file, writerOpts, cacheOpts, internalTableOpt, &prevPointKey)
|
||||
|
||||
fileMeta.CreationTime = time.Now().Unix()
|
||||
@@ -2428,10 +2534,8 @@ func (d *DB) runCompaction(
|
||||
tw = nil
|
||||
return err
|
||||
}
|
||||
if additionalCPUProcs > 0 {
|
||||
d.opts.Experimental.CPUWorkPermissionGranter.ReturnProcs(additionalCPUProcs)
|
||||
additionalCPUProcs = 0
|
||||
}
|
||||
d.opts.Experimental.CPUWorkPermissionGranter.CPUWorkDone(cpuWorkHandle)
|
||||
cpuWorkHandle = nil
|
||||
writerMeta, err := tw.Metadata()
|
||||
if err != nil {
|
||||
tw = nil
|
||||
@@ -2737,12 +2841,19 @@ func validateVersionEdit(
|
||||
// and adds those to the internal lists of obsolete files. Note that the files
|
||||
// are not actually deleted by this method. A subsequent call to
|
||||
// deleteObsoleteFiles must be performed. Must be not be called concurrently
|
||||
// with compactions and flushes. db.mu must be held when calling this function,
|
||||
// however as this function is expected to only be called during Open(), no
|
||||
// other operations should be contending on it just yet.
|
||||
// with compactions and flushes. db.mu must be held when calling this function.
|
||||
func (d *DB) scanObsoleteFiles(list []string) {
|
||||
// Disable automatic compactions temporarily to avoid concurrent compactions /
|
||||
// flushes from interfering. The original value is restored on completion.
|
||||
disabledPrev := d.opts.DisableAutomaticCompactions
|
||||
defer func() {
|
||||
d.opts.DisableAutomaticCompactions = disabledPrev
|
||||
}()
|
||||
d.opts.DisableAutomaticCompactions = true
|
||||
|
||||
// Wait for any ongoing compaction to complete before continuing.
|
||||
if d.mu.compact.compactingCount > 0 || d.mu.compact.flushing {
|
||||
panic("pebble: cannot scan obsolete files concurrently with compaction/flushing")
|
||||
d.mu.compact.cond.Wait()
|
||||
}
|
||||
|
||||
liveFileNums := make(map[FileNum]struct{})
|
||||
|
||||
44
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
44
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -12,6 +13,7 @@ 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"
|
||||
)
|
||||
@@ -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
|
||||
@@ -252,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)
|
||||
}
|
||||
@@ -464,7 +475,12 @@ func (i *compactionIter) skipInStripe() {
|
||||
}
|
||||
|
||||
func (i *compactionIter) iterNext() bool {
|
||||
i.iterKey, i.iterValue = i.iter.Next()
|
||||
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
|
||||
}
|
||||
|
||||
@@ -491,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
|
||||
}
|
||||
@@ -734,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 {
|
||||
|
||||
69
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
69
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
@@ -19,8 +19,6 @@ import (
|
||||
// heuristic.
|
||||
const minIntraL0Count = 4
|
||||
|
||||
const levelMultiplier = 10
|
||||
|
||||
type compactionEnv struct {
|
||||
earliestUnflushedSeqNum uint64
|
||||
earliestSnapshotSeqNum uint64
|
||||
@@ -294,7 +292,9 @@ func (pc *pickedCompaction) setupInputs(
|
||||
// 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
|
||||
@@ -504,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 {
|
||||
@@ -522,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 {
|
||||
@@ -538,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
|
||||
}
|
||||
|
||||
@@ -576,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
|
||||
}
|
||||
@@ -590,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{}
|
||||
|
||||
@@ -607,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{} {
|
||||
@@ -622,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
|
||||
}
|
||||
@@ -783,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).
|
||||
@@ -795,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
|
||||
@@ -823,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
|
||||
@@ -836,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
|
||||
@@ -863,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])
|
||||
@@ -987,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
|
||||
@@ -1020,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()
|
||||
}
|
||||
@@ -1082,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]),
|
||||
)
|
||||
|
||||
@@ -1223,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
|
||||
@@ -1322,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)
|
||||
@@ -1360,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
|
||||
}
|
||||
@@ -1662,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
|
||||
}
|
||||
}
|
||||
|
||||
176
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
176
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 (
|
||||
@@ -168,18 +169,40 @@ type Writer interface {
|
||||
RangeKeyDelete(start, end []byte, opts *WriteOptions) error
|
||||
}
|
||||
|
||||
// CPUWorkPermissionGranter is used to request permission to opportunistically
|
||||
// use additional CPUs to speed up internal background work. Each granted "proc"
|
||||
// can be used to spin up a CPU bound goroutine, i.e, if scheduled each such
|
||||
// goroutine can consume one P in the goroutine scheduler. The calls to
|
||||
// ReturnProcs can be a bit delayed, since Pebble interacts with this interface
|
||||
// in a coarse manner. So one should assume that the total number of granted
|
||||
// procs is a non tight upper bound on the CPU that will get consumed.
|
||||
type CPUWorkPermissionGranter interface {
|
||||
TryGetProcs(count int) int
|
||||
ReturnProcs(count int)
|
||||
// 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
|
||||
@@ -341,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 {
|
||||
@@ -523,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,
|
||||
}
|
||||
@@ -769,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()
|
||||
}
|
||||
|
||||
@@ -902,10 +935,8 @@ 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,
|
||||
@@ -920,6 +951,9 @@ func (d *DB) newIterInternal(batch *Batch, s *Snapshot, o *IterOptions) *Iterato
|
||||
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()
|
||||
}
|
||||
@@ -959,6 +993,8 @@ func finishInitializingIter(buf *iterAlloc) *Iterator {
|
||||
}
|
||||
|
||||
if dbi.opts.rangeKeys() {
|
||||
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
|
||||
@@ -969,7 +1005,8 @@ func finishInitializingIter(buf *iterAlloc) *Iterator {
|
||||
// contains any range keys.
|
||||
useLazyCombinedIteration := dbi.rangeKey == nil &&
|
||||
dbi.opts.KeyTypes == IterKeyTypePointsAndRanges &&
|
||||
(dbi.batch == nil || dbi.batch.countRangeKeys == 0)
|
||||
(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
|
||||
@@ -993,10 +1030,15 @@ func finishInitializingIter(buf *iterAlloc) *Iterator {
|
||||
}
|
||||
dbi.iter = &dbi.lazyCombinedIter
|
||||
} else {
|
||||
dbi.lazyCombinedIter.combinedIterState = combinedIterState{
|
||||
initialized: true,
|
||||
}
|
||||
if dbi.rangeKey == nil {
|
||||
dbi.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
dbi.rangeKey.init(dbi.cmp, dbi.split, &dbi.opts)
|
||||
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
|
||||
@@ -1006,8 +1048,8 @@ func finishInitializingIter(buf *iterAlloc) *Iterator {
|
||||
// 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.cmp, dbi.iter, dbi.rangeKey.rangeKeyIter, dbi.rangeKey,
|
||||
dbi.opts.LowerBound, dbi.opts.UpperBound)
|
||||
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 {
|
||||
@@ -1025,6 +1067,10 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
// Already have one.
|
||||
return
|
||||
}
|
||||
internalOpts := internalIterOpts{stats: &i.stats.InternalStats}
|
||||
if i.opts.RangeKeyMasking.Filter != nil {
|
||||
internalOpts.boundLimitedFilter = &i.rangeKeyMasking
|
||||
}
|
||||
|
||||
// Merging levels and levels from iterAlloc.
|
||||
mlevels := buf.mlevels[:0]
|
||||
@@ -1068,7 +1114,7 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
rangeDelIter: newErrorKeyspanIter(ErrNotIndexed),
|
||||
})
|
||||
} else {
|
||||
i.batch.initInternalIter(&i.opts, &i.batchPointIter, i.batchSeqNum)
|
||||
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
|
||||
@@ -1080,7 +1126,7 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
rangeDelIter = &i.batchRangeDelIter
|
||||
}
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: base.WrapIterWithStats(&i.batchPointIter),
|
||||
iter: &i.batchPointIter,
|
||||
rangeDelIter: rangeDelIter,
|
||||
})
|
||||
}
|
||||
@@ -1090,7 +1136,7 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
for j := len(memtables) - 1; j >= 0; j-- {
|
||||
mem := memtables[j]
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: base.WrapIterWithStats(mem.newIter(&i.opts)),
|
||||
iter: mem.newIter(&i.opts),
|
||||
rangeDelIter: mem.newRangeDelIter(&i.opts),
|
||||
})
|
||||
}
|
||||
@@ -1100,11 +1146,10 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
levelsIndex := len(levels)
|
||||
mlevels = mlevels[:numMergingLevels]
|
||||
levels = levels[:numLevelIters]
|
||||
|
||||
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
||||
li := &levels[levelsIndex]
|
||||
|
||||
li.init(i.opts, i.cmp, i.split, i.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.initBoundaryContext(&mlevels[mlevelsIndex].levelIterBoundaryContext)
|
||||
li.initCombinedIterState(&i.lazyCombinedIter.combinedIterState)
|
||||
@@ -1127,11 +1172,13 @@ func (i *Iterator) constructPointIter(memtables flushableList, buf *iterAlloc) {
|
||||
}
|
||||
addLevelIterForFiles(current.Levels[level].Iter(), manifest.Level(level))
|
||||
}
|
||||
buf.merging.init(&i.opts, i.cmp, i.split, mlevels...)
|
||||
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
|
||||
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
|
||||
@@ -1190,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 {
|
||||
@@ -1279,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
|
||||
}
|
||||
|
||||
@@ -1466,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()
|
||||
}
|
||||
@@ -1539,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()
|
||||
@@ -1831,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()
|
||||
|
||||
@@ -1924,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
|
||||
|
||||
32
vendor/github.com/cockroachdb/pebble/error_iter.go
generated
vendored
32
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, flags base.SeekGEFlags) (*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, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
return nil, nil
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
func (c *errorIter) SeekLT(key []byte, flags base.SeekLTFlags) (*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
|
||||
|
||||
423
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
423
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
@@ -5,6 +5,9 @@
|
||||
package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
@@ -12,12 +15,59 @@ import (
|
||||
"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.
|
||||
//
|
||||
@@ -27,8 +77,8 @@ import (
|
||||
func NewExternalIter(
|
||||
o *Options,
|
||||
iterOpts *IterOptions,
|
||||
files []sstable.ReadableFile,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
files [][]sstable.ReadableFile,
|
||||
extraOpts ...ExternalIterOption,
|
||||
) (it *Iterator, err error) {
|
||||
if iterOpts != nil {
|
||||
if err := validateExternalIterOpts(iterOpts); err != nil {
|
||||
@@ -36,17 +86,32 @@ func NewExternalIter(
|
||||
}
|
||||
}
|
||||
|
||||
var readers []*sstable.Reader
|
||||
var readers [][]*sstable.Reader
|
||||
|
||||
// Ensure we close all the opened readers if we error out.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
for i := range readers {
|
||||
_ = readers[i].Close()
|
||||
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
|
||||
}
|
||||
@@ -55,10 +120,8 @@ 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,
|
||||
@@ -67,7 +130,7 @@ func NewExternalIter(
|
||||
// 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, bytesIterated *uint64) (internalIterator, keyspan.FragmentIterator, error) {
|
||||
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
|
||||
@@ -82,6 +145,9 @@ func NewExternalIter(
|
||||
dbi.opts = *iterOpts
|
||||
dbi.saveBounds(iterOpts.LowerBound, iterOpts.UpperBound)
|
||||
}
|
||||
for i := range extraOpts {
|
||||
extraOpts[i].iterApply(dbi)
|
||||
}
|
||||
finishInitializingExternal(dbi)
|
||||
return dbi, nil
|
||||
}
|
||||
@@ -102,71 +168,147 @@ func validateExternalIterOpts(iterOpts *IterOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func finishInitializingExternal(it *Iterator) {
|
||||
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.
|
||||
|
||||
mlevels := it.alloc.mlevels[:0]
|
||||
if !it.opts.pointKeys() {
|
||||
it.pointIter = emptyIter
|
||||
} else if it.pointIter == nil {
|
||||
if len(it.externalReaders) > cap(mlevels) {
|
||||
mlevels = make([]mergingIterLevel, 0, len(it.externalReaders))
|
||||
}
|
||||
for _, r := range it.externalReaders {
|
||||
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 {
|
||||
var (
|
||||
rangeDelIter keyspan.FragmentIterator
|
||||
pointIter internalIterator
|
||||
err error
|
||||
)
|
||||
pointIter, err = r.NewIter(it.opts.LowerBound, it.opts.UpperBound)
|
||||
if err == nil {
|
||||
rangeDelIter, err = r.NewRawRangeDelIter()
|
||||
}
|
||||
pointIter, err = r.NewIterWithBlockPropertyFilters(
|
||||
it.opts.LowerBound,
|
||||
it.opts.UpperBound,
|
||||
nil, /* BlockPropertiesFilterer */
|
||||
false, /* useFilterBlock */
|
||||
&it.stats.InternalStats,
|
||||
)
|
||||
if err != nil {
|
||||
pointIter = &errorIter{err: err}
|
||||
rangeDelIter = &errorKeyspanIter{err: err}
|
||||
return nil, err
|
||||
}
|
||||
rangeDelIter, err = r.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
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,
|
||||
})
|
||||
}
|
||||
it.alloc.merging.init(&it.opts, it.cmp, it.split, mlevels...)
|
||||
it.alloc.merging.snapshot = base.InternalKeySeqNumMax
|
||||
it.alloc.merging.elideRangeTombstones = true
|
||||
it.pointIter = &it.alloc.merging
|
||||
if len(combinedIters) == 1 {
|
||||
mlevels = append(mlevels, mergingIterLevel{
|
||||
iter: combinedIters[0],
|
||||
})
|
||||
} else if len(combinedIters) > 1 {
|
||||
sli := &simpleLevelIter{
|
||||
cmp: it.cmp,
|
||||
iters: combinedIters,
|
||||
}
|
||||
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 {
|
||||
it.rangeKey = iterRangeKeyStateAllocPool.Get().(*iteratorRangeKeyState)
|
||||
it.rangeKey.init(it.cmp, it.split, &it.opts)
|
||||
it.rangeKey.rangeKeyIter = it.rangeKey.iterConfig.Init(
|
||||
it.cmp,
|
||||
base.InternalKeySeqNumMax,
|
||||
)
|
||||
for _, r := range it.externalReaders {
|
||||
if rki, err := r.NewRawRangeKeyIter(); err != nil {
|
||||
it.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
||||
} else if rki != nil {
|
||||
it.rangeKey.iterConfig.AddLevel(rki)
|
||||
// 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
it.rangeKey.iiter.Init(it.cmp, it.iter, it.rangeKey.rangeKeyIter, it.rangeKey,
|
||||
it.opts.LowerBound, it.opts.UpperBound)
|
||||
it.iter = &it.rangeKey.iiter
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openExternalTables(
|
||||
o *Options,
|
||||
files []sstable.ReadableFile,
|
||||
seqNumOffset int,
|
||||
readerOpts sstable.ReaderOptions,
|
||||
extraReaderOpts ...sstable.ReaderOption,
|
||||
) (readers []*sstable.Reader, err error) {
|
||||
@@ -178,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{}
|
||||
|
||||
8
vendor/github.com/cockroachdb/pebble/flushable.go
generated
vendored
8
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"
|
||||
)
|
||||
@@ -37,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
|
||||
|
||||
242
vendor/github.com/cockroachdb/pebble/format_major_version.go
generated
vendored
242
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,14 +82,17 @@ 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
|
||||
@@ -92,11 +100,28 @@ const (
|
||||
// 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 = FormatMinTableFormatPebblev1
|
||||
FormatNewest FormatMajorVersion = FormatPrePebblev1MarkedCompacted
|
||||
)
|
||||
|
||||
// MaxTableFormat returns the maximum sstable.TableFormat that can be used at
|
||||
@@ -106,9 +131,11 @@ 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, FormatMinTableFormatPebblev1:
|
||||
case FormatRangeKeys, FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
||||
FormatPrePebblev1MarkedCompacted:
|
||||
return sstable.TableFormatPebblev2
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||
@@ -121,9 +148,11 @@ func (v FormatMajorVersion) MinTableFormat() sstable.TableFormat {
|
||||
switch v {
|
||||
case FormatDefault, FormatMostCompatible, formatVersionedManifestMarker,
|
||||
FormatVersioned, FormatSetWithDelete, FormatBlockPropertyCollector,
|
||||
FormatSplitUserKeysMarked, FormatMarkedCompacted, FormatRangeKeys:
|
||||
FormatSplitUserKeysMarked, FormatSplitUserKeysMarkedCompacted,
|
||||
FormatRangeKeys:
|
||||
return sstable.TableFormatLevelDB
|
||||
case FormatMinTableFormatPebblev1:
|
||||
case FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
||||
FormatPrePebblev1MarkedCompacted:
|
||||
return sstable.TableFormatPebblev1
|
||||
default:
|
||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||
@@ -209,19 +238,19 @@ 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)
|
||||
@@ -229,6 +258,23 @@ var formatMajorVersionMigrations = map[FormatMajorVersion]func(*DB) error{
|
||||
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`
|
||||
@@ -339,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 {
|
||||
@@ -362,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() {
|
||||
@@ -407,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
|
||||
@@ -418,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
|
||||
|
||||
51
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
51
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
@@ -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, flags base.SeekGEFlags) (*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, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (g *getIter) SeekLT(key []byte, flags base.SeekLTFlags) (*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()
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
if g.rangeDelIter != nil {
|
||||
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
|
||||
}
|
||||
@@ -98,10 +98,10 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
// 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,7 +113,7 @@ 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{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,11 +121,16 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
if g.batch != nil {
|
||||
if g.batch.index == nil {
|
||||
g.err = ErrNotIndexed
|
||||
g.iterKey, g.iterValue = nil, nil
|
||||
return nil, nil
|
||||
g.iterKey, g.iterValue = nil, base.LazyValue{}
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
g.iter = g.batch.newInternalIter(nil)
|
||||
g.rangeDelIter = g.batch.newRangeDelIter(nil, g.batch.nextSeqNum())
|
||||
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
|
||||
continue
|
||||
@@ -134,7 +139,7 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
// If we have a tombstone from a previous level it is guaranteed to delete
|
||||
// keys in lower levels.
|
||||
if g.tombstone != nil && g.tombstone.VisibleAt(g.snapshot) {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
|
||||
// Create iterators from memtables from newest to oldest.
|
||||
@@ -154,7 +159,7 @@ 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, base.SeekGEFlagsNone)
|
||||
@@ -164,7 +169,7 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
}
|
||||
|
||||
if g.level >= numLevels {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
if g.version.Levels[g.level].Empty() {
|
||||
g.level++
|
||||
@@ -173,7 +178,7 @@ 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
|
||||
@@ -181,18 +186,10 @@ func (g *getIter) Next() (*InternalKey, []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
@@ -511,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
|
||||
}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
@@ -33,8 +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
|
||||
|
||||
22
vendor/github.com/cockroachdb/pebble/internal/arenaskl/flush_iterator.go
generated
vendored
22
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, flags base.SeekGEFlags) (*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, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("pebble: SeekPrefixGE unimplemented")
|
||||
}
|
||||
|
||||
func (it *flushIterator) SeekLT(key []byte, flags base.SeekLTFlags) (*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, flags base.SeekLTFlags) (*base.Inter
|
||||
// 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")
|
||||
}
|
||||
|
||||
56
vendor/github.com/cockroachdb/pebble/internal/arenaskl/iterator.go
generated
vendored
56
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, flags base.SeekGEFlags) (*base.InternalKey, []byte) {
|
||||
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, flags base.SeekGEFlags) (*base.InternalKe
|
||||
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
|
||||
@@ -120,7 +120,7 @@ func (it *Iterator) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKe
|
||||
// internal/base.InternalIterator interface.
|
||||
func (it *Iterator) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
return it.SeekGE(key, flags)
|
||||
}
|
||||
|
||||
@@ -128,85 +128,85 @@ func (it *Iterator) SeekPrefixGE(
|
||||
// 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, flags base.SeekLTFlags) (*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.
|
||||
|
||||
102
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
102
vendor/github.com/cockroachdb/pebble/internal/base/iterator.go
generated
vendored
@@ -85,17 +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.
|
||||
SeekGE(key []byte, flags SeekGEFlags) (*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.
|
||||
//
|
||||
@@ -104,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
|
||||
@@ -116,52 +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.
|
||||
SeekPrefixGE(prefix, key []byte, flags SeekGEFlags) (*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, flags SeekLTFlags) (*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
|
||||
@@ -190,6 +193,7 @@ type SeekGEFlags uint8
|
||||
const (
|
||||
seekGEFlagTrySeekUsingNext uint8 = iota
|
||||
seekGEFlagRelativeSeek
|
||||
seekGEFlagBatchJustRefreshed
|
||||
)
|
||||
|
||||
// SeekGEFlagsNone is the default value of SeekGEFlags, with all flags disabled.
|
||||
@@ -234,6 +238,13 @@ func (s SeekGEFlags) TrySeekUsingNext() bool { return (s & (1 << seekGEFlagTrySe
|
||||
// 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.
|
||||
@@ -259,6 +270,19 @@ 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
|
||||
@@ -295,21 +319,14 @@ func (s SeekLTFlags) DisableRelativeSeek() SeekLTFlags {
|
||||
return s &^ (1 << seekLTFlagRelativeSeek)
|
||||
}
|
||||
|
||||
// InternalIteratorWithStats extends InternalIterator to expose stats.
|
||||
type InternalIteratorWithStats interface {
|
||||
InternalIterator
|
||||
Stats() InternalIteratorStats
|
||||
ResetStats()
|
||||
}
|
||||
|
||||
// InternalIteratorStats contains miscellaneous stats produced by
|
||||
// InternalIterators that are part of the InternalIterator tree. Not every
|
||||
// field is relevant for an InternalIterator implementation. The field values
|
||||
// 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
|
||||
@@ -341,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
|
||||
}
|
||||
53
vendor/github.com/cockroachdb/pebble/internal/keyspan/defragment.go
generated
vendored
53
vendor/github.com/cockroachdb/pebble/internal/keyspan/defragment.go
generated
vendored
@@ -21,26 +21,30 @@ const bufferReuseMaxCapacity = 10 << 10 // 10 KB
|
||||
type DefragmentMethod interface {
|
||||
// ShouldDefragment takes two abutting spans and returns whether the two
|
||||
// spans should be combined into a single, defragmented Span.
|
||||
ShouldDefragment(cmp base.Compare, left, right *Span) bool
|
||||
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(cmp base.Compare, left, right *Span) bool
|
||||
type DefragmentMethodFunc func(equal base.Equal, left, right *Span) bool
|
||||
|
||||
// ShouldDefragment calls f(cmp, left, right).
|
||||
func (f DefragmentMethodFunc) ShouldDefragment(cmp base.Compare, left, right *Span) bool {
|
||||
return f(cmp, left, right)
|
||||
// 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 = DefragmentMethodFunc(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
|
||||
}
|
||||
@@ -48,7 +52,7 @@ var DefragmentInternal DefragmentMethod = DefragmentMethodFunc(func(cmp base.Com
|
||||
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) {
|
||||
@@ -114,7 +118,8 @@ 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
|
||||
iterPos iterPos
|
||||
@@ -149,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,
|
||||
method: equal,
|
||||
reduce: reducer,
|
||||
comparer: comparer,
|
||||
equal: comparer.Equal,
|
||||
iter: iter,
|
||||
method: equal,
|
||||
reduce: reducer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,11 +182,14 @@ func (i *DefragmentingIter) SeekGE(key []byte) *Span {
|
||||
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.iter.Prev()
|
||||
if i.iterSpan != nil && 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
|
||||
@@ -206,6 +215,9 @@ func (i *DefragmentingIter) SeekLT(key []byte) *Span {
|
||||
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()
|
||||
@@ -355,7 +367,7 @@ func (i *DefragmentingIter) Prev() *Span {
|
||||
// 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 (!left.Empty() && !right.Empty()) && i.method.ShouldDefragment(i.cmp, i.iterSpan, &i.curr)
|
||||
return (!left.Empty() && !right.Empty()) && i.method.ShouldDefragment(i.equal, i.iterSpan, &i.curr)
|
||||
}
|
||||
|
||||
// defragmentForward defragments spans in the forward direction, starting from
|
||||
@@ -374,7 +386,7 @@ func (i *DefragmentingIter) defragmentForward() *Span {
|
||||
i.iterPos = iterPosNext
|
||||
i.iterSpan = i.iter.Next()
|
||||
for i.iterSpan != nil {
|
||||
if i.cmp(i.curr.End, i.iterSpan.Start) != 0 {
|
||||
if !i.equal(i.curr.End, i.iterSpan.Start) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
@@ -407,7 +419,7 @@ func (i *DefragmentingIter) defragmentBackward() *Span {
|
||||
i.iterPos = iterPosPrev
|
||||
i.iterSpan = i.iter.Prev()
|
||||
for i.iterSpan != nil {
|
||||
if i.cmp(i.curr.Start, i.iterSpan.End) != 0 {
|
||||
if !i.equal(i.curr.Start, i.iterSpan.End) {
|
||||
// Not a continuation.
|
||||
break
|
||||
}
|
||||
@@ -438,8 +450,9 @@ func (i *DefragmentingIter) saveCurrent() {
|
||||
return
|
||||
}
|
||||
i.curr = Span{
|
||||
Start: i.saveBytes(i.iterSpan.Start),
|
||||
End: i.saveBytes(i.iterSpan.End),
|
||||
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{
|
||||
|
||||
4
vendor/github.com/cockroachdb/pebble/internal/keyspan/fragmenter.go
generated
vendored
4
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); {
|
||||
|
||||
357
vendor/github.com/cockroachdb/pebble/internal/keyspan/interleaving_iter.go
generated
vendored
357
vendor/github.com/cockroachdb/pebble/internal/keyspan/interleaving_iter.go
generated
vendored
@@ -35,14 +35,6 @@ type SpanMask interface {
|
||||
SkipPoint(userKey []byte) bool
|
||||
}
|
||||
|
||||
type maskState int8
|
||||
|
||||
const (
|
||||
maskStale maskState = iota
|
||||
maskCleared
|
||||
maskCurrent
|
||||
)
|
||||
|
||||
// InterleavingIter combines an iterator over point keys with an iterator over
|
||||
// key spans.
|
||||
//
|
||||
@@ -101,7 +93,8 @@ const (
|
||||
// All spans containing keys are exposed during iteration.
|
||||
type InterleavingIter struct {
|
||||
cmp base.Compare
|
||||
pointIter base.InternalIteratorWithStats
|
||||
comparer *base.Comparer
|
||||
pointIter base.InternalIterator
|
||||
keyspanIter FragmentIterator
|
||||
mask SpanMask
|
||||
|
||||
@@ -111,9 +104,13 @@ type InterleavingIter struct {
|
||||
// to truncate a span. The byte slices backing a SeekGE/SeekPrefixGE search
|
||||
// keys can come directly from the end user, so they're copied into keyBuf
|
||||
// to ensure key stability.
|
||||
keyBuf []byte
|
||||
pointKey *base.InternalKey
|
||||
pointVal []byte
|
||||
keyBuf []byte
|
||||
// nextPrefixBuf is used during SeekPrefixGE calls to store the truncated
|
||||
// upper bound of the returned spans. SeekPrefixGE truncates the returned
|
||||
// spans to an upper bound of the seeked prefix's immediate successor.
|
||||
nextPrefixBuf []byte
|
||||
pointKey *base.InternalKey
|
||||
pointVal base.LazyValue
|
||||
// span holds the span at the keyspanIter's current position. If the span is
|
||||
// wholly contained within the iterator bounds, this span is directly
|
||||
// returned to the iterator consumer through Span(). If either bound needed
|
||||
@@ -158,32 +155,15 @@ type InterleavingIter struct {
|
||||
// span's start bound marker to the search key. It's returned to false on
|
||||
// the next repositioning of the keyspan iterator.
|
||||
spanMarkerTruncated bool
|
||||
// maskState holds a tri-state enum describing the state of the state
|
||||
// machine managing invocations to i.mask.SpanChanged. When i.span is
|
||||
// updated, `maskState` is set to the starting state `maskStale` to reflect
|
||||
// that the mask has not been informed of the new span. From `maskStale`,
|
||||
// the state transitions to `maskCleared` if the next returned key does not
|
||||
// fall within i.span's bounds. The state transitions to `maskCurrent` if
|
||||
// the next returned key does fall within i.span's bounds.
|
||||
//
|
||||
// return key outside [s,e)
|
||||
// keyspan.Iter.{Next,Prev}
|
||||
// _________________________________
|
||||
// \/ |
|
||||
// _____________ |
|
||||
// | maskStale | |
|
||||
// |_____________| |
|
||||
// return key / \ return key |
|
||||
// outside [s,e) / \ within [s,e) |
|
||||
// \/ \/ |
|
||||
// ________________________ __________________________ |
|
||||
// | SpanChanged(nil) | | SpanChanged(i.span) | __|
|
||||
// | maskState = maskCleared| | maskState = maskCurrent |
|
||||
// |________________________| |__________________________|
|
||||
// |__________________________^
|
||||
// return key within [s,e)
|
||||
//
|
||||
maskState maskState
|
||||
// maskSpanChangedCalled records whether or not the last call to
|
||||
// SpanMask.SpanChanged provided the current span (i.span) or not.
|
||||
maskSpanChangedCalled bool
|
||||
// prefix records whether the iteator is in prefix mode. During prefix mode,
|
||||
// Pebble will truncate spans to the next prefix. If the iterator
|
||||
// subsequently leaves prefix mode, the existing span cached in i.span must
|
||||
// be invalidated because its bounds do not reflect the original span's true
|
||||
// bounds.
|
||||
prefix bool
|
||||
// dir indicates the direction of iteration: forward (+1) or backward (-1)
|
||||
dir int8
|
||||
}
|
||||
@@ -197,14 +177,15 @@ var _ base.InternalIterator = &InterleavingIter{}
|
||||
// The point iterator must already have the provided bounds. Init does not
|
||||
// propagate the bounds down the iterator stack.
|
||||
func (i *InterleavingIter) Init(
|
||||
cmp base.Compare,
|
||||
pointIter base.InternalIteratorWithStats,
|
||||
comparer *base.Comparer,
|
||||
pointIter base.InternalIterator,
|
||||
keyspanIter FragmentIterator,
|
||||
mask SpanMask,
|
||||
lowerBound, upperBound []byte,
|
||||
) {
|
||||
*i = InterleavingIter{
|
||||
cmp: cmp,
|
||||
cmp: comparer.Compare,
|
||||
comparer: comparer,
|
||||
pointIter: pointIter,
|
||||
keyspanIter: keyspanIter,
|
||||
mask: mask,
|
||||
@@ -224,16 +205,18 @@ func (i *InterleavingIter) Init(
|
||||
// It allows for seeding the iterator with the current position of the point
|
||||
// iterator.
|
||||
func (i *InterleavingIter) InitSeekGE(
|
||||
key []byte, pointKey *base.InternalKey, pointValue []byte,
|
||||
) (*base.InternalKey, []byte) {
|
||||
prefix, key []byte, pointKey *base.InternalKey, pointValue base.LazyValue,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.dir = +1
|
||||
i.clearMask()
|
||||
i.prefix = prefix != nil
|
||||
i.pointKey, i.pointVal = pointKey, pointValue
|
||||
i.pointKeyInterleaved = false
|
||||
// NB: This keyspanSeekGE call will truncate the span to the seek key if
|
||||
// necessary. This truncation is important for cases where a switch to
|
||||
// combined iteration is made during a user-initiated SeekGE.
|
||||
i.keyspanSeekGE(key)
|
||||
return i.interleaveForward(key)
|
||||
i.keyspanSeekGE(key, prefix)
|
||||
return i.interleaveForward(key, prefix)
|
||||
}
|
||||
|
||||
// InitSeekLT may be called after Init but before any positioning method.
|
||||
@@ -247,9 +230,10 @@ func (i *InterleavingIter) InitSeekGE(
|
||||
// It allows for seeding the iterator with the current position of the point
|
||||
// iterator.
|
||||
func (i *InterleavingIter) InitSeekLT(
|
||||
key []byte, pointKey *base.InternalKey, pointValue []byte,
|
||||
) (*base.InternalKey, []byte) {
|
||||
key []byte, pointKey *base.InternalKey, pointValue base.LazyValue,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.dir = -1
|
||||
i.clearMask()
|
||||
i.pointKey, i.pointVal = pointKey, pointValue
|
||||
i.pointKeyInterleaved = false
|
||||
i.keyspanSeekLT(key)
|
||||
@@ -265,14 +249,30 @@ func (i *InterleavingIter) InitSeekLT(
|
||||
// the span returned by Span.
|
||||
//
|
||||
// NB: In accordance with the base.InternalIterator contract:
|
||||
// i.lower ≤ key
|
||||
func (i *InterleavingIter) SeekGE(key []byte, flags base.SeekGEFlags) (*base.InternalKey, []byte) {
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
//
|
||||
// i.lower ≤ key
|
||||
func (i *InterleavingIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.clearMask()
|
||||
i.disablePrefixMode()
|
||||
i.pointKey, i.pointVal = i.pointIter.SeekGE(key, flags)
|
||||
i.pointKeyInterleaved = false
|
||||
i.keyspanSeekGE(key)
|
||||
|
||||
// We need to seek the keyspan iterator too. If the keyspan iterator was
|
||||
// already positioned at a span, we might be able to avoid the seek if the
|
||||
// seek key falls within the existing span's bounds.
|
||||
if i.span != nil && i.cmp(key, i.span.End) < 0 && i.cmp(key, i.span.Start) >= 0 {
|
||||
// We're seeking within the existing span's bounds. We still might need
|
||||
// truncate the span to the iterator's bounds.
|
||||
i.checkForwardBound(nil /* prefix */)
|
||||
i.savedKeyspan()
|
||||
} else {
|
||||
i.keyspanSeekGE(key, nil /* prefix */)
|
||||
}
|
||||
|
||||
i.dir = +1
|
||||
return i.interleaveForward(key)
|
||||
return i.interleaveForward(key, nil /* prefix */)
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements (base.InternalIterator).SeekPrefixGE.
|
||||
@@ -284,43 +284,113 @@ func (i *InterleavingIter) SeekGE(key []byte, flags base.SeekGEFlags) (*base.Int
|
||||
// to the span returned by Span.
|
||||
//
|
||||
// NB: In accordance with the base.InternalIterator contract:
|
||||
// i.lower ≤ key
|
||||
//
|
||||
// i.lower ≤ key
|
||||
func (i *InterleavingIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.clearMask()
|
||||
i.pointKey, i.pointVal = i.pointIter.SeekPrefixGE(prefix, key, flags)
|
||||
i.pointKeyInterleaved = false
|
||||
i.keyspanSeekGE(key)
|
||||
i.prefix = true
|
||||
|
||||
// We need to seek the keyspan iterator too. If the keyspan iterator was
|
||||
// already positioned at a span, we might be able to avoid the seek if the
|
||||
// entire seek prefix key falls within the existing span's bounds.
|
||||
//
|
||||
// During a SeekPrefixGE, Pebble defragments range keys within the bounds of
|
||||
// the prefix. For example, a SeekPrefixGE('c', 'c@8') must defragment the
|
||||
// any overlapping range keys within the bounds of [c,c\00).
|
||||
//
|
||||
// If range keys are fragmented within a prefix (eg, because a version
|
||||
// within a prefix was chosen as an sstable boundary), then it's possible
|
||||
// the seek key falls into the current i.span, but the current i.span does
|
||||
// not wholly cover the seek prefix.
|
||||
//
|
||||
// For example, a SeekPrefixGE('d@5') may only defragment a range key to
|
||||
// the bounds of [c@2,e). A subsequent SeekPrefixGE('c@0') must re-seek the
|
||||
// keyspan iterator, because although 'c@0' is contained within [c@2,e), the
|
||||
// full span of the prefix is not.
|
||||
//
|
||||
// Similarly, a SeekPrefixGE('a@3') may only defragment a range key to the
|
||||
// bounds [a,c@8). A subsequent SeekPrefixGE('c@10') must re-seek the
|
||||
// keyspan iterator, because although 'c@10' is contained within [a,c@8),
|
||||
// the full span of the prefix is not.
|
||||
seekKeyspanIter := true
|
||||
if i.span != nil && i.cmp(prefix, i.span.Start) >= 0 {
|
||||
if ei := i.comparer.Split(i.span.End); i.cmp(prefix, i.span.End[:ei]) < 0 {
|
||||
// We're seeking within the existing span's bounds. We still might need
|
||||
// truncate the span to the iterator's bounds.
|
||||
i.checkForwardBound(prefix)
|
||||
i.savedKeyspan()
|
||||
seekKeyspanIter = false
|
||||
}
|
||||
}
|
||||
if seekKeyspanIter {
|
||||
i.keyspanSeekGE(key, prefix)
|
||||
}
|
||||
|
||||
i.dir = +1
|
||||
return i.interleaveForward(key)
|
||||
return i.interleaveForward(key, prefix)
|
||||
}
|
||||
|
||||
// SeekLT implements (base.InternalIterator).SeekLT.
|
||||
func (i *InterleavingIter) SeekLT(key []byte, flags base.SeekLTFlags) (*base.InternalKey, []byte) {
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
func (i *InterleavingIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.clearMask()
|
||||
i.disablePrefixMode()
|
||||
i.pointKey, i.pointVal = i.pointIter.SeekLT(key, flags)
|
||||
i.pointKeyInterleaved = false
|
||||
i.keyspanSeekLT(key)
|
||||
|
||||
// We need to seek the keyspan iterator too. If the keyspan iterator was
|
||||
// already positioned at a span, we might be able to avoid the seek if the
|
||||
// seek key falls within the existing span's bounds.
|
||||
if i.span != nil && i.cmp(key, i.span.Start) > 0 && i.cmp(key, i.span.End) < 0 {
|
||||
// We're seeking within the existing span's bounds. We still might need
|
||||
// truncate the span to the iterator's bounds.
|
||||
i.checkBackwardBound()
|
||||
// The span's start key is still not guaranteed to be less than key,
|
||||
// because of the bounds enforcement. Consider the following example:
|
||||
//
|
||||
// Bounds are set to [d,e). The user performs a SeekLT(d). The
|
||||
// FragmentIterator.SeekLT lands on a span [b,f). This span has a start
|
||||
// key less than d, as expected. Above, checkBackwardBound truncates the
|
||||
// span to match the iterator's current bounds, modifying the span to
|
||||
// [d,e), which does not overlap the search space of [-∞, d).
|
||||
//
|
||||
// This problem is a consequence of the SeekLT's exclusive search key
|
||||
// and the fact that we don't perform bounds truncation at every leaf
|
||||
// iterator.
|
||||
if i.span != nil && i.truncated && i.cmp(i.truncatedSpan.Start, key) >= 0 {
|
||||
i.span = nil
|
||||
}
|
||||
i.savedKeyspan()
|
||||
} else {
|
||||
i.keyspanSeekLT(key)
|
||||
}
|
||||
|
||||
i.dir = -1
|
||||
return i.interleaveBackward()
|
||||
}
|
||||
|
||||
// First implements (base.InternalIterator).First.
|
||||
func (i *InterleavingIter) First() (*base.InternalKey, []byte) {
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
func (i *InterleavingIter) First() (*base.InternalKey, base.LazyValue) {
|
||||
i.clearMask()
|
||||
i.disablePrefixMode()
|
||||
i.pointKey, i.pointVal = i.pointIter.First()
|
||||
i.pointKeyInterleaved = false
|
||||
i.span = i.keyspanIter.First()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(nil /* prefix */)
|
||||
i.savedKeyspan()
|
||||
i.dir = +1
|
||||
return i.interleaveForward(i.lower)
|
||||
return i.interleaveForward(i.lower, nil /* prefix */)
|
||||
}
|
||||
|
||||
// Last implements (base.InternalIterator).Last.
|
||||
func (i *InterleavingIter) Last() (*base.InternalKey, []byte) {
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
func (i *InterleavingIter) Last() (*base.InternalKey, base.LazyValue) {
|
||||
i.clearMask()
|
||||
i.disablePrefixMode()
|
||||
i.pointKey, i.pointVal = i.pointIter.Last()
|
||||
i.pointKeyInterleaved = false
|
||||
i.span = i.keyspanIter.Last()
|
||||
@@ -331,11 +401,18 @@ func (i *InterleavingIter) Last() (*base.InternalKey, []byte) {
|
||||
}
|
||||
|
||||
// Next implements (base.InternalIterator).Next.
|
||||
func (i *InterleavingIter) Next() (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) Next() (*base.InternalKey, base.LazyValue) {
|
||||
if i.dir == -1 {
|
||||
// Switching directions.
|
||||
i.dir = +1
|
||||
|
||||
if i.mask != nil {
|
||||
// Clear the mask while we reposition the point iterator. While
|
||||
// switching directions, we may move the point iterator outside of
|
||||
// i.span's bounds.
|
||||
i.clearMask()
|
||||
}
|
||||
|
||||
// The existing point key (denoted below with *) is either the last
|
||||
// key we returned (the current iterator position):
|
||||
// points: x (y*) z
|
||||
@@ -358,7 +435,7 @@ func (i *InterleavingIter) Next() (*base.InternalKey, []byte) {
|
||||
// There was no span in the reverse direction, but there may be
|
||||
// a span in the forward direction.
|
||||
i.span = i.keyspanIter.Next()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(nil /* prefix */)
|
||||
i.savedKeyspan()
|
||||
} else {
|
||||
// Regardless of the current iterator state, we mark any existing
|
||||
@@ -396,18 +473,25 @@ func (i *InterleavingIter) Next() (*base.InternalKey, []byte) {
|
||||
if i.keyspanInterleaved && i.pointKey != nil && i.span != nil &&
|
||||
i.cmp(i.pointKey.UserKey, i.span.End) >= 0 {
|
||||
i.span = i.keyspanIter.Next()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(nil /* prefix */)
|
||||
i.savedKeyspan()
|
||||
}
|
||||
return i.interleaveForward(i.lower)
|
||||
return i.interleaveForward(i.lower, nil /* prefix */)
|
||||
}
|
||||
|
||||
// Prev implements (base.InternalIterator).Prev.
|
||||
func (i *InterleavingIter) Prev() (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) Prev() (*base.InternalKey, base.LazyValue) {
|
||||
if i.dir == +1 {
|
||||
// Switching directions.
|
||||
i.dir = -1
|
||||
|
||||
if i.mask != nil {
|
||||
// Clear the mask while we reposition the point iterator. While
|
||||
// switching directions, we may move the point iterator outside of
|
||||
// i.span's bounds.
|
||||
i.clearMask()
|
||||
}
|
||||
|
||||
if i.keyspanInterleaved {
|
||||
// The current span's start key has already been interleaved in the
|
||||
// forward direction. The start key may have been interleaved a
|
||||
@@ -490,7 +574,9 @@ func (i *InterleavingIter) Prev() (*base.InternalKey, []byte) {
|
||||
return i.interleaveBackward()
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) interleaveForward(lowerBound []byte) (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) interleaveForward(
|
||||
lowerBound []byte, prefix []byte,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
// This loop determines whether a point key or a span marker key should be
|
||||
// interleaved on each iteration. If masking is disabled and the span is
|
||||
// nonempty, this loop executes for exactly one iteration. If masking is
|
||||
@@ -536,7 +622,7 @@ func (i *InterleavingIter) interleaveForward(lowerBound []byte) (*base.InternalK
|
||||
// advancing past the current point key.
|
||||
if i.keyspanInterleaved {
|
||||
i.span = i.keyspanIter.Next()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(prefix)
|
||||
i.savedKeyspan()
|
||||
if i.span == nil {
|
||||
return i.yieldNil()
|
||||
@@ -558,7 +644,7 @@ func (i *InterleavingIter) interleaveForward(lowerBound []byte) (*base.InternalK
|
||||
// keyspanInterleaved would likely trip up the invariant check
|
||||
// above.
|
||||
i.span = i.keyspanIter.Next()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(prefix)
|
||||
i.savedKeyspan()
|
||||
} else {
|
||||
i.keyspanInterleaved = true
|
||||
@@ -600,7 +686,7 @@ func (i *InterleavingIter) interleaveForward(lowerBound []byte) (*base.InternalK
|
||||
// reestablish it before the next iteration.
|
||||
if i.pointKey != nil && i.cmp(i.pointKey.UserKey, i.span.End) >= 0 {
|
||||
i.span = i.keyspanIter.Next()
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(prefix)
|
||||
i.savedKeyspan()
|
||||
}
|
||||
continue
|
||||
@@ -614,7 +700,7 @@ func (i *InterleavingIter) interleaveForward(lowerBound []byte) (*base.InternalK
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) interleaveBackward() (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) interleaveBackward() (*base.InternalKey, base.LazyValue) {
|
||||
// This loop determines whether a point key or a span's start key should be
|
||||
// interleaved on each iteration. If masking is disabled and the span is
|
||||
// nonempty, this loop executes for exactly one iteration. If masking is
|
||||
@@ -697,7 +783,7 @@ func (i *InterleavingIter) interleaveBackward() (*base.InternalKey, []byte) {
|
||||
// keyspanSeekGE seeks the keyspan iterator to the first span covering k ≥ key.
|
||||
// Note that this differs from the FragmentIterator.SeekGE semantics, which
|
||||
// seek to the first span with a start key ≥ key.
|
||||
func (i *InterleavingIter) keyspanSeekGE(key []byte) {
|
||||
func (i *InterleavingIter) keyspanSeekGE(key []byte, prefix []byte) {
|
||||
// Seek using SeekLT to look for a span that starts before key, with an end
|
||||
// boundary extending beyond key.
|
||||
i.span = i.keyspanIter.SeekLT(key)
|
||||
@@ -706,7 +792,7 @@ func (i *InterleavingIter) keyspanSeekGE(key []byte) {
|
||||
// found ends before key. Next to the first key with a start ≥ key.
|
||||
i.span = i.keyspanIter.Next()
|
||||
}
|
||||
i.checkForwardBound()
|
||||
i.checkForwardBound(prefix)
|
||||
i.savedKeyspan()
|
||||
}
|
||||
|
||||
@@ -733,7 +819,7 @@ func (i *InterleavingIter) keyspanSeekLT(key []byte) {
|
||||
i.savedKeyspan()
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) checkForwardBound() {
|
||||
func (i *InterleavingIter) checkForwardBound(prefix []byte) {
|
||||
i.truncated = false
|
||||
i.truncatedSpan = Span{}
|
||||
if i.span == nil {
|
||||
@@ -765,7 +851,23 @@ func (i *InterleavingIter) checkForwardBound() {
|
||||
}
|
||||
i.truncatedSpan.End = i.upper
|
||||
}
|
||||
if i.truncated && i.cmp(i.truncatedSpan.Start, i.truncatedSpan.End) == 0 {
|
||||
// If this is a part of a SeekPrefixGE call, we may also need to truncate to
|
||||
// the prefix's bounds.
|
||||
if prefix != nil {
|
||||
if !i.truncated {
|
||||
i.truncated = true
|
||||
i.truncatedSpan = *i.span
|
||||
}
|
||||
if i.cmp(prefix, i.truncatedSpan.Start) > 0 {
|
||||
i.truncatedSpan.Start = prefix
|
||||
}
|
||||
i.nextPrefixBuf = i.comparer.ImmediateSuccessor(i.nextPrefixBuf[:0], prefix)
|
||||
if i.truncated && i.cmp(i.nextPrefixBuf, i.truncatedSpan.End) < 0 {
|
||||
i.truncatedSpan.End = i.nextPrefixBuf
|
||||
}
|
||||
}
|
||||
|
||||
if i.truncated && i.comparer.Equal(i.truncatedSpan.Start, i.truncatedSpan.End) {
|
||||
i.span = nil
|
||||
}
|
||||
}
|
||||
@@ -802,25 +904,27 @@ func (i *InterleavingIter) checkBackwardBound() {
|
||||
}
|
||||
i.truncatedSpan.End = i.upper
|
||||
}
|
||||
if i.truncated && i.cmp(i.truncatedSpan.Start, i.truncatedSpan.End) == 0 {
|
||||
if i.truncated && i.comparer.Equal(i.truncatedSpan.Start, i.truncatedSpan.End) {
|
||||
i.span = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) yieldNil() (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) yieldNil() (*base.InternalKey, base.LazyValue) {
|
||||
i.spanCoversKey = false
|
||||
i.maybeUpdateMask(false /* covered */)
|
||||
return i.verify(nil, nil)
|
||||
i.clearMask()
|
||||
return i.verify(nil, base.LazyValue{})
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) yieldPointKey(covered bool) (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) yieldPointKey(covered bool) (*base.InternalKey, base.LazyValue) {
|
||||
i.pointKeyInterleaved = true
|
||||
i.spanCoversKey = covered
|
||||
i.maybeUpdateMask(covered)
|
||||
return i.verify(i.pointKey, i.pointVal)
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) yieldSyntheticSpanMarker(lowerBound []byte) (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) yieldSyntheticSpanMarker(
|
||||
lowerBound []byte,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
i.spanMarker.UserKey = i.startKey()
|
||||
i.spanMarker.Trailer = base.MakeTrailer(base.InternalKeySeqNumMax, i.span.Keys[0].Kind())
|
||||
i.keyspanInterleaved = true
|
||||
@@ -836,7 +940,7 @@ func (i *InterleavingIter) yieldSyntheticSpanMarker(lowerBound []byte) (*base.In
|
||||
// bound for truncating a span. The span a-z will be truncated to [k,
|
||||
// z). If i.upper == k, we'd mistakenly try to return a span [k, k), an
|
||||
// invariant violation.
|
||||
if i.cmp(lowerBound, i.upper) == 0 {
|
||||
if i.comparer.Equal(lowerBound, i.upper) {
|
||||
return i.yieldNil()
|
||||
}
|
||||
|
||||
@@ -856,10 +960,21 @@ func (i *InterleavingIter) yieldSyntheticSpanMarker(lowerBound []byte) (*base.In
|
||||
i.spanMarkerTruncated = true
|
||||
}
|
||||
i.maybeUpdateMask(true /* covered */)
|
||||
return i.verify(&i.spanMarker, nil)
|
||||
return i.verify(&i.spanMarker, base.LazyValue{})
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) verify(k *base.InternalKey, v []byte) (*base.InternalKey, []byte) {
|
||||
func (i *InterleavingIter) disablePrefixMode() {
|
||||
if i.prefix {
|
||||
i.prefix = false
|
||||
// Clear the existing span. It may not hold the true end bound of the
|
||||
// underlying span.
|
||||
i.span = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InterleavingIter) verify(
|
||||
k *base.InternalKey, v base.LazyValue,
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
// Wrap the entire function body in the invariants build tag, so that
|
||||
// production builds elide this entire function.
|
||||
if invariants.Enabled {
|
||||
@@ -893,38 +1008,32 @@ func (i *InterleavingIter) verify(k *base.InternalKey, v []byte) (*base.Internal
|
||||
func (i *InterleavingIter) savedKeyspan() {
|
||||
i.keyspanInterleaved = false
|
||||
i.spanMarkerTruncated = false
|
||||
if i.maskState == maskCurrent {
|
||||
// There's a span set as current. If the keyspan iterator was
|
||||
// exhausted or moved to an empty keyspan, the current mask will need to be
|
||||
// cleared. If the keyspan iterator was moved to a new keyspan, then we'll need
|
||||
// to set the new mask once we step into the new keyspan's bounds.
|
||||
//
|
||||
// Either way, we need to reset to to `maskStale`.
|
||||
i.maskState = maskStale
|
||||
}
|
||||
i.maskSpanChangedCalled = false
|
||||
}
|
||||
|
||||
// maybeUpdateMask updates the current mask, if a mask is configured and
|
||||
// the mask hasn't been updated with the current keyspan yet.
|
||||
func (i *InterleavingIter) maybeUpdateMask(covered bool) {
|
||||
if i.mask == nil {
|
||||
return
|
||||
if i.mask != nil {
|
||||
if !covered || i.span.Empty() {
|
||||
i.clearMask()
|
||||
} else if !i.maskSpanChangedCalled {
|
||||
if i.truncated {
|
||||
i.mask.SpanChanged(&i.truncatedSpan)
|
||||
} else {
|
||||
i.mask.SpanChanged(i.span)
|
||||
}
|
||||
i.maskSpanChangedCalled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Although covered=true, the covering span might be empty. Empty span's
|
||||
// don't mask.
|
||||
covered = covered && !i.span.Empty()
|
||||
|
||||
if covered && i.maskState != maskCurrent {
|
||||
// We're currently positioned within the bounds of i.span, and we
|
||||
// haven't updated the mask to this span yet. Update it now.
|
||||
i.mask.SpanChanged(i.span)
|
||||
i.maskState = maskCurrent
|
||||
} else if !covered && i.maskState != maskCleared {
|
||||
// We're not positioned beneath i.span, so we shouldn't set the mask to
|
||||
// i.span. However, we've never cleared the previous mask.
|
||||
// clearMask clears the current mask, if a mask is configured and no mask should
|
||||
// be active.
|
||||
func (i *InterleavingIter) clearMask() {
|
||||
if i.mask != nil {
|
||||
i.maskSpanChangedCalled = false
|
||||
i.mask.SpanChanged(nil)
|
||||
i.maskState = maskCleared
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,6 +1064,16 @@ func (i *InterleavingIter) Span() *Span {
|
||||
func (i *InterleavingIter) SetBounds(lower, upper []byte) {
|
||||
i.lower, i.upper = lower, upper
|
||||
i.pointIter.SetBounds(lower, upper)
|
||||
i.Invalidate()
|
||||
}
|
||||
|
||||
// Invalidate invalidates the interleaving iterator's current position, clearing
|
||||
// its state. This prevents optimizations such as reusing the current span on
|
||||
// seek.
|
||||
func (i *InterleavingIter) Invalidate() {
|
||||
i.span = nil
|
||||
i.pointKey = nil
|
||||
i.pointVal = base.LazyValue{}
|
||||
}
|
||||
|
||||
// Error implements (base.InternalIterator).Error.
|
||||
@@ -974,18 +1093,6 @@ func (i *InterleavingIter) String() string {
|
||||
return fmt.Sprintf("keyspan-interleaving(%q)", i.pointIter.String())
|
||||
}
|
||||
|
||||
var _ base.InternalIteratorWithStats = &InterleavingIter{}
|
||||
|
||||
// Stats implements InternalIteratorWithStats.
|
||||
func (i *InterleavingIter) Stats() base.InternalIteratorStats {
|
||||
return i.pointIter.Stats()
|
||||
}
|
||||
|
||||
// ResetStats implements InternalIteratorWithStats.
|
||||
func (i *InterleavingIter) ResetStats() {
|
||||
i.pointIter.ResetStats()
|
||||
}
|
||||
|
||||
func firstError(err0, err1 error) error {
|
||||
if err0 != nil {
|
||||
return err0
|
||||
|
||||
22
vendor/github.com/cockroachdb/pebble/internal/keyspan/internal_iter_shim.go
generated
vendored
22
vendor/github.com/cockroachdb/pebble/internal/keyspan/internal_iter_shim.go
generated
vendored
@@ -38,57 +38,57 @@ func (i *InternalIteratorShim) Span() *Span {
|
||||
// SeekGE implements (base.InternalIterator).SeekGE.
|
||||
func (i *InternalIteratorShim) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SeekPrefixGE implements (base.InternalIterator).SeekPrefixGE.
|
||||
func (i *InternalIteratorShim) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SeekLT implements (base.InternalIterator).SeekLT.
|
||||
func (i *InternalIteratorShim) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*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 != nil && i.span.Empty() {
|
||||
i.span = i.miter.Next()
|
||||
}
|
||||
if i.span == nil {
|
||||
return nil, 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 != nil && i.span.Empty() {
|
||||
i.span = i.miter.Next()
|
||||
}
|
||||
if i.span == nil {
|
||||
return nil, 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")
|
||||
}
|
||||
|
||||
|
||||
31
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
31
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
@@ -52,7 +52,11 @@ func visibleTransform(snapshot uint64) Transformer {
|
||||
dst.Start, dst.End = s.Start, s.End
|
||||
dst.Keys = dst.Keys[:0]
|
||||
for _, k := range s.Keys {
|
||||
if base.Visible(k.SeqNum(), snapshot) {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -68,6 +72,10 @@ func visibleTransform(snapshot uint64) Transformer {
|
||||
// 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
|
||||
@@ -210,9 +218,9 @@ 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.
|
||||
@@ -809,15 +817,22 @@ 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.
|
||||
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.transformer.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, nil
|
||||
}
|
||||
|
||||
90
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
90
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
@@ -31,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.
|
||||
@@ -69,9 +83,9 @@ func (k Key) Kind() base.InternalKeyKind {
|
||||
// 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(cmp base.Compare, b Key) bool {
|
||||
func (k Key) Equal(equal base.Equal, b Key) bool {
|
||||
return k.Trailer == b.Trailer &&
|
||||
cmp(k.Suffix, b.Suffix) == 0 &&
|
||||
equal(k.Suffix, b.Suffix) &&
|
||||
bytes.Equal(k.Value, b.Value)
|
||||
}
|
||||
|
||||
@@ -90,10 +104,13 @@ func (s *Span) Empty() bool {
|
||||
}
|
||||
|
||||
// SmallestKey returns the smallest internal key defined by the span's keys.
|
||||
// It panics if the span contains no keys.
|
||||
// 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{
|
||||
@@ -108,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.
|
||||
// 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()
|
||||
@@ -119,19 +139,26 @@ 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.
|
||||
// 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.
|
||||
// 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()
|
||||
}
|
||||
@@ -140,11 +167,16 @@ func (s *Span) LargestSeqNum() uint64 {
|
||||
// that avoid the need to construct a new Span.
|
||||
|
||||
// Visible returns a span with the subset of keys visible at the provided
|
||||
// sequence number.
|
||||
// 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
|
||||
@@ -214,7 +246,13 @@ func (s Span) Visible(snapshot uint64) Span {
|
||||
// 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 {
|
||||
@@ -235,9 +273,10 @@ func (s *Span) VisibleAt(snapshot uint64) bool {
|
||||
// None of the key byte slices are cloned (see Span.DeepClone).
|
||||
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
|
||||
@@ -248,9 +287,10 @@ func (s *Span) ShallowClone() Span {
|
||||
// because it is allocation heavy.
|
||||
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)
|
||||
@@ -274,7 +314,13 @@ func (s *Span) Contains(cmp base.Compare, key []byte) bool {
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -284,7 +330,13 @@ func (s Span) Covers(seqNum uint64) bool {
|
||||
//
|
||||
// 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 {
|
||||
@@ -336,8 +388,8 @@ func (s prettySpan) Format(fs fmt.State, c rune) {
|
||||
fmt.Fprintf(fs, "}")
|
||||
}
|
||||
|
||||
// SortKeys sorts a keys slice by trailer.
|
||||
func SortKeys(keys *[]Key) {
|
||||
// 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)
|
||||
|
||||
22
vendor/github.com/cockroachdb/pebble/internal/manifest/l0_sublevels.go
generated
vendored
22
vendor/github.com/cockroachdb/pebble/internal/manifest/l0_sublevels.go
generated
vendored
@@ -724,7 +724,7 @@ func (s *L0Sublevels) InitCompactingFileInfo(inProgress []L0Compaction) {
|
||||
s.formatKey(s.orderedIntervals[f.maxIntervalIndex+1].startKey.key), s.formatKey(f.Smallest.UserKey)))
|
||||
}
|
||||
}
|
||||
if !f.Compacting {
|
||||
if !f.IsCompacting() {
|
||||
continue
|
||||
}
|
||||
for i := f.minIntervalIndex; i <= f.maxIntervalIndex; i++ {
|
||||
@@ -805,7 +805,7 @@ func (s *L0Sublevels) describe(verbose bool) string {
|
||||
}
|
||||
sumIntervals += intervals
|
||||
totalBytes += f.Size
|
||||
if f.Compacting {
|
||||
if f.IsCompacting() {
|
||||
numCompactingFiles++
|
||||
}
|
||||
}
|
||||
@@ -1052,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",
|
||||
@@ -1396,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
|
||||
@@ -1428,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
|
||||
@@ -1549,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
|
||||
@@ -1600,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)
|
||||
@@ -1624,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
|
||||
@@ -1676,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++
|
||||
@@ -1946,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
|
||||
@@ -1998,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))
|
||||
|
||||
119
vendor/github.com/cockroachdb/pebble/internal/manifest/version.go
generated
vendored
119
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
|
||||
@@ -183,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
|
||||
@@ -794,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
|
||||
}
|
||||
|
||||
|
||||
147
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
147
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
@@ -18,33 +18,36 @@ import (
|
||||
// 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
|
||||
defragBufA keysBySuffix
|
||||
defragBufB keysBySuffix
|
||||
// defragBufAlloc defines two arrays used to preallocate defragBuf{A,B} keys
|
||||
// slices to prevent additional allocations during iteration. Each array
|
||||
// preallocates up to two keys.
|
||||
defragBufAlloc [2][2]keyspan.Key
|
||||
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.
|
||||
// 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 (ui *UserIteratorConfig) Init(
|
||||
cmp base.Compare, snapshot uint64, iters ...keyspan.FragmentIterator,
|
||||
comparer *base.Comparer,
|
||||
snapshot uint64,
|
||||
lower, upper []byte,
|
||||
hasPrefix *bool,
|
||||
prefix *[]byte,
|
||||
iters ...keyspan.FragmentIterator,
|
||||
) keyspan.FragmentIterator {
|
||||
ui.snapshot = snapshot
|
||||
ui.defragBufA.keys = ui.defragBufAlloc[0][:0]
|
||||
ui.defragBufB.keys = ui.defragBufAlloc[1][:0]
|
||||
ui.miter.Init(cmp, ui, iters...)
|
||||
ui.diter.Init(cmp, &ui.miter, ui, keyspan.StaticDefragmentReducer)
|
||||
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
|
||||
}
|
||||
@@ -66,21 +69,31 @@ func (ui *UserIteratorConfig) NewLevelIter() *keyspan.LevelIter {
|
||||
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.
|
||||
// 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
|
||||
if err := Coalesce(cmp, s.Visible(ui.snapshot).Keys, &dst.Keys); err != nil {
|
||||
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.
|
||||
@@ -89,8 +102,14 @@ func (ui *UserIteratorConfig) Transform(cmp base.Compare, s keyspan.Span, dst *k
|
||||
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:
|
||||
@@ -100,6 +119,8 @@ func (ui *UserIteratorConfig) Transform(cmp base.Compare, s keyspan.Span, dst *k
|
||||
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
|
||||
}
|
||||
|
||||
@@ -107,50 +128,45 @@ func (ui *UserIteratorConfig) Transform(cmp base.Compare, s keyspan.Span, dst *k
|
||||
// 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 (UserIterationConfig).Transform, 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.
|
||||
//
|
||||
// This implementation is stateful, and must not be used on multiple
|
||||
// DefragmentingIters concurrently.
|
||||
func (ui *UserIteratorConfig) ShouldDefragment(cmp base.Compare, a, b *keyspan.Span) bool {
|
||||
// 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 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.
|
||||
// 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.
|
||||
ui.defragBufA.cmp = cmp
|
||||
ui.defragBufA.keys = append(ui.defragBufA.keys[:0], a.Keys...)
|
||||
sort.Sort(&ui.defragBufA)
|
||||
ui.defragBufB.cmp = cmp
|
||||
ui.defragBufB.keys = append(ui.defragBufB.keys[:0], b.Keys...)
|
||||
sort.Sort(&ui.defragBufB)
|
||||
|
||||
for i := range ui.defragBufA.keys {
|
||||
if ui.defragBufA.keys[i].Kind() != base.InternalKeyKindRangeKeySet ||
|
||||
ui.defragBufB.keys[i].Kind() != base.InternalKeyKindRangeKeySet {
|
||||
panic("pebble: unexpected non-RangeKeySet during defragmentation")
|
||||
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 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")
|
||||
}
|
||||
}
|
||||
if cmp(ui.defragBufA.keys[i].Suffix, ui.defragBufB.keys[i].Suffix) != 0 {
|
||||
return false
|
||||
if !equal(a.Keys[i].Suffix, b.Keys[i].Suffix) {
|
||||
ret = false
|
||||
break
|
||||
}
|
||||
if !bytes.Equal(ui.defragBufA.keys[i].Value, ui.defragBufB.keys[i].Value) {
|
||||
return false
|
||||
if !bytes.Equal(a.Keys[i].Value, b.Keys[i].Value) {
|
||||
ret = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
return ret
|
||||
}
|
||||
|
||||
// Coalesce imposes range key semantics and coalesces range keys with the same
|
||||
@@ -186,14 +202,24 @@ func (ui *UserIteratorConfig) ShouldDefragment(cmp base.Compare, a, b *keyspan.S
|
||||
// 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.
|
||||
//
|
||||
// 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)[: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(keys) && !deleted; i++ {
|
||||
k := keys[i]
|
||||
@@ -215,7 +241,7 @@ func Coalesce(cmp base.Compare, keys []keyspan.Key, dst *[]keyspan.Key) error {
|
||||
continue
|
||||
}
|
||||
keysBySuffix.keys = append(keysBySuffix.keys, k)
|
||||
sort.Sort(&keysBySuffix)
|
||||
sort.Sort(keysBySuffix)
|
||||
case base.InternalKeyKindRangeKeyUnset:
|
||||
n := len(keysBySuffix.keys)
|
||||
|
||||
@@ -225,7 +251,7 @@ func Coalesce(cmp base.Compare, keys []keyspan.Key, dst *[]keyspan.Key) error {
|
||||
continue
|
||||
}
|
||||
keysBySuffix.keys = append(keysBySuffix.keys, k)
|
||||
sort.Sort(&keysBySuffix)
|
||||
sort.Sort(keysBySuffix)
|
||||
case base.InternalKeyKindRangeKeyDelete:
|
||||
// All remaining range keys in this span have been deleted by this
|
||||
// RangeKeyDelete. There's no need to continue looping, because all
|
||||
@@ -239,22 +265,13 @@ func Coalesce(cmp base.Compare, keys []keyspan.Key, dst *[]keyspan.Key) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Update the span with the (potentially reduced) keys slice, and re-sort it
|
||||
// by Trailer.
|
||||
// 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
|
||||
keyspan.SortKeys(dst)
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
812
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
812
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
File diff suppressed because it is too large
Load Diff
21
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
21
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
@@ -52,7 +52,7 @@ type simpleMergingIterLevel struct {
|
||||
levelIterBoundaryContext
|
||||
|
||||
iterKey *InternalKey
|
||||
iterValue []byte
|
||||
iterValue base.LazyValue
|
||||
tombstone *keyspan.Span
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
@@ -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
|
||||
}
|
||||
@@ -634,7 +639,7 @@ 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.initBoundaryContext(&mlevelAlloc[0].levelIterBoundaryContext)
|
||||
mlevelAlloc[0].iter = li
|
||||
@@ -648,7 +653,7 @@ 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.initBoundaryContext(&mlevelAlloc[0].levelIterBoundaryContext)
|
||||
mlevelAlloc[0].iter = li
|
||||
|
||||
223
vendor/github.com/cockroachdb/pebble/level_iter.go
generated
vendored
223
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
|
||||
@@ -81,7 +91,7 @@ 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
|
||||
// filteredIter is an optional interface that may be implemented by internal
|
||||
// iterators that perform filtering of keys. When a new file's iterator is
|
||||
@@ -107,8 +117,6 @@ 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::levelIterBoundaryContext`.
|
||||
// We populate it with the corresponding bounds for the currently opened file. It is used for
|
||||
@@ -158,8 +166,9 @@ type levelIter struct {
|
||||
// - `*Boundary` is not exposed to the next higher-level iterator, i.e., `mergingIter`.
|
||||
boundaryContext *levelIterBoundaryContext
|
||||
|
||||
// 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
|
||||
@@ -205,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
|
||||
}
|
||||
|
||||
@@ -216,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
|
||||
@@ -232,7 +241,7 @@ func (l *levelIter) init(
|
||||
l.iterFile = nil
|
||||
l.newIters = newIters
|
||||
l.files = files
|
||||
l.bytesIterated = bytesIterated
|
||||
l.internalOpts = internalOpts
|
||||
}
|
||||
|
||||
func (l *levelIter) initRangeDel(rangeDelIter *keyspan.FragmentIterator) {
|
||||
@@ -260,9 +269,16 @@ func (l *levelIter) maybeTriggerCombinedIteration(file *fileMetadata, dir int) {
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
(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
|
||||
@@ -294,22 +310,64 @@ func (l *levelIter) maybeTriggerCombinedIteration(file *fileMetadata, dir int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *levelIter) findFileGE(key []byte, isRelativeSeek bool) *fileMetadata {
|
||||
// Find the earliest file whose largest key is >= ikey.
|
||||
func (l *levelIter) findFileGE(key []byte, flags base.SeekGEFlags) *fileMetadata {
|
||||
// Find the earliest file whose largest key is >= key.
|
||||
|
||||
// Ordinarily we seek the LevelIterator using SeekGE.
|
||||
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.
|
||||
//
|
||||
// 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 end key, we
|
||||
// need to check whether all of the files between the old position and the
|
||||
// new position contain any range keys.
|
||||
// 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.
|
||||
//
|
||||
// In this scenario, we don't seek the LevelIterator and instead we Next it,
|
||||
// one file at a time, checking each for range keys.
|
||||
nextInsteadOfSeek := isRelativeSeek && l.combinedIterState != nil && !l.combinedIterState.initialized
|
||||
// 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 {
|
||||
@@ -349,11 +407,22 @@ func (l *levelIter) findFileGE(key []byte, isRelativeSeek bool) *fileMetadata {
|
||||
// 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 point key bound ≥ `key`. But the largest point key
|
||||
// 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
|
||||
@@ -374,7 +443,7 @@ func (l *levelIter) findFileGE(key []byte, isRelativeSeek bool) *fileMetadata {
|
||||
return m
|
||||
}
|
||||
|
||||
func (l *levelIter) findFileLT(key []byte, isRelativeSeek bool) *fileMetadata {
|
||||
func (l *levelIter) findFileLT(key []byte, flags base.SeekLTFlags) *fileMetadata {
|
||||
// Find the last file whose smallest key is < ikey.
|
||||
|
||||
// Ordinarily we seek the LevelIterator using SeekLT.
|
||||
@@ -389,7 +458,7 @@ func (l *levelIter) findFileLT(key []byte, isRelativeSeek bool) *fileMetadata {
|
||||
//
|
||||
// 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 := isRelativeSeek && l.combinedIterState != nil && !l.combinedIterState.initialized
|
||||
prevInsteadOfSeek := flags.RelativeSeek() && l.combinedIterState != nil && !l.combinedIterState.initialized
|
||||
|
||||
var m *fileMetadata
|
||||
if prevInsteadOfSeek {
|
||||
@@ -563,8 +632,8 @@ 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
|
||||
}
|
||||
@@ -594,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.
|
||||
@@ -612,7 +681,7 @@ func (l *levelIter) verify(key *InternalKey, val []byte) (*InternalKey, []byte)
|
||||
return key, val
|
||||
}
|
||||
|
||||
func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []byte) {
|
||||
func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -621,9 +690,9 @@ func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.LowerBound.
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key, flags.RelativeSeek()), +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
|
||||
@@ -638,7 +707,7 @@ func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, []
|
||||
|
||||
func (l *levelIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -647,9 +716,9 @@ func (l *levelIter) SeekPrefixGE(
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.LowerBound.
|
||||
loadFileIndicator := l.loadFile(l.findFileGE(key, flags.RelativeSeek()), +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
|
||||
@@ -673,7 +742,7 @@ func (l *levelIter) SeekPrefixGE(
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = true
|
||||
l.boundaryContext.isIgnorableBoundaryKey = false
|
||||
}
|
||||
return l.verify(l.largestBoundary, nil)
|
||||
return l.verify(l.largestBoundary, base.LazyValue{})
|
||||
}
|
||||
// Return the file's largest bound, ensuring this file stays open until
|
||||
// the mergingIter advances beyond the file's bounds. We set
|
||||
@@ -684,7 +753,7 @@ func (l *levelIter) SeekPrefixGE(
|
||||
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
|
||||
@@ -695,12 +764,12 @@ func (l *levelIter) SeekPrefixGE(
|
||||
// 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.LargestPointKey.UserKey); l.cmp(prefix, l.iterFile.LargestPointKey.UserKey[:n]) < 0 {
|
||||
return nil, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
return l.verify(l.skipEmptyFileForward())
|
||||
}
|
||||
|
||||
func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []byte) {
|
||||
func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||
l.err = nil // clear cached iteration error
|
||||
if l.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -709,8 +778,8 @@ func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []
|
||||
|
||||
// NB: the top-level Iterator has already adjusted key based on
|
||||
// IterOptions.UpperBound.
|
||||
if l.loadFile(l.findFileLT(key, flags.RelativeSeek()), -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, flags); key != nil {
|
||||
return l.verify(key, val)
|
||||
@@ -718,7 +787,7 @@ func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []
|
||||
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.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -728,7 +797,7 @@ func (l *levelIter) First() (*InternalKey, []byte) {
|
||||
// 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)
|
||||
@@ -736,7 +805,7 @@ 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.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -746,7 +815,7 @@ func (l *levelIter) Last() (*InternalKey, []byte) {
|
||||
// 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)
|
||||
@@ -754,9 +823,9 @@ 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.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -774,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 {
|
||||
@@ -783,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.
|
||||
@@ -795,9 +864,9 @@ 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.boundaryContext != nil {
|
||||
l.boundaryContext.isSyntheticIterBoundsKey = false
|
||||
@@ -815,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 {
|
||||
@@ -824,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.
|
||||
@@ -836,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
|
||||
@@ -875,18 +944,18 @@ func (l *levelIter) skipEmptyFileForward() (*InternalKey, []byte) {
|
||||
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.LargestPointKey.Kind() == InternalKeyKindRangeDelete {
|
||||
l.largestBoundary = &l.iterFile.LargestPointKey
|
||||
return l.largestBoundary, nil
|
||||
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
|
||||
@@ -912,21 +981,21 @@ func (l *levelIter) skipEmptyFileForward() (*InternalKey, []byte) {
|
||||
l.filteredIter.MaybeFilteredKeys() {
|
||||
l.largestBoundary = &l.iterFile.Largest
|
||||
l.boundaryContext.isIgnorableBoundaryKey = true
|
||||
return l.largestBoundary, nil
|
||||
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
|
||||
@@ -962,18 +1031,18 @@ func (l *levelIter) skipEmptyFileBackward() (*InternalKey, []byte) {
|
||||
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.SmallestPointKey.Kind() == InternalKeyKindRangeDelete {
|
||||
l.smallestBoundary = &l.iterFile.SmallestPointKey
|
||||
return l.smallestBoundary, nil
|
||||
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
|
||||
@@ -998,13 +1067,13 @@ func (l *levelIter) skipEmptyFileBackward() (*InternalKey, []byte) {
|
||||
if *l.rangeDelIterPtr != nil && l.filteredIter != nil && l.filteredIter.MaybeFilteredKeys() {
|
||||
l.smallestBoundary = &l.iterFile.Smallest
|
||||
l.boundaryContext.isIgnorableBoundaryKey = true
|
||||
return l.smallestBoundary, nil
|
||||
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
|
||||
@@ -1019,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
|
||||
}
|
||||
@@ -1060,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{}
|
||||
|
||||
2
vendor/github.com/cockroachdb/pebble/mem_table.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/mem_table.go
generated
vendored
@@ -336,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)
|
||||
}
|
||||
|
||||
149
vendor/github.com/cockroachdb/pebble/merging_iter.go
generated
vendored
149
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,7 +24,7 @@ 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
|
||||
|
||||
// levelIterBoundaryContext's fields are set when using levelIter, in order
|
||||
// to surface sstable boundary keys and file-level context. See levelIter
|
||||
@@ -230,17 +230,18 @@ type levelIterBoundaryContext 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
|
||||
|
||||
@@ -264,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()
|
||||
@@ -285,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 {
|
||||
@@ -666,29 +677,48 @@ 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 already seeked the iterator due to a
|
||||
// range tombstone and are now 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.
|
||||
// For prefix iteration, stop if we've already exceeded the iterator's
|
||||
// current prefix. There are two cases where we perform this check.
|
||||
//
|
||||
// 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.
|
||||
if m.prefix != nil && reseeked {
|
||||
// 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, nil
|
||||
return nil, base.LazyValue{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,14 +728,14 @@ func (m *mergingIter) findNextEntry() (*InternalKey, []byte) {
|
||||
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.
|
||||
@@ -841,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 {
|
||||
@@ -852,14 +882,14 @@ 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.
|
||||
@@ -953,7 +983,7 @@ 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, flags base.SeekGEFlags) (*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 */, flags)
|
||||
@@ -965,7 +995,7 @@ func (m *mergingIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey,
|
||||
// that key is greater than or equal to the lower bound.
|
||||
func (m *mergingIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*base.InternalKey, base.LazyValue) {
|
||||
m.err = nil // clear cached iteration error
|
||||
m.prefix = prefix
|
||||
m.seekGE(key, 0 /* start level */, flags)
|
||||
@@ -1043,7 +1073,7 @@ func (m *mergingIter) seekLT(key []byte, level int, flags base.SeekLTFlags) {
|
||||
// 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, flags base.SeekLTFlags) (*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 */, flags)
|
||||
@@ -1053,7 +1083,7 @@ func (m *mergingIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey,
|
||||
// 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]
|
||||
@@ -1068,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 {
|
||||
@@ -1079,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 {
|
||||
@@ -1090,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])
|
||||
@@ -1182,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 {
|
||||
|
||||
64
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
64
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
@@ -6,13 +6,15 @@ package pebble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/HdrHistogram/hdrhistogram-go"
|
||||
"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/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// CacheMetrics holds metrics for the block and table cache.
|
||||
@@ -158,7 +160,8 @@ type Metrics struct {
|
||||
|
||||
Flush struct {
|
||||
// The total number of flushes.
|
||||
Count int64
|
||||
Count int64
|
||||
WriteThroughput ThroughputMetric
|
||||
}
|
||||
|
||||
Filter FilterMetrics
|
||||
@@ -178,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
|
||||
@@ -222,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 {
|
||||
@@ -424,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.
|
||||
}
|
||||
|
||||
17
vendor/github.com/cockroachdb/pebble/open.go
generated
vendored
17
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 (
|
||||
@@ -419,8 +419,15 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
||||
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)
|
||||
@@ -538,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 {
|
||||
@@ -720,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
|
||||
}
|
||||
|
||||
158
vendor/github.com/cockroachdb/pebble/options.go
generated
vendored
158
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
|
||||
@@ -505,6 +550,10 @@ 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.
|
||||
@@ -527,6 +576,12 @@ type Options struct {
|
||||
// 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
|
||||
@@ -535,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
|
||||
@@ -632,7 +699,8 @@ 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
|
||||
@@ -727,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,8 +907,8 @@ func (o *Options) EnsureDefaults() *Options {
|
||||
o.Merger = DefaultMerger
|
||||
}
|
||||
o.private.strictWALTail = true
|
||||
if o.MaxConcurrentCompactions <= 0 {
|
||||
o.MaxConcurrentCompactions = 1
|
||||
if o.MaxConcurrentCompactions == nil {
|
||||
o.MaxConcurrentCompactions = func() int { return 1 }
|
||||
}
|
||||
if o.NumPrevManifest <= 0 {
|
||||
o.NumPrevManifest = 1
|
||||
@@ -855,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
|
||||
}
|
||||
@@ -864,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
|
||||
@@ -938,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)
|
||||
@@ -947,13 +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_deletion_rate=%d\n", o.Experimental.MinDeletionRate)
|
||||
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)
|
||||
@@ -974,6 +1063,15 @@ func (o *Options) String() string {
|
||||
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]
|
||||
fmt.Fprintf(&buf, "\n")
|
||||
@@ -1111,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":
|
||||
@@ -1141,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":
|
||||
@@ -1159,6 +1273,8 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
||||
case "min_flush_rate":
|
||||
// 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":
|
||||
|
||||
390
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
390
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
@@ -9,12 +9,15 @@ import (
|
||||
"github.com/cockroachdb/pebble/internal/invariants"
|
||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||
"github.com/cockroachdb/pebble/internal/manifest"
|
||||
"github.com/cockroachdb/pebble/sstable"
|
||||
)
|
||||
|
||||
// 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.cmp, i.seqNum)
|
||||
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 i.batch != nil {
|
||||
@@ -53,8 +56,13 @@ func (i *Iterator) constructRangeKeyIter() {
|
||||
// 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.First(); f != nil; f = iter.Next() {
|
||||
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 {
|
||||
@@ -77,6 +85,302 @@ func (i *Iterator) constructRangeKeyIter() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -87,7 +391,7 @@ type lazyCombinedIter struct {
|
||||
// iterator. It's used to mutate the internalIterator in use when switching
|
||||
// to combined iteration.
|
||||
parent *Iterator
|
||||
pointIter internalIteratorWithStats
|
||||
pointIter internalIterator
|
||||
combinedIterState combinedIterState
|
||||
}
|
||||
|
||||
@@ -134,8 +438,8 @@ var _ internalIterator = (*lazyCombinedIter)(nil)
|
||||
// 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 []byte, seekKey []byte,
|
||||
) (*InternalKey, []byte) {
|
||||
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 {
|
||||
@@ -147,19 +451,6 @@ func (i *lazyCombinedIter) initCombinedIteration(
|
||||
}
|
||||
}
|
||||
|
||||
// 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.cmp, i.parent.split, &i.parent.opts)
|
||||
i.parent.constructRangeKeyIter()
|
||||
|
||||
// Initialize the Iterator's interleaving iterator.
|
||||
i.parent.rangeKey.iiter.Init(
|
||||
i.parent.cmp, i.parent.pointIter, i.parent.rangeKey.rangeKeyIter,
|
||||
i.parent.rangeKey, i.parent.opts.LowerBound, i.parent.opts.UpperBound)
|
||||
|
||||
// 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.
|
||||
@@ -205,6 +496,37 @@ func (i *lazyCombinedIter) initCombinedIteration(
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -225,12 +547,18 @@ func (i *lazyCombinedIter) initCombinedIteration(
|
||||
// which case the range key will be interleaved next instead of the point
|
||||
// key.
|
||||
if dir == +1 {
|
||||
return i.parent.rangeKey.iiter.InitSeekGE(seekKey, pointKey, pointValue)
|
||||
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, []byte) {
|
||||
func (i *lazyCombinedIter) SeekGE(
|
||||
key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekGE(key, flags)
|
||||
}
|
||||
@@ -243,7 +571,7 @@ func (i *lazyCombinedIter) SeekGE(key []byte, flags base.SeekGEFlags) (*Internal
|
||||
|
||||
func (i *lazyCombinedIter) SeekPrefixGE(
|
||||
prefix, key []byte, flags base.SeekGEFlags,
|
||||
) (*InternalKey, []byte) {
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekPrefixGE(prefix, key, flags)
|
||||
}
|
||||
@@ -254,7 +582,9 @@ func (i *lazyCombinedIter) SeekPrefixGE(
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, []byte) {
|
||||
func (i *lazyCombinedIter) SeekLT(
|
||||
key []byte, flags base.SeekLTFlags,
|
||||
) (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.SeekLT(key, flags)
|
||||
}
|
||||
@@ -265,7 +595,7 @@ func (i *lazyCombinedIter) SeekLT(key []byte, flags base.SeekLTFlags) (*Internal
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) First() (*InternalKey, []byte) {
|
||||
func (i *lazyCombinedIter) First() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.First()
|
||||
}
|
||||
@@ -276,7 +606,7 @@ func (i *lazyCombinedIter) First() (*InternalKey, []byte) {
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Last() (*InternalKey, []byte) {
|
||||
func (i *lazyCombinedIter) Last() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Last()
|
||||
}
|
||||
@@ -287,7 +617,7 @@ func (i *lazyCombinedIter) Last() (*InternalKey, []byte) {
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Next() (*InternalKey, []byte) {
|
||||
func (i *lazyCombinedIter) Next() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Next()
|
||||
}
|
||||
@@ -298,7 +628,7 @@ func (i *lazyCombinedIter) Next() (*InternalKey, []byte) {
|
||||
return k, v
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Prev() (*InternalKey, []byte) {
|
||||
func (i *lazyCombinedIter) Prev() (*InternalKey, base.LazyValue) {
|
||||
if i.combinedIterState.initialized {
|
||||
return i.parent.rangeKey.iiter.Prev()
|
||||
}
|
||||
@@ -337,11 +667,3 @@ func (i *lazyCombinedIter) String() string {
|
||||
}
|
||||
return i.pointIter.String()
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) Stats() InternalIteratorStats {
|
||||
return i.pointIter.Stats()
|
||||
}
|
||||
|
||||
func (i *lazyCombinedIter) ResetStats() {
|
||||
i.pointIter.ResetStats()
|
||||
}
|
||||
|
||||
51
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
51
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
@@ -13,10 +13,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/HdrHistogram/hdrhistogram-go"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/cockroachdb/pebble/internal/base"
|
||||
"github.com/cockroachdb/pebble/internal/crc"
|
||||
"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()
|
||||
|
||||
80
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
80
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, flags base.SeekGEFlags) (*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, flags base.SeekGEFlags) (*InternalKey, []
|
||||
// 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, flags base.SeekGEFlags,
|
||||
) (*base.InternalKey, []byte) {
|
||||
) (*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, flags base.SeekLTFlags) (*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, flags base.SeekLTFlags) (*InternalKey, []
|
||||
// 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, flags base.SeekLTFlags) (*InternalKey, []
|
||||
// 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, flags base.SeekLTFlags) (*InternalKey, []
|
||||
}
|
||||
|
||||
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,7 +1019,7 @@ 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 nil
|
||||
@@ -1028,6 +1030,7 @@ 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 nil
|
||||
@@ -1037,7 +1040,8 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
// 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 {
|
||||
@@ -1053,7 +1057,8 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, internalValue []byte)
|
||||
i.span = keyspan.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
|
||||
@@ -1067,7 +1072,7 @@ 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 nil
|
||||
@@ -1078,6 +1083,7 @@ 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 nil
|
||||
@@ -1087,7 +1093,8 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
// 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 {
|
||||
@@ -1103,13 +1110,14 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, internalValue []byte)
|
||||
i.span = keyspan.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
|
||||
}
|
||||
@@ -1171,7 +1179,8 @@ 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 (keyspan.FragmentIterator).Prev.
|
||||
@@ -1204,7 +1213,8 @@ 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 (keyspan.FragmentIterator).SeekGE.
|
||||
|
||||
255
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
255
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
@@ -61,19 +61,25 @@ import (
|
||||
// re-applying the kv filter while reading results back from Pebble.
|
||||
//
|
||||
// b) Block property filtering may surface deleted key-value pairs if the
|
||||
// the kv filter is not a strict function of the key's user key. A block
|
||||
// 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 the user key. A filter that examines values or key kinds
|
||||
// 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
|
||||
@@ -140,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.
|
||||
@@ -354,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
|
||||
@@ -387,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
|
||||
@@ -482,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{
|
||||
@@ -492,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
|
||||
}
|
||||
|
||||
@@ -546,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
|
||||
@@ -560,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
|
||||
}
|
||||
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 {
|
||||
|
||||
922
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
922
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
File diff suppressed because it is too large
Load Diff
9
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
9
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
@@ -178,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)
|
||||
}
|
||||
@@ -410,7 +411,11 @@ 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()
|
||||
|
||||
7
vendor/github.com/cockroachdb/pebble/sstable/write_queue.go
generated
vendored
7
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,
|
||||
|
||||
136
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
136
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
|
||||
}
|
||||
@@ -1082,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
|
||||
@@ -1141,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
|
||||
@@ -1815,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()
|
||||
@@ -1840,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)
|
||||
}
|
||||
|
||||
40
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
40
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
@@ -133,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(
|
||||
@@ -299,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) {
|
||||
@@ -309,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 {
|
||||
@@ -318,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)
|
||||
}
|
||||
@@ -331,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 {
|
||||
@@ -352,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
|
||||
@@ -369,7 +376,8 @@ 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)
|
||||
@@ -405,11 +413,11 @@ 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 {
|
||||
@@ -454,7 +462,7 @@ func (c *tableCacheShard) newRangeKeyIter(
|
||||
// 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)
|
||||
ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters, nil)
|
||||
}
|
||||
if err != nil {
|
||||
c.unrefValue(v)
|
||||
@@ -848,10 +856,14 @@ 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
|
||||
}
|
||||
|
||||
|
||||
103
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
103
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
@@ -53,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
|
||||
}
|
||||
@@ -126,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()
|
||||
@@ -174,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
|
||||
}
|
||||
|
||||
@@ -216,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
|
||||
}
|
||||
|
||||
@@ -270,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
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ func (d *DB) loadTablePointKeyStats(
|
||||
func (d *DB) loadTableRangeDelStats(
|
||||
r *sstable.Reader, v *version, level int, meta *fileMetadata, stats *manifest.TableStats,
|
||||
) ([]deleteCompactionHint, error) {
|
||||
iter, err := newCombinedDeletionKeyspanIter(d.cmp, r, meta)
|
||||
iter, err := newCombinedDeletionKeyspanIter(d.opts.Comparer, r, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -550,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
|
||||
}
|
||||
|
||||
@@ -657,11 +655,11 @@ func estimateEntrySizes(
|
||||
// corresponding to the largest and smallest sequence numbers encountered across
|
||||
// the range deletes and range keys deletes that comprised the merged spans.
|
||||
func newCombinedDeletionKeyspanIter(
|
||||
cmp base.Compare, r *sstable.Reader, m *fileMetadata,
|
||||
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.Compare, a, b *keyspan.Span) bool { return true })
|
||||
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.
|
||||
@@ -690,7 +688,7 @@ func newCombinedDeletionKeyspanIter(
|
||||
smallest = last
|
||||
}
|
||||
}
|
||||
if largest.Equal(cmp, smallest) {
|
||||
if largest.Equal(comparer.Equal, smallest) {
|
||||
current = append(current[:0], largest)
|
||||
} else {
|
||||
current = append(current[:0], largest, smallest)
|
||||
@@ -703,11 +701,21 @@ func newCombinedDeletionKeyspanIter(
|
||||
// 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(cmp, transform)
|
||||
mIter.Init(comparer.Compare, transform)
|
||||
|
||||
iter, err := r.NewRawRangeDelIter()
|
||||
if err != nil {
|
||||
@@ -715,12 +723,12 @@ func newCombinedDeletionKeyspanIter(
|
||||
}
|
||||
if iter != nil {
|
||||
dIter := &keyspan.DefragmentingIter{}
|
||||
dIter.Init(cmp, iter, equal, reducer)
|
||||
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(
|
||||
cmp, iter, m.Smallest.UserKey, m.Largest.UserKey, nil, nil,
|
||||
comparer.Compare, iter, m.Smallest.UserKey, m.Largest.UserKey, nil, nil,
|
||||
)
|
||||
mIter.AddLevel(iter)
|
||||
}
|
||||
@@ -744,10 +752,57 @@ func newCombinedDeletionKeyspanIter(
|
||||
return len(out.Keys) > 0
|
||||
})
|
||||
dIter := &keyspan.DefragmentingIter{}
|
||||
dIter.Init(cmp, iter, equal, reducer)
|
||||
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
|
||||
}
|
||||
|
||||
77
vendor/github.com/cockroachdb/pebble/version_set.go
generated
vendored
77
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
|
||||
@@ -474,6 +540,11 @@ func (vs *versionSet) logAndApply(
|
||||
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()
|
||||
|
||||
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
|
||||
|
||||
6
vendor/github.com/cockroachdb/redact/README.md
generated
vendored
6
vendor/github.com/cockroachdb/redact/README.md
generated
vendored
@@ -1,5 +1,9 @@
|
||||
# redact
|
||||
Utilities to redact Go strings for confidentiality
|
||||
|
||||
Godoc link: https://pkg.go.dev/github.com/cockroachdb/redact?tab=doc
|
||||

|
||||
[](https://pkg.go.dev/github.com/cockroachdb/redact)
|
||||
|
||||
Some explanations about how this is used in e.g. CockroachDB:
|
||||
|
||||
https://wiki.crdb.io/wiki/spaces/CRDB/pages/1824817806/Log+and+error+redactability
|
||||
|
||||
179
vendor/github.com/cockroachdb/redact/api.go
generated
vendored
179
vendor/github.com/cockroachdb/redact/api.go
generated
vendored
@@ -14,22 +14,23 @@
|
||||
|
||||
package redact
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/cockroachdb/redact/builder"
|
||||
i "github.com/cockroachdb/redact/interfaces"
|
||||
b "github.com/cockroachdb/redact/internal/buffer"
|
||||
fw "github.com/cockroachdb/redact/internal/fmtforward"
|
||||
m "github.com/cockroachdb/redact/internal/markers"
|
||||
w "github.com/cockroachdb/redact/internal/redact"
|
||||
ifmt "github.com/cockroachdb/redact/internal/rfmt"
|
||||
)
|
||||
|
||||
// SafeFormatter is implemented by object types that want to separate
|
||||
// safe and non-safe information when printed out by a Printf-like
|
||||
// formatter.
|
||||
type SafeFormatter interface {
|
||||
// SafeFormat is like the Format method of fmt.Formatter, except
|
||||
// that it operates using a SafePrinter instead of a fmt.State for
|
||||
// output.
|
||||
//
|
||||
// The verb argument is the control character that defines
|
||||
// the formatting mode in the surrounding Printf call.
|
||||
// For example, if this method is called to format %03d,
|
||||
// the verb is 'd'.
|
||||
SafeFormat(s SafePrinter, verb rune)
|
||||
}
|
||||
type SafeFormatter = i.SafeFormatter
|
||||
|
||||
// SafeValue is a marker interface to be implemented by types that
|
||||
// alias base Go types and whose natural representation via Printf is
|
||||
@@ -48,17 +49,13 @@ type SafeFormatter interface {
|
||||
// collect all the types that implement this interface, as well as all
|
||||
// uses of this type, and produce a report. Changes to this report
|
||||
// should receive maximal amount of scrutiny during code reviews.
|
||||
type SafeValue interface {
|
||||
SafeValue()
|
||||
}
|
||||
type SafeValue = i.SafeValue
|
||||
|
||||
// SafeMessager is an alternative to SafeFormatter used in previous
|
||||
// versions of CockroachDB.
|
||||
// NB: this interface is obsolete. Use SafeFormatter instead.
|
||||
// TODO(knz): Remove this.
|
||||
type SafeMessager interface {
|
||||
SafeMessage() string
|
||||
}
|
||||
type SafeMessager = i.SafeMessager
|
||||
|
||||
// SafePrinter is a stateful helper that abstracts an output stream in
|
||||
// the context of printf-like formatting, but with the ability to
|
||||
@@ -67,60 +64,112 @@ type SafeMessager interface {
|
||||
// This package provides one implementation of this using marker
|
||||
// delimiters for unsafe data, see markers.go. We would like to aim
|
||||
// for alternate implementations to generate more structured formats.
|
||||
type SafePrinter interface {
|
||||
// SafePrinter inherits fmt.State to access format flags, however
|
||||
// calls to fmt.State's underlying Write() as unsafe.
|
||||
fmt.State
|
||||
|
||||
// SafePrinter provides the SafeWriter interface.
|
||||
SafeWriter
|
||||
}
|
||||
type SafePrinter = i.SafePrinter
|
||||
|
||||
// SafeWriter provides helper functions for use in implementations of
|
||||
// SafeFormatter, to format mixes of safe and unsafe strings.
|
||||
type SafeWriter interface {
|
||||
// SafeString emits a safe string.
|
||||
SafeString(SafeString)
|
||||
type SafeWriter = i.SafeWriter
|
||||
|
||||
// SafeRune emits a safe rune.
|
||||
SafeRune(SafeRune)
|
||||
// SafeString represents a string that is not a sensitive value.
|
||||
type SafeString = i.SafeString
|
||||
|
||||
// Print emits its arguments separated by spaces.
|
||||
// For each argument it dynamically checks for the SafeFormatter or
|
||||
// SafeValue interface and either use that, or mark the argument
|
||||
// payload as unsafe.
|
||||
Print(args ...interface{})
|
||||
// SafeInt represents an integer that is not a sensitive value.
|
||||
type SafeInt = i.SafeInt
|
||||
|
||||
// For printf, a linter checks that the format string is
|
||||
// a constant literal, so the implementation can assume it's always
|
||||
// safe.
|
||||
Printf(format string, arg ...interface{})
|
||||
// SafeUint represents an integer that is not a sensitive value.
|
||||
type SafeUint = i.SafeUint
|
||||
|
||||
// UnsafeString writes an unsafe string.
|
||||
UnsafeString(string)
|
||||
|
||||
// UnsafeByte writes an unsafe byte.
|
||||
UnsafeByte(byte)
|
||||
|
||||
// UnsafeBytes writes an unsafe byte slice.
|
||||
UnsafeBytes([]byte)
|
||||
|
||||
// UnsafeRune writes an unsafe rune.
|
||||
UnsafeRune(rune)
|
||||
}
|
||||
|
||||
// SafeString aliases string. This is not meant to be used directly;
|
||||
// the type definition ensures that SafePrinter's SafeString method
|
||||
// can only take constant string literals as arguments. Typically, a
|
||||
// Go linter would ensure that ConstantString is never used to cast a
|
||||
// value.
|
||||
type SafeString string
|
||||
|
||||
// SafeValue makes SafeString a SafeValue.
|
||||
func (SafeString) SafeValue() {}
|
||||
// SafeFloat represents a floating-point value that is not a sensitive value.
|
||||
type SafeFloat = i.SafeFloat
|
||||
|
||||
// SafeRune aliases rune. See the explanation for SafeString.
|
||||
type SafeRune rune
|
||||
type SafeRune = i.SafeRune
|
||||
|
||||
// SafeValue makes SafeRune a SafeValue.
|
||||
func (SafeRune) SafeValue() {}
|
||||
// RedactableString is a string that contains a mix of safe and unsafe
|
||||
// bits of data, but where it is known that unsafe bits are enclosed
|
||||
// by redaction markers ‹ and ›, and occurrences of the markers
|
||||
// inside the original data items have been escaped.
|
||||
//
|
||||
// Instances of RedactableString should not be constructed directly;
|
||||
// instead use the facilities from print.go (Sprint, Sprintf)
|
||||
// or the methods below.
|
||||
type RedactableString = m.RedactableString
|
||||
|
||||
// RedactableBytes is like RedactableString but is a byte slice.
|
||||
//
|
||||
// Instances of RedactableBytes should not be constructed directly;
|
||||
// instead use the facilities from print.go (Sprint, Sprintf)
|
||||
// or the methods below.
|
||||
type RedactableBytes = m.RedactableBytes
|
||||
|
||||
// StartMarker returns the start delimiter for an unsafe string.
|
||||
func StartMarker() []byte { return m.StartMarker() }
|
||||
|
||||
// EndMarker returns the end delimiter for an unsafe string.
|
||||
func EndMarker() []byte { return m.EndMarker() }
|
||||
|
||||
// RedactedMarker returns the special string used by Redact.
|
||||
func RedactedMarker() []byte { return m.RedactedMarker() }
|
||||
|
||||
// EscapeMarkers escapes the special delimiters from the provided
|
||||
// byte slice.
|
||||
func EscapeMarkers(s []byte) []byte { return m.EscapeMarkers(s) }
|
||||
|
||||
// EscapeBytes escapes markers inside the given byte slice and encloses
|
||||
// the entire byte slice between redaction markers.
|
||||
// EscapeBytes escapes markers inside the given byte slice and encloses
|
||||
// the entire byte slice between redaction markers.
|
||||
func EscapeBytes(s []byte) RedactableBytes { return ifmt.EscapeBytes(s) }
|
||||
|
||||
// ManualBuffer is a variable-sized buffer of bytes with
|
||||
// Write methods. Writes are escaped in different ways
|
||||
// depending on the output mode.
|
||||
// Note: This type is only meant for "advanced" usage.
|
||||
// For most common uses, consider using StringBuilder instead.
|
||||
type ManualBuffer = b.Buffer
|
||||
|
||||
// Unsafe turns any value that would otherwise be considered safe,
|
||||
// into an unsafe value.
|
||||
func Unsafe(a interface{}) interface{} { return w.Unsafe(a) }
|
||||
|
||||
// Safe turns any value into an object that is considered as safe by
|
||||
// the formatter.
|
||||
//
|
||||
// This is provided as an “escape hatch” for cases where the other
|
||||
// interfaces and conventions fail. Increased usage of this mechanism
|
||||
// should be taken as a signal that a new abstraction is missing.
|
||||
// The implementation is also slow.
|
||||
func Safe(a interface{}) SafeValue { return w.Safe(a) }
|
||||
|
||||
// RegisterRedactErrorFn registers an error redaction function for use
|
||||
// during automatic redaction by this package.
|
||||
// Provided e.g. by cockroachdb/errors.
|
||||
func RegisterRedactErrorFn(fn func(err error, p i.SafePrinter, verb rune)) {
|
||||
ifmt.RegisterRedactErrorFn(fn)
|
||||
}
|
||||
|
||||
// MakeFormat is a helper for use by implementations of the
|
||||
// SafeFormatter interface. It reproduces the format currently active
|
||||
// in fmt.State and verb. This is provided because Go's standard
|
||||
// fmt.State does not make the original format string available to us.
|
||||
//
|
||||
// If the return value justV is true, then the current state
|
||||
// was found to be %v exactly; in that case the caller
|
||||
// can avoid a full-blown Printf call and use just Print instead
|
||||
// to take a shortcut.
|
||||
func MakeFormat(s fmt.State, verb rune) (justV bool, format string) {
|
||||
return fw.MakeFormat(s, verb)
|
||||
}
|
||||
|
||||
// StringBuilder accumulates strings with optional redaction markers.
|
||||
//
|
||||
// It implements io.Writer but marks direct writes as redactable.
|
||||
// To distinguish safe and unsafe bits, it also implements the SafeWriter
|
||||
// interface.
|
||||
type StringBuilder = builder.StringBuilder
|
||||
|
||||
// RegisterSafeType registers a data type to always be considered safe
|
||||
// during the production of redactable strings.
|
||||
func RegisterSafeType(t reflect.Type) {
|
||||
ifmt.RegisterSafeType(t)
|
||||
}
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package redact
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
|
||||
i "github.com/cockroachdb/redact/interfaces"
|
||||
ib "github.com/cockroachdb/redact/internal/buffer"
|
||||
ifmt "github.com/cockroachdb/redact/internal/rfmt"
|
||||
)
|
||||
|
||||
// StringBuilder accumulates strings with optional redaction markers.
|
||||
@@ -27,26 +29,14 @@ import (
|
||||
// To distinguish safe and unsafe bits, it also implements the SafeWriter
|
||||
// interface.
|
||||
type StringBuilder struct {
|
||||
// we use bytes.Buffer internally to simplify the implementation of
|
||||
// the SafeWriter interface.
|
||||
buf bytes.Buffer
|
||||
ib.Buffer
|
||||
}
|
||||
|
||||
// String returns the accumulated string, with redaction markers stripped out.
|
||||
// To obtain the redactable string, call RedactableString().
|
||||
func (b StringBuilder) String() string { return b.RedactableString().StripMarkers() }
|
||||
|
||||
var _ fmt.Stringer = StringBuilder{}
|
||||
var _ fmt.Stringer = (*StringBuilder)(nil)
|
||||
|
||||
// RedactableString returns the accumulated string, including redaction markers.
|
||||
func (b StringBuilder) RedactableString() RedactableString { return RedactableString(b.buf.String()) }
|
||||
|
||||
// RedactableBytes returns the accumulated bytes, including redaction markers.
|
||||
func (b StringBuilder) RedactableBytes() RedactableBytes { return RedactableBytes(b.buf.Bytes()) }
|
||||
|
||||
// SafeFormat implements SafeFormatter.
|
||||
func (b StringBuilder) SafeFormat(p SafePrinter, _ rune) {
|
||||
func (b StringBuilder) SafeFormat(p i.SafePrinter, _ rune) {
|
||||
// We only support the %v / %s natural print here.
|
||||
// Go supports other formatting verbs for strings: %x/%X/%q.
|
||||
//
|
||||
@@ -64,20 +54,8 @@ func (b StringBuilder) SafeFormat(p SafePrinter, _ rune) {
|
||||
p.Print(b.RedactableString())
|
||||
}
|
||||
|
||||
var _ SafeFormatter = StringBuilder{}
|
||||
var _ SafeFormatter = (*StringBuilder)(nil)
|
||||
|
||||
// Len returns the number of accumulated bytes, including redaction
|
||||
// markers; b.Len() == len(b.RedactableString()).
|
||||
func (b *StringBuilder) Len() int { return b.buf.Len() }
|
||||
|
||||
// Cap returns the capacity of the builder's underlying byte slice. It is the
|
||||
// total space allocated for the string being built and includes any bytes
|
||||
// already written.
|
||||
func (b *StringBuilder) Cap() int { return b.buf.Cap() }
|
||||
|
||||
// Reset resets the Builder to be empty.
|
||||
func (b *StringBuilder) Reset() { b.buf.Reset() }
|
||||
var _ i.SafeFormatter = StringBuilder{}
|
||||
var _ i.SafeFormatter = (*StringBuilder)(nil)
|
||||
|
||||
// StringBuilder implements io.Writer.
|
||||
// Direct Write() calls are considered unsafe.
|
||||
@@ -85,65 +63,75 @@ var _ io.Writer = (*StringBuilder)(nil)
|
||||
|
||||
// Write implements the io.Writer interface.
|
||||
func (b *StringBuilder) Write(s []byte) (int, error) {
|
||||
b.UnsafeBytes(s)
|
||||
return len(s), nil
|
||||
b.SetMode(ib.UnsafeEscaped)
|
||||
return b.Buffer.Write(s)
|
||||
}
|
||||
|
||||
// StringBuilder implements SafeWriter.
|
||||
var _ SafeWriter = (*StringBuilder)(nil)
|
||||
var _ i.SafeWriter = (*StringBuilder)(nil)
|
||||
|
||||
// Print is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) Print(args ...interface{}) {
|
||||
_, _ = Fprint(&b.buf, args...)
|
||||
b.SetMode(ib.PreRedactable)
|
||||
_, _ = ifmt.Fprint(&b.Buffer, args...)
|
||||
}
|
||||
|
||||
// Printf is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) Printf(format string, args ...interface{}) {
|
||||
_, _ = Fprintf(&b.buf, format, args...)
|
||||
b.SetMode(ib.PreRedactable)
|
||||
_, _ = ifmt.Fprintf(&b.Buffer, format, args...)
|
||||
}
|
||||
|
||||
// SafeString is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) SafeString(s SafeString) {
|
||||
w := escapeWriter{w: &b.buf, enclose: false}
|
||||
_, _ = w.Write([]byte(s))
|
||||
func (b *StringBuilder) SafeString(s i.SafeString) {
|
||||
b.SetMode(ib.SafeEscaped)
|
||||
_, _ = b.Buffer.WriteString(string(s))
|
||||
}
|
||||
|
||||
// SafeInt is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) SafeInt(s i.SafeInt) {
|
||||
b.SetMode(ib.SafeEscaped)
|
||||
_, _ = ifmt.Fprintf(&b.Buffer, "%d", s)
|
||||
}
|
||||
|
||||
// SafeUint is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) SafeUint(s i.SafeUint) {
|
||||
b.SetMode(ib.SafeEscaped)
|
||||
_, _ = ifmt.Fprintf(&b.Buffer, "%d", s)
|
||||
}
|
||||
|
||||
// SafeFloat is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) SafeFloat(s i.SafeFloat) {
|
||||
b.SetMode(ib.SafeEscaped)
|
||||
_, _ = ifmt.Fprintf(&b.Buffer, "%v", s)
|
||||
}
|
||||
|
||||
// SafeRune is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) SafeRune(s SafeRune) {
|
||||
if s == startRedactable || s == endRedactable {
|
||||
s = escapeMark
|
||||
}
|
||||
_, _ = b.buf.WriteRune(rune(s))
|
||||
func (b *StringBuilder) SafeRune(s i.SafeRune) {
|
||||
b.SetMode(ib.SafeEscaped)
|
||||
_ = b.Buffer.WriteRune(rune(s))
|
||||
}
|
||||
|
||||
// UnsafeString is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) UnsafeString(s string) {
|
||||
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
|
||||
_, _ = w.Write([]byte(s))
|
||||
b.SetMode(ib.UnsafeEscaped)
|
||||
_, _ = b.Buffer.WriteString(s)
|
||||
}
|
||||
|
||||
// UnsafeRune is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) UnsafeRune(s rune) {
|
||||
_, _ = b.buf.WriteRune(startRedactable)
|
||||
b.SafeRune(SafeRune(s))
|
||||
_, _ = b.buf.WriteRune(endRedactable)
|
||||
b.SetMode(ib.UnsafeEscaped)
|
||||
_ = b.Buffer.WriteRune(s)
|
||||
}
|
||||
|
||||
// UnsafeByte is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) UnsafeByte(s byte) {
|
||||
_, _ = b.buf.WriteRune(startRedactable)
|
||||
if s >= utf8.RuneSelf ||
|
||||
s == startRedactableBytes[0] || s == endRedactableBytes[0] {
|
||||
// Unsafe byte. Escape it.
|
||||
_, _ = b.buf.Write(escapeBytes)
|
||||
} else {
|
||||
_ = b.buf.WriteByte(s)
|
||||
}
|
||||
_, _ = b.buf.WriteRune(endRedactable)
|
||||
b.SetMode(ib.UnsafeEscaped)
|
||||
_ = b.Buffer.WriteByte(s)
|
||||
}
|
||||
|
||||
// UnsafeBytes is part of the SafeWriter interface.
|
||||
func (b *StringBuilder) UnsafeBytes(s []byte) {
|
||||
w := escapeWriter{w: &b.buf, enclose: true, strip: true}
|
||||
_, _ = w.Write(s)
|
||||
b.SetMode(ib.UnsafeEscaped)
|
||||
_, _ = b.Buffer.Write(s)
|
||||
}
|
||||
149
vendor/github.com/cockroachdb/redact/interfaces/interfaces.go
generated
vendored
Normal file
149
vendor/github.com/cockroachdb/redact/interfaces/interfaces.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// 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 interfaces
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SafeFormatter is implemented by object types that want to separate
|
||||
// safe and non-safe information when printed out by a Printf-like
|
||||
// formatter.
|
||||
type SafeFormatter interface {
|
||||
// SafeFormat is like the Format method of fmt.Formatter, except
|
||||
// that it operates using a SafePrinter instead of a fmt.State for
|
||||
// output.
|
||||
//
|
||||
// The verb argument is the control character that defines
|
||||
// the formatting mode in the surrounding Printf call.
|
||||
// For example, if this method is called to format %03d,
|
||||
// the verb is 'd'.
|
||||
SafeFormat(s SafePrinter, verb rune)
|
||||
}
|
||||
|
||||
// SafePrinter is a stateful helper that abstracts an output stream in
|
||||
// the context of printf-like formatting, but with the ability to
|
||||
// separate safe and unsafe bits of data.
|
||||
//
|
||||
// This package provides one implementation of this using marker
|
||||
// delimiters for unsafe data, see markers.go. We would like to aim
|
||||
// for alternate implementations to generate more structured formats.
|
||||
type SafePrinter interface {
|
||||
// SafePrinter inherits fmt.State to access format flags, however
|
||||
// calls to fmt.State's underlying Write() as unsafe.
|
||||
fmt.State
|
||||
|
||||
// SafePrinter provides the SafeWriter interface.
|
||||
SafeWriter
|
||||
}
|
||||
|
||||
// SafeWriter provides helper functions for use in implementations of
|
||||
// SafeFormatter, to format mixes of safe and unsafe strings.
|
||||
type SafeWriter interface {
|
||||
// SafeString emits a safe string.
|
||||
SafeString(SafeString)
|
||||
|
||||
// SafeInt emits a safe integer.
|
||||
SafeInt(SafeInt)
|
||||
|
||||
// SafeUint emits a safe unsigned integer.
|
||||
SafeUint(SafeUint)
|
||||
|
||||
// SafeFloat emits a safe floating-point value.
|
||||
SafeFloat(SafeFloat)
|
||||
|
||||
// SafeRune emits a safe rune.
|
||||
SafeRune(SafeRune)
|
||||
|
||||
// Print emits its arguments separated by spaces.
|
||||
// For each argument it dynamically checks for the SafeFormatter or
|
||||
// SafeValue interface and either use that, or mark the argument
|
||||
// payload as unsafe.
|
||||
Print(args ...interface{})
|
||||
|
||||
// For printf, a linter checks that the format string is
|
||||
// a constant literal, so the implementation can assume it's always
|
||||
// safe.
|
||||
Printf(format string, arg ...interface{})
|
||||
|
||||
// UnsafeString writes an unsafe string.
|
||||
UnsafeString(string)
|
||||
|
||||
// UnsafeByte writes an unsafe byte.
|
||||
UnsafeByte(byte)
|
||||
|
||||
// UnsafeBytes writes an unsafe byte slice.
|
||||
UnsafeBytes([]byte)
|
||||
|
||||
// UnsafeRune writes an unsafe rune.
|
||||
UnsafeRune(rune)
|
||||
}
|
||||
|
||||
// SafeString represents a string that is not a sensitive value.
|
||||
type SafeString string
|
||||
|
||||
// SafeValue makes SafeString a SafeValue.
|
||||
func (SafeString) SafeValue() {}
|
||||
|
||||
// SafeInt represents an integer that is not a sensitive value.
|
||||
type SafeInt int64
|
||||
|
||||
// SafeValue makes SafeInt a SafeValue.
|
||||
func (SafeInt) SafeValue() {}
|
||||
|
||||
// SafeUint represents an integer that is not a sensitive value.
|
||||
type SafeUint uint64
|
||||
|
||||
// SafeValue makes SafeUint a SafeValue.
|
||||
func (SafeUint) SafeValue() {}
|
||||
|
||||
// SafeFloat represents a floating-point value that is not a sensitive value.
|
||||
type SafeFloat float64
|
||||
|
||||
// SafeValue makes SafeFloat a SafeValue.
|
||||
func (SafeFloat) SafeValue() {}
|
||||
|
||||
// SafeRune aliases rune. See the explanation for SafeString.
|
||||
type SafeRune rune
|
||||
|
||||
// SafeValue makes SafeRune a SafeValue.
|
||||
func (SafeRune) SafeValue() {}
|
||||
|
||||
// SafeValue is a marker interface to be implemented by types that
|
||||
// alias base Go types and whose natural representation via Printf is
|
||||
// always safe for reporting.
|
||||
//
|
||||
// This is recognized by the SafePrinter interface as an alternative
|
||||
// to SafeFormatter.
|
||||
//
|
||||
// It is provided to decorate "leaf" Go types, such as aliases to int.
|
||||
//
|
||||
// Typically, a linter enforces that a type can only implement this
|
||||
// interface if it aliases a base go type. More complex types should
|
||||
// implement SafeFormatter instead.
|
||||
//
|
||||
// It is advised to build an automatic process during builds to
|
||||
// collect all the types that implement this interface, as well as all
|
||||
// uses of this type, and produce a report. Changes to this report
|
||||
// should receive maximal amount of scrutiny during code reviews.
|
||||
type SafeValue interface {
|
||||
SafeValue()
|
||||
}
|
||||
|
||||
// SafeMessager is an alternative to SafeFormatter used in previous
|
||||
// versions of CockroachDB.
|
||||
// NB: this interface is obsolete. Use SafeFormatter instead.
|
||||
// TODO(knz): Remove this.
|
||||
type SafeMessager = interface {
|
||||
SafeMessage() string
|
||||
}
|
||||
333
vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go
generated
vendored
Normal file
333
vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go
generated
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
// 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 buffer
|
||||
|
||||
import (
|
||||
origFmt "fmt"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cockroachdb/redact/internal/escape"
|
||||
m "github.com/cockroachdb/redact/internal/markers"
|
||||
)
|
||||
|
||||
// Buffer is a variable-sized buffer of bytes with a
|
||||
// Write method. Writes are escaped in different ways
|
||||
// depending on the output mode.
|
||||
type Buffer struct {
|
||||
buf []byte
|
||||
validUntil int // exclusive upper bound of data that's already validated
|
||||
mode OutputMode
|
||||
markerOpen bool
|
||||
}
|
||||
|
||||
// OutputMode determines how writes are processed in the Buffer.
|
||||
type OutputMode int
|
||||
|
||||
const (
|
||||
// UnsafeEscaped says that written data is unsafe for reporting,
|
||||
// so it should be enclosed in redaction markers,
|
||||
// and may contain redaction markers itself, and so it should be escaped.
|
||||
UnsafeEscaped OutputMode = iota
|
||||
// SafeEscaped says that written data is safe for reporting
|
||||
// (should not be enclosed in redaction markers) but may itself
|
||||
// contain redaction markers, and so it should be escaped.
|
||||
SafeEscaped
|
||||
// SafeRaw says that written data is safe for reporting
|
||||
// (should not be enclosed in redaction markers) and is
|
||||
// guaranteed not to contain redaction markers, so it
|
||||
// needs not be escaped.
|
||||
SafeRaw
|
||||
// PreRedactable says that written data already contains a mixed
|
||||
// of safe and unsafe information with suitably placed redaction markers,
|
||||
// and so should be inlined.
|
||||
PreRedactable = SafeRaw
|
||||
)
|
||||
|
||||
// RedactableBytes returns the bytes in the buffer.
|
||||
func (b Buffer) RedactableBytes() m.RedactableBytes {
|
||||
// NB: we're dependent on the fact this is a copy of the original
|
||||
// buffer. The finalize() method should not be called
|
||||
// in a conceputally read-only accessor like RedactableBytes().
|
||||
b.finalize()
|
||||
return m.RedactableBytes(b.buf)
|
||||
}
|
||||
|
||||
// RedactableString returns the bytes in the buffer.
|
||||
func (b Buffer) RedactableString() m.RedactableString {
|
||||
// NB: we're dependent on the fact this is a copy of the original
|
||||
// buffer. The finalize() method should not be called
|
||||
// in a conceputally read-only accessor like RedactableString().
|
||||
b.finalize()
|
||||
return m.RedactableString(b.buf)
|
||||
}
|
||||
|
||||
// String implemens fmt.Stringer.
|
||||
func (b Buffer) String() string {
|
||||
// NB: we're dependent on the fact this is a copy of the original
|
||||
// buffer. The finalize() method should not be called
|
||||
// in a conceputally read-only accessor like String().
|
||||
b.finalize()
|
||||
return m.RedactableString(b.buf).StripMarkers()
|
||||
}
|
||||
|
||||
// TakeRedactableBytes returns the buffer
|
||||
// contents and reinitializes the buffer. This saves
|
||||
// a memory allocation compared to RedactableBytes().
|
||||
func (b *Buffer) TakeRedactableBytes() m.RedactableBytes {
|
||||
b.finalize()
|
||||
r := b.buf
|
||||
b.buf = nil
|
||||
b.validUntil = 0
|
||||
b.mode = UnsafeEscaped
|
||||
return m.RedactableBytes(r)
|
||||
}
|
||||
|
||||
// TakeRedactableString returns the buffer
|
||||
// contents and reinitializes the buffer. This saves
|
||||
// a memory allocation compared to RedactableString().
|
||||
func (b *Buffer) TakeRedactableString() m.RedactableString {
|
||||
if b == nil {
|
||||
// Special case, useful in debugging.
|
||||
return "<nil>"
|
||||
}
|
||||
b.finalize()
|
||||
r := *(*m.RedactableString)(unsafe.Pointer(&b.buf))
|
||||
b.buf = nil
|
||||
b.validUntil = 0
|
||||
b.mode = UnsafeEscaped
|
||||
return r
|
||||
}
|
||||
|
||||
// Len returns the number of bytes in the buffer.
|
||||
func (b *Buffer) Len() int {
|
||||
copy := *b
|
||||
copy.finalize()
|
||||
return len(copy.buf)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the buffer's underlying byte slice,
|
||||
// that is, the total space allocated for the buffer's data.
|
||||
func (b *Buffer) Cap() int {
|
||||
return cap(b.buf)
|
||||
}
|
||||
|
||||
// Write appends the contents of p to the buffer, growing the buffer as
|
||||
// needed. The return value n is the length of p; err is always nil. If the
|
||||
// buffer becomes too large, Write will panic with ErrTooLarge.
|
||||
func (b *Buffer) Write(p []byte) (n int, err error) {
|
||||
b.startWrite()
|
||||
m, ok := b.tryGrowByReslice(len(p))
|
||||
if !ok {
|
||||
m = b.grow(len(p))
|
||||
}
|
||||
return copy(b.buf[m:], p), nil
|
||||
}
|
||||
|
||||
// WriteString appends the contents of s to the buffer, growing the buffer as
|
||||
// needed. The return value n is the length of s; err is always nil. If the
|
||||
// buffer becomes too large, WriteString will panic with ErrTooLarge.
|
||||
func (b *Buffer) WriteString(s string) (n int, err error) {
|
||||
b.startWrite()
|
||||
m, ok := b.tryGrowByReslice(len(s))
|
||||
if !ok {
|
||||
m = b.grow(len(s))
|
||||
}
|
||||
return copy(b.buf[m:], s), nil
|
||||
}
|
||||
|
||||
// WriteByte emits a single byte.
|
||||
func (b *Buffer) WriteByte(s byte) error {
|
||||
b.startWrite()
|
||||
if b.mode == UnsafeEscaped &&
|
||||
(s >= utf8.RuneSelf ||
|
||||
s == m.StartS[0] || s == m.EndS[0]) {
|
||||
// Unsafe byte. Escape it.
|
||||
_, err := b.WriteString(m.EscapeMarkS)
|
||||
return err
|
||||
}
|
||||
m, ok := b.tryGrowByReslice(1)
|
||||
if !ok {
|
||||
m = b.grow(1)
|
||||
}
|
||||
b.buf[m] = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRune emits a single rune.
|
||||
func (b *Buffer) WriteRune(s rune) error {
|
||||
b.startWrite()
|
||||
l := utf8.RuneLen(s)
|
||||
m, ok := b.tryGrowByReslice(l)
|
||||
if !ok {
|
||||
m = b.grow(l)
|
||||
}
|
||||
_ = utf8.EncodeRune(b.buf[m:], s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// finalize ensures that all the buffer is properly
|
||||
// marked.
|
||||
func (b *Buffer) finalize() {
|
||||
if b.mode == SafeRaw {
|
||||
b.validUntil = len(b.buf)
|
||||
} else {
|
||||
b.escapeToEnd(b.mode == UnsafeEscaped /* breakNewLines */)
|
||||
}
|
||||
if b.markerOpen {
|
||||
b.endRedactable()
|
||||
b.validUntil = len(b.buf)
|
||||
}
|
||||
}
|
||||
|
||||
// endRedactable adds the closing redaction marker.
|
||||
func (b *Buffer) endRedactable() {
|
||||
if len(b.buf) == 0 {
|
||||
return
|
||||
}
|
||||
p, ok := b.tryGrowByReslice(len(m.EndS))
|
||||
if !ok {
|
||||
p = b.grow(len(m.EndS))
|
||||
}
|
||||
copy(b.buf[p:], m.EndS)
|
||||
b.markerOpen = false
|
||||
}
|
||||
|
||||
func (b *Buffer) startWrite() {
|
||||
if b.mode == UnsafeEscaped && !b.markerOpen {
|
||||
b.startRedactable()
|
||||
b.validUntil = len(b.buf)
|
||||
}
|
||||
}
|
||||
|
||||
// endRedactable adds the closing redaction marker.
|
||||
func (b *Buffer) startRedactable() {
|
||||
p, ok := b.tryGrowByReslice(len(m.StartS))
|
||||
if !ok {
|
||||
p = b.grow(len(m.StartS))
|
||||
}
|
||||
copy(b.buf[p:], m.StartS)
|
||||
b.markerOpen = true
|
||||
}
|
||||
|
||||
// escapeToEnd escapes occurrences of redaction markers in
|
||||
// b.buf[b.validUntil:] and advances b.validUntil until the end.
|
||||
func (b *Buffer) escapeToEnd(breakNewLines bool) {
|
||||
b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, breakNewLines)
|
||||
b.validUntil = len(b.buf)
|
||||
}
|
||||
|
||||
// GetMode retrieves the output mode.
|
||||
func (b *Buffer) GetMode() OutputMode {
|
||||
return b.mode
|
||||
}
|
||||
|
||||
// SetMode changes the output mode.
|
||||
func (b *Buffer) SetMode(newMode OutputMode) {
|
||||
if b.mode == newMode {
|
||||
// noop
|
||||
return
|
||||
}
|
||||
if b.mode == UnsafeEscaped || b.mode == SafeEscaped {
|
||||
b.escapeToEnd(b.mode == UnsafeEscaped /* breakNewLines */)
|
||||
}
|
||||
if b.markerOpen {
|
||||
b.endRedactable()
|
||||
}
|
||||
b.validUntil = len(b.buf)
|
||||
b.mode = newMode
|
||||
}
|
||||
|
||||
// Reset resets the buffer to be empty,
|
||||
// but it retains the underlying storage for use by future writes.
|
||||
// It also resets the output mode to UnsafeEscaped.
|
||||
func (b *Buffer) Reset() {
|
||||
b.buf = b.buf[:0]
|
||||
b.validUntil = 0
|
||||
b.mode = UnsafeEscaped
|
||||
b.markerOpen = false
|
||||
}
|
||||
|
||||
// tryGrowByReslice is a inlineable version of grow for the fast-case where the
|
||||
// internal buffer only needs to be resliced.
|
||||
// It returns the index where bytes should be written and whether it succeeded.
|
||||
func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
|
||||
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||
b.buf = b.buf[:l+n]
|
||||
return l, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// grow grows the buffer to guarantee space for n more bytes.
|
||||
// It returns the index where bytes should be written.
|
||||
// If the buffer can't grow it will panic with ErrTooLarge.
|
||||
func (b *Buffer) grow(n int) int {
|
||||
m := len(b.buf)
|
||||
// Try to grow by means of a reslice.
|
||||
if i, ok := b.tryGrowByReslice(n); ok {
|
||||
return i
|
||||
}
|
||||
if b.buf == nil && n <= smallBufferSize {
|
||||
b.buf = make([]byte, n, smallBufferSize)
|
||||
return 0
|
||||
}
|
||||
c := cap(b.buf)
|
||||
const maxInt = int(^uint(0) >> 1)
|
||||
if n <= c/2-m {
|
||||
// let capacity get twice as large.
|
||||
} else if c > maxInt-c-n {
|
||||
panic(ErrTooLarge)
|
||||
} else {
|
||||
// Not enough space anywhere, we need to allocate.
|
||||
buf := makeSlice(2*c + n)
|
||||
copy(buf, b.buf)
|
||||
b.buf = buf
|
||||
}
|
||||
b.buf = b.buf[:m+n]
|
||||
return m
|
||||
}
|
||||
|
||||
// Grow grows the buffer's capacity, if necessary, to guarantee space for
|
||||
// another n bytes. After Grow(n), at least n bytes can be written to the
|
||||
// buffer without another allocation.
|
||||
// If n is negative, Grow will panic.
|
||||
// If the buffer can't grow it will panic with ErrTooLarge.
|
||||
func (b *Buffer) Grow(n int) {
|
||||
if n < 0 {
|
||||
panic(origFmt.Errorf("redact.Buffer.Grow: negative count"))
|
||||
}
|
||||
m := b.grow(n)
|
||||
b.buf = b.buf[:m]
|
||||
}
|
||||
|
||||
// makeSlice allocates a slice of size n. If the allocation fails, it panics
|
||||
// with ErrTooLarge.
|
||||
func makeSlice(n int) []byte {
|
||||
// If the make fails, give a known error.
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
panic(ErrTooLarge)
|
||||
}
|
||||
}()
|
||||
return make([]byte, n)
|
||||
}
|
||||
|
||||
// ErrTooLarge is passed to panic if memory cannot be allocated to
|
||||
// store data in a buffer.
|
||||
var ErrTooLarge = origFmt.Errorf("redact.Buffer: too large")
|
||||
|
||||
// smallBufferSize is an initial allocation minimal capacity.
|
||||
const smallBufferSize = 64
|
||||
129
vendor/github.com/cockroachdb/redact/internal/escape/escape.go
generated
vendored
Normal file
129
vendor/github.com/cockroachdb/redact/internal/escape/escape.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2020 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 escape
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode/utf8"
|
||||
|
||||
m "github.com/cockroachdb/redact/internal/markers"
|
||||
)
|
||||
|
||||
// InternalEscapeBytes escapes redaction markers in the provided buf
|
||||
// starting at the location startLoc.
|
||||
// The bytes before startLoc are considered safe (already escaped).
|
||||
//
|
||||
// If breakNewLines is set, a closing redaction marker
|
||||
// is placed before sequences of one or more newline characters,
|
||||
// and an open redaction marker is placed afterwards.
|
||||
func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res []byte) {
|
||||
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
|
||||
// because the ...S variant is a compile-time constant so this
|
||||
// accelerates the loops below.
|
||||
start, ls := m.StartBytes, len(m.StartS)
|
||||
end, le := m.EndBytes, len(m.EndS)
|
||||
escape := m.EscapeMarkBytes
|
||||
|
||||
// Trim final newlines/spaces, for convenience.
|
||||
if strip {
|
||||
end := len(b)
|
||||
for i := end - 1; i >= startLoc; i-- {
|
||||
if b[i] == '\n' || b[i] == ' ' {
|
||||
end = i
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
b = b[:end]
|
||||
}
|
||||
|
||||
// res is the output slice. In the common case where there is
|
||||
// nothing to escape, the input slice is returned directly
|
||||
// and no allocation takes place.
|
||||
res = b
|
||||
// copied is true if and only if `res` is a copy of `b`. It only
|
||||
// turns to true if the loop below finds something to escape.
|
||||
copied := false
|
||||
// k is the index in b up to (and excluding) the byte which we've
|
||||
// already copied into res (if copied=true).
|
||||
k := 0
|
||||
|
||||
for i := startLoc; i < len(b); i++ {
|
||||
if breakNewLines && b[i] == '\n' {
|
||||
if !copied {
|
||||
// We only allocate an output slice when we know we definitely
|
||||
// need it.
|
||||
res = make([]byte, 0, len(b))
|
||||
copied = true
|
||||
}
|
||||
res = append(res, b[k:i]...)
|
||||
res = append(res, end...)
|
||||
// Advance to the last newline character. We want to forward
|
||||
// them all in a single call to doWrite, for performance.
|
||||
lastNewLine := i
|
||||
for lastNewLine < len(b) && b[lastNewLine] == '\n' {
|
||||
lastNewLine++
|
||||
}
|
||||
res = append(res, b[i:lastNewLine]...)
|
||||
res = append(res, start...)
|
||||
k = lastNewLine
|
||||
i = lastNewLine - 1
|
||||
} else
|
||||
// Ensure that occurrences of the delimiter inside the string get
|
||||
// escaped.
|
||||
// Reminder: ls and le are likely greater than 1, as we are scanning
|
||||
// utf-8 encoded delimiters (the utf-8 encoding is multibyte).
|
||||
if i+ls <= len(b) && bytes.Equal(b[i:i+ls], start) {
|
||||
if !copied {
|
||||
// We only allocate an output slice when we know we definitely
|
||||
// need it.
|
||||
res = make([]byte, 0, len(b)+len(escape))
|
||||
copied = true
|
||||
}
|
||||
res = append(res, b[k:i]...)
|
||||
res = append(res, escape...)
|
||||
// Advance the counters by the length (in bytes) of the delimiter.
|
||||
k = i + ls
|
||||
i += ls - 1 /* -1 because we have i++ at the end of every iteration */
|
||||
} else if i+le <= len(b) && bytes.Equal(b[i:i+le], end) {
|
||||
if !copied {
|
||||
// See the comment above about res allocation.
|
||||
res = make([]byte, 0, len(b)+len(escape))
|
||||
copied = true
|
||||
}
|
||||
res = append(res, b[k:i]...)
|
||||
res = append(res, escape...)
|
||||
// Advance the counters by the length (in bytes) of the delimiter.
|
||||
k = i + le
|
||||
i += le - 1 /* -1 because we have i++ at the end of every iteration */
|
||||
}
|
||||
}
|
||||
// If the string terminates with an invalid utf-8 sequence, we
|
||||
// want to avoid a run-in with a subsequent redaction marker.
|
||||
if r, s := utf8.DecodeLastRune(b); s == 1 && r == utf8.RuneError {
|
||||
if !copied {
|
||||
// See the comment above about res allocation.
|
||||
res = make([]byte, 0, len(b)+len(escape))
|
||||
copied = true
|
||||
}
|
||||
res = append(res, b[k:]...)
|
||||
res = append(res, escape...)
|
||||
k = len(b)
|
||||
}
|
||||
if copied {
|
||||
res = append(res, b[k:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package redact
|
||||
package fmtforward
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -21,7 +21,10 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func reproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
|
||||
// ReproducePrintf calls fmt.Print/fmt.Printf on the argument
|
||||
// based on the details present in the fmt.State and
|
||||
// writes to the io.Writer.
|
||||
func ReproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
|
||||
justV, revFmt := MakeFormat(s, verb)
|
||||
if justV {
|
||||
// Common case, avoids generating then parsing the format again.
|
||||
@@ -31,8 +34,7 @@ func reproducePrintf(w io.Writer, s fmt.State, verb rune, arg interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// MakeFormat is a helper for use by implementations of the
|
||||
// SafeFormatter interface. It reproduces the format currently active
|
||||
// MakeFormat reproduces the format currently active
|
||||
// in fmt.State and verb. This is provided because Go's standard
|
||||
// fmt.State does not make the original format string available to us.
|
||||
//
|
||||
83
vendor/github.com/cockroachdb/redact/internal/hooks.go
generated
vendored
83
vendor/github.com/cockroachdb/redact/internal/hooks.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
// Copyright 2020 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 fmt
|
||||
|
||||
// printArg overrides the original implementation by redirecting the
|
||||
// call to a substitute function in the printer, if available.
|
||||
func (p *pp) printArg(a interface{}, verb rune) {
|
||||
if p.printArgSubstituteFn != nil {
|
||||
p.substituteState = p.printArgSubstituteFn(p, a, verb)
|
||||
} else {
|
||||
p.printArgOrig(a, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// InternalPrinter exposes pp to the redact package.
|
||||
type InternalPrinter = pp
|
||||
|
||||
// InternalBuffer exposes buffer to the redact package.
|
||||
type InternalBuffer = buffer
|
||||
|
||||
// NewInternalPrinter exposes pp allocation to the redact package.
|
||||
func NewInternalPrinter() *InternalPrinter { return newPrinter() }
|
||||
|
||||
// SetHook connects an outer printer to the inner printer.
|
||||
func SetHook(
|
||||
p *InternalPrinter, fn func(p *InternalPrinter, arg interface{}, verb rune) (newState int),
|
||||
) {
|
||||
p.printArgSubstituteFn = fn
|
||||
}
|
||||
|
||||
// Free exposes pp deallocation to the redact package.
|
||||
func Free(p *InternalPrinter) {
|
||||
p.substituteState = 0
|
||||
p.printArgSubstituteFn = nil
|
||||
p.free()
|
||||
}
|
||||
|
||||
// Buf exposes the string buffer to the redact package.
|
||||
func Buf(p *InternalPrinter) []byte { return []byte(p.buf) }
|
||||
|
||||
// GetState exposes the state to the redact package.
|
||||
func GetState(p *InternalPrinter) int {
|
||||
return p.substituteState
|
||||
}
|
||||
|
||||
// SetState exposes the string buffer to the redact package.
|
||||
func SetState(p *InternalPrinter, b []byte) {
|
||||
p.buf = buffer(b)
|
||||
p.substituteState = len(p.buf)
|
||||
}
|
||||
|
||||
// Append adds bytes to the buffer.
|
||||
func Append(p *InternalPrinter, b []byte) { p.buf.write(b) }
|
||||
|
||||
// PrintArg exposes the printArgOrig() method to the redact package.
|
||||
func PrintArg(p *InternalPrinter, a interface{}, verb rune) { p.printArgOrig(a, verb) }
|
||||
|
||||
// DoPrint exposes the doPrint() method to the redact package.
|
||||
func DoPrint(p *InternalPrinter, a []interface{}) { p.doPrint(a) }
|
||||
|
||||
// DoPrintf exposes the doPrintf() method to the redact package.
|
||||
func DoPrintf(p *InternalPrinter, format string, a []interface{}) { p.doPrintf(format, a) }
|
||||
|
||||
// SetCollectError enables wrapped error collection.
|
||||
func SetCollectError(p *InternalPrinter) { p.wrapErrs = true }
|
||||
|
||||
// CollectingError exposes wrapped error collection.
|
||||
func CollectingError(p *InternalPrinter) bool { return p.wrapErrs }
|
||||
|
||||
// WrappedError retrieves the wrapped error if found.
|
||||
func WrappedError(p *InternalPrinter) error { return p.wrappedErr }
|
||||
38
vendor/github.com/cockroachdb/redact/internal/markers/constants.go
generated
vendored
Normal file
38
vendor/github.com/cockroachdb/redact/internal/markers/constants.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 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 markers
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Internal constants.
|
||||
const (
|
||||
Start = '‹'
|
||||
StartS = string(Start)
|
||||
End = '›'
|
||||
EndS = string(End)
|
||||
EscapeMark = '?'
|
||||
EscapeMarkS = string(EscapeMark)
|
||||
RedactedS = StartS + "×" + EndS
|
||||
)
|
||||
|
||||
// Internal variables.
|
||||
var (
|
||||
StartBytes = []byte(StartS)
|
||||
EndBytes = []byte(EndS)
|
||||
EscapeMarkBytes = []byte(EscapeMarkS)
|
||||
RedactedBytes = []byte(RedactedS)
|
||||
ReStripSensitive = regexp.MustCompile(StartS + "[^" + StartS + EndS + "]*" + EndS)
|
||||
ReStripMarkers = regexp.MustCompile("[" + StartS + EndS + "]")
|
||||
)
|
||||
@@ -12,9 +12,9 @@
|
||||
// implied. See the License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
package redact
|
||||
package markers
|
||||
|
||||
import "bytes"
|
||||
import i "github.com/cockroachdb/redact/interfaces"
|
||||
|
||||
// RedactableString is a string that contains a mix of safe and unsafe
|
||||
// bits of data, but where it is known that unsafe bits are enclosed
|
||||
@@ -30,13 +30,13 @@ type RedactableString string
|
||||
// RedactableString. This returns an unsafe string where all safe and
|
||||
// unsafe bits are mixed together.
|
||||
func (s RedactableString) StripMarkers() string {
|
||||
return reStripMarkers.ReplaceAllString(string(s), "")
|
||||
return ReStripMarkers.ReplaceAllString(string(s), "")
|
||||
}
|
||||
|
||||
// Redact replaces all occurrences of unsafe substrings by the
|
||||
// “Redacted” marker, ‹×›. The result string is still safe.
|
||||
func (s RedactableString) Redact() RedactableString {
|
||||
return RedactableString(reStripSensitive.ReplaceAllString(string(s), redactedS))
|
||||
return RedactableString(ReStripSensitive.ReplaceAllString(string(s), RedactedS))
|
||||
}
|
||||
|
||||
// ToBytes converts the string to a byte slice.
|
||||
@@ -45,7 +45,7 @@ func (s RedactableString) ToBytes() RedactableBytes {
|
||||
}
|
||||
|
||||
// SafeFormat formats the redactable safely.
|
||||
func (s RedactableString) SafeFormat(sp SafePrinter, _ rune) {
|
||||
func (s RedactableString) SafeFormat(sp i.SafePrinter, _ rune) {
|
||||
// As per annotateArgs() in markers_internal_print.go,
|
||||
// we consider the redactable string not further formattable.
|
||||
sp.Print(s)
|
||||
@@ -62,13 +62,13 @@ type RedactableBytes []byte
|
||||
// RedactableBytes. This returns an unsafe string where all safe and
|
||||
// unsafe bits are mixed together.
|
||||
func (s RedactableBytes) StripMarkers() []byte {
|
||||
return reStripMarkers.ReplaceAll([]byte(s), nil)
|
||||
return ReStripMarkers.ReplaceAll([]byte(s), nil)
|
||||
}
|
||||
|
||||
// Redact replaces all occurrences of unsafe substrings by the
|
||||
// “Redacted” marker, ‹×›.
|
||||
func (s RedactableBytes) Redact() RedactableBytes {
|
||||
return RedactableBytes(reStripSensitive.ReplaceAll(s, redactedBytes))
|
||||
return RedactableBytes(ReStripSensitive.ReplaceAll(s, RedactedBytes))
|
||||
}
|
||||
|
||||
// ToString converts the byte slice to a string.
|
||||
@@ -77,33 +77,23 @@ func (s RedactableBytes) ToString() RedactableString {
|
||||
}
|
||||
|
||||
// SafeFormat formats the redactable safely.
|
||||
func (s RedactableBytes) SafeFormat(sp SafePrinter, _ rune) {
|
||||
func (s RedactableBytes) SafeFormat(sp i.SafePrinter, _ rune) {
|
||||
// As per annotateArgs() in markers_internal_print.go,
|
||||
// we consider the redactable bytes not further formattable.
|
||||
sp.Print(s)
|
||||
}
|
||||
|
||||
// EscapeBytes escapes markers inside the given byte slice and encloses
|
||||
// the entire byte slice between redaction markers.
|
||||
func EscapeBytes(s []byte) RedactableBytes {
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(len(s) + len(startRedactableS) + len(endRedactableS))
|
||||
e := escapeWriter{w: &buf, enclose: true, strip: false}
|
||||
_, _ = e.Write(s)
|
||||
return RedactableBytes(buf.Bytes())
|
||||
}
|
||||
|
||||
// StartMarker returns the start delimiter for an unsafe string.
|
||||
func StartMarker() []byte { return startRedactableBytes }
|
||||
func StartMarker() []byte { return []byte(StartS) }
|
||||
|
||||
// EndMarker returns the end delimiter for an unsafe string.
|
||||
func EndMarker() []byte { return endRedactableBytes }
|
||||
func EndMarker() []byte { return []byte(EndS) }
|
||||
|
||||
// RedactedMarker returns the special string used by Redact.
|
||||
func RedactedMarker() []byte { return redactedBytes }
|
||||
func RedactedMarker() []byte { return []byte(RedactedS) }
|
||||
|
||||
// EscapeMarkers escapes the special delimiters from the provided
|
||||
// byte slice.
|
||||
func EscapeMarkers(s []byte) []byte {
|
||||
return reStripMarkers.ReplaceAll(s, escapeBytes)
|
||||
return ReStripMarkers.ReplaceAll(s, EscapeMarkBytes)
|
||||
}
|
||||
58
vendor/github.com/cockroachdb/redact/internal/print.go.diff
generated
vendored
58
vendor/github.com/cockroachdb/redact/internal/print.go.diff
generated
vendored
@@ -1,58 +0,0 @@
|
||||
--- print.go.orig 2020-08-26 14:49:30.396184000 +0200
|
||||
+++ print.go 2020-08-26 14:50:29.330829000 +0200
|
||||
@@ -1,3 +1,6 @@
|
||||
+// Code generated from print.go.orig. DO NOT EDIT
|
||||
+// GENERATED FILE DO NOT EDIT
|
||||
+//
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
@@ -5,12 +8,16 @@
|
||||
package fmt
|
||||
|
||||
import (
|
||||
- "internal/fmtsort"
|
||||
+ // CUSTOM: needed to avoid a type mismatch on Formatter.
|
||||
+ origFmt "fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
+
|
||||
+ // CUSTOM: our own import since we can't use internal from go stdlib.
|
||||
+ "github.com/cockroachdb/redact/internal/fmtsort"
|
||||
)
|
||||
|
||||
// Strings for use with buffer.WriteString.
|
||||
@@ -51,7 +58,8 @@
|
||||
// The implementation of Format may call Sprint(f) or Fprint(f) etc.
|
||||
// to generate its output.
|
||||
type Formatter interface {
|
||||
- Format(f State, c rune)
|
||||
+ // CUSTOM: refer to the original type, not the one defined here.
|
||||
+ Format(f origFmt.State, c rune)
|
||||
}
|
||||
|
||||
// Stringer is implemented by any value that has a String method,
|
||||
@@ -105,6 +113,10 @@
|
||||
type pp struct {
|
||||
buf buffer
|
||||
|
||||
+ // CUSTOM: hook fn for the redact package.
|
||||
+ printArgSubstituteFn func(p *pp, a interface{}, verb rune) (newState int)
|
||||
+ substituteState int
|
||||
+
|
||||
// arg holds the current item, as an interface{}.
|
||||
arg interface{}
|
||||
|
||||
@@ -635,7 +647,8 @@
|
||||
return false
|
||||
}
|
||||
|
||||
-func (p *pp) printArg(arg interface{}, verb rune) {
|
||||
+// CUSTOM: printArg() renamed to printArgOrig().
|
||||
+func (p *pp) printArgOrig(arg interface{}, verb rune) {
|
||||
p.arg = arg
|
||||
p.value = reflect.Value{}
|
||||
|
||||
19
vendor/github.com/cockroachdb/redact/internal/redact/doc.go
generated
vendored
Normal file
19
vendor/github.com/cockroachdb/redact/internal/redact/doc.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.
|
||||
|
||||
// Package redact is a helper internal package that provides the
|
||||
// Unsafe and Safe value wrappers. Its name is 'redact' even though a
|
||||
// better name would be 'wrappers' so as to ensure that the type name
|
||||
// of generated values contains the 'redact.' prefix.
|
||||
package redact
|
||||
@@ -14,7 +14,12 @@
|
||||
|
||||
package redact
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
origFmt "fmt"
|
||||
|
||||
i "github.com/cockroachdb/redact/interfaces"
|
||||
"github.com/cockroachdb/redact/internal/fmtforward"
|
||||
)
|
||||
|
||||
// Unsafe turns any value that would otherwise be considered safe,
|
||||
// into an unsafe value.
|
||||
@@ -22,12 +27,20 @@ func Unsafe(a interface{}) interface{} {
|
||||
return unsafeWrap{a}
|
||||
}
|
||||
|
||||
// UnsafeWrap is the type of wrapper produced by Unsafe.
|
||||
// This is exported only for use by the rfmt package.
|
||||
// Client packages should not make assumptions about
|
||||
// the concrete return type of Unsafe().
|
||||
type UnsafeWrap = unsafeWrap
|
||||
|
||||
type unsafeWrap struct {
|
||||
a interface{}
|
||||
}
|
||||
|
||||
func (w unsafeWrap) Format(s fmt.State, verb rune) {
|
||||
reproducePrintf(s, s, verb, w.a)
|
||||
func (w unsafeWrap) GetValue() interface{} { return w.a }
|
||||
|
||||
func (w unsafeWrap) Format(s origFmt.State, verb rune) {
|
||||
fmtforward.ReproducePrintf(s, s, verb, w.a)
|
||||
}
|
||||
|
||||
// Safe turns any value into an object that is considered as safe by
|
||||
@@ -37,27 +50,35 @@ func (w unsafeWrap) Format(s fmt.State, verb rune) {
|
||||
// interfaces and conventions fail. Increased usage of this mechanism
|
||||
// should be taken as a signal that a new abstraction is missing.
|
||||
// The implementation is also slow.
|
||||
func Safe(a interface{}) SafeValue {
|
||||
func Safe(a interface{}) i.SafeValue {
|
||||
return safeWrapper{a}
|
||||
}
|
||||
|
||||
// SafeWrapper is the type of wrapper produced by Safe.
|
||||
// This is exported only for use by the rfmt package.
|
||||
// Client packages should not make assumptions about
|
||||
// the concrete return type of Safe().
|
||||
type SafeWrapper = safeWrapper
|
||||
|
||||
type safeWrapper struct {
|
||||
a interface{}
|
||||
}
|
||||
|
||||
var _ SafeValue = safeWrapper{}
|
||||
var _ fmt.Formatter = safeWrapper{}
|
||||
var _ SafeMessager = safeWrapper{}
|
||||
var _ i.SafeValue = safeWrapper{}
|
||||
var _ origFmt.Formatter = safeWrapper{}
|
||||
var _ i.SafeMessager = safeWrapper{}
|
||||
|
||||
func (w safeWrapper) GetValue() interface{} { return w.a }
|
||||
|
||||
// SafeValue implements the SafeValue interface.
|
||||
func (w safeWrapper) SafeValue() {}
|
||||
|
||||
// Format implements the fmt.Formatter interface.
|
||||
func (w safeWrapper) Format(s fmt.State, verb rune) {
|
||||
reproducePrintf(s, s, verb, w.a)
|
||||
func (w safeWrapper) Format(s origFmt.State, verb rune) {
|
||||
fmtforward.ReproducePrintf(s, s, verb, w.a)
|
||||
}
|
||||
|
||||
// SafeMessage implements SafeMessager.
|
||||
func (w safeWrapper) SafeMessage() string {
|
||||
return fmt.Sprintf("%v", w.a)
|
||||
return origFmt.Sprintf("%v", w.a)
|
||||
}
|
||||
@@ -24,15 +24,6 @@ Overall, the original Go code is structured as follows:
|
||||
characters from the format string, as well as certain *constant*
|
||||
error strings (e.g. `%(BADPREC)`).
|
||||
|
||||
At this point **we make the following assumption**: there is no data
|
||||
from the printed *arguments* that is emitted to `pp.buf` directly by a
|
||||
`doPrint()` method. This is true of the implementation as of Go 1.14.
|
||||
|
||||
From this point, this package contains a *custom patch* onto the
|
||||
default implementation: the calls to `p.printArg` performed by the
|
||||
`doPrint()` methods are redirected to a function callback that is
|
||||
injected by the `redact` package.
|
||||
|
||||
Refreshing the sources
|
||||
======================
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Code generated from the Go standard library. DO NOT EDIT
|
||||
// GENERATED FILE DO NOT EDIT
|
||||
//
|
||||
// Copyright 2009 The Go 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 fmt
|
||||
package rfmt
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
@@ -67,25 +68,17 @@ func (f *fmt) writePadding(n int) {
|
||||
if n <= 0 { // No padding bytes needed.
|
||||
return
|
||||
}
|
||||
buf := *f.buf
|
||||
oldLen := len(buf)
|
||||
newLen := oldLen + n
|
||||
// Make enough room for padding.
|
||||
if newLen > cap(buf) {
|
||||
buf = make(buffer, cap(buf)*2+n)
|
||||
copy(buf, *f.buf)
|
||||
}
|
||||
f.buf.Grow(n)
|
||||
// Decide which byte the padding should be filled with.
|
||||
padByte := byte(' ')
|
||||
if f.zero {
|
||||
padByte = byte('0')
|
||||
}
|
||||
// Fill padding with padByte.
|
||||
padding := buf[oldLen:newLen]
|
||||
for i := range padding {
|
||||
padding[i] = padByte
|
||||
for i := 0; i < n; i++ {
|
||||
f.buf.writeByte(padByte)
|
||||
}
|
||||
*f.buf = buf[:newLen]
|
||||
}
|
||||
|
||||
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
|
||||
@@ -403,19 +396,20 @@ func (f *fmt) fmtSbx(s string, b []byte, digits string) {
|
||||
f.writePadding(f.wid - width)
|
||||
}
|
||||
// Write the encoding directly into the output buffer.
|
||||
buf := *f.buf
|
||||
if f.sharp {
|
||||
// Add leading 0x or 0X.
|
||||
buf = append(buf, '0', digits[16])
|
||||
f.buf.WriteByte('0')
|
||||
f.buf.WriteByte(digits[16])
|
||||
}
|
||||
var c byte
|
||||
for i := 0; i < length; i++ {
|
||||
if f.space && i > 0 {
|
||||
// Separate elements with a space.
|
||||
buf = append(buf, ' ')
|
||||
f.buf.WriteByte(' ')
|
||||
if f.sharp {
|
||||
// Add leading 0x or 0X for each element.
|
||||
buf = append(buf, '0', digits[16])
|
||||
f.buf.WriteByte('0')
|
||||
f.buf.WriteByte(digits[16])
|
||||
}
|
||||
}
|
||||
if b != nil {
|
||||
@@ -424,9 +418,9 @@ func (f *fmt) fmtSbx(s string, b []byte, digits string) {
|
||||
c = s[i] // Take a byte from the input string.
|
||||
}
|
||||
// Encode each byte as two hexadecimal digits.
|
||||
buf = append(buf, digits[c>>4], digits[c&0xF])
|
||||
f.buf.WriteByte(digits[c>>4])
|
||||
f.buf.WriteByte(digits[c&0xF])
|
||||
}
|
||||
*f.buf = buf
|
||||
// Handle padding to the right.
|
||||
if f.widPresent && f.wid > width && f.minus {
|
||||
f.writePadding(f.wid - width)
|
||||
75
vendor/github.com/cockroachdb/redact/internal/rfmt/format.go.diff
generated
vendored
Normal file
75
vendor/github.com/cockroachdb/redact/internal/rfmt/format.go.diff
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
--- format.go.orig 2021-06-18 16:45:51.065116000 +0200
|
||||
+++ format.go 2021-06-18 17:30:58.628179000 +0200
|
||||
@@ -1,3 +1,6 @@
|
||||
+// Code generated from the Go standard library. DO NOT EDIT
|
||||
+// GENERATED FILE DO NOT EDIT
|
||||
+//
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
@@ -65,25 +68,17 @@
|
||||
if n <= 0 { // No padding bytes needed.
|
||||
return
|
||||
}
|
||||
- buf := *f.buf
|
||||
- oldLen := len(buf)
|
||||
- newLen := oldLen + n
|
||||
// Make enough room for padding.
|
||||
- if newLen > cap(buf) {
|
||||
- buf = make(buffer, cap(buf)*2+n)
|
||||
- copy(buf, *f.buf)
|
||||
- }
|
||||
+ f.buf.Grow(n)
|
||||
// Decide which byte the padding should be filled with.
|
||||
padByte := byte(' ')
|
||||
if f.zero {
|
||||
padByte = byte('0')
|
||||
}
|
||||
// Fill padding with padByte.
|
||||
- padding := buf[oldLen:newLen]
|
||||
- for i := range padding {
|
||||
- padding[i] = padByte
|
||||
+ for i := 0; i < n; i++ {
|
||||
+ f.buf.writeByte(padByte)
|
||||
}
|
||||
- *f.buf = buf[:newLen]
|
||||
}
|
||||
|
||||
// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
|
||||
@@ -401,19 +396,20 @@
|
||||
f.writePadding(f.wid - width)
|
||||
}
|
||||
// Write the encoding directly into the output buffer.
|
||||
- buf := *f.buf
|
||||
if f.sharp {
|
||||
// Add leading 0x or 0X.
|
||||
- buf = append(buf, '0', digits[16])
|
||||
+ f.buf.WriteByte('0')
|
||||
+ f.buf.WriteByte(digits[16])
|
||||
}
|
||||
var c byte
|
||||
for i := 0; i < length; i++ {
|
||||
if f.space && i > 0 {
|
||||
// Separate elements with a space.
|
||||
- buf = append(buf, ' ')
|
||||
+ f.buf.WriteByte(' ')
|
||||
if f.sharp {
|
||||
// Add leading 0x or 0X for each element.
|
||||
- buf = append(buf, '0', digits[16])
|
||||
+ f.buf.WriteByte('0')
|
||||
+ f.buf.WriteByte(digits[16])
|
||||
}
|
||||
}
|
||||
if b != nil {
|
||||
@@ -422,9 +418,9 @@
|
||||
c = s[i] // Take a byte from the input string.
|
||||
}
|
||||
// Encode each byte as two hexadecimal digits.
|
||||
- buf = append(buf, digits[c>>4], digits[c&0xF])
|
||||
+ f.buf.WriteByte(digits[c>>4])
|
||||
+ f.buf.WriteByte(digits[c&0xF])
|
||||
}
|
||||
- *f.buf = buf
|
||||
// Handle padding to the right.
|
||||
if f.widPresent && f.wid > width && f.minus {
|
||||
f.writePadding(f.wid - width)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user