Compare commits
2 Commits
37898d3013
...
ac243bf4fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac243bf4fd | ||
|
|
747ff1454e |
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -131,7 +130,7 @@ func ReadConfig() MyConfig {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := ioutil.ReadFile(location)
|
buf, err := os.ReadFile(location)
|
||||||
|
|
||||||
// Не удалось прочитать, попробуем следующего кандидата
|
// Не удалось прочитать, попробуем следующего кандидата
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -388,32 +388,32 @@ func msgParser(ctx context.Context, msg string) {
|
|||||||
j.Message = fortune()
|
j.Message = fortune()
|
||||||
} else if (cmd == "karma") || (cmd == "карма") {
|
} else if (cmd == "karma") || (cmd == "карма") {
|
||||||
j.Message = getKarma(j.ChatID, "")
|
j.Message = getKarma(j.ChatID, "")
|
||||||
} else if match, _ := regexp.MatchString("^(rum|ром)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(rum|ром)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me притаскивает на подносе стопку рома для %s, края стопки искрятся кристаллами соли."
|
format := "/me притаскивает на подносе стопку рома для %s, края стопки искрятся кристаллами соли."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^(vodka|водка)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(vodka|водка)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me подаёт шот водки с небольшим маринованным огурчиком на блюдце для %s. Из огурчика торчит "
|
format := "/me подаёт шот водки с небольшим маринованным огурчиком на блюдце для %s. Из огурчика торчит "
|
||||||
format += "небольшая вилочка."
|
format += "небольшая вилочка."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^(beer|пиво)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(beer|пиво)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me бахает об стол перед %s кружкой холодного пива, часть пенной шапки сползает по запотевшей "
|
format := "/me бахает об стол перед %s кружкой холодного пива, часть пенной шапки сползает по запотевшей "
|
||||||
format += "стенке кружки."
|
format += "стенке кружки."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^(tequila|текила)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(tequila|текила)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me ставит рядом с %s шот текилы, аккуратно на ребро стопки насаживает дольку лайма и ставит "
|
format := "/me ставит рядом с %s шот текилы, аккуратно на ребро стопки насаживает дольку лайма и ставит "
|
||||||
format += "кофейное блюдце с горочкой соли."
|
format += "кофейное блюдце с горочкой соли."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^(whisky|виски)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(whisky|виски)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me демонстративно достаёт из морозилки пару кубических камушков, бросает их в толстодонный "
|
format := "/me демонстративно достаёт из морозилки пару кубических камушков, бросает их в толстодонный "
|
||||||
format += "стакан и аккуратно наливает Jack Daniels. Запускает стакан вдоль барной стойки, он "
|
format += "стакан и аккуратно наливает Jack Daniels. Запускает стакан вдоль барной стойки, он "
|
||||||
format += "останавливается около %s."
|
format += "останавливается около %s."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^(absinth|абсент)[::space::]?([::space::].+)*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^(absinth|абсент)[[:space:]]?([[:space:]].+)*$", cmd); match {
|
||||||
format := "/me наливает абсент в стопку. Смочив кубик сахара в абсенте кладёт его на дырявую ложечку и "
|
format := "/me наливает абсент в стопку. Смочив кубик сахара в абсенте кладёт его на дырявую ложечку и "
|
||||||
format += "поджигает. Как только пламя потухнет, %s размешивает оплавившийся кубик в абсенте и подносит "
|
format += "поджигает. Как только пламя потухнет, %s размешивает оплавившийся кубик в абсенте и подносит "
|
||||||
format += "стопку %s."
|
format += "стопку %s."
|
||||||
j.Message = fmt.Sprintf(format, j.Misc.BotNick, j.Misc.Username)
|
j.Message = fmt.Sprintf(format, j.Misc.BotNick, j.Misc.Username)
|
||||||
} else if match, _ := regexp.MatchString("^fuck[::space::]*$", cmd); match {
|
} else if match, _ := regexp.MatchString("^fuck[[:space:]]*$", cmd); match {
|
||||||
j.Message = "Не ругайся."
|
j.Message = "Не ругайся."
|
||||||
} else {
|
} else {
|
||||||
cmdLen := len(cmd)
|
cmdLen := len(cmd)
|
||||||
|
|||||||
20
go.mod
20
go.mod
@@ -3,11 +3,11 @@ module main
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cockroachdb/errors v1.9.1
|
github.com/cockroachdb/errors v1.10.0
|
||||||
github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6
|
github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible
|
github.com/hjson/hjson-go v3.3.0+incompatible
|
||||||
github.com/sirupsen/logrus v1.9.2
|
github.com/sirupsen/logrus v1.9.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -15,24 +15,24 @@ require (
|
|||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||||
github.com/cockroachdb/redact v1.1.4 // indirect
|
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/getsentry/sentry-go v0.21.0 // indirect
|
github.com/getsentry/sentry-go v0.21.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/klauspost/compress v1.16.5 // indirect
|
github.com/klauspost/compress v1.16.6 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.15.1 // indirect
|
github.com/prometheus/client_golang v1.15.1 // indirect
|
||||||
github.com/prometheus/client_model v0.4.0 // indirect
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
github.com/prometheus/common v0.43.0 // indirect
|
github.com/prometheus/common v0.44.0 // indirect
|
||||||
github.com/prometheus/procfs v0.9.0 // indirect
|
github.com/prometheus/procfs v0.10.1 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/sys v0.9.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.10.0 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
312
go.sum
312
go.sum
@@ -1,388 +1,120 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
|
||||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
|
||||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
|
||||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
|
||||||
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
|
||||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||||
github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8=
|
github.com/cockroachdb/errors v1.10.0 h1:lfxS8zZz1+OjtV4MtNWgboi/W5tyLEB6VQZBXN+0VUU=
|
||||||
github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk=
|
github.com/cockroachdb/errors v1.10.0/go.mod h1:lknhIsEVQ9Ss/qKDBQS/UqFSvPQjOwNq2qyKAxtHRqE=
|
||||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||||
github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6 h1:qmPygPODBjUWt7dT3kR118Yn7y0Ol6ULetf78jj/fNQ=
|
github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839 h1:avSJHOeU0yPVq2rSBuBLlBC7Q6hU1J3IEPAt9fK7bvY=
|
||||||
github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6/go.mod h1:TkdVsGYRqtULUppt2RbC+YaKtTHnHoWa2apfFrSKABw=
|
github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839/go.mod h1:TkdVsGYRqtULUppt2RbC+YaKtTHnHoWa2apfFrSKABw=
|
||||||
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
|
||||||
github.com/cockroachdb/redact v1.1.4 h1:Y0XrVI2FAyofNyGveodTN//qdpPtFKcKTeCBsK3AHAQ=
|
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||||
github.com/cockroachdb/redact v1.1.4/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
|
||||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
|
||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
|
||||||
github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
|
|
||||||
github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4=
|
github.com/getsentry/sentry-go v0.21.0 h1:c9l5F1nPF30JIppulk4veau90PK6Smu3abgVtVQWon4=
|
||||||
github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
github.com/getsentry/sentry-go v0.21.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
|
||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
|
||||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
|
||||||
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
|
||||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
|
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
|
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
|
||||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
|
|
||||||
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
|
|
||||||
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
|
|
||||||
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
|
|
||||||
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
|
||||||
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
|
|
||||||
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
|
|
||||||
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
|
|
||||||
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
|
|
||||||
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=
|
||||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
|
|
||||||
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
|
||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
|
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
|
||||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
|
||||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
|
||||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||||
github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us=
|
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||||
github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc=
|
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
|
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
|
|
||||||
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
|
||||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
|
||||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
|
||||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
|
||||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
|
||||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
|
||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
|
||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
|
|
||||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
|
||||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
|
||||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ func main() {
|
|||||||
|
|
||||||
// "Заглатывать" файл целиком - плохая практика, но мы предополагаем, что файлы будут небольшие.
|
// "Заглатывать" файл целиком - плохая практика, но мы предополагаем, что файлы будут небольшие.
|
||||||
fortuneFile := fmt.Sprintf("%s/%s", srcDir, file.Name())
|
fortuneFile := fmt.Sprintf("%s/%s", srcDir, file.Name())
|
||||||
bytesRead, err := ioutil.ReadFile(fortuneFile)
|
bytesRead, err := os.ReadFile(fortuneFile)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to read %s: %s\n", file.Name(), err)
|
log.Errorf("Unable to read %s: %s\n", file.Name(), err)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -80,7 +79,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
file := config.DataDir + "/friday_src/friday.txt"
|
file := config.DataDir + "/friday_src/friday.txt"
|
||||||
bytesRead, err := ioutil.ReadFile(file)
|
bytesRead, err := os.ReadFile(file)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to read %s: %s\n", file, err)
|
log.Errorf("Unable to read %s: %s\n", file, err)
|
||||||
|
|||||||
12
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
12
vendor/github.com/cockroachdb/pebble/Makefile
generated
vendored
@@ -16,6 +16,8 @@ all:
|
|||||||
@echo " make stressmeta"
|
@echo " make stressmeta"
|
||||||
@echo " make crossversion-meta"
|
@echo " make crossversion-meta"
|
||||||
@echo " make mod-update"
|
@echo " make mod-update"
|
||||||
|
@echo " make generate"
|
||||||
|
@echo " make generate-test-data"
|
||||||
@echo " make clean"
|
@echo " make clean"
|
||||||
|
|
||||||
override testflags :=
|
override testflags :=
|
||||||
@@ -69,6 +71,16 @@ stress-crossversion:
|
|||||||
generate:
|
generate:
|
||||||
${GO} generate ${PKG}
|
${GO} generate ${PKG}
|
||||||
|
|
||||||
|
generate:
|
||||||
|
|
||||||
|
# Note that the output of generate-test-data is not deterministic. This should
|
||||||
|
# only be run manually as needed.
|
||||||
|
.PHONY: generate-test-data
|
||||||
|
generate-test-data:
|
||||||
|
${GO} run -tags make_incorrect_manifests ./tool/make_incorrect_manifests.go
|
||||||
|
${GO} run -tags make_test_find_db ./tool/make_test_find_db.go
|
||||||
|
${GO} run -tags make_test_sstables ./tool/make_test_sstables.go
|
||||||
|
|
||||||
mod-update:
|
mod-update:
|
||||||
${GO} get -u
|
${GO} get -u
|
||||||
${GO} mod tidy
|
${GO} mod tidy
|
||||||
|
|||||||
133
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
133
vendor/github.com/cockroachdb/pebble/batch.go
generated
vendored
@@ -265,6 +265,13 @@ type Batch struct {
|
|||||||
// then it will only contain key kinds of IngestSST.
|
// then it will only contain key kinds of IngestSST.
|
||||||
ingestedSSTBatch bool
|
ingestedSSTBatch bool
|
||||||
|
|
||||||
|
// minimumFormatMajorVersion indicates the format major version required in
|
||||||
|
// order to commit this batch. If an operation requires a particular format
|
||||||
|
// major version, it ratchets the batch's minimumFormatMajorVersion. When
|
||||||
|
// the batch is committed, this is validated against the database's current
|
||||||
|
// format major version.
|
||||||
|
minimumFormatMajorVersion FormatMajorVersion
|
||||||
|
|
||||||
// Synchronous Apply uses the commit WaitGroup for both publishing the
|
// Synchronous Apply uses the commit WaitGroup for both publishing the
|
||||||
// seqnum and waiting for the WAL fsync (if needed). Asynchronous
|
// seqnum and waiting for the WAL fsync (if needed). Asynchronous
|
||||||
// ApplyNoSyncWait, which implies WriteOptions.Sync is true, uses the commit
|
// ApplyNoSyncWait, which implies WriteOptions.Sync is true, uses the commit
|
||||||
@@ -413,6 +420,7 @@ func (b *Batch) refreshMemTableSize() {
|
|||||||
|
|
||||||
b.countRangeDels = 0
|
b.countRangeDels = 0
|
||||||
b.countRangeKeys = 0
|
b.countRangeKeys = 0
|
||||||
|
b.minimumFormatMajorVersion = 0
|
||||||
for r := b.Reader(); ; {
|
for r := b.Reader(); ; {
|
||||||
kind, key, value, ok := r.Next()
|
kind, key, value, ok := r.Next()
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -423,12 +431,22 @@ func (b *Batch) refreshMemTableSize() {
|
|||||||
b.countRangeDels++
|
b.countRangeDels++
|
||||||
case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
||||||
b.countRangeKeys++
|
b.countRangeKeys++
|
||||||
|
case InternalKeyKindDeleteSized:
|
||||||
|
if b.minimumFormatMajorVersion < ExperimentalFormatDeleteSizedAndObsolete {
|
||||||
|
b.minimumFormatMajorVersion = ExperimentalFormatDeleteSizedAndObsolete
|
||||||
|
}
|
||||||
case InternalKeyKindIngestSST:
|
case InternalKeyKindIngestSST:
|
||||||
|
if b.minimumFormatMajorVersion < FormatFlushableIngest {
|
||||||
|
b.minimumFormatMajorVersion = FormatFlushableIngest
|
||||||
|
}
|
||||||
// This key kind doesn't contribute to the memtable size.
|
// This key kind doesn't contribute to the memtable size.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.memTableSize += memTableEntrySize(len(key), len(value))
|
b.memTableSize += memTableEntrySize(len(key), len(value))
|
||||||
}
|
}
|
||||||
|
if b.countRangeKeys > 0 && b.minimumFormatMajorVersion < FormatRangeKeys {
|
||||||
|
b.minimumFormatMajorVersion = FormatRangeKeys
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the operations contained in the batch to the receiver batch.
|
// Apply the operations contained in the batch to the receiver batch.
|
||||||
@@ -598,6 +616,41 @@ func (b *Batch) prepareDeferredKeyRecord(keyLen int, kind InternalKeyKind) {
|
|||||||
b.data = b.data[:pos+keyLen]
|
b.data = b.data[:pos+keyLen]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddInternalKey allows the caller to add an internal key of point key kinds to
|
||||||
|
// a batch. Passing in an internal key of kind RangeKey* or RangeDelete will
|
||||||
|
// result in a panic. Note that the seqnum in the internal key is effectively
|
||||||
|
// ignored, even though the Kind is preserved. This is because the batch format
|
||||||
|
// does not allow for a per-key seqnum to be specified, only a batch-wide one.
|
||||||
|
//
|
||||||
|
// Note that non-indexed keys (IngestKeyKind{LogData,IngestSST}) are not
|
||||||
|
// supported with this method as they require specialized logic.
|
||||||
|
func (b *Batch) AddInternalKey(key *base.InternalKey, value []byte, _ *WriteOptions) error {
|
||||||
|
keyLen := len(key.UserKey)
|
||||||
|
hasValue := false
|
||||||
|
switch key.Kind() {
|
||||||
|
case InternalKeyKindRangeDelete, InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
||||||
|
panic("unexpected range delete or range key kind in AddInternalKey")
|
||||||
|
case InternalKeyKindSingleDelete, InternalKeyKindDelete:
|
||||||
|
b.prepareDeferredKeyRecord(len(key.UserKey), key.Kind())
|
||||||
|
default:
|
||||||
|
b.prepareDeferredKeyValueRecord(keyLen, len(value), key.Kind())
|
||||||
|
hasValue = true
|
||||||
|
}
|
||||||
|
b.deferredOp.index = b.index
|
||||||
|
copy(b.deferredOp.Key, key.UserKey)
|
||||||
|
if hasValue {
|
||||||
|
copy(b.deferredOp.Value, value)
|
||||||
|
}
|
||||||
|
// TODO(peter): Manually inline DeferredBatchOp.Finish(). Mid-stack inlining
|
||||||
|
// in go1.13 will remove the need for this.
|
||||||
|
if b.index != nil {
|
||||||
|
if err := b.index.Add(b.deferredOp.offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Set adds an action to the batch that sets the key to map to the value.
|
// Set adds an action to the batch that sets the key to map to the value.
|
||||||
//
|
//
|
||||||
// It is safe to modify the contents of the arguments after Set returns.
|
// It is safe to modify the contents of the arguments after Set returns.
|
||||||
@@ -680,6 +733,72 @@ func (b *Batch) DeleteDeferred(keyLen int) *DeferredBatchOp {
|
|||||||
return &b.deferredOp
|
return &b.deferredOp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteSized behaves identically to Delete, but takes an additional
|
||||||
|
// argument indicating the size of the value being deleted. DeleteSized
|
||||||
|
// should be preferred when the caller has the expectation that there exists
|
||||||
|
// a single internal KV pair for the key (eg, the key has not been
|
||||||
|
// overwritten recently), and the caller knows the size of its value.
|
||||||
|
//
|
||||||
|
// DeleteSized will record the value size within the tombstone and use it to
|
||||||
|
// inform compaction-picking heuristics which strive to reduce space
|
||||||
|
// amplification in the LSM. This "calling your shot" mechanic allows the
|
||||||
|
// storage engine to more accurately estimate and reduce space amplification.
|
||||||
|
//
|
||||||
|
// It is safe to modify the contents of the arguments after DeleteSized
|
||||||
|
// returns.
|
||||||
|
func (b *Batch) DeleteSized(key []byte, deletedValueSize uint32, _ *WriteOptions) error {
|
||||||
|
deferredOp := b.DeleteSizedDeferred(len(key), deletedValueSize)
|
||||||
|
copy(b.deferredOp.Key, key)
|
||||||
|
// TODO(peter): Manually inline DeferredBatchOp.Finish(). Check if in a
|
||||||
|
// later Go release this is unnecessary.
|
||||||
|
if b.index != nil {
|
||||||
|
if err := b.index.Add(deferredOp.offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSizedDeferred is similar to DeleteSized in that it adds a sized delete
|
||||||
|
// operation to the batch, except it only takes in key length instead of a
|
||||||
|
// complete key slice, letting the caller encode into the DeferredBatchOp.Key
|
||||||
|
// slice and then call Finish() on the returned object.
|
||||||
|
func (b *Batch) DeleteSizedDeferred(keyLen int, deletedValueSize uint32) *DeferredBatchOp {
|
||||||
|
if b.minimumFormatMajorVersion < ExperimentalFormatDeleteSizedAndObsolete {
|
||||||
|
b.minimumFormatMajorVersion = ExperimentalFormatDeleteSizedAndObsolete
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the sum of the key length and the value in the value.
|
||||||
|
v := uint64(deletedValueSize) + uint64(keyLen)
|
||||||
|
|
||||||
|
// Encode `v` as a varint.
|
||||||
|
var buf [binary.MaxVarintLen64]byte
|
||||||
|
n := 0
|
||||||
|
{
|
||||||
|
x := v
|
||||||
|
for x >= 0x80 {
|
||||||
|
buf[n] = byte(x) | 0x80
|
||||||
|
x >>= 7
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
buf[n] = byte(x)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: In batch entries and sstable entries, values are stored as
|
||||||
|
// varstrings. Here, the value is itself a simple varint. This results in an
|
||||||
|
// unnecessary double layer of encoding:
|
||||||
|
// varint(n) varint(deletedValueSize)
|
||||||
|
// The first varint will always be 1-byte, since a varint-encoded uint64
|
||||||
|
// will never exceed 128 bytes. This unnecessary extra byte and wrapping is
|
||||||
|
// preserved to avoid special casing across the database, and in particular
|
||||||
|
// in sstable block decoding which is performance sensitive.
|
||||||
|
b.prepareDeferredKeyValueRecord(keyLen, n, InternalKeyKindDeleteSized)
|
||||||
|
b.deferredOp.index = b.index
|
||||||
|
copy(b.deferredOp.Value, buf[:n])
|
||||||
|
return &b.deferredOp
|
||||||
|
}
|
||||||
|
|
||||||
// SingleDelete adds an action to the batch that single deletes the entry for key.
|
// SingleDelete adds an action to the batch that single deletes the entry for key.
|
||||||
// See Writer.SingleDelete for more details on the semantics of SingleDelete.
|
// See Writer.SingleDelete for more details on the semantics of SingleDelete.
|
||||||
//
|
//
|
||||||
@@ -782,6 +901,9 @@ func (b *Batch) rangeKeySetDeferred(startLen, internalValueLen int) *DeferredBat
|
|||||||
|
|
||||||
func (b *Batch) incrementRangeKeysCount() {
|
func (b *Batch) incrementRangeKeysCount() {
|
||||||
b.countRangeKeys++
|
b.countRangeKeys++
|
||||||
|
if b.minimumFormatMajorVersion < FormatRangeKeys {
|
||||||
|
b.minimumFormatMajorVersion = FormatRangeKeys
|
||||||
|
}
|
||||||
if b.index != nil {
|
if b.index != nil {
|
||||||
b.rangeKeys = nil
|
b.rangeKeys = nil
|
||||||
b.rangeKeysSeqNum = 0
|
b.rangeKeysSeqNum = 0
|
||||||
@@ -895,6 +1017,7 @@ func (b *Batch) ingestSST(fileNum base.FileNum) {
|
|||||||
// is not reset because for the InternalKeyKindIngestSST the count is the
|
// is not reset because for the InternalKeyKindIngestSST the count is the
|
||||||
// number of sstable paths which have been added to the batch.
|
// number of sstable paths which have been added to the batch.
|
||||||
b.memTableSize = origMemTableSize
|
b.memTableSize = origMemTableSize
|
||||||
|
b.minimumFormatMajorVersion = FormatFlushableIngest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty returns true if the batch is empty, and false otherwise.
|
// Empty returns true if the batch is empty, and false otherwise.
|
||||||
@@ -1199,6 +1322,7 @@ func (b *Batch) Reset() {
|
|||||||
b.commitStats = BatchCommitStats{}
|
b.commitStats = BatchCommitStats{}
|
||||||
b.commitErr = nil
|
b.commitErr = nil
|
||||||
b.applied.Store(false)
|
b.applied.Store(false)
|
||||||
|
b.minimumFormatMajorVersion = 0
|
||||||
if b.data != nil {
|
if b.data != nil {
|
||||||
if cap(b.data) > batchMaxRetainedSize {
|
if cap(b.data) > batchMaxRetainedSize {
|
||||||
// If the capacity of the buffer is larger than our maximum
|
// If the capacity of the buffer is larger than our maximum
|
||||||
@@ -1365,7 +1489,8 @@ func (r *BatchReader) Next() (kind InternalKeyKind, ukey []byte, value []byte, o
|
|||||||
}
|
}
|
||||||
switch kind {
|
switch kind {
|
||||||
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
||||||
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete,
|
||||||
|
InternalKeyKindDeleteSized:
|
||||||
*r, value, ok = batchDecodeStr(*r)
|
*r, value, ok = batchDecodeStr(*r)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, nil, nil, false
|
return 0, nil, nil, false
|
||||||
@@ -1500,7 +1625,8 @@ func (i *batchIter) value() []byte {
|
|||||||
|
|
||||||
switch InternalKeyKind(data[offset]) {
|
switch InternalKeyKind(data[offset]) {
|
||||||
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
||||||
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete,
|
||||||
|
InternalKeyKindDeleteSized:
|
||||||
_, value, ok := batchDecodeStr(data[keyEnd:])
|
_, value, ok := batchDecodeStr(data[keyEnd:])
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@@ -1944,7 +2070,8 @@ func (i *flushableBatchIter) value() base.LazyValue {
|
|||||||
var ok bool
|
var ok bool
|
||||||
switch kind {
|
switch kind {
|
||||||
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete,
|
||||||
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete:
|
InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete,
|
||||||
|
InternalKeyKindDeleteSized:
|
||||||
keyEnd := i.offsets[i.index].keyEnd
|
keyEnd := i.offsets[i.index].keyEnd
|
||||||
_, value, ok = batchDecodeStr(i.data[keyEnd:])
|
_, value, ok = batchDecodeStr(i.data[keyEnd:])
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
68
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
68
vendor/github.com/cockroachdb/pebble/compaction.go
generated
vendored
@@ -1333,10 +1333,10 @@ func (c *compaction) newInputIter(
|
|||||||
// internal iterator interface). The resulting merged rangedel iterator is
|
// internal iterator interface). The resulting merged rangedel iterator is
|
||||||
// then included with the point levels in a single mergingIter.
|
// then included with the point levels in a single mergingIter.
|
||||||
newRangeDelIter := func(
|
newRangeDelIter := func(
|
||||||
f manifest.LevelFile, _ *IterOptions, bytesIterated *uint64,
|
f manifest.LevelFile, _ *IterOptions, l manifest.Level, bytesIterated *uint64,
|
||||||
) (keyspan.FragmentIterator, error) {
|
) (keyspan.FragmentIterator, error) {
|
||||||
iter, rangeDelIter, err := newIters(context.Background(), f.FileMetadata,
|
iter, rangeDelIter, err := newIters(context.Background(), f.FileMetadata,
|
||||||
nil /* iter options */, internalIterOpts{bytesIterated: &c.bytesIterated})
|
&IterOptions{level: l}, internalIterOpts{bytesIterated: &c.bytesIterated})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO(peter): It is mildly wasteful to open the point iterator only to
|
// 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
|
// immediately close it. One way to solve this would be to add new
|
||||||
@@ -1415,6 +1415,10 @@ func (c *compaction) newInputIter(
|
|||||||
iterOpts := IterOptions{logger: c.logger}
|
iterOpts := IterOptions{logger: c.logger}
|
||||||
// TODO(bananabrick): Get rid of the extra manifest.Level parameter and fold it into
|
// TODO(bananabrick): Get rid of the extra manifest.Level parameter and fold it into
|
||||||
// compactionLevel.
|
// compactionLevel.
|
||||||
|
//
|
||||||
|
// TODO(bilal): when we start using strict obsolete sstables for L5 and L6
|
||||||
|
// in disaggregated storage, and rely on the obsolete bit, we will also need
|
||||||
|
// to configure the levelIter at these levels to hide the obsolete points.
|
||||||
addItersForLevel := func(level *compactionLevel, l manifest.Level) error {
|
addItersForLevel := func(level *compactionLevel, l manifest.Level) error {
|
||||||
iters = append(iters, newLevelIter(iterOpts, c.cmp, nil /* split */, newIters,
|
iters = append(iters, newLevelIter(iterOpts, c.cmp, nil /* split */, newIters,
|
||||||
level.files.Iter(), l, &c.bytesIterated))
|
level.files.Iter(), l, &c.bytesIterated))
|
||||||
@@ -1455,8 +1459,10 @@ func (c *compaction) newInputIter(
|
|||||||
// mergingIter.
|
// mergingIter.
|
||||||
iter := level.files.Iter()
|
iter := level.files.Iter()
|
||||||
for f := iter.First(); f != nil; f = iter.Next() {
|
for f := iter.First(); f != nil; f = iter.Next() {
|
||||||
rangeDelIter, err := newRangeDelIter(iter.Take(), nil, &c.bytesIterated)
|
rangeDelIter, err := newRangeDelIter(iter.Take(), nil, l, &c.bytesIterated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// The error will already be annotated with the BackingFileNum, so
|
||||||
|
// we annotate it with the FileNum.
|
||||||
return errors.Wrapf(err, "pebble: could not open table %s", errors.Safe(f.FileNum))
|
return errors.Wrapf(err, "pebble: could not open table %s", errors.Safe(f.FileNum))
|
||||||
}
|
}
|
||||||
if rangeDelIter != emptyKeyspanIter {
|
if rangeDelIter != emptyKeyspanIter {
|
||||||
@@ -1474,7 +1480,7 @@ func (c *compaction) newInputIter(
|
|||||||
}
|
}
|
||||||
if hasRangeKeys {
|
if hasRangeKeys {
|
||||||
li := &keyspan.LevelIter{}
|
li := &keyspan.LevelIter{}
|
||||||
newRangeKeyIterWrapper := func(file *manifest.FileMetadata, iterOptions *keyspan.SpanIterOptions) (keyspan.FragmentIterator, error) {
|
newRangeKeyIterWrapper := func(file *manifest.FileMetadata, iterOptions keyspan.SpanIterOptions) (keyspan.FragmentIterator, error) {
|
||||||
iter, err := newRangeKeyIter(file, iterOptions)
|
iter, err := newRangeKeyIter(file, iterOptions)
|
||||||
if iter != nil {
|
if iter != nil {
|
||||||
// Ensure that the range key iter is not closed until the compaction is
|
// Ensure that the range key iter is not closed until the compaction is
|
||||||
@@ -1495,7 +1501,7 @@ func (c *compaction) newInputIter(
|
|||||||
}
|
}
|
||||||
return iter, err
|
return iter, err
|
||||||
}
|
}
|
||||||
li.Init(keyspan.SpanIterOptions{}, c.cmp, newRangeKeyIterWrapper, level.files.Iter(), l, manifest.KeyTypeRange)
|
li.Init(keyspan.SpanIterOptions{Level: l}, c.cmp, newRangeKeyIterWrapper, level.files.Iter(), l, manifest.KeyTypeRange)
|
||||||
rangeKeyIters = append(rangeKeyIters, li)
|
rangeKeyIters = append(rangeKeyIters, li)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -2066,12 +2072,12 @@ func (d *DB) flush1() (bytesFlushed uint64, err error) {
|
|||||||
bytesFlushed = c.bytesIterated
|
bytesFlushed = c.bytesIterated
|
||||||
d.mu.snapshots.cumulativePinnedCount += stats.cumulativePinnedKeys
|
d.mu.snapshots.cumulativePinnedCount += stats.cumulativePinnedKeys
|
||||||
d.mu.snapshots.cumulativePinnedSize += stats.cumulativePinnedSize
|
d.mu.snapshots.cumulativePinnedSize += stats.cumulativePinnedSize
|
||||||
|
d.mu.versions.metrics.Keys.MissizedTombstonesCount += stats.countMissizedDels
|
||||||
|
|
||||||
d.maybeUpdateDeleteCompactionHints(c)
|
d.maybeUpdateDeleteCompactionHints(c)
|
||||||
d.clearCompactingState(c, err != nil)
|
d.clearCompactingState(c, err != nil)
|
||||||
delete(d.mu.compact.inProgress, c)
|
delete(d.mu.compact.inProgress, c)
|
||||||
d.mu.versions.incrementCompactions(c.kind, c.extraLevels)
|
d.mu.versions.incrementCompactions(c.kind, c.extraLevels)
|
||||||
d.mu.versions.incrementCompactionBytes(-c.bytesWritten)
|
|
||||||
|
|
||||||
var flushed flushableList
|
var flushed flushableList
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -2559,9 +2565,20 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) {
|
|||||||
info.Duration = d.timeNow().Sub(startTime)
|
info.Duration = d.timeNow().Sub(startTime)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
d.mu.versions.logLock()
|
d.mu.versions.logLock()
|
||||||
err = d.mu.versions.logAndApply(jobID, ve, c.metrics, false /* forceRotation */, func() []compactionInfo {
|
// Confirm if any of this compaction's inputs were deleted while this
|
||||||
return d.getInProgressCompactionInfoLocked(c)
|
// compaction was ongoing.
|
||||||
})
|
for i := range c.inputs {
|
||||||
|
c.inputs[i].files.Each(func(m *manifest.FileMetadata) {
|
||||||
|
if m.Deleted {
|
||||||
|
err = firstError(err, errors.New("pebble: file deleted by a concurrent operation, will retry compaction"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = d.mu.versions.logAndApply(jobID, ve, c.metrics, false /* forceRotation */, func() []compactionInfo {
|
||||||
|
return d.getInProgressCompactionInfoLocked(c)
|
||||||
|
})
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(peter): untested.
|
// TODO(peter): untested.
|
||||||
for _, f := range pendingOutputs {
|
for _, f := range pendingOutputs {
|
||||||
@@ -2617,6 +2634,7 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) {
|
|||||||
type compactStats struct {
|
type compactStats struct {
|
||||||
cumulativePinnedKeys uint64
|
cumulativePinnedKeys uint64
|
||||||
cumulativePinnedSize uint64
|
cumulativePinnedSize uint64
|
||||||
|
countMissizedDels uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCompactions runs a compaction that produces new on-disk tables from
|
// runCompactions runs a compaction that produces new on-disk tables from
|
||||||
@@ -2850,10 +2868,12 @@ func (d *DB) runCompaction(
|
|||||||
Path: d.objProvider.Path(objMeta),
|
Path: d.objProvider.Path(objMeta),
|
||||||
FileNum: fileNum,
|
FileNum: fileNum,
|
||||||
})
|
})
|
||||||
writable = &compactionWritable{
|
if c.kind != compactionKindFlush {
|
||||||
Writable: writable,
|
writable = &compactionWritable{
|
||||||
versions: d.mu.versions,
|
Writable: writable,
|
||||||
written: &c.bytesWritten,
|
versions: d.mu.versions,
|
||||||
|
written: &c.bytesWritten,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createdFiles = append(createdFiles, fileNum.DiskFileNum())
|
createdFiles = append(createdFiles, fileNum.DiskFileNum())
|
||||||
cacheOpts := private.SSTableCacheOpts(d.cacheID, fileNum.DiskFileNum()).(sstable.WriterOption)
|
cacheOpts := private.SSTableCacheOpts(d.cacheID, fileNum.DiskFileNum()).(sstable.WriterOption)
|
||||||
@@ -3232,7 +3252,10 @@ func (d *DB) runCompaction(
|
|||||||
return nil, pendingOutputs, stats, err
|
return nil, pendingOutputs, stats, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := tw.Add(*key, val); err != nil {
|
// iter.snapshotPinned is broader than whether the point was covered by
|
||||||
|
// a RANGEDEL, but it is harmless to pass true when the callee will also
|
||||||
|
// independently discover that the point is obsolete.
|
||||||
|
if err := tw.AddWithForceObsolete(*key, val, iter.snapshotPinned); err != nil {
|
||||||
return nil, pendingOutputs, stats, err
|
return nil, pendingOutputs, stats, err
|
||||||
}
|
}
|
||||||
if iter.snapshotPinned {
|
if iter.snapshotPinned {
|
||||||
@@ -3285,6 +3308,11 @@ func (d *DB) runCompaction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The compaction iterator keeps track of a count of the number of DELSIZED
|
||||||
|
// keys that encoded an incorrect size. Propagate it up as a part of
|
||||||
|
// compactStats.
|
||||||
|
stats.countMissizedDels = iter.stats.countMissizedDels
|
||||||
|
|
||||||
if err := d.objProvider.Sync(); err != nil {
|
if err := d.objProvider.Sync(); err != nil {
|
||||||
return nil, pendingOutputs, stats, err
|
return nil, pendingOutputs, stats, err
|
||||||
}
|
}
|
||||||
@@ -3613,7 +3641,7 @@ func (d *DB) doDeleteObsoleteFiles(jobID int) {
|
|||||||
if len(filesToDelete) > 0 {
|
if len(filesToDelete) > 0 {
|
||||||
d.deleters.Add(1)
|
d.deleters.Add(1)
|
||||||
// Delete asynchronously if that could get held up in the pacer.
|
// Delete asynchronously if that could get held up in the pacer.
|
||||||
if d.opts.Experimental.MinDeletionRate > 0 {
|
if d.opts.TargetByteDeletionRate > 0 {
|
||||||
go d.paceAndDeleteObsoleteFiles(jobID, filesToDelete)
|
go d.paceAndDeleteObsoleteFiles(jobID, filesToDelete)
|
||||||
} else {
|
} else {
|
||||||
d.paceAndDeleteObsoleteFiles(jobID, filesToDelete)
|
d.paceAndDeleteObsoleteFiles(jobID, filesToDelete)
|
||||||
@@ -3626,14 +3654,20 @@ func (d *DB) doDeleteObsoleteFiles(jobID int) {
|
|||||||
func (d *DB) paceAndDeleteObsoleteFiles(jobID int, files []obsoleteFile) {
|
func (d *DB) paceAndDeleteObsoleteFiles(jobID int, files []obsoleteFile) {
|
||||||
defer d.deleters.Done()
|
defer d.deleters.Done()
|
||||||
pacer := (pacer)(nilPacer)
|
pacer := (pacer)(nilPacer)
|
||||||
if d.opts.Experimental.MinDeletionRate > 0 {
|
if d.opts.TargetByteDeletionRate > 0 {
|
||||||
pacer = newDeletionPacer(d.deletionLimiter, d.getDeletionPacerInfo)
|
pacer = newDeletionPacer(d.deletionLimiter, d.getDeletionPacerInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, of := range files {
|
for _, of := range files {
|
||||||
path := base.MakeFilepath(d.opts.FS, of.dir, of.fileType, of.fileNum)
|
path := base.MakeFilepath(d.opts.FS, of.dir, of.fileType, of.fileNum)
|
||||||
if of.fileType == fileTypeTable {
|
if of.fileType == fileTypeTable {
|
||||||
_ = pacer.maybeThrottle(of.fileSize)
|
// Don't throttle deletion of shared objects.
|
||||||
|
meta, err := d.objProvider.Lookup(of.fileType, of.fileNum)
|
||||||
|
// If we get an error here, deleteObsoleteObject won't actually delete
|
||||||
|
// anything, so we don't need to throttle.
|
||||||
|
if err == nil && !meta.IsShared() {
|
||||||
|
_ = pacer.maybeThrottle(of.fileSize)
|
||||||
|
}
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
d.mu.versions.metrics.Table.ObsoleteCount--
|
d.mu.versions.metrics.Table.ObsoleteCount--
|
||||||
d.mu.versions.metrics.Table.ObsoleteSize -= of.fileSize
|
d.mu.versions.metrics.Table.ObsoleteSize -= of.fileSize
|
||||||
|
|||||||
160
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
160
vendor/github.com/cockroachdb/pebble/compaction_iter.go
generated
vendored
@@ -6,6 +6,7 @@ package pebble
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -205,6 +206,21 @@ type compactionIter struct {
|
|||||||
// compaction iterator was only returned because an open snapshot prevents
|
// compaction iterator was only returned because an open snapshot prevents
|
||||||
// its elision. This field only applies to point keys, and not to range
|
// its elision. This field only applies to point keys, and not to range
|
||||||
// deletions or range keys.
|
// deletions or range keys.
|
||||||
|
//
|
||||||
|
// snapshotPinned is also used to set the forceObsolete value in the call to
|
||||||
|
// Writer.AddWithForceObsolete. Note that in that call, it is sufficient to
|
||||||
|
// mark all keys obsoleted by RANGEDELs as forceObsolete=true and that the
|
||||||
|
// implementation of Writer.AddWithForceObsolete will itself discover other
|
||||||
|
// causes of obsolescence. We mention this since in the presence of MERGE,
|
||||||
|
// obsolescence due to multiple keys at the same user key is not fully
|
||||||
|
// represented by snapshotPinned=true:
|
||||||
|
//
|
||||||
|
// For MERGE, it is possible that doing the merge is interrupted even when
|
||||||
|
// the next point key is in the same stripe. This can happen if the loop in
|
||||||
|
// mergeNext gets interrupted by sameStripeNonSkippable.
|
||||||
|
// sameStripeNonSkippable occurs due to RANGEDELs that sort before
|
||||||
|
// SET/MERGE/DEL with the same seqnum, so the RANGEDEL does not necessarily
|
||||||
|
// delete the subsequent SET/MERGE/DEL keys.
|
||||||
snapshotPinned bool
|
snapshotPinned bool
|
||||||
// The index of the snapshot for the current key within the snapshots slice.
|
// The index of the snapshot for the current key within the snapshots slice.
|
||||||
curSnapshotIdx int
|
curSnapshotIdx int
|
||||||
@@ -240,6 +256,10 @@ type compactionIter struct {
|
|||||||
// The on-disk format major version. This informs the types of keys that
|
// The on-disk format major version. This informs the types of keys that
|
||||||
// may be written to disk during a compaction.
|
// may be written to disk during a compaction.
|
||||||
formatVersion FormatMajorVersion
|
formatVersion FormatMajorVersion
|
||||||
|
stats struct {
|
||||||
|
// count of DELSIZED keys that were missized.
|
||||||
|
countMissizedDels uint64
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCompactionIter(
|
func newCompactionIter(
|
||||||
@@ -310,10 +330,10 @@ func (i *compactionIter) Next() (*InternalKey, []byte) {
|
|||||||
// respect to `iterKey` and related state:
|
// respect to `iterKey` and related state:
|
||||||
//
|
//
|
||||||
// - `!skip && pos == iterPosNext`: `iterKey` is already at the next key.
|
// - `!skip && pos == iterPosNext`: `iterKey` is already at the next key.
|
||||||
// - `!skip && pos == iterPosCur`: We are at the key that has been returned.
|
// - `!skip && pos == iterPosCurForward`: We are at the key that has been returned.
|
||||||
// To move forward we advance by one key, even if that lands us in the same
|
// To move forward we advance by one key, even if that lands us in the same
|
||||||
// snapshot stripe.
|
// snapshot stripe.
|
||||||
// - `skip && pos == iterPosCur`: We are at the key that has been returned.
|
// - `skip && pos == iterPosCurForward`: We are at the key that has been returned.
|
||||||
// To move forward we skip skippable entries in the stripe.
|
// To move forward we skip skippable entries in the stripe.
|
||||||
if i.pos == iterPosCurForward {
|
if i.pos == iterPosCurForward {
|
||||||
if i.skip {
|
if i.skip {
|
||||||
@@ -382,11 +402,18 @@ func (i *compactionIter) Next() (*InternalKey, []byte) {
|
|||||||
} else if cover == keyspan.CoversInvisibly {
|
} else if cover == keyspan.CoversInvisibly {
|
||||||
// i.iterKey would be deleted by a range deletion if there weren't
|
// i.iterKey would be deleted by a range deletion if there weren't
|
||||||
// any open snapshots. Mark it as pinned.
|
// any open snapshots. Mark it as pinned.
|
||||||
|
//
|
||||||
|
// NB: there are multiple places in this file where we call
|
||||||
|
// i.rangeDelFrag.Covers and this is the only one where we are writing
|
||||||
|
// to i.snapshotPinned. Those other cases occur in mergeNext where the
|
||||||
|
// caller is deciding whether the value should be merged or not, and the
|
||||||
|
// key is in the same snapshot stripe. Hence, snapshotPinned is by
|
||||||
|
// definition false in those cases.
|
||||||
i.snapshotPinned = true
|
i.snapshotPinned = true
|
||||||
}
|
}
|
||||||
|
|
||||||
switch i.iterKey.Kind() {
|
switch i.iterKey.Kind() {
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
if i.elideTombstone(i.iterKey.UserKey) {
|
if i.elideTombstone(i.iterKey.UserKey) {
|
||||||
if i.curSnapshotIdx == 0 {
|
if i.curSnapshotIdx == 0 {
|
||||||
// If we're at the last snapshot stripe and the tombstone
|
// If we're at the last snapshot stripe and the tombstone
|
||||||
@@ -410,6 +437,12 @@ func (i *compactionIter) Next() (*InternalKey, []byte) {
|
|||||||
i.skip = true
|
i.skip = true
|
||||||
return &i.key, i.value
|
return &i.key, i.value
|
||||||
|
|
||||||
|
case InternalKeyKindDeleteSized:
|
||||||
|
// We may skip subsequent keys because of this tombstone. Scan
|
||||||
|
// ahead to see just how much data this tombstone drops and if
|
||||||
|
// the tombstone's value should be updated accordingly.
|
||||||
|
return i.deleteSizedNext()
|
||||||
|
|
||||||
case InternalKeyKindSingleDelete:
|
case InternalKeyKindSingleDelete:
|
||||||
if i.singleDeleteNext() {
|
if i.singleDeleteNext() {
|
||||||
return &i.key, i.value
|
return &i.key, i.value
|
||||||
@@ -686,11 +719,12 @@ func (i *compactionIter) setNext() {
|
|||||||
i.skip = true
|
i.skip = true
|
||||||
return
|
return
|
||||||
case sameStripeSkippable:
|
case sameStripeSkippable:
|
||||||
// We're still in the same stripe. If this is a DEL/SINGLEDEL, we
|
// We're still in the same stripe. If this is a
|
||||||
// stop looking and emit a SETWITHDEL. Subsequent keys are
|
// DEL/SINGLEDEL/DELSIZED, we stop looking and emit a SETWITHDEL.
|
||||||
// eligible for skipping.
|
// Subsequent keys are eligible for skipping.
|
||||||
if i.iterKey.Kind() == InternalKeyKindDelete ||
|
if i.iterKey.Kind() == InternalKeyKindDelete ||
|
||||||
i.iterKey.Kind() == InternalKeyKindSingleDelete {
|
i.iterKey.Kind() == InternalKeyKindSingleDelete ||
|
||||||
|
i.iterKey.Kind() == InternalKeyKindDeleteSized {
|
||||||
i.key.SetKind(InternalKeyKindSetWithDelete)
|
i.key.SetKind(InternalKeyKindSetWithDelete)
|
||||||
i.skip = true
|
i.skip = true
|
||||||
return
|
return
|
||||||
@@ -715,7 +749,7 @@ func (i *compactionIter) mergeNext(valueMerger ValueMerger) stripeChangeType {
|
|||||||
}
|
}
|
||||||
key := i.iterKey
|
key := i.iterKey
|
||||||
switch key.Kind() {
|
switch key.Kind() {
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
// We've hit a deletion tombstone. Return everything up to this point and
|
// We've hit a deletion tombstone. Return everything up to this point and
|
||||||
// then skip entries until the next snapshot stripe. We change the kind
|
// then skip entries until the next snapshot stripe. We change the kind
|
||||||
// of the result key to a Set so that it shadows keys in lower
|
// of the result key to a Set so that it shadows keys in lower
|
||||||
@@ -802,9 +836,9 @@ func (i *compactionIter) singleDeleteNext() bool {
|
|||||||
|
|
||||||
key := i.iterKey
|
key := i.iterKey
|
||||||
switch key.Kind() {
|
switch key.Kind() {
|
||||||
case InternalKeyKindDelete, InternalKeyKindMerge, InternalKeyKindSetWithDelete:
|
case InternalKeyKindDelete, InternalKeyKindMerge, InternalKeyKindSetWithDelete, InternalKeyKindDeleteSized:
|
||||||
// We've hit a Delete, Merge or SetWithDelete, transform the
|
// We've hit a Delete, DeleteSized, Merge, SetWithDelete, transform
|
||||||
// SingleDelete into a full Delete.
|
// the SingleDelete into a full Delete.
|
||||||
i.key.SetKind(InternalKeyKindDelete)
|
i.key.SetKind(InternalKeyKindDelete)
|
||||||
i.skip = true
|
i.skip = true
|
||||||
return true
|
return true
|
||||||
@@ -825,6 +859,110 @@ func (i *compactionIter) singleDeleteNext() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteSizedNext processes a DELSIZED point tombstone. Unlike ordinary DELs,
|
||||||
|
// these tombstones carry a value that's a varint indicating the size of the
|
||||||
|
// entry (len(key)+len(value)) that the tombstone is expected to delete.
|
||||||
|
//
|
||||||
|
// When a deleteSizedNext is encountered, we skip ahead to see which keys, if
|
||||||
|
// any, are elided as a result of the tombstone.
|
||||||
|
func (i *compactionIter) deleteSizedNext() (*base.InternalKey, []byte) {
|
||||||
|
i.saveKey()
|
||||||
|
i.valid = true
|
||||||
|
i.skip = true
|
||||||
|
|
||||||
|
// The DELSIZED tombstone may have no value at all. This happens when the
|
||||||
|
// tombstone has already deleted the key that the user originally predicted.
|
||||||
|
// In this case, we still peek forward in case there's another DELSIZED key
|
||||||
|
// with a lower sequence number, in which case we'll adopt its value.
|
||||||
|
if len(i.iterValue) == 0 {
|
||||||
|
i.value = i.valueBuf[:0]
|
||||||
|
} else {
|
||||||
|
i.valueBuf = append(i.valueBuf[:0], i.iterValue...)
|
||||||
|
i.value = i.valueBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all the keys within this stripe that are skippable,
|
||||||
|
// totalling the size of their keys and values.
|
||||||
|
i.pos = iterPosNext
|
||||||
|
var elidedSize uint64
|
||||||
|
for i.nextInStripe() == sameStripeSkippable {
|
||||||
|
elidedSize += uint64(len(i.iterKey.UserKey)) + uint64(len(i.iterValue))
|
||||||
|
|
||||||
|
if i.iterKey.Kind() == InternalKeyKindDeleteSized {
|
||||||
|
// We encountered a DELSIZED that's deleted by the original
|
||||||
|
// DELSIZED. These tombstones could've been intended to delete two
|
||||||
|
// distinct values, eg:
|
||||||
|
//
|
||||||
|
// a.DELSIZED.9 a.SET.7 a.DELSIZED.5 a.SET.4
|
||||||
|
//
|
||||||
|
// If a.DELSIZED.9 has already deleted a.SET.7, its size has already
|
||||||
|
// been zeroed out. In this case, we want to adopt the value of the
|
||||||
|
// DELSIZED with the lower sequence number, in case the a.SET.4 key
|
||||||
|
// has not yet been elided.
|
||||||
|
i.valueBuf = append(i.valueBuf[:0], i.iterValue...)
|
||||||
|
i.value = i.valueBuf
|
||||||
|
// Reset the elided total.
|
||||||
|
elidedSize = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reset skip if we landed outside the original stripe. Otherwise, we landed
|
||||||
|
// in the same stripe on a non-skippable key. In that case we should preserve
|
||||||
|
// `i.skip == true` such that later keys in the stripe will continue to be
|
||||||
|
// skipped.
|
||||||
|
if i.iterStripeChange == newStripeNewKey || i.iterStripeChange == newStripeSameKey {
|
||||||
|
i.skip = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if elidedSize == 0 {
|
||||||
|
// If we didn't find any keys that we could elide due to this tombstone
|
||||||
|
// (or at least none since the last DELSIZED encountered), we can return
|
||||||
|
// the key and our existing saved value verbatim.
|
||||||
|
return &i.key, i.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some key(s) were elided as a result of this tombstone. Decode the
|
||||||
|
// tombstone's value to see if it matches the amount of data actually
|
||||||
|
// elided.
|
||||||
|
var v uint64
|
||||||
|
if len(i.value) > 0 {
|
||||||
|
var n int
|
||||||
|
v, n = binary.Uvarint(i.value)
|
||||||
|
if n != len(i.value) {
|
||||||
|
i.err = base.CorruptionErrorf("DELSIZED holds invalid value: %x", errors.Safe(i.value))
|
||||||
|
i.valid = false
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user's prediction of the value size was correct, then from now on
|
||||||
|
// it's predicted to delete nothing. Return an empty value.
|
||||||
|
//
|
||||||
|
// NB: It's important that we still always return the tombstone, because
|
||||||
|
// DELSIZED's values are a best effort optimization, not a correctness
|
||||||
|
// mechanic. There is no guarantee that this key was only set once before
|
||||||
|
// the DELSIZED was written.
|
||||||
|
|
||||||
|
// If the tombstone carried a larger size than what was actually elided,
|
||||||
|
// it's unclear what to do. The user-provided size was wrong, so it's
|
||||||
|
// unlikely to be accurate or meaningful. We could:
|
||||||
|
//
|
||||||
|
// 1. return the DELSIZED with the original user-provided size unmodified
|
||||||
|
// 2. return the DELZIZED with a zeroed size to reflect that a key was
|
||||||
|
// elided, even if it wasn't the anticipated size.
|
||||||
|
// 3. subtract the elided size from the estimate and re-encode.
|
||||||
|
// 4. convert the DELSIZED into a value-less DEL, so that ordinary DEL
|
||||||
|
// heuristics apply.
|
||||||
|
//
|
||||||
|
// We opt for (4) under the rationale that we can't rely on the
|
||||||
|
// user-provided size for accuracy, so ordinary DEL heuristics are safer.
|
||||||
|
i.value = i.valueBuf[:0]
|
||||||
|
if elidedSize != v {
|
||||||
|
i.stats.countMissizedDels++
|
||||||
|
i.key.SetKind(InternalKeyKindDelete)
|
||||||
|
}
|
||||||
|
return &i.key, i.value
|
||||||
|
}
|
||||||
|
|
||||||
func (i *compactionIter) saveKey() {
|
func (i *compactionIter) saveKey() {
|
||||||
i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...)
|
i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...)
|
||||||
i.key.UserKey = i.keyBuf
|
i.key.UserKey = i.keyBuf
|
||||||
|
|||||||
84
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
84
vendor/github.com/cockroachdb/pebble/compaction_picker.go
generated
vendored
@@ -674,15 +674,16 @@ type candidateLevelInfo struct {
|
|||||||
file manifest.LevelFile
|
file manifest.LevelFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fileCompensation(f *fileMetadata) uint64 {
|
||||||
|
return uint64(f.Stats.PointDeletionsBytesEstimate) + f.Stats.RangeDeletionsBytesEstimate
|
||||||
|
}
|
||||||
|
|
||||||
// compensatedSize returns f's file size, inflated according to compaction
|
// compensatedSize returns f's file size, inflated according to compaction
|
||||||
// priorities.
|
// priorities.
|
||||||
func compensatedSize(f *fileMetadata, pointTombstoneWeight float64) uint64 {
|
func compensatedSize(f *fileMetadata) uint64 {
|
||||||
sz := f.Size
|
// Add in the estimate of disk space that may be reclaimed by compacting the
|
||||||
// Add in the estimate of disk space that may be reclaimed by compacting
|
// file's tombstones.
|
||||||
// the file's tombstones.
|
return f.Size + fileCompensation(f)
|
||||||
sz += uint64(float64(f.Stats.PointDeletionsBytesEstimate) * pointTombstoneWeight)
|
|
||||||
sz += f.Stats.RangeDeletionsBytesEstimate
|
|
||||||
return sz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compensatedSizeAnnotator implements manifest.Annotator, annotating B-Tree
|
// compensatedSizeAnnotator implements manifest.Annotator, annotating B-Tree
|
||||||
@@ -691,7 +692,6 @@ func compensatedSize(f *fileMetadata, pointTombstoneWeight float64) uint64 {
|
|||||||
// asynchronously, so its values are marked as cacheable only if a file's
|
// asynchronously, so its values are marked as cacheable only if a file's
|
||||||
// stats have been loaded.
|
// stats have been loaded.
|
||||||
type compensatedSizeAnnotator struct {
|
type compensatedSizeAnnotator struct {
|
||||||
pointTombstoneWeight float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ manifest.Annotator = compensatedSizeAnnotator{}
|
var _ manifest.Annotator = compensatedSizeAnnotator{}
|
||||||
@@ -709,7 +709,7 @@ func (a compensatedSizeAnnotator) Accumulate(
|
|||||||
f *fileMetadata, dst interface{},
|
f *fileMetadata, dst interface{},
|
||||||
) (v interface{}, cacheOK bool) {
|
) (v interface{}, cacheOK bool) {
|
||||||
vptr := dst.(*uint64)
|
vptr := dst.(*uint64)
|
||||||
*vptr = *vptr + compensatedSize(f, a.pointTombstoneWeight)
|
*vptr = *vptr + compensatedSize(f)
|
||||||
return vptr, f.StatsValid()
|
return vptr, f.StatsValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,10 +724,10 @@ func (a compensatedSizeAnnotator) Merge(src interface{}, dst interface{}) interf
|
|||||||
// iterator. Note that this function is linear in the files available to the
|
// iterator. Note that this function is linear in the files available to the
|
||||||
// iterator. Use the compensatedSizeAnnotator if querying the total
|
// iterator. Use the compensatedSizeAnnotator if querying the total
|
||||||
// compensated size of a level.
|
// compensated size of a level.
|
||||||
func totalCompensatedSize(iter manifest.LevelIterator, pointTombstoneWeight float64) uint64 {
|
func totalCompensatedSize(iter manifest.LevelIterator) uint64 {
|
||||||
var sz uint64
|
var sz uint64
|
||||||
for f := iter.First(); f != nil; f = iter.Next() {
|
for f := iter.First(); f != nil; f = iter.Next() {
|
||||||
sz += compensatedSize(f, pointTombstoneWeight)
|
sz += compensatedSize(f)
|
||||||
}
|
}
|
||||||
return sz
|
return sz
|
||||||
}
|
}
|
||||||
@@ -925,9 +925,7 @@ func (p *compactionPickerByScore) initLevelMaxBytes(inProgressCompactions []comp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateSizeAdjust(
|
func calculateSizeAdjust(inProgressCompactions []compactionInfo) [numLevels]int64 {
|
||||||
inProgressCompactions []compactionInfo, pointTombstoneWeight float64,
|
|
||||||
) [numLevels]int64 {
|
|
||||||
// Compute a size adjustment for each level based on the in-progress
|
// Compute a size adjustment for each level based on the in-progress
|
||||||
// compactions. We subtract the compensated size of start level inputs.
|
// compactions. We subtract the compensated size of start level inputs.
|
||||||
// Since compensated file sizes may be compensated because they reclaim
|
// Since compensated file sizes may be compensated because they reclaim
|
||||||
@@ -937,10 +935,16 @@ func calculateSizeAdjust(
|
|||||||
var sizeAdjust [numLevels]int64
|
var sizeAdjust [numLevels]int64
|
||||||
for i := range inProgressCompactions {
|
for i := range inProgressCompactions {
|
||||||
c := &inProgressCompactions[i]
|
c := &inProgressCompactions[i]
|
||||||
|
// If this compaction's version edit has already been applied, there's
|
||||||
|
// no need to adjust: The LSM we'll examine will already reflect the
|
||||||
|
// new LSM state.
|
||||||
|
if c.versionEditApplied {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for _, input := range c.inputs {
|
for _, input := range c.inputs {
|
||||||
real := int64(input.files.SizeSum())
|
real := int64(input.files.SizeSum())
|
||||||
compensated := int64(totalCompensatedSize(input.files.Iter(), pointTombstoneWeight))
|
compensated := int64(totalCompensatedSize(input.files.Iter()))
|
||||||
|
|
||||||
if input.level != c.outputLevel {
|
if input.level != c.outputLevel {
|
||||||
sizeAdjust[input.level] -= compensated
|
sizeAdjust[input.level] -= compensated
|
||||||
@@ -967,10 +971,7 @@ func (p *compactionPickerByScore) calculateScores(
|
|||||||
}
|
}
|
||||||
scores[0] = p.calculateL0Score(inProgressCompactions)
|
scores[0] = p.calculateL0Score(inProgressCompactions)
|
||||||
|
|
||||||
sizeAdjust := calculateSizeAdjust(
|
sizeAdjust := calculateSizeAdjust(inProgressCompactions)
|
||||||
inProgressCompactions,
|
|
||||||
p.opts.Experimental.PointTombstoneWeight,
|
|
||||||
)
|
|
||||||
for level := 1; level < numLevels; level++ {
|
for level := 1; level < numLevels; level++ {
|
||||||
levelSize := int64(levelCompensatedSize(p.vers.Levels[level])) + sizeAdjust[level]
|
levelSize := int64(levelCompensatedSize(p.vers.Levels[level])) + sizeAdjust[level]
|
||||||
scores[level].score = float64(levelSize) / float64(p.levelMaxBytes[level])
|
scores[level].score = float64(levelSize) / float64(p.levelMaxBytes[level])
|
||||||
@@ -1088,14 +1089,20 @@ func (p *compactionPickerByScore) pickFile(
|
|||||||
|
|
||||||
for f := startIter.First(); f != nil; f = startIter.Next() {
|
for f := startIter.First(); f != nil; f = startIter.Next() {
|
||||||
var overlappingBytes uint64
|
var overlappingBytes uint64
|
||||||
|
compacting := f.IsCompacting()
|
||||||
|
if compacting {
|
||||||
|
// Move on if this file is already being compacted. We'll likely
|
||||||
|
// still need to move past the overlapping output files regardless,
|
||||||
|
// but in cases where all start-level files are compacting we won't.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Trim any output-level files smaller than f.
|
// Trim any output-level files smaller than f.
|
||||||
for outputFile != nil && base.InternalCompare(cmp, outputFile.Largest, f.Smallest) < 0 {
|
for outputFile != nil && sstableKeyCompare(cmp, outputFile.Largest, f.Smallest) < 0 {
|
||||||
outputFile = outputIter.Next()
|
outputFile = outputIter.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
compacting := f.IsCompacting()
|
for outputFile != nil && sstableKeyCompare(cmp, outputFile.Smallest, f.Largest) <= 0 && !compacting {
|
||||||
for outputFile != nil && base.InternalCompare(cmp, outputFile.Smallest, f.Largest) < 0 {
|
|
||||||
overlappingBytes += outputFile.Size
|
overlappingBytes += outputFile.Size
|
||||||
compacting = compacting || outputFile.IsCompacting()
|
compacting = compacting || outputFile.IsCompacting()
|
||||||
|
|
||||||
@@ -1114,7 +1121,32 @@ func (p *compactionPickerByScore) pickFile(
|
|||||||
// If the file in the next level extends beyond f's largest key,
|
// If the file in the next level extends beyond f's largest key,
|
||||||
// break out and don't advance outputIter because f's successor
|
// break out and don't advance outputIter because f's successor
|
||||||
// might also overlap.
|
// might also overlap.
|
||||||
if base.InternalCompare(cmp, outputFile.Largest, f.Largest) > 0 {
|
//
|
||||||
|
// Note, we stop as soon as we encounter an output-level file with a
|
||||||
|
// largest key beyond the input-level file's largest bound. We
|
||||||
|
// perform a simple user key comparison here using sstableKeyCompare
|
||||||
|
// which handles the potential for exclusive largest key bounds.
|
||||||
|
// There's some subtlety when the bounds are equal (eg, equal and
|
||||||
|
// inclusive, or equal and exclusive). Current Pebble doesn't split
|
||||||
|
// user keys across sstables within a level (and in format versions
|
||||||
|
// FormatSplitUserKeysMarkedCompacted and later we guarantee no
|
||||||
|
// split user keys exist within the entire LSM). In that case, we're
|
||||||
|
// assured that neither the input level nor the output level's next
|
||||||
|
// file shares the same user key, so compaction expansion will not
|
||||||
|
// include them in any compaction compacting `f`.
|
||||||
|
//
|
||||||
|
// NB: If we /did/ allow split user keys, or we're running on an
|
||||||
|
// old database with an earlier format major version where there are
|
||||||
|
// existing split user keys, this logic would be incorrect. Consider
|
||||||
|
// L1: [a#120,a#100] [a#80,a#60]
|
||||||
|
// L2: [a#55,a#45] [a#35,a#25] [a#15,a#5]
|
||||||
|
// While considering the first file in L1, [a#120,a#100], we'd skip
|
||||||
|
// past all of the files in L2. When considering the second file in
|
||||||
|
// L1, we'd improperly conclude that the second file overlaps
|
||||||
|
// nothing in the second level and is cheap to compact, when in
|
||||||
|
// reality we'd need to expand the compaction to include all 5
|
||||||
|
// files.
|
||||||
|
if sstableKeyCompare(cmp, outputFile.Largest, f.Largest) > 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
outputFile = outputIter.Next()
|
outputFile = outputIter.Next()
|
||||||
@@ -1127,9 +1159,9 @@ func (p *compactionPickerByScore) pickFile(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
compSz := compensatedSize(f, p.opts.Experimental.PointTombstoneWeight)
|
compSz := compensatedSize(f)
|
||||||
scaledRatio := overlappingBytes * 1024 / compSz
|
scaledRatio := overlappingBytes * 1024 / compSz
|
||||||
if scaledRatio < smallestRatio && !f.IsCompacting() {
|
if scaledRatio < smallestRatio {
|
||||||
smallestRatio = scaledRatio
|
smallestRatio = scaledRatio
|
||||||
file = startIter.Take()
|
file = startIter.Take()
|
||||||
}
|
}
|
||||||
@@ -1191,7 +1223,7 @@ func (p *compactionPickerByScore) pickAuto(env compactionEnv) (pc *pickedCompact
|
|||||||
fmt.Fprintf(&buf, " %sL%d: %5.1f %5.1f %8s %8s",
|
fmt.Fprintf(&buf, " %sL%d: %5.1f %5.1f %8s %8s",
|
||||||
marker, info.level, info.score, info.origScore,
|
marker, info.level, info.score, info.origScore,
|
||||||
humanize.Int64(int64(totalCompensatedSize(
|
humanize.Int64(int64(totalCompensatedSize(
|
||||||
p.vers.Levels[info.level].Iter(), p.opts.Experimental.PointTombstoneWeight,
|
p.vers.Levels[info.level].Iter(),
|
||||||
))),
|
))),
|
||||||
humanize.Int64(p.levelMaxBytes[info.level]),
|
humanize.Int64(p.levelMaxBytes[info.level]),
|
||||||
)
|
)
|
||||||
|
|||||||
84
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
84
vendor/github.com/cockroachdb/pebble/db.go
generated
vendored
@@ -96,6 +96,22 @@ type Writer interface {
|
|||||||
// It is safe to modify the contents of the arguments after Delete returns.
|
// It is safe to modify the contents of the arguments after Delete returns.
|
||||||
Delete(key []byte, o *WriteOptions) error
|
Delete(key []byte, o *WriteOptions) error
|
||||||
|
|
||||||
|
// DeleteSized behaves identically to Delete, but takes an additional
|
||||||
|
// argument indicating the size of the value being deleted. DeleteSized
|
||||||
|
// should be preferred when the caller has the expectation that there exists
|
||||||
|
// a single internal KV pair for the key (eg, the key has not been
|
||||||
|
// overwritten recently), and the caller knows the size of its value.
|
||||||
|
//
|
||||||
|
// DeleteSized will record the value size within the tombstone and use it to
|
||||||
|
// inform compaction-picking heuristics which strive to reduce space
|
||||||
|
// amplification in the LSM. This "calling your shot" mechanic allows the
|
||||||
|
// storage engine to more accurately estimate and reduce space
|
||||||
|
// amplification.
|
||||||
|
//
|
||||||
|
// It is safe to modify the contents of the arguments after DeleteSized
|
||||||
|
// returns.
|
||||||
|
DeleteSized(key []byte, valueSize uint32, _ *WriteOptions) error
|
||||||
|
|
||||||
// SingleDelete is similar to Delete in that it deletes the value for the given key. Like Delete,
|
// SingleDelete is similar to Delete in that it deletes the value for the given key. Like Delete,
|
||||||
// it is a blind operation that will succeed even if the given key does not exist.
|
// it is a blind operation that will succeed even if the given key does not exist.
|
||||||
//
|
//
|
||||||
@@ -620,6 +636,30 @@ func (d *DB) Delete(key []byte, opts *WriteOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteSized behaves identically to Delete, but takes an additional
|
||||||
|
// argument indicating the size of the value being deleted. DeleteSized
|
||||||
|
// should be preferred when the caller has the expectation that there exists
|
||||||
|
// a single internal KV pair for the key (eg, the key has not been
|
||||||
|
// overwritten recently), and the caller knows the size of its value.
|
||||||
|
//
|
||||||
|
// DeleteSized will record the value size within the tombstone and use it to
|
||||||
|
// inform compaction-picking heuristics which strive to reduce space
|
||||||
|
// amplification in the LSM. This "calling your shot" mechanic allows the
|
||||||
|
// storage engine to more accurately estimate and reduce space amplification.
|
||||||
|
//
|
||||||
|
// It is safe to modify the contents of the arguments after DeleteSized
|
||||||
|
// returns.
|
||||||
|
func (d *DB) DeleteSized(key []byte, valueSize uint32, opts *WriteOptions) error {
|
||||||
|
b := newBatch(d)
|
||||||
|
_ = b.DeleteSized(key, valueSize, opts)
|
||||||
|
if err := d.Apply(b, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Only release the batch on success.
|
||||||
|
b.release()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SingleDelete adds an action to the batch that single deletes the entry for key.
|
// SingleDelete adds an action to the batch that single deletes the entry for key.
|
||||||
// See Writer.SingleDelete for more details on the semantics of SingleDelete.
|
// See Writer.SingleDelete for more details on the semantics of SingleDelete.
|
||||||
//
|
//
|
||||||
@@ -786,17 +826,19 @@ func (d *DB) applyInternal(batch *Batch, opts *WriteOptions, noSyncWait bool) er
|
|||||||
return errors.New("pebble: WAL disabled")
|
return errors.New("pebble: WAL disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if batch.minimumFormatMajorVersion != FormatMostCompatible {
|
||||||
|
if fmv := d.FormatMajorVersion(); fmv < batch.minimumFormatMajorVersion {
|
||||||
|
panic(fmt.Sprintf(
|
||||||
|
"pebble: batch requires at least format major version %d (current: %d)",
|
||||||
|
batch.minimumFormatMajorVersion, fmv,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if batch.countRangeKeys > 0 {
|
if batch.countRangeKeys > 0 {
|
||||||
if d.split == nil {
|
if d.split == nil {
|
||||||
return errNoSplit
|
return errNoSplit
|
||||||
}
|
}
|
||||||
if d.FormatMajorVersion() < FormatRangeKeys {
|
|
||||||
panic(fmt.Sprintf(
|
|
||||||
"pebble: range keys require at least format major version %d (current: %d)",
|
|
||||||
FormatRangeKeys, d.FormatMajorVersion(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(jackson): Assert that all range key operands are suffixless.
|
// TODO(jackson): Assert that all range key operands are suffixless.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1320,6 +1362,7 @@ func (i *Iterator) constructPointIter(
|
|||||||
levelsIndex := len(levels)
|
levelsIndex := len(levels)
|
||||||
mlevels = mlevels[:numMergingLevels]
|
mlevels = mlevels[:numMergingLevels]
|
||||||
levels = levels[:numLevelIters]
|
levels = levels[:numLevelIters]
|
||||||
|
i.opts.snapshotForHideObsoletePoints = buf.dbi.seqNum
|
||||||
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
||||||
li := &levels[levelsIndex]
|
li := &levels[levelsIndex]
|
||||||
|
|
||||||
@@ -1523,6 +1566,16 @@ func (d *DB) Close() error {
|
|||||||
d.mu.Unlock()
|
d.mu.Unlock()
|
||||||
d.deleters.Wait()
|
d.deleters.Wait()
|
||||||
d.compactionSchedulers.Wait()
|
d.compactionSchedulers.Wait()
|
||||||
|
|
||||||
|
// Sanity check metrics.
|
||||||
|
if invariants.Enabled {
|
||||||
|
m := d.Metrics()
|
||||||
|
if m.Compact.NumInProgress > 0 || m.Compact.InProgressBytes > 0 {
|
||||||
|
d.mu.Lock()
|
||||||
|
panic(fmt.Sprintf("invalid metrics on close:\n%s", m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
|
|
||||||
// As a sanity check, ensure that there are no zombie tables. A non-zero count
|
// As a sanity check, ensure that there are no zombie tables. A non-zero count
|
||||||
@@ -1823,6 +1876,10 @@ func (d *DB) Metrics() *Metrics {
|
|||||||
type sstablesOptions struct {
|
type sstablesOptions struct {
|
||||||
// set to true will return the sstable properties in TableInfo
|
// set to true will return the sstable properties in TableInfo
|
||||||
withProperties bool
|
withProperties bool
|
||||||
|
|
||||||
|
// if set, return sstables that overlap the key range (end-exclusive)
|
||||||
|
start []byte
|
||||||
|
end []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSTablesOption set optional parameter used by `DB.SSTables`.
|
// SSTablesOption set optional parameter used by `DB.SSTables`.
|
||||||
@@ -1838,6 +1895,15 @@ func WithProperties() SSTablesOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithKeyRangeFilter ensures returned sstables overlap start and end (end-exclusive)
|
||||||
|
// if start and end are both nil these properties have no effect
|
||||||
|
func WithKeyRangeFilter(start, end []byte) SSTablesOption {
|
||||||
|
return func(opt *sstablesOptions) {
|
||||||
|
opt.end = end
|
||||||
|
opt.start = start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SSTableInfo export manifest.TableInfo with sstable.Properties
|
// SSTableInfo export manifest.TableInfo with sstable.Properties
|
||||||
type SSTableInfo struct {
|
type SSTableInfo struct {
|
||||||
manifest.TableInfo
|
manifest.TableInfo
|
||||||
@@ -1883,6 +1949,9 @@ func (d *DB) SSTables(opts ...SSTablesOption) ([][]SSTableInfo, error) {
|
|||||||
iter := srcLevels[i].Iter()
|
iter := srcLevels[i].Iter()
|
||||||
j := 0
|
j := 0
|
||||||
for m := iter.First(); m != nil; m = iter.Next() {
|
for m := iter.First(); m != nil; m = iter.Next() {
|
||||||
|
if opt.start != nil && opt.end != nil && !m.Overlaps(d.opts.Comparer.Compare, opt.start, opt.end, true /* exclusive end */) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
destTables[j] = SSTableInfo{TableInfo: m.TableInfo()}
|
destTables[j] = SSTableInfo{TableInfo: m.TableInfo()}
|
||||||
if opt.withProperties {
|
if opt.withProperties {
|
||||||
p, err := d.tableCache.getTableProperties(
|
p, err := d.tableCache.getTableProperties(
|
||||||
@@ -1900,6 +1969,7 @@ func (d *DB) SSTables(opts ...SSTablesOption) ([][]SSTableInfo, error) {
|
|||||||
destLevels[i] = destTables[:j]
|
destLevels[i] = destTables[:j]
|
||||||
destTables = destTables[j:]
|
destTables = destTables[j:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return destLevels, nil
|
return destLevels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/external_iterator.go
generated
vendored
@@ -209,15 +209,15 @@ func createExternalPointIter(ctx context.Context, it *Iterator) (internalIterato
|
|||||||
pointIter internalIterator
|
pointIter internalIterator
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
pointIter, err = r.NewIterWithBlockPropertyFiltersAndContext(
|
// We could set hideObsoletePoints=true, since we are reading at
|
||||||
ctx,
|
// InternalKeySeqNumMax, but we don't bother since these sstables should
|
||||||
it.opts.LowerBound,
|
// not have obsolete points (so the performance optimization is
|
||||||
it.opts.UpperBound,
|
// unnecessary), and we don't want to bother constructing a
|
||||||
nil, /* BlockPropertiesFilterer */
|
// BlockPropertiesFilterer that includes obsoleteKeyBlockPropertyFilter.
|
||||||
false, /* useFilterBlock */
|
pointIter, err = r.NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||||
&it.stats.InternalStats,
|
ctx, it.opts.LowerBound, it.opts.UpperBound, nil, /* BlockPropertiesFilterer */
|
||||||
sstable.TrivialReaderProvider{Reader: r},
|
false /* hideObsoletePoints */, false, /* useFilterBlock */
|
||||||
)
|
&it.stats.InternalStats, sstable.TrivialReaderProvider{Reader: r})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/pebble/flushable.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/flushable.go
generated
vendored
@@ -184,7 +184,7 @@ func (s *ingestedFlushable) newFlushIter(o *IterOptions, bytesFlushed *uint64) i
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ingestedFlushable) constructRangeDelIter(
|
func (s *ingestedFlushable) constructRangeDelIter(
|
||||||
file *manifest.FileMetadata, _ *keyspan.SpanIterOptions,
|
file *manifest.FileMetadata, _ keyspan.SpanIterOptions,
|
||||||
) (keyspan.FragmentIterator, error) {
|
) (keyspan.FragmentIterator, error) {
|
||||||
// Note that the keyspan level iter expects a non-nil iterator to be
|
// Note that the keyspan level iter expects a non-nil iterator to be
|
||||||
// returned even if there is an error. So, we return the emptyKeyspanIter.
|
// returned even if there is an error. So, we return the emptyKeyspanIter.
|
||||||
|
|||||||
40
vendor/github.com/cockroachdb/pebble/format_major_version.go
generated
vendored
40
vendor/github.com/cockroachdb/pebble/format_major_version.go
generated
vendored
@@ -151,8 +151,29 @@ const (
|
|||||||
// compactions for files marked for compaction are complete.
|
// compactions for files marked for compaction are complete.
|
||||||
FormatPrePebblev1MarkedCompacted
|
FormatPrePebblev1MarkedCompacted
|
||||||
|
|
||||||
|
// ExperimentalFormatDeleteSizedAndObsolete is a format major version that adds support
|
||||||
|
// for deletion tombstones that encode the size of the value they're
|
||||||
|
// expected to delete. This format major version is required before the
|
||||||
|
// associated key kind may be committed through batch applications or
|
||||||
|
// ingests. It also adds support for keys that are marked obsolete (see
|
||||||
|
// sstable/format.go for details).
|
||||||
|
ExperimentalFormatDeleteSizedAndObsolete
|
||||||
|
|
||||||
|
// ExperimentalFormatVirtualSSTables is a format major version that adds support for
|
||||||
|
// virtual sstables that can reference a sub-range of keys in an underlying
|
||||||
|
// physical sstable. This information is persisted through new,
|
||||||
|
// backward-incompatible fields in the Manifest, and therefore requires
|
||||||
|
// a format major version.
|
||||||
|
ExperimentalFormatVirtualSSTables
|
||||||
|
|
||||||
|
// internalFormatNewest holds the newest format major version, including
|
||||||
|
// experimental ones excluded from the exported FormatNewest constant until
|
||||||
|
// they've stabilized. Used in tests.
|
||||||
|
internalFormatNewest FormatMajorVersion = iota - 1
|
||||||
|
|
||||||
// FormatNewest always contains the most recent format major version.
|
// FormatNewest always contains the most recent format major version.
|
||||||
FormatNewest FormatMajorVersion = iota - 1
|
// TODO(jackson): restore FormatNewest to internalFormatNewest.
|
||||||
|
FormatNewest FormatMajorVersion = FormatPrePebblev1MarkedCompacted
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxTableFormat returns the maximum sstable.TableFormat that can be used at
|
// MaxTableFormat returns the maximum sstable.TableFormat that can be used at
|
||||||
@@ -170,6 +191,8 @@ func (v FormatMajorVersion) MaxTableFormat() sstable.TableFormat {
|
|||||||
return sstable.TableFormatPebblev2
|
return sstable.TableFormatPebblev2
|
||||||
case FormatSSTableValueBlocks, FormatFlushableIngest, FormatPrePebblev1MarkedCompacted:
|
case FormatSSTableValueBlocks, FormatFlushableIngest, FormatPrePebblev1MarkedCompacted:
|
||||||
return sstable.TableFormatPebblev3
|
return sstable.TableFormatPebblev3
|
||||||
|
case ExperimentalFormatDeleteSizedAndObsolete, ExperimentalFormatVirtualSSTables:
|
||||||
|
return sstable.TableFormatPebblev4
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||||
}
|
}
|
||||||
@@ -186,7 +209,8 @@ func (v FormatMajorVersion) MinTableFormat() sstable.TableFormat {
|
|||||||
return sstable.TableFormatLevelDB
|
return sstable.TableFormatLevelDB
|
||||||
case FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
case FormatMinTableFormatPebblev1, FormatPrePebblev1Marked,
|
||||||
FormatUnusedPrePebblev1MarkedCompacted, FormatSSTableValueBlocks,
|
FormatUnusedPrePebblev1MarkedCompacted, FormatSSTableValueBlocks,
|
||||||
FormatFlushableIngest, FormatPrePebblev1MarkedCompacted:
|
FormatFlushableIngest, FormatPrePebblev1MarkedCompacted,
|
||||||
|
ExperimentalFormatDeleteSizedAndObsolete, ExperimentalFormatVirtualSSTables:
|
||||||
return sstable.TableFormatPebblev1
|
return sstable.TableFormatPebblev1
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
panic(fmt.Sprintf("pebble: unsupported format major version: %s", v))
|
||||||
@@ -319,6 +343,12 @@ var formatMajorVersionMigrations = map[FormatMajorVersion]func(*DB) error{
|
|||||||
}
|
}
|
||||||
return d.finalizeFormatVersUpgrade(FormatPrePebblev1MarkedCompacted)
|
return d.finalizeFormatVersUpgrade(FormatPrePebblev1MarkedCompacted)
|
||||||
},
|
},
|
||||||
|
ExperimentalFormatDeleteSizedAndObsolete: func(d *DB) error {
|
||||||
|
return d.finalizeFormatVersUpgrade(ExperimentalFormatDeleteSizedAndObsolete)
|
||||||
|
},
|
||||||
|
ExperimentalFormatVirtualSSTables: func(d *DB) error {
|
||||||
|
return d.finalizeFormatVersUpgrade(ExperimentalFormatVirtualSSTables)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatVersionMarkerName = `format-version`
|
const formatVersionMarkerName = `format-version`
|
||||||
@@ -341,7 +371,7 @@ func lookupFormatMajorVersion(
|
|||||||
if vers == FormatDefault {
|
if vers == FormatDefault {
|
||||||
return 0, nil, errors.Newf("pebble: default format major version should not persisted", vers)
|
return 0, nil, errors.Newf("pebble: default format major version should not persisted", vers)
|
||||||
}
|
}
|
||||||
if vers > FormatNewest {
|
if vers > internalFormatNewest {
|
||||||
return 0, nil, errors.Newf("pebble: database %q written in format major version %d", dirname, vers)
|
return 0, nil, errors.Newf("pebble: database %q written in format major version %d", dirname, vers)
|
||||||
}
|
}
|
||||||
return vers, m, nil
|
return vers, m, nil
|
||||||
@@ -377,8 +407,8 @@ func (d *DB) ratchetFormatMajorVersionLocked(formatVers FormatMajorVersion) erro
|
|||||||
if d.opts.ReadOnly {
|
if d.opts.ReadOnly {
|
||||||
return ErrReadOnly
|
return ErrReadOnly
|
||||||
}
|
}
|
||||||
if formatVers > FormatNewest {
|
if formatVers > internalFormatNewest {
|
||||||
// Guard against accidentally forgetting to update FormatNewest.
|
// Guard against accidentally forgetting to update internalFormatNewest.
|
||||||
return errors.Errorf("pebble: unknown format version %d", formatVers)
|
return errors.Errorf("pebble: unknown format version %d", formatVers)
|
||||||
}
|
}
|
||||||
if d.mu.formatVers.vers > formatVers {
|
if d.mu.formatVers.vers > formatVers {
|
||||||
|
|||||||
4
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/get_iter.go
generated
vendored
@@ -158,7 +158,7 @@ func (g *getIter) Next() (*InternalKey, base.LazyValue) {
|
|||||||
if n := len(g.l0); n > 0 {
|
if n := len(g.l0); n > 0 {
|
||||||
files := g.l0[n-1].Iter()
|
files := g.l0[n-1].Iter()
|
||||||
g.l0 = g.l0[:n-1]
|
g.l0 = g.l0[:n-1]
|
||||||
iterOpts := IterOptions{logger: g.logger}
|
iterOpts := IterOptions{logger: g.logger, snapshotForHideObsoletePoints: g.snapshot}
|
||||||
g.levelIter.init(context.Background(), iterOpts, g.cmp, nil /* split */, g.newIters,
|
g.levelIter.init(context.Background(), iterOpts, g.cmp, nil /* split */, g.newIters,
|
||||||
files, manifest.L0Sublevel(n), internalIterOpts{})
|
files, manifest.L0Sublevel(n), internalIterOpts{})
|
||||||
g.levelIter.initRangeDel(&g.rangeDelIter)
|
g.levelIter.initRangeDel(&g.rangeDelIter)
|
||||||
@@ -177,7 +177,7 @@ func (g *getIter) Next() (*InternalKey, base.LazyValue) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
iterOpts := IterOptions{logger: g.logger}
|
iterOpts := IterOptions{logger: g.logger, snapshotForHideObsoletePoints: g.snapshot}
|
||||||
g.levelIter.init(context.Background(), iterOpts, g.cmp, nil /* split */, g.newIters,
|
g.levelIter.init(context.Background(), iterOpts, g.cmp, nil /* split */, g.newIters,
|
||||||
g.version.Levels[g.level].Iter(), manifest.Level(g.level), internalIterOpts{})
|
g.version.Levels[g.level].Iter(), manifest.Level(g.level), internalIterOpts{})
|
||||||
g.levelIter.initRangeDel(&g.rangeDelIter)
|
g.levelIter.initRangeDel(&g.rangeDelIter)
|
||||||
|
|||||||
756
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
756
vendor/github.com/cockroachdb/pebble/ingest.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
1
vendor/github.com/cockroachdb/pebble/internal.go
generated
vendored
@@ -23,6 +23,7 @@ const (
|
|||||||
InternalKeyKindRangeKeyUnset = base.InternalKeyKindRangeKeyUnset
|
InternalKeyKindRangeKeyUnset = base.InternalKeyKindRangeKeyUnset
|
||||||
InternalKeyKindRangeKeyDelete = base.InternalKeyKindRangeKeyDelete
|
InternalKeyKindRangeKeyDelete = base.InternalKeyKindRangeKeyDelete
|
||||||
InternalKeyKindIngestSST = base.InternalKeyKindIngestSST
|
InternalKeyKindIngestSST = base.InternalKeyKindIngestSST
|
||||||
|
InternalKeyKindDeleteSized = base.InternalKeyKindDeleteSized
|
||||||
InternalKeyKindInvalid = base.InternalKeyKindInvalid
|
InternalKeyKindInvalid = base.InternalKeyKindInvalid
|
||||||
InternalKeySeqNumBatch = base.InternalKeySeqNumBatch
|
InternalKeySeqNumBatch = base.InternalKeySeqNumBatch
|
||||||
InternalKeySeqNumMax = base.InternalKeySeqNumMax
|
InternalKeySeqNumMax = base.InternalKeySeqNumMax
|
||||||
|
|||||||
35
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
35
vendor/github.com/cockroachdb/pebble/internal/base/internal.go
generated
vendored
@@ -24,6 +24,14 @@ const (
|
|||||||
//InternalKeyKindColumnFamilyDeletion InternalKeyKind = 4
|
//InternalKeyKindColumnFamilyDeletion InternalKeyKind = 4
|
||||||
//InternalKeyKindColumnFamilyValue InternalKeyKind = 5
|
//InternalKeyKindColumnFamilyValue InternalKeyKind = 5
|
||||||
//InternalKeyKindColumnFamilyMerge InternalKeyKind = 6
|
//InternalKeyKindColumnFamilyMerge InternalKeyKind = 6
|
||||||
|
|
||||||
|
// InternalKeyKindSingleDelete (SINGLEDEL) is a performance optimization
|
||||||
|
// solely for compactions (to reduce write amp and space amp). Readers other
|
||||||
|
// than compactions should treat SINGLEDEL as equivalent to a DEL.
|
||||||
|
// Historically, it was simpler for readers other than compactions to treat
|
||||||
|
// SINGLEDEL as equivalent to DEL, but as of the introduction of
|
||||||
|
// InternalKeyKindSSTableInternalObsoleteBit, this is also necessary for
|
||||||
|
// correctness.
|
||||||
InternalKeyKindSingleDelete InternalKeyKind = 7
|
InternalKeyKindSingleDelete InternalKeyKind = 7
|
||||||
//InternalKeyKindColumnFamilySingleDelete InternalKeyKind = 8
|
//InternalKeyKindColumnFamilySingleDelete InternalKeyKind = 8
|
||||||
//InternalKeyKindBeginPrepareXID InternalKeyKind = 9
|
//InternalKeyKindBeginPrepareXID InternalKeyKind = 9
|
||||||
@@ -66,8 +74,15 @@ const (
|
|||||||
// batch, or in an sstable.
|
// batch, or in an sstable.
|
||||||
InternalKeyKindIngestSST InternalKeyKind = 22
|
InternalKeyKindIngestSST InternalKeyKind = 22
|
||||||
|
|
||||||
// This maximum value isn't part of the file format. It's unlikely,
|
// InternalKeyKindDeleteSized keys behave identically to
|
||||||
// but future extensions may increase this value.
|
// InternalKeyKindDelete keys, except that they hold an associated uint64
|
||||||
|
// value indicating the (len(key)+len(value)) of the shadowed entry the
|
||||||
|
// tombstone is expected to delete. This value is used to inform compaction
|
||||||
|
// heuristics, but is not required to be accurate for correctness.
|
||||||
|
InternalKeyKindDeleteSized InternalKeyKind = 23
|
||||||
|
|
||||||
|
// This maximum value isn't part of the file format. Future extensions may
|
||||||
|
// increase this value.
|
||||||
//
|
//
|
||||||
// When constructing an internal key to pass to DB.Seek{GE,LE},
|
// When constructing an internal key to pass to DB.Seek{GE,LE},
|
||||||
// internalKeyComparer sorts decreasing by kind (after sorting increasing by
|
// internalKeyComparer sorts decreasing by kind (after sorting increasing by
|
||||||
@@ -75,14 +90,19 @@ const (
|
|||||||
// which sorts 'less than or equal to' any other valid internalKeyKind, when
|
// which sorts 'less than or equal to' any other valid internalKeyKind, when
|
||||||
// searching for any kind of internal key formed by a certain user key and
|
// searching for any kind of internal key formed by a certain user key and
|
||||||
// seqNum.
|
// seqNum.
|
||||||
InternalKeyKindMax InternalKeyKind = 22
|
InternalKeyKindMax InternalKeyKind = 23
|
||||||
|
|
||||||
|
// Internal to the sstable format. Not exposed by any sstable iterator.
|
||||||
|
// Declared here to prevent definition of valid key kinds that set this bit.
|
||||||
|
InternalKeyKindSSTableInternalObsoleteBit InternalKeyKind = 64
|
||||||
|
InternalKeyKindSSTableInternalObsoleteMask InternalKeyKind = 191
|
||||||
|
|
||||||
// InternalKeyZeroSeqnumMaxTrailer is the largest trailer with a
|
// InternalKeyZeroSeqnumMaxTrailer is the largest trailer with a
|
||||||
// zero sequence number.
|
// zero sequence number.
|
||||||
InternalKeyZeroSeqnumMaxTrailer = uint64(InternalKeyKindInvalid)
|
InternalKeyZeroSeqnumMaxTrailer = uint64(255)
|
||||||
|
|
||||||
// A marker for an invalid key.
|
// A marker for an invalid key.
|
||||||
InternalKeyKindInvalid InternalKeyKind = 255
|
InternalKeyKindInvalid InternalKeyKind = InternalKeyKindSSTableInternalObsoleteMask
|
||||||
|
|
||||||
// InternalKeySeqNumBatch is a bit that is set on batch sequence numbers
|
// InternalKeySeqNumBatch is a bit that is set on batch sequence numbers
|
||||||
// which prevents those entries from being excluded from iteration.
|
// which prevents those entries from being excluded from iteration.
|
||||||
@@ -105,6 +125,9 @@ const (
|
|||||||
InternalKeyBoundaryRangeKey = (InternalKeySeqNumMax << 8) | uint64(InternalKeyKindRangeKeySet)
|
InternalKeyBoundaryRangeKey = (InternalKeySeqNumMax << 8) | uint64(InternalKeyKindRangeKeySet)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Assert InternalKeyKindSSTableInternalObsoleteBit > InternalKeyKindMax
|
||||||
|
const _ = uint(InternalKeyKindSSTableInternalObsoleteBit - InternalKeyKindMax - 1)
|
||||||
|
|
||||||
var internalKeyKindNames = []string{
|
var internalKeyKindNames = []string{
|
||||||
InternalKeyKindDelete: "DEL",
|
InternalKeyKindDelete: "DEL",
|
||||||
InternalKeyKindSet: "SET",
|
InternalKeyKindSet: "SET",
|
||||||
@@ -118,6 +141,7 @@ var internalKeyKindNames = []string{
|
|||||||
InternalKeyKindRangeKeyUnset: "RANGEKEYUNSET",
|
InternalKeyKindRangeKeyUnset: "RANGEKEYUNSET",
|
||||||
InternalKeyKindRangeKeyDelete: "RANGEKEYDEL",
|
InternalKeyKindRangeKeyDelete: "RANGEKEYDEL",
|
||||||
InternalKeyKindIngestSST: "INGESTSST",
|
InternalKeyKindIngestSST: "INGESTSST",
|
||||||
|
InternalKeyKindDeleteSized: "DELSIZED",
|
||||||
InternalKeyKindInvalid: "INVALID",
|
InternalKeyKindInvalid: "INVALID",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +228,7 @@ var kindsMap = map[string]InternalKeyKind{
|
|||||||
"RANGEKEYUNSET": InternalKeyKindRangeKeyUnset,
|
"RANGEKEYUNSET": InternalKeyKindRangeKeyUnset,
|
||||||
"RANGEKEYDEL": InternalKeyKindRangeKeyDelete,
|
"RANGEKEYDEL": InternalKeyKindRangeKeyDelete,
|
||||||
"INGESTSST": InternalKeyKindIngestSST,
|
"INGESTSST": InternalKeyKindIngestSST,
|
||||||
|
"DELSIZED": InternalKeyKindDeleteSized,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseInternalKey parses the string representation of an internal key. The
|
// ParseInternalKey parses the string representation of an internal key. The
|
||||||
|
|||||||
9
vendor/github.com/cockroachdb/pebble/internal/base/metrics.go
generated
vendored
9
vendor/github.com/cockroachdb/pebble/internal/base/metrics.go
generated
vendored
@@ -53,6 +53,15 @@ func (tm *ThroughputMetric) Rate() int64 {
|
|||||||
float64(time.Second))
|
float64(time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utilization returns a percent [0, 1.0] indicating the percent of time
|
||||||
|
// work was performed.
|
||||||
|
func (tm *ThroughputMetric) Utilization() float64 {
|
||||||
|
if tm.WorkDuration == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return float64(tm.WorkDuration) / float64(tm.WorkDuration+tm.IdleDuration)
|
||||||
|
}
|
||||||
|
|
||||||
// GaugeSampleMetric is used to measure a gauge value (e.g. queue length) by
|
// GaugeSampleMetric is used to measure a gauge value (e.g. queue length) by
|
||||||
// accumulating samples of that gauge.
|
// accumulating samples of that gauge.
|
||||||
type GaugeSampleMetric struct {
|
type GaugeSampleMetric struct {
|
||||||
|
|||||||
64
vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go
generated
vendored
64
vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go
generated
vendored
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
package base
|
package base
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// This file defines sequence numbers that are reserved for foreign keys i.e.
|
// This file defines sequence numbers that are reserved for foreign keys i.e.
|
||||||
// internal keys coming from other Pebble instances and existing in shared
|
// internal keys coming from other Pebble instances and existing in shared
|
||||||
// storage, as those will "slot below" any internal keys added by our own Pebble
|
// storage, as those will "slot below" any internal keys added by our own Pebble
|
||||||
@@ -14,41 +16,41 @@ const (
|
|||||||
// guarantee there are no keys underneath an internal key.
|
// guarantee there are no keys underneath an internal key.
|
||||||
SeqNumZero = uint64(0)
|
SeqNumZero = uint64(0)
|
||||||
|
|
||||||
// SeqNumL6Point is the sequence number reserved for foreign point keys in L6.
|
// SeqNumL6 is the sequence number reserved for foreign keys in L6. This seqnum
|
||||||
// This sequence number must be lower than SeqNumL6RangeKey for range key
|
// will be used to expose any range key sets as well as point keys in L6. Range
|
||||||
// masking to work correctly.
|
// deletes do not need to be exposed in L6.
|
||||||
SeqNumL6Point = uint64(1)
|
SeqNumL6 = uint64(1)
|
||||||
|
|
||||||
// SeqNumL6RangeKey is the sequence number reserved for foreign range keys in
|
// SeqNumL5 is the sequence number reserved for foreign keys in L5. This seqnum
|
||||||
// L6. Only RangeKeySets are expected at this level.
|
// needs to be greater than SeqNumL6, so that range deletes in L5 can be
|
||||||
SeqNumL6RangeKey = uint64(2)
|
// exposed at this level and will correctly delete covering points in L6. Also
|
||||||
|
// range key unsets/dels will be exposed at this seqnum and will need to shadow
|
||||||
|
// overlapping range keys in L6.
|
||||||
|
//
|
||||||
|
// Note that we can use the same sequence number for all exposed keys in L5 as
|
||||||
|
// range dels do not delete points at the same seqnum, and range key
|
||||||
|
// unsets/deletes do not coalesce with range key sets at the same seqnum. Range
|
||||||
|
// key masking does not care about the sequence number of overlapping points
|
||||||
|
// (rather, it applies to points based on suffixes), so we can use this seqnum
|
||||||
|
// for all L5 keys exposed.
|
||||||
|
SeqNumL5 = uint64(2)
|
||||||
|
|
||||||
// SeqNumL5RangeDel is the sequence number reserved for foreign range deletes
|
// Sequence numbers 3-9 are reserved for future use.
|
||||||
// in L5. These keys could delete L6 points.
|
|
||||||
SeqNumL5RangeDel = uint64(3)
|
|
||||||
|
|
||||||
// SeqNumL5Point is the sequence number reserved for foreign point keys in L5.
|
|
||||||
// Any sst-local range deletions would have already been applied to these keys,
|
|
||||||
// so they can safely get a sequence number higher than SeqNumL5RangeDel.
|
|
||||||
// However they must have a sequence number lower than SeqNumL5RangeKey* for
|
|
||||||
// range key masking to work correctly.
|
|
||||||
SeqNumL5Point = uint64(4)
|
|
||||||
|
|
||||||
// SeqNumL5RangeKeyUnsetDel is the sequence number reserved for foreign
|
|
||||||
// range key unsets/deletes in L5. These operations could apply to L6
|
|
||||||
// RangeKeySets, so this sequence number must be > SeqNumL6RangeKey.
|
|
||||||
SeqNumL5RangeKeyUnsetDel = uint64(5)
|
|
||||||
|
|
||||||
// SeqNumL5RangeKeySet is the sequence number reserved for foreign range key
|
|
||||||
// Sets in L5. These operations could apply to L6 RangeKeySets, so this
|
|
||||||
// sequence number must be > SeqNumL6RangeKey. Any SST-local rangekey
|
|
||||||
// unsets/dels have already been applied to them, so their sequence number must
|
|
||||||
// be > SeqNumL5RangeKeyUnsetDel.
|
|
||||||
SeqNumL5RangeKeySet = uint64(6)
|
|
||||||
|
|
||||||
// Sequence numbers 7-9 are reserved for future use.
|
|
||||||
|
|
||||||
// SeqNumStart is the first sequence number assigned to a key written by
|
// SeqNumStart is the first sequence number assigned to a key written by
|
||||||
// ourselves.
|
// ourselves.
|
||||||
SeqNumStart = uint64(10)
|
SeqNumStart = uint64(10)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SeqNumForLevel returns the appropriate reserved sequence number for keys in
|
||||||
|
// foreign sstables at the specified level.
|
||||||
|
func SeqNumForLevel(level int) uint64 {
|
||||||
|
switch level {
|
||||||
|
case 5:
|
||||||
|
return SeqNumL5
|
||||||
|
case 6:
|
||||||
|
return SeqNumL6
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected foreign sstable at level %d", level))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
5
vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go
generated
vendored
5
vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go
generated
vendored
@@ -62,7 +62,7 @@ type FragmentIterator interface {
|
|||||||
|
|
||||||
// TableNewSpanIter creates a new iterator for range key spans for the given
|
// TableNewSpanIter creates a new iterator for range key spans for the given
|
||||||
// file.
|
// file.
|
||||||
type TableNewSpanIter func(file *manifest.FileMetadata, iterOptions *SpanIterOptions) (FragmentIterator, error)
|
type TableNewSpanIter func(file *manifest.FileMetadata, iterOptions SpanIterOptions) (FragmentIterator, error)
|
||||||
|
|
||||||
// SpanIterOptions is a subset of IterOptions that are necessary to instantiate
|
// SpanIterOptions is a subset of IterOptions that are necessary to instantiate
|
||||||
// per-sstable span iterators.
|
// per-sstable span iterators.
|
||||||
@@ -70,6 +70,9 @@ type SpanIterOptions struct {
|
|||||||
// RangeKeyFilters can be used to avoid scanning tables and blocks in tables
|
// RangeKeyFilters can be used to avoid scanning tables and blocks in tables
|
||||||
// when iterating over range keys.
|
// when iterating over range keys.
|
||||||
RangeKeyFilters []base.BlockPropertyFilter
|
RangeKeyFilters []base.BlockPropertyFilter
|
||||||
|
// Level specifies the level where this sstable is being read. Must be
|
||||||
|
// specified for foreign (i.e. shared not-created-by-this-instance) sstables.
|
||||||
|
Level manifest.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iter is an iterator over a set of fragmented spans.
|
// Iter is an iterator over a set of fragmented spans.
|
||||||
|
|||||||
4
vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go
generated
vendored
@@ -98,7 +98,7 @@ func (l *LevelIter) Init(
|
|||||||
) {
|
) {
|
||||||
l.err = nil
|
l.err = nil
|
||||||
l.level = level
|
l.level = level
|
||||||
l.tableOpts.RangeKeyFilters = opts.RangeKeyFilters
|
l.tableOpts = opts
|
||||||
l.cmp = cmp
|
l.cmp = cmp
|
||||||
l.iterFile = nil
|
l.iterFile = nil
|
||||||
l.newIter = newIter
|
l.newIter = newIter
|
||||||
@@ -179,7 +179,7 @@ func (l *LevelIter) loadFile(file *manifest.FileMetadata, dir int) loadFileRetur
|
|||||||
return noFileLoaded
|
return noFileLoaded
|
||||||
}
|
}
|
||||||
if indicator != fileAlreadyLoaded {
|
if indicator != fileAlreadyLoaded {
|
||||||
l.iter, l.err = l.newIter(file, &l.tableOpts)
|
l.iter, l.err = l.newIter(file, l.tableOpts)
|
||||||
indicator = newFileLoaded
|
indicator = newFileLoaded
|
||||||
}
|
}
|
||||||
if l.err != nil {
|
if l.err != nil {
|
||||||
|
|||||||
43
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
43
vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go
generated
vendored
@@ -25,49 +25,6 @@ import (
|
|||||||
// MergingIter implementation, but will require a bit of plumbing to thread the
|
// MergingIter implementation, but will require a bit of plumbing to thread the
|
||||||
// Equal function.
|
// Equal function.
|
||||||
|
|
||||||
// Transformer defines a transformation to be applied to a Span.
|
|
||||||
type Transformer interface {
|
|
||||||
// Transform takes a Span as input and writes the transformed Span to the
|
|
||||||
// provided output *Span pointer. The output Span's Keys slice may be reused
|
|
||||||
// by Transform to reduce allocations.
|
|
||||||
Transform(cmp base.Compare, in Span, out *Span) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// The TransformerFunc type is an adapter to allow the use of ordinary functions
|
|
||||||
// as Transformers. If f is a function with the appropriate signature,
|
|
||||||
// TransformerFunc(f) is a Transformer that calls f.
|
|
||||||
type TransformerFunc func(base.Compare, Span, *Span) error
|
|
||||||
|
|
||||||
// Transform calls f(cmp, in, out).
|
|
||||||
func (tf TransformerFunc) Transform(cmp base.Compare, in Span, out *Span) error {
|
|
||||||
return tf(cmp, in, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
var noopTransform Transformer = TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
|
||||||
dst.Start, dst.End = s.Start, s.End
|
|
||||||
dst.Keys = append(dst.Keys[:0], s.Keys...)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// VisibleTransform filters keys that are invisible at the provided snapshot
|
|
||||||
// sequence number.
|
|
||||||
func VisibleTransform(snapshot uint64) Transformer {
|
|
||||||
return TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
|
||||||
dst.Start, dst.End = s.Start, s.End
|
|
||||||
dst.Keys = dst.Keys[:0]
|
|
||||||
for _, k := range s.Keys {
|
|
||||||
// NB: The InternalKeySeqNumMax value is used for the batch snapshot
|
|
||||||
// because a batch's visible span keys are filtered when they're
|
|
||||||
// fragmented. There's no requirement to enforce visibility at
|
|
||||||
// iteration time.
|
|
||||||
if base.Visible(k.SeqNum(), snapshot, base.InternalKeySeqNumMax) {
|
|
||||||
dst.Keys = append(dst.Keys, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergingIter merges spans across levels of the LSM, exposing an iterator over
|
// MergingIter merges spans across levels of the LSM, exposing an iterator over
|
||||||
// spans that yields sets of spans fragmented at unique user key boundaries.
|
// spans that yields sets of spans fragmented at unique user key boundaries.
|
||||||
//
|
//
|
||||||
|
|||||||
1
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
1
vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go
generated
vendored
@@ -368,6 +368,7 @@ func (s Span) String() string {
|
|||||||
|
|
||||||
// Pretty returns a formatter for the span.
|
// Pretty returns a formatter for the span.
|
||||||
func (s Span) Pretty(f base.FormatKey) fmt.Formatter {
|
func (s Span) Pretty(f base.FormatKey) fmt.Formatter {
|
||||||
|
// TODO(jackson): Take a base.FormatValue to format Key.Value too.
|
||||||
return prettySpan{s, f}
|
return prettySpan{s, f}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
149
vendor/github.com/cockroachdb/pebble/internal/keyspan/transformer.go
generated
vendored
Normal file
149
vendor/github.com/cockroachdb/pebble/internal/keyspan/transformer.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2023 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"
|
||||||
|
|
||||||
|
// Transformer defines a transformation to be applied to a Span.
|
||||||
|
type Transformer interface {
|
||||||
|
// Transform takes a Span as input and writes the transformed Span to the
|
||||||
|
// provided output *Span pointer. The output Span's Keys slice may be reused
|
||||||
|
// by Transform to reduce allocations.
|
||||||
|
Transform(cmp base.Compare, in Span, out *Span) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// The TransformerFunc type is an adapter to allow the use of ordinary functions
|
||||||
|
// as Transformers. If f is a function with the appropriate signature,
|
||||||
|
// TransformerFunc(f) is a Transformer that calls f.
|
||||||
|
type TransformerFunc func(base.Compare, Span, *Span) error
|
||||||
|
|
||||||
|
// Transform calls f(cmp, in, out).
|
||||||
|
func (tf TransformerFunc) Transform(cmp base.Compare, in Span, out *Span) error {
|
||||||
|
return tf(cmp, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
var noopTransform Transformer = TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
||||||
|
dst.Start, dst.End = s.Start, s.End
|
||||||
|
dst.Keys = append(dst.Keys[:0], s.Keys...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// VisibleTransform filters keys that are invisible at the provided snapshot
|
||||||
|
// sequence number.
|
||||||
|
func VisibleTransform(snapshot uint64) Transformer {
|
||||||
|
return TransformerFunc(func(_ base.Compare, s Span, dst *Span) error {
|
||||||
|
dst.Start, dst.End = s.Start, s.End
|
||||||
|
dst.Keys = dst.Keys[:0]
|
||||||
|
for _, k := range s.Keys {
|
||||||
|
// NB: The InternalKeySeqNumMax value is used for the batch snapshot
|
||||||
|
// because a batch's visible span keys are filtered when they're
|
||||||
|
// fragmented. There's no requirement to enforce visibility at
|
||||||
|
// iteration time.
|
||||||
|
if base.Visible(k.SeqNum(), snapshot, base.InternalKeySeqNumMax) {
|
||||||
|
dst.Keys = append(dst.Keys, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformerIter is a FragmentIterator that applies a Transformer on all
|
||||||
|
// returned keys. Used for when a caller needs to apply a transformer on an
|
||||||
|
// iterator but does not otherwise need the mergingiter's merging ability.
|
||||||
|
type TransformerIter struct {
|
||||||
|
FragmentIterator
|
||||||
|
|
||||||
|
// Transformer is applied on every Span returned by this iterator.
|
||||||
|
Transformer Transformer
|
||||||
|
// Comparer in use for this keyspace.
|
||||||
|
Compare base.Compare
|
||||||
|
|
||||||
|
span Span
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TransformerIter) applyTransform(span *Span) *Span {
|
||||||
|
t.span = Span{
|
||||||
|
Start: t.span.Start[:0],
|
||||||
|
End: t.span.End[:0],
|
||||||
|
Keys: t.span.Keys[:0],
|
||||||
|
}
|
||||||
|
if err := t.Transformer.Transform(t.Compare, *span, &t.span); err != nil {
|
||||||
|
t.err = err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &t.span
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeekGE implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) SeekGE(key []byte) *Span {
|
||||||
|
span := t.FragmentIterator.SeekGE(key)
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeekLT implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) SeekLT(key []byte) *Span {
|
||||||
|
span := t.FragmentIterator.SeekLT(key)
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) First() *Span {
|
||||||
|
span := t.FragmentIterator.First()
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) Last() *Span {
|
||||||
|
span := t.FragmentIterator.Last()
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) Next() *Span {
|
||||||
|
span := t.FragmentIterator.Next()
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prev implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) Prev() *Span {
|
||||||
|
span := t.FragmentIterator.Prev()
|
||||||
|
if span == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.applyTransform(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) Error() error {
|
||||||
|
if t.err != nil {
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
|
return t.FragmentIterator.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements the FragmentIterator interface.
|
||||||
|
func (t *TransformerIter) Close() error {
|
||||||
|
err := t.FragmentIterator.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
20
vendor/github.com/cockroachdb/pebble/internal/manifest/version.go
generated
vendored
20
vendor/github.com/cockroachdb/pebble/internal/manifest/version.go
generated
vendored
@@ -227,6 +227,12 @@ type FileMetadata struct {
|
|||||||
// we'd have to write virtual sstable stats to the version edit.
|
// we'd have to write virtual sstable stats to the version edit.
|
||||||
Stats TableStats
|
Stats TableStats
|
||||||
|
|
||||||
|
// Deleted is set to true if a VersionEdit gets installed that has deleted
|
||||||
|
// this file. Protected by the manifest lock (see versionSet.logLock()).
|
||||||
|
Deleted bool
|
||||||
|
|
||||||
|
// For L0 files only. Protected by DB.mu. Used to generate L0 sublevels and
|
||||||
|
// pick L0 compactions. Only accurate for the most recent Version.
|
||||||
SubLevel int
|
SubLevel int
|
||||||
L0Index int
|
L0Index int
|
||||||
minIntervalIndex int
|
minIntervalIndex int
|
||||||
@@ -235,9 +241,6 @@ type FileMetadata struct {
|
|||||||
// NB: the alignment of this struct is 8 bytes. We pack all the bools to
|
// NB: the alignment of this struct is 8 bytes. We pack all the bools to
|
||||||
// ensure an optimal packing.
|
// ensure an optimal packing.
|
||||||
|
|
||||||
// For L0 files only. Protected by DB.mu. Used to generate L0 sublevels and
|
|
||||||
// pick L0 compactions. Only accurate for the most recent Version.
|
|
||||||
//
|
|
||||||
// IsIntraL0Compacting is set to True if this file is part of an intra-L0
|
// IsIntraL0Compacting is set to True if this file is part of an intra-L0
|
||||||
// compaction. When it's true, IsCompacting must also return true. If
|
// compaction. When it's true, IsCompacting must also return true. If
|
||||||
// Compacting is true and IsIntraL0Compacting is false for an L0 file, the
|
// Compacting is true and IsIntraL0Compacting is false for an L0 file, the
|
||||||
@@ -370,6 +373,17 @@ func (m *FileMetadata) InitPhysicalBacking() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitProviderBacking creates a new FileBacking for a file backed by
|
||||||
|
// an objstorage.Provider.
|
||||||
|
func (m *FileMetadata) InitProviderBacking(fileNum base.DiskFileNum) {
|
||||||
|
if !m.Virtual {
|
||||||
|
panic("pebble: provider-backed sstables must be virtual")
|
||||||
|
}
|
||||||
|
if m.FileBacking == nil {
|
||||||
|
m.FileBacking = &FileBacking{DiskFileNum: fileNum}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateVirtual should be called once the FileMetadata for a virtual sstable
|
// ValidateVirtual should be called once the FileMetadata for a virtual sstable
|
||||||
// is created to verify that the fields of the virtual sstable are sound.
|
// is created to verify that the fields of the virtual sstable are sound.
|
||||||
func (m *FileMetadata) ValidateVirtual(createdFrom *FileMetadata) {
|
func (m *FileMetadata) ValidateVirtual(createdFrom *FileMetadata) {
|
||||||
|
|||||||
107
vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go
generated
vendored
107
vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go
generated
vendored
@@ -51,7 +51,9 @@ const (
|
|||||||
tagMaxColumnFamily = 203
|
tagMaxColumnFamily = 203
|
||||||
|
|
||||||
// Pebble tags.
|
// Pebble tags.
|
||||||
tagNewFile5 = 104 // Range keys.
|
tagNewFile5 = 104 // Range keys.
|
||||||
|
tagCreatedBackingTable = 105
|
||||||
|
tagRemovedBackingTable = 106
|
||||||
|
|
||||||
// The custom tags sub-format used by tagNewFile4 and above.
|
// The custom tags sub-format used by tagNewFile4 and above.
|
||||||
customTagTerminate = 1
|
customTagTerminate = 1
|
||||||
@@ -59,6 +61,7 @@ const (
|
|||||||
customTagCreationTime = 6
|
customTagCreationTime = 6
|
||||||
customTagPathID = 65
|
customTagPathID = 65
|
||||||
customTagNonSafeIgnoreMask = 1 << 6
|
customTagNonSafeIgnoreMask = 1 << 6
|
||||||
|
customTagVirtual = 66
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeletedFileEntry holds the state for a file deletion from a level. The file
|
// DeletedFileEntry holds the state for a file deletion from a level. The file
|
||||||
@@ -73,6 +76,9 @@ type DeletedFileEntry struct {
|
|||||||
type NewFileEntry struct {
|
type NewFileEntry struct {
|
||||||
Level int
|
Level int
|
||||||
Meta *FileMetadata
|
Meta *FileMetadata
|
||||||
|
// BackingFileNum is only set during manifest replay, and only for virtual
|
||||||
|
// sstables.
|
||||||
|
BackingFileNum base.DiskFileNum
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionEdit holds the state for an edit to a Version along with other
|
// VersionEdit holds the state for an edit to a Version along with other
|
||||||
@@ -143,7 +149,9 @@ type VersionEdit struct {
|
|||||||
|
|
||||||
// Decode decodes an edit from the specified reader.
|
// Decode decodes an edit from the specified reader.
|
||||||
//
|
//
|
||||||
// TODO(bananabrick): Support decoding of virtual sstable state.
|
// Note that the Decode step will not set the FileBacking for virtual sstables
|
||||||
|
// and the responsibility is left to the caller. However, the Decode step will
|
||||||
|
// populate the NewFileEntry.BackingFileNum in VersionEdit.NewFiles.
|
||||||
func (v *VersionEdit) Decode(r io.Reader) error {
|
func (v *VersionEdit) Decode(r io.Reader) error {
|
||||||
br, ok := r.(byteReader)
|
br, ok := r.(byteReader)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -196,6 +204,28 @@ func (v *VersionEdit) Decode(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
// NB: RocksDB does not use compaction pointers anymore.
|
// NB: RocksDB does not use compaction pointers anymore.
|
||||||
|
|
||||||
|
case tagRemovedBackingTable:
|
||||||
|
n, err := d.readUvarint()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.RemovedBackingTables = append(
|
||||||
|
v.RemovedBackingTables, base.FileNum(n).DiskFileNum(),
|
||||||
|
)
|
||||||
|
case tagCreatedBackingTable:
|
||||||
|
dfn, err := d.readUvarint()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size, err := d.readUvarint()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileBacking := &FileBacking{
|
||||||
|
DiskFileNum: base.FileNum(dfn).DiskFileNum(),
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
v.CreatedBackingTables = append(v.CreatedBackingTables, fileBacking)
|
||||||
case tagDeletedFile:
|
case tagDeletedFile:
|
||||||
level, err := d.readLevel()
|
level, err := d.readLevel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -301,6 +331,10 @@ func (v *VersionEdit) Decode(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
var markedForCompaction bool
|
var markedForCompaction bool
|
||||||
var creationTime uint64
|
var creationTime uint64
|
||||||
|
virtualState := struct {
|
||||||
|
virtual bool
|
||||||
|
backingFileNum uint64
|
||||||
|
}{}
|
||||||
if tag == tagNewFile4 || tag == tagNewFile5 {
|
if tag == tagNewFile4 || tag == tagNewFile5 {
|
||||||
for {
|
for {
|
||||||
customTag, err := d.readUvarint()
|
customTag, err := d.readUvarint()
|
||||||
@@ -309,7 +343,16 @@ func (v *VersionEdit) Decode(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
if customTag == customTagTerminate {
|
if customTag == customTagTerminate {
|
||||||
break
|
break
|
||||||
|
} else if customTag == customTagVirtual {
|
||||||
|
virtualState.virtual = true
|
||||||
|
n, err := d.readUvarint()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
virtualState.backingFileNum = n
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
field, err := d.readBytes()
|
field, err := d.readBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -345,6 +388,7 @@ func (v *VersionEdit) Decode(r io.Reader) error {
|
|||||||
SmallestSeqNum: smallestSeqNum,
|
SmallestSeqNum: smallestSeqNum,
|
||||||
LargestSeqNum: largestSeqNum,
|
LargestSeqNum: largestSeqNum,
|
||||||
MarkedForCompaction: markedForCompaction,
|
MarkedForCompaction: markedForCompaction,
|
||||||
|
Virtual: virtualState.virtual,
|
||||||
}
|
}
|
||||||
if tag != tagNewFile5 { // no range keys present
|
if tag != tagNewFile5 { // no range keys present
|
||||||
m.SmallestPointKey = base.DecodeInternalKey(smallestPointKey)
|
m.SmallestPointKey = base.DecodeInternalKey(smallestPointKey)
|
||||||
@@ -376,11 +420,18 @@ func (v *VersionEdit) Decode(r io.Reader) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.boundsSet = true
|
m.boundsSet = true
|
||||||
m.InitPhysicalBacking()
|
if !virtualState.virtual {
|
||||||
v.NewFiles = append(v.NewFiles, NewFileEntry{
|
m.InitPhysicalBacking()
|
||||||
|
}
|
||||||
|
|
||||||
|
nfe := NewFileEntry{
|
||||||
Level: level,
|
Level: level,
|
||||||
Meta: m,
|
Meta: m,
|
||||||
})
|
}
|
||||||
|
if virtualState.virtual {
|
||||||
|
nfe.BackingFileNum = base.FileNum(virtualState.backingFileNum).DiskFileNum()
|
||||||
|
}
|
||||||
|
v.NewFiles = append(v.NewFiles, nfe)
|
||||||
|
|
||||||
case tagPrevLogNumber:
|
case tagPrevLogNumber:
|
||||||
n, err := d.readUvarint()
|
n, err := d.readUvarint()
|
||||||
@@ -442,8 +493,6 @@ func (v *VersionEdit) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes an edit to the specified writer.
|
// Encode encodes an edit to the specified writer.
|
||||||
//
|
|
||||||
// TODO(bananabrick): Support encoding of virtual sstable state.
|
|
||||||
func (v *VersionEdit) Encode(w io.Writer) error {
|
func (v *VersionEdit) Encode(w io.Writer) error {
|
||||||
e := versionEditEncoder{new(bytes.Buffer)}
|
e := versionEditEncoder{new(bytes.Buffer)}
|
||||||
|
|
||||||
@@ -463,6 +512,15 @@ func (v *VersionEdit) Encode(w io.Writer) error {
|
|||||||
e.writeUvarint(tagNextFileNumber)
|
e.writeUvarint(tagNextFileNumber)
|
||||||
e.writeUvarint(uint64(v.NextFileNum))
|
e.writeUvarint(uint64(v.NextFileNum))
|
||||||
}
|
}
|
||||||
|
for _, dfn := range v.RemovedBackingTables {
|
||||||
|
e.writeUvarint(tagRemovedBackingTable)
|
||||||
|
e.writeUvarint(uint64(dfn.FileNum()))
|
||||||
|
}
|
||||||
|
for _, fileBacking := range v.CreatedBackingTables {
|
||||||
|
e.writeUvarint(tagCreatedBackingTable)
|
||||||
|
e.writeUvarint(uint64(fileBacking.DiskFileNum.FileNum()))
|
||||||
|
e.writeUvarint(fileBacking.Size)
|
||||||
|
}
|
||||||
// RocksDB requires LastSeqNum to be encoded for the first MANIFEST entry,
|
// RocksDB requires LastSeqNum to be encoded for the first MANIFEST entry,
|
||||||
// even though its value is zero. We detect this by encoding LastSeqNum when
|
// even though its value is zero. We detect this by encoding LastSeqNum when
|
||||||
// ComparerName is set.
|
// ComparerName is set.
|
||||||
@@ -476,7 +534,7 @@ func (v *VersionEdit) Encode(w io.Writer) error {
|
|||||||
e.writeUvarint(uint64(x.FileNum))
|
e.writeUvarint(uint64(x.FileNum))
|
||||||
}
|
}
|
||||||
for _, x := range v.NewFiles {
|
for _, x := range v.NewFiles {
|
||||||
customFields := x.Meta.MarkedForCompaction || x.Meta.CreationTime != 0
|
customFields := x.Meta.MarkedForCompaction || x.Meta.CreationTime != 0 || x.Meta.Virtual
|
||||||
var tag uint64
|
var tag uint64
|
||||||
switch {
|
switch {
|
||||||
case x.Meta.HasRangeKeys:
|
case x.Meta.HasRangeKeys:
|
||||||
@@ -529,6 +587,10 @@ func (v *VersionEdit) Encode(w io.Writer) error {
|
|||||||
e.writeUvarint(customTagNeedsCompaction)
|
e.writeUvarint(customTagNeedsCompaction)
|
||||||
e.writeBytes([]byte{1})
|
e.writeBytes([]byte{1})
|
||||||
}
|
}
|
||||||
|
if x.Meta.Virtual {
|
||||||
|
e.writeUvarint(customTagVirtual)
|
||||||
|
e.writeUvarint(uint64(x.Meta.FileBacking.DiskFileNum.FileNum()))
|
||||||
|
}
|
||||||
e.writeUvarint(customTagTerminate)
|
e.writeUvarint(customTagTerminate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,6 +598,7 @@ func (v *VersionEdit) Encode(w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// versionEditDecoder should be used to decode version edits.
|
||||||
type versionEditDecoder struct {
|
type versionEditDecoder struct {
|
||||||
byteReader
|
byteReader
|
||||||
}
|
}
|
||||||
@@ -634,7 +697,9 @@ type BulkVersionEdit struct {
|
|||||||
Added [NumLevels]map[base.FileNum]*FileMetadata
|
Added [NumLevels]map[base.FileNum]*FileMetadata
|
||||||
Deleted [NumLevels]map[base.FileNum]*FileMetadata
|
Deleted [NumLevels]map[base.FileNum]*FileMetadata
|
||||||
|
|
||||||
AddedFileBacking []*FileBacking
|
// AddedFileBacking is a map to support lookup so that we can populate the
|
||||||
|
// FileBacking of virtual sstables during manifest replay.
|
||||||
|
AddedFileBacking map[base.DiskFileNum]*FileBacking
|
||||||
RemovedFileBacking []base.DiskFileNum
|
RemovedFileBacking []base.DiskFileNum
|
||||||
|
|
||||||
// AddedByFileNum maps file number to file metadata for all added files
|
// AddedByFileNum maps file number to file metadata for all added files
|
||||||
@@ -697,6 +762,16 @@ func (b *BulkVersionEdit) Accumulate(ve *VersionEdit) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate state for Added backing files. Note that these must be generated
|
||||||
|
// before we loop through the NewFiles, because we need to populate the
|
||||||
|
// FileBackings which might be used by the NewFiles loop.
|
||||||
|
if b.AddedFileBacking == nil {
|
||||||
|
b.AddedFileBacking = make(map[base.DiskFileNum]*FileBacking)
|
||||||
|
}
|
||||||
|
for _, fb := range ve.CreatedBackingTables {
|
||||||
|
b.AddedFileBacking[fb.DiskFileNum] = fb
|
||||||
|
}
|
||||||
|
|
||||||
for _, nf := range ve.NewFiles {
|
for _, nf := range ve.NewFiles {
|
||||||
// A new file should not have been deleted in this or a preceding
|
// A new file should not have been deleted in this or a preceding
|
||||||
// VersionEdit at the same level (though files can move across levels).
|
// VersionEdit at the same level (though files can move across levels).
|
||||||
@@ -705,6 +780,17 @@ func (b *BulkVersionEdit) Accumulate(ve *VersionEdit) error {
|
|||||||
return base.CorruptionErrorf("pebble: file deleted L%d.%s before it was inserted", nf.Level, nf.Meta.FileNum)
|
return base.CorruptionErrorf("pebble: file deleted L%d.%s before it was inserted", nf.Level, nf.Meta.FileNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if nf.Meta.Virtual && nf.Meta.FileBacking == nil {
|
||||||
|
// FileBacking for a virtual sstable must only be nil if we're performing
|
||||||
|
// manifest replay.
|
||||||
|
nf.Meta.FileBacking = b.AddedFileBacking[nf.BackingFileNum]
|
||||||
|
if nf.Meta.FileBacking == nil {
|
||||||
|
return errors.Errorf("FileBacking for virtual sstable must not be nil")
|
||||||
|
}
|
||||||
|
} else if nf.Meta.FileBacking == nil {
|
||||||
|
return errors.Errorf("Added file L%d.%s's has no FileBacking", nf.Level, nf.Meta.FileNum)
|
||||||
|
}
|
||||||
|
|
||||||
if b.Added[nf.Level] == nil {
|
if b.Added[nf.Level] == nil {
|
||||||
b.Added[nf.Level] = make(map[base.FileNum]*FileMetadata)
|
b.Added[nf.Level] = make(map[base.FileNum]*FileMetadata)
|
||||||
}
|
}
|
||||||
@@ -717,9 +803,6 @@ func (b *BulkVersionEdit) Accumulate(ve *VersionEdit) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate state for the backing files.
|
|
||||||
b.AddedFileBacking = append(b.AddedFileBacking, ve.CreatedBackingTables...)
|
|
||||||
|
|
||||||
// Since a file can be removed from backing files in exactly one version
|
// Since a file can be removed from backing files in exactly one version
|
||||||
// edit it is safe to just append without any de-duplication.
|
// edit it is safe to just append without any de-duplication.
|
||||||
b.RemovedFileBacking = append(b.RemovedFileBacking, ve.RemovedBackingTables...)
|
b.RemovedFileBacking = append(b.RemovedFileBacking, ve.RemovedBackingTables...)
|
||||||
|
|||||||
76
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
76
vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go
generated
vendored
@@ -6,6 +6,7 @@ package rangekey
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@@ -367,3 +368,78 @@ func coalesce(
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForeignSSTTransformer implements a keyspan.Transformer for range keys in
|
||||||
|
// foreign sstables (i.e. shared sstables not created by us). It is largely
|
||||||
|
// similar to the Transform function implemented in UserIteratorConfig in that
|
||||||
|
// it calls coalesce to remove range keys shadowed by other range keys, but also
|
||||||
|
// retains the range key that does the shadowing. In addition, it elides
|
||||||
|
// RangeKey unsets/dels in L6 as they are inapplicable when reading from a
|
||||||
|
// different Pebble instance.
|
||||||
|
type ForeignSSTTransformer struct {
|
||||||
|
Comparer *base.Comparer
|
||||||
|
Level int
|
||||||
|
sortBuf keyspan.KeysBySuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform implements the Transformer interface.
|
||||||
|
func (f *ForeignSSTTransformer) Transform(
|
||||||
|
cmp base.Compare, s keyspan.Span, dst *keyspan.Span,
|
||||||
|
) error {
|
||||||
|
// Apply shadowing of keys.
|
||||||
|
dst.Start = s.Start
|
||||||
|
dst.End = s.End
|
||||||
|
f.sortBuf = keyspan.KeysBySuffix{
|
||||||
|
Cmp: cmp,
|
||||||
|
Keys: f.sortBuf.Keys[:0],
|
||||||
|
}
|
||||||
|
if err := coalesce(f.Comparer.Equal, &f.sortBuf, math.MaxUint64, s.Keys); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
keys := f.sortBuf.Keys
|
||||||
|
dst.Keys = dst.Keys[:0]
|
||||||
|
for i := range keys {
|
||||||
|
seqNum := keys[i].SeqNum()
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
switch f.Level {
|
||||||
|
case 5:
|
||||||
|
fallthrough
|
||||||
|
case 6:
|
||||||
|
if seqNum != base.SeqNumForLevel(f.Level) {
|
||||||
|
panic(fmt.Sprintf("pebble: expected range key iter to return seqnum %d, got %d", base.SeqNumForLevel(f.Level), seqNum))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case base.InternalKeyKindRangeKeyDelete:
|
||||||
|
switch f.Level {
|
||||||
|
case 5:
|
||||||
|
// Emit this key.
|
||||||
|
if seqNum != base.SeqNumForLevel(f.Level) {
|
||||||
|
panic(fmt.Sprintf("pebble: expected range key iter to return seqnum %d, got %d", base.SeqNumForLevel(f.Level), seqNum))
|
||||||
|
}
|
||||||
|
case 6:
|
||||||
|
// Skip this key, as foreign sstable in L6 do not need to emit range key
|
||||||
|
// unsets/dels as they do not apply to any other sstables.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return base.CorruptionErrorf("pebble: unrecognized range key kind %s", keys[i].Kind())
|
||||||
|
}
|
||||||
|
dst.Keys = append(dst.Keys, keyspan.Key{
|
||||||
|
Trailer: base.MakeTrailer(seqNum, keys[i].Kind()),
|
||||||
|
Suffix: keys[i].Suffix,
|
||||||
|
Value: keys[i].Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// coalesce results in dst.Keys being sorted by Suffix.
|
||||||
|
dst.KeysOrder = keyspan.BySuffixAsc
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
18
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/iterator.go
generated
vendored
@@ -568,7 +568,10 @@ func (i *Iterator) findNextEntry(limit []byte) {
|
|||||||
i.iterValidityState = IterValid
|
i.iterValidityState = IterValid
|
||||||
return
|
return
|
||||||
|
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
|
// NB: treating InternalKeyKindSingleDelete as equivalent to DEL is not
|
||||||
|
// only simpler, but is also necessary for correctness due to
|
||||||
|
// InternalKeyKindSSTableInternalObsoleteBit.
|
||||||
i.nextUserKey()
|
i.nextUserKey()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -631,7 +634,10 @@ func (i *Iterator) nextPointCurrentUserKey() bool {
|
|||||||
i.err = base.CorruptionErrorf("pebble: unexpected range key set mid-user key")
|
i.err = base.CorruptionErrorf("pebble: unexpected range key set mid-user key")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
|
// NB: treating InternalKeyKindSingleDelete as equivalent to DEL is not
|
||||||
|
// only simpler, but is also necessary for correctness due to
|
||||||
|
// InternalKeyKindSSTableInternalObsoleteBit.
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
|
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
|
||||||
@@ -929,7 +935,7 @@ func (i *Iterator) findPrevEntry(limit []byte) {
|
|||||||
// return the key even if the MERGE point key is deleted.
|
// return the key even if the MERGE point key is deleted.
|
||||||
rangeKeyBoundary = true
|
rangeKeyBoundary = true
|
||||||
|
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
i.value = LazyValue{}
|
i.value = LazyValue{}
|
||||||
i.iterValidityState = IterExhausted
|
i.iterValidityState = IterExhausted
|
||||||
valueMerger = nil
|
valueMerger = nil
|
||||||
@@ -1092,9 +1098,13 @@ func (i *Iterator) mergeNext(key InternalKey, valueMerger ValueMerger) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch key.Kind() {
|
switch key.Kind() {
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized:
|
||||||
// We've hit a deletion tombstone. Return everything up to this
|
// We've hit a deletion tombstone. Return everything up to this
|
||||||
// point.
|
// point.
|
||||||
|
//
|
||||||
|
// NB: treating InternalKeyKindSingleDelete as equivalent to DEL is not
|
||||||
|
// only simpler, but is also necessary for correctness due to
|
||||||
|
// InternalKeyKindSSTableInternalObsoleteBit.
|
||||||
return
|
return
|
||||||
|
|
||||||
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
|
case InternalKeyKindSet, InternalKeyKindSetWithDelete:
|
||||||
|
|||||||
4
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/level_checker.go
generated
vendored
@@ -165,7 +165,7 @@ func (m *simpleMergingIter) step() bool {
|
|||||||
if m.valueMerger != nil {
|
if m.valueMerger != nil {
|
||||||
// Ongoing series of MERGE records.
|
// Ongoing series of MERGE records.
|
||||||
switch item.key.Kind() {
|
switch item.key.Kind() {
|
||||||
case InternalKeyKindSingleDelete, InternalKeyKindDelete:
|
case InternalKeyKindSingleDelete, InternalKeyKindDelete, InternalKeyKindDeleteSized:
|
||||||
var closer io.Closer
|
var closer io.Closer
|
||||||
_, closer, m.err = m.valueMerger.Finish(true /* includesBase */)
|
_, closer, m.err = m.valueMerger.Finish(true /* includesBase */)
|
||||||
if m.err == nil && closer != nil {
|
if m.err == nil && closer != nil {
|
||||||
@@ -379,7 +379,7 @@ func checkRangeTombstones(c *checkConfig) error {
|
|||||||
atomicUnit, _ := expandToAtomicUnit(c.cmp, lf.Slice(), true /* disableIsCompacting */)
|
atomicUnit, _ := expandToAtomicUnit(c.cmp, lf.Slice(), true /* disableIsCompacting */)
|
||||||
lower, upper := manifest.KeyRange(c.cmp, atomicUnit.Iter())
|
lower, upper := manifest.KeyRange(c.cmp, atomicUnit.Iter())
|
||||||
iterToClose, iter, err := c.newIters(
|
iterToClose, iter, err := c.newIters(
|
||||||
context.Background(), lf.FileMetadata, nil, internalIterOpts{})
|
context.Background(), lf.FileMetadata, &IterOptions{level: manifest.Level(lsmLevel)}, internalIterOpts{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
13
vendor/github.com/cockroachdb/pebble/level_iter.go
generated
vendored
13
vendor/github.com/cockroachdb/pebble/level_iter.go
generated
vendored
@@ -36,9 +36,9 @@ type tableNewIters func(
|
|||||||
// tableNewRangeDelIter takes a tableNewIters and returns a TableNewSpanIter
|
// tableNewRangeDelIter takes a tableNewIters and returns a TableNewSpanIter
|
||||||
// for the rangedel iterator returned by tableNewIters.
|
// for the rangedel iterator returned by tableNewIters.
|
||||||
func tableNewRangeDelIter(ctx context.Context, newIters tableNewIters) keyspan.TableNewSpanIter {
|
func tableNewRangeDelIter(ctx context.Context, newIters tableNewIters) keyspan.TableNewSpanIter {
|
||||||
return func(file *manifest.FileMetadata, iterOptions *keyspan.SpanIterOptions) (keyspan.FragmentIterator, error) {
|
return func(file *manifest.FileMetadata, iterOptions keyspan.SpanIterOptions) (keyspan.FragmentIterator, error) {
|
||||||
iter, rangeDelIter, err := newIters(
|
iter, rangeDelIter, err := newIters(
|
||||||
ctx, file, &IterOptions{RangeKeyFilters: iterOptions.RangeKeyFilters}, internalIterOpts{})
|
ctx, file, &IterOptions{RangeKeyFilters: iterOptions.RangeKeyFilters, level: iterOptions.Level}, internalIterOpts{})
|
||||||
if iter != nil {
|
if iter != nil {
|
||||||
_ = iter.Close()
|
_ = iter.Close()
|
||||||
}
|
}
|
||||||
@@ -200,6 +200,11 @@ type levelIter struct {
|
|||||||
// cache when constructing new table iterators.
|
// cache when constructing new table iterators.
|
||||||
internalOpts internalIterOpts
|
internalOpts internalIterOpts
|
||||||
|
|
||||||
|
// Scratch space for the obsolete keys filter, when there are no other block
|
||||||
|
// property filters specified. See the performance note where
|
||||||
|
// IterOptions.PointKeyFilters is declared.
|
||||||
|
filtersBuf [1]BlockPropertyFilter
|
||||||
|
|
||||||
// Disable invariant checks even if they are otherwise enabled. Used by tests
|
// Disable invariant checks even if they are otherwise enabled. Used by tests
|
||||||
// which construct "impossible" situations (e.g. seeking to a key before the
|
// which construct "impossible" situations (e.g. seeking to a key before the
|
||||||
// lower bound).
|
// lower bound).
|
||||||
@@ -267,8 +272,12 @@ func (l *levelIter) init(
|
|||||||
l.upper = opts.UpperBound
|
l.upper = opts.UpperBound
|
||||||
l.tableOpts.TableFilter = opts.TableFilter
|
l.tableOpts.TableFilter = opts.TableFilter
|
||||||
l.tableOpts.PointKeyFilters = opts.PointKeyFilters
|
l.tableOpts.PointKeyFilters = opts.PointKeyFilters
|
||||||
|
if len(opts.PointKeyFilters) == 0 {
|
||||||
|
l.tableOpts.PointKeyFilters = l.filtersBuf[:0:1]
|
||||||
|
}
|
||||||
l.tableOpts.UseL6Filters = opts.UseL6Filters
|
l.tableOpts.UseL6Filters = opts.UseL6Filters
|
||||||
l.tableOpts.level = l.level
|
l.tableOpts.level = l.level
|
||||||
|
l.tableOpts.snapshotForHideObsoletePoints = opts.snapshotForHideObsoletePoints
|
||||||
l.cmp = cmp
|
l.cmp = cmp
|
||||||
l.split = split
|
l.split = split
|
||||||
l.iterFile = nil
|
l.iterFile = nil
|
||||||
|
|||||||
11
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
11
vendor/github.com/cockroachdb/pebble/metrics.go
generated
vendored
@@ -184,6 +184,11 @@ type Metrics struct {
|
|||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ingest struct {
|
||||||
|
// The total number of ingestions
|
||||||
|
Count uint64
|
||||||
|
}
|
||||||
|
|
||||||
Flush struct {
|
Flush struct {
|
||||||
// The total number of flushes.
|
// The total number of flushes.
|
||||||
Count int64
|
Count int64
|
||||||
@@ -225,6 +230,9 @@ type Metrics struct {
|
|||||||
// The approximate count of internal tombstones (DEL, SINGLEDEL and
|
// The approximate count of internal tombstones (DEL, SINGLEDEL and
|
||||||
// RANGEDEL key kinds) within the database.
|
// RANGEDEL key kinds) within the database.
|
||||||
TombstoneCount uint64
|
TombstoneCount uint64
|
||||||
|
// A cumulative total number of missized DELSIZED keys encountered by
|
||||||
|
// compactions since the database was opened.
|
||||||
|
MissizedTombstonesCount uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
Snapshots struct {
|
Snapshots struct {
|
||||||
@@ -504,6 +512,9 @@ func (m *Metrics) SafeFormat(w redact.SafePrinter, _ rune) {
|
|||||||
notApplicable,
|
notApplicable,
|
||||||
notApplicable,
|
notApplicable,
|
||||||
redact.Safe(hitRate(m.Filter.Hits, m.Filter.Misses)))
|
redact.Safe(hitRate(m.Filter.Hits, m.Filter.Misses)))
|
||||||
|
w.Printf(" ingest %9d\n",
|
||||||
|
redact.Safe(m.Ingest.Count),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hitRate(hits, misses int64) float64 {
|
func hitRate(hits, misses int64) float64 {
|
||||||
|
|||||||
4
vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go
generated
vendored
4
vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go
generated
vendored
@@ -27,8 +27,8 @@ func (h *NoopReadHandle) ReadAt(ctx context.Context, p []byte, off int64) error
|
|||||||
// Close is part of the ReadHandle interface.
|
// Close is part of the ReadHandle interface.
|
||||||
func (*NoopReadHandle) Close() error { return nil }
|
func (*NoopReadHandle) Close() error { return nil }
|
||||||
|
|
||||||
// MaxReadahead is part of the ReadHandle interface.
|
// SetupForCompaction is part of the ReadHandle interface.
|
||||||
func (*NoopReadHandle) MaxReadahead() {}
|
func (*NoopReadHandle) SetupForCompaction() {}
|
||||||
|
|
||||||
// RecordCacheHit is part of the ReadHandle interface.
|
// RecordCacheHit is part of the ReadHandle interface.
|
||||||
func (*NoopReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {}
|
func (*NoopReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {}
|
||||||
|
|||||||
18
vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go
generated
vendored
@@ -16,6 +16,9 @@ import (
|
|||||||
type Readable interface {
|
type Readable interface {
|
||||||
// ReadAt reads len(p) bytes into p starting at offset off.
|
// ReadAt reads len(p) bytes into p starting at offset off.
|
||||||
//
|
//
|
||||||
|
// Does not return partial results; if off + len(p) is past the end of the
|
||||||
|
// object, an error is returned.
|
||||||
|
//
|
||||||
// Clients of ReadAt can execute parallel ReadAt calls on the
|
// Clients of ReadAt can execute parallel ReadAt calls on the
|
||||||
// same Readable.
|
// same Readable.
|
||||||
ReadAt(ctx context.Context, p []byte, off int64) error
|
ReadAt(ctx context.Context, p []byte, off int64) error
|
||||||
@@ -39,14 +42,18 @@ type Readable interface {
|
|||||||
type ReadHandle interface {
|
type ReadHandle interface {
|
||||||
// ReadAt reads len(p) bytes into p starting at offset off.
|
// ReadAt reads len(p) bytes into p starting at offset off.
|
||||||
//
|
//
|
||||||
|
// Does not return partial results; if off + len(p) is past the end of the
|
||||||
|
// object, an error is returned.
|
||||||
|
//
|
||||||
// Parallel ReadAt calls on the same ReadHandle are not allowed.
|
// Parallel ReadAt calls on the same ReadHandle are not allowed.
|
||||||
ReadAt(ctx context.Context, p []byte, off int64) error
|
ReadAt(ctx context.Context, p []byte, off int64) error
|
||||||
|
|
||||||
Close() error
|
Close() error
|
||||||
|
|
||||||
// MaxReadahead configures the implementation to expect large sequential
|
// SetupForCompaction informs the implementation that the read handle will
|
||||||
// reads. Used to skip any initial read-ahead ramp-up.
|
// be used to read data blocks for a compaction. The implementation can expect
|
||||||
MaxReadahead()
|
// sequential reads, and can decide to not retain data in any caches.
|
||||||
|
SetupForCompaction()
|
||||||
|
|
||||||
// RecordCacheHit informs the implementation that we were able to retrieve a
|
// RecordCacheHit informs the implementation that we were able to retrieve a
|
||||||
// block from cache. This is useful for example when the implementation is
|
// block from cache. This is useful for example when the implementation is
|
||||||
@@ -209,6 +216,11 @@ type Provider interface {
|
|||||||
// Cannot be called if shared storage is not configured for the provider.
|
// Cannot be called if shared storage is not configured for the provider.
|
||||||
SetCreatorID(creatorID CreatorID) error
|
SetCreatorID(creatorID CreatorID) error
|
||||||
|
|
||||||
|
// IsForeign returns whether this object is owned by a different node. Return
|
||||||
|
// value undefined if creator ID is not set yet, or if this object does not
|
||||||
|
// exist in this provider.
|
||||||
|
IsForeign(meta ObjectMetadata) bool
|
||||||
|
|
||||||
// SharedObjectBacking encodes the shared object metadata.
|
// SharedObjectBacking encodes the shared object metadata.
|
||||||
SharedObjectBacking(meta *ObjectMetadata) (SharedObjectBackingHandle, error)
|
SharedObjectBacking(meta *ObjectMetadata) (SharedObjectBackingHandle, error)
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ const (
|
|||||||
// RecordCacheHitOp happens when a read is satisfied from the block cache. See
|
// RecordCacheHitOp happens when a read is satisfied from the block cache. See
|
||||||
// objstorage.ReadHandle.RecordCacheHit().
|
// objstorage.ReadHandle.RecordCacheHit().
|
||||||
RecordCacheHitOp
|
RecordCacheHitOp
|
||||||
// MaxReadaheadOp is a "meta operation" that configures a read handle for
|
// SetupForCompactionOp is a "meta operation" that configures a read handle
|
||||||
// large sequential reads. See objstorage.ReadHandle.MaxReadahead().
|
// for large sequential reads. See objstorage.ReadHandle.SetupForCompaction().
|
||||||
MaxReadaheadOp
|
SetupForCompactionOp
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reason indicates the higher-level context of the operation.
|
// Reason indicates the higher-level context of the operation.
|
||||||
|
|||||||
@@ -209,14 +209,14 @@ func (rh *readHandle) Close() error {
|
|||||||
return rh.rh.Close()
|
return rh.rh.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxReadahead is part of the objstorage.ReadHandle interface.
|
// SetupForCompaction is part of the objstorage.ReadHandle interface.
|
||||||
func (rh *readHandle) MaxReadahead() {
|
func (rh *readHandle) SetupForCompaction() {
|
||||||
rh.g.add(context.Background(), Event{
|
rh.g.add(context.Background(), Event{
|
||||||
Op: MaxReadaheadOp,
|
Op: SetupForCompactionOp,
|
||||||
FileNum: rh.fileNum,
|
FileNum: rh.fileNum,
|
||||||
HandleID: rh.handleID,
|
HandleID: rh.handleID,
|
||||||
})
|
})
|
||||||
rh.rh.MaxReadahead()
|
rh.rh.SetupForCompaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordCacheHit is part of the objstorage.ReadHandle interface.
|
// RecordCacheHit is part of the objstorage.ReadHandle interface.
|
||||||
|
|||||||
15
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go
generated
vendored
15
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go
generated
vendored
@@ -93,6 +93,21 @@ type Settings struct {
|
|||||||
// (experimental).
|
// (experimental).
|
||||||
Shared struct {
|
Shared struct {
|
||||||
Storage shared.Storage
|
Storage shared.Storage
|
||||||
|
|
||||||
|
// CacheSizeBytes is the size of the on-disk block cache for objects
|
||||||
|
// on shared storage. If it is 0, no cache is used.
|
||||||
|
CacheSizeBytes int64
|
||||||
|
|
||||||
|
// CacheBlockSize is the block size of the cache; if 0, the default of 32KB is used.
|
||||||
|
CacheBlockSize int
|
||||||
|
|
||||||
|
// The number of independent shards the cache leverages. Each shard is the same size,
|
||||||
|
// and a hash of filenum & offset map a read to a certain shard. If set to 0,
|
||||||
|
// 2*runtime.GOMAXPROCS is used as the shard count.
|
||||||
|
CacheShardCount int
|
||||||
|
|
||||||
|
// TODO(radu): allow the cache to live on another FS/location (e.g. to use
|
||||||
|
// instance-local SSD).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go
generated
vendored
47
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go
generated
vendored
@@ -11,15 +11,15 @@ const (
|
|||||||
minFileReadsForReadahead = 2
|
minFileReadsForReadahead = 2
|
||||||
// TODO(bilal): Have the initial size value be a factor of the block size,
|
// TODO(bilal): Have the initial size value be a factor of the block size,
|
||||||
// as opposed to a hardcoded value.
|
// as opposed to a hardcoded value.
|
||||||
initialReadaheadSize = 64 << 10 /* 64KB */
|
initialReadaheadSize = 64 << 10 /* 64KB */
|
||||||
maxReadaheadSize = 256 << 10 /* 256KB */
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// readaheadState contains state variables related to readahead. Updated on
|
// readaheadState contains state variables related to readahead. Updated on
|
||||||
// file reads.
|
// file reads.
|
||||||
type readaheadState struct {
|
type readaheadState struct {
|
||||||
// Number of sequential reads.
|
// Number of sequential reads.
|
||||||
numReads int64
|
numReads int64
|
||||||
|
maxReadaheadSize int64
|
||||||
// Size issued to the next call to Prefetch. Starts at or above
|
// Size issued to the next call to Prefetch. Starts at or above
|
||||||
// initialReadaheadSize and grows exponentially until maxReadaheadSize.
|
// initialReadaheadSize and grows exponentially until maxReadaheadSize.
|
||||||
size int64
|
size int64
|
||||||
@@ -32,22 +32,23 @@ type readaheadState struct {
|
|||||||
limit int64
|
limit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeReadaheadState() readaheadState {
|
func makeReadaheadState(maxReadaheadSize int64) readaheadState {
|
||||||
return readaheadState{
|
return readaheadState{
|
||||||
size: initialReadaheadSize,
|
size: initialReadaheadSize,
|
||||||
|
maxReadaheadSize: maxReadaheadSize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *readaheadState) recordCacheHit(offset, blockLength int64) {
|
func (rs *readaheadState) recordCacheHit(offset, blockLength int64) {
|
||||||
currentReadEnd := offset + blockLength
|
currentReadEnd := offset + blockLength
|
||||||
if rs.numReads >= minFileReadsForReadahead {
|
if rs.numReads >= minFileReadsForReadahead {
|
||||||
if currentReadEnd >= rs.limit && offset <= rs.limit+maxReadaheadSize {
|
if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
|
||||||
// This is a read that would have resulted in a readahead, had it
|
// This is a read that would have resulted in a readahead, had it
|
||||||
// not been a cache hit.
|
// not been a cache hit.
|
||||||
rs.limit = currentReadEnd
|
rs.limit = currentReadEnd
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+maxReadaheadSize {
|
if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+rs.maxReadaheadSize {
|
||||||
// We read too far away from rs.limit to benefit from readahead in
|
// We read too far away from rs.limit to benefit from readahead in
|
||||||
// any scenario. Reset all variables.
|
// any scenario. Reset all variables.
|
||||||
rs.numReads = 1
|
rs.numReads = 1
|
||||||
@@ -61,7 +62,7 @@ func (rs *readaheadState) recordCacheHit(offset, blockLength int64) {
|
|||||||
// readahead.
|
// readahead.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if currentReadEnd >= rs.limit && offset <= rs.limit+maxReadaheadSize {
|
if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
|
||||||
// Blocks are being read sequentially and would benefit from readahead
|
// Blocks are being read sequentially and would benefit from readahead
|
||||||
// down the line.
|
// down the line.
|
||||||
rs.numReads++
|
rs.numReads++
|
||||||
@@ -89,28 +90,28 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
// as well as the interval where a read would benefit from read ahead:
|
// as well as the interval where a read would benefit from read ahead:
|
||||||
// [rs.limit, rs.limit + rs.size]
|
// [rs.limit, rs.limit + rs.size]
|
||||||
// We increase the latter interval to
|
// We increase the latter interval to
|
||||||
// [rs.limit, rs.limit + maxReadaheadSize] to account for cases where
|
// [rs.limit, rs.limit + rs.maxReadaheadSize] to account for cases where
|
||||||
// readahead may not be beneficial with a small readahead size, but over
|
// readahead may not be beneficial with a small readahead size, but over
|
||||||
// time the readahead size would increase exponentially to make it
|
// time the readahead size would increase exponentially to make it
|
||||||
// beneficial.
|
// beneficial.
|
||||||
if currentReadEnd >= rs.limit && offset <= rs.limit+maxReadaheadSize {
|
if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
|
||||||
// We are doing a read in the interval ahead of
|
// We are doing a read in the interval ahead of
|
||||||
// the last readahead range. In the diagrams below, ++++ is the last
|
// the last readahead range. In the diagrams below, ++++ is the last
|
||||||
// readahead range, ==== is the range represented by
|
// readahead range, ==== is the range represented by
|
||||||
// [rs.limit, rs.limit + maxReadaheadSize], and ---- is the range
|
// [rs.limit, rs.limit + rs.maxReadaheadSize], and ---- is the range
|
||||||
// being read.
|
// being read.
|
||||||
//
|
//
|
||||||
// rs.limit rs.limit + maxReadaheadSize
|
// rs.limit rs.limit + rs.maxReadaheadSize
|
||||||
// ++++++++++|===========================|
|
// ++++++++++|===========================|
|
||||||
//
|
//
|
||||||
// |-------------|
|
// |-------------|
|
||||||
// offset currentReadEnd
|
// offset currentReadEnd
|
||||||
//
|
//
|
||||||
// This case is also possible, as are all cases with an overlap
|
// This case is also possible, as are all cases with an overlap
|
||||||
// between [rs.limit, rs.limit + maxReadaheadSize] and [offset,
|
// between [rs.limit, rs.limit + rs.maxReadaheadSize] and [offset,
|
||||||
// currentReadEnd]:
|
// currentReadEnd]:
|
||||||
//
|
//
|
||||||
// rs.limit rs.limit + maxReadaheadSize
|
// rs.limit rs.limit + rs.maxReadaheadSize
|
||||||
// ++++++++++|===========================|
|
// ++++++++++|===========================|
|
||||||
//
|
//
|
||||||
// |-------------|
|
// |-------------|
|
||||||
@@ -122,19 +123,19 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
rs.prevSize = rs.size
|
rs.prevSize = rs.size
|
||||||
// Increase rs.size for the next read.
|
// Increase rs.size for the next read.
|
||||||
rs.size *= 2
|
rs.size *= 2
|
||||||
if rs.size > maxReadaheadSize {
|
if rs.size > rs.maxReadaheadSize {
|
||||||
rs.size = maxReadaheadSize
|
rs.size = rs.maxReadaheadSize
|
||||||
}
|
}
|
||||||
return rs.prevSize
|
return rs.prevSize
|
||||||
}
|
}
|
||||||
if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+maxReadaheadSize {
|
if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+rs.maxReadaheadSize {
|
||||||
// The above conditional has rs.limit > rs.prevSize to confirm that
|
// The above conditional has rs.limit > rs.prevSize to confirm that
|
||||||
// rs.limit - rs.prevSize would not underflow.
|
// rs.limit - rs.prevSize would not underflow.
|
||||||
// We read too far away from rs.limit to benefit from readahead in
|
// We read too far away from rs.limit to benefit from readahead in
|
||||||
// any scenario. Reset all variables.
|
// any scenario. Reset all variables.
|
||||||
// The case where we read too far ahead:
|
// The case where we read too far ahead:
|
||||||
//
|
//
|
||||||
// (rs.limit - rs.prevSize) (rs.limit) (rs.limit + maxReadaheadSize)
|
// (rs.limit - rs.prevSize) (rs.limit) (rs.limit + rs.maxReadaheadSize)
|
||||||
// |+++++++++++++|=============|
|
// |+++++++++++++|=============|
|
||||||
//
|
//
|
||||||
// |-------------|
|
// |-------------|
|
||||||
@@ -142,7 +143,7 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
//
|
//
|
||||||
// Or too far behind:
|
// Or too far behind:
|
||||||
//
|
//
|
||||||
// (rs.limit - rs.prevSize) (rs.limit) (rs.limit + maxReadaheadSize)
|
// (rs.limit - rs.prevSize) (rs.limit) (rs.limit + rs.maxReadaheadSize)
|
||||||
// |+++++++++++++|=============|
|
// |+++++++++++++|=============|
|
||||||
//
|
//
|
||||||
// |-------------|
|
// |-------------|
|
||||||
@@ -160,7 +161,7 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
// readahead, but there's no reason to issue a readahead call at the
|
// readahead, but there's no reason to issue a readahead call at the
|
||||||
// moment.
|
// moment.
|
||||||
//
|
//
|
||||||
// (rs.limit - rs.prevSize) (rs.limit + maxReadaheadSize)
|
// (rs.limit - rs.prevSize) (rs.limit + rs.maxReadaheadSize)
|
||||||
// |+++++++++++++|===============|
|
// |+++++++++++++|===============|
|
||||||
// (rs.limit)
|
// (rs.limit)
|
||||||
//
|
//
|
||||||
@@ -170,11 +171,11 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
rs.numReads++
|
rs.numReads++
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if currentReadEnd >= rs.limit && offset <= rs.limit+maxReadaheadSize {
|
if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
|
||||||
// Blocks are being read sequentially and would benefit from readahead
|
// Blocks are being read sequentially and would benefit from readahead
|
||||||
// down the line.
|
// down the line.
|
||||||
//
|
//
|
||||||
// (rs.limit) (rs.limit + maxReadaheadSize)
|
// (rs.limit) (rs.limit + rs.maxReadaheadSize)
|
||||||
// |=============|
|
// |=============|
|
||||||
//
|
//
|
||||||
// |-------|
|
// |-------|
|
||||||
@@ -186,7 +187,7 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
|
|||||||
// We read too far ahead of the last read, or before it. This indicates
|
// We read too far ahead of the last read, or before it. This indicates
|
||||||
// a random read, where readahead is not desirable. Reset all variables.
|
// a random read, where readahead is not desirable. Reset all variables.
|
||||||
//
|
//
|
||||||
// (rs.limit - maxReadaheadSize) (rs.limit) (rs.limit + maxReadaheadSize)
|
// (rs.limit - rs.maxReadaheadSize) (rs.limit) (rs.limit + rs.maxReadaheadSize)
|
||||||
// |+++++++++++++|=============|
|
// |+++++++++++++|=============|
|
||||||
//
|
//
|
||||||
// |-------|
|
// |-------|
|
||||||
|
|||||||
31
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go
generated
vendored
31
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go
generated
vendored
@@ -6,6 +6,7 @@ package objstorageprovider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/cockroachdb/pebble/internal/base"
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
"github.com/cockroachdb/pebble/internal/invariants"
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||||||
"github.com/cockroachdb/pebble/objstorage"
|
"github.com/cockroachdb/pebble/objstorage"
|
||||||
|
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache"
|
||||||
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedobjcat"
|
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedobjcat"
|
||||||
"github.com/cockroachdb/pebble/objstorage/shared"
|
"github.com/cockroachdb/pebble/objstorage/shared"
|
||||||
)
|
)
|
||||||
@@ -21,6 +23,7 @@ import (
|
|||||||
// All fields remain unset if shared storage is not configured.
|
// All fields remain unset if shared storage is not configured.
|
||||||
type sharedSubsystem struct {
|
type sharedSubsystem struct {
|
||||||
catalog *sharedobjcat.Catalog
|
catalog *sharedobjcat.Catalog
|
||||||
|
cache *sharedcache.Cache
|
||||||
|
|
||||||
// checkRefsOnOpen controls whether we check the ref marker file when opening
|
// checkRefsOnOpen controls whether we check the ref marker file when opening
|
||||||
// an object. Normally this is true when invariants are enabled (but the provider
|
// an object. Normally this is true when invariants are enabled (but the provider
|
||||||
@@ -65,6 +68,24 @@ func (p *provider) sharedInit() error {
|
|||||||
p.st.Logger.Infof("shared storage configured; no creatorID yet")
|
p.st.Logger.Infof("shared storage configured; no creatorID yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.st.Shared.CacheSizeBytes > 0 {
|
||||||
|
const defaultBlockSize = 32 * 1024
|
||||||
|
blockSize := p.st.Shared.CacheBlockSize
|
||||||
|
if blockSize == 0 {
|
||||||
|
blockSize = defaultBlockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
numShards := p.st.Shared.CacheShardCount
|
||||||
|
if numShards == 0 {
|
||||||
|
numShards = 2 * runtime.GOMAXPROCS(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.shared.cache, err = sharedcache.Open(p.st.FS, p.st.Logger, p.st.FSDirName, blockSize, p.st.Shared.CacheSizeBytes, numShards)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "pebble: could not open shared object cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, meta := range contents.Objects {
|
for _, meta := range contents.Objects {
|
||||||
o := objstorage.ObjectMetadata{
|
o := objstorage.ObjectMetadata{
|
||||||
DiskFileNum: meta.FileNum,
|
DiskFileNum: meta.FileNum,
|
||||||
@@ -95,6 +116,14 @@ func (p *provider) SetCreatorID(creatorID objstorage.CreatorID) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsForeign is part of the objstorage.Provider interface.
|
||||||
|
func (p *provider) IsForeign(meta objstorage.ObjectMetadata) bool {
|
||||||
|
if !p.shared.initialized.Load() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return meta.IsShared() && p.shared.creatorID != meta.Shared.CreatorID
|
||||||
|
}
|
||||||
|
|
||||||
func (p *provider) sharedCheckInitialized() error {
|
func (p *provider) sharedCheckInitialized() error {
|
||||||
if p.sharedStorage() == nil {
|
if p.sharedStorage() == nil {
|
||||||
return errors.Errorf("shared object support not configured")
|
return errors.Errorf("shared object support not configured")
|
||||||
@@ -211,7 +240,7 @@ func (p *provider) sharedOpenForReading(
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newSharedReadable(reader, size), nil
|
return p.newSharedReadable(reader, size, meta.DiskFileNum), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *provider) sharedSize(meta objstorage.ObjectMetadata) (int64, error) {
|
func (p *provider) sharedSize(meta objstorage.ObjectMetadata) (int64, error) {
|
||||||
|
|||||||
109
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go
generated
vendored
109
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go
generated
vendored
@@ -6,28 +6,55 @@ package objstorageprovider
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
"github.com/cockroachdb/pebble/objstorage"
|
"github.com/cockroachdb/pebble/objstorage"
|
||||||
|
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache"
|
||||||
"github.com/cockroachdb/pebble/objstorage/shared"
|
"github.com/cockroachdb/pebble/objstorage/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sharedMaxReadaheadSize = 1024 * 1024 /* 1MB */
|
||||||
|
|
||||||
// sharedReadable is a very simple implementation of Readable on top of the
|
// sharedReadable is a very simple implementation of Readable on top of the
|
||||||
// ReadCloser returned by shared.Storage.CreateObject.
|
// ReadCloser returned by shared.Storage.CreateObject.
|
||||||
type sharedReadable struct {
|
type sharedReadable struct {
|
||||||
objReader shared.ObjectReader
|
objReader shared.ObjectReader
|
||||||
size int64
|
size int64
|
||||||
|
fileNum base.DiskFileNum
|
||||||
|
provider *provider
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ objstorage.Readable = (*sharedReadable)(nil)
|
var _ objstorage.Readable = (*sharedReadable)(nil)
|
||||||
|
|
||||||
func newSharedReadable(objReader shared.ObjectReader, size int64) *sharedReadable {
|
func (p *provider) newSharedReadable(
|
||||||
|
objReader shared.ObjectReader, size int64, fileNum base.DiskFileNum,
|
||||||
|
) *sharedReadable {
|
||||||
return &sharedReadable{
|
return &sharedReadable{
|
||||||
objReader: objReader,
|
objReader: objReader,
|
||||||
size: size,
|
size: size,
|
||||||
|
fileNum: fileNum,
|
||||||
|
provider: p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadAt is part of the objstorage.Readable interface.
|
||||||
func (r *sharedReadable) ReadAt(ctx context.Context, p []byte, offset int64) error {
|
func (r *sharedReadable) ReadAt(ctx context.Context, p []byte, offset int64) error {
|
||||||
|
return r.readInternal(ctx, p, offset, false /* forCompaction */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readInternal performs a read for the object, using the cache when
|
||||||
|
// appropriate.
|
||||||
|
func (r *sharedReadable) readInternal(
|
||||||
|
ctx context.Context, p []byte, offset int64, forCompaction bool,
|
||||||
|
) error {
|
||||||
|
if cache := r.provider.shared.cache; cache != nil {
|
||||||
|
flags := sharedcache.ReadFlags{
|
||||||
|
// Don't add data to the cache if this read is for a compaction.
|
||||||
|
ReadOnly: forCompaction,
|
||||||
|
}
|
||||||
|
return r.provider.shared.cache.ReadAt(ctx, r.fileNum, p, offset, r.objReader, r.size, flags)
|
||||||
|
}
|
||||||
return r.objReader.ReadAt(ctx, p, offset)
|
return r.objReader.ReadAt(ctx, p, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,24 +69,94 @@ func (r *sharedReadable) Size() int64 {
|
|||||||
|
|
||||||
func (r *sharedReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle {
|
func (r *sharedReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle {
|
||||||
// TODO(radu): use a pool.
|
// TODO(radu): use a pool.
|
||||||
return &sharedReadHandle{readable: r}
|
rh := &sharedReadHandle{readable: r}
|
||||||
|
rh.readahead.state = makeReadaheadState(sharedMaxReadaheadSize)
|
||||||
|
return rh
|
||||||
}
|
}
|
||||||
|
|
||||||
type sharedReadHandle struct {
|
type sharedReadHandle struct {
|
||||||
readable *sharedReadable
|
readable *sharedReadable
|
||||||
|
readahead struct {
|
||||||
|
state readaheadState
|
||||||
|
data []byte
|
||||||
|
offset int64
|
||||||
|
}
|
||||||
|
forCompaction bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ objstorage.ReadHandle = (*sharedReadHandle)(nil)
|
var _ objstorage.ReadHandle = (*sharedReadHandle)(nil)
|
||||||
|
|
||||||
|
// ReadAt is part of the objstorage.ReadHandle interface.
|
||||||
func (r *sharedReadHandle) ReadAt(ctx context.Context, p []byte, offset int64) error {
|
func (r *sharedReadHandle) ReadAt(ctx context.Context, p []byte, offset int64) error {
|
||||||
return r.readable.ReadAt(ctx, p, offset)
|
readaheadSize := r.maybeReadahead(offset, len(p))
|
||||||
|
|
||||||
|
// Check if we already have the data from a previous read-ahead.
|
||||||
|
if rhSize := int64(len(r.readahead.data)); rhSize > 0 {
|
||||||
|
if r.readahead.offset <= offset && r.readahead.offset+rhSize > offset {
|
||||||
|
n := copy(p, r.readahead.data[offset-r.readahead.offset:])
|
||||||
|
if n == len(p) {
|
||||||
|
// All data was available.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Use the data that we had and do a shorter read.
|
||||||
|
offset += int64(n)
|
||||||
|
p = p[n:]
|
||||||
|
readaheadSize -= n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if readaheadSize > len(p) {
|
||||||
|
// Don't try to read past EOF.
|
||||||
|
if offset+int64(readaheadSize) > r.readable.size {
|
||||||
|
readaheadSize = int(r.readable.size - offset)
|
||||||
|
if readaheadSize <= 0 {
|
||||||
|
// This shouldn't happen in practice (Pebble should never try to read
|
||||||
|
// past EOF).
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.readahead.offset = offset
|
||||||
|
// TODO(radu): we need to somehow account for this memory.
|
||||||
|
if cap(r.readahead.data) >= readaheadSize {
|
||||||
|
r.readahead.data = r.readahead.data[:readaheadSize]
|
||||||
|
} else {
|
||||||
|
r.readahead.data = make([]byte, readaheadSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.readable.readInternal(ctx, r.readahead.data, offset, r.forCompaction); err != nil {
|
||||||
|
// Make sure we don't treat the data as valid next time.
|
||||||
|
r.readahead.data = r.readahead.data[:0]
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(p, r.readahead.data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.readable.readInternal(ctx, p, offset, r.forCompaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *sharedReadHandle) maybeReadahead(offset int64, len int) int {
|
||||||
|
if r.forCompaction {
|
||||||
|
return sharedMaxReadaheadSize
|
||||||
|
}
|
||||||
|
return int(r.readahead.state.maybeReadahead(offset, int64(len)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is part of the objstorage.ReadHandle interface.
|
||||||
func (r *sharedReadHandle) Close() error {
|
func (r *sharedReadHandle) Close() error {
|
||||||
r.readable = nil
|
r.readable = nil
|
||||||
|
r.readahead.data = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sharedReadHandle) MaxReadahead() {}
|
// SetupForCompaction is part of the objstorage.ReadHandle interface.
|
||||||
|
func (r *sharedReadHandle) SetupForCompaction() {
|
||||||
|
r.forCompaction = true
|
||||||
|
}
|
||||||
|
|
||||||
func (r *sharedReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {}
|
// RecordCacheHit is part of the objstorage.ReadHandle interface.
|
||||||
|
func (r *sharedReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {
|
||||||
|
if !r.forCompaction {
|
||||||
|
r.readahead.state.recordCacheHit(offset, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
570
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go
generated
vendored
Normal file
570
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go
generated
vendored
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
// Copyright 2023 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 sharedcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/cockroachdb/errors"
|
||||||
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||||||
|
"github.com/cockroachdb/pebble/objstorage/shared"
|
||||||
|
"github.com/cockroachdb/pebble/vfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
shardingBlockSize = 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cache is a persistent cache backed by a local filesystem. It is intended
|
||||||
|
// to cache data that is in slower shared storage (e.g. S3), hence the
|
||||||
|
// package name 'sharedcache'.
|
||||||
|
type Cache struct {
|
||||||
|
shards []shard
|
||||||
|
logger base.Logger
|
||||||
|
|
||||||
|
blockSize int
|
||||||
|
|
||||||
|
writeBackWaitGroup sync.WaitGroup
|
||||||
|
// TODO(josh): Have a dedicated metrics struct. Right now, this
|
||||||
|
// is just for testing.
|
||||||
|
misses atomic.Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens a cache. If there is no existing cache at fsDir, a new one
|
||||||
|
// is created.
|
||||||
|
func Open(
|
||||||
|
fs vfs.FS, logger base.Logger, fsDir string, blockSize int, sizeBytes int64, numShards int,
|
||||||
|
) (*Cache, error) {
|
||||||
|
min := shardingBlockSize * int64(numShards)
|
||||||
|
if sizeBytes < min {
|
||||||
|
return nil, errors.Errorf("cache size %d lower than min %d", sizeBytes, min)
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := &Cache{
|
||||||
|
logger: logger,
|
||||||
|
blockSize: blockSize,
|
||||||
|
}
|
||||||
|
sc.shards = make([]shard, numShards)
|
||||||
|
blocksPerShard := sizeBytes / int64(numShards) / int64(blockSize)
|
||||||
|
for i := range sc.shards {
|
||||||
|
if err := sc.shards[i].init(fs, fsDir, i, blocksPerShard, blockSize); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the cache. Methods such as ReadAt should not be called after Close is
|
||||||
|
// called.
|
||||||
|
func (c *Cache) Close() error {
|
||||||
|
c.writeBackWaitGroup.Wait()
|
||||||
|
|
||||||
|
var retErr error
|
||||||
|
for i := range c.shards {
|
||||||
|
if err := c.shards[i].close(); err != nil && retErr == nil {
|
||||||
|
retErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.shards = nil
|
||||||
|
return retErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFlags contains options for Cache.ReadAt.
|
||||||
|
type ReadFlags struct {
|
||||||
|
// ReadOnly instructs ReadAt to not write any new data into the cache; it is
|
||||||
|
// used when the data is unlikely to be used again.
|
||||||
|
ReadOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAt performs a read form an object, attempting to use cached data when
|
||||||
|
// possible.
|
||||||
|
func (c *Cache) ReadAt(
|
||||||
|
ctx context.Context,
|
||||||
|
fileNum base.DiskFileNum,
|
||||||
|
p []byte,
|
||||||
|
ofs int64,
|
||||||
|
objReader shared.ObjectReader,
|
||||||
|
objSize int64,
|
||||||
|
flags ReadFlags,
|
||||||
|
) error {
|
||||||
|
if ofs >= objSize {
|
||||||
|
if invariants.Enabled {
|
||||||
|
panic("invalid ReadAt offset")
|
||||||
|
}
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
// TODO(radu): for compaction reads, we may not want to read from the cache at
|
||||||
|
// all.
|
||||||
|
{
|
||||||
|
n, err := c.get(fileNum, p, ofs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == len(p) {
|
||||||
|
// Everything was in cache!
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this. The below code does not need the original ofs, as with the earlier
|
||||||
|
// reading from the cache done, the relevant offset is ofs + int64(n). Same with p.
|
||||||
|
ofs += int64(n)
|
||||||
|
p = p[n:]
|
||||||
|
|
||||||
|
if invariants.Enabled {
|
||||||
|
if n != 0 && ofs%int64(c.blockSize) != 0 {
|
||||||
|
panic(fmt.Sprintf("after non-zero read from cache, ofs is not block-aligned: %v %v", ofs, n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.misses.Add(1)
|
||||||
|
|
||||||
|
if flags.ReadOnly {
|
||||||
|
return objReader.ReadAt(ctx, p, ofs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must do reads with offset & size that are multiples of the block size. Else
|
||||||
|
// later cache hits may return incorrect zeroed results from the cache.
|
||||||
|
firstBlockInd := ofs / int64(c.blockSize)
|
||||||
|
adjustedOfs := firstBlockInd * int64(c.blockSize)
|
||||||
|
|
||||||
|
// Take the length of what is left to read plus the length of the adjustment of
|
||||||
|
// the offset plus the size of a block minus one and divide by the size of a block
|
||||||
|
// to get the number of blocks to read from the object.
|
||||||
|
sizeOfOffAdjustment := int(ofs - adjustedOfs)
|
||||||
|
numBlocksToRead := ((len(p) + sizeOfOffAdjustment) + (c.blockSize - 1)) / c.blockSize
|
||||||
|
adjustedLen := numBlocksToRead * c.blockSize
|
||||||
|
adjustedP := make([]byte, adjustedLen)
|
||||||
|
|
||||||
|
// Read the rest from the object. We may need to cap the length to avoid past EOF reads.
|
||||||
|
eofCap := int64(adjustedLen)
|
||||||
|
if adjustedOfs+eofCap > objSize {
|
||||||
|
eofCap = objSize - adjustedOfs
|
||||||
|
}
|
||||||
|
if err := objReader.ReadAt(ctx, adjustedP[:eofCap], adjustedOfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(p, adjustedP[sizeOfOffAdjustment:])
|
||||||
|
|
||||||
|
// TODO(josh): For writing back to the cache, we may want a concurrency-limited approach
|
||||||
|
// that does batching, instead of what is below. I think it's reasonable though to
|
||||||
|
// run a production experiment with what is below, before making adjustments. Note that
|
||||||
|
// the writes being done are in multiples of the filesystem block size. The filesystem
|
||||||
|
// might do okay with them.
|
||||||
|
c.writeBackWaitGroup.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer c.writeBackWaitGroup.Done()
|
||||||
|
if err := c.set(fileNum, adjustedP, adjustedOfs); err != nil {
|
||||||
|
// TODO(josh): Would like to log at error severity, but base.Logger doesn't
|
||||||
|
// have error severity.
|
||||||
|
c.logger.Infof("writing back to cache after miss failed: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get attempts to read the requested data from the cache, if it is already
|
||||||
|
// there.
|
||||||
|
//
|
||||||
|
// If all data is available, returns n = len(p).
|
||||||
|
//
|
||||||
|
// If data is partially available, a prefix of the data is read; returns n < len(p)
|
||||||
|
// and no error. If no prefix is available, returns n = 0 and no error.
|
||||||
|
func (c *Cache) get(fileNum base.DiskFileNum, p []byte, ofs int64) (n int, _ error) {
|
||||||
|
// The data extent might cross shard boundaries, hence the loop. In the hot
|
||||||
|
// path, max two iterations of this loop will be executed, since reads are sized
|
||||||
|
// in units of sstable block size.
|
||||||
|
for {
|
||||||
|
shard := c.getShard(fileNum, ofs+int64(n))
|
||||||
|
cappedLen := len(p[n:])
|
||||||
|
if toBoundary := int(shardingBlockSize - ((ofs + int64(n)) % shardingBlockSize)); cappedLen > toBoundary {
|
||||||
|
cappedLen = toBoundary
|
||||||
|
}
|
||||||
|
numRead, err := shard.get(fileNum, p[n:n+cappedLen], ofs+int64(n))
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
n += numRead
|
||||||
|
if numRead < cappedLen {
|
||||||
|
// We only read a prefix from this shard.
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if n == len(p) {
|
||||||
|
// We are done.
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
// Data extent crosses shard boundary, continue with next shard.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set attempts to write the requested data to the cache. Both ofs & len(p) must
|
||||||
|
// be multiples of the block size.
|
||||||
|
//
|
||||||
|
// If all of p is not written to the shard, set returns a non-nil error.
|
||||||
|
func (c *Cache) set(fileNum base.DiskFileNum, p []byte, ofs int64) error {
|
||||||
|
if invariants.Enabled {
|
||||||
|
if ofs%int64(c.blockSize) != 0 || len(p)%c.blockSize != 0 {
|
||||||
|
panic(fmt.Sprintf("set with ofs & len not multiples of block size: %v %v", ofs, len(p)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data extent might cross shard boundaries, hence the loop. In the hot
|
||||||
|
// path, max two iterations of this loop will be executed, since reads are sized
|
||||||
|
// in units of sstable block size.
|
||||||
|
n := 0
|
||||||
|
for {
|
||||||
|
shard := c.getShard(fileNum, ofs+int64(n))
|
||||||
|
cappedLen := len(p[n:])
|
||||||
|
if toBoundary := int(shardingBlockSize - ((ofs + int64(n)) % shardingBlockSize)); cappedLen > toBoundary {
|
||||||
|
cappedLen = toBoundary
|
||||||
|
}
|
||||||
|
err := shard.set(fileNum, p[n:n+cappedLen], ofs+int64(n))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// set returns an error if cappedLen bytes aren't written to the shard.
|
||||||
|
n += cappedLen
|
||||||
|
if n == len(p) {
|
||||||
|
// We are done.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Data extent crosses shard boundary, continue with next shard.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) getShard(fileNum base.DiskFileNum, ofs int64) *shard {
|
||||||
|
const prime64 = 1099511628211
|
||||||
|
hash := uint64(fileNum.FileNum())*prime64 + uint64(ofs)/shardingBlockSize
|
||||||
|
// TODO(josh): Instance change ops are often run in production. Such an operation
|
||||||
|
// updates len(c.shards); see openSharedCache. As a result, the behavior of this
|
||||||
|
// function changes, and the cache empties out at restart time. We may want a better
|
||||||
|
// story here eventually.
|
||||||
|
return &c.shards[hash%uint64(len(c.shards))]
|
||||||
|
}
|
||||||
|
|
||||||
|
type shard struct {
|
||||||
|
file vfs.File
|
||||||
|
sizeInBlocks int64
|
||||||
|
blockSize int
|
||||||
|
mu struct {
|
||||||
|
sync.Mutex
|
||||||
|
// TODO(josh): None of these datastructures are space-efficient.
|
||||||
|
// Focusing on correctness to start.
|
||||||
|
where whereMap
|
||||||
|
locks []lockState
|
||||||
|
free []cacheBlockIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a logical block in an SST to an index of the cache block with the
|
||||||
|
// file contents (to the "cache block index").
|
||||||
|
type whereMap map[logicalBlockID]cacheBlockIndex
|
||||||
|
|
||||||
|
type logicalBlockID struct {
|
||||||
|
filenum base.DiskFileNum
|
||||||
|
// Offset into the file, in units of shard.BlockSize.
|
||||||
|
offsetInUnitsOfBlocks int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheBlockIndex int64
|
||||||
|
|
||||||
|
type lockState int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
unlocked lockState = 0
|
||||||
|
// >0 lockState tracks the number of distinct readers of some cache block / logical block
|
||||||
|
// which is in the secondary cache. It is used to ensure that a cache block is not evicted
|
||||||
|
// and overwritten, while there are active readers.
|
||||||
|
readLockTakenInc = 1
|
||||||
|
// -1 lockState indicates that some cache block is currently being populated with data from
|
||||||
|
// blob storage. It is used to ensure that a cache block is not read or evicted again, while
|
||||||
|
// it is being populated.
|
||||||
|
writeLockTaken = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *shard) init(
|
||||||
|
fs vfs.FS, fsDir string, shardIdx int, sizeInBlocks int64, blockSize int,
|
||||||
|
) error {
|
||||||
|
*s = shard{
|
||||||
|
sizeInBlocks: sizeInBlocks,
|
||||||
|
}
|
||||||
|
if blockSize < 1024 || shardingBlockSize%blockSize != 0 {
|
||||||
|
return errors.Newf("invalid block size %d (must divide %d)", blockSize, shardingBlockSize)
|
||||||
|
}
|
||||||
|
s.blockSize = blockSize
|
||||||
|
file, err := fs.OpenReadWrite(fs.PathJoin(fsDir, fmt.Sprintf("SHARED-CACHE-%03d", shardIdx)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO(radu): truncate file if necessary (especially important if we restart
|
||||||
|
// with more shards).
|
||||||
|
if err := file.Preallocate(0, int64(blockSize)*sizeInBlocks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.file = file
|
||||||
|
|
||||||
|
// TODO(josh): Right now, the secondary cache is not persistent. All existing
|
||||||
|
// cache contents will be over-written, since all metadata is only stored in
|
||||||
|
// memory.
|
||||||
|
s.mu.where = make(whereMap)
|
||||||
|
for i := int64(0); i < sizeInBlocks; i++ {
|
||||||
|
s.mu.locks = append(s.mu.locks, unlocked)
|
||||||
|
s.mu.free = append(s.mu.free, cacheBlockIndex(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shard) close() error {
|
||||||
|
defer func() {
|
||||||
|
s.file = nil
|
||||||
|
}()
|
||||||
|
return s.file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get attempts to read the requested data from the shard. The data must not
|
||||||
|
// cross a shard boundary.
|
||||||
|
//
|
||||||
|
// If all data is available, returns n = len(p).
|
||||||
|
//
|
||||||
|
// If data is partially available, a prefix of the data is read; returns n < len(p)
|
||||||
|
// and no error. If no prefix is available, returns n = 0 and no error.
|
||||||
|
//
|
||||||
|
// TODO(josh): Today, if there are two cache blocks needed to satisfy a read, and the
|
||||||
|
// first block is not in the cache and the second one is, we will read both from
|
||||||
|
// blob storage. We should fix this. This is not an unlikely scenario if we are doing
|
||||||
|
// a reverse scan, since those iterate over sstable blocks in reverse order and due to
|
||||||
|
// cache block aligned reads will have read the suffix of the sstable block that will
|
||||||
|
// be needed next.
|
||||||
|
func (s *shard) get(fileNum base.DiskFileNum, p []byte, ofs int64) (n int, _ error) {
|
||||||
|
if invariants.Enabled {
|
||||||
|
if ofs/shardingBlockSize != (ofs+int64(len(p))-1)/shardingBlockSize {
|
||||||
|
panic(fmt.Sprintf("get crosses shard boundary: %v %v", ofs, len(p)))
|
||||||
|
}
|
||||||
|
s.assertShardStateIsConsistent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data extent might cross cache block boundaries, hence the loop. In the hot
|
||||||
|
// path, max two iterations of this loop will be executed, since reads are sized
|
||||||
|
// in units of sstable block size.
|
||||||
|
for {
|
||||||
|
k := logicalBlockID{
|
||||||
|
filenum: fileNum,
|
||||||
|
offsetInUnitsOfBlocks: (ofs + int64(n)) / int64(s.blockSize),
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
cacheBlockInd, ok := s.mu.where[k]
|
||||||
|
// TODO(josh): Multiple reads within the same few milliseconds (anything that is smaller
|
||||||
|
// than blob storage read latency) that miss on the same logical block ID will not necessarily
|
||||||
|
// be rare. We may want to do only one read, with the later readers blocking on the first read
|
||||||
|
// completing. This could be implemented either here or in the primary block cache. See
|
||||||
|
// https://github.com/cockroachdb/pebble/pull/2586 for additional discussion.
|
||||||
|
if !ok {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if s.mu.locks[cacheBlockInd] == writeLockTaken {
|
||||||
|
// In practice, if we have two reads of the same SST block in close succession, we
|
||||||
|
// would expect the second to hit in the in-memory block cache. So it's not worth
|
||||||
|
// optimizing this case here.
|
||||||
|
s.mu.Unlock()
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
s.mu.locks[cacheBlockInd] += readLockTakenInc
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
readAt := int64(cacheBlockInd) * int64(s.blockSize)
|
||||||
|
if n == 0 { // if first read
|
||||||
|
readAt += ofs % int64(s.blockSize)
|
||||||
|
}
|
||||||
|
readSize := s.blockSize
|
||||||
|
if n == 0 { // if first read
|
||||||
|
// Cast to int safe since ofs is modded by block size.
|
||||||
|
readSize -= int(ofs % int64(s.blockSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p[n:]) <= readSize {
|
||||||
|
numRead, err := s.file.ReadAt(p[n:], readAt)
|
||||||
|
s.dropReadLock(cacheBlockInd)
|
||||||
|
return n + numRead, err
|
||||||
|
}
|
||||||
|
numRead, err := s.file.ReadAt(p[n:n+readSize], readAt)
|
||||||
|
s.dropReadLock(cacheBlockInd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that numRead == readSize, since we checked for an error above.
|
||||||
|
n += numRead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set attempts to write the requested data to the shard. The data must not
|
||||||
|
// cross a shard boundary, and both ofs & len(p) must be multiples of the
|
||||||
|
// block size.
|
||||||
|
//
|
||||||
|
// If all of p is not written to the shard, set returns a non-nil error.
|
||||||
|
func (s *shard) set(fileNum base.DiskFileNum, p []byte, ofs int64) error {
|
||||||
|
if invariants.Enabled {
|
||||||
|
if ofs/shardingBlockSize != (ofs+int64(len(p))-1)/shardingBlockSize {
|
||||||
|
panic(fmt.Sprintf("set crosses shard boundary: %v %v", ofs, len(p)))
|
||||||
|
}
|
||||||
|
if ofs%int64(s.blockSize) != 0 || len(p)%s.blockSize != 0 {
|
||||||
|
panic(fmt.Sprintf("set with ofs & len not multiples of block size: %v %v", ofs, len(p)))
|
||||||
|
}
|
||||||
|
s.assertShardStateIsConsistent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data extent might cross cache block boundaries, hence the loop. In the hot
|
||||||
|
// path, max two iterations of this loop will be executed, since reads are sized
|
||||||
|
// in units of sstable block size.
|
||||||
|
n := 0
|
||||||
|
for {
|
||||||
|
if n == len(p) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if invariants.Enabled {
|
||||||
|
if n > len(p) {
|
||||||
|
panic(fmt.Sprintf("set with n greater than len(p): %v %v", n, len(p)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the logical block is already in the cache, we should skip doing a set.
|
||||||
|
k := logicalBlockID{
|
||||||
|
filenum: fileNum,
|
||||||
|
offsetInUnitsOfBlocks: (ofs + int64(n)) / int64(s.blockSize),
|
||||||
|
}
|
||||||
|
s.mu.Lock()
|
||||||
|
if _, ok := s.mu.where[k]; ok {
|
||||||
|
s.mu.Unlock()
|
||||||
|
n += s.blockSize
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine cache block index by looking at the free list and randomly evicting if
|
||||||
|
// it is empty.
|
||||||
|
var cacheBlockInd cacheBlockIndex
|
||||||
|
if len(s.mu.free) == 0 {
|
||||||
|
// TODO(josh): Right now, we do random eviction. Eventually, we will do something
|
||||||
|
// more sophisticated, e.g. leverage ClockPro.
|
||||||
|
foundBlockToEvict := false
|
||||||
|
var k logicalBlockID
|
||||||
|
for k1, v := range s.mu.where {
|
||||||
|
// This implies that hot blocks will not be evicted as often, meaning the
|
||||||
|
// eviction scheme is not quite random. I think that's fine for now, since we plan
|
||||||
|
// to leverage a more sophisticated eviction scheme eventually anyway.
|
||||||
|
lock := s.mu.locks[v]
|
||||||
|
if lock >= readLockTakenInc || lock == writeLockTaken {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foundBlockToEvict = true
|
||||||
|
cacheBlockInd = v
|
||||||
|
k = k1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// TODO(josh): We may want to block until a block frees up, instead of returning
|
||||||
|
// an error here. But I think we can do that later on, e.g. after running some production
|
||||||
|
// experiments.
|
||||||
|
if !foundBlockToEvict {
|
||||||
|
s.mu.Unlock()
|
||||||
|
return errors.New("no block to evict so skipping write to cache")
|
||||||
|
}
|
||||||
|
delete(s.mu.where, k)
|
||||||
|
} else {
|
||||||
|
cacheBlockInd = s.mu.free[len(s.mu.free)-1]
|
||||||
|
s.mu.free = s.mu.free[:len(s.mu.free)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.where[k] = cacheBlockInd
|
||||||
|
s.mu.locks[cacheBlockInd] = writeLockTaken
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
writeAt := int64(cacheBlockInd) * int64(s.blockSize)
|
||||||
|
|
||||||
|
writeSize := s.blockSize
|
||||||
|
if len(p[n:]) <= writeSize {
|
||||||
|
writeSize = len(p[n:])
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := s.file.WriteAt(p[n:n+writeSize], writeAt)
|
||||||
|
if err != nil {
|
||||||
|
s.freeBlock(k, cacheBlockInd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.dropWriteLock(cacheBlockInd)
|
||||||
|
|
||||||
|
n += writeSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't inline currently. This might be okay, but something to keep in mind.
|
||||||
|
func (s *shard) dropReadLock(cacheBlockInd cacheBlockIndex) {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.mu.locks[cacheBlockInd] -= readLockTakenInc
|
||||||
|
if invariants.Enabled {
|
||||||
|
if s.mu.locks[cacheBlockInd] < 0 {
|
||||||
|
panic(fmt.Sprintf("unexpected lock state %v in dropReadLock: %v", s.mu.locks[cacheBlockInd], s.mu.locks))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't inline currently. This might be okay, but something to keep in mind.
|
||||||
|
func (s *shard) dropWriteLock(cacheBlockInd cacheBlockIndex) {
|
||||||
|
s.mu.Lock()
|
||||||
|
if invariants.Enabled {
|
||||||
|
if s.mu.locks[cacheBlockInd] != writeLockTaken {
|
||||||
|
panic(fmt.Sprintf("unexpected lock state %v in dropWriteLock: %v", s.mu.locks[cacheBlockInd], s.mu.locks))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mu.locks[cacheBlockInd] = unlocked
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't inline currently. This might be okay, but something to keep in mind.
|
||||||
|
func (s *shard) freeBlock(k logicalBlockID, cacheBlockInd cacheBlockIndex) {
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.mu.where, k)
|
||||||
|
s.mu.locks[cacheBlockInd] = unlocked
|
||||||
|
s.mu.free = append(s.mu.free, cacheBlockInd)
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shard) assertShardStateIsConsistent() {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
cacheBlockInds := make(map[cacheBlockIndex]bool)
|
||||||
|
for _, v := range s.mu.where {
|
||||||
|
if cacheBlockInds[v] {
|
||||||
|
panic(fmt.Sprintf("repeated cache block index (where): %v %v %v %v", v, cacheBlockInds, s.mu.where, s.mu.free))
|
||||||
|
}
|
||||||
|
cacheBlockInds[v] = true
|
||||||
|
}
|
||||||
|
for _, v := range s.mu.free {
|
||||||
|
if cacheBlockInds[v] {
|
||||||
|
panic(fmt.Sprintf("repeated cache block index (free): %v %v %v %v", v, cacheBlockInds, s.mu.where, s.mu.free))
|
||||||
|
}
|
||||||
|
cacheBlockInds[v] = true
|
||||||
|
}
|
||||||
|
for i := int64(0); i < s.sizeInBlocks; i++ {
|
||||||
|
if !cacheBlockInds[cacheBlockIndex(i)] {
|
||||||
|
panic(fmt.Sprintf("missing cache block index: %v %v %v %v", i, cacheBlockInds, s.mu.where, s.mu.free))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if int64(len(s.mu.locks)) != s.sizeInBlocks {
|
||||||
|
panic(fmt.Sprintf("lock table isn't correct size: %v %v", len(s.mu.locks), s.sizeInBlocks))
|
||||||
|
}
|
||||||
|
for _, ls := range s.mu.locks {
|
||||||
|
if ls < writeLockTaken {
|
||||||
|
panic(fmt.Sprintf("lock state %v is not allowed: %v", ls, s.mu.locks))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go
generated
vendored
16
vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go
generated
vendored
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/cockroachdb/pebble/vfs"
|
"github.com/cockroachdb/pebble/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fileMaxReadaheadSize = 256 * 1024 /* 256KB */
|
||||||
|
|
||||||
// fileReadable implements objstorage.Readable on top of a vfs.File.
|
// fileReadable implements objstorage.Readable on top of a vfs.File.
|
||||||
//
|
//
|
||||||
// The implementation might use Prealloc and might reopen the file with
|
// The implementation might use Prealloc and might reopen the file with
|
||||||
@@ -75,7 +77,7 @@ func (r *fileReadable) Size() int64 {
|
|||||||
func (r *fileReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle {
|
func (r *fileReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle {
|
||||||
rh := readHandlePool.Get().(*vfsReadHandle)
|
rh := readHandlePool.Get().(*vfsReadHandle)
|
||||||
rh.r = r
|
rh.r = r
|
||||||
rh.rs = makeReadaheadState()
|
rh.rs = makeReadaheadState(fileMaxReadaheadSize)
|
||||||
return rh
|
return rh
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,10 +128,10 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error
|
|||||||
n, err = rh.sequentialFile.ReadAt(p, offset)
|
n, err = rh.sequentialFile.ReadAt(p, offset)
|
||||||
} else {
|
} else {
|
||||||
if readaheadSize := rh.rs.maybeReadahead(offset, int64(len(p))); readaheadSize > 0 {
|
if readaheadSize := rh.rs.maybeReadahead(offset, int64(len(p))); readaheadSize > 0 {
|
||||||
if readaheadSize >= maxReadaheadSize {
|
if readaheadSize >= fileMaxReadaheadSize {
|
||||||
// We've reached the maximum readahead size. Beyond this point, rely on
|
// We've reached the maximum readahead size. Beyond this point, rely on
|
||||||
// OS-level readahead.
|
// OS-level readahead.
|
||||||
rh.MaxReadahead()
|
rh.switchToOSReadahead()
|
||||||
} else {
|
} else {
|
||||||
_ = rh.r.file.Prefetch(offset, readaheadSize)
|
_ = rh.r.file.Prefetch(offset, readaheadSize)
|
||||||
}
|
}
|
||||||
@@ -142,8 +144,12 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxReadahead is part of the objstorage.ReadHandle interface.
|
// SetupForCompaction is part of the objstorage.ReadHandle interface.
|
||||||
func (rh *vfsReadHandle) MaxReadahead() {
|
func (rh *vfsReadHandle) SetupForCompaction() {
|
||||||
|
rh.switchToOSReadahead()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rh *vfsReadHandle) switchToOSReadahead() {
|
||||||
if rh.sequentialFile != nil {
|
if rh.sequentialFile != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
7
vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go
generated
vendored
7
vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go
generated
vendored
@@ -66,8 +66,11 @@ type Storage interface {
|
|||||||
type ObjectReader interface {
|
type ObjectReader interface {
|
||||||
// ReadAt reads len(p) bytes into p starting at offset off.
|
// ReadAt reads len(p) bytes into p starting at offset off.
|
||||||
//
|
//
|
||||||
// Clients of ReadAt can execute parallel ReadAt calls on the
|
// Does not return partial results; if offset + len(p) is past the end of the
|
||||||
// same ObjectReader.
|
// object, an error is returned.
|
||||||
|
//
|
||||||
|
// Clients of ReadAt can execute parallel ReadAt calls on the same
|
||||||
|
// ObjectReader.
|
||||||
ReadAt(ctx context.Context, p []byte, offset int64) error
|
ReadAt(ctx context.Context, p []byte, offset int64) error
|
||||||
|
|
||||||
Close() error
|
Close() error
|
||||||
|
|||||||
13
vendor/github.com/cockroachdb/pebble/open.go
generated
vendored
13
vendor/github.com/cockroachdb/pebble/open.go
generated
vendored
@@ -210,8 +210,8 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
|||||||
write: d.commitWrite,
|
write: d.commitWrite,
|
||||||
})
|
})
|
||||||
d.deletionLimiter = rate.NewLimiter(
|
d.deletionLimiter = rate.NewLimiter(
|
||||||
rate.Limit(d.opts.Experimental.MinDeletionRate),
|
rate.Limit(d.opts.TargetByteDeletionRate),
|
||||||
d.opts.Experimental.MinDeletionRate)
|
d.opts.TargetByteDeletionRate)
|
||||||
d.mu.nextJobID = 1
|
d.mu.nextJobID = 1
|
||||||
d.mu.mem.nextSize = opts.MemTableSize
|
d.mu.mem.nextSize = opts.MemTableSize
|
||||||
if d.mu.mem.nextSize > initialMemTableSize {
|
if d.mu.mem.nextSize > initialMemTableSize {
|
||||||
@@ -512,7 +512,7 @@ func Open(dirname string, opts *Options) (db *DB, _ error) {
|
|||||||
}
|
}
|
||||||
d.mu.tableStats.cond.L = &d.mu.Mutex
|
d.mu.tableStats.cond.L = &d.mu.Mutex
|
||||||
d.mu.tableValidation.cond.L = &d.mu.Mutex
|
d.mu.tableValidation.cond.L = &d.mu.Mutex
|
||||||
if !d.opts.ReadOnly && !d.opts.private.disableTableStats {
|
if !d.opts.ReadOnly {
|
||||||
d.maybeCollectTableStatsLocked()
|
d.maybeCollectTableStatsLocked()
|
||||||
}
|
}
|
||||||
d.calculateDiskAvailableBytes()
|
d.calculateDiskAvailableBytes()
|
||||||
@@ -804,13 +804,12 @@ func (d *DB) replayWAL(
|
|||||||
paths[i] = base.MakeFilepath(d.opts.FS, d.dirname, fileTypeTable, n)
|
paths[i] = base.MakeFilepath(d.opts.FS, d.dirname, fileTypeTable, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
var meta []*manifest.FileMetadata
|
var lr ingestLoadResult
|
||||||
meta, _, err = ingestLoad(
|
lr, err = ingestLoad(d.opts, d.mu.formatVers.vers, paths, nil, d.cacheID, fileNums)
|
||||||
d.opts, d.mu.formatVers.vers, paths, d.cacheID, fileNums,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
meta := lr.localMeta
|
||||||
|
|
||||||
if uint32(len(meta)) != b.Count() {
|
if uint32(len(meta)) != b.Count() {
|
||||||
panic("pebble: couldn't load all files in WAL entry.")
|
panic("pebble: couldn't load all files in WAL entry.")
|
||||||
|
|||||||
102
vendor/github.com/cockroachdb/pebble/options.go
generated
vendored
102
vendor/github.com/cockroachdb/pebble/options.go
generated
vendored
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/cockroachdb/pebble/internal/base"
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
"github.com/cockroachdb/pebble/internal/cache"
|
"github.com/cockroachdb/pebble/internal/cache"
|
||||||
"github.com/cockroachdb/pebble/internal/humanize"
|
"github.com/cockroachdb/pebble/internal/humanize"
|
||||||
|
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||||
"github.com/cockroachdb/pebble/internal/manifest"
|
"github.com/cockroachdb/pebble/internal/manifest"
|
||||||
"github.com/cockroachdb/pebble/objstorage/shared"
|
"github.com/cockroachdb/pebble/objstorage/shared"
|
||||||
"github.com/cockroachdb/pebble/sstable"
|
"github.com/cockroachdb/pebble/sstable"
|
||||||
@@ -118,10 +119,13 @@ type IterOptions struct {
|
|||||||
// function can be used by multiple iterators, if the iterator is cloned.
|
// function can be used by multiple iterators, if the iterator is cloned.
|
||||||
TableFilter func(userProps map[string]string) bool
|
TableFilter func(userProps map[string]string) bool
|
||||||
// PointKeyFilters can be used to avoid scanning tables and blocks in tables
|
// PointKeyFilters can be used to avoid scanning tables and blocks in tables
|
||||||
// when iterating over point keys. It is requires that this slice is sorted in
|
// when iterating over point keys. This slice represents an intersection
|
||||||
// increasing order of the BlockPropertyFilter.ShortID. This slice represents
|
// across all filters, i.e., all filters must indicate that the block is
|
||||||
// an intersection across all filters, i.e., all filters must indicate that the
|
// relevant.
|
||||||
// block is relevant.
|
//
|
||||||
|
// Performance note: When len(PointKeyFilters) > 0, the caller should ensure
|
||||||
|
// that cap(PointKeyFilters) is at least len(PointKeyFilters)+1. This helps
|
||||||
|
// avoid allocations in Pebble internal code that mutates the slice.
|
||||||
PointKeyFilters []BlockPropertyFilter
|
PointKeyFilters []BlockPropertyFilter
|
||||||
// RangeKeyFilters can be usefd to avoid scanning tables and blocks in tables
|
// RangeKeyFilters can be usefd to avoid scanning tables and blocks in tables
|
||||||
// when iterating over range keys. The same requirements that apply to
|
// when iterating over range keys. The same requirements that apply to
|
||||||
@@ -180,6 +184,10 @@ type IterOptions struct {
|
|||||||
level manifest.Level
|
level manifest.Level
|
||||||
// disableLazyCombinedIteration is an internal testing option.
|
// disableLazyCombinedIteration is an internal testing option.
|
||||||
disableLazyCombinedIteration bool
|
disableLazyCombinedIteration bool
|
||||||
|
// snapshotForHideObsoletePoints is specified for/by levelIter when opening
|
||||||
|
// files and is used to decide whether to hide obsolete points. A value of 0
|
||||||
|
// implies obsolete points should not be hidden.
|
||||||
|
snapshotForHideObsoletePoints uint64
|
||||||
|
|
||||||
// NB: If adding new Options, you must account for them in iterator
|
// NB: If adding new Options, you must account for them in iterator
|
||||||
// construction and Iterator.SetOptions.
|
// construction and Iterator.SetOptions.
|
||||||
@@ -222,6 +230,17 @@ func (o *IterOptions) getLogger() Logger {
|
|||||||
return o.logger
|
return o.logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpanIterOptions creates a SpanIterOptions from this IterOptions.
|
||||||
|
func (o *IterOptions) SpanIterOptions(level manifest.Level) keyspan.SpanIterOptions {
|
||||||
|
if o == nil {
|
||||||
|
return keyspan.SpanIterOptions{Level: level}
|
||||||
|
}
|
||||||
|
return keyspan.SpanIterOptions{
|
||||||
|
RangeKeyFilters: o.RangeKeyFilters,
|
||||||
|
Level: level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// scanInternalOptions is similar to IterOptions, meant for use with
|
// scanInternalOptions is similar to IterOptions, meant for use with
|
||||||
// scanInternalIterator.
|
// scanInternalIterator.
|
||||||
type scanInternalOptions struct {
|
type scanInternalOptions struct {
|
||||||
@@ -509,17 +528,6 @@ type Options struct {
|
|||||||
// concurrency slots as determined by the two options is chosen.
|
// concurrency slots as determined by the two options is chosen.
|
||||||
CompactionDebtConcurrency int
|
CompactionDebtConcurrency int
|
||||||
|
|
||||||
// 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
|
|
||||||
// cleaning up. Deleting lots of files at once can cause disk latency to
|
|
||||||
// go up on some SSDs, which this functionality guards against. This is a
|
|
||||||
// minimum as the maximum is theoretically unlimited; pacing is disabled
|
|
||||||
// when there are too many obsolete files relative to live bytes, or
|
|
||||||
// there isn't enough disk space available. Setting this to 0 disables
|
|
||||||
// deletion pacing, which is also the default.
|
|
||||||
MinDeletionRate int
|
|
||||||
|
|
||||||
// ReadCompactionRate controls the frequency of read triggered
|
// ReadCompactionRate controls the frequency of read triggered
|
||||||
// compactions by adjusting `AllowedSeeks` in manifest.FileMetadata:
|
// compactions by adjusting `AllowedSeeks` in manifest.FileMetadata:
|
||||||
//
|
//
|
||||||
@@ -606,12 +614,6 @@ type Options struct {
|
|||||||
// for CPUWorkPermissionGranter for more details.
|
// for CPUWorkPermissionGranter for more details.
|
||||||
CPUWorkPermissionGranter CPUWorkPermissionGranter
|
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
|
|
||||||
|
|
||||||
// EnableValueBlocks is used to decide whether to enable writing
|
// EnableValueBlocks is used to decide whether to enable writing
|
||||||
// TableFormatPebblev3 sstables. This setting is only respected by a
|
// TableFormatPebblev3 sstables. This setting is only respected by a
|
||||||
// specific subset of format major versions: FormatSSTableValueBlocks,
|
// specific subset of format major versions: FormatSSTableValueBlocks,
|
||||||
@@ -626,18 +628,24 @@ type Options struct {
|
|||||||
ShortAttributeExtractor ShortAttributeExtractor
|
ShortAttributeExtractor ShortAttributeExtractor
|
||||||
|
|
||||||
// RequiredInPlaceValueBound specifies an optional span of user key
|
// RequiredInPlaceValueBound specifies an optional span of user key
|
||||||
// prefixes for which the values must be stored with the key. This is
|
// prefixes that are not-MVCC, but have a suffix. For these the values
|
||||||
// useful for statically known exclusions to value separation. In
|
// must be stored with the key, since the concept of "older versions" is
|
||||||
// CockroachDB, this will be used for the lock table key space that has
|
// not defined. It is also useful for statically known exclusions to value
|
||||||
// non-empty suffixes, but those locks don't represent actual MVCC
|
// separation. In CockroachDB, this will be used for the lock table key
|
||||||
// versions (the suffix ordering is arbitrary). We will also need to add
|
// space that has non-empty suffixes, but those locks don't represent
|
||||||
// support for dynamically configured exclusions (we want the default to
|
// actual MVCC versions (the suffix ordering is arbitrary). We will also
|
||||||
// be to allow Pebble to decide whether to separate the value or not,
|
// need to add support for dynamically configured exclusions (we want the
|
||||||
// hence this is structured as exclusions), for example, for users of
|
// default to be to allow Pebble to decide whether to separate the value
|
||||||
// CockroachDB to dynamically exclude certain tables.
|
// or not, hence this is structured as exclusions), for example, for users
|
||||||
|
// of CockroachDB to dynamically exclude certain tables.
|
||||||
//
|
//
|
||||||
// Any change in exclusion behavior takes effect only on future written
|
// Any change in exclusion behavior takes effect only on future written
|
||||||
// sstables, and does not start rewriting existing sstables.
|
// sstables, and does not start rewriting existing sstables.
|
||||||
|
//
|
||||||
|
// Even ignoring changes in this setting, exclusions are interpreted as a
|
||||||
|
// guidance by Pebble, and not necessarily honored. Specifically, user
|
||||||
|
// keys with multiple Pebble-versions *may* have the older versions stored
|
||||||
|
// in value blocks.
|
||||||
RequiredInPlaceValueBound UserKeyPrefixBound
|
RequiredInPlaceValueBound UserKeyPrefixBound
|
||||||
|
|
||||||
// DisableIngestAsFlushable disables lazy ingestion of sstables through
|
// DisableIngestAsFlushable disables lazy ingestion of sstables through
|
||||||
@@ -858,6 +866,19 @@ type Options struct {
|
|||||||
// changing options dynamically?
|
// changing options dynamically?
|
||||||
WALMinSyncInterval func() time.Duration
|
WALMinSyncInterval func() time.Duration
|
||||||
|
|
||||||
|
// TargetByteDeletionRate is the rate (in bytes per second) at which sstable file
|
||||||
|
// deletions are limited to (under normal circumstances).
|
||||||
|
//
|
||||||
|
// Deletion pacing is used to slow down deletions when compactions finish up
|
||||||
|
// or readers close and newly-obsolete files need cleaning up. Deleting lots
|
||||||
|
// of files at once can cause disk latency to go up on some SSDs, which this
|
||||||
|
// functionality guards against. This is only a best-effort target; pacing is
|
||||||
|
// disabled when there are too many obsolete files relative to live bytes, or
|
||||||
|
// there isn't enough disk space available.
|
||||||
|
//
|
||||||
|
// Setting this to 0 disables deletion pacing, which is also the default.
|
||||||
|
TargetByteDeletionRate int
|
||||||
|
|
||||||
// private options are only used by internal tests or are used internally
|
// private options are only used by internal tests or are used internally
|
||||||
// for facilitating upgrade paths of unconfigurable functionality.
|
// for facilitating upgrade paths of unconfigurable functionality.
|
||||||
private struct {
|
private struct {
|
||||||
@@ -1043,10 +1064,6 @@ func (o *Options) EnsureDefaults() *Options {
|
|||||||
if o.Experimental.CPUWorkPermissionGranter == nil {
|
if o.Experimental.CPUWorkPermissionGranter == nil {
|
||||||
o.Experimental.CPUWorkPermissionGranter = defaultCPUWorkGranter{}
|
o.Experimental.CPUWorkPermissionGranter = defaultCPUWorkGranter{}
|
||||||
}
|
}
|
||||||
if o.Experimental.PointTombstoneWeight == 0 {
|
|
||||||
o.Experimental.PointTombstoneWeight = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.Experimental.MultiLevelCompactionHueristic == nil {
|
if o.Experimental.MultiLevelCompactionHueristic == nil {
|
||||||
o.Experimental.MultiLevelCompactionHueristic = NoMultiLevel{}
|
o.Experimental.MultiLevelCompactionHueristic = NoMultiLevel{}
|
||||||
}
|
}
|
||||||
@@ -1167,9 +1184,8 @@ func (o *Options) String() string {
|
|||||||
fmt.Fprintf(&buf, " max_open_files=%d\n", o.MaxOpenFiles)
|
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_size=%d\n", o.MemTableSize)
|
||||||
fmt.Fprintf(&buf, " mem_table_stop_writes_threshold=%d\n", o.MemTableStopWritesThreshold)
|
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, " min_deletion_rate=%d\n", o.TargetByteDeletionRate)
|
||||||
fmt.Fprintf(&buf, " merger=%s\n", o.Merger.Name)
|
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_compaction_rate=%d\n", o.Experimental.ReadCompactionRate)
|
||||||
fmt.Fprintf(&buf, " read_sampling_multiplier=%d\n", o.Experimental.ReadSamplingMultiplier)
|
fmt.Fprintf(&buf, " read_sampling_multiplier=%d\n", o.Experimental.ReadSamplingMultiplier)
|
||||||
fmt.Fprintf(&buf, " strict_wal_tail=%t\n", o.private.strictWALTail)
|
fmt.Fprintf(&buf, " strict_wal_tail=%t\n", o.private.strictWALTail)
|
||||||
@@ -1374,7 +1390,7 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
|||||||
// version is valid right here.
|
// version is valid right here.
|
||||||
var v uint64
|
var v uint64
|
||||||
v, err = strconv.ParseUint(value, 10, 64)
|
v, err = strconv.ParseUint(value, 10, 64)
|
||||||
if vers := FormatMajorVersion(v); vers > FormatNewest || vers == FormatDefault {
|
if vers := FormatMajorVersion(v); vers > internalFormatNewest || vers == FormatDefault {
|
||||||
err = errors.Newf("unknown format major version %d", o.FormatMajorVersion)
|
err = errors.Newf("unknown format major version %d", o.FormatMajorVersion)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -1414,12 +1430,12 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error {
|
|||||||
// Do nothing; option existed in older versions of pebble, and
|
// Do nothing; option existed in older versions of pebble, and
|
||||||
// may be meaningful again eventually.
|
// may be meaningful again eventually.
|
||||||
case "min_deletion_rate":
|
case "min_deletion_rate":
|
||||||
o.Experimental.MinDeletionRate, err = strconv.Atoi(value)
|
o.TargetByteDeletionRate, err = strconv.Atoi(value)
|
||||||
case "min_flush_rate":
|
case "min_flush_rate":
|
||||||
// Do nothing; option existed in older versions of pebble, and
|
// Do nothing; option existed in older versions of pebble, and
|
||||||
// may be meaningful again eventually.
|
// may be meaningful again eventually.
|
||||||
case "point_tombstone_weight":
|
case "point_tombstone_weight":
|
||||||
o.Experimental.PointTombstoneWeight, err = strconv.ParseFloat(value, 64)
|
// Do nothing; deprecated.
|
||||||
case "strict_wal_tail":
|
case "strict_wal_tail":
|
||||||
o.private.strictWALTail, err = strconv.ParseBool(value)
|
o.private.strictWALTail, err = strconv.ParseBool(value)
|
||||||
case "merger":
|
case "merger":
|
||||||
@@ -1594,9 +1610,9 @@ func (o *Options) Validate() error {
|
|||||||
fmt.Fprintf(&buf, "MemTableStopWritesThreshold (%d) must be >= 2\n",
|
fmt.Fprintf(&buf, "MemTableStopWritesThreshold (%d) must be >= 2\n",
|
||||||
o.MemTableStopWritesThreshold)
|
o.MemTableStopWritesThreshold)
|
||||||
}
|
}
|
||||||
if o.FormatMajorVersion > FormatNewest {
|
if o.FormatMajorVersion > internalFormatNewest {
|
||||||
fmt.Fprintf(&buf, "FormatMajorVersion (%d) must be <= %d\n",
|
fmt.Fprintf(&buf, "FormatMajorVersion (%d) must be <= %d\n",
|
||||||
o.FormatMajorVersion, FormatNewest)
|
o.FormatMajorVersion, internalFormatNewest)
|
||||||
}
|
}
|
||||||
if o.TableCache != nil && o.Cache != o.TableCache.cache {
|
if o.TableCache != nil && o.Cache != o.TableCache.cache {
|
||||||
fmt.Fprintf(&buf, "underlying cache in the TableCache and the Cache dont match\n")
|
fmt.Fprintf(&buf, "underlying cache in the TableCache and the Cache dont match\n")
|
||||||
@@ -1616,6 +1632,7 @@ func (o *Options) MakeReaderOptions() sstable.ReaderOptions {
|
|||||||
readerOpts.Comparer = o.Comparer
|
readerOpts.Comparer = o.Comparer
|
||||||
readerOpts.Filters = o.Filters
|
readerOpts.Filters = o.Filters
|
||||||
if o.Merger != nil {
|
if o.Merger != nil {
|
||||||
|
readerOpts.Merge = o.Merger.Merge
|
||||||
readerOpts.MergerName = o.Merger.Name
|
readerOpts.MergerName = o.Merger.Name
|
||||||
}
|
}
|
||||||
readerOpts.LoggerAndTracer = o.LoggerAndTracer
|
readerOpts.LoggerAndTracer = o.LoggerAndTracer
|
||||||
@@ -1640,6 +1657,9 @@ func (o *Options) MakeWriterOptions(level int, format sstable.TableFormat) sstab
|
|||||||
if format >= sstable.TableFormatPebblev3 {
|
if format >= sstable.TableFormatPebblev3 {
|
||||||
writerOpts.ShortAttributeExtractor = o.Experimental.ShortAttributeExtractor
|
writerOpts.ShortAttributeExtractor = o.Experimental.ShortAttributeExtractor
|
||||||
writerOpts.RequiredInPlaceValueBound = o.Experimental.RequiredInPlaceValueBound
|
writerOpts.RequiredInPlaceValueBound = o.Experimental.RequiredInPlaceValueBound
|
||||||
|
if format >= sstable.TableFormatPebblev4 && level == numLevels-1 {
|
||||||
|
writerOpts.WritingToLowestLevel = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
levelOpts := o.Level(level)
|
levelOpts := o.Level(level)
|
||||||
writerOpts.BlockRestartInterval = levelOpts.BlockRestartInterval
|
writerOpts.BlockRestartInterval = levelOpts.BlockRestartInterval
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/pebble/pacer.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/pacer.go
generated
vendored
@@ -106,7 +106,7 @@ func (p *deletionPacer) limit(amount uint64, info deletionPacerInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// maybeThrottle slows down a deletion of this file if it's faster than
|
// maybeThrottle slows down a deletion of this file if it's faster than
|
||||||
// opts.Experimental.MinDeletionRate.
|
// opts.TargetByteDeletionRate.
|
||||||
func (p *deletionPacer) maybeThrottle(bytesToDelete uint64) error {
|
func (p *deletionPacer) maybeThrottle(bytesToDelete uint64) error {
|
||||||
return p.limit(bytesToDelete, p.getInfo())
|
return p.limit(bytesToDelete, p.getInfo())
|
||||||
}
|
}
|
||||||
|
|||||||
5
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
5
vendor/github.com/cockroachdb/pebble/range_keys.go
generated
vendored
@@ -63,8 +63,7 @@ func (i *Iterator) constructRangeKeyIter() {
|
|||||||
// around Key Trailer order.
|
// around Key Trailer order.
|
||||||
iter := current.RangeKeyLevels[0].Iter()
|
iter := current.RangeKeyLevels[0].Iter()
|
||||||
for f := iter.Last(); f != nil; f = iter.Prev() {
|
for f := iter.Last(); f != nil; f = iter.Prev() {
|
||||||
spanIterOpts := &keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
spanIter, err := i.newIterRangeKey(f, i.opts.SpanIterOptions(manifest.Level(0)))
|
||||||
spanIter, err := i.newIterRangeKey(f, spanIterOpts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
||||||
continue
|
continue
|
||||||
@@ -78,7 +77,7 @@ func (i *Iterator) constructRangeKeyIter() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
li := i.rangeKey.iterConfig.NewLevelIter()
|
li := i.rangeKey.iterConfig.NewLevelIter()
|
||||||
spanIterOpts := keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
spanIterOpts := i.opts.SpanIterOptions(manifest.Level(level))
|
||||||
li.Init(spanIterOpts, i.cmp, i.newIterRangeKey, current.RangeKeyLevels[level].Iter(),
|
li.Init(spanIterOpts, i.cmp, i.newIterRangeKey, current.RangeKeyLevels[level].Iter(),
|
||||||
manifest.Level(level), manifest.KeyTypeRange)
|
manifest.Level(level), manifest.KeyTypeRange)
|
||||||
i.rangeKey.iterConfig.AddLevel(li)
|
i.rangeKey.iterConfig.AddLevel(li)
|
||||||
|
|||||||
407
vendor/github.com/cockroachdb/pebble/scan_internal.go
generated
vendored
407
vendor/github.com/cockroachdb/pebble/scan_internal.go
generated
vendored
@@ -7,12 +7,15 @@ package pebble
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
"github.com/cockroachdb/pebble/internal/base"
|
"github.com/cockroachdb/pebble/internal/base"
|
||||||
|
"github.com/cockroachdb/pebble/internal/invariants"
|
||||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||||
"github.com/cockroachdb/pebble/internal/manifest"
|
"github.com/cockroachdb/pebble/internal/manifest"
|
||||||
"github.com/cockroachdb/pebble/objstorage"
|
"github.com/cockroachdb/pebble/objstorage"
|
||||||
|
"github.com/cockroachdb/pebble/sstable"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -92,8 +95,41 @@ type pcIterPos int
|
|||||||
const (
|
const (
|
||||||
pcIterPosCur pcIterPos = iota
|
pcIterPosCur pcIterPos = iota
|
||||||
pcIterPosNext
|
pcIterPosNext
|
||||||
|
pcIterPosPrev
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// pointCollapsingSSTIterator implements sstable.Iterator while composing
|
||||||
|
// pointCollapsingIterator.
|
||||||
|
type pointCollapsingSSTIterator struct {
|
||||||
|
pointCollapsingIterator
|
||||||
|
childIter sstable.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
var pcSSTIterPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &pointCollapsingSSTIterator{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaybeFilteredKeys implements the sstable.Iterator interface.
|
||||||
|
func (p *pointCollapsingSSTIterator) MaybeFilteredKeys() bool {
|
||||||
|
return p.childIter.MaybeFilteredKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCloseHook implements the sstable.Iterator interface.
|
||||||
|
func (p *pointCollapsingSSTIterator) SetCloseHook(fn func(i sstable.Iterator) error) {
|
||||||
|
p.childIter.SetCloseHook(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements the sstable.Iterator interface.
|
||||||
|
func (p *pointCollapsingSSTIterator) Close() error {
|
||||||
|
err := p.pointCollapsingIterator.Close()
|
||||||
|
p.pointCollapsingIterator = pointCollapsingIterator{}
|
||||||
|
p.childIter = nil
|
||||||
|
pcSSTIterPool.Put(p)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// pointCollapsingIterator is an internalIterator that collapses point keys and
|
// pointCollapsingIterator is an internalIterator that collapses point keys and
|
||||||
// returns at most one point internal key for each user key. Merges are merged,
|
// returns at most one point internal key for each user key. Merges are merged,
|
||||||
// sets are emitted as-is, and SingleDeletes are collapsed with the next point
|
// sets are emitted as-is, and SingleDeletes are collapsed with the next point
|
||||||
@@ -112,32 +148,52 @@ type pointCollapsingIterator struct {
|
|||||||
merge base.Merge
|
merge base.Merge
|
||||||
err error
|
err error
|
||||||
seqNum uint64
|
seqNum uint64
|
||||||
// The current position of `iter`. Could be backed by savedKey (i.e. iterKey ==
|
// The current position of `iter`. Always owned by the underlying iter.
|
||||||
// &savedKey) or could be owned by `iter`. findNextEntry and similar methods
|
iterKey *InternalKey
|
||||||
// are expected to save the current value of this to savedKey if they're
|
// The last saved key. findNextEntry and similar methods are expected to save
|
||||||
// iterating away from the current key but still need to retain it. See
|
// the current value of iterKey to savedKey if they're iterating away from the
|
||||||
// comments in findNextEntry on how this field is used.
|
// current key but still need to retain it. See comments in findNextEntry on
|
||||||
|
// how this field is used.
|
||||||
//
|
//
|
||||||
// At the end of a positioning call:
|
// At the end of a positioning call:
|
||||||
// - if pos == pcIterPosNext, iterKey is pointing to the next user key owned
|
// - if pos == pcIterPosNext, iterKey is pointing to the next user key owned
|
||||||
// by `iter` while savedKey is holding a copy to our current key.
|
// by `iter` while savedKey is holding a copy to our current key.
|
||||||
// - If pos == pcIterPosCur, iterKey is pointing to either a savedKey-backed
|
// - If pos == pcIterPosCur, iterKey is pointing to an `iter`-owned current
|
||||||
// copy of the current key, or an `iter`-owned current key in which case
|
// key, and savedKey is either undefined or pointing to a version of the
|
||||||
// the value of savedKey is undefined.
|
// current key owned by this iterator (i.e. backed by savedKeyBuf).
|
||||||
iterKey *InternalKey
|
// - if pos == pcIterPosPrev, iterKey is pointing to the key before
|
||||||
savedKey InternalKey
|
// p.savedKey. p.savedKey is treated as the current key and is owned by
|
||||||
|
// this iterator, while p.iterKey is the previous key that is the current
|
||||||
|
// position of the child iterator.
|
||||||
|
savedKey InternalKey
|
||||||
|
savedKeyBuf []byte
|
||||||
|
// elideRangeDeletes ignores range deletes returned by the interleaving
|
||||||
|
// iterator if true.
|
||||||
|
elideRangeDeletes bool
|
||||||
// Value at the current iterator position, at iterKey.
|
// Value at the current iterator position, at iterKey.
|
||||||
value base.LazyValue
|
iterValue base.LazyValue
|
||||||
|
// Saved value backed by valueBuf, if set. Used in reverse iteration, and
|
||||||
|
// for merges.
|
||||||
|
savedValue base.LazyValue
|
||||||
// Used for Merge keys only.
|
// Used for Merge keys only.
|
||||||
valueMerger ValueMerger
|
valueMerger ValueMerger
|
||||||
valueBuf []byte
|
valueBuf []byte
|
||||||
|
// If fixedSeqNum is non-zero, all emitted points are verified to have this
|
||||||
|
// fixed sequence number.
|
||||||
|
fixedSeqNum uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekPrefixGE implements the InternalIterator interface.
|
// SeekPrefixGE implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) SeekPrefixGE(
|
func (p *pointCollapsingIterator) SeekPrefixGE(
|
||||||
prefix, key []byte, flags base.SeekGEFlags,
|
prefix, key []byte, flags base.SeekGEFlags,
|
||||||
) (*base.InternalKey, base.LazyValue) {
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
panic("unimplemented")
|
p.resetKey()
|
||||||
|
p.iterKey, p.iterValue = p.iter.SeekPrefixGE(prefix, key, flags)
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
if p.iterKey == nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
return p.findNextEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekGE implements the InternalIterator interface.
|
// SeekGE implements the InternalIterator interface.
|
||||||
@@ -145,7 +201,7 @@ func (p *pointCollapsingIterator) SeekGE(
|
|||||||
key []byte, flags base.SeekGEFlags,
|
key []byte, flags base.SeekGEFlags,
|
||||||
) (*base.InternalKey, base.LazyValue) {
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
p.resetKey()
|
p.resetKey()
|
||||||
p.iterKey, p.value = p.iter.SeekGE(key, flags)
|
p.iterKey, p.iterValue = p.iter.SeekGE(key, flags)
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
if p.iterKey == nil {
|
if p.iterKey == nil {
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
@@ -157,11 +213,17 @@ func (p *pointCollapsingIterator) SeekGE(
|
|||||||
func (p *pointCollapsingIterator) SeekLT(
|
func (p *pointCollapsingIterator) SeekLT(
|
||||||
key []byte, flags base.SeekLTFlags,
|
key []byte, flags base.SeekLTFlags,
|
||||||
) (*base.InternalKey, base.LazyValue) {
|
) (*base.InternalKey, base.LazyValue) {
|
||||||
panic("unimplemented")
|
p.resetKey()
|
||||||
|
p.iterKey, p.iterValue = p.iter.SeekLT(key, flags)
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
if p.iterKey == nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
return p.findPrevEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pointCollapsingIterator) resetKey() {
|
func (p *pointCollapsingIterator) resetKey() {
|
||||||
p.savedKey.UserKey = p.savedKey.UserKey[:0]
|
p.savedKey.UserKey = p.savedKeyBuf[:0]
|
||||||
p.savedKey.Trailer = 0
|
p.savedKey.Trailer = 0
|
||||||
p.valueMerger = nil
|
p.valueMerger = nil
|
||||||
p.valueBuf = p.valueBuf[:0]
|
p.valueBuf = p.valueBuf[:0]
|
||||||
@@ -169,59 +231,73 @@ func (p *pointCollapsingIterator) resetKey() {
|
|||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
}
|
}
|
||||||
|
|
||||||
// findNextEntry is called to return the next key. p.iter must be positioned at
|
func (p *pointCollapsingIterator) verifySeqNum(key *base.InternalKey) *base.InternalKey {
|
||||||
// the start of the first user key we are interested in.
|
if !invariants.Enabled {
|
||||||
func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyValue) {
|
return key
|
||||||
finishAndReturnMerge := func() (*base.InternalKey, base.LazyValue) {
|
|
||||||
value, closer, err := p.valueMerger.Finish(true /* includesBase */)
|
|
||||||
if err != nil {
|
|
||||||
p.err = err
|
|
||||||
return nil, base.LazyValue{}
|
|
||||||
}
|
|
||||||
p.valueBuf = append(p.valueBuf[:0], value...)
|
|
||||||
if closer != nil {
|
|
||||||
_ = closer.Close()
|
|
||||||
}
|
|
||||||
p.valueMerger = nil
|
|
||||||
newValue := base.MakeInPlaceValue(value)
|
|
||||||
return &p.savedKey, newValue
|
|
||||||
}
|
}
|
||||||
|
if p.fixedSeqNum == 0 || key == nil || key.Kind() == InternalKeyKindRangeDelete {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
if key.SeqNum() != p.fixedSeqNum {
|
||||||
|
panic(fmt.Sprintf("expected foreign point key to have seqnum %d, got %d", p.fixedSeqNum, key.SeqNum()))
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
// saveKey sets p.iterKey (if not-nil) to &p.savedKey. We can use this equality
|
// finishAndReturnMerge finishes off the valueMerger and returns the saved key.
|
||||||
// as a proxy to determine if we're at the first internal key for a user key.
|
func (p *pointCollapsingIterator) finishAndReturnMerge() (*base.InternalKey, base.LazyValue) {
|
||||||
|
value, closer, err := p.valueMerger.Finish(true /* includesBase */)
|
||||||
|
if err != nil {
|
||||||
|
p.err = err
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
p.valueBuf = append(p.valueBuf[:0], value...)
|
||||||
|
if closer != nil {
|
||||||
|
_ = closer.Close()
|
||||||
|
}
|
||||||
|
p.valueMerger = nil
|
||||||
|
val := base.MakeInPlaceValue(p.valueBuf)
|
||||||
|
return p.verifySeqNum(&p.savedKey), val
|
||||||
|
}
|
||||||
|
|
||||||
|
// findNextEntry is called to return the next key. p.iter must be positioned at the
|
||||||
|
// start of the first user key we are interested in.
|
||||||
|
func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyValue) {
|
||||||
p.saveKey()
|
p.saveKey()
|
||||||
|
// Saves a comparison in the fast path
|
||||||
|
firstIteration := true
|
||||||
for p.iterKey != nil {
|
for p.iterKey != nil {
|
||||||
// NB: p.savedKey is either the current key (iff p.iterKey == &p.savedKey),
|
// NB: p.savedKey is either the current key (iff p.iterKey == firstKey),
|
||||||
// or the previous key.
|
// or the previous key.
|
||||||
if p.iterKey != &p.savedKey && !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
if !firstIteration && !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
||||||
if p.valueMerger != nil {
|
if p.valueMerger != nil {
|
||||||
if p.savedKey.Kind() != InternalKeyKindMerge {
|
if p.savedKey.Kind() != InternalKeyKindMerge {
|
||||||
panic(fmt.Sprintf("expected key %s to have MERGE kind", p.iterKey))
|
panic(fmt.Sprintf("expected key %s to have MERGE kind", p.iterKey))
|
||||||
}
|
}
|
||||||
p.pos = pcIterPosNext
|
p.pos = pcIterPosNext
|
||||||
return finishAndReturnMerge()
|
return p.finishAndReturnMerge()
|
||||||
}
|
}
|
||||||
p.saveKey()
|
p.saveKey()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
firstIteration = false
|
||||||
if s := p.iter.Span(); s != nil && s.CoversAt(p.seqNum, p.iterKey.SeqNum()) {
|
if s := p.iter.Span(); s != nil && s.CoversAt(p.seqNum, p.iterKey.SeqNum()) {
|
||||||
// All future keys for this user key must be deleted.
|
// All future keys for this user key must be deleted.
|
||||||
if p.valueMerger != nil {
|
if p.valueMerger != nil {
|
||||||
return finishAndReturnMerge()
|
return p.finishAndReturnMerge()
|
||||||
} else if p.savedKey.Kind() == InternalKeyKindSingleDelete {
|
} else if p.savedKey.Kind() == InternalKeyKindSingleDelete {
|
||||||
panic("cannot process singledel key in point collapsing iterator")
|
panic("cannot process singledel key in point collapsing iterator")
|
||||||
}
|
}
|
||||||
// Fast forward to the next user key.
|
// Fast forward to the next user key.
|
||||||
p.saveKey()
|
p.saveKey()
|
||||||
p.iterKey, p.value = p.iter.Next()
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
for p.iterKey != nil && p.savedKey.SeqNum() >= p.iterKey.SeqNum() && p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
for p.iterKey != nil && p.savedKey.SeqNum() >= p.iterKey.SeqNum() && p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
||||||
p.iterKey, p.value = p.iter.Next()
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch p.savedKey.Kind() {
|
switch p.savedKey.Kind() {
|
||||||
case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete:
|
case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete, InternalKeyKindDeleteSized:
|
||||||
p.saveKey()
|
|
||||||
// Note that we return SETs directly, even if they would otherwise get
|
// Note that we return SETs directly, even if they would otherwise get
|
||||||
// compacted into a Del to turn into a SetWithDelete. This is a fast
|
// compacted into a Del to turn into a SetWithDelete. This is a fast
|
||||||
// path optimization that can break SINGLEDEL determinism. To lead to
|
// path optimization that can break SINGLEDEL determinism. To lead to
|
||||||
@@ -240,14 +316,14 @@ func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyV
|
|||||||
// of blocks and can determine user key changes without doing key saves
|
// of blocks and can determine user key changes without doing key saves
|
||||||
// or comparisons.
|
// or comparisons.
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
return p.iterKey, p.value
|
return p.verifySeqNum(p.iterKey), p.iterValue
|
||||||
case InternalKeyKindSingleDelete:
|
case InternalKeyKindSingleDelete:
|
||||||
// Panic, as this iterator is not expected to observe single deletes.
|
// Panic, as this iterator is not expected to observe single deletes.
|
||||||
panic("cannot process singledel key in point collapsing iterator")
|
panic("cannot process singledel key in point collapsing iterator")
|
||||||
case InternalKeyKindMerge:
|
case InternalKeyKindMerge:
|
||||||
if p.valueMerger == nil {
|
if p.valueMerger == nil {
|
||||||
// Set up merger. This is the first Merge key encountered.
|
// Set up merger. This is the first Merge key encountered.
|
||||||
value, callerOwned, err := p.value.Value(p.valueBuf[:0])
|
value, callerOwned, err := p.iterValue.Value(p.valueBuf[:0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.err = err
|
p.err = err
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
@@ -263,13 +339,13 @@ func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyV
|
|||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
p.saveKey()
|
p.saveKey()
|
||||||
p.iterKey, p.value = p.iter.Next()
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch p.iterKey.Kind() {
|
switch p.iterKey.Kind() {
|
||||||
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindSetWithDelete:
|
case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindSetWithDelete:
|
||||||
// Merge into key.
|
// Merge into key.
|
||||||
value, callerOwned, err := p.value.Value(p.valueBuf[:0])
|
value, callerOwned, err := p.iterValue.Value(p.valueBuf[:0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.err = err
|
p.err = err
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
@@ -287,31 +363,144 @@ func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyV
|
|||||||
if p.iterKey.Kind() != InternalKeyKindMerge {
|
if p.iterKey.Kind() != InternalKeyKindMerge {
|
||||||
p.savedKey.SetKind(p.iterKey.Kind())
|
p.savedKey.SetKind(p.iterKey.Kind())
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
return finishAndReturnMerge()
|
return p.finishAndReturnMerge()
|
||||||
}
|
}
|
||||||
p.iterKey, p.value = p.iter.Next()
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
case InternalKeyKindRangeDelete:
|
case InternalKeyKindRangeDelete:
|
||||||
|
if p.elideRangeDeletes {
|
||||||
|
// Skip this range delete, and process any point after it.
|
||||||
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
|
p.saveKey()
|
||||||
|
continue
|
||||||
|
}
|
||||||
// These are interleaved by the interleaving iterator ahead of all points.
|
// These are interleaved by the interleaving iterator ahead of all points.
|
||||||
// We should pass them as-is, but also account for any points ahead of
|
// We should pass them as-is, but also account for any points ahead of
|
||||||
// them.
|
// them.
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
return p.iterKey, p.value
|
return p.verifySeqNum(p.iterKey), p.iterValue
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unexpected kind: %d", p.iterKey.Kind()))
|
panic(fmt.Sprintf("unexpected kind: %d", p.iterKey.Kind()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.valueMerger != nil {
|
if p.valueMerger != nil {
|
||||||
p.pos = pcIterPosNext
|
p.pos = pcIterPosNext
|
||||||
return finishAndReturnMerge()
|
return p.finishAndReturnMerge()
|
||||||
}
|
}
|
||||||
p.resetKey()
|
p.resetKey()
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findPrevEntry finds the relevant point key to return for the previous user key
|
||||||
|
// (i.e. in reverse iteration). Requires that the iterator is already positioned
|
||||||
|
// at the first-in-reverse (i.e. rightmost / largest) internal key encountered
|
||||||
|
// for that user key.
|
||||||
|
func (p *pointCollapsingIterator) findPrevEntry() (*base.InternalKey, base.LazyValue) {
|
||||||
|
if p.iterKey == nil {
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.saveKey()
|
||||||
|
firstIteration := true
|
||||||
|
for p.iterKey != nil {
|
||||||
|
if !firstIteration && !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
||||||
|
p.pos = pcIterPosPrev
|
||||||
|
return p.verifySeqNum(&p.savedKey), p.savedValue
|
||||||
|
}
|
||||||
|
firstIteration = false
|
||||||
|
if s := p.iter.Span(); s != nil && s.CoversAt(p.seqNum, p.iterKey.SeqNum()) {
|
||||||
|
// Skip this key.
|
||||||
|
p.iterKey, p.iterValue = p.iter.Prev()
|
||||||
|
p.saveKey()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch p.iterKey.Kind() {
|
||||||
|
case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete:
|
||||||
|
// Instead of calling saveKey(), we take advantage of the invariant that
|
||||||
|
// p.savedKey.UserKey == p.iterKey.UserKey (otherwise we'd have gone into
|
||||||
|
// the !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) case at
|
||||||
|
// the top of this for loop). That allows us to just save the trailer
|
||||||
|
// and move on.
|
||||||
|
if invariants.Enabled && !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
||||||
|
panic("unexpected inequality between p.iterKey and p.savedKey")
|
||||||
|
}
|
||||||
|
p.savedKey.Trailer = p.iterKey.Trailer
|
||||||
|
// Copy value into p.savedValue.
|
||||||
|
value, callerOwned, err := p.iterValue.Value(p.valueBuf[:0])
|
||||||
|
if err != nil {
|
||||||
|
p.err = err
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if !callerOwned {
|
||||||
|
p.valueBuf = append(p.valueBuf[:0], value...)
|
||||||
|
} else {
|
||||||
|
p.valueBuf = value
|
||||||
|
}
|
||||||
|
p.valueMerger = nil
|
||||||
|
p.savedValue = base.MakeInPlaceValue(p.valueBuf)
|
||||||
|
p.iterKey, p.iterValue = p.iter.Prev()
|
||||||
|
continue
|
||||||
|
case InternalKeyKindSingleDelete:
|
||||||
|
// Panic, as this iterator is not expected to observe single deletes.
|
||||||
|
panic("cannot process singledel key in point collapsing iterator")
|
||||||
|
case InternalKeyKindMerge:
|
||||||
|
panic("cannot process merge key in point collapsing iterator in reverse iteration")
|
||||||
|
case InternalKeyKindRangeDelete:
|
||||||
|
if p.elideRangeDeletes {
|
||||||
|
// Skip this range delete, and process any point before it.
|
||||||
|
p.iterKey, p.iterValue = p.iter.Prev()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// These are interleaved by the interleaving iterator behind all points.
|
||||||
|
if p.savedKey.Kind() != InternalKeyKindRangeDelete {
|
||||||
|
// If the previous key was not a rangedel, we need to return it. Pretend that we're at the
|
||||||
|
// previous user key (i.e. with p.pos = pcIterPosPrev) even if we're not, so on the next
|
||||||
|
// Prev() we encounter and return this rangedel. For now return the point ahead of
|
||||||
|
// this range del (if any).
|
||||||
|
p.pos = pcIterPosPrev
|
||||||
|
return p.verifySeqNum(&p.savedKey), p.savedValue
|
||||||
|
}
|
||||||
|
// We take advantage of the fact that a Prev() *on* a RangeDel iterKey
|
||||||
|
// always takes us to a different user key, so on the next iteration
|
||||||
|
// we will fall into the !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey)
|
||||||
|
// case.
|
||||||
|
//
|
||||||
|
// Instead of calling saveKey(), we take advantage of the invariant that
|
||||||
|
// p.savedKey.UserKey == p.iterKey.UserKey (otherwise we'd have gone into
|
||||||
|
// the !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) case at
|
||||||
|
// the top of this for loop). That allows us to just save the trailer
|
||||||
|
// and move on.
|
||||||
|
if invariants.Enabled && !p.comparer.Equal(p.iterKey.UserKey, p.savedKey.UserKey) {
|
||||||
|
panic("unexpected inequality between p.iterKey and p.savedKey")
|
||||||
|
}
|
||||||
|
p.savedKey.Trailer = p.iterKey.Trailer
|
||||||
|
// Copy value into p.savedValue.
|
||||||
|
value, callerOwned, err := p.iterValue.Value(p.valueBuf[:0])
|
||||||
|
if err != nil {
|
||||||
|
p.err = err
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
if !callerOwned {
|
||||||
|
p.valueBuf = append(p.valueBuf[:0], value...)
|
||||||
|
} else {
|
||||||
|
p.valueBuf = value
|
||||||
|
}
|
||||||
|
p.valueMerger = nil
|
||||||
|
p.savedValue = base.MakeInPlaceValue(p.valueBuf)
|
||||||
|
p.iterKey, p.iterValue = p.iter.Prev()
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected kind: %d", p.iterKey.Kind()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.pos = pcIterPosPrev
|
||||||
|
return p.verifySeqNum(&p.savedKey), p.savedValue
|
||||||
|
}
|
||||||
|
|
||||||
// First implements the InternalIterator interface.
|
// First implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) First() (*base.InternalKey, base.LazyValue) {
|
func (p *pointCollapsingIterator) First() (*base.InternalKey, base.LazyValue) {
|
||||||
p.resetKey()
|
p.resetKey()
|
||||||
p.iterKey, p.value = p.iter.First()
|
p.iterKey, p.iterValue = p.iter.First()
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
if p.iterKey == nil {
|
if p.iterKey == nil {
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
@@ -321,41 +510,67 @@ func (p *pointCollapsingIterator) First() (*base.InternalKey, base.LazyValue) {
|
|||||||
|
|
||||||
// Last implements the InternalIterator interface.
|
// Last implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) Last() (*base.InternalKey, base.LazyValue) {
|
func (p *pointCollapsingIterator) Last() (*base.InternalKey, base.LazyValue) {
|
||||||
panic("unimplemented")
|
p.resetKey()
|
||||||
|
p.iterKey, p.iterValue = p.iter.Last()
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
if p.iterKey == nil {
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
return p.findPrevEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pointCollapsingIterator) saveKey() {
|
func (p *pointCollapsingIterator) saveKey() {
|
||||||
if p.iterKey == nil {
|
if p.iterKey == nil {
|
||||||
p.savedKey = InternalKey{UserKey: p.savedKey.UserKey[:0]}
|
p.savedKey = InternalKey{UserKey: p.savedKeyBuf[:0]}
|
||||||
return
|
|
||||||
} else if p.iterKey == &p.savedKey {
|
|
||||||
// Do nothing.
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.savedKey.CopyFrom(*p.iterKey)
|
p.savedKeyBuf = append(p.savedKeyBuf[:0], p.iterKey.UserKey...)
|
||||||
p.iterKey = &p.savedKey
|
p.savedKey = InternalKey{UserKey: p.savedKeyBuf, Trailer: p.iterKey.Trailer}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next implements the InternalIterator interface.
|
// Next implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) {
|
func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) {
|
||||||
switch p.pos {
|
switch p.pos {
|
||||||
|
case pcIterPosPrev:
|
||||||
|
p.saveKey()
|
||||||
|
if p.iterKey != nil && p.iterKey.Kind() == InternalKeyKindRangeDelete && !p.elideRangeDeletes {
|
||||||
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
} else {
|
||||||
|
// Fast forward to the next user key.
|
||||||
|
key, val := p.iter.Next()
|
||||||
|
// p.iterKey.SeqNum() >= key.SeqNum() is an optimization that allows us to
|
||||||
|
// use p.iterKey.SeqNum() < key.SeqNum() as a sign that the user key has
|
||||||
|
// changed, without needing to do the full key comparison.
|
||||||
|
for key != nil && p.savedKey.SeqNum() >= key.SeqNum() &&
|
||||||
|
p.comparer.Equal(p.savedKey.UserKey, key.UserKey) {
|
||||||
|
key, val = p.iter.Next()
|
||||||
|
}
|
||||||
|
if key == nil {
|
||||||
|
// There are no keys to return.
|
||||||
|
p.resetKey()
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
p.iterKey, p.iterValue = key, val
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
case pcIterPosCur:
|
case pcIterPosCur:
|
||||||
p.saveKey()
|
p.saveKey()
|
||||||
if p.iterKey != nil && p.iterKey.Kind() == InternalKeyKindRangeDelete {
|
if p.iterKey != nil && p.iterKey.Kind() == InternalKeyKindRangeDelete && !p.elideRangeDeletes {
|
||||||
// Step over the interleaved range delete and process the very next
|
// Step over the interleaved range delete and process the very next
|
||||||
// internal key, even if it's at the same user key. This is because a
|
// internal key, even if it's at the same user key. This is because a
|
||||||
// point for that user key has not been returned yet.
|
// point for that user key has not been returned yet.
|
||||||
p.iterKey, p.value = p.iter.Next()
|
p.iterKey, p.iterValue = p.iter.Next()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Fast forward to the next user key. p.iterKey stayed at p.savedKey for
|
// Fast forward to the next user key.
|
||||||
// this loop.
|
|
||||||
key, val := p.iter.Next()
|
key, val := p.iter.Next()
|
||||||
// p.iterKey.SeqNum() >= key.SeqNum() is an optimization that allows us to
|
// p.iterKey.SeqNum() >= key.SeqNum() is an optimization that allows us to
|
||||||
// use p.iterKey.SeqNum() < key.SeqNum() as a sign that the user key has
|
// use p.iterKey.SeqNum() < key.SeqNum() as a sign that the user key has
|
||||||
// changed, without needing to do the full key comparison.
|
// changed, without needing to do the full key comparison.
|
||||||
for p.iterKey != nil && key != nil && p.iterKey.SeqNum() >= key.SeqNum() &&
|
for key != nil && p.savedKey.SeqNum() >= key.SeqNum() &&
|
||||||
p.comparer.Equal(p.iterKey.UserKey, key.UserKey) {
|
p.comparer.Equal(p.savedKey.UserKey, key.UserKey) {
|
||||||
key, val = p.iter.Next()
|
key, val = p.iter.Next()
|
||||||
}
|
}
|
||||||
if key == nil {
|
if key == nil {
|
||||||
@@ -363,7 +578,7 @@ func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) {
|
|||||||
p.resetKey()
|
p.resetKey()
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
p.iterKey, p.value = key, val
|
p.iterKey, p.iterValue = key, val
|
||||||
case pcIterPosNext:
|
case pcIterPosNext:
|
||||||
p.pos = pcIterPosCur
|
p.pos = pcIterPosCur
|
||||||
}
|
}
|
||||||
@@ -376,12 +591,56 @@ func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) {
|
|||||||
|
|
||||||
// NextPrefix implements the InternalIterator interface.
|
// NextPrefix implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
|
func (p *pointCollapsingIterator) NextPrefix(succKey []byte) (*base.InternalKey, base.LazyValue) {
|
||||||
|
// TODO(bilal): Implement this. It'll be similar to SeekGE, except we'll call
|
||||||
|
// the child iterator's NextPrefix, and have some special logic in case pos
|
||||||
|
// is pcIterPosNext.
|
||||||
panic("unimplemented")
|
panic("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prev implements the InternalIterator interface.
|
// Prev implements the InternalIterator interface.
|
||||||
func (p *pointCollapsingIterator) Prev() (*base.InternalKey, base.LazyValue) {
|
func (p *pointCollapsingIterator) Prev() (*base.InternalKey, base.LazyValue) {
|
||||||
panic("unimplemented")
|
switch p.pos {
|
||||||
|
case pcIterPosNext:
|
||||||
|
// Rewind backwards to the previous iter key.
|
||||||
|
p.saveKey()
|
||||||
|
key, val := p.iter.Prev()
|
||||||
|
for key != nil && p.savedKey.SeqNum() <= key.SeqNum() &&
|
||||||
|
p.comparer.Equal(p.savedKey.UserKey, key.UserKey) {
|
||||||
|
if key.Kind() == InternalKeyKindRangeDelete && !p.elideRangeDeletes {
|
||||||
|
// We need to pause at this range delete and return it as-is, as "cur"
|
||||||
|
// is referencing the point key after it, not the range delete.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
key, val = p.iter.Prev()
|
||||||
|
}
|
||||||
|
p.iterKey = key
|
||||||
|
p.iterValue = val
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
fallthrough
|
||||||
|
case pcIterPosCur:
|
||||||
|
p.saveKey()
|
||||||
|
key, val := p.iter.Prev()
|
||||||
|
for key != nil && p.savedKey.SeqNum() <= key.SeqNum() &&
|
||||||
|
p.comparer.Equal(p.savedKey.UserKey, key.UserKey) {
|
||||||
|
if key.Kind() == InternalKeyKindRangeDelete && !p.elideRangeDeletes {
|
||||||
|
// We need to pause at this range delete and return it as-is, as "cur"
|
||||||
|
// is referencing the point key after it, not the range delete.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
key, val = p.iter.Prev()
|
||||||
|
}
|
||||||
|
p.iterKey = key
|
||||||
|
p.iterValue = val
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
case pcIterPosPrev:
|
||||||
|
// Do nothing.
|
||||||
|
p.pos = pcIterPosCur
|
||||||
|
}
|
||||||
|
if p.iterKey == nil {
|
||||||
|
p.resetKey()
|
||||||
|
return nil, base.LazyValue{}
|
||||||
|
}
|
||||||
|
return p.findPrevEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements the InternalIterator interface.
|
// Error implements the InternalIterator interface.
|
||||||
@@ -477,10 +736,6 @@ func (d *DB) truncateSharedFile(
|
|||||||
|
|
||||||
// We will need to truncate file bounds in at least one direction. Open all
|
// We will need to truncate file bounds in at least one direction. Open all
|
||||||
// relevant iterators.
|
// relevant iterators.
|
||||||
//
|
|
||||||
// TODO(bilal): Once virtual sstables go in, verify that the constraining of
|
|
||||||
// bounds to virtual sstable bounds happens below this method, so we aren't
|
|
||||||
// unintentionally exposing keys we shouldn't be exposing.
|
|
||||||
iter, rangeDelIter, err := d.newIters(ctx, file, &IterOptions{
|
iter, rangeDelIter, err := d.newIters(ctx, file, &IterOptions{
|
||||||
LowerBound: lower,
|
LowerBound: lower,
|
||||||
UpperBound: upper,
|
UpperBound: upper,
|
||||||
@@ -497,7 +752,7 @@ func (d *DB) truncateSharedFile(
|
|||||||
)
|
)
|
||||||
defer rangeDelIter.Close()
|
defer rangeDelIter.Close()
|
||||||
}
|
}
|
||||||
rangeKeyIter, err := d.tableNewRangeKeyIter(file, nil /* spanIterOptions */)
|
rangeKeyIter, err := d.tableNewRangeKeyIter(file, keyspan.SpanIterOptions{Level: manifest.Level(level)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@@ -584,6 +839,10 @@ func (d *DB) truncateSharedFile(
|
|||||||
if len(sst.Smallest.UserKey) == 0 {
|
if len(sst.Smallest.UserKey) == 0 {
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
}
|
}
|
||||||
|
sst.Size, err = d.tableCache.estimateSize(file, sst.Smallest.UserKey, sst.Largest.UserKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
return sst, false, nil
|
return sst, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,7 +875,7 @@ func scanInternalImpl(
|
|||||||
for f := files.SeekGE(cmp, lower); f != nil && cmp(f.Smallest.UserKey, upper) < 0; f = files.Next() {
|
for f := files.SeekGE(cmp, lower); f != nil && cmp(f.Smallest.UserKey, upper) < 0; f = files.Next() {
|
||||||
var objMeta objstorage.ObjectMetadata
|
var objMeta objstorage.ObjectMetadata
|
||||||
var err error
|
var err error
|
||||||
objMeta, err = provider.Lookup(fileTypeTable, f.FileNum.DiskFileNum())
|
objMeta, err = provider.Lookup(fileTypeTable, f.FileBacking.DiskFileNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -718,6 +977,7 @@ func (i *scanInternalIterator) constructPointIter(memtables flushableList, buf *
|
|||||||
mlevels = mlevels[:numMergingLevels]
|
mlevels = mlevels[:numMergingLevels]
|
||||||
levels = levels[:numLevelIters]
|
levels = levels[:numLevelIters]
|
||||||
rangeDelLevels = rangeDelLevels[:numLevelIters]
|
rangeDelLevels = rangeDelLevels[:numLevelIters]
|
||||||
|
i.opts.IterOptions.snapshotForHideObsoletePoints = i.seqNum
|
||||||
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) {
|
||||||
li := &levels[levelsIndex]
|
li := &levels[levelsIndex]
|
||||||
rli := &rangeDelLevels[levelsIndex]
|
rli := &rangeDelLevels[levelsIndex]
|
||||||
@@ -804,8 +1064,7 @@ func (i *scanInternalIterator) constructRangeKeyIter() {
|
|||||||
// around Key Trailer order.
|
// around Key Trailer order.
|
||||||
iter := current.RangeKeyLevels[0].Iter()
|
iter := current.RangeKeyLevels[0].Iter()
|
||||||
for f := iter.Last(); f != nil; f = iter.Prev() {
|
for f := iter.Last(); f != nil; f = iter.Prev() {
|
||||||
spanIterOpts := &keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
spanIter, err := i.newIterRangeKey(f, i.opts.SpanIterOptions(manifest.Level(0)))
|
||||||
spanIter, err := i.newIterRangeKey(f, spanIterOpts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err})
|
||||||
continue
|
continue
|
||||||
@@ -822,7 +1081,7 @@ func (i *scanInternalIterator) constructRangeKeyIter() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
li := i.rangeKey.iterConfig.NewLevelIter()
|
li := i.rangeKey.iterConfig.NewLevelIter()
|
||||||
spanIterOpts := keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters}
|
spanIterOpts := i.opts.SpanIterOptions(manifest.Level(level))
|
||||||
li.Init(spanIterOpts, i.comparer.Compare, i.newIterRangeKey, current.RangeKeyLevels[level].Iter(),
|
li.Init(spanIterOpts, i.comparer.Compare, i.newIterRangeKey, current.RangeKeyLevels[level].Iter(),
|
||||||
manifest.Level(level), manifest.KeyTypeRange)
|
manifest.Level(level), manifest.KeyTypeRange)
|
||||||
i.rangeKey.iterConfig.AddLevel(li)
|
i.rangeKey.iterConfig.AddLevel(li)
|
||||||
|
|||||||
301
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
301
vendor/github.com/cockroachdb/pebble/sstable/block.go
generated
vendored
@@ -47,7 +47,10 @@ type blockWriter struct {
|
|||||||
// ensuring that any of the keys with the same prefix can be used to
|
// ensuring that any of the keys with the same prefix can be used to
|
||||||
// assemble the full key when the prefix does change.
|
// assemble the full key when the prefix does change.
|
||||||
restarts []uint32
|
restarts []uint32
|
||||||
curKey []byte
|
// Do not read curKey directly from outside blockWriter since it can have
|
||||||
|
// the InternalKeyKindSSTableInternalObsoleteBit set. Use getCurKey() or
|
||||||
|
// getCurUserKey() instead.
|
||||||
|
curKey []byte
|
||||||
// curValue excludes the optional prefix provided to
|
// curValue excludes the optional prefix provided to
|
||||||
// storeWithOptionalValuePrefix.
|
// storeWithOptionalValuePrefix.
|
||||||
curValue []byte
|
curValue []byte
|
||||||
@@ -79,6 +82,20 @@ const setHasSameKeyPrefixRestartMask uint32 = 1 << 31
|
|||||||
const restartMaskLittleEndianHighByteWithoutSetHasSamePrefix byte = 0b0111_1111
|
const restartMaskLittleEndianHighByteWithoutSetHasSamePrefix byte = 0b0111_1111
|
||||||
const restartMaskLittleEndianHighByteOnlySetHasSamePrefix byte = 0b1000_0000
|
const restartMaskLittleEndianHighByteOnlySetHasSamePrefix byte = 0b1000_0000
|
||||||
|
|
||||||
|
func (w *blockWriter) getCurKey() InternalKey {
|
||||||
|
k := base.DecodeInternalKey(w.curKey)
|
||||||
|
k.Trailer = k.Trailer & trailerObsoleteMask
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *blockWriter) getCurUserKey() []byte {
|
||||||
|
n := len(w.curKey) - base.InternalTrailerLen
|
||||||
|
if n < 0 {
|
||||||
|
panic(errors.AssertionFailedf("corrupt key in blockWriter buffer"))
|
||||||
|
}
|
||||||
|
return w.curKey[:n:n]
|
||||||
|
}
|
||||||
|
|
||||||
// If !addValuePrefix, the valuePrefix is ignored.
|
// If !addValuePrefix, the valuePrefix is ignored.
|
||||||
func (w *blockWriter) storeWithOptionalValuePrefix(
|
func (w *blockWriter) storeWithOptionalValuePrefix(
|
||||||
keySize int,
|
keySize int,
|
||||||
@@ -191,12 +208,21 @@ func (w *blockWriter) storeWithOptionalValuePrefix(
|
|||||||
|
|
||||||
func (w *blockWriter) add(key InternalKey, value []byte) {
|
func (w *blockWriter) add(key InternalKey, value []byte) {
|
||||||
w.addWithOptionalValuePrefix(
|
w.addWithOptionalValuePrefix(
|
||||||
key, value, len(key.UserKey), false, 0, false)
|
key, false, value, len(key.UserKey), false, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callers that always set addValuePrefix to false should use add() instead.
|
// Callers that always set addValuePrefix to false should use add() instead.
|
||||||
|
//
|
||||||
|
// isObsolete indicates whether this key-value pair is obsolete in this
|
||||||
|
// sstable (only applicable when writing data blocks) -- see the comment in
|
||||||
|
// table.go and the longer one in format.go. addValuePrefix adds a 1 byte
|
||||||
|
// prefix to the value, specified in valuePrefix -- this is used for data
|
||||||
|
// blocks in TableFormatPebblev3 onwards for SETs (see the comment in
|
||||||
|
// format.go, with more details in value_block.go). setHasSameKeyPrefix is
|
||||||
|
// also used in TableFormatPebblev3 onwards for SETs.
|
||||||
func (w *blockWriter) addWithOptionalValuePrefix(
|
func (w *blockWriter) addWithOptionalValuePrefix(
|
||||||
key InternalKey,
|
key InternalKey,
|
||||||
|
isObsolete bool,
|
||||||
value []byte,
|
value []byte,
|
||||||
maxSharedKeyLen int,
|
maxSharedKeyLen int,
|
||||||
addValuePrefix bool,
|
addValuePrefix bool,
|
||||||
@@ -210,6 +236,9 @@ func (w *blockWriter) addWithOptionalValuePrefix(
|
|||||||
w.curKey = make([]byte, 0, size*2)
|
w.curKey = make([]byte, 0, size*2)
|
||||||
}
|
}
|
||||||
w.curKey = w.curKey[:size]
|
w.curKey = w.curKey[:size]
|
||||||
|
if isObsolete {
|
||||||
|
key.Trailer = key.Trailer | trailerObsoleteBit
|
||||||
|
}
|
||||||
key.Encode(w.curKey)
|
key.Encode(w.curKey)
|
||||||
|
|
||||||
w.storeWithOptionalValuePrefix(
|
w.storeWithOptionalValuePrefix(
|
||||||
@@ -374,13 +403,14 @@ type blockIter struct {
|
|||||||
cached []blockEntry
|
cached []blockEntry
|
||||||
cachedBuf []byte
|
cachedBuf []byte
|
||||||
cacheHandle cache.Handle
|
cacheHandle cache.Handle
|
||||||
// The first key in the block. This is used by the caller to set bounds
|
// The first user key in the block. This is used by the caller to set bounds
|
||||||
// for block iteration for already loaded blocks.
|
// for block iteration for already loaded blocks.
|
||||||
firstKey InternalKey
|
firstUserKey []byte
|
||||||
lazyValueHandling struct {
|
lazyValueHandling struct {
|
||||||
vbr *valueBlockReader
|
vbr *valueBlockReader
|
||||||
hasValuePrefix bool
|
hasValuePrefix bool
|
||||||
}
|
}
|
||||||
|
hideObsoletePoints bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// blockIter implements the base.InternalIterator interface.
|
// blockIter implements the base.InternalIterator interface.
|
||||||
@@ -388,14 +418,16 @@ var _ base.InternalIterator = (*blockIter)(nil)
|
|||||||
|
|
||||||
func newBlockIter(cmp Compare, block block) (*blockIter, error) {
|
func newBlockIter(cmp Compare, block block) (*blockIter, error) {
|
||||||
i := &blockIter{}
|
i := &blockIter{}
|
||||||
return i, i.init(cmp, block, 0)
|
return i, i.init(cmp, block, 0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) String() string {
|
func (i *blockIter) String() string {
|
||||||
return "block"
|
return "block"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) init(cmp Compare, block block, globalSeqNum uint64) error {
|
func (i *blockIter) init(
|
||||||
|
cmp Compare, block block, globalSeqNum uint64, hideObsoletePoints bool,
|
||||||
|
) error {
|
||||||
numRestarts := int32(binary.LittleEndian.Uint32(block[len(block)-4:]))
|
numRestarts := int32(binary.LittleEndian.Uint32(block[len(block)-4:]))
|
||||||
if numRestarts == 0 {
|
if numRestarts == 0 {
|
||||||
return base.CorruptionErrorf("pebble/table: invalid table (block has no restart points)")
|
return base.CorruptionErrorf("pebble/table: invalid table (block has no restart points)")
|
||||||
@@ -408,6 +440,7 @@ func (i *blockIter) init(cmp Compare, block block, globalSeqNum uint64) error {
|
|||||||
i.data = block
|
i.data = block
|
||||||
i.fullKey = i.fullKey[:0]
|
i.fullKey = i.fullKey[:0]
|
||||||
i.val = nil
|
i.val = nil
|
||||||
|
i.hideObsoletePoints = hideObsoletePoints
|
||||||
i.clearCache()
|
i.clearCache()
|
||||||
if i.restarts > 0 {
|
if i.restarts > 0 {
|
||||||
if err := i.readFirstKey(); err != nil {
|
if err := i.readFirstKey(); err != nil {
|
||||||
@@ -415,15 +448,21 @@ func (i *blockIter) init(cmp Compare, block block, globalSeqNum uint64) error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Block is empty.
|
// Block is empty.
|
||||||
i.firstKey = InternalKey{}
|
i.firstUserKey = nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) initHandle(cmp Compare, block cache.Handle, globalSeqNum uint64) error {
|
// NB: two cases of hideObsoletePoints:
|
||||||
|
// - Local sstable iteration: globalSeqNum will be set iff the sstable was
|
||||||
|
// ingested.
|
||||||
|
// - Foreign sstable iteration: globalSeqNum is always set.
|
||||||
|
func (i *blockIter) initHandle(
|
||||||
|
cmp Compare, block cache.Handle, globalSeqNum uint64, hideObsoletePoints bool,
|
||||||
|
) error {
|
||||||
i.cacheHandle.Release()
|
i.cacheHandle.Release()
|
||||||
i.cacheHandle = block
|
i.cacheHandle = block
|
||||||
return i.init(cmp, block.Get(), globalSeqNum)
|
return i.init(cmp, block.Get(), globalSeqNum, hideObsoletePoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) invalidate() {
|
func (i *blockIter) invalidate() {
|
||||||
@@ -590,24 +629,27 @@ func (i *blockIter) readFirstKey() error {
|
|||||||
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
||||||
// BlockIter benchmarks.
|
// BlockIter benchmarks.
|
||||||
if n := len(firstKey) - 8; n >= 0 {
|
if n := len(firstKey) - 8; n >= 0 {
|
||||||
i.firstKey.Trailer = binary.LittleEndian.Uint64(firstKey[n:])
|
i.firstUserKey = firstKey[:n:n]
|
||||||
i.firstKey.UserKey = firstKey[:n:n]
|
|
||||||
if i.globalSeqNum != 0 {
|
|
||||||
i.firstKey.SetSeqNum(i.globalSeqNum)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
i.firstKey.Trailer = uint64(InternalKeyKindInvalid)
|
i.firstUserKey = nil
|
||||||
i.firstKey.UserKey = nil
|
|
||||||
return base.CorruptionErrorf("pebble/table: invalid firstKey in block")
|
return base.CorruptionErrorf("pebble/table: invalid firstKey in block")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) decodeInternalKey(key []byte) {
|
// The sstable internal obsolete bit is set when writing a block and unset by
|
||||||
|
// blockIter, so no code outside block writing/reading code ever sees it.
|
||||||
|
const trailerObsoleteBit = uint64(base.InternalKeyKindSSTableInternalObsoleteBit)
|
||||||
|
const trailerObsoleteMask = (InternalKeySeqNumMax << 8) | uint64(base.InternalKeyKindSSTableInternalObsoleteMask)
|
||||||
|
|
||||||
|
func (i *blockIter) decodeInternalKey(key []byte) (hiddenPoint bool) {
|
||||||
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
||||||
// BlockIter benchmarks.
|
// BlockIter benchmarks.
|
||||||
if n := len(key) - 8; n >= 0 {
|
if n := len(key) - 8; n >= 0 {
|
||||||
i.ikey.Trailer = binary.LittleEndian.Uint64(key[n:])
|
trailer := binary.LittleEndian.Uint64(key[n:])
|
||||||
|
hiddenPoint = i.hideObsoletePoints &&
|
||||||
|
(trailer&trailerObsoleteBit != 0)
|
||||||
|
i.ikey.Trailer = trailer & trailerObsoleteMask
|
||||||
i.ikey.UserKey = key[:n:n]
|
i.ikey.UserKey = key[:n:n]
|
||||||
if i.globalSeqNum != 0 {
|
if i.globalSeqNum != 0 {
|
||||||
i.ikey.SetSeqNum(i.globalSeqNum)
|
i.ikey.SetSeqNum(i.globalSeqNum)
|
||||||
@@ -616,6 +658,7 @@ func (i *blockIter) decodeInternalKey(key []byte) {
|
|||||||
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
||||||
i.ikey.UserKey = nil
|
i.ikey.UserKey = nil
|
||||||
}
|
}
|
||||||
|
return hiddenPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *blockIter) clearCache() {
|
func (i *blockIter) clearCache() {
|
||||||
@@ -640,13 +683,15 @@ func (i *blockIter) cacheEntry() {
|
|||||||
i.cachedBuf = append(i.cachedBuf, i.key...)
|
i.cachedBuf = append(i.cachedBuf, i.key...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *blockIter) getFirstUserKey() []byte {
|
||||||
|
return i.firstUserKey
|
||||||
|
}
|
||||||
|
|
||||||
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
// SeekGE implements internalIterator.SeekGE, as documented in the pebble
|
||||||
// package.
|
// package.
|
||||||
func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) {
|
||||||
i.clearCache()
|
i.clearCache()
|
||||||
|
|
||||||
ikey := base.MakeSearchKey(key)
|
|
||||||
|
|
||||||
// Find the index of the smallest restart point whose key is > the key
|
// Find the index of the smallest restart point whose key is > the key
|
||||||
// sought; index will be numRestarts if there is no such restart point.
|
// sought; index will be numRestarts if there is no such restart point.
|
||||||
i.offset = 0
|
i.offset = 0
|
||||||
@@ -700,23 +745,23 @@ func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, ba
|
|||||||
ptr = unsafe.Pointer(uintptr(ptr) + 5)
|
ptr = unsafe.Pointer(uintptr(ptr) + 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
// Manually inlining part of base.DecodeInternalKey provides a 5-10%
|
||||||
// BlockIter benchmarks.
|
// speedup on BlockIter benchmarks.
|
||||||
s := getBytes(ptr, int(v1))
|
s := getBytes(ptr, int(v1))
|
||||||
var k InternalKey
|
var k []byte
|
||||||
if n := len(s) - 8; n >= 0 {
|
if n := len(s) - 8; n >= 0 {
|
||||||
k.Trailer = binary.LittleEndian.Uint64(s[n:])
|
k = s[:n:n]
|
||||||
k.UserKey = s[:n:n]
|
|
||||||
// NB: We can't have duplicate keys if the globalSeqNum != 0, so we
|
|
||||||
// leave the seqnum on this key as 0 as it won't affect our search
|
|
||||||
// since ikey has the maximum seqnum.
|
|
||||||
} else {
|
|
||||||
k.Trailer = uint64(InternalKeyKindInvalid)
|
|
||||||
}
|
}
|
||||||
|
// Else k is invalid, and left as nil
|
||||||
|
|
||||||
if base.InternalCompare(i.cmp, ikey, k) >= 0 {
|
if i.cmp(key, k) > 0 {
|
||||||
|
// The search key is greater than the user key at this restart point.
|
||||||
|
// Search beyond this restart point, since we are trying to find the
|
||||||
|
// first restart point with a user key >= the search key.
|
||||||
index = h + 1 // preserves f(i-1) == false
|
index = h + 1 // preserves f(i-1) == false
|
||||||
} else {
|
} else {
|
||||||
|
// k >= search key, so prune everything after index (since index
|
||||||
|
// satisfies the property we are looking for).
|
||||||
upper = h // preserves f(j) == true
|
upper = h // preserves f(j) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -724,21 +769,25 @@ func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, ba
|
|||||||
// => answer is index.
|
// => answer is index.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since keys are strictly increasing, if index > 0 then the restart point at
|
// index is the first restart point with key >= search key. Define the keys
|
||||||
// index-1 will be the largest whose key is <= the key sought. If index ==
|
// between a restart point and the next restart point as belonging to that
|
||||||
// 0, then all keys in this block are larger than the key sought, and offset
|
// restart point.
|
||||||
// remains at zero.
|
//
|
||||||
|
// Since keys are strictly increasing, if index > 0 then the restart point
|
||||||
|
// at index-1 will be the first one that has some keys belonging to it that
|
||||||
|
// could be equal to the search key. If index == 0, then all keys in this
|
||||||
|
// block are larger than the key sought, and offset remains at zero.
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
||||||
}
|
}
|
||||||
i.readEntry()
|
i.readEntry()
|
||||||
i.decodeInternalKey(i.key)
|
hiddenPoint := i.decodeInternalKey(i.key)
|
||||||
|
|
||||||
// Iterate from that restart point to somewhere >= the key sought.
|
// Iterate from that restart point to somewhere >= the key sought.
|
||||||
if !i.valid() {
|
if !i.valid() {
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
if base.InternalCompare(i.cmp, i.ikey, ikey) >= 0 {
|
if !hiddenPoint && i.cmp(i.ikey.UserKey, key) >= 0 {
|
||||||
// Initialize i.lazyValue
|
// Initialize i.lazyValue
|
||||||
if !i.lazyValueHandling.hasValuePrefix ||
|
if !i.lazyValueHandling.hasValuePrefix ||
|
||||||
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
||||||
@@ -751,7 +800,7 @@ func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, ba
|
|||||||
return &i.ikey, i.lazyValue
|
return &i.ikey, i.lazyValue
|
||||||
}
|
}
|
||||||
for i.Next(); i.valid(); i.Next() {
|
for i.Next(); i.valid(); i.Next() {
|
||||||
if base.InternalCompare(i.cmp, i.ikey, ikey) >= 0 {
|
if i.cmp(i.ikey.UserKey, key) >= 0 {
|
||||||
// i.Next() has already initialized i.lazyValue.
|
// i.Next() has already initialized i.lazyValue.
|
||||||
return &i.ikey, i.lazyValue
|
return &i.ikey, i.lazyValue
|
||||||
}
|
}
|
||||||
@@ -773,8 +822,6 @@ func (i *blockIter) SeekPrefixGE(
|
|||||||
func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, base.LazyValue) {
|
||||||
i.clearCache()
|
i.clearCache()
|
||||||
|
|
||||||
ikey := base.MakeSearchKey(key)
|
|
||||||
|
|
||||||
// Find the index of the smallest restart point whose key is >= the key
|
// Find the index of the smallest restart point whose key is >= the key
|
||||||
// sought; index will be numRestarts if there is no such restart point.
|
// sought; index will be numRestarts if there is no such restart point.
|
||||||
i.offset = 0
|
i.offset = 0
|
||||||
@@ -828,23 +875,23 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba
|
|||||||
ptr = unsafe.Pointer(uintptr(ptr) + 5)
|
ptr = unsafe.Pointer(uintptr(ptr) + 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manually inlining base.DecodeInternalKey provides a 5-10% speedup on
|
// Manually inlining part of base.DecodeInternalKey provides a 5-10%
|
||||||
// BlockIter benchmarks.
|
// speedup on BlockIter benchmarks.
|
||||||
s := getBytes(ptr, int(v1))
|
s := getBytes(ptr, int(v1))
|
||||||
var k InternalKey
|
var k []byte
|
||||||
if n := len(s) - 8; n >= 0 {
|
if n := len(s) - 8; n >= 0 {
|
||||||
k.Trailer = binary.LittleEndian.Uint64(s[n:])
|
k = s[:n:n]
|
||||||
k.UserKey = s[:n:n]
|
|
||||||
// NB: We can't have duplicate keys if the globalSeqNum != 0, so we
|
|
||||||
// leave the seqnum on this key as 0 as it won't affect our search
|
|
||||||
// since ikey has the maximum seqnum.
|
|
||||||
} else {
|
|
||||||
k.Trailer = uint64(InternalKeyKindInvalid)
|
|
||||||
}
|
}
|
||||||
|
// Else k is invalid, and left as nil
|
||||||
|
|
||||||
if base.InternalCompare(i.cmp, ikey, k) > 0 {
|
if i.cmp(key, k) > 0 {
|
||||||
|
// The search key is greater than the user key at this restart point.
|
||||||
|
// Search beyond this restart point, since we are trying to find the
|
||||||
|
// first restart point with a user key >= the search key.
|
||||||
index = h + 1 // preserves f(i-1) == false
|
index = h + 1 // preserves f(i-1) == false
|
||||||
} else {
|
} else {
|
||||||
|
// k >= search key, so prune everything after index (since index
|
||||||
|
// satisfies the property we are looking for).
|
||||||
upper = h // preserves f(j) == true
|
upper = h // preserves f(j) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,8 +899,15 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba
|
|||||||
// => answer is index.
|
// => answer is index.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since keys are strictly increasing, if index > 0 then the restart point at
|
// index is the first restart point with key >= search key. Define the keys
|
||||||
// index-1 will be the largest whose key is < the key sought.
|
// between a restart point and the next restart point as belonging to that
|
||||||
|
// restart point. Note that index could be equal to i.numRestarts, i.e., we
|
||||||
|
// are past the last restart.
|
||||||
|
//
|
||||||
|
// Since keys are strictly increasing, if index > 0 then the restart point
|
||||||
|
// at index-1 will be the first one that has some keys belonging to it that
|
||||||
|
// are less than the search key. If index == 0, then all keys in this block
|
||||||
|
// are larger than the search key, so there is no match.
|
||||||
targetOffset := i.restarts
|
targetOffset := i.restarts
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
||||||
@@ -876,24 +930,41 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba
|
|||||||
for {
|
for {
|
||||||
i.offset = i.nextOffset
|
i.offset = i.nextOffset
|
||||||
i.readEntry()
|
i.readEntry()
|
||||||
i.decodeInternalKey(i.key)
|
// When hidden keys are common, there is additional optimization possible
|
||||||
|
// by not caching entries that are hidden (note that some calls to
|
||||||
|
// cacheEntry don't decode the internal key before caching, but checking
|
||||||
|
// whether a key is hidden does not require full decoding). However, we do
|
||||||
|
// need to use the blockEntry.offset in the cache for the first entry at
|
||||||
|
// the reset point to do the binary search when the cache is empty -- so
|
||||||
|
// we would need to cache that first entry (though not the key) even if
|
||||||
|
// was hidden. Our current assumption is that if there are large numbers
|
||||||
|
// of hidden keys we will be able to skip whole blocks (using block
|
||||||
|
// property filters) so we don't bother optimizing.
|
||||||
|
hiddenPoint := i.decodeInternalKey(i.key)
|
||||||
|
|
||||||
if i.cmp(i.ikey.UserKey, ikey.UserKey) >= 0 {
|
// NB: we don't use the hiddenPoint return value of decodeInternalKey
|
||||||
|
// since we want to stop as soon as we reach a key >= ikey.UserKey, so
|
||||||
|
// that we can reverse.
|
||||||
|
if i.cmp(i.ikey.UserKey, key) >= 0 {
|
||||||
// The current key is greater than or equal to our search key. Back up to
|
// The current key is greater than or equal to our search key. Back up to
|
||||||
// the previous key which was less than our search key. Note that this for
|
// the previous key which was less than our search key. Note that this for
|
||||||
// loop will execute at least once with this if-block not being true, so
|
// 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.
|
// the key we are backing up to is the last one this loop cached.
|
||||||
i.Prev()
|
return i.Prev()
|
||||||
// i.Prev() has already initialized i.lazyValue.
|
|
||||||
return &i.ikey, i.lazyValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if i.nextOffset >= targetOffset {
|
if i.nextOffset >= targetOffset {
|
||||||
// We've reached the end of the current restart block. Return the current
|
// We've reached the end of the current restart block. Return the
|
||||||
// key. When the restart interval is 1, the first iteration of the for
|
// current key if not hidden, else call Prev().
|
||||||
// loop will bring us here. In that case ikey is backed by the block so
|
//
|
||||||
// we get the desired key stability guarantee for the lifetime of the
|
// When the restart interval is 1, the first iteration of the for loop
|
||||||
// blockIter.
|
// will bring us here. In that case ikey is backed by the block so we
|
||||||
|
// get the desired key stability guarantee for the lifetime of the
|
||||||
|
// blockIter. That is, we never cache anything and therefore never
|
||||||
|
// return a key backed by cachedBuf.
|
||||||
|
if hiddenPoint {
|
||||||
|
return i.Prev()
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -923,7 +994,10 @@ func (i *blockIter) First() (*InternalKey, base.LazyValue) {
|
|||||||
}
|
}
|
||||||
i.clearCache()
|
i.clearCache()
|
||||||
i.readEntry()
|
i.readEntry()
|
||||||
i.decodeInternalKey(i.key)
|
hiddenPoint := i.decodeInternalKey(i.key)
|
||||||
|
if hiddenPoint {
|
||||||
|
return i.Next()
|
||||||
|
}
|
||||||
if !i.lazyValueHandling.hasValuePrefix ||
|
if !i.lazyValueHandling.hasValuePrefix ||
|
||||||
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
||||||
i.lazyValue = base.MakeInPlaceValue(i.val)
|
i.lazyValue = base.MakeInPlaceValue(i.val)
|
||||||
@@ -958,7 +1032,10 @@ func (i *blockIter) Last() (*InternalKey, base.LazyValue) {
|
|||||||
i.readEntry()
|
i.readEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
i.decodeInternalKey(i.key)
|
hiddenPoint := i.decodeInternalKey(i.key)
|
||||||
|
if hiddenPoint {
|
||||||
|
return i.Prev()
|
||||||
|
}
|
||||||
if !i.lazyValueHandling.hasValuePrefix ||
|
if !i.lazyValueHandling.hasValuePrefix ||
|
||||||
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
||||||
i.lazyValue = base.MakeInPlaceValue(i.val)
|
i.lazyValue = base.MakeInPlaceValue(i.val)
|
||||||
@@ -988,6 +1065,7 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) {
|
|||||||
i.clearCache()
|
i.clearCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start:
|
||||||
i.offset = i.nextOffset
|
i.offset = i.nextOffset
|
||||||
if !i.valid() {
|
if !i.valid() {
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
@@ -995,11 +1073,17 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) {
|
|||||||
i.readEntry()
|
i.readEntry()
|
||||||
// Manually inlined version of i.decodeInternalKey(i.key).
|
// Manually inlined version of i.decodeInternalKey(i.key).
|
||||||
if n := len(i.key) - 8; n >= 0 {
|
if n := len(i.key) - 8; n >= 0 {
|
||||||
i.ikey.Trailer = binary.LittleEndian.Uint64(i.key[n:])
|
trailer := binary.LittleEndian.Uint64(i.key[n:])
|
||||||
|
hiddenPoint := i.hideObsoletePoints &&
|
||||||
|
(trailer&trailerObsoleteBit != 0)
|
||||||
|
i.ikey.Trailer = trailer & trailerObsoleteMask
|
||||||
i.ikey.UserKey = i.key[:n:n]
|
i.ikey.UserKey = i.key[:n:n]
|
||||||
if i.globalSeqNum != 0 {
|
if i.globalSeqNum != 0 {
|
||||||
i.ikey.SetSeqNum(i.globalSeqNum)
|
i.ikey.SetSeqNum(i.globalSeqNum)
|
||||||
}
|
}
|
||||||
|
if hiddenPoint {
|
||||||
|
goto start
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
i.ikey.Trailer = uint64(InternalKeyKindInvalid)
|
||||||
i.ikey.UserKey = nil
|
i.ikey.UserKey = nil
|
||||||
@@ -1015,8 +1099,8 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) {
|
|||||||
return &i.ikey, i.lazyValue
|
return &i.ikey, i.lazyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextPrefix is used for implementing NPrefix.
|
// NextPrefix implements (base.InternalIterator).NextPrefix.
|
||||||
func (i *blockIter) nextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
|
func (i *blockIter) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
|
||||||
if i.lazyValueHandling.hasValuePrefix {
|
if i.lazyValueHandling.hasValuePrefix {
|
||||||
return i.nextPrefixV3(succKey)
|
return i.nextPrefixV3(succKey)
|
||||||
}
|
}
|
||||||
@@ -1141,6 +1225,7 @@ func (i *blockIter) nextPrefixV3(succKey []byte) (*InternalKey, base.LazyValue)
|
|||||||
// The trailer is written in little endian, so the key kind is the first
|
// The trailer is written in little endian, so the key kind is the first
|
||||||
// byte in the trailer that is encoded in the slice [unshared-8:unshared].
|
// byte in the trailer that is encoded in the slice [unshared-8:unshared].
|
||||||
keyKind := InternalKeyKind((*[manual.MaxArrayLen]byte)(ptr)[unshared-8])
|
keyKind := InternalKeyKind((*[manual.MaxArrayLen]byte)(ptr)[unshared-8])
|
||||||
|
keyKind = keyKind & base.InternalKeyKindSSTableInternalObsoleteMask
|
||||||
prefixChanged := false
|
prefixChanged := false
|
||||||
if keyKind == InternalKeyKindSet {
|
if keyKind == InternalKeyKindSet {
|
||||||
if invariants.Enabled && value == 0 {
|
if invariants.Enabled && value == 0 {
|
||||||
@@ -1256,8 +1341,12 @@ func (i *blockIter) nextPrefixV3(succKey []byte) (*InternalKey, base.LazyValue)
|
|||||||
i.key = i.fullKey
|
i.key = i.fullKey
|
||||||
}
|
}
|
||||||
// Manually inlined version of i.decodeInternalKey(i.key).
|
// Manually inlined version of i.decodeInternalKey(i.key).
|
||||||
|
hiddenPoint := false
|
||||||
if n := len(i.key) - 8; n >= 0 {
|
if n := len(i.key) - 8; n >= 0 {
|
||||||
i.ikey.Trailer = binary.LittleEndian.Uint64(i.key[n:])
|
trailer := binary.LittleEndian.Uint64(i.key[n:])
|
||||||
|
hiddenPoint = i.hideObsoletePoints &&
|
||||||
|
(trailer&trailerObsoleteBit != 0)
|
||||||
|
i.ikey.Trailer = trailer & trailerObsoleteMask
|
||||||
i.ikey.UserKey = i.key[:n:n]
|
i.ikey.UserKey = i.key[:n:n]
|
||||||
if i.globalSeqNum != 0 {
|
if i.globalSeqNum != 0 {
|
||||||
i.ikey.SetSeqNum(i.globalSeqNum)
|
i.ikey.SetSeqNum(i.globalSeqNum)
|
||||||
@@ -1273,6 +1362,9 @@ func (i *blockIter) nextPrefixV3(succKey []byte) (*InternalKey, base.LazyValue)
|
|||||||
}
|
}
|
||||||
if prefixChanged || i.cmp(i.ikey.UserKey, succKey) >= 0 {
|
if prefixChanged || i.cmp(i.ikey.UserKey, succKey) >= 0 {
|
||||||
// Prefix has changed.
|
// Prefix has changed.
|
||||||
|
if hiddenPoint {
|
||||||
|
return i.Next()
|
||||||
|
}
|
||||||
if invariants.Enabled && !i.lazyValueHandling.hasValuePrefix {
|
if invariants.Enabled && !i.lazyValueHandling.hasValuePrefix {
|
||||||
panic(errors.AssertionFailedf("nextPrefixV3 being run for non-v3 sstable"))
|
panic(errors.AssertionFailedf("nextPrefixV3 being run for non-v3 sstable"))
|
||||||
}
|
}
|
||||||
@@ -1294,23 +1386,11 @@ func (i *blockIter) nextPrefixV3(succKey []byte) (*InternalKey, base.LazyValue)
|
|||||||
return i.SeekGE(succKey, base.SeekGEFlagsNone)
|
return i.SeekGE(succKey, base.SeekGEFlagsNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextPrefix implements (base.InternalIterator).NextPrefix
|
|
||||||
func (i *blockIter) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) {
|
|
||||||
const nextsBeforeSeek = 3
|
|
||||||
k, v := i.Next()
|
|
||||||
for j := 1; k != nil && i.cmp(k.UserKey, succKey) < 0; j++ {
|
|
||||||
if j >= nextsBeforeSeek {
|
|
||||||
return i.SeekGE(succKey, base.SeekGEFlagsNone)
|
|
||||||
}
|
|
||||||
k, v = i.Next()
|
|
||||||
}
|
|
||||||
return k, v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prev implements internalIterator.Prev, as documented in the pebble
|
// Prev implements internalIterator.Prev, as documented in the pebble
|
||||||
// package.
|
// package.
|
||||||
func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
||||||
if n := len(i.cached) - 1; n >= 0 {
|
start:
|
||||||
|
for n := len(i.cached) - 1; n >= 0; n-- {
|
||||||
i.nextOffset = i.offset
|
i.nextOffset = i.offset
|
||||||
e := &i.cached[n]
|
e := &i.cached[n]
|
||||||
i.offset = e.offset
|
i.offset = e.offset
|
||||||
@@ -1318,7 +1398,13 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
|||||||
// Manually inlined version of i.decodeInternalKey(i.key).
|
// Manually inlined version of i.decodeInternalKey(i.key).
|
||||||
i.key = i.cachedBuf[e.keyStart:e.keyEnd]
|
i.key = i.cachedBuf[e.keyStart:e.keyEnd]
|
||||||
if n := len(i.key) - 8; n >= 0 {
|
if n := len(i.key) - 8; n >= 0 {
|
||||||
i.ikey.Trailer = binary.LittleEndian.Uint64(i.key[n:])
|
trailer := binary.LittleEndian.Uint64(i.key[n:])
|
||||||
|
hiddenPoint := i.hideObsoletePoints &&
|
||||||
|
(trailer&trailerObsoleteBit != 0)
|
||||||
|
if hiddenPoint {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i.ikey.Trailer = trailer & trailerObsoleteMask
|
||||||
i.ikey.UserKey = i.key[:n:n]
|
i.ikey.UserKey = i.key[:n:n]
|
||||||
if i.globalSeqNum != 0 {
|
if i.globalSeqNum != 0 {
|
||||||
i.ikey.SetSeqNum(i.globalSeqNum)
|
i.ikey.SetSeqNum(i.globalSeqNum)
|
||||||
@@ -1360,6 +1446,8 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
|||||||
// index ≤ h < upper
|
// index ≤ h < upper
|
||||||
offset := decodeRestart(i.data[i.restarts+4*h:])
|
offset := decodeRestart(i.data[i.restarts+4*h:])
|
||||||
if offset < targetOffset {
|
if offset < targetOffset {
|
||||||
|
// Looking for the first restart that has offset >= targetOffset, so
|
||||||
|
// ignore h and earlier.
|
||||||
index = h + 1 // preserves f(i-1) == false
|
index = h + 1 // preserves f(i-1) == false
|
||||||
} else {
|
} else {
|
||||||
upper = h // preserves f(j) == true
|
upper = h // preserves f(j) == true
|
||||||
@@ -1369,20 +1457,35 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) {
|
|||||||
// => answer is index.
|
// => answer is index.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index is first restart with offset >= targetOffset. Note that
|
||||||
|
// targetOffset may not be at a restart point since one can call Prev()
|
||||||
|
// after Next() (so the cache was not populated) and targetOffset refers to
|
||||||
|
// the current entry. index-1 must have an offset < targetOffset (it can't
|
||||||
|
// be equal to targetOffset since the binary search would have selected that
|
||||||
|
// as the index).
|
||||||
i.offset = 0
|
i.offset = 0
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
i.offset = decodeRestart(i.data[i.restarts+4*(index-1):])
|
||||||
}
|
}
|
||||||
|
// TODO(sumeer): why is the else case not an error given targetOffset is a
|
||||||
|
// valid offset.
|
||||||
|
|
||||||
i.readEntry()
|
i.readEntry()
|
||||||
|
|
||||||
|
// We stop when i.nextOffset == targetOffset since the targetOffset is the
|
||||||
|
// entry we are stepping back from, and we don't need to cache the entry
|
||||||
|
// before it, since it is the candidate to return.
|
||||||
for i.nextOffset < targetOffset {
|
for i.nextOffset < targetOffset {
|
||||||
i.cacheEntry()
|
i.cacheEntry()
|
||||||
i.offset = i.nextOffset
|
i.offset = i.nextOffset
|
||||||
i.readEntry()
|
i.readEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
i.decodeInternalKey(i.key)
|
hiddenPoint := i.decodeInternalKey(i.key)
|
||||||
|
if hiddenPoint {
|
||||||
|
// Use the cache.
|
||||||
|
goto start
|
||||||
|
}
|
||||||
if !i.lazyValueHandling.hasValuePrefix ||
|
if !i.lazyValueHandling.hasValuePrefix ||
|
||||||
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet {
|
||||||
i.lazyValue = base.MakeInPlaceValue(i.val)
|
i.lazyValue = base.MakeInPlaceValue(i.val)
|
||||||
@@ -1455,6 +1558,10 @@ type fragmentBlockIter struct {
|
|||||||
err error
|
err error
|
||||||
dir int8
|
dir int8
|
||||||
closeHook func(i keyspan.FragmentIterator) error
|
closeHook func(i keyspan.FragmentIterator) error
|
||||||
|
|
||||||
|
// elideSameSeqnum, if true, returns only the first-occurring (in forward
|
||||||
|
// order) Key for each sequence number.
|
||||||
|
elideSameSeqnum bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *fragmentBlockIter) resetForReuse() fragmentBlockIter {
|
func (i *fragmentBlockIter) resetForReuse() fragmentBlockIter {
|
||||||
@@ -1483,6 +1590,24 @@ func (i *fragmentBlockIter) decodeSpanKeys(k *InternalKey, internalValue []byte)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *fragmentBlockIter) elideKeysOfSameSeqNum() {
|
||||||
|
if invariants.Enabled {
|
||||||
|
if !i.elideSameSeqnum || len(i.span.Keys) == 0 {
|
||||||
|
panic("elideKeysOfSameSeqNum called when it should not be")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastSeqNum := i.span.Keys[0].SeqNum()
|
||||||
|
k := 1
|
||||||
|
for j := 1; j < len(i.span.Keys); j++ {
|
||||||
|
if lastSeqNum != i.span.Keys[j].SeqNum() {
|
||||||
|
lastSeqNum = i.span.Keys[j].SeqNum()
|
||||||
|
i.span.Keys[k] = i.span.Keys[j]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.span.Keys = i.span.Keys[:k]
|
||||||
|
}
|
||||||
|
|
||||||
// gatherForward gathers internal keys with identical bounds. Keys defined over
|
// gatherForward gathers internal keys with identical bounds. Keys defined over
|
||||||
// spans of the keyspace are fragmented such that any overlapping key spans have
|
// spans of the keyspace are fragmented such that any overlapping key spans have
|
||||||
// identical bounds. When these spans are persisted to a range deletion or range
|
// identical bounds. When these spans are persisted to a range deletion or range
|
||||||
@@ -1532,6 +1657,9 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, lazyValue base.LazyVal
|
|||||||
k, lazyValue = i.blockIter.Next()
|
k, lazyValue = i.blockIter.Next()
|
||||||
internalValue = lazyValue.InPlaceValue()
|
internalValue = lazyValue.InPlaceValue()
|
||||||
}
|
}
|
||||||
|
if i.elideSameSeqnum && len(i.span.Keys) > 0 {
|
||||||
|
i.elideKeysOfSameSeqNum()
|
||||||
|
}
|
||||||
// i.blockIter is positioned over the first internal key for the next span.
|
// i.blockIter is positioned over the first internal key for the next span.
|
||||||
return &i.span
|
return &i.span
|
||||||
}
|
}
|
||||||
@@ -1591,6 +1719,9 @@ func (i *fragmentBlockIter) gatherBackward(k *InternalKey, lazyValue base.LazyVa
|
|||||||
// Backwards iteration encounters internal keys in the wrong order.
|
// Backwards iteration encounters internal keys in the wrong order.
|
||||||
keyspan.SortKeysByTrailer(&i.span.Keys)
|
keyspan.SortKeysByTrailer(&i.span.Keys)
|
||||||
|
|
||||||
|
if i.elideSameSeqnum && len(i.span.Keys) > 0 {
|
||||||
|
i.elideKeysOfSameSeqNum()
|
||||||
|
}
|
||||||
return &i.span
|
return &i.span
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/sstable/block_property.go
generated
vendored
@@ -117,7 +117,7 @@ type BlockPropertyCollector interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SuffixReplaceableBlockCollector is an extension to the BlockPropertyCollector
|
// SuffixReplaceableBlockCollector is an extension to the BlockPropertyCollector
|
||||||
// interface that allows a block property collector to indicate the it supports
|
// interface that allows a block property collector to indicate that it supports
|
||||||
// being *updated* during suffix replacement, i.e. when an existing SST in which
|
// being *updated* during suffix replacement, i.e. when an existing SST in which
|
||||||
// all keys have the same key suffix is updated to have a new suffix.
|
// all keys have the same key suffix is updated to have a new suffix.
|
||||||
//
|
//
|
||||||
|
|||||||
152
vendor/github.com/cockroachdb/pebble/sstable/format.go
generated
vendored
152
vendor/github.com/cockroachdb/pebble/sstable/format.go
generated
vendored
@@ -24,11 +24,161 @@ const (
|
|||||||
TableFormatPebblev1 // Block properties.
|
TableFormatPebblev1 // Block properties.
|
||||||
TableFormatPebblev2 // Range keys.
|
TableFormatPebblev2 // Range keys.
|
||||||
TableFormatPebblev3 // Value blocks.
|
TableFormatPebblev3 // Value blocks.
|
||||||
TableFormatPebblev4 // Reserved.
|
TableFormatPebblev4 // DELSIZED tombstones.
|
||||||
|
|
||||||
TableFormatMax = TableFormatPebblev4
|
TableFormatMax = TableFormatPebblev4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TableFormatPebblev4, in addition to DELSIZED, introduces the use of
|
||||||
|
// InternalKeyKindSSTableInternalObsoleteBit.
|
||||||
|
//
|
||||||
|
// 1. Motivation
|
||||||
|
//
|
||||||
|
// We have various related problems caused by Pebble snapshots:
|
||||||
|
//
|
||||||
|
// - P1: RANGEDELs that delete points in the same sstable, but the points
|
||||||
|
// happen to not get deleted during compactions because of an open snapshot.
|
||||||
|
// This causes very expensive iteration, that has been observed in
|
||||||
|
// production deployments
|
||||||
|
//
|
||||||
|
// - P2: When iterating over a foreign sstable (in disaggregated storage), we
|
||||||
|
// need to do (a) point collapsing to expose at most one point per user key,
|
||||||
|
// (b) apply RANGEDELs in the sstable to hide deleted points in the same
|
||||||
|
// sstable. This per-sstable point collapsing iteration needs to be very
|
||||||
|
// efficient (ideally as efficient from a CPU perspective as iteration over
|
||||||
|
// regular sstables) since foreign sstables can be very long-lived -- one of
|
||||||
|
// the goals of disaggregated storage is to scale compute and disk bandwidth
|
||||||
|
// resources as a function of the hot (from a write perspective) data and
|
||||||
|
// not the whole data, so we don't want to have to rewrite foreign sstables
|
||||||
|
// solely to improve read performance.
|
||||||
|
//
|
||||||
|
// The ideal solution for P2 would allow user-facing reads to utilize the
|
||||||
|
// existing SST iterators (with slight modifications) and with no loss of
|
||||||
|
// efficiency. And for P1 and P2 we would like to skip whole blocks of
|
||||||
|
// overwritten/deleted points. Even when we can't skip whole blocks, avoiding
|
||||||
|
// key comparisons at iteration time to discover what points are deleted is
|
||||||
|
// very desirable, since keys can be long.
|
||||||
|
//
|
||||||
|
// We observe that:
|
||||||
|
//
|
||||||
|
// - Reads:
|
||||||
|
// - All user-facing reads in CockroachDB use iterators over the DB, hence
|
||||||
|
// have a higher read seqnum than all sstables (there are some rare cases
|
||||||
|
// that can violate this, but those are not important from a performance
|
||||||
|
// optimization perspective).
|
||||||
|
//
|
||||||
|
// - Certain internal-facing reads in CockroachDB use snapshots, but the
|
||||||
|
// snapshots are shortlived enough that most L5 and L6 sstables will have
|
||||||
|
// all seqnums lower than the snapshot seqnum.
|
||||||
|
//
|
||||||
|
// - Writes:
|
||||||
|
// - We already do key comparisons between points when writing the sstable
|
||||||
|
// to ensure that the sstable invariant (monotonically increasing internal
|
||||||
|
// keys) is not violated. So we know which points share the same userkey,
|
||||||
|
// and thereby which points are obsolete because there is a more recent
|
||||||
|
// point in the same sstable.
|
||||||
|
//
|
||||||
|
// - The compactionIter knows which point id deleted by a RANGEDEL even if
|
||||||
|
// the point does need to be written because of a snapshot.
|
||||||
|
//
|
||||||
|
// So this known information can be encoded in the sstable at write time and
|
||||||
|
// utilized for optimized reading.
|
||||||
|
//
|
||||||
|
// 2. Solution
|
||||||
|
//
|
||||||
|
// We primarily scope the solution to the following point kinds: SET,
|
||||||
|
// SETWITHDEL, DEL, DELSIZED, SINGLEDEL. These are the ones marked locally
|
||||||
|
// obsolete, i.e., obsolete within the sstable, and we can guarantee that at
|
||||||
|
// most one point will be exposed per user key. MERGE keys create more
|
||||||
|
// complexity: MERGE followed by MERGE causes multiple keys to not be
|
||||||
|
// obsolete. Same applies for MERGE followed by SET/SETWITHDEL. Note that:
|
||||||
|
//
|
||||||
|
// - For regular sst iteration, the obsolete marking is a performance
|
||||||
|
// optimization, and multiple keys for the same userkey can be handled by
|
||||||
|
// higher layers in the iterator tree (specifically pebble.Iterator).
|
||||||
|
//
|
||||||
|
// - For foreign sst iteration, we disallow MERGEs to be written to such
|
||||||
|
// shared ssts (details below).
|
||||||
|
//
|
||||||
|
// The key kinds are marked with an obsolete bit
|
||||||
|
// (InternalKeyKindSSTableInternalObsoleteBit) when the key-value pair is
|
||||||
|
// obsolete. This marking is done within blockWriter, based on information
|
||||||
|
// passed to it by Writer. In turn, Writer uses a combination of key
|
||||||
|
// comparisons, and information provided by compactionIter to decide whether a
|
||||||
|
// key-value pair is obsolete. Additionally, a Pebble-internal
|
||||||
|
// BlockPropertyCollector (obsoleteKeyBlockPropertyCollector) is used to mark
|
||||||
|
// blocks where all key-value pairs are obsolete. Since the common case is
|
||||||
|
// non-obsolete blocks, this block property collector uses the empty byte
|
||||||
|
// slice to represent a non-obsolete block, which consumes no space in
|
||||||
|
// BlockHandleWithProperties.Props.
|
||||||
|
//
|
||||||
|
// At read time, the obsolete bit is only visible to the blockIter, which can
|
||||||
|
// be optionally configured to hide obsolete points. This hiding is only
|
||||||
|
// configured for data block iterators for sstables being read by user-facing
|
||||||
|
// iterators at a seqnum greater than the max seqnum in the sstable.
|
||||||
|
// Additionally, when this hiding is configured, a Pebble-internal block
|
||||||
|
// property filter (obsoleteKeyBlockPropertyFilter), is used to skip whole
|
||||||
|
// blocks that are obsolete.
|
||||||
|
//
|
||||||
|
// 2.1 Correctness
|
||||||
|
//
|
||||||
|
// Due to the level invariant, the sequence of seqnums for a user key in a
|
||||||
|
// sstable represents a contiguous subsequence of the seqnums for the userkey
|
||||||
|
// across the whole LSM, and is more recent than the seqnums in a sstable in a
|
||||||
|
// lower level. So exposing exactly one point from a sstable for a userkey
|
||||||
|
// will also mask the points for the userkey in lower levels. If we expose no
|
||||||
|
// point, because of RANGEDELs, that RANGEDEL will also mask the points in
|
||||||
|
// lower levels.
|
||||||
|
//
|
||||||
|
// Note that we do not need to do anything special at write time for
|
||||||
|
// SETWITHDEL and SINGLEDEL. This is because these key kinds are treated
|
||||||
|
// specially only by compactions, which do not hide obsolete points. For
|
||||||
|
// regular reads, SETWITHDEL behaves the same as SET and SINGLEDEL behaves the
|
||||||
|
// same as DEL.
|
||||||
|
//
|
||||||
|
// 2.2 Strictness and MERGE
|
||||||
|
//
|
||||||
|
// Setting the obsolete bit on point keys is advanced usage, so we support two
|
||||||
|
// modes, both of which must be truthful when setting the obsolete bit, but
|
||||||
|
// vary in when they don't set the obsolete bit.
|
||||||
|
//
|
||||||
|
// - Non-strict: In this mode, the bit does not need to be set for keys that
|
||||||
|
// are obsolete. Additionally, any sstable containing MERGE keys can only
|
||||||
|
// use this mode. An iterator over such an sstable, when configured to
|
||||||
|
// hideObsoletePoints, can expose multiple internal keys per user key, and
|
||||||
|
// can expose keys that are deleted by rangedels in the same sstable. This
|
||||||
|
// is the mode that non-advanced users should use. Pebble without
|
||||||
|
// disaggregated storage will also use this mode and will best-effort set
|
||||||
|
// the obsolete bit, to optimize iteration when snapshots have retained many
|
||||||
|
// obsolete keys.
|
||||||
|
//
|
||||||
|
// - Strict: In this mode, every obsolete key must have the obsolete bit set,
|
||||||
|
// and no MERGE keys are permitted. An iterator over such an sstable, when
|
||||||
|
// configured to hideObsoletePoints satisfies two properties:
|
||||||
|
// - S1: will expose at most one internal key per user key, which is the
|
||||||
|
// most recent one.
|
||||||
|
// - S2: will never expose keys that are deleted by rangedels in the same
|
||||||
|
// sstable.
|
||||||
|
//
|
||||||
|
// This is the mode for two use cases in disaggregated storage (which will
|
||||||
|
// exclude parts of the key space that has MERGEs), for levels that contain
|
||||||
|
// sstables that can become foreign sstables:
|
||||||
|
// - Pebble compaction output to these levels that can become foreign
|
||||||
|
// sstables.
|
||||||
|
//
|
||||||
|
// - CockroachDB ingest operations that can ingest into the levels that can
|
||||||
|
// become foreign sstables. Note, these are not sstables corresponding to
|
||||||
|
// copied data for CockroachDB range snapshots. This case occurs for
|
||||||
|
// operations like index backfills: these trivially satisfy the strictness
|
||||||
|
// criteria since they only write one key per userkey.
|
||||||
|
//
|
||||||
|
// TODO(sumeer): this latter case is not currently supported, since only
|
||||||
|
// Writer.AddWithForceObsolete calls are permitted for writing strict
|
||||||
|
// obsolete sstables. This is done to reduce the likelihood of bugs. One
|
||||||
|
// simple way to lift this limitation would be to disallow adding any
|
||||||
|
// RANGEDELs when a Pebble-external writer is trying to construct a strict
|
||||||
|
// obsolete sstable.
|
||||||
|
|
||||||
// ParseTableFormat parses the given magic bytes and version into its
|
// ParseTableFormat parses the given magic bytes and version into its
|
||||||
// corresponding internal TableFormat.
|
// corresponding internal TableFormat.
|
||||||
func ParseTableFormat(magic []byte, version uint32) (TableFormat, error) {
|
func ParseTableFormat(magic []byte, version uint32) (TableFormat, error) {
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
2
vendor/github.com/cockroachdb/pebble/sstable/internal.go
generated
vendored
@@ -23,6 +23,8 @@ const (
|
|||||||
InternalKeyKindLogData = base.InternalKeyKindLogData
|
InternalKeyKindLogData = base.InternalKeyKindLogData
|
||||||
InternalKeyKindSingleDelete = base.InternalKeyKindSingleDelete
|
InternalKeyKindSingleDelete = base.InternalKeyKindSingleDelete
|
||||||
InternalKeyKindRangeDelete = base.InternalKeyKindRangeDelete
|
InternalKeyKindRangeDelete = base.InternalKeyKindRangeDelete
|
||||||
|
InternalKeyKindSetWithDelete = base.InternalKeyKindSetWithDelete
|
||||||
|
InternalKeyKindDeleteSized = base.InternalKeyKindDeleteSized
|
||||||
InternalKeyKindMax = base.InternalKeyKindMax
|
InternalKeyKindMax = base.InternalKeyKindMax
|
||||||
InternalKeyKindInvalid = base.InternalKeyKindInvalid
|
InternalKeyKindInvalid = base.InternalKeyKindInvalid
|
||||||
InternalKeySeqNumBatch = base.InternalKeySeqNumBatch
|
InternalKeySeqNumBatch = base.InternalKeySeqNumBatch
|
||||||
|
|||||||
34
vendor/github.com/cockroachdb/pebble/sstable/options.go
generated
vendored
34
vendor/github.com/cockroachdb/pebble/sstable/options.go
generated
vendored
@@ -21,6 +21,17 @@ const (
|
|||||||
NCompression
|
NCompression
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ignoredInternalProperties = map[string]struct{}{
|
||||||
|
"rocksdb.column.family.id": {},
|
||||||
|
"rocksdb.fixed.key.length": {},
|
||||||
|
"rocksdb.index.key.is.user.key": {},
|
||||||
|
"rocksdb.index.value.is.delta.encoded": {},
|
||||||
|
"rocksdb.oldest.key.time": {},
|
||||||
|
"rocksdb.creation.time": {},
|
||||||
|
"rocksdb.file.creation.time": {},
|
||||||
|
"rocksdb.format.version": {},
|
||||||
|
}
|
||||||
|
|
||||||
func (c Compression) String() string {
|
func (c Compression) String() string {
|
||||||
switch c {
|
switch c {
|
||||||
case DefaultCompression:
|
case DefaultCompression:
|
||||||
@@ -100,6 +111,9 @@ type ReaderOptions struct {
|
|||||||
// The default cache size is a zero-size cache.
|
// The default cache size is a zero-size cache.
|
||||||
Cache *cache.Cache
|
Cache *cache.Cache
|
||||||
|
|
||||||
|
// User properties specified in this map will not be added to sst.Properties.UserProperties.
|
||||||
|
DeniedUserProperties map[string]struct{}
|
||||||
|
|
||||||
// Comparer defines a total ordering over the space of []byte keys: a 'less
|
// Comparer defines a total ordering over the space of []byte keys: a 'less
|
||||||
// than' relationship. The same comparison algorithm must be used for reads
|
// than' relationship. The same comparison algorithm must be used for reads
|
||||||
// and writes over the lifetime of the DB.
|
// and writes over the lifetime of the DB.
|
||||||
@@ -107,6 +121,9 @@ type ReaderOptions struct {
|
|||||||
// The default value uses the same ordering as bytes.Compare.
|
// The default value uses the same ordering as bytes.Compare.
|
||||||
Comparer *Comparer
|
Comparer *Comparer
|
||||||
|
|
||||||
|
// Merge defines the Merge function in use for this keyspace.
|
||||||
|
Merge base.Merge
|
||||||
|
|
||||||
// Filters is a map from filter policy name to filter policy. It is used for
|
// Filters is a map from filter policy name to filter policy. It is used for
|
||||||
// debugging tools which may be used on multiple databases configured with
|
// debugging tools which may be used on multiple databases configured with
|
||||||
// different filter policies. It is not necessary to populate this filters
|
// different filter policies. It is not necessary to populate this filters
|
||||||
@@ -126,12 +143,18 @@ func (o ReaderOptions) ensureDefaults() ReaderOptions {
|
|||||||
if o.Comparer == nil {
|
if o.Comparer == nil {
|
||||||
o.Comparer = base.DefaultComparer
|
o.Comparer = base.DefaultComparer
|
||||||
}
|
}
|
||||||
|
if o.Merge == nil {
|
||||||
|
o.Merge = base.DefaultMerger.Merge
|
||||||
|
}
|
||||||
if o.MergerName == "" {
|
if o.MergerName == "" {
|
||||||
o.MergerName = base.DefaultMerger.Name
|
o.MergerName = base.DefaultMerger.Name
|
||||||
}
|
}
|
||||||
if o.LoggerAndTracer == nil {
|
if o.LoggerAndTracer == nil {
|
||||||
o.LoggerAndTracer = base.NoopLoggerAndTracer{}
|
o.LoggerAndTracer = base.NoopLoggerAndTracer{}
|
||||||
}
|
}
|
||||||
|
if o.DeniedUserProperties == nil {
|
||||||
|
o.DeniedUserProperties = ignoredInternalProperties
|
||||||
|
}
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +233,17 @@ type WriterOptions struct {
|
|||||||
// by a wider range of tools and libraries.
|
// by a wider range of tools and libraries.
|
||||||
TableFormat TableFormat
|
TableFormat TableFormat
|
||||||
|
|
||||||
|
// IsStrictObsolete is only relevant for >= TableFormatPebblev4. See comment
|
||||||
|
// in format.go. Must be false if format < TableFormatPebblev4.
|
||||||
|
//
|
||||||
|
// TODO(bilal): set this when writing shared ssts.
|
||||||
|
IsStrictObsolete bool
|
||||||
|
|
||||||
|
// WritingToLowestLevel is only relevant for >= TableFormatPebblev4. It is
|
||||||
|
// used to set the obsolete bit on DEL/DELSIZED/SINGLEDEL if they are the
|
||||||
|
// youngest for a userkey.
|
||||||
|
WritingToLowestLevel bool
|
||||||
|
|
||||||
// TablePropertyCollectors is a list of TablePropertyCollector creation
|
// TablePropertyCollectors is a list of TablePropertyCollector creation
|
||||||
// functions. A new TablePropertyCollector is created for each sstable built
|
// functions. A new TablePropertyCollector is created for each sstable built
|
||||||
// and lives for the lifetime of the table.
|
// and lives for the lifetime of the table.
|
||||||
|
|||||||
98
vendor/github.com/cockroachdb/pebble/sstable/properties.go
generated
vendored
98
vendor/github.com/cockroachdb/pebble/sstable/properties.go
generated
vendored
@@ -23,14 +23,6 @@ var propTagMap = make(map[string]reflect.StructField)
|
|||||||
var propBoolTrue = []byte{'1'}
|
var propBoolTrue = []byte{'1'}
|
||||||
var propBoolFalse = []byte{'0'}
|
var propBoolFalse = []byte{'0'}
|
||||||
|
|
||||||
var columnFamilyIDField = func() reflect.StructField {
|
|
||||||
f, ok := reflect.TypeOf(Properties{}).FieldByName("ColumnFamilyID")
|
|
||||||
if !ok {
|
|
||||||
panic("Properties.ColumnFamilyID field not found")
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}()
|
|
||||||
|
|
||||||
var propOffsetTagMap = make(map[uintptr]string)
|
var propOffsetTagMap = make(map[uintptr]string)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -56,51 +48,35 @@ func init() {
|
|||||||
// automatically populated during sstable creation and load from the properties
|
// automatically populated during sstable creation and load from the properties
|
||||||
// meta block when an sstable is opened.
|
// meta block when an sstable is opened.
|
||||||
type Properties struct {
|
type Properties struct {
|
||||||
// ID of column family for this SST file, corresponding to the CF identified
|
|
||||||
// by column_family_name.
|
|
||||||
ColumnFamilyID uint64 `prop:"rocksdb.column.family.id"`
|
|
||||||
// Name of the column family with which this SST file is associated. Empty if
|
|
||||||
// the column family is unknown.
|
|
||||||
ColumnFamilyName string `prop:"rocksdb.column.family.name"`
|
|
||||||
// The name of the comparer used in this table.
|
// The name of the comparer used in this table.
|
||||||
ComparerName string `prop:"rocksdb.comparator"`
|
ComparerName string `prop:"rocksdb.comparator"`
|
||||||
// The compression algorithm used to compress blocks.
|
// The compression algorithm used to compress blocks.
|
||||||
CompressionName string `prop:"rocksdb.compression"`
|
CompressionName string `prop:"rocksdb.compression"`
|
||||||
// The compression options used to compress blocks.
|
// The compression options used to compress blocks.
|
||||||
CompressionOptions string `prop:"rocksdb.compression_options"`
|
CompressionOptions string `prop:"rocksdb.compression_options"`
|
||||||
// The time when the SST file was created. Since SST files are immutable,
|
|
||||||
// this is equivalent to last modified time.
|
|
||||||
CreationTime uint64 `prop:"rocksdb.creation.time"`
|
|
||||||
// The total size of all data blocks.
|
// The total size of all data blocks.
|
||||||
DataSize uint64 `prop:"rocksdb.data.size"`
|
DataSize uint64 `prop:"rocksdb.data.size"`
|
||||||
// The external sstable version format. Version 2 is the one RocksDB has been
|
// The external sstable version format. Version 2 is the one RocksDB has been
|
||||||
// using since 5.13. RocksDB only uses the global sequence number for an
|
// using since 5.13. RocksDB only uses the global sequence number for an
|
||||||
// sstable if this property has been set.
|
// sstable if this property has been set.
|
||||||
ExternalFormatVersion uint32 `prop:"rocksdb.external_sst_file.version"`
|
ExternalFormatVersion uint32 `prop:"rocksdb.external_sst_file.version"`
|
||||||
// Actual SST file creation time. 0 means unknown.
|
|
||||||
FileCreationTime uint64 `prop:"rocksdb.file.creation.time"`
|
|
||||||
// The name of the filter policy used in this table. Empty if no filter
|
// The name of the filter policy used in this table. Empty if no filter
|
||||||
// policy is used.
|
// policy is used.
|
||||||
FilterPolicyName string `prop:"rocksdb.filter.policy"`
|
FilterPolicyName string `prop:"rocksdb.filter.policy"`
|
||||||
// The size of filter block.
|
// The size of filter block.
|
||||||
FilterSize uint64 `prop:"rocksdb.filter.size"`
|
FilterSize uint64 `prop:"rocksdb.filter.size"`
|
||||||
// If 0, key is variable length. Otherwise number of bytes for each key.
|
|
||||||
FixedKeyLen uint64 `prop:"rocksdb.fixed.key.length"`
|
|
||||||
// Format version, reserved for backward compatibility.
|
|
||||||
FormatVersion uint64 `prop:"rocksdb.format.version"`
|
|
||||||
// The global sequence number to use for all entries in the table. Present if
|
// The global sequence number to use for all entries in the table. Present if
|
||||||
// the table was created externally and ingested whole.
|
// the table was created externally and ingested whole.
|
||||||
GlobalSeqNum uint64 `prop:"rocksdb.external_sst_file.global_seqno"`
|
GlobalSeqNum uint64 `prop:"rocksdb.external_sst_file.global_seqno"`
|
||||||
// Whether the index key is user key or an internal key.
|
|
||||||
IndexKeyIsUserKey uint64 `prop:"rocksdb.index.key.is.user.key"`
|
|
||||||
// Total number of index partitions if kTwoLevelIndexSearch is used.
|
// Total number of index partitions if kTwoLevelIndexSearch is used.
|
||||||
IndexPartitions uint64 `prop:"rocksdb.index.partitions"`
|
IndexPartitions uint64 `prop:"rocksdb.index.partitions"`
|
||||||
// The size of index block.
|
// The size of index block.
|
||||||
IndexSize uint64 `prop:"rocksdb.index.size"`
|
IndexSize uint64 `prop:"rocksdb.index.size"`
|
||||||
// The index type. TODO(peter): add a more detailed description.
|
// The index type. TODO(peter): add a more detailed description.
|
||||||
IndexType uint32 `prop:"rocksdb.block.based.table.index.type"`
|
IndexType uint32 `prop:"rocksdb.block.based.table.index.type"`
|
||||||
// Whether delta encoding is used to encode the index values.
|
// For formats >= TableFormatPebblev4, this is set to true if the obsolete
|
||||||
IndexValueIsDeltaEncoded uint64 `prop:"rocksdb.index.value.is.delta.encoded"`
|
// bit is strict for all the point keys.
|
||||||
|
IsStrictObsolete bool `prop:"pebble.obsolete.is_strict"`
|
||||||
// The name of the merger used in this table. Empty if no merger is used.
|
// The name of the merger used in this table. Empty if no merger is used.
|
||||||
MergerName string `prop:"rocksdb.merge.operator"`
|
MergerName string `prop:"rocksdb.merge.operator"`
|
||||||
// The number of blocks in this table.
|
// The number of blocks in this table.
|
||||||
@@ -108,6 +84,9 @@ type Properties struct {
|
|||||||
// The number of deletion entries in this table, including both point and
|
// The number of deletion entries in this table, including both point and
|
||||||
// range deletions.
|
// range deletions.
|
||||||
NumDeletions uint64 `prop:"rocksdb.deleted.keys"`
|
NumDeletions uint64 `prop:"rocksdb.deleted.keys"`
|
||||||
|
// The number of point deletion entries ("tombstones") in this table that
|
||||||
|
// carry a size hint indicating the size of the value the tombstone deletes.
|
||||||
|
NumSizedDeletions uint64 `prop:"pebble.num.deletions.sized"`
|
||||||
// The number of entries in this table.
|
// The number of entries in this table.
|
||||||
NumEntries uint64 `prop:"rocksdb.num.entries"`
|
NumEntries uint64 `prop:"rocksdb.num.entries"`
|
||||||
// The number of merge operands in the table.
|
// The number of merge operands in the table.
|
||||||
@@ -124,8 +103,6 @@ type Properties struct {
|
|||||||
NumValueBlocks uint64 `prop:"pebble.num.value-blocks"`
|
NumValueBlocks uint64 `prop:"pebble.num.value-blocks"`
|
||||||
// The number of values stored in value blocks. Only serialized if > 0.
|
// The number of values stored in value blocks. Only serialized if > 0.
|
||||||
NumValuesInValueBlocks uint64 `prop:"pebble.num.values.in.value-blocks"`
|
NumValuesInValueBlocks uint64 `prop:"pebble.num.values.in.value-blocks"`
|
||||||
// Timestamp of the earliest key. 0 if unknown.
|
|
||||||
OldestKeyTime uint64 `prop:"rocksdb.oldest.key.time"`
|
|
||||||
// The name of the prefix extractor used in this table. Empty if no prefix
|
// The name of the prefix extractor used in this table. Empty if no prefix
|
||||||
// extractor is used.
|
// extractor is used.
|
||||||
PrefixExtractorName string `prop:"rocksdb.prefix.extractor.name"`
|
PrefixExtractorName string `prop:"rocksdb.prefix.extractor.name"`
|
||||||
@@ -139,6 +116,10 @@ type Properties struct {
|
|||||||
// Total raw key size of point deletion tombstones. This value is comparable
|
// Total raw key size of point deletion tombstones. This value is comparable
|
||||||
// to RawKeySize.
|
// to RawKeySize.
|
||||||
RawPointTombstoneKeySize uint64 `prop:"pebble.raw.point-tombstone.key.size"`
|
RawPointTombstoneKeySize uint64 `prop:"pebble.raw.point-tombstone.key.size"`
|
||||||
|
// Sum of the raw value sizes carried by point deletion tombstones
|
||||||
|
// containing size estimates. See the DeleteSized key kind. This value is
|
||||||
|
// comparable to Raw{Key,Value}Size.
|
||||||
|
RawPointTombstoneValueSize uint64 `prop:"pebble.raw.point-tombstone.value.size"`
|
||||||
// Total raw rangekey key size.
|
// Total raw rangekey key size.
|
||||||
RawRangeKeyKeySize uint64 `prop:"pebble.raw.range-key.key.size"`
|
RawRangeKeyKeySize uint64 `prop:"pebble.raw.range-key.key.size"`
|
||||||
// Total raw rangekey value size.
|
// Total raw rangekey value size.
|
||||||
@@ -206,12 +187,7 @@ func (p *Properties) String() string {
|
|||||||
case reflect.Uint32:
|
case reflect.Uint32:
|
||||||
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
u := f.Uint()
|
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
||||||
if ft.Offset == columnFamilyIDField.Offset && u == math.MaxInt32 {
|
|
||||||
fmt.Fprintf(&buf, "-\n")
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(&buf, "%d\n", f.Uint())
|
|
||||||
}
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
fmt.Fprintf(&buf, "%s\n", f.String())
|
fmt.Fprintf(&buf, "%s\n", f.String())
|
||||||
default:
|
default:
|
||||||
@@ -229,7 +205,9 @@ func (p *Properties) String() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Properties) load(b block, blockOffset uint64) error {
|
func (p *Properties) load(
|
||||||
|
b block, blockOffset uint64, deniedUserProperties map[string]struct{},
|
||||||
|
) error {
|
||||||
i, err := newRawBlockIter(bytes.Compare, b)
|
i, err := newRawBlockIter(bytes.Compare, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -237,8 +215,7 @@ func (p *Properties) load(b block, blockOffset uint64) error {
|
|||||||
p.Loaded = make(map[uintptr]struct{})
|
p.Loaded = make(map[uintptr]struct{})
|
||||||
v := reflect.ValueOf(p).Elem()
|
v := reflect.ValueOf(p).Elem()
|
||||||
for valid := i.First(); valid; valid = i.Next() {
|
for valid := i.First(); valid; valid = i.Next() {
|
||||||
tag := intern.Bytes(i.Key().UserKey)
|
if f, ok := propTagMap[string(i.Key().UserKey)]; ok {
|
||||||
if f, ok := propTagMap[tag]; ok {
|
|
||||||
p.Loaded[f.Offset] = struct{}{}
|
p.Loaded[f.Offset] = struct{}{}
|
||||||
field := v.FieldByIndex(f.Index)
|
field := v.FieldByIndex(f.Index)
|
||||||
switch f.Type.Kind() {
|
switch f.Type.Kind() {
|
||||||
@@ -248,7 +225,7 @@ func (p *Properties) load(b block, blockOffset uint64) error {
|
|||||||
field.SetUint(uint64(binary.LittleEndian.Uint32(i.Value())))
|
field.SetUint(uint64(binary.LittleEndian.Uint32(i.Value())))
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
var n uint64
|
var n uint64
|
||||||
if tag == propGlobalSeqnumName {
|
if string(i.Key().UserKey) == propGlobalSeqnumName {
|
||||||
n = binary.LittleEndian.Uint64(i.Value())
|
n = binary.LittleEndian.Uint64(i.Value())
|
||||||
} else {
|
} else {
|
||||||
n, _ = binary.Uvarint(i.Value())
|
n, _ = binary.Uvarint(i.Value())
|
||||||
@@ -264,7 +241,10 @@ func (p *Properties) load(b block, blockOffset uint64) error {
|
|||||||
if p.UserProperties == nil {
|
if p.UserProperties == nil {
|
||||||
p.UserProperties = make(map[string]string)
|
p.UserProperties = make(map[string]string)
|
||||||
}
|
}
|
||||||
p.UserProperties[tag] = string(i.Value())
|
|
||||||
|
if _, denied := deniedUserProperties[string(i.Key().UserKey)]; !denied {
|
||||||
|
p.UserProperties[intern.Bytes(i.Key().UserKey)] = string(i.Value())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -306,10 +286,6 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) {
|
|||||||
m[k] = []byte(v)
|
m[k] = []byte(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.ColumnFamilyID), p.ColumnFamilyID)
|
|
||||||
if p.ColumnFamilyName != "" {
|
|
||||||
p.saveString(m, unsafe.Offsetof(p.ColumnFamilyName), p.ColumnFamilyName)
|
|
||||||
}
|
|
||||||
if p.ComparerName != "" {
|
if p.ComparerName != "" {
|
||||||
p.saveString(m, unsafe.Offsetof(p.ComparerName), p.ComparerName)
|
p.saveString(m, unsafe.Offsetof(p.ComparerName), p.ComparerName)
|
||||||
}
|
}
|
||||||
@@ -319,44 +295,45 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) {
|
|||||||
if p.CompressionOptions != "" {
|
if p.CompressionOptions != "" {
|
||||||
p.saveString(m, unsafe.Offsetof(p.CompressionOptions), p.CompressionOptions)
|
p.saveString(m, unsafe.Offsetof(p.CompressionOptions), p.CompressionOptions)
|
||||||
}
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.CreationTime), p.CreationTime)
|
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.DataSize), p.DataSize)
|
p.saveUvarint(m, unsafe.Offsetof(p.DataSize), p.DataSize)
|
||||||
if p.ExternalFormatVersion != 0 {
|
if p.ExternalFormatVersion != 0 {
|
||||||
p.saveUint32(m, unsafe.Offsetof(p.ExternalFormatVersion), p.ExternalFormatVersion)
|
p.saveUint32(m, unsafe.Offsetof(p.ExternalFormatVersion), p.ExternalFormatVersion)
|
||||||
p.saveUint64(m, unsafe.Offsetof(p.GlobalSeqNum), p.GlobalSeqNum)
|
p.saveUint64(m, unsafe.Offsetof(p.GlobalSeqNum), p.GlobalSeqNum)
|
||||||
}
|
}
|
||||||
if p.FileCreationTime > 0 {
|
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.FileCreationTime), p.FileCreationTime)
|
|
||||||
}
|
|
||||||
if p.FilterPolicyName != "" {
|
if p.FilterPolicyName != "" {
|
||||||
p.saveString(m, unsafe.Offsetof(p.FilterPolicyName), p.FilterPolicyName)
|
p.saveString(m, unsafe.Offsetof(p.FilterPolicyName), p.FilterPolicyName)
|
||||||
}
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.FilterSize), p.FilterSize)
|
p.saveUvarint(m, unsafe.Offsetof(p.FilterSize), p.FilterSize)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.FixedKeyLen), p.FixedKeyLen)
|
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.FormatVersion), p.FormatVersion)
|
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.IndexKeyIsUserKey), p.IndexKeyIsUserKey)
|
|
||||||
if p.IndexPartitions != 0 {
|
if p.IndexPartitions != 0 {
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.IndexPartitions), p.IndexPartitions)
|
p.saveUvarint(m, unsafe.Offsetof(p.IndexPartitions), p.IndexPartitions)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.TopLevelIndexSize), p.TopLevelIndexSize)
|
p.saveUvarint(m, unsafe.Offsetof(p.TopLevelIndexSize), p.TopLevelIndexSize)
|
||||||
}
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.IndexSize), p.IndexSize)
|
p.saveUvarint(m, unsafe.Offsetof(p.IndexSize), p.IndexSize)
|
||||||
p.saveUint32(m, unsafe.Offsetof(p.IndexType), p.IndexType)
|
p.saveUint32(m, unsafe.Offsetof(p.IndexType), p.IndexType)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.IndexValueIsDeltaEncoded), p.IndexValueIsDeltaEncoded)
|
if p.IsStrictObsolete {
|
||||||
|
p.saveBool(m, unsafe.Offsetof(p.IsStrictObsolete), p.IsStrictObsolete)
|
||||||
|
}
|
||||||
if p.MergerName != "" {
|
if p.MergerName != "" {
|
||||||
p.saveString(m, unsafe.Offsetof(p.MergerName), p.MergerName)
|
p.saveString(m, unsafe.Offsetof(p.MergerName), p.MergerName)
|
||||||
}
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumDataBlocks), p.NumDataBlocks)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumDataBlocks), p.NumDataBlocks)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumEntries), p.NumEntries)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumEntries), p.NumEntries)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumDeletions), p.NumDeletions)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumDeletions), p.NumDeletions)
|
||||||
|
if p.NumSizedDeletions > 0 {
|
||||||
|
p.saveUvarint(m, unsafe.Offsetof(p.NumSizedDeletions), p.NumSizedDeletions)
|
||||||
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumMergeOperands), p.NumMergeOperands)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumMergeOperands), p.NumMergeOperands)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeDeletions), p.NumRangeDeletions)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeDeletions), p.NumRangeDeletions)
|
||||||
// NB: We only write out the RawTombstoneKeySize for Pebble formats. This
|
// NB: We only write out some properties for Pebble formats. This isn't
|
||||||
// isn't strictly necessary because unrecognized properties are interpreted
|
// strictly necessary because unrecognized properties are interpreted as
|
||||||
// as user-defined properties, however writing them prevents byte-for-byte
|
// user-defined properties, however writing them prevents byte-for-byte
|
||||||
// equivalence with RocksDB files that some of our testing requires.
|
// equivalence with RocksDB files that some of our testing requires.
|
||||||
if p.RawPointTombstoneKeySize > 0 && tblFormat >= TableFormatPebblev1 {
|
if p.RawPointTombstoneKeySize > 0 && tblFormat >= TableFormatPebblev1 {
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.RawPointTombstoneKeySize), p.RawPointTombstoneKeySize)
|
p.saveUvarint(m, unsafe.Offsetof(p.RawPointTombstoneKeySize), p.RawPointTombstoneKeySize)
|
||||||
}
|
}
|
||||||
|
if p.RawPointTombstoneValueSize > 0 {
|
||||||
|
p.saveUvarint(m, unsafe.Offsetof(p.RawPointTombstoneValueSize), p.RawPointTombstoneValueSize)
|
||||||
|
}
|
||||||
if p.NumRangeKeys() > 0 {
|
if p.NumRangeKeys() > 0 {
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeKeyDels), p.NumRangeKeyDels)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeKeyDels), p.NumRangeKeyDels)
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeKeySets), p.NumRangeKeySets)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumRangeKeySets), p.NumRangeKeySets)
|
||||||
@@ -370,7 +347,6 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) {
|
|||||||
if p.NumValuesInValueBlocks > 0 {
|
if p.NumValuesInValueBlocks > 0 {
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.NumValuesInValueBlocks), p.NumValuesInValueBlocks)
|
p.saveUvarint(m, unsafe.Offsetof(p.NumValuesInValueBlocks), p.NumValuesInValueBlocks)
|
||||||
}
|
}
|
||||||
p.saveUvarint(m, unsafe.Offsetof(p.OldestKeyTime), p.OldestKeyTime)
|
|
||||||
if p.PrefixExtractorName != "" {
|
if p.PrefixExtractorName != "" {
|
||||||
p.saveString(m, unsafe.Offsetof(p.PrefixExtractorName), p.PrefixExtractorName)
|
p.saveString(m, unsafe.Offsetof(p.PrefixExtractorName), p.PrefixExtractorName)
|
||||||
}
|
}
|
||||||
@@ -390,6 +366,16 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) {
|
|||||||
}
|
}
|
||||||
p.saveBool(m, unsafe.Offsetof(p.WholeKeyFiltering), p.WholeKeyFiltering)
|
p.saveBool(m, unsafe.Offsetof(p.WholeKeyFiltering), p.WholeKeyFiltering)
|
||||||
|
|
||||||
|
if tblFormat < TableFormatPebblev1 {
|
||||||
|
m["rocksdb.column.family.id"] = binary.AppendUvarint([]byte(nil), math.MaxInt32)
|
||||||
|
m["rocksdb.fixed.key.length"] = []byte{0x00}
|
||||||
|
m["rocksdb.index.key.is.user.key"] = []byte{0x00}
|
||||||
|
m["rocksdb.index.value.is.delta.encoded"] = []byte{0x00}
|
||||||
|
m["rocksdb.oldest.key.time"] = []byte{0x00}
|
||||||
|
m["rocksdb.creation.time"] = []byte{0x00}
|
||||||
|
m["rocksdb.format.version"] = []byte{0x00}
|
||||||
|
}
|
||||||
|
|
||||||
keys := make([]string, 0, len(m))
|
keys := make([]string, 0, len(m))
|
||||||
for key := range m {
|
for key := range m {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
|
|||||||
95
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
95
vendor/github.com/cockroachdb/pebble/sstable/reader.go
generated
vendored
@@ -323,6 +323,8 @@ type singleLevelIterator struct {
|
|||||||
// is high).
|
// is high).
|
||||||
useFilter bool
|
useFilter bool
|
||||||
lastBloomFilterMatched bool
|
lastBloomFilterMatched bool
|
||||||
|
|
||||||
|
hideObsoletePoints bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// singleLevelIterator implements the base.InternalIterator interface.
|
// singleLevelIterator implements the base.InternalIterator interface.
|
||||||
@@ -404,7 +406,7 @@ func (i *singleLevelIterator) init(
|
|||||||
v *virtualState,
|
v *virtualState,
|
||||||
lower, upper []byte,
|
lower, upper []byte,
|
||||||
filterer *BlockPropertiesFilterer,
|
filterer *BlockPropertiesFilterer,
|
||||||
useFilter bool,
|
useFilter, hideObsoletePoints bool,
|
||||||
stats *base.InternalIteratorStats,
|
stats *base.InternalIteratorStats,
|
||||||
rp ReaderProvider,
|
rp ReaderProvider,
|
||||||
) error {
|
) error {
|
||||||
@@ -428,7 +430,8 @@ func (i *singleLevelIterator) init(
|
|||||||
i.reader = r
|
i.reader = r
|
||||||
i.cmp = r.Compare
|
i.cmp = r.Compare
|
||||||
i.stats = stats
|
i.stats = stats
|
||||||
err = i.index.initHandle(i.cmp, indexH, r.Properties.GlobalSeqNum)
|
i.hideObsoletePoints = hideObsoletePoints
|
||||||
|
err = i.index.initHandle(i.cmp, indexH, r.Properties.GlobalSeqNum, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// blockIter.Close releases indexH and always returns a nil error
|
// blockIter.Close releases indexH and always returns a nil error
|
||||||
_ = i.index.Close()
|
_ = i.index.Close()
|
||||||
@@ -464,9 +467,9 @@ func (i *singleLevelIterator) init(
|
|||||||
// setupForCompaction sets up the singleLevelIterator for use with compactionIter.
|
// setupForCompaction sets up the singleLevelIterator for use with compactionIter.
|
||||||
// Currently, it skips readahead ramp-up. It should be called after init is called.
|
// Currently, it skips readahead ramp-up. It should be called after init is called.
|
||||||
func (i *singleLevelIterator) setupForCompaction() {
|
func (i *singleLevelIterator) setupForCompaction() {
|
||||||
i.dataRH.MaxReadahead()
|
i.dataRH.SetupForCompaction()
|
||||||
if i.vbRH != nil {
|
if i.vbRH != nil {
|
||||||
i.vbRH.MaxReadahead()
|
i.vbRH.SetupForCompaction()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +569,7 @@ func (i *singleLevelIterator) loadBlock(dir int8) loadBlockResult {
|
|||||||
i.err = err
|
i.err = err
|
||||||
return loadBlockFailed
|
return loadBlockFailed
|
||||||
}
|
}
|
||||||
i.err = i.data.initHandle(i.cmp, block, i.reader.Properties.GlobalSeqNum)
|
i.err = i.data.initHandle(i.cmp, block, i.reader.Properties.GlobalSeqNum, i.hideObsoletePoints)
|
||||||
if i.err != nil {
|
if i.err != nil {
|
||||||
// The block is partially loaded, and we don't want it to appear valid.
|
// The block is partially loaded, and we don't want it to appear valid.
|
||||||
i.data.invalidate()
|
i.data.invalidate()
|
||||||
@@ -653,12 +656,13 @@ func (i *singleLevelIterator) resolveMaybeExcluded(dir int8) intersectsResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *singleLevelIterator) initBoundsForAlreadyLoadedBlock() {
|
func (i *singleLevelIterator) initBoundsForAlreadyLoadedBlock() {
|
||||||
if i.data.firstKey.UserKey == nil {
|
if i.data.getFirstUserKey() == nil {
|
||||||
panic("initBoundsForAlreadyLoadedBlock must not be called on empty or corrupted block")
|
panic("initBoundsForAlreadyLoadedBlock must not be called on empty or corrupted block")
|
||||||
}
|
}
|
||||||
i.blockLower = i.lower
|
i.blockLower = i.lower
|
||||||
if i.blockLower != nil {
|
if i.blockLower != nil {
|
||||||
if i.data.firstKey.UserKey != nil && i.cmp(i.blockLower, i.data.firstKey.UserKey) < 0 {
|
firstUserKey := i.data.getFirstUserKey()
|
||||||
|
if firstUserKey != nil && i.cmp(i.blockLower, firstUserKey) < 0 {
|
||||||
// The lower-bound is less than the first key in the block. No need
|
// The lower-bound is less than the first key in the block. No need
|
||||||
// to check the lower-bound again for this block.
|
// to check the lower-bound again for this block.
|
||||||
i.blockLower = nil
|
i.blockLower = nil
|
||||||
@@ -1066,7 +1070,7 @@ func (i *singleLevelIterator) SeekLT(
|
|||||||
|
|
||||||
var dontSeekWithinBlock bool
|
var dontSeekWithinBlock bool
|
||||||
if !i.data.isDataInvalidated() && !i.index.isDataInvalidated() && i.data.valid() && i.index.valid() &&
|
if !i.data.isDataInvalidated() && !i.index.isDataInvalidated() && i.data.valid() && i.index.valid() &&
|
||||||
boundsCmp < 0 && i.cmp(i.data.firstKey.UserKey, key) < 0 {
|
boundsCmp < 0 && i.cmp(i.data.getFirstUserKey(), key) < 0 {
|
||||||
// Fast-path: The bounds have moved backward, and this SeekLT is
|
// Fast-path: The bounds have moved backward, and this SeekLT is
|
||||||
// respecting the upper bound (guaranteed by Iterator). We know that
|
// respecting the upper bound (guaranteed by Iterator). We know that
|
||||||
// the iterator must already be positioned within or just outside the
|
// the iterator must already be positioned within or just outside the
|
||||||
@@ -1314,7 +1318,7 @@ func (i *singleLevelIterator) NextPrefix(succKey []byte) (*InternalKey, base.Laz
|
|||||||
if i.err != nil {
|
if i.err != nil {
|
||||||
return nil, base.LazyValue{}
|
return nil, base.LazyValue{}
|
||||||
}
|
}
|
||||||
if key, val := i.data.nextPrefix(succKey); key != nil {
|
if key, val := i.data.NextPrefix(succKey); key != nil {
|
||||||
if i.blockUpper != nil {
|
if i.blockUpper != nil {
|
||||||
cmp := i.cmp(key.UserKey, i.blockUpper)
|
cmp := i.cmp(key.UserKey, i.blockUpper)
|
||||||
if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
|
if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 {
|
||||||
@@ -1763,8 +1767,7 @@ func (i *twoLevelIterator) loadIndex(dir int8) loadBlockResult {
|
|||||||
i.err = err
|
i.err = err
|
||||||
return loadBlockFailed
|
return loadBlockFailed
|
||||||
}
|
}
|
||||||
if i.err = i.index.initHandle(
|
if i.err = i.index.initHandle(i.cmp, indexBlock, i.reader.Properties.GlobalSeqNum, false); i.err == nil {
|
||||||
i.cmp, indexBlock, i.reader.Properties.GlobalSeqNum); i.err == nil {
|
|
||||||
return loadBlockOK
|
return loadBlockOK
|
||||||
}
|
}
|
||||||
return loadBlockFailed
|
return loadBlockFailed
|
||||||
@@ -1842,7 +1845,7 @@ func (i *twoLevelIterator) init(
|
|||||||
v *virtualState,
|
v *virtualState,
|
||||||
lower, upper []byte,
|
lower, upper []byte,
|
||||||
filterer *BlockPropertiesFilterer,
|
filterer *BlockPropertiesFilterer,
|
||||||
useFilter bool,
|
useFilter, hideObsoletePoints bool,
|
||||||
stats *base.InternalIteratorStats,
|
stats *base.InternalIteratorStats,
|
||||||
rp ReaderProvider,
|
rp ReaderProvider,
|
||||||
) error {
|
) error {
|
||||||
@@ -1867,7 +1870,8 @@ func (i *twoLevelIterator) init(
|
|||||||
i.reader = r
|
i.reader = r
|
||||||
i.cmp = r.Compare
|
i.cmp = r.Compare
|
||||||
i.stats = stats
|
i.stats = stats
|
||||||
err = i.topLevelIndex.initHandle(i.cmp, topLevelIndexH, r.Properties.GlobalSeqNum)
|
i.hideObsoletePoints = hideObsoletePoints
|
||||||
|
err = i.topLevelIndex.initHandle(i.cmp, topLevelIndexH, r.Properties.GlobalSeqNum, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// blockIter.Close releases topLevelIndexH and always returns a nil error
|
// blockIter.Close releases topLevelIndexH and always returns a nil error
|
||||||
_ = i.topLevelIndex.Close()
|
_ = i.topLevelIndex.Close()
|
||||||
@@ -2907,21 +2911,20 @@ func (v *VirtualReader) NewCompactionIter(
|
|||||||
return v.reader.newCompactionIter(bytesIterated, rp, &v.vState)
|
return v.reader.newCompactionIter(bytesIterated, rp, &v.vState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterWithBlockPropertyFiltersAndContext wraps
|
// NewIterWithBlockPropertyFiltersAndContextEtc wraps
|
||||||
// Reader.NewIterWithBlockPropertyFiltersAndContext. We assume that the passed
|
// Reader.NewIterWithBlockPropertyFiltersAndContext. We assume that the passed
|
||||||
// in [lower, upper) bounds will have at least some overlap with the virtual
|
// in [lower, upper) bounds will have at least some overlap with the virtual
|
||||||
// sstable bounds. No overlap is not currently supported in the iterator.
|
// sstable bounds. No overlap is not currently supported in the iterator.
|
||||||
func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContext(
|
func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
lower, upper []byte,
|
lower, upper []byte,
|
||||||
filterer *BlockPropertiesFilterer,
|
filterer *BlockPropertiesFilterer,
|
||||||
useFilterBlock bool,
|
hideObsoletePoints, useFilterBlock bool,
|
||||||
stats *base.InternalIteratorStats,
|
stats *base.InternalIteratorStats,
|
||||||
rp ReaderProvider,
|
rp ReaderProvider,
|
||||||
) (Iterator, error) {
|
) (Iterator, error) {
|
||||||
return v.reader.newIterWithBlockPropertyFiltersAndContext(
|
return v.reader.newIterWithBlockPropertyFiltersAndContext(
|
||||||
ctx,
|
ctx, lower, upper, filterer, hideObsoletePoints, useFilterBlock, stats, rp, &v.vState,
|
||||||
lower, upper, filterer, useFilterBlock, stats, rp, &v.vState,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3060,31 +3063,51 @@ func (r *Reader) NewIterWithBlockPropertyFilters(
|
|||||||
) (Iterator, error) {
|
) (Iterator, error) {
|
||||||
return r.newIterWithBlockPropertyFiltersAndContext(
|
return r.newIterWithBlockPropertyFiltersAndContext(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
lower, upper, filterer, useFilterBlock, stats, rp, nil,
|
lower, upper, filterer, false, useFilterBlock, stats, rp, nil,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIterWithBlockPropertyFiltersAndContext is similar to
|
// NewIterWithBlockPropertyFiltersAndContextEtc is similar to
|
||||||
// NewIterWithBlockPropertyFilters and additionally accepts a context for
|
// NewIterWithBlockPropertyFilters and additionally accepts a context for
|
||||||
// tracing.
|
// tracing.
|
||||||
func (r *Reader) NewIterWithBlockPropertyFiltersAndContext(
|
//
|
||||||
|
// If hideObsoletePoints, the callee assumes that filterer already includes
|
||||||
|
// obsoleteKeyBlockPropertyFilter. The caller can satisfy this contract by
|
||||||
|
// first calling TryAddBlockPropertyFilterForHideObsoletePoints.
|
||||||
|
func (r *Reader) NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
lower, upper []byte,
|
lower, upper []byte,
|
||||||
filterer *BlockPropertiesFilterer,
|
filterer *BlockPropertiesFilterer,
|
||||||
useFilterBlock bool,
|
hideObsoletePoints, useFilterBlock bool,
|
||||||
stats *base.InternalIteratorStats,
|
stats *base.InternalIteratorStats,
|
||||||
rp ReaderProvider,
|
rp ReaderProvider,
|
||||||
) (Iterator, error) {
|
) (Iterator, error) {
|
||||||
return r.newIterWithBlockPropertyFiltersAndContext(
|
return r.newIterWithBlockPropertyFiltersAndContext(
|
||||||
ctx,
|
ctx, lower, upper, filterer, hideObsoletePoints, useFilterBlock, stats, rp, nil,
|
||||||
lower, upper, filterer, useFilterBlock, stats, rp, nil,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TryAddBlockPropertyFilterForHideObsoletePoints is expected to be called
|
||||||
|
// before the call to NewIterWithBlockPropertyFiltersAndContextEtc, to get the
|
||||||
|
// value of hideObsoletePoints and potentially add a block property filter.
|
||||||
|
func (r *Reader) TryAddBlockPropertyFilterForHideObsoletePoints(
|
||||||
|
snapshotForHideObsoletePoints uint64,
|
||||||
|
fileLargestSeqNum uint64,
|
||||||
|
pointKeyFilters []BlockPropertyFilter,
|
||||||
|
) (hideObsoletePoints bool, filters []BlockPropertyFilter) {
|
||||||
|
hideObsoletePoints = r.tableFormat >= TableFormatPebblev4 &&
|
||||||
|
snapshotForHideObsoletePoints > fileLargestSeqNum
|
||||||
|
if hideObsoletePoints {
|
||||||
|
pointKeyFilters = append(pointKeyFilters, obsoleteKeyBlockPropertyFilter{})
|
||||||
|
}
|
||||||
|
return hideObsoletePoints, pointKeyFilters
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) newIterWithBlockPropertyFiltersAndContext(
|
func (r *Reader) newIterWithBlockPropertyFiltersAndContext(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
lower, upper []byte,
|
lower, upper []byte,
|
||||||
filterer *BlockPropertiesFilterer,
|
filterer *BlockPropertiesFilterer,
|
||||||
|
hideObsoletePoints bool,
|
||||||
useFilterBlock bool,
|
useFilterBlock bool,
|
||||||
stats *base.InternalIteratorStats,
|
stats *base.InternalIteratorStats,
|
||||||
rp ReaderProvider,
|
rp ReaderProvider,
|
||||||
@@ -3095,7 +3118,7 @@ func (r *Reader) newIterWithBlockPropertyFiltersAndContext(
|
|||||||
// until the final iterator closes.
|
// until the final iterator closes.
|
||||||
if r.Properties.IndexType == twoLevelIndex {
|
if r.Properties.IndexType == twoLevelIndex {
|
||||||
i := twoLevelIterPool.Get().(*twoLevelIterator)
|
i := twoLevelIterPool.Get().(*twoLevelIterator)
|
||||||
err := i.init(ctx, r, v, lower, upper, filterer, useFilterBlock, stats, rp)
|
err := i.init(ctx, r, v, lower, upper, filterer, useFilterBlock, hideObsoletePoints, stats, rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -3103,7 +3126,7 @@ func (r *Reader) newIterWithBlockPropertyFiltersAndContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
i := singleLevelIterPool.Get().(*singleLevelIterator)
|
i := singleLevelIterPool.Get().(*singleLevelIterator)
|
||||||
err := i.init(ctx, r, v, lower, upper, filterer, useFilterBlock, stats, rp)
|
err := i.init(ctx, r, v, lower, upper, filterer, useFilterBlock, hideObsoletePoints, stats, rp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -3135,7 +3158,8 @@ func (r *Reader) newCompactionIter(
|
|||||||
err := i.init(
|
err := i.init(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
r, v, nil /* lower */, nil /* upper */, nil,
|
r, v, nil /* lower */, nil /* upper */, nil,
|
||||||
false /* useFilter */, nil /* stats */, rp,
|
false /* useFilter */, false, /* hideObsoletePoints */
|
||||||
|
nil /* stats */, rp,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -3149,7 +3173,8 @@ func (r *Reader) newCompactionIter(
|
|||||||
i := singleLevelIterPool.Get().(*singleLevelIterator)
|
i := singleLevelIterPool.Get().(*singleLevelIterator)
|
||||||
err := i.init(
|
err := i.init(
|
||||||
context.Background(), r, v, nil /* lower */, nil, /* upper */
|
context.Background(), r, v, nil /* lower */, nil, /* upper */
|
||||||
nil, false /* useFilter */, nil /* stats */, rp,
|
nil, false /* useFilter */, false, /* hideObsoletePoints */
|
||||||
|
nil /* stats */, rp,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -3175,8 +3200,8 @@ func (r *Reader) NewRawRangeDelIter() (keyspan.FragmentIterator, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
i := &fragmentBlockIter{}
|
i := &fragmentBlockIter{elideSameSeqnum: true}
|
||||||
if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum); err != nil {
|
if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
@@ -3197,7 +3222,7 @@ func (r *Reader) NewRawRangeKeyIter() (keyspan.FragmentIterator, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
i := rangeKeyFragmentBlockIterPool.Get().(*rangeKeyFragmentBlockIter)
|
i := rangeKeyFragmentBlockIterPool.Get().(*rangeKeyFragmentBlockIter)
|
||||||
if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum); err != nil {
|
if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
@@ -3357,7 +3382,7 @@ func (r *Reader) transformRangeDelV1(b []byte) ([]byte, error) {
|
|||||||
// tombstones. We need properly fragmented and sorted range tombstones in
|
// tombstones. We need properly fragmented and sorted range tombstones in
|
||||||
// order to serve from them directly.
|
// order to serve from them directly.
|
||||||
iter := &blockIter{}
|
iter := &blockIter{}
|
||||||
if err := iter.init(r.Compare, b, r.Properties.GlobalSeqNum); err != nil {
|
if err := iter.init(r.Compare, b, r.Properties.GlobalSeqNum, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var tombstones []keyspan.Span
|
var tombstones []keyspan.Span
|
||||||
@@ -3444,7 +3469,7 @@ func (r *Reader) readMetaindex(metaindexBH BlockHandle) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r.propertiesBH = bh
|
r.propertiesBH = bh
|
||||||
err := r.Properties.load(b.Get(), bh.Offset)
|
err := r.Properties.load(b.Get(), bh.Offset, r.opts.DeniedUserProperties)
|
||||||
b.Release()
|
b.Release()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -3549,7 +3574,8 @@ func (r *Reader) Layout() (*Layout, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := iter.init(r.Compare, subIndex.Get(), 0 /* globalSeqNum */); err != nil {
|
if err := iter.init(r.Compare, subIndex.Get(), 0, /* globalSeqNum */
|
||||||
|
false /* hideObsoletePoints */); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for key, value := iter.First(); key != nil; key, value = iter.Next() {
|
for key, value := iter.First(); key != nil; key, value = iter.Next() {
|
||||||
@@ -3867,6 +3893,7 @@ func NewReader(f objstorage.Readable, o ReaderOptions, extraOpts ...ReaderOption
|
|||||||
if r.err != nil {
|
if r.err != nil {
|
||||||
return nil, r.Close()
|
return nil, r.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
37
vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go
generated
vendored
@@ -43,6 +43,9 @@ func RewriteKeySuffixes(
|
|||||||
// modification as they are not affected by the suffix, while block and table
|
// modification as they are not affected by the suffix, while block and table
|
||||||
// properties are only minimally recomputed.
|
// properties are only minimally recomputed.
|
||||||
//
|
//
|
||||||
|
// TODO(sumeer): document limitations, if any, due to this limited
|
||||||
|
// re-computation of properties (is there any loss of fidelity?).
|
||||||
|
//
|
||||||
// Any block and table property collectors configured in the WriterOptions must
|
// Any block and table property collectors configured in the WriterOptions must
|
||||||
// implement SuffixReplaceableTableCollector/SuffixReplaceableBlockCollector.
|
// implement SuffixReplaceableTableCollector/SuffixReplaceableBlockCollector.
|
||||||
//
|
//
|
||||||
@@ -50,6 +53,26 @@ func RewriteKeySuffixes(
|
|||||||
// same TableFormat as the input, which is returned in case the caller wants
|
// same TableFormat as the input, which is returned in case the caller wants
|
||||||
// to do some error checking. Suffix rewriting is meant to be efficient, and
|
// to do some error checking. Suffix rewriting is meant to be efficient, and
|
||||||
// allowing changes in the TableFormat detracts from that efficiency.
|
// allowing changes in the TableFormat detracts from that efficiency.
|
||||||
|
//
|
||||||
|
// Any obsolete bits that key-value pairs may be annotated with are ignored
|
||||||
|
// and lost during the rewrite. Additionally, the output sstable has the
|
||||||
|
// pebble.obsolete.is_strict property set to false. These limitations could be
|
||||||
|
// removed if needed. The current use case for
|
||||||
|
// RewriteKeySuffixesAndReturnFormat in CockroachDB is for MVCC-compliant file
|
||||||
|
// ingestion, where these files do not contain RANGEDELs and have one
|
||||||
|
// key-value pair per userkey -- so they trivially satisfy the strict
|
||||||
|
// criteria, and we don't need the obsolete bit as a performance optimization.
|
||||||
|
// For disaggregated storage, strict obsolete sstables are needed for L5 and
|
||||||
|
// L6, but at the time of writing, we expect such MVCC-compliant file
|
||||||
|
// ingestion to only ingest into levels L4 and higher. If this changes, we can
|
||||||
|
// do one of two things to get rid of this limitation:
|
||||||
|
// - Validate that there are no duplicate userkeys and no RANGEDELs/MERGEs
|
||||||
|
// in the sstable to be rewritten. Validating no duplicate userkeys is
|
||||||
|
// non-trivial when rewriting blocks in parallel, so we could encode the
|
||||||
|
// pre-existing condition in the (existing) SnapshotPinnedKeys property --
|
||||||
|
// we need to update the external sst writer to calculate and encode this
|
||||||
|
// property.
|
||||||
|
// - Preserve the obsolete bit (with changes to the blockIter).
|
||||||
func RewriteKeySuffixesAndReturnFormat(
|
func RewriteKeySuffixesAndReturnFormat(
|
||||||
sst []byte,
|
sst []byte,
|
||||||
rOpts ReaderOptions,
|
rOpts ReaderOptions,
|
||||||
@@ -184,7 +207,7 @@ func rewriteBlocks(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := iter.init(r.Compare, inputBlock, r.Properties.GlobalSeqNum); err != nil {
|
if err := iter.init(r.Compare, inputBlock, r.Properties.GlobalSeqNum, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,8 +456,13 @@ func (c copyFilterWriter) policyName() string { return c.origPolicyName }
|
|||||||
// RewriteKeySuffixesViaWriter is similar to RewriteKeySuffixes but uses just a
|
// RewriteKeySuffixesViaWriter is similar to RewriteKeySuffixes but uses just a
|
||||||
// single loop over the Reader that writes each key to the Writer with the new
|
// single loop over the Reader that writes each key to the Writer with the new
|
||||||
// suffix. The is significantly slower than the parallelized rewriter, and does
|
// suffix. The is significantly slower than the parallelized rewriter, and does
|
||||||
// more work to rederive filters, props, etc, however re-doing that work makes
|
// more work to rederive filters, props, etc.
|
||||||
// it less restrictive -- props no longer need to
|
//
|
||||||
|
// Any obsolete bits that key-value pairs may be annotated with are ignored
|
||||||
|
// and lost during the rewrite. Some of the obsolete bits may be recreated --
|
||||||
|
// specifically when there are multiple keys with the same user key.
|
||||||
|
// Additionally, the output sstable has the pebble.obsolete.is_strict property
|
||||||
|
// set to false. See the longer comment at RewriteKeySuffixesAndReturnFormat.
|
||||||
func RewriteKeySuffixesViaWriter(
|
func RewriteKeySuffixesViaWriter(
|
||||||
r *Reader, out objstorage.Writable, o WriterOptions, from, to []byte,
|
r *Reader, out objstorage.Writable, o WriterOptions, from, to []byte,
|
||||||
) (*WriterMetadata, error) {
|
) (*WriterMetadata, error) {
|
||||||
@@ -442,6 +470,7 @@ func RewriteKeySuffixesViaWriter(
|
|||||||
return nil, errors.New("a valid splitter is required to rewrite suffixes")
|
return nil, errors.New("a valid splitter is required to rewrite suffixes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.IsStrictObsolete = false
|
||||||
w := NewWriter(out, o)
|
w := NewWriter(out, o)
|
||||||
defer func() {
|
defer func() {
|
||||||
if w != nil {
|
if w != nil {
|
||||||
@@ -472,7 +501,7 @@ func RewriteKeySuffixesViaWriter(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if w.addPoint(scratch, val); err != nil {
|
if w.addPoint(scratch, val, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
k, v = i.Next()
|
k, v = i.Next()
|
||||||
|
|||||||
21
vendor/github.com/cockroachdb/pebble/sstable/table.go
generated
vendored
21
vendor/github.com/cockroachdb/pebble/sstable/table.go
generated
vendored
@@ -160,8 +160,27 @@ The metaindex block also contains block handles as values, with keys being
|
|||||||
the names of the meta blocks.
|
the names of the meta blocks.
|
||||||
|
|
||||||
For a description of value blocks and the meta value index block, see
|
For a description of value blocks and the meta value index block, see
|
||||||
value_block.go
|
value_block.go.
|
||||||
|
|
||||||
|
Data blocks have some additional features:
|
||||||
|
- For TableFormatPebblev3 onwards:
|
||||||
|
- For SETs, the value has a 1 byte value prefix, which indicates whether the
|
||||||
|
value is inline, or in a separate value block, and indicates whether the
|
||||||
|
prefix of the userkey (as defined by split) has changed or not. See
|
||||||
|
value_block.go for details.
|
||||||
|
- The most significant bit of the restart points is used to indicate whether
|
||||||
|
userkey prefix has changed since the last restart point. See the detailed
|
||||||
|
comment in blockWriter.
|
||||||
|
- The maximum length of the "shared prefix" when encoding the key, is the
|
||||||
|
length of the prefix of the userkey (as defined by split) of the previous
|
||||||
|
key.
|
||||||
|
|
||||||
|
- For TableFormatPebblev4 onwards:
|
||||||
|
- The key kinds may be altered to set the
|
||||||
|
InternalKeyKindSSTableInternalObsoleteBit if the key-value pair is obsolete
|
||||||
|
in the context of that sstable (for a reader that reads at a higher seqnum
|
||||||
|
than the highest seqnum in the sstable). For details, see the comment in
|
||||||
|
format.go.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
307
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
307
vendor/github.com/cockroachdb/pebble/sstable/writer.go
generated
vendored
@@ -132,6 +132,8 @@ type Writer struct {
|
|||||||
separator Separator
|
separator Separator
|
||||||
successor Successor
|
successor Successor
|
||||||
tableFormat TableFormat
|
tableFormat TableFormat
|
||||||
|
isStrictObsolete bool
|
||||||
|
writingToLowestLevel bool
|
||||||
cache *cache.Cache
|
cache *cache.Cache
|
||||||
restartInterval int
|
restartInterval int
|
||||||
checksumType ChecksumType
|
checksumType ChecksumType
|
||||||
@@ -166,6 +168,7 @@ type Writer struct {
|
|||||||
props Properties
|
props Properties
|
||||||
propCollectors []TablePropertyCollector
|
propCollectors []TablePropertyCollector
|
||||||
blockPropCollectors []BlockPropertyCollector
|
blockPropCollectors []BlockPropertyCollector
|
||||||
|
obsoleteCollector obsoleteKeyBlockPropertyCollector
|
||||||
blockPropsEncoder blockPropertiesEncoder
|
blockPropsEncoder blockPropertiesEncoder
|
||||||
// filter accumulates the filter block. If populated, the filter ingests
|
// filter accumulates the filter block. If populated, the filter ingests
|
||||||
// either the output of w.split (i.e. a prefix extractor) if w.split is not
|
// either the output of w.split (i.e. a prefix extractor) if w.split is not
|
||||||
@@ -215,6 +218,8 @@ type pointKeyInfo struct {
|
|||||||
// prefixLen uses w.split, if not nil. Only computed when w.valueBlockWriter
|
// prefixLen uses w.split, if not nil. Only computed when w.valueBlockWriter
|
||||||
// is not nil.
|
// is not nil.
|
||||||
prefixLen int
|
prefixLen int
|
||||||
|
// True iff the point was marked obsolete.
|
||||||
|
isObsolete bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type coordinationState struct {
|
type coordinationState struct {
|
||||||
@@ -673,7 +678,12 @@ func (w *Writer) Set(key, value []byte) error {
|
|||||||
if w.err != nil {
|
if w.err != nil {
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindSet), value)
|
if w.isStrictObsolete {
|
||||||
|
return errors.Errorf("use AddWithForceObsolete")
|
||||||
|
}
|
||||||
|
// forceObsolete is false based on the assumption that no RANGEDELs in the
|
||||||
|
// sstable delete the added points.
|
||||||
|
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindSet), value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the value for the given key. The sequence number is set to
|
// Delete deletes the value for the given key. The sequence number is set to
|
||||||
@@ -685,7 +695,12 @@ func (w *Writer) Delete(key []byte) error {
|
|||||||
if w.err != nil {
|
if w.err != nil {
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindDelete), nil)
|
if w.isStrictObsolete {
|
||||||
|
return errors.Errorf("use AddWithForceObsolete")
|
||||||
|
}
|
||||||
|
// forceObsolete is false based on the assumption that no RANGEDELs in the
|
||||||
|
// sstable delete the added points.
|
||||||
|
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindDelete), nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRange deletes all of the keys (and values) in the range [start,end)
|
// DeleteRange deletes all of the keys (and values) in the range [start,end)
|
||||||
@@ -711,7 +726,13 @@ func (w *Writer) Merge(key, value []byte) error {
|
|||||||
if w.err != nil {
|
if w.err != nil {
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindMerge), value)
|
if w.isStrictObsolete {
|
||||||
|
return errors.Errorf("use AddWithForceObsolete")
|
||||||
|
}
|
||||||
|
// forceObsolete is false based on the assumption that no RANGEDELs in the
|
||||||
|
// sstable that delete the added points. If the user configured this writer
|
||||||
|
// to be strict-obsolete, addPoint will reject the addition of this MERGE.
|
||||||
|
return w.addPoint(base.MakeInternalKey(key, 0, InternalKeyKindMerge), value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a key/value pair to the table being written. For a given Writer,
|
// Add adds a key/value pair to the table being written. For a given Writer,
|
||||||
@@ -721,6 +742,24 @@ func (w *Writer) Merge(key, value []byte) error {
|
|||||||
// point entries. Additionally, range deletion tombstones must be fragmented
|
// point entries. Additionally, range deletion tombstones must be fragmented
|
||||||
// (i.e. by keyspan.Fragmenter).
|
// (i.e. by keyspan.Fragmenter).
|
||||||
func (w *Writer) Add(key InternalKey, value []byte) error {
|
func (w *Writer) Add(key InternalKey, value []byte) error {
|
||||||
|
if w.isStrictObsolete {
|
||||||
|
return errors.Errorf("use AddWithForceObsolete")
|
||||||
|
}
|
||||||
|
return w.AddWithForceObsolete(key, value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWithForceObsolete must be used when writing a strict-obsolete sstable.
|
||||||
|
//
|
||||||
|
// forceObsolete indicates whether the caller has determined that this key is
|
||||||
|
// obsolete even though it may be the latest point key for this userkey. This
|
||||||
|
// should be set to true for keys obsoleted by RANGEDELs, and is required for
|
||||||
|
// strict-obsolete sstables.
|
||||||
|
//
|
||||||
|
// Note that there are two properties, S1 and S2 (see comment in format.go)
|
||||||
|
// that strict-obsolete ssts must satisfy. S2, due to RANGEDELs, is solely the
|
||||||
|
// responsibility of the caller. S1 is solely the responsibility of the
|
||||||
|
// callee.
|
||||||
|
func (w *Writer) AddWithForceObsolete(key InternalKey, value []byte, forceObsolete bool) error {
|
||||||
if w.err != nil {
|
if w.err != nil {
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
@@ -735,7 +774,7 @@ func (w *Writer) Add(key InternalKey, value []byte) error {
|
|||||||
"pebble: range keys must be added via one of the RangeKey* functions")
|
"pebble: range keys must be added via one of the RangeKey* functions")
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
return w.addPoint(key, value)
|
return w.addPoint(key, value, forceObsolete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
|
func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
|
||||||
@@ -745,12 +784,7 @@ func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !w.disableKeyOrderChecks {
|
if !w.disableKeyOrderChecks {
|
||||||
prevPointUserKey := w.dataBlockBuf.dataBlock.curKey
|
prevPointUserKey := w.dataBlockBuf.dataBlock.getCurUserKey()
|
||||||
n := len(prevPointUserKey) - base.InternalTrailerLen
|
|
||||||
if n < 0 {
|
|
||||||
panic("corrupt key in blockWriter buffer")
|
|
||||||
}
|
|
||||||
prevPointUserKey = prevPointUserKey[:n:n]
|
|
||||||
cmpUser := w.compare(prevPointUserKey, key.UserKey)
|
cmpUser := w.compare(prevPointUserKey, key.UserKey)
|
||||||
if cmpUser > 0 || (cmpUser == 0 && prevTrailer <= key.Trailer) {
|
if cmpUser > 0 || (cmpUser == 0 && prevTrailer <= key.Trailer) {
|
||||||
return errors.Errorf(
|
return errors.Errorf(
|
||||||
@@ -762,9 +796,17 @@ func (w *Writer) makeAddPointDecisionV2(key InternalKey) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REQUIRES: at least one point has been written to the Writer.
|
||||||
|
func (w *Writer) getLastPointUserKey() []byte {
|
||||||
|
if w.dataBlockBuf.dataBlock.nEntries == 0 {
|
||||||
|
panic(errors.AssertionFailedf("no point keys added to writer"))
|
||||||
|
}
|
||||||
|
return w.dataBlockBuf.dataBlock.getCurUserKey()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Writer) makeAddPointDecisionV3(
|
func (w *Writer) makeAddPointDecisionV3(
|
||||||
key InternalKey, valueLen int,
|
key InternalKey, valueLen int,
|
||||||
) (setHasSamePrefix bool, writeToValueBlock bool, err error) {
|
) (setHasSamePrefix bool, writeToValueBlock bool, isObsolete bool, err error) {
|
||||||
prevPointKeyInfo := w.lastPointKeyInfo
|
prevPointKeyInfo := w.lastPointKeyInfo
|
||||||
w.lastPointKeyInfo.userKeyLen = len(key.UserKey)
|
w.lastPointKeyInfo.userKeyLen = len(key.UserKey)
|
||||||
w.lastPointKeyInfo.prefixLen = w.lastPointKeyInfo.userKeyLen
|
w.lastPointKeyInfo.prefixLen = w.lastPointKeyInfo.userKeyLen
|
||||||
@@ -772,18 +814,16 @@ func (w *Writer) makeAddPointDecisionV3(
|
|||||||
w.lastPointKeyInfo.prefixLen = w.split(key.UserKey)
|
w.lastPointKeyInfo.prefixLen = w.split(key.UserKey)
|
||||||
}
|
}
|
||||||
w.lastPointKeyInfo.trailer = key.Trailer
|
w.lastPointKeyInfo.trailer = key.Trailer
|
||||||
if w.dataBlockBuf.dataBlock.nEntries == 0 {
|
w.lastPointKeyInfo.isObsolete = false
|
||||||
return false, false, nil
|
if !w.meta.HasPointKeys {
|
||||||
|
return false, false, false, nil
|
||||||
}
|
}
|
||||||
prevPointUserKey := w.dataBlockBuf.dataBlock.curKey
|
keyKind := base.TrailerKind(key.Trailer)
|
||||||
n := len(prevPointUserKey) - base.InternalTrailerLen
|
prevPointUserKey := w.getLastPointUserKey()
|
||||||
if n < 0 {
|
|
||||||
panic("corrupt key in blockWriter buffer")
|
|
||||||
}
|
|
||||||
prevPointUserKey = prevPointUserKey[:n:n]
|
|
||||||
prevPointKey := InternalKey{UserKey: prevPointUserKey, Trailer: prevPointKeyInfo.trailer}
|
prevPointKey := InternalKey{UserKey: prevPointUserKey, Trailer: prevPointKeyInfo.trailer}
|
||||||
considerWriteToValueBlock := base.TrailerKind(prevPointKeyInfo.trailer) == InternalKeyKindSet &&
|
prevKeyKind := base.TrailerKind(prevPointKeyInfo.trailer)
|
||||||
base.TrailerKind(key.Trailer) == InternalKeyKindSet
|
considerWriteToValueBlock := prevKeyKind == InternalKeyKindSet &&
|
||||||
|
keyKind == InternalKeyKindSet
|
||||||
if considerWriteToValueBlock && !w.requiredInPlaceValueBound.IsEmpty() {
|
if considerWriteToValueBlock && !w.requiredInPlaceValueBound.IsEmpty() {
|
||||||
keyPrefix := key.UserKey[:w.lastPointKeyInfo.prefixLen]
|
keyPrefix := key.UserKey[:w.lastPointKeyInfo.prefixLen]
|
||||||
cmpUpper := w.compare(
|
cmpUpper := w.compare(
|
||||||
@@ -812,14 +852,59 @@ func (w *Writer) makeAddPointDecisionV3(
|
|||||||
} else {
|
} else {
|
||||||
cmpUser = w.compare(prevPointUserKey, key.UserKey)
|
cmpUser = w.compare(prevPointUserKey, key.UserKey)
|
||||||
}
|
}
|
||||||
|
keyKindIsSetOrMerge := keyKind == InternalKeyKindSet || keyKind == InternalKeyKindSetWithDelete ||
|
||||||
|
keyKind == InternalKeyKindMerge
|
||||||
|
keyKindIsPointDelete := keyKind == InternalKeyKindDelete ||
|
||||||
|
keyKind == InternalKeyKindSingleDelete || keyKind == InternalKeyKindDeleteSized
|
||||||
|
if !keyKindIsSetOrMerge && !keyKindIsPointDelete {
|
||||||
|
panic(errors.AssertionFailedf("unexpected key kind %s", keyKind.String()))
|
||||||
|
}
|
||||||
|
// If same user key, then the current key is obsolete if any of the
|
||||||
|
// following is true:
|
||||||
|
// C1 The prev key was obsolete.
|
||||||
|
// C2 The prev key was not a MERGE
|
||||||
|
// C3 The current key is not a SET or SETWITHDELETE or MERGE, then it is
|
||||||
|
// obsolete. This rule excludes SET, SETWITHDELETE, MERGE, since they
|
||||||
|
// will be merged with the previous key.
|
||||||
|
//
|
||||||
|
// Regardless of whether it is the same user key or not
|
||||||
|
// C4 The current key is some kind of point delete, and we are writing to
|
||||||
|
// the lowest level, then it is also obsolete.
|
||||||
|
//
|
||||||
|
// C1 ensures that for a user key there is at most one transition from
|
||||||
|
// !obsolete to obsolete. Consider a user key k, for which the first n keys
|
||||||
|
// are not obsolete. We consider the various value of n:
|
||||||
|
//
|
||||||
|
// n = 0: This happens due to forceObsolete being set by the caller, or due
|
||||||
|
// to C4. forceObsolete must only be set due a RANGEDEL, and that RANGEDEL
|
||||||
|
// must also delete all the lower seqnums for the same user key. C4 triggers
|
||||||
|
// due to a point delete and that deletes all the lower seqnums for the same
|
||||||
|
// user key.
|
||||||
|
//
|
||||||
|
// n = 1: This is the common case. It happens when the first key is not a
|
||||||
|
// MERGE, or the current key is some kind of point delete.
|
||||||
|
//
|
||||||
|
// n > 1: This is due to a sequence of MERGE keys, potentially followed by a
|
||||||
|
// single SET or SETWITHDELETE.
|
||||||
|
isObsolete = (cmpUser == 0 &&
|
||||||
|
(prevKeyKind != InternalKeyKindMerge || prevPointKeyInfo.isObsolete || !keyKindIsSetOrMerge)) ||
|
||||||
|
(w.writingToLowestLevel &&
|
||||||
|
(keyKind == InternalKeyKindDelete || keyKind == InternalKeyKindSingleDelete ||
|
||||||
|
keyKind == InternalKeyKindDeleteSized))
|
||||||
|
// TODO(sumeer): storing isObsolete SET and SETWITHDEL in value blocks is
|
||||||
|
// possible, but requires some care in documenting and checking invariants.
|
||||||
|
// There is code that assumes nothing in value blocks because of single MVCC
|
||||||
|
// version (those should be ok). We have to ensure setHasSamePrefix is
|
||||||
|
// correctly initialized here etc.
|
||||||
|
|
||||||
if !w.disableKeyOrderChecks &&
|
if !w.disableKeyOrderChecks &&
|
||||||
(cmpUser > 0 || (cmpUser == 0 && prevPointKeyInfo.trailer <= key.Trailer)) {
|
(cmpUser > 0 || (cmpUser == 0 && prevPointKeyInfo.trailer <= key.Trailer)) {
|
||||||
return false, false, errors.Errorf(
|
return false, false, false, errors.Errorf(
|
||||||
"pebble: keys must be added in strictly increasing order: %s, %s",
|
"pebble: keys must be added in strictly increasing order: %s, %s",
|
||||||
prevPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
prevPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey))
|
||||||
}
|
}
|
||||||
if !considerWriteToValueBlock {
|
if !considerWriteToValueBlock {
|
||||||
return false, false, nil
|
return false, false, isObsolete, nil
|
||||||
}
|
}
|
||||||
// NB: it is possible that cmpUser == 0, i.e., these two SETs have identical
|
// NB: it is possible that cmpUser == 0, i.e., these two SETs have identical
|
||||||
// user keys (because of an open snapshot). This should be the rare case.
|
// user keys (because of an open snapshot). This should be the rare case.
|
||||||
@@ -832,19 +917,24 @@ func (w *Writer) makeAddPointDecisionV3(
|
|||||||
if considerWriteToValueBlock && valueLen <= tinyValueThreshold {
|
if considerWriteToValueBlock && valueLen <= tinyValueThreshold {
|
||||||
considerWriteToValueBlock = false
|
considerWriteToValueBlock = false
|
||||||
}
|
}
|
||||||
return setHasSamePrefix, considerWriteToValueBlock, nil
|
return setHasSamePrefix, considerWriteToValueBlock, isObsolete, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
func (w *Writer) addPoint(key InternalKey, value []byte, forceObsolete bool) error {
|
||||||
|
if w.isStrictObsolete && key.Kind() == InternalKeyKindMerge {
|
||||||
|
return errors.Errorf("MERGE not supported in a strict-obsolete sstable")
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
var setHasSameKeyPrefix, writeToValueBlock, addPrefixToValueStoredWithKey bool
|
var setHasSameKeyPrefix, writeToValueBlock, addPrefixToValueStoredWithKey bool
|
||||||
|
var isObsolete bool
|
||||||
maxSharedKeyLen := len(key.UserKey)
|
maxSharedKeyLen := len(key.UserKey)
|
||||||
if w.valueBlockWriter != nil {
|
if w.valueBlockWriter != nil {
|
||||||
// maxSharedKeyLen is limited to the prefix of the preceding key. If the
|
// maxSharedKeyLen is limited to the prefix of the preceding key. If the
|
||||||
// preceding key was in a different block, then the blockWriter will
|
// preceding key was in a different block, then the blockWriter will
|
||||||
// ignore this maxSharedKeyLen.
|
// ignore this maxSharedKeyLen.
|
||||||
maxSharedKeyLen = w.lastPointKeyInfo.prefixLen
|
maxSharedKeyLen = w.lastPointKeyInfo.prefixLen
|
||||||
setHasSameKeyPrefix, writeToValueBlock, err = w.makeAddPointDecisionV3(key, len(value))
|
setHasSameKeyPrefix, writeToValueBlock, isObsolete, err =
|
||||||
|
w.makeAddPointDecisionV3(key, len(value))
|
||||||
addPrefixToValueStoredWithKey = base.TrailerKind(key.Trailer) == InternalKeyKindSet
|
addPrefixToValueStoredWithKey = base.TrailerKind(key.Trailer) == InternalKeyKindSet
|
||||||
} else {
|
} else {
|
||||||
err = w.makeAddPointDecisionV2(key)
|
err = w.makeAddPointDecisionV2(key)
|
||||||
@@ -852,6 +942,8 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
isObsolete = w.tableFormat >= TableFormatPebblev4 && (isObsolete || forceObsolete)
|
||||||
|
w.lastPointKeyInfo.isObsolete = isObsolete
|
||||||
var valueStoredWithKey []byte
|
var valueStoredWithKey []byte
|
||||||
var prefix valuePrefix
|
var prefix valuePrefix
|
||||||
var valueStoredWithKeyLen int
|
var valueStoredWithKeyLen int
|
||||||
@@ -907,16 +999,19 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if w.tableFormat >= TableFormatPebblev4 {
|
||||||
|
w.obsoleteCollector.AddPoint(isObsolete)
|
||||||
|
}
|
||||||
|
|
||||||
w.maybeAddToFilter(key.UserKey)
|
w.maybeAddToFilter(key.UserKey)
|
||||||
w.dataBlockBuf.dataBlock.addWithOptionalValuePrefix(
|
w.dataBlockBuf.dataBlock.addWithOptionalValuePrefix(
|
||||||
key, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix,
|
key, isObsolete, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix,
|
||||||
setHasSameKeyPrefix)
|
setHasSameKeyPrefix)
|
||||||
|
|
||||||
w.meta.updateSeqNum(key.SeqNum())
|
w.meta.updateSeqNum(key.SeqNum())
|
||||||
|
|
||||||
if !w.meta.HasPointKeys {
|
if !w.meta.HasPointKeys {
|
||||||
k := base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey)
|
k := w.dataBlockBuf.dataBlock.getCurKey()
|
||||||
// NB: We need to ensure that SmallestPoint.UserKey is set, so we create
|
// NB: We need to ensure that SmallestPoint.UserKey is set, so we create
|
||||||
// an InternalKey which is semantically identical to the key, but won't
|
// an InternalKey which is semantically identical to the key, but won't
|
||||||
// have a nil UserKey. We do this, because key.UserKey could be nil, and
|
// have a nil UserKey. We do this, because key.UserKey could be nil, and
|
||||||
@@ -932,7 +1027,22 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error {
|
|||||||
switch key.Kind() {
|
switch key.Kind() {
|
||||||
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
case InternalKeyKindDelete, InternalKeyKindSingleDelete:
|
||||||
w.props.NumDeletions++
|
w.props.NumDeletions++
|
||||||
w.props.RawPointTombstoneKeySize += uint64(key.Size())
|
w.props.RawPointTombstoneKeySize += uint64(len(key.UserKey))
|
||||||
|
case InternalKeyKindDeleteSized:
|
||||||
|
var size uint64
|
||||||
|
if len(value) > 0 {
|
||||||
|
var n int
|
||||||
|
size, n = binary.Uvarint(value)
|
||||||
|
if n <= 0 {
|
||||||
|
w.err = errors.Newf("%s key's value (%x) does not parse as uvarint",
|
||||||
|
errors.Safe(key.Kind().String()), value)
|
||||||
|
return w.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.props.NumDeletions++
|
||||||
|
w.props.NumSizedDeletions++
|
||||||
|
w.props.RawPointTombstoneKeySize += uint64(len(key.UserKey))
|
||||||
|
w.props.RawPointTombstoneValueSize += size
|
||||||
case InternalKeyKindMerge:
|
case InternalKeyKindMerge:
|
||||||
w.props.NumMergeOperands++
|
w.props.NumMergeOperands++
|
||||||
}
|
}
|
||||||
@@ -953,7 +1063,7 @@ func (w *Writer) addTombstone(key InternalKey, value []byte) error {
|
|||||||
if !w.disableKeyOrderChecks && !w.rangeDelV1Format && w.rangeDelBlock.nEntries > 0 {
|
if !w.disableKeyOrderChecks && !w.rangeDelV1Format && w.rangeDelBlock.nEntries > 0 {
|
||||||
// Check that tombstones are being added in fragmented order. If the two
|
// Check that tombstones are being added in fragmented order. If the two
|
||||||
// tombstones overlap, their start and end keys must be identical.
|
// tombstones overlap, their start and end keys must be identical.
|
||||||
prevKey := base.DecodeInternalKey(w.rangeDelBlock.curKey)
|
prevKey := w.rangeDelBlock.getCurKey()
|
||||||
switch c := w.compare(prevKey.UserKey, key.UserKey); {
|
switch c := w.compare(prevKey.UserKey, key.UserKey); {
|
||||||
case c > 0:
|
case c > 0:
|
||||||
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
w.err = errors.Errorf("pebble: keys must be added in order: %s, %s",
|
||||||
@@ -1149,7 +1259,7 @@ func (w *Writer) encodeRangeKeySpan(span keyspan.Span) {
|
|||||||
|
|
||||||
func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
|
func (w *Writer) addRangeKey(key InternalKey, value []byte) error {
|
||||||
if !w.disableKeyOrderChecks && w.rangeKeyBlock.nEntries > 0 {
|
if !w.disableKeyOrderChecks && w.rangeKeyBlock.nEntries > 0 {
|
||||||
prevStartKey := base.DecodeInternalKey(w.rangeKeyBlock.curKey)
|
prevStartKey := w.rangeKeyBlock.getCurKey()
|
||||||
prevEndKey, _, ok := rangekey.DecodeEndKey(prevStartKey.Kind(), w.rangeKeyBlock.curValue)
|
prevEndKey, _, ok := rangekey.DecodeEndKey(prevStartKey.Kind(), w.rangeKeyBlock.curValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
// We panic here as we should have previously decoded and validated this
|
// We panic here as we should have previously decoded and validated this
|
||||||
@@ -1295,7 +1405,7 @@ func (w *Writer) flush(key InternalKey) error {
|
|||||||
// dataBlockBuf is added back to a sync.Pool. In this particular case, the
|
// dataBlockBuf is added back to a sync.Pool. In this particular case, the
|
||||||
// byte slice which supports "sep" will eventually be copied when "sep" is
|
// byte slice which supports "sep" will eventually be copied when "sep" is
|
||||||
// added to the index block.
|
// added to the index block.
|
||||||
prevKey := base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey)
|
prevKey := w.dataBlockBuf.dataBlock.getCurKey()
|
||||||
sep := w.indexEntrySep(prevKey, key, w.dataBlockBuf)
|
sep := w.indexEntrySep(prevKey, key, w.dataBlockBuf)
|
||||||
// We determine that we should flush an index block from the Writer client
|
// We determine that we should flush an index block from the Writer client
|
||||||
// goroutine, but we actually finish the index block from the writeQueue.
|
// goroutine, but we actually finish the index block from the writeQueue.
|
||||||
@@ -1585,7 +1695,7 @@ func (w *Writer) finishIndexBlock(indexBuf *indexBlockBuf, props []byte) error {
|
|||||||
nEntries: indexBuf.block.nEntries, properties: props,
|
nEntries: indexBuf.block.nEntries, properties: props,
|
||||||
}
|
}
|
||||||
w.indexSepAlloc, part.sep = cloneKeyWithBuf(
|
w.indexSepAlloc, part.sep = cloneKeyWithBuf(
|
||||||
base.DecodeInternalKey(indexBuf.block.curKey), w.indexSepAlloc,
|
indexBuf.block.getCurKey(), w.indexSepAlloc,
|
||||||
)
|
)
|
||||||
bk := indexBuf.finish()
|
bk := indexBuf.finish()
|
||||||
if len(w.indexBlockAlloc) < len(bk) {
|
if len(w.indexBlockAlloc) < len(bk) {
|
||||||
@@ -1729,12 +1839,20 @@ func (w *Writer) assertFormatCompatibility() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PebbleDBv3: value blocks.
|
||||||
if (w.props.NumValueBlocks > 0 || w.props.NumValuesInValueBlocks > 0 ||
|
if (w.props.NumValueBlocks > 0 || w.props.NumValuesInValueBlocks > 0 ||
|
||||||
w.props.ValueBlocksSize > 0) && w.tableFormat < TableFormatPebblev3 {
|
w.props.ValueBlocksSize > 0) && w.tableFormat < TableFormatPebblev3 {
|
||||||
return errors.Newf(
|
return errors.Newf(
|
||||||
"table format version %s is less than the minimum required version %s for value blocks",
|
"table format version %s is less than the minimum required version %s for value blocks",
|
||||||
w.tableFormat, TableFormatPebblev3)
|
w.tableFormat, TableFormatPebblev3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PebbleDBv4: DELSIZED tombstones.
|
||||||
|
if w.props.NumSizedDeletions > 0 && w.tableFormat < TableFormatPebblev4 {
|
||||||
|
return errors.Newf(
|
||||||
|
"table format version %s is less than the minimum required version %s for sized deletion tombstones",
|
||||||
|
w.tableFormat, TableFormatPebblev4)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1781,7 +1899,7 @@ func (w *Writer) Close() (err error) {
|
|||||||
// however, if a dataBlock is flushed, then we add a key to the new w.dataBlockBuf in the
|
// however, if a dataBlock is flushed, then we add a key to the new w.dataBlockBuf in the
|
||||||
// addPoint function after the flush occurs.
|
// addPoint function after the flush occurs.
|
||||||
if w.dataBlockBuf.dataBlock.nEntries >= 1 {
|
if w.dataBlockBuf.dataBlock.nEntries >= 1 {
|
||||||
w.meta.SetLargestPointKey(base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey).Clone())
|
w.meta.SetLargestPointKey(w.dataBlockBuf.dataBlock.getCurKey().Clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finish the last data block, or force an empty data block if there
|
// Finish the last data block, or force an empty data block if there
|
||||||
@@ -1795,7 +1913,7 @@ func (w *Writer) Close() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
prevKey := base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey)
|
prevKey := w.dataBlockBuf.dataBlock.getCurKey()
|
||||||
if err := w.addIndexEntrySync(prevKey, InternalKey{}, bhp, w.dataBlockBuf.tmp[:]); err != nil {
|
if err := w.addIndexEntrySync(prevKey, InternalKey{}, bhp, w.dataBlockBuf.tmp[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1874,7 +1992,7 @@ func (w *Writer) Close() (err error) {
|
|||||||
|
|
||||||
var rangeKeyBH BlockHandle
|
var rangeKeyBH BlockHandle
|
||||||
if w.props.NumRangeKeys() > 0 {
|
if w.props.NumRangeKeys() > 0 {
|
||||||
key := base.DecodeInternalKey(w.rangeKeyBlock.curKey)
|
key := w.rangeKeyBlock.getCurKey()
|
||||||
kind := key.Kind()
|
kind := key.Kind()
|
||||||
endKey, _, ok := rangekey.DecodeEndKey(kind, w.rangeKeyBlock.curValue)
|
endKey, _, ok := rangekey.DecodeEndKey(kind, w.rangeKeyBlock.curValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -2063,7 +2181,7 @@ func (o PreviousPointKeyOpt) UnsafeKey() base.InternalKey {
|
|||||||
if o.w.dataBlockBuf.dataBlock.nEntries >= 1 {
|
if o.w.dataBlockBuf.dataBlock.nEntries >= 1 {
|
||||||
// o.w.dataBlockBuf.dataBlock.curKey is guaranteed to point to the last point key
|
// o.w.dataBlockBuf.dataBlock.curKey is guaranteed to point to the last point key
|
||||||
// which was added to the Writer.
|
// which was added to the Writer.
|
||||||
return base.DecodeInternalKey(o.w.dataBlockBuf.dataBlock.curKey)
|
return o.w.dataBlockBuf.dataBlock.getCurKey()
|
||||||
}
|
}
|
||||||
return base.InternalKey{}
|
return base.InternalKey{}
|
||||||
}
|
}
|
||||||
@@ -2092,6 +2210,8 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write
|
|||||||
separator: o.Comparer.Separator,
|
separator: o.Comparer.Separator,
|
||||||
successor: o.Comparer.Successor,
|
successor: o.Comparer.Successor,
|
||||||
tableFormat: o.TableFormat,
|
tableFormat: o.TableFormat,
|
||||||
|
isStrictObsolete: o.IsStrictObsolete,
|
||||||
|
writingToLowestLevel: o.WritingToLowestLevel,
|
||||||
cache: o.Cache,
|
cache: o.Cache,
|
||||||
restartInterval: o.BlockRestartInterval,
|
restartInterval: o.BlockRestartInterval,
|
||||||
checksumType: o.Checksum,
|
checksumType: o.Checksum,
|
||||||
@@ -2158,14 +2278,14 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.props.ColumnFamilyID = math.MaxInt32
|
|
||||||
w.props.ComparerName = o.Comparer.Name
|
w.props.ComparerName = o.Comparer.Name
|
||||||
w.props.CompressionName = o.Compression.String()
|
w.props.CompressionName = o.Compression.String()
|
||||||
w.props.MergerName = o.MergerName
|
w.props.MergerName = o.MergerName
|
||||||
w.props.PropertyCollectorNames = "[]"
|
w.props.PropertyCollectorNames = "[]"
|
||||||
w.props.ExternalFormatVersion = rocksDBExternalFormatVersion
|
w.props.ExternalFormatVersion = rocksDBExternalFormatVersion
|
||||||
|
|
||||||
if len(o.TablePropertyCollectors) > 0 || len(o.BlockPropertyCollectors) > 0 {
|
if len(o.TablePropertyCollectors) > 0 || len(o.BlockPropertyCollectors) > 0 ||
|
||||||
|
w.tableFormat >= TableFormatPebblev4 {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("[")
|
buf.WriteString("[")
|
||||||
if len(o.TablePropertyCollectors) > 0 {
|
if len(o.TablePropertyCollectors) > 0 {
|
||||||
@@ -2178,16 +2298,22 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write
|
|||||||
buf.WriteString(w.propCollectors[i].Name())
|
buf.WriteString(w.propCollectors[i].Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
numBlockPropertyCollectors := len(o.BlockPropertyCollectors)
|
||||||
|
if w.tableFormat >= TableFormatPebblev4 {
|
||||||
|
numBlockPropertyCollectors++
|
||||||
|
}
|
||||||
|
// shortID is a uint8, so we cannot exceed that number of block
|
||||||
|
// property collectors.
|
||||||
|
if numBlockPropertyCollectors > math.MaxUint8 {
|
||||||
|
w.err = errors.New("pebble: too many block property collectors")
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
if numBlockPropertyCollectors > 0 {
|
||||||
|
w.blockPropCollectors = make([]BlockPropertyCollector, numBlockPropertyCollectors)
|
||||||
|
}
|
||||||
if len(o.BlockPropertyCollectors) > 0 {
|
if len(o.BlockPropertyCollectors) > 0 {
|
||||||
// shortID is a uint8, so we cannot exceed that number of block
|
|
||||||
// property collectors.
|
|
||||||
if len(o.BlockPropertyCollectors) > math.MaxUint8 {
|
|
||||||
w.err = errors.New("pebble: too many block property collectors")
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
// The shortID assigned to a collector is the same as its index in
|
// The shortID assigned to a collector is the same as its index in
|
||||||
// this slice.
|
// this slice.
|
||||||
w.blockPropCollectors = make([]BlockPropertyCollector, len(o.BlockPropertyCollectors))
|
|
||||||
for i := range o.BlockPropertyCollectors {
|
for i := range o.BlockPropertyCollectors {
|
||||||
w.blockPropCollectors[i] = o.BlockPropertyCollectors[i]()
|
w.blockPropCollectors[i] = o.BlockPropertyCollectors[i]()
|
||||||
if i > 0 || len(o.TablePropertyCollectors) > 0 {
|
if i > 0 || len(o.TablePropertyCollectors) > 0 {
|
||||||
@@ -2196,6 +2322,13 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write
|
|||||||
buf.WriteString(w.blockPropCollectors[i].Name())
|
buf.WriteString(w.blockPropCollectors[i].Name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if w.tableFormat >= TableFormatPebblev4 {
|
||||||
|
if numBlockPropertyCollectors > 1 || len(o.TablePropertyCollectors) > 0 {
|
||||||
|
buf.WriteString(",")
|
||||||
|
}
|
||||||
|
w.blockPropCollectors[numBlockPropertyCollectors-1] = &w.obsoleteCollector
|
||||||
|
buf.WriteString(w.obsoleteCollector.Name())
|
||||||
|
}
|
||||||
buf.WriteString("]")
|
buf.WriteString("]")
|
||||||
w.props.PropertyCollectorNames = buf.String()
|
w.props.PropertyCollectorNames = buf.String()
|
||||||
}
|
}
|
||||||
@@ -2229,3 +2362,85 @@ func init() {
|
|||||||
}
|
}
|
||||||
private.SSTableInternalProperties = internalGetProperties
|
private.SSTableInternalProperties = internalGetProperties
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type obsoleteKeyBlockPropertyCollector struct {
|
||||||
|
blockIsNonObsolete bool
|
||||||
|
indexIsNonObsolete bool
|
||||||
|
tableIsNonObsolete bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeNonObsolete(isNonObsolete bool, buf []byte) []byte {
|
||||||
|
if isNonObsolete {
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
return append(buf, 't')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) Name() string {
|
||||||
|
return "obsolete-key"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) Add(key InternalKey, value []byte) error {
|
||||||
|
// Ignore.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) AddPoint(isObsolete bool) {
|
||||||
|
o.blockIsNonObsolete = o.blockIsNonObsolete || !isObsolete
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) FinishDataBlock(buf []byte) ([]byte, error) {
|
||||||
|
o.tableIsNonObsolete = o.tableIsNonObsolete || o.blockIsNonObsolete
|
||||||
|
return encodeNonObsolete(o.blockIsNonObsolete, buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) AddPrevDataBlockToIndexBlock() {
|
||||||
|
o.indexIsNonObsolete = o.indexIsNonObsolete || o.blockIsNonObsolete
|
||||||
|
o.blockIsNonObsolete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) FinishIndexBlock(buf []byte) ([]byte, error) {
|
||||||
|
indexIsNonObsolete := o.indexIsNonObsolete
|
||||||
|
o.indexIsNonObsolete = false
|
||||||
|
return encodeNonObsolete(indexIsNonObsolete, buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) FinishTable(buf []byte) ([]byte, error) {
|
||||||
|
return encodeNonObsolete(o.tableIsNonObsolete, buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *obsoleteKeyBlockPropertyCollector) UpdateKeySuffixes(
|
||||||
|
oldProp []byte, oldSuffix, newSuffix []byte,
|
||||||
|
) error {
|
||||||
|
_, err := propToIsObsolete(oldProp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Suffix rewriting currently loses the obsolete bit.
|
||||||
|
o.blockIsNonObsolete = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: obsoleteKeyBlockPropertyFilter is stateless. This aspect of the filter
|
||||||
|
// is used in table_cache.go for in-place modification of a filters slice.
|
||||||
|
type obsoleteKeyBlockPropertyFilter struct{}
|
||||||
|
|
||||||
|
func (o obsoleteKeyBlockPropertyFilter) Name() string {
|
||||||
|
return "obsolete-key"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersects returns true if the set represented by prop intersects with
|
||||||
|
// the set in the filter.
|
||||||
|
func (o obsoleteKeyBlockPropertyFilter) Intersects(prop []byte) (bool, error) {
|
||||||
|
return propToIsObsolete(prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func propToIsObsolete(prop []byte) (bool, error) {
|
||||||
|
if len(prop) == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if len(prop) > 1 || prop[0] != 't' {
|
||||||
|
return false, errors.Errorf("unexpected property %x", prop)
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|||||||
163
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
163
vendor/github.com/cockroachdb/pebble/table_cache.go
generated
vendored
@@ -9,6 +9,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
"github.com/cockroachdb/pebble/internal/keyspan"
|
"github.com/cockroachdb/pebble/internal/keyspan"
|
||||||
"github.com/cockroachdb/pebble/internal/manifest"
|
"github.com/cockroachdb/pebble/internal/manifest"
|
||||||
"github.com/cockroachdb/pebble/internal/private"
|
"github.com/cockroachdb/pebble/internal/private"
|
||||||
|
"github.com/cockroachdb/pebble/internal/rangekey"
|
||||||
"github.com/cockroachdb/pebble/objstorage"
|
"github.com/cockroachdb/pebble/objstorage"
|
||||||
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
|
"github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing"
|
||||||
"github.com/cockroachdb/pebble/sstable"
|
"github.com/cockroachdb/pebble/sstable"
|
||||||
@@ -138,7 +140,7 @@ func (c *tableCacheContainer) newIters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *tableCacheContainer) newRangeKeyIter(
|
func (c *tableCacheContainer) newRangeKeyIter(
|
||||||
file *manifest.FileMetadata, opts *keyspan.SpanIterOptions,
|
file *manifest.FileMetadata, opts keyspan.SpanIterOptions,
|
||||||
) (keyspan.FragmentIterator, error) {
|
) (keyspan.FragmentIterator, error) {
|
||||||
return c.tableCache.getShard(file.FileBacking.DiskFileNum).newRangeKeyIter(file, opts, &c.dbOpts)
|
return c.tableCache.getShard(file.FileBacking.DiskFileNum).newRangeKeyIter(file, opts, &c.dbOpts)
|
||||||
}
|
}
|
||||||
@@ -168,6 +170,32 @@ func (c *tableCacheContainer) metrics() (CacheMetrics, FilterMetrics) {
|
|||||||
return m, f
|
return m, f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *tableCacheContainer) estimateSize(
|
||||||
|
meta *fileMetadata, lower, upper []byte,
|
||||||
|
) (size uint64, err error) {
|
||||||
|
if meta.Virtual {
|
||||||
|
err = c.withVirtualReader(
|
||||||
|
meta.VirtualMeta(),
|
||||||
|
func(r sstable.VirtualReader) (err error) {
|
||||||
|
size, err = r.EstimateDiskUsage(lower, upper)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = c.withReader(
|
||||||
|
meta.PhysicalMeta(),
|
||||||
|
func(r *sstable.Reader) (err error) {
|
||||||
|
size, err = r.EstimateDiskUsage(lower, upper)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *tableCacheContainer) withReader(meta physicalMeta, fn func(*sstable.Reader) error) error {
|
func (c *tableCacheContainer) withReader(meta physicalMeta, fn func(*sstable.Reader) error) error {
|
||||||
s := c.tableCache.getShard(meta.FileBacking.DiskFileNum)
|
s := c.tableCache.getShard(meta.FileBacking.DiskFileNum)
|
||||||
v := s.findNode(meta.FileMetadata, &c.dbOpts)
|
v := s.findNode(meta.FileMetadata, &c.dbOpts)
|
||||||
@@ -373,6 +401,26 @@ func (c *tableCacheShard) newIters(
|
|||||||
return nil, nil, v.err
|
return nil, nil, v.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideObsoletePoints := false
|
||||||
|
if opts != nil {
|
||||||
|
// This code is appending (at most one filter) in-place to
|
||||||
|
// opts.PointKeyFilters even though the slice is shared for iterators in
|
||||||
|
// the same iterator tree. This is acceptable since all the following
|
||||||
|
// properties are true:
|
||||||
|
// - The iterator tree is single threaded, so the shared backing for the
|
||||||
|
// slice is being mutated in a single threaded manner.
|
||||||
|
// - Each shallow copy of the slice has its own notion of length.
|
||||||
|
// - The appended element is always the obsoleteKeyBlockPropertyFilter
|
||||||
|
// struct, which is stateless, so overwriting that struct when creating
|
||||||
|
// one sstable iterator is harmless to other sstable iterators that are
|
||||||
|
// relying on that struct.
|
||||||
|
//
|
||||||
|
// An alternative would be to have different slices for different sstable
|
||||||
|
// iterators, but that requires more work to avoid allocations.
|
||||||
|
hideObsoletePoints, opts.PointKeyFilters =
|
||||||
|
v.reader.TryAddBlockPropertyFilterForHideObsoletePoints(
|
||||||
|
opts.snapshotForHideObsoletePoints, file.LargestSeqNum, opts.PointKeyFilters)
|
||||||
|
}
|
||||||
ok := true
|
ok := true
|
||||||
var filterer *sstable.BlockPropertiesFilterer
|
var filterer *sstable.BlockPropertiesFilterer
|
||||||
var err error
|
var err error
|
||||||
@@ -387,14 +435,7 @@ func (c *tableCacheShard) newIters(
|
|||||||
|
|
||||||
type iterCreator interface {
|
type iterCreator interface {
|
||||||
NewRawRangeDelIter() (keyspan.FragmentIterator, error)
|
NewRawRangeDelIter() (keyspan.FragmentIterator, error)
|
||||||
NewIterWithBlockPropertyFiltersAndContext(
|
NewIterWithBlockPropertyFiltersAndContextEtc(ctx context.Context, lower, upper []byte, filterer *sstable.BlockPropertiesFilterer, hideObsoletePoints, useFilterBlock bool, stats *base.InternalIteratorStats, rp sstable.ReaderProvider) (sstable.Iterator, error)
|
||||||
ctx context.Context,
|
|
||||||
lower, upper []byte,
|
|
||||||
filterer *sstable.BlockPropertiesFilterer,
|
|
||||||
useFilterBlock bool,
|
|
||||||
stats *base.InternalIteratorStats,
|
|
||||||
rp sstable.ReaderProvider,
|
|
||||||
) (sstable.Iterator, error)
|
|
||||||
NewCompactionIter(
|
NewCompactionIter(
|
||||||
bytesIterated *uint64,
|
bytesIterated *uint64,
|
||||||
rp sstable.ReaderProvider,
|
rp sstable.ReaderProvider,
|
||||||
@@ -410,11 +451,38 @@ func (c *tableCacheShard) newIters(
|
|||||||
ic = &virtualReader
|
ic = &virtualReader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider := dbOpts.objProvider
|
||||||
|
// Check if this file is a foreign file.
|
||||||
|
objMeta, err := provider.Lookup(fileTypeTable, file.FileBacking.DiskFileNum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// NB: range-del iterator does not maintain a reference to the table, nor
|
// NB: range-del iterator does not maintain a reference to the table, nor
|
||||||
// does it need to read from it after creation.
|
// does it need to read from it after creation.
|
||||||
var rangeDelIter keyspan.FragmentIterator
|
var rangeDelIter keyspan.FragmentIterator
|
||||||
rangeDelIter, err = ic.NewRawRangeDelIter()
|
if provider.IsForeign(objMeta) {
|
||||||
|
if opts == nil {
|
||||||
|
panic("unexpected nil opts when reading foreign file")
|
||||||
|
}
|
||||||
|
if !file.Virtual {
|
||||||
|
// Foreign sstables must be virtual by definition.
|
||||||
|
panic(fmt.Sprintf("sstable is foreign but not virtual: %s", file.FileNum))
|
||||||
|
}
|
||||||
|
switch manifest.LevelToInt(opts.level) {
|
||||||
|
case 5:
|
||||||
|
rangeDelIter, err = ic.NewRawRangeDelIter()
|
||||||
|
case 6:
|
||||||
|
// Let rangeDelIter remain nil. We don't need to return rangedels from
|
||||||
|
// this file as they will not apply to any other files. For the purpose
|
||||||
|
// of collapsing rangedels within this file, we create another rangeDelIter
|
||||||
|
// below for use with the interleaving iter.
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unexpected level for foreign sstable: %d", manifest.LevelToInt(opts.level)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rangeDelIter, err = ic.NewRawRangeDelIter()
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.unrefValue(v)
|
c.unrefValue(v)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -454,11 +522,9 @@ func (c *tableCacheShard) newIters(
|
|||||||
if internalOpts.bytesIterated != nil {
|
if internalOpts.bytesIterated != nil {
|
||||||
iter, err = ic.NewCompactionIter(internalOpts.bytesIterated, rp)
|
iter, err = ic.NewCompactionIter(internalOpts.bytesIterated, rp)
|
||||||
} else {
|
} else {
|
||||||
iter, err = ic.NewIterWithBlockPropertyFiltersAndContext(
|
iter, err = ic.NewIterWithBlockPropertyFiltersAndContextEtc(
|
||||||
ctx,
|
ctx, opts.GetLowerBound(), opts.GetUpperBound(), filterer, hideObsoletePoints, useFilter,
|
||||||
opts.GetLowerBound(), opts.GetUpperBound(),
|
internalOpts.stats, rp)
|
||||||
filterer, useFilter, internalOpts.stats, rp,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rangeDelIter != nil {
|
if rangeDelIter != nil {
|
||||||
@@ -470,6 +536,35 @@ func (c *tableCacheShard) newIters(
|
|||||||
// NB: v.closeHook takes responsibility for calling unrefValue(v) here. Take
|
// NB: v.closeHook takes responsibility for calling unrefValue(v) here. Take
|
||||||
// care to avoid introducing an allocation here by adding a closure.
|
// care to avoid introducing an allocation here by adding a closure.
|
||||||
iter.SetCloseHook(v.closeHook)
|
iter.SetCloseHook(v.closeHook)
|
||||||
|
if provider.IsForeign(objMeta) {
|
||||||
|
// NB: IsForeign() guarantees IsShared, so opts must not be nil as we've
|
||||||
|
// already panicked on the nil case above.
|
||||||
|
pointKeySeqNum := base.SeqNumForLevel(manifest.LevelToInt(opts.level))
|
||||||
|
pcIter := pointCollapsingIterator{
|
||||||
|
comparer: dbOpts.opts.Comparer,
|
||||||
|
merge: dbOpts.opts.Merge,
|
||||||
|
seqNum: math.MaxUint64,
|
||||||
|
elideRangeDeletes: true,
|
||||||
|
fixedSeqNum: pointKeySeqNum,
|
||||||
|
}
|
||||||
|
// Open a second rangedel iter. This is solely for the interleaving iter to
|
||||||
|
// be able to efficiently delete covered range deletes. We don't need to fix
|
||||||
|
// the sequence number in this iter, as these range deletes will not be
|
||||||
|
// exposed to anything other than the interleaving iter and
|
||||||
|
// pointCollapsingIter.
|
||||||
|
rangeDelIter, err := v.reader.NewRawRangeDelIter()
|
||||||
|
if err != nil {
|
||||||
|
c.unrefValue(v)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if rangeDelIter == nil {
|
||||||
|
rangeDelIter = emptyKeyspanIter
|
||||||
|
}
|
||||||
|
pcIter.iter.Init(dbOpts.opts.Comparer, iter, rangeDelIter, nil /* mask */, opts.LowerBound, opts.UpperBound)
|
||||||
|
pcSSTIter := pcSSTIterPool.Get().(*pointCollapsingSSTIterator)
|
||||||
|
*pcSSTIter = pointCollapsingSSTIterator{pointCollapsingIterator: pcIter, childIter: iter}
|
||||||
|
iter = pcSSTIter
|
||||||
|
}
|
||||||
|
|
||||||
c.iterCount.Add(1)
|
c.iterCount.Add(1)
|
||||||
dbOpts.iterCount.Add(1)
|
dbOpts.iterCount.Add(1)
|
||||||
@@ -482,7 +577,7 @@ func (c *tableCacheShard) newIters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *tableCacheShard) newRangeKeyIter(
|
func (c *tableCacheShard) newRangeKeyIter(
|
||||||
file *manifest.FileMetadata, opts *keyspan.SpanIterOptions, dbOpts *tableCacheOpts,
|
file *manifest.FileMetadata, opts keyspan.SpanIterOptions, dbOpts *tableCacheOpts,
|
||||||
) (keyspan.FragmentIterator, error) {
|
) (keyspan.FragmentIterator, error) {
|
||||||
// Calling findNode gives us the responsibility of decrementing v's
|
// Calling findNode gives us the responsibility of decrementing v's
|
||||||
// refCount. If opening the underlying table resulted in error, then we
|
// refCount. If opening the underlying table resulted in error, then we
|
||||||
@@ -501,7 +596,7 @@ func (c *tableCacheShard) newRangeKeyIter(
|
|||||||
// file's range key blocks may surface deleted range keys below. This is
|
// file's range key blocks may surface deleted range keys below. This is
|
||||||
// done here, rather than deferring to the block-property collector in order
|
// done here, rather than deferring to the block-property collector in order
|
||||||
// to maintain parity with point keys and the treatment of RANGEDELs.
|
// to maintain parity with point keys and the treatment of RANGEDELs.
|
||||||
if opts != nil && v.reader.Properties.NumRangeKeyDels == 0 {
|
if v.reader.Properties.NumRangeKeyDels == 0 {
|
||||||
ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters, nil)
|
ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters, nil)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -539,6 +634,29 @@ func (c *tableCacheShard) newRangeKeyIter(
|
|||||||
return emptyKeyspanIter, nil
|
return emptyKeyspanIter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objMeta, err := dbOpts.objProvider.Lookup(fileTypeTable, file.FileBacking.DiskFileNum)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dbOpts.objProvider.IsForeign(objMeta) {
|
||||||
|
if opts.Level == 0 {
|
||||||
|
panic("unexpected zero level when reading foreign file")
|
||||||
|
}
|
||||||
|
transform := &rangekey.ForeignSSTTransformer{
|
||||||
|
Comparer: dbOpts.opts.Comparer,
|
||||||
|
Level: manifest.LevelToInt(opts.Level),
|
||||||
|
}
|
||||||
|
if iter == nil {
|
||||||
|
iter = emptyKeyspanIter
|
||||||
|
}
|
||||||
|
transformIter := &keyspan.TransformerIter{
|
||||||
|
FragmentIterator: iter,
|
||||||
|
Transformer: transform,
|
||||||
|
Compare: dbOpts.opts.Comparer.Compare,
|
||||||
|
}
|
||||||
|
return transformIter, nil
|
||||||
|
}
|
||||||
|
|
||||||
return iter, nil
|
return iter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -986,9 +1104,16 @@ type loadInfo struct {
|
|||||||
func (v *tableCacheValue) load(loadInfo loadInfo, c *tableCacheShard, dbOpts *tableCacheOpts) {
|
func (v *tableCacheValue) load(loadInfo loadInfo, c *tableCacheShard, dbOpts *tableCacheOpts) {
|
||||||
// Try opening the file first.
|
// Try opening the file first.
|
||||||
var f objstorage.Readable
|
var f objstorage.Readable
|
||||||
f, v.err = dbOpts.objProvider.OpenForReading(
|
var err error
|
||||||
|
f, err = dbOpts.objProvider.OpenForReading(
|
||||||
context.TODO(), fileTypeTable, loadInfo.backingFileNum, objstorage.OpenOptions{MustExist: true},
|
context.TODO(), fileTypeTable, loadInfo.backingFileNum, objstorage.OpenOptions{MustExist: true},
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
v.err = errors.Wrap(
|
||||||
|
err,
|
||||||
|
fmt.Sprintf("pebble: could not open backing file %s", errors.Safe(loadInfo.backingFileNum.FileNum())),
|
||||||
|
)
|
||||||
|
}
|
||||||
if v.err == nil {
|
if v.err == nil {
|
||||||
cacheOpts := private.SSTableCacheOpts(dbOpts.cacheID, loadInfo.backingFileNum).(sstable.ReaderOption)
|
cacheOpts := private.SSTableCacheOpts(dbOpts.cacheID, loadInfo.backingFileNum).(sstable.ReaderOption)
|
||||||
v.reader, v.err = sstable.NewReader(f, dbOpts.opts, cacheOpts, dbOpts.filterMetrics)
|
v.reader, v.err = sstable.NewReader(f, dbOpts.opts, cacheOpts, dbOpts.filterMetrics)
|
||||||
|
|||||||
195
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
195
vendor/github.com/cockroachdb/pebble/table_stats.go
generated
vendored
@@ -126,7 +126,7 @@ func (d *DB) collectTableStats() bool {
|
|||||||
maybeCompact := false
|
maybeCompact := false
|
||||||
for _, c := range collected {
|
for _, c := range collected {
|
||||||
c.fileMetadata.Stats = c.TableStats
|
c.fileMetadata.Stats = c.TableStats
|
||||||
maybeCompact = maybeCompact || c.TableStats.RangeDeletionsBytesEstimate > 0
|
maybeCompact = maybeCompact || fileCompensation(c.fileMetadata) > 0
|
||||||
c.fileMetadata.StatsMarkValid()
|
c.fileMetadata.StatsMarkValid()
|
||||||
}
|
}
|
||||||
d.mu.tableStats.cond.Broadcast()
|
d.mu.tableStats.cond.Broadcast()
|
||||||
@@ -186,6 +186,11 @@ func (d *DB) loadNewFileStats(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nf.Meta.Virtual {
|
||||||
|
// cannot load virtual table stats
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
stats, newHints, err := d.loadTableStats(
|
stats, newHints, err := d.loadTableStats(
|
||||||
rs.current, nf.Level,
|
rs.current, nf.Level,
|
||||||
nf.Meta.PhysicalMeta(),
|
nf.Meta.PhysicalMeta(),
|
||||||
@@ -217,6 +222,12 @@ func (d *DB) scanReadStateTableStats(
|
|||||||
for l, levelMetadata := range rs.current.Levels {
|
for l, levelMetadata := range rs.current.Levels {
|
||||||
iter := levelMetadata.Iter()
|
iter := levelMetadata.Iter()
|
||||||
for f := iter.First(); f != nil; f = iter.Next() {
|
for f := iter.First(); f != nil; f = iter.Next() {
|
||||||
|
if f.Virtual {
|
||||||
|
// TODO(bananabrick): Support stats collection for virtual
|
||||||
|
// sstables.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// NB: We're not holding d.mu which protects f.Stats, but only the
|
// NB: We're not holding d.mu which protects f.Stats, but only the
|
||||||
// active stats collection job updates f.Stats for active files,
|
// active stats collection job updates f.Stats for active files,
|
||||||
// and we ensure only one goroutine runs it at a time through
|
// and we ensure only one goroutine runs it at a time through
|
||||||
@@ -225,6 +236,11 @@ func (d *DB) scanReadStateTableStats(
|
|||||||
if f.StatsValid() {
|
if f.StatsValid() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// TODO(bilal): Remove this guard when table stats collection is
|
||||||
|
// implemented for virtual sstables.
|
||||||
|
if f.Virtual {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Limit how much work we do per read state. The older the read
|
// Limit how much work we do per read state. The older the read
|
||||||
// state is, the higher the likelihood files are no longer being
|
// state is, the higher the likelihood files are no longer being
|
||||||
@@ -304,12 +320,12 @@ func (d *DB) loadTablePointKeyStats(
|
|||||||
// We could write the ranges of 'clusters' of point tombstones to
|
// We could write the ranges of 'clusters' of point tombstones to
|
||||||
// a sstable property and call averageValueSizeBeneath for each of
|
// a sstable property and call averageValueSizeBeneath for each of
|
||||||
// these narrower ranges to improve the estimate.
|
// these narrower ranges to improve the estimate.
|
||||||
avgValSize, err := d.averagePhysicalValueSizeBeneath(v, level, meta)
|
avgValLogicalSize, compressionRatio, err := d.estimateSizesBeneath(v, level, meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
stats.PointDeletionsBytesEstimate =
|
stats.PointDeletionsBytesEstimate =
|
||||||
pointDeletionsBytesEstimate(meta.Size, &r.Properties, avgValSize)
|
pointDeletionsBytesEstimate(meta.Size, &r.Properties, avgValLogicalSize, compressionRatio)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,51 +436,60 @@ func (d *DB) loadTableRangeDelStats(
|
|||||||
return compactionHints, err
|
return compactionHints, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) averagePhysicalValueSizeBeneath(
|
func (d *DB) estimateSizesBeneath(
|
||||||
v *version, level int, meta physicalMeta,
|
v *version, level int, meta physicalMeta,
|
||||||
) (avgValueSize uint64, err error) {
|
) (avgValueLogicalSize, compressionRatio float64, err error) {
|
||||||
// Find all files in lower levels that overlap with meta,
|
// Find all files in lower levels that overlap with meta,
|
||||||
// summing their value sizes and entry counts.
|
// summing their value sizes and entry counts.
|
||||||
|
file := meta.FileMetadata
|
||||||
var fileSum, keySum, valSum, entryCount uint64
|
var fileSum, keySum, valSum, entryCount uint64
|
||||||
|
|
||||||
|
addPhysicalTableStats := func(r *sstable.Reader) (err error) {
|
||||||
|
fileSum += file.Size
|
||||||
|
entryCount += r.Properties.NumEntries
|
||||||
|
keySum += r.Properties.RawKeySize
|
||||||
|
valSum += r.Properties.RawValueSize
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
addVirtualTableStats := func(v sstable.VirtualReader) (err error) {
|
||||||
|
fileSum += file.Size
|
||||||
|
entryCount += file.Stats.NumEntries
|
||||||
|
keySum += v.Properties.RawKeySize
|
||||||
|
valSum += v.Properties.RawValueSize
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the file itself. This is important because in some instances, the
|
||||||
|
// computed compression ratio is applied to the tombstones contained within
|
||||||
|
// `meta` itself. If there are no files beneath `meta` in the LSM, we would
|
||||||
|
// calculate a compression ratio of 0 which is not accurate for the file's
|
||||||
|
// own tombstones.
|
||||||
|
if err = d.tableCache.withReader(meta, addPhysicalTableStats); err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
for l := level + 1; l < numLevels; l++ {
|
for l := level + 1; l < numLevels; l++ {
|
||||||
overlaps := v.Overlaps(l, d.cmp, meta.Smallest.UserKey,
|
overlaps := v.Overlaps(l, d.cmp, meta.Smallest.UserKey,
|
||||||
meta.Largest.UserKey, meta.Largest.IsExclusiveSentinel())
|
meta.Largest.UserKey, meta.Largest.IsExclusiveSentinel())
|
||||||
iter := overlaps.Iter()
|
iter := overlaps.Iter()
|
||||||
for file := iter.First(); file != nil; file = iter.Next() {
|
for file = iter.First(); file != nil; file = iter.Next() {
|
||||||
var err error
|
var err error
|
||||||
if file.Virtual {
|
if file.Virtual {
|
||||||
err = d.tableCache.withVirtualReader(
|
err = d.tableCache.withVirtualReader(file.VirtualMeta(), addVirtualTableStats)
|
||||||
file.VirtualMeta(),
|
|
||||||
func(v sstable.VirtualReader) (err error) {
|
|
||||||
fileSum += file.Size
|
|
||||||
entryCount += file.Stats.NumEntries
|
|
||||||
keySum += v.Properties.RawKeySize
|
|
||||||
valSum += v.Properties.RawValueSize
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
err = d.tableCache.withReader(
|
err = d.tableCache.withReader(file.PhysicalMeta(), addPhysicalTableStats)
|
||||||
file.PhysicalMeta(),
|
|
||||||
func(r *sstable.Reader) (err error) {
|
|
||||||
fileSum += file.Size
|
|
||||||
entryCount += r.Properties.NumEntries
|
|
||||||
keySum += r.Properties.RawKeySize
|
|
||||||
valSum += r.Properties.RawValueSize
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if entryCount == 0 {
|
if entryCount == 0 {
|
||||||
return 0, nil
|
return 0, 0, nil
|
||||||
}
|
}
|
||||||
// RawKeySize and RawValueSize are uncompressed totals. Scale the value sum
|
// RawKeySize and RawValueSize are uncompressed totals. We'll need to scale
|
||||||
// according to the data size to account for compression, index blocks and
|
// the value sum according to the data size to account for compression,
|
||||||
// metadata overhead. Eg:
|
// index blocks and metadata overhead. Eg:
|
||||||
//
|
//
|
||||||
// Compression rate × Average uncompressed value size
|
// Compression rate × Average uncompressed value size
|
||||||
//
|
//
|
||||||
@@ -473,10 +498,14 @@ func (d *DB) averagePhysicalValueSizeBeneath(
|
|||||||
// FileSize RawValueSize
|
// FileSize RawValueSize
|
||||||
// ----------------------- × ------------
|
// ----------------------- × ------------
|
||||||
// RawKeySize+RawValueSize NumEntries
|
// RawKeySize+RawValueSize NumEntries
|
||||||
|
//
|
||||||
|
// We return the average logical value size plus the compression ratio,
|
||||||
|
// leaving the scaling to the caller. This allows the caller to perform
|
||||||
|
// additional compression ratio scaling if necessary.
|
||||||
uncompressedSum := float64(keySum + valSum)
|
uncompressedSum := float64(keySum + valSum)
|
||||||
compressionRatio := float64(fileSum) / uncompressedSum
|
compressionRatio = float64(fileSum) / uncompressedSum
|
||||||
avgCompressedValueSize := (float64(valSum) / float64(entryCount)) * compressionRatio
|
avgValueLogicalSize = (float64(valSum) / float64(entryCount))
|
||||||
return uint64(avgCompressedValueSize), nil
|
return avgValueLogicalSize, compressionRatio, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) estimateReclaimedSizeBeneath(
|
func (d *DB) estimateReclaimedSizeBeneath(
|
||||||
@@ -593,12 +622,12 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a table is more than 10% point deletions, don't calculate the
|
// If a table is more than 10% point deletions without user-provided size
|
||||||
// PointDeletionsBytesEstimate statistic using our limited knowledge. The
|
// estimates, don't calculate the PointDeletionsBytesEstimate statistic
|
||||||
// table stats collector can populate the stats and calculate an average
|
// using our limited knowledge. The table stats collector can populate the
|
||||||
// of value size of all the tables beneath the table in the LSM, which
|
// stats and calculate an average of value size of all the tables beneath
|
||||||
// will be more accurate.
|
// the table in the LSM, which will be more accurate.
|
||||||
if props.NumDeletions > props.NumEntries/10 {
|
if unsizedDels := (props.NumDeletions - props.NumSizedDeletions); unsizedDels > props.NumEntries/10 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,8 +637,8 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b
|
|||||||
// doesn't require any additional IO and since the number of point
|
// doesn't require any additional IO and since the number of point
|
||||||
// deletions in the file is low, the error introduced by this crude
|
// deletions in the file is low, the error introduced by this crude
|
||||||
// estimate is expected to be small.
|
// estimate is expected to be small.
|
||||||
avgValSize := estimateValuePhysicalSize(meta.Size, props)
|
avgValSize, compressionRatio := estimatePhysicalSizes(meta.Size, props)
|
||||||
pointEstimate = pointDeletionsBytesEstimate(meta.Size, props, avgValSize)
|
pointEstimate = pointDeletionsBytesEstimate(meta.Size, props, avgValSize, compressionRatio)
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.Stats.NumEntries = props.NumEntries
|
meta.Stats.NumEntries = props.NumEntries
|
||||||
@@ -623,7 +652,7 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pointDeletionsBytesEstimate(
|
func pointDeletionsBytesEstimate(
|
||||||
fileSize uint64, props *sstable.Properties, avgValPhysicalSize uint64,
|
fileSize uint64, props *sstable.Properties, avgValLogicalSize, compressionRatio float64,
|
||||||
) (estimate uint64) {
|
) (estimate uint64) {
|
||||||
if props.NumEntries == 0 {
|
if props.NumEntries == 0 {
|
||||||
return 0
|
return 0
|
||||||
@@ -639,43 +668,79 @@ func pointDeletionsBytesEstimate(
|
|||||||
// If there are covered key(s), we also get to drop key and value bytes for
|
// If there are covered key(s), we also get to drop key and value bytes for
|
||||||
// each covered key.
|
// each covered key.
|
||||||
//
|
//
|
||||||
// We estimate assuming that each point tombstone on average covers 1 key.
|
// Some point tombstones (DELSIZEDs) carry a user-provided estimate of the
|
||||||
|
// uncompressed size of entries that will be elided by fully compacting the
|
||||||
|
// tombstone. For these tombstones, there's no guesswork—we use the
|
||||||
|
// RawPointTombstoneValueSizeHint property which is the sum of all these
|
||||||
|
// tombstones' encoded values.
|
||||||
|
//
|
||||||
|
// For un-sized point tombstones (DELs), we estimate assuming that each
|
||||||
|
// point tombstone on average covers 1 key and using average vaue sizes.
|
||||||
// This is almost certainly an overestimate, but that's probably okay
|
// This is almost certainly an overestimate, but that's probably okay
|
||||||
// because point tombstones can slow range iterations even when they don't
|
// because point tombstones can slow range iterations even when they don't
|
||||||
// cover a key. It may be beneficial in the future to more accurately
|
// cover a key.
|
||||||
// estimate which tombstones cover keys and which do not.
|
//
|
||||||
|
// TODO(jackson): This logic doesn't directly incorporate fixed per-key
|
||||||
|
// overhead (8-byte trailer, plus at least 1 byte encoding the length of the
|
||||||
|
// key and 1 byte encoding the length of the value). This overhead is
|
||||||
|
// indirectly incorporated through the compression ratios, but that results
|
||||||
|
// in the overhead being smeared per key-byte and value-byte, rather than
|
||||||
|
// per-entry. This per-key fixed overhead can be nontrivial, especially for
|
||||||
|
// dense swaths of point tombstones. Give some thought as to whether we
|
||||||
|
// should directly include fixed per-key overhead in the calculations.
|
||||||
|
|
||||||
// Calculate the contribution of the key sizes: 1x for the point tombstone
|
// Below, we calculate the tombstone contributions and the shadowed keys'
|
||||||
// itself and 1x for the key that's expected to exist lower in the LSM.
|
// contributions separately.
|
||||||
var keysLogicalSize uint64
|
var tombstonesLogicalSize float64
|
||||||
|
var shadowedLogicalSize float64
|
||||||
|
|
||||||
|
// 1. Calculate the contribution of the tombstone keys themselves.
|
||||||
if props.RawPointTombstoneKeySize > 0 {
|
if props.RawPointTombstoneKeySize > 0 {
|
||||||
// This table has a RawPointTombstoneKeySize property, so we know the
|
tombstonesLogicalSize += float64(props.RawPointTombstoneKeySize)
|
||||||
// exact size of the logical, uncompressed bytes of the point tombstone
|
|
||||||
// keys.
|
|
||||||
keysLogicalSize = 2 * props.RawPointTombstoneKeySize
|
|
||||||
} else {
|
} else {
|
||||||
// This sstable predates the existence of the RawPointTombstoneKeySize
|
// This sstable predates the existence of the RawPointTombstoneKeySize
|
||||||
// property. We can use the average key size within the file itself and
|
// property. We can use the average key size within the file itself and
|
||||||
// the count of point deletions to estimate the size.
|
// the count of point deletions to estimate the size.
|
||||||
keysLogicalSize = 2 * numPointDels * props.RawKeySize / props.NumEntries
|
tombstonesLogicalSize += float64(numPointDels * props.RawKeySize / props.NumEntries)
|
||||||
}
|
}
|
||||||
// Scale the logical key size by the logical:physical ratio of the file to
|
|
||||||
|
// 2. Calculate the contribution of the keys shadowed by tombstones.
|
||||||
|
//
|
||||||
|
// 2a. First account for keys shadowed by DELSIZED tombstones. THE DELSIZED
|
||||||
|
// tombstones encode the size of both the key and value of the shadowed KV
|
||||||
|
// entries. These sizes are aggregated into a sstable property.
|
||||||
|
shadowedLogicalSize += float64(props.RawPointTombstoneValueSize)
|
||||||
|
|
||||||
|
// 2b. Calculate the contribution of the KV entries shadowed by ordinary DEL
|
||||||
|
// keys.
|
||||||
|
numUnsizedDels := numPointDels - props.NumSizedDeletions
|
||||||
|
{
|
||||||
|
// The shadowed keys have the same exact user keys as the tombstones
|
||||||
|
// themselves, so we can use the `tombstonesLogicalSize` we computed
|
||||||
|
// earlier as an estimate. There's a complication that
|
||||||
|
// `tombstonesLogicalSize` may include DELSIZED keys we already
|
||||||
|
// accounted for.
|
||||||
|
shadowedLogicalSize += float64(tombstonesLogicalSize) / float64(numPointDels) * float64(numUnsizedDels)
|
||||||
|
|
||||||
|
// Calculate the contribution of the deleted values. The caller has
|
||||||
|
// already computed an average logical size (possibly computed across
|
||||||
|
// many sstables).
|
||||||
|
shadowedLogicalSize += float64(numUnsizedDels) * avgValLogicalSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale both tombstone and shadowed totals by logical:physical ratios to
|
||||||
// account for compression, metadata overhead, etc.
|
// account for compression, metadata overhead, etc.
|
||||||
//
|
//
|
||||||
// Physical FileSize
|
// Physical FileSize
|
||||||
// ----------- = -----------------------
|
// ----------- = -----------------------
|
||||||
// Logical RawKeySize+RawValueSize
|
// Logical RawKeySize+RawValueSize
|
||||||
//
|
//
|
||||||
estimate += (keysLogicalSize * fileSize) / (props.RawKeySize + props.RawValueSize)
|
return uint64((tombstonesLogicalSize + shadowedLogicalSize) * compressionRatio)
|
||||||
|
|
||||||
// Calculate the contribution of the deleted values. The caller has already
|
|
||||||
// computed an average physical size (possibly comptued across many
|
|
||||||
// sstables), so there's no need to scale it.
|
|
||||||
estimate += numPointDels * avgValPhysicalSize
|
|
||||||
return estimate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func estimateValuePhysicalSize(fileSize uint64, props *sstable.Properties) (avgValSize uint64) {
|
func estimatePhysicalSizes(
|
||||||
|
fileSize uint64, props *sstable.Properties,
|
||||||
|
) (avgValLogicalSize, compressionRatio float64) {
|
||||||
// RawKeySize and RawValueSize are uncompressed totals. Scale according to
|
// RawKeySize and RawValueSize are uncompressed totals. Scale according to
|
||||||
// the data size to account for compression, index blocks and metadata
|
// the data size to account for compression, index blocks and metadata
|
||||||
// overhead. Eg:
|
// overhead. Eg:
|
||||||
@@ -689,9 +754,9 @@ func estimateValuePhysicalSize(fileSize uint64, props *sstable.Properties) (avgV
|
|||||||
// RawKeySize+RawValueSize NumEntries
|
// RawKeySize+RawValueSize NumEntries
|
||||||
//
|
//
|
||||||
uncompressedSum := props.RawKeySize + props.RawValueSize
|
uncompressedSum := props.RawKeySize + props.RawValueSize
|
||||||
compressionRatio := float64(fileSize) / float64(uncompressedSum)
|
compressionRatio = float64(fileSize) / float64(uncompressedSum)
|
||||||
avgCompressedValueSize := (float64(props.RawValueSize) / float64(props.NumEntries)) * compressionRatio
|
avgValLogicalSize = (float64(props.RawValueSize) / float64(props.NumEntries))
|
||||||
return uint64(avgCompressedValueSize)
|
return avgValLogicalSize, compressionRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCombinedDeletionKeyspanIter returns a keyspan.FragmentIterator that
|
// newCombinedDeletionKeyspanIter returns a keyspan.FragmentIterator that
|
||||||
|
|||||||
18
vendor/github.com/cockroachdb/pebble/version_set.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/version_set.go
generated
vendored
@@ -293,8 +293,8 @@ func (vs *versionSet) load(
|
|||||||
}
|
}
|
||||||
vs.markFileNumUsed(vs.minUnflushedLogNum)
|
vs.markFileNumUsed(vs.minUnflushedLogNum)
|
||||||
|
|
||||||
// Populate the fileBackingMap since we have finished version
|
// Populate the fileBackingMap and the FileBacking for virtual sstables since
|
||||||
// edit accumulation.
|
// we have finished version edit accumulation.
|
||||||
for _, s := range bve.AddedFileBacking {
|
for _, s := range bve.AddedFileBacking {
|
||||||
vs.fileBackingMap[s.DiskFileNum] = s
|
vs.fileBackingMap[s.DiskFileNum] = s
|
||||||
}
|
}
|
||||||
@@ -490,6 +490,9 @@ func (vs *versionSet) logAndApply(
|
|||||||
defer vs.mu.Lock()
|
defer vs.mu.Lock()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if vs.opts.FormatMajorVersion < ExperimentalFormatVirtualSSTables && len(ve.CreatedBackingTables) > 0 {
|
||||||
|
return errors.AssertionFailedf("MANIFEST cannot contain virtual sstable records due to format major version")
|
||||||
|
}
|
||||||
newVersion, zombies, err = manifest.AccumulateIncompleteAndApplySingleVE(
|
newVersion, zombies, err = manifest.AccumulateIncompleteAndApplySingleVE(
|
||||||
ve, currentVersion, vs.cmp, vs.opts.Comparer.FormatKey,
|
ve, currentVersion, vs.cmp, vs.opts.Comparer.FormatKey,
|
||||||
vs.opts.FlushSplitBytes, vs.opts.Experimental.ReadCompactionRate,
|
vs.opts.FlushSplitBytes, vs.opts.Experimental.ReadCompactionRate,
|
||||||
@@ -516,7 +519,7 @@ func (vs *versionSet) logAndApply(
|
|||||||
return errors.Wrap(err, "MANIFEST next record write failed")
|
return errors.Wrap(err, "MANIFEST next record write failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Any error from this point on is considered fatal as we don't now if
|
// NB: Any error from this point on is considered fatal as we don't know if
|
||||||
// the MANIFEST write occurred or not. Trying to determine that is
|
// the MANIFEST write occurred or not. Trying to determine that is
|
||||||
// fraught. Instead we rely on the standard recovery mechanism run when a
|
// fraught. Instead we rely on the standard recovery mechanism run when a
|
||||||
// database is open. In particular, that mechanism generates a new MANIFEST
|
// database is open. In particular, that mechanism generates a new MANIFEST
|
||||||
@@ -569,6 +572,14 @@ func (vs *versionSet) logAndApply(
|
|||||||
for fileNum, size := range zombies {
|
for fileNum, size := range zombies {
|
||||||
vs.zombieTables[fileNum] = size
|
vs.zombieTables[fileNum] = size
|
||||||
}
|
}
|
||||||
|
// Update the Deleted bools. We can't use the zombieTables struct for this
|
||||||
|
// as it works on FileBackings, not FileMetadatas.
|
||||||
|
for _, f := range ve.DeletedFiles {
|
||||||
|
f.Deleted = true
|
||||||
|
}
|
||||||
|
for i := range ve.NewFiles {
|
||||||
|
ve.NewFiles[i].Meta.Deleted = false
|
||||||
|
}
|
||||||
|
|
||||||
// Install the new version.
|
// Install the new version.
|
||||||
vs.append(newVersion)
|
vs.append(newVersion)
|
||||||
@@ -688,7 +699,6 @@ func (vs *versionSet) createManifest(
|
|||||||
Level: level,
|
Level: level,
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
})
|
})
|
||||||
// TODO(bananabrick): Test snapshot changes.
|
|
||||||
if _, ok := dedup[meta.FileBacking.DiskFileNum]; meta.Virtual && !ok {
|
if _, ok := dedup[meta.FileBacking.DiskFileNum]; meta.Virtual && !ok {
|
||||||
dedup[meta.FileBacking.DiskFileNum] = struct{}{}
|
dedup[meta.FileBacking.DiskFileNum] = struct{}{}
|
||||||
snapshot.CreatedBackingTables = append(
|
snapshot.CreatedBackingTables = append(
|
||||||
|
|||||||
25
vendor/github.com/cockroachdb/pebble/vfs/disk_full.go
generated
vendored
25
vendor/github.com/cockroachdb/pebble/vfs/disk_full.go
generated
vendored
@@ -228,6 +228,17 @@ func (fs *enospcFS) Open(name string, opts ...OpenOption) (File, error) {
|
|||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *enospcFS) OpenReadWrite(name string, opts ...OpenOption) (File, error) {
|
||||||
|
f, err := fs.inner.OpenReadWrite(name, opts...)
|
||||||
|
if f != nil {
|
||||||
|
f = &enospcFile{
|
||||||
|
fs: fs,
|
||||||
|
inner: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *enospcFS) OpenDir(name string) (File, error) {
|
func (fs *enospcFS) OpenDir(name string) (File, error) {
|
||||||
f, err := fs.inner.OpenDir(name)
|
f, err := fs.inner.OpenDir(name)
|
||||||
if f != nil {
|
if f != nil {
|
||||||
@@ -375,6 +386,20 @@ func (f *enospcFile) Write(p []byte) (n int, err error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *enospcFile) WriteAt(p []byte, ofs int64) (n int, err error) {
|
||||||
|
gen := f.fs.waitUntilReady()
|
||||||
|
|
||||||
|
n, err = f.inner.WriteAt(p, ofs)
|
||||||
|
|
||||||
|
if err != nil && isENOSPC(err) {
|
||||||
|
f.fs.handleENOSPC(gen)
|
||||||
|
var n2 int
|
||||||
|
n2, err = f.inner.WriteAt(p[n:], ofs+int64(n))
|
||||||
|
n += n2
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
func (f *enospcFile) Prefetch(offset, length int64) error {
|
func (f *enospcFile) Prefetch(offset, length int64) error {
|
||||||
return f.inner.Prefetch(offset, length)
|
return f.inner.Prefetch(offset, length)
|
||||||
}
|
}
|
||||||
|
|||||||
13
vendor/github.com/cockroachdb/pebble/vfs/disk_health.go
generated
vendored
13
vendor/github.com/cockroachdb/pebble/vfs/disk_health.go
generated
vendored
@@ -219,6 +219,14 @@ func (d *diskHealthCheckingFile) Write(p []byte) (n int, err error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write implements the io.WriterAt interface.
|
||||||
|
func (d *diskHealthCheckingFile) WriteAt(p []byte, ofs int64) (n int, err error) {
|
||||||
|
d.timeDiskOp(OpTypeWrite, int64(len(p)), func() {
|
||||||
|
n, err = d.file.WriteAt(p, ofs)
|
||||||
|
})
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
// Close implements the io.Closer interface.
|
// Close implements the io.Closer interface.
|
||||||
func (d *diskHealthCheckingFile) Close() error {
|
func (d *diskHealthCheckingFile) Close() error {
|
||||||
d.stopTicker()
|
d.stopTicker()
|
||||||
@@ -680,6 +688,11 @@ func (d *diskHealthCheckingFS) Open(name string, opts ...OpenOption) (File, erro
|
|||||||
return d.fs.Open(name, opts...)
|
return d.fs.Open(name, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenReadWrite implements the FS interface.
|
||||||
|
func (d *diskHealthCheckingFS) OpenReadWrite(name string, opts ...OpenOption) (File, error) {
|
||||||
|
return d.fs.Open(name, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
// OpenDir implements the FS interface.
|
// OpenDir implements the FS interface.
|
||||||
func (d *diskHealthCheckingFS) OpenDir(name string) (File, error) {
|
func (d *diskHealthCheckingFS) OpenDir(name string) (File, error) {
|
||||||
f, err := d.fs.OpenDir(name)
|
f, err := d.fs.OpenDir(name)
|
||||||
|
|||||||
14
vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go
generated
vendored
14
vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go
generated
vendored
@@ -46,6 +46,15 @@ func (fs *loggingFS) Open(name string, opts ...OpenOption) (File, error) {
|
|||||||
return newLoggingFile(f, name, fs.logFn), nil
|
return newLoggingFile(f, name, fs.logFn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *loggingFS) OpenReadWrite(name string, opts ...OpenOption) (File, error) {
|
||||||
|
fs.logFn("open-read-write: %s", name)
|
||||||
|
f, err := fs.FS.OpenReadWrite(name, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newLoggingFile(f, name, fs.logFn), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *loggingFS) Link(oldname, newname string) error {
|
func (fs *loggingFS) Link(oldname, newname string) error {
|
||||||
fs.logFn("link: %s -> %s", oldname, newname)
|
fs.logFn("link: %s -> %s", oldname, newname)
|
||||||
return fs.FS.Link(oldname, newname)
|
return fs.FS.Link(oldname, newname)
|
||||||
@@ -137,6 +146,11 @@ func (f *loggingFile) ReadAt(p []byte, offset int64) (int, error) {
|
|||||||
return f.File.ReadAt(p, offset)
|
return f.File.ReadAt(p, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *loggingFile) WriteAt(p []byte, offset int64) (int, error) {
|
||||||
|
f.logFn("write-at(%d, %d): %s", offset, len(p), f.name)
|
||||||
|
return f.File.WriteAt(p, offset)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *loggingFile) Prefetch(offset int64, length int64) error {
|
func (f *loggingFile) Prefetch(offset int64, length int64) error {
|
||||||
f.logFn("prefetch(%d, %d): %s", offset, length, f.name)
|
f.logFn("prefetch(%d, %d): %s", offset, length, f.name)
|
||||||
return f.File.Prefetch(offset, length)
|
return f.File.Prefetch(offset, length)
|
||||||
|
|||||||
47
vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go
generated
vendored
47
vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go
generated
vendored
@@ -208,6 +208,7 @@ func (y *MemFS) Create(fullname string) (File, error) {
|
|||||||
ret = &memFile{
|
ret = &memFile{
|
||||||
n: n,
|
n: n,
|
||||||
fs: y,
|
fs: y,
|
||||||
|
read: true,
|
||||||
write: true,
|
write: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +263,7 @@ func (y *MemFS) Link(oldname, newname string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (y *MemFS) open(fullname string) (File, error) {
|
func (y *MemFS) open(fullname string, openForWrite bool) (File, error) {
|
||||||
var ret *memFile
|
var ret *memFile
|
||||||
err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
|
err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
|
||||||
if final {
|
if final {
|
||||||
@@ -275,9 +276,10 @@ func (y *MemFS) open(fullname string) (File, error) {
|
|||||||
}
|
}
|
||||||
if n := dir.children[frag]; n != nil {
|
if n := dir.children[frag]; n != nil {
|
||||||
ret = &memFile{
|
ret = &memFile{
|
||||||
n: n,
|
n: n,
|
||||||
fs: y,
|
fs: y,
|
||||||
read: true,
|
read: true,
|
||||||
|
write: openForWrite,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,12 +301,22 @@ func (y *MemFS) open(fullname string) (File, error) {
|
|||||||
|
|
||||||
// Open implements FS.Open.
|
// Open implements FS.Open.
|
||||||
func (y *MemFS) Open(fullname string, opts ...OpenOption) (File, error) {
|
func (y *MemFS) Open(fullname string, opts ...OpenOption) (File, error) {
|
||||||
return y.open(fullname)
|
return y.open(fullname, false /* openForWrite */)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenReadWrite implements FS.OpenReadWrite.
|
||||||
|
func (y *MemFS) OpenReadWrite(fullname string, opts ...OpenOption) (File, error) {
|
||||||
|
f, err := y.open(fullname, true /* openForWrite */)
|
||||||
|
pathErr, ok := err.(*os.PathError)
|
||||||
|
if ok && pathErr.Err == oserror.ErrNotExist {
|
||||||
|
return y.Create(fullname)
|
||||||
|
}
|
||||||
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDir implements FS.OpenDir.
|
// OpenDir implements FS.OpenDir.
|
||||||
func (y *MemFS) OpenDir(fullname string) (File, error) {
|
func (y *MemFS) OpenDir(fullname string) (File, error) {
|
||||||
return y.open(fullname)
|
return y.open(fullname, false /* openForWrite */)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove implements FS.Remove.
|
// Remove implements FS.Remove.
|
||||||
@@ -702,6 +714,29 @@ func (f *memFile) Write(p []byte) (int, error) {
|
|||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *memFile) WriteAt(p []byte, ofs int64) (int, error) {
|
||||||
|
if !f.write {
|
||||||
|
return 0, errors.New("pebble/vfs: file was not created for writing")
|
||||||
|
}
|
||||||
|
if f.n.isDir {
|
||||||
|
return 0, errors.New("pebble/vfs: cannot write a directory")
|
||||||
|
}
|
||||||
|
f.n.mu.Lock()
|
||||||
|
defer f.n.mu.Unlock()
|
||||||
|
f.n.mu.modTime = time.Now()
|
||||||
|
|
||||||
|
for len(f.n.mu.data) < int(ofs)+len(p) {
|
||||||
|
f.n.mu.data = append(f.n.mu.data, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := copy(f.n.mu.data[int(ofs):int(ofs)+len(p)], p)
|
||||||
|
if n != len(p) {
|
||||||
|
panic("stuff")
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *memFile) Prefetch(offset int64, length int64) error { return nil }
|
func (f *memFile) Prefetch(offset int64, length int64) error { return nil }
|
||||||
func (f *memFile) Preallocate(offset, length int64) error { return nil }
|
func (f *memFile) Preallocate(offset, length int64) error { return nil }
|
||||||
|
|
||||||
|
|||||||
18
vendor/github.com/cockroachdb/pebble/vfs/vfs.go
generated
vendored
18
vendor/github.com/cockroachdb/pebble/vfs/vfs.go
generated
vendored
@@ -29,6 +29,8 @@ type File interface {
|
|||||||
// method *is* allowed to modify the slice passed in, whether temporarily
|
// method *is* allowed to modify the slice passed in, whether temporarily
|
||||||
// or permanently. Callers of Write() need to take this into account.
|
// or permanently. Callers of Write() need to take this into account.
|
||||||
io.Writer
|
io.Writer
|
||||||
|
// WriteAt() is only supported for files that were opened with FS.OpenReadWrite.
|
||||||
|
io.WriterAt
|
||||||
|
|
||||||
// Preallocate optionally preallocates storage for `length` at `offset`
|
// Preallocate optionally preallocates storage for `length` at `offset`
|
||||||
// within the file. Implementations may choose to do nothing.
|
// within the file. Implementations may choose to do nothing.
|
||||||
@@ -99,6 +101,10 @@ type FS interface {
|
|||||||
// Open opens the named file for reading. openOptions provides
|
// Open opens the named file for reading. openOptions provides
|
||||||
Open(name string, opts ...OpenOption) (File, error)
|
Open(name string, opts ...OpenOption) (File, error)
|
||||||
|
|
||||||
|
// OpenReadWrite opens the named file for reading and writing. If the file
|
||||||
|
// does not exist, it is created.
|
||||||
|
OpenReadWrite(name string, opts ...OpenOption) (File, error)
|
||||||
|
|
||||||
// OpenDir opens the named directory for syncing.
|
// OpenDir opens the named directory for syncing.
|
||||||
OpenDir(name string) (File, error)
|
OpenDir(name string) (File, error)
|
||||||
|
|
||||||
@@ -232,6 +238,18 @@ func (defaultFS) Open(name string, opts ...OpenOption) (File, error) {
|
|||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (defaultFS) OpenReadWrite(name string, opts ...OpenOption) (File, error) {
|
||||||
|
osFile, err := os.OpenFile(name, os.O_RDWR|syscall.O_CLOEXEC|os.O_CREATE, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
file := wrapOSFile(osFile)
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.Apply(file)
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (defaultFS) Remove(name string) error {
|
func (defaultFS) Remove(name string) error {
|
||||||
return errors.WithStack(os.Remove(name))
|
return errors.WithStack(os.Remove(name))
|
||||||
}
|
}
|
||||||
|
|||||||
36
vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go
generated
vendored
36
vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go
generated
vendored
@@ -15,6 +15,7 @@
|
|||||||
package buffer
|
package buffer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
origFmt "fmt"
|
origFmt "fmt"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -197,11 +198,16 @@ func (b *Buffer) endRedactable() {
|
|||||||
if len(b.buf) == 0 {
|
if len(b.buf) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p, ok := b.tryGrowByReslice(len(m.EndS))
|
if bytes.HasSuffix(b.buf, m.StartBytes) {
|
||||||
if !ok {
|
// Special case: remove a trailing open marker and call it a day.
|
||||||
p = b.grow(len(m.EndS))
|
b.buf = b.buf[:len(b.buf)-m.StartLen]
|
||||||
|
} else {
|
||||||
|
p, ok := b.tryGrowByReslice(m.EndLen)
|
||||||
|
if !ok {
|
||||||
|
p = b.grow(m.EndLen)
|
||||||
|
}
|
||||||
|
copy(b.buf[p:], m.EndS)
|
||||||
}
|
}
|
||||||
copy(b.buf[p:], m.EndS)
|
|
||||||
b.markerOpen = false
|
b.markerOpen = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,18 +220,23 @@ func (b *Buffer) startWrite() {
|
|||||||
|
|
||||||
// endRedactable adds the closing redaction marker.
|
// endRedactable adds the closing redaction marker.
|
||||||
func (b *Buffer) startRedactable() {
|
func (b *Buffer) startRedactable() {
|
||||||
p, ok := b.tryGrowByReslice(len(m.StartS))
|
if bytes.HasSuffix(b.buf, m.EndBytes) {
|
||||||
if !ok {
|
// Special case: remove a trailing closing marker and call it a day.
|
||||||
p = b.grow(len(m.StartS))
|
b.buf = b.buf[:len(b.buf)-m.EndLen]
|
||||||
|
} else {
|
||||||
|
p, ok := b.tryGrowByReslice(len(m.StartS))
|
||||||
|
if !ok {
|
||||||
|
p = b.grow(len(m.StartS))
|
||||||
|
}
|
||||||
|
copy(b.buf[p:], m.StartS)
|
||||||
}
|
}
|
||||||
copy(b.buf[p:], m.StartS)
|
|
||||||
b.markerOpen = true
|
b.markerOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// escapeToEnd escapes occurrences of redaction markers in
|
// escapeToEnd escapes occurrences of redaction markers in
|
||||||
// b.buf[b.validUntil:] and advances b.validUntil until the end.
|
// b.buf[b.validUntil:] and advances b.validUntil until the end.
|
||||||
func (b *Buffer) escapeToEnd(breakNewLines bool) {
|
func (b *Buffer) escapeToEnd(breakNewLines bool) {
|
||||||
b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, breakNewLines)
|
b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, false /* trim */)
|
||||||
b.validUntil = len(b.buf)
|
b.validUntil = len(b.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +324,13 @@ func (b *Buffer) Grow(n int) {
|
|||||||
b.buf = b.buf[:m]
|
b.buf = b.buf[:m]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clone is used in tests.
|
||||||
|
func (b *Buffer) clone() *Buffer {
|
||||||
|
c := *b
|
||||||
|
c.buf = append([]byte(nil), b.buf...)
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
// makeSlice allocates a slice of size n. If the allocation fails, it panics
|
// makeSlice allocates a slice of size n. If the allocation fails, it panics
|
||||||
// with ErrTooLarge.
|
// with ErrTooLarge.
|
||||||
func makeSlice(n int) []byte {
|
func makeSlice(n int) []byte {
|
||||||
|
|||||||
10
vendor/github.com/cockroachdb/redact/internal/escape/escape.go
generated
vendored
10
vendor/github.com/cockroachdb/redact/internal/escape/escape.go
generated
vendored
@@ -28,6 +28,9 @@ import (
|
|||||||
// If breakNewLines is set, a closing redaction marker
|
// If breakNewLines is set, a closing redaction marker
|
||||||
// is placed before sequences of one or more newline characters,
|
// is placed before sequences of one or more newline characters,
|
||||||
// and an open redaction marker is placed afterwards.
|
// and an open redaction marker is placed afterwards.
|
||||||
|
//
|
||||||
|
// If strip is set, final newlines and spaces are trimmed from the
|
||||||
|
// output.
|
||||||
func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res []byte) {
|
func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res []byte) {
|
||||||
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
|
// Note: we use len(...RedactableS) and not len(...RedactableBytes)
|
||||||
// because the ...S variant is a compile-time constant so this
|
// because the ...S variant is a compile-time constant so this
|
||||||
@@ -69,7 +72,12 @@ func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res
|
|||||||
copied = true
|
copied = true
|
||||||
}
|
}
|
||||||
res = append(res, b[k:i]...)
|
res = append(res, b[k:i]...)
|
||||||
res = append(res, end...)
|
// Either add an end marker, or elide a start marker immediately prior.
|
||||||
|
if bytes.HasSuffix(res, start) {
|
||||||
|
res = res[:len(res)-ls]
|
||||||
|
} else {
|
||||||
|
res = append(res, end...)
|
||||||
|
}
|
||||||
// Advance to the last newline character. We want to forward
|
// Advance to the last newline character. We want to forward
|
||||||
// them all in a single call to doWrite, for performance.
|
// them all in a single call to doWrite, for performance.
|
||||||
lastNewLine := i
|
lastNewLine := i
|
||||||
|
|||||||
2
vendor/github.com/cockroachdb/redact/internal/markers/constants.go
generated
vendored
2
vendor/github.com/cockroachdb/redact/internal/markers/constants.go
generated
vendored
@@ -20,8 +20,10 @@ import "regexp"
|
|||||||
const (
|
const (
|
||||||
Start = '‹'
|
Start = '‹'
|
||||||
StartS = string(Start)
|
StartS = string(Start)
|
||||||
|
StartLen = len(StartS)
|
||||||
End = '›'
|
End = '›'
|
||||||
EndS = string(End)
|
EndS = string(End)
|
||||||
|
EndLen = len(EndS)
|
||||||
EscapeMark = '?'
|
EscapeMark = '?'
|
||||||
EscapeMarkS = string(EscapeMark)
|
EscapeMarkS = string(EscapeMark)
|
||||||
RedactedS = StartS + "×" + EndS
|
RedactedS = StartS + "×" + EndS
|
||||||
|
|||||||
4
vendor/github.com/klauspost/compress/README.md
generated
vendored
4
vendor/github.com/klauspost/compress/README.md
generated
vendored
@@ -16,6 +16,10 @@ This package provides various compression algorithms.
|
|||||||
|
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
* Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5)
|
||||||
|
* zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802
|
||||||
|
* gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804
|
||||||
|
|
||||||
* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4)
|
* Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4)
|
||||||
* zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784
|
* zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784
|
||||||
* zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792
|
* zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792
|
||||||
|
|||||||
25
vendor/github.com/klauspost/compress/SECURITY.md
generated
vendored
Normal file
25
vendor/github.com/klauspost/compress/SECURITY.md
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Security updates are applied only to the latest release.
|
||||||
|
|
||||||
|
## Vulnerability Definition
|
||||||
|
|
||||||
|
A security vulnerability is a bug that with certain input triggers a crash or an infinite loop. Most calls will have varying execution time and only in rare cases will slow operation be considered a security vulnerability.
|
||||||
|
|
||||||
|
Corrupted output generally is not considered a security vulnerability, unless independent operations are able to affect each other. Note that not all functionality is re-entrant and safe to use concurrently.
|
||||||
|
|
||||||
|
Out-of-memory crashes only applies if the en/decoder uses an abnormal amount of memory, with appropriate options applied, to limit maximum window size, concurrency, etc. However, if you are in doubt you are welcome to file a security issue.
|
||||||
|
|
||||||
|
It is assumed that all callers are trusted, meaning internal data exposed through reflection or inspection of returned data structures is not considered a vulnerability.
|
||||||
|
|
||||||
|
Vulnerabilities resulting from compiler/assembler errors should be reported upstream. Depending on the severity this package may or may not implement a workaround.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
|
||||||
|
|
||||||
|
Please disclose it at [security advisory](https://github.com/klaupost/compress/security/advisories/new). If possible please provide a minimal reproducer. If the issue only applies to a single platform, it would be helpful to provide access to that.
|
||||||
|
|
||||||
|
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base.
|
||||||
8
vendor/github.com/klauspost/compress/huff0/bitwriter.go
generated
vendored
8
vendor/github.com/klauspost/compress/huff0/bitwriter.go
generated
vendored
@@ -13,14 +13,6 @@ type bitWriter struct {
|
|||||||
out []byte
|
out []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// bitMask16 is bitmasks. Has extra to avoid bounds check.
|
|
||||||
var bitMask16 = [32]uint16{
|
|
||||||
0, 1, 3, 7, 0xF, 0x1F,
|
|
||||||
0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
|
|
||||||
0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF,
|
|
||||||
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
|
|
||||||
0xFFFF, 0xFFFF} /* up to 16 bits */
|
|
||||||
|
|
||||||
// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated.
|
// addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated.
|
||||||
// It will not check if there is space for them, so the caller must ensure that it has flushed recently.
|
// It will not check if there is space for them, so the caller must ensure that it has flushed recently.
|
||||||
func (b *bitWriter) addBits16Clean(value uint16, bits uint8) {
|
func (b *bitWriter) addBits16Clean(value uint16, bits uint8) {
|
||||||
|
|||||||
2
vendor/github.com/klauspost/compress/huff0/decompress.go
generated
vendored
2
vendor/github.com/klauspost/compress/huff0/decompress.go
generated
vendored
@@ -253,7 +253,7 @@ func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) {
|
|||||||
|
|
||||||
switch d.actualTableLog {
|
switch d.actualTableLog {
|
||||||
case 8:
|
case 8:
|
||||||
const shift = 8 - 8
|
const shift = 0
|
||||||
for br.off >= 4 {
|
for br.off >= 4 {
|
||||||
br.fillFast()
|
br.fillFast()
|
||||||
v := dt[uint8(br.value>>(56+shift))]
|
v := dt[uint8(br.value>>(56+shift))]
|
||||||
|
|||||||
12
vendor/github.com/klauspost/compress/internal/snapref/encode_other.go
generated
vendored
12
vendor/github.com/klauspost/compress/internal/snapref/encode_other.go
generated
vendored
@@ -87,18 +87,6 @@ func emitCopy(dst []byte, offset, length int) int {
|
|||||||
return i + 2
|
return i + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// extendMatch returns the largest k such that k <= len(src) and that
|
|
||||||
// src[i:i+k-j] and src[j:k] have the same contents.
|
|
||||||
//
|
|
||||||
// It assumes that:
|
|
||||||
//
|
|
||||||
// 0 <= i && i < j && j <= len(src)
|
|
||||||
func extendMatch(src []byte, i, j int) int {
|
|
||||||
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
|
|
||||||
}
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(u, shift uint32) uint32 {
|
func hash(u, shift uint32) uint32 {
|
||||||
return (u * 0x1e35a7bd) >> shift
|
return (u * 0x1e35a7bd) >> shift
|
||||||
}
|
}
|
||||||
|
|||||||
2
vendor/github.com/klauspost/compress/zstd/README.md
generated
vendored
2
vendor/github.com/klauspost/compress/zstd/README.md
generated
vendored
@@ -304,7 +304,7 @@ import "github.com/klauspost/compress/zstd"
|
|||||||
|
|
||||||
// Create a reader that caches decompressors.
|
// Create a reader that caches decompressors.
|
||||||
// For this operation type we supply a nil Reader.
|
// For this operation type we supply a nil Reader.
|
||||||
var decoder, _ = zstd.NewReader(nil, WithDecoderConcurrency(0))
|
var decoder, _ = zstd.NewReader(nil, zstd.WithDecoderConcurrency(0))
|
||||||
|
|
||||||
// Decompress a buffer. We don't supply a destination buffer,
|
// Decompress a buffer. We don't supply a destination buffer,
|
||||||
// so it will be allocated by the decoder.
|
// so it will be allocated by the decoder.
|
||||||
|
|||||||
2
vendor/github.com/klauspost/compress/zstd/blockdec.go
generated
vendored
2
vendor/github.com/klauspost/compress/zstd/blockdec.go
generated
vendored
@@ -592,7 +592,7 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) {
|
|||||||
}
|
}
|
||||||
seq.fse.setRLE(symb)
|
seq.fse.setRLE(symb)
|
||||||
if debugDecoder {
|
if debugDecoder {
|
||||||
printf("RLE set to %+v, code: %v", symb, v)
|
printf("RLE set to 0x%x, code: %v", symb, v)
|
||||||
}
|
}
|
||||||
case compModeFSE:
|
case compModeFSE:
|
||||||
println("Reading table for", tableIndex(i))
|
println("Reading table for", tableIndex(i))
|
||||||
|
|||||||
2
vendor/github.com/klauspost/compress/zstd/decoder_options.go
generated
vendored
2
vendor/github.com/klauspost/compress/zstd/decoder_options.go
generated
vendored
@@ -107,7 +107,7 @@ func WithDecoderDicts(dicts ...[]byte) DOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithEncoderDictRaw registers a dictionary that may be used by the decoder.
|
// WithDecoderDictRaw registers a dictionary that may be used by the decoder.
|
||||||
// The slice content can be arbitrary data.
|
// The slice content can be arbitrary data.
|
||||||
func WithDecoderDictRaw(id uint32, content []byte) DOption {
|
func WithDecoderDictRaw(id uint32, content []byte) DOption {
|
||||||
return func(o *decoderOptions) error {
|
return func(o *decoderOptions) error {
|
||||||
|
|||||||
6
vendor/github.com/klauspost/compress/zstd/enc_fast.go
generated
vendored
6
vendor/github.com/klauspost/compress/zstd/enc_fast.go
generated
vendored
@@ -133,8 +133,7 @@ encodeLoop:
|
|||||||
if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) {
|
if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) {
|
||||||
// Consider history as well.
|
// Consider history as well.
|
||||||
var seq seq
|
var seq seq
|
||||||
var length int32
|
length := 4 + e.matchlen(s+6, repIndex+4, src)
|
||||||
length = 4 + e.matchlen(s+6, repIndex+4, src)
|
|
||||||
seq.matchLen = uint32(length - zstdMinMatch)
|
seq.matchLen = uint32(length - zstdMinMatch)
|
||||||
|
|
||||||
// We might be able to match backwards.
|
// We might be able to match backwards.
|
||||||
@@ -645,8 +644,7 @@ encodeLoop:
|
|||||||
if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) {
|
if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) {
|
||||||
// Consider history as well.
|
// Consider history as well.
|
||||||
var seq seq
|
var seq seq
|
||||||
var length int32
|
length := 4 + e.matchlen(s+6, repIndex+4, src)
|
||||||
length = 4 + e.matchlen(s+6, repIndex+4, src)
|
|
||||||
|
|
||||||
seq.matchLen = uint32(length - zstdMinMatch)
|
seq.matchLen = uint32(length - zstdMinMatch)
|
||||||
|
|
||||||
|
|||||||
2
vendor/github.com/klauspost/compress/zstd/encoder_options.go
generated
vendored
2
vendor/github.com/klauspost/compress/zstd/encoder_options.go
generated
vendored
@@ -129,7 +129,7 @@ func WithEncoderPadding(n int) EOption {
|
|||||||
}
|
}
|
||||||
// No need to waste our time.
|
// No need to waste our time.
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
o.pad = 0
|
n = 0
|
||||||
}
|
}
|
||||||
if n > 1<<30 {
|
if n > 1<<30 {
|
||||||
return fmt.Errorf("padding must less than 1GB (1<<30 bytes) ")
|
return fmt.Errorf("padding must less than 1GB (1<<30 bytes) ")
|
||||||
|
|||||||
8
vendor/github.com/klauspost/compress/zstd/framedec.go
generated
vendored
8
vendor/github.com/klauspost/compress/zstd/framedec.go
generated
vendored
@@ -73,20 +73,20 @@ func (d *frameDec) reset(br byteBuffer) error {
|
|||||||
switch err {
|
switch err {
|
||||||
case io.EOF, io.ErrUnexpectedEOF:
|
case io.EOF, io.ErrUnexpectedEOF:
|
||||||
return io.EOF
|
return io.EOF
|
||||||
default:
|
|
||||||
return err
|
|
||||||
case nil:
|
case nil:
|
||||||
signature[0] = b[0]
|
signature[0] = b[0]
|
||||||
|
default:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// Read the rest, don't allow io.ErrUnexpectedEOF
|
// Read the rest, don't allow io.ErrUnexpectedEOF
|
||||||
b, err = br.readSmall(3)
|
b, err = br.readSmall(3)
|
||||||
switch err {
|
switch err {
|
||||||
case io.EOF:
|
case io.EOF:
|
||||||
return io.EOF
|
return io.EOF
|
||||||
default:
|
|
||||||
return err
|
|
||||||
case nil:
|
case nil:
|
||||||
copy(signature[1:], b)
|
copy(signature[1:], b)
|
||||||
|
default:
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(signature[1:4]) != skippableFrameMagic || signature[0]&0xf0 != 0x50 {
|
if string(signature[1:4]) != skippableFrameMagic || signature[0]&0xf0 != 0x50 {
|
||||||
|
|||||||
16
vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go
generated
vendored
Normal file
16
vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//go:build amd64 && !appengine && !noasm && gc
|
||||||
|
// +build amd64,!appengine,!noasm,gc
|
||||||
|
|
||||||
|
// Copyright 2019+ Klaus Post. All rights reserved.
|
||||||
|
// License information can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package zstd
|
||||||
|
|
||||||
|
// matchLen returns how many bytes match in a and b
|
||||||
|
//
|
||||||
|
// It assumes that:
|
||||||
|
//
|
||||||
|
// len(a) <= len(b) and len(a) > 0
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func matchLen(a []byte, b []byte) int
|
||||||
68
vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s
generated
vendored
Normal file
68
vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copied from S2 implementation.
|
||||||
|
|
||||||
|
//go:build !appengine && !noasm && gc && !noasm
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func matchLen(a []byte, b []byte) int
|
||||||
|
// Requires: BMI
|
||||||
|
TEXT ·matchLen(SB), NOSPLIT, $0-56
|
||||||
|
MOVQ a_base+0(FP), AX
|
||||||
|
MOVQ b_base+24(FP), CX
|
||||||
|
MOVQ a_len+8(FP), DX
|
||||||
|
|
||||||
|
// matchLen
|
||||||
|
XORL SI, SI
|
||||||
|
CMPL DX, $0x08
|
||||||
|
JB matchlen_match4_standalone
|
||||||
|
|
||||||
|
matchlen_loopback_standalone:
|
||||||
|
MOVQ (AX)(SI*1), BX
|
||||||
|
XORQ (CX)(SI*1), BX
|
||||||
|
TESTQ BX, BX
|
||||||
|
JZ matchlen_loop_standalone
|
||||||
|
|
||||||
|
#ifdef GOAMD64_v3
|
||||||
|
TZCNTQ BX, BX
|
||||||
|
#else
|
||||||
|
BSFQ BX, BX
|
||||||
|
#endif
|
||||||
|
SARQ $0x03, BX
|
||||||
|
LEAL (SI)(BX*1), SI
|
||||||
|
JMP gen_match_len_end
|
||||||
|
|
||||||
|
matchlen_loop_standalone:
|
||||||
|
LEAL -8(DX), DX
|
||||||
|
LEAL 8(SI), SI
|
||||||
|
CMPL DX, $0x08
|
||||||
|
JAE matchlen_loopback_standalone
|
||||||
|
|
||||||
|
matchlen_match4_standalone:
|
||||||
|
CMPL DX, $0x04
|
||||||
|
JB matchlen_match2_standalone
|
||||||
|
MOVL (AX)(SI*1), BX
|
||||||
|
CMPL (CX)(SI*1), BX
|
||||||
|
JNE matchlen_match2_standalone
|
||||||
|
LEAL -4(DX), DX
|
||||||
|
LEAL 4(SI), SI
|
||||||
|
|
||||||
|
matchlen_match2_standalone:
|
||||||
|
CMPL DX, $0x02
|
||||||
|
JB matchlen_match1_standalone
|
||||||
|
MOVW (AX)(SI*1), BX
|
||||||
|
CMPW (CX)(SI*1), BX
|
||||||
|
JNE matchlen_match1_standalone
|
||||||
|
LEAL -2(DX), DX
|
||||||
|
LEAL 2(SI), SI
|
||||||
|
|
||||||
|
matchlen_match1_standalone:
|
||||||
|
CMPL DX, $0x01
|
||||||
|
JB gen_match_len_end
|
||||||
|
MOVB (AX)(SI*1), BL
|
||||||
|
CMPB (CX)(SI*1), BL
|
||||||
|
JNE gen_match_len_end
|
||||||
|
INCL SI
|
||||||
|
|
||||||
|
gen_match_len_end:
|
||||||
|
MOVQ SI, ret+48(FP)
|
||||||
|
RET
|
||||||
33
vendor/github.com/klauspost/compress/zstd/matchlen_generic.go
generated
vendored
Normal file
33
vendor/github.com/klauspost/compress/zstd/matchlen_generic.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//go:build !amd64 || appengine || !gc || noasm
|
||||||
|
// +build !amd64 appengine !gc noasm
|
||||||
|
|
||||||
|
// Copyright 2019+ Klaus Post. All rights reserved.
|
||||||
|
// License information can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package zstd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
// matchLen returns the maximum common prefix length of a and b.
|
||||||
|
// a must be the shortest of the two.
|
||||||
|
func matchLen(a, b []byte) (n int) {
|
||||||
|
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
|
||||||
|
diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b)
|
||||||
|
if diff != 0 {
|
||||||
|
return n + bits.TrailingZeros64(diff)>>3
|
||||||
|
}
|
||||||
|
n += 8
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
|
||||||
|
}
|
||||||
22
vendor/github.com/klauspost/compress/zstd/zstd.go
generated
vendored
22
vendor/github.com/klauspost/compress/zstd/zstd.go
generated
vendored
@@ -9,7 +9,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"math/bits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// enable debug printing
|
// enable debug printing
|
||||||
@@ -106,27 +105,6 @@ func printf(format string, a ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchLen returns the maximum common prefix length of a and b.
|
|
||||||
// a must be the shortest of the two.
|
|
||||||
func matchLen(a, b []byte) (n int) {
|
|
||||||
for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] {
|
|
||||||
diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b)
|
|
||||||
if diff != 0 {
|
|
||||||
return n + bits.TrailingZeros64(diff)>>3
|
|
||||||
}
|
|
||||||
n += 8
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range a {
|
|
||||||
if a[i] != b[i] {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func load3232(b []byte, i int32) uint32 {
|
func load3232(b []byte, i int32) uint32 {
|
||||||
return binary.LittleEndian.Uint32(b[:len(b):len(b)][i:])
|
return binary.LittleEndian.Uint32(b[:len(b):len(b)][i:])
|
||||||
}
|
}
|
||||||
|
|||||||
16
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
16
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
@@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_
|
|||||||
SKIP_GOLANGCI_LINT :=
|
SKIP_GOLANGCI_LINT :=
|
||||||
GOLANGCI_LINT :=
|
GOLANGCI_LINT :=
|
||||||
GOLANGCI_LINT_OPTS ?=
|
GOLANGCI_LINT_OPTS ?=
|
||||||
GOLANGCI_LINT_VERSION ?= v1.49.0
|
GOLANGCI_LINT_VERSION ?= v1.51.2
|
||||||
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
|
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
|
||||||
# windows isn't included here because of the path separator being different.
|
# windows isn't included here because of the path separator being different.
|
||||||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
|
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
|
||||||
@@ -91,6 +91,8 @@ BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS))
|
|||||||
PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
|
PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
|
||||||
TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS))
|
TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS))
|
||||||
|
|
||||||
|
SANITIZED_DOCKER_IMAGE_TAG := $(subst +,-,$(DOCKER_IMAGE_TAG))
|
||||||
|
|
||||||
ifeq ($(GOHOSTARCH),amd64)
|
ifeq ($(GOHOSTARCH),amd64)
|
||||||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
|
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
|
||||||
# Only supported on amd64
|
# Only supported on amd64
|
||||||
@@ -205,7 +207,7 @@ common-tarball: promu
|
|||||||
.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
|
.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
|
||||||
common-docker: $(BUILD_DOCKER_ARCHS)
|
common-docker: $(BUILD_DOCKER_ARCHS)
|
||||||
$(BUILD_DOCKER_ARCHS): common-docker-%:
|
$(BUILD_DOCKER_ARCHS): common-docker-%:
|
||||||
docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \
|
docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \
|
||||||
-f $(DOCKERFILE_PATH) \
|
-f $(DOCKERFILE_PATH) \
|
||||||
--build-arg ARCH="$*" \
|
--build-arg ARCH="$*" \
|
||||||
--build-arg OS="linux" \
|
--build-arg OS="linux" \
|
||||||
@@ -214,19 +216,19 @@ $(BUILD_DOCKER_ARCHS): common-docker-%:
|
|||||||
.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
|
.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
|
||||||
common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
|
common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
|
||||||
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
|
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
|
||||||
docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
|
docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)"
|
||||||
|
|
||||||
DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION)))
|
DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION)))
|
||||||
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
|
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
|
||||||
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
|
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
|
||||||
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
|
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
|
||||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
|
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
|
||||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"
|
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"
|
||||||
|
|
||||||
.PHONY: common-docker-manifest
|
.PHONY: common-docker-manifest
|
||||||
common-docker-manifest:
|
common-docker-manifest:
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG))
|
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG))
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"
|
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)"
|
||||||
|
|
||||||
.PHONY: promu
|
.PHONY: promu
|
||||||
promu: $(PROMU)
|
promu: $(PROMU)
|
||||||
|
|||||||
9
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
9
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
@@ -21,6 +21,7 @@ import (
|
|||||||
// kernel data structures.
|
// kernel data structures.
|
||||||
type FS struct {
|
type FS struct {
|
||||||
proc fs.FS
|
proc fs.FS
|
||||||
|
real bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultMountPoint is the common mount point of the proc filesystem.
|
// DefaultMountPoint is the common mount point of the proc filesystem.
|
||||||
@@ -39,5 +40,11 @@ func NewFS(mountPoint string) (FS, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return FS{}, err
|
return FS{}, err
|
||||||
}
|
}
|
||||||
return FS{fs}, nil
|
|
||||||
|
real, err := isRealProc(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return FS{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return FS{fs, real}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
23
vendor/github.com/prometheus/procfs/fs_statfs_notype.go
generated
vendored
Normal file
23
vendor/github.com/prometheus/procfs/fs_statfs_notype.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2018 The Prometheus 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.
|
||||||
|
|
||||||
|
//go:build netbsd || openbsd || solaris || windows
|
||||||
|
// +build netbsd openbsd solaris windows
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
// isRealProc returns true on architectures that don't have a Type argument
|
||||||
|
// in their Statfs_t struct
|
||||||
|
func isRealProc(mountPoint string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
33
vendor/github.com/prometheus/procfs/fs_statfs_type.go
generated
vendored
Normal file
33
vendor/github.com/prometheus/procfs/fs_statfs_type.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2018 The Prometheus 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.
|
||||||
|
|
||||||
|
//go:build !netbsd && !openbsd && !solaris && !windows
|
||||||
|
// +build !netbsd,!openbsd,!solaris,!windows
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isRealProc determines whether supplied mountpoint is really a proc filesystem.
|
||||||
|
func isRealProc(mountPoint string) (bool, error) {
|
||||||
|
stat := syscall.Statfs_t{}
|
||||||
|
err := syscall.Statfs(mountPoint, &stat)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x9fa0 is PROC_SUPER_MAGIC: https://elixir.bootlin.com/linux/v6.1/source/include/uapi/linux/magic.h#L87
|
||||||
|
return stat.Type == 0x9fa0, nil
|
||||||
|
}
|
||||||
15
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
15
vendor/github.com/prometheus/procfs/internal/util/parse.go
generated
vendored
@@ -64,6 +64,21 @@ func ParsePInt64s(ss []string) ([]*int64, error) {
|
|||||||
return us, nil
|
return us, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses a uint64 from given hex in string.
|
||||||
|
func ParseHexUint64s(ss []string) ([]*uint64, error) {
|
||||||
|
us := make([]*uint64, 0, len(ss))
|
||||||
|
for _, s := range ss {
|
||||||
|
u, err := strconv.ParseUint(s, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
us = append(us, &u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return us, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
|
// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
|
||||||
func ReadUintFromFile(path string) (uint64, error) {
|
func ReadUintFromFile(path string) (uint64, error) {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
|
|||||||
6
vendor/github.com/prometheus/procfs/mountstats.go
generated
vendored
6
vendor/github.com/prometheus/procfs/mountstats.go
generated
vendored
@@ -186,6 +186,8 @@ type NFSOperationStats struct {
|
|||||||
CumulativeTotalResponseMilliseconds uint64
|
CumulativeTotalResponseMilliseconds uint64
|
||||||
// Duration from when a request was enqueued to when it was completely handled.
|
// Duration from when a request was enqueued to when it was completely handled.
|
||||||
CumulativeTotalRequestMilliseconds uint64
|
CumulativeTotalRequestMilliseconds uint64
|
||||||
|
// The average time from the point the client sends RPC requests until it receives the response.
|
||||||
|
AverageRTTMilliseconds float64
|
||||||
// The count of operations that complete with tk_status < 0. These statuses usually indicate error conditions.
|
// The count of operations that complete with tk_status < 0. These statuses usually indicate error conditions.
|
||||||
Errors uint64
|
Errors uint64
|
||||||
}
|
}
|
||||||
@@ -534,7 +536,6 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
|
|||||||
|
|
||||||
ns = append(ns, n)
|
ns = append(ns, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
opStats := NFSOperationStats{
|
opStats := NFSOperationStats{
|
||||||
Operation: strings.TrimSuffix(ss[0], ":"),
|
Operation: strings.TrimSuffix(ss[0], ":"),
|
||||||
Requests: ns[0],
|
Requests: ns[0],
|
||||||
@@ -546,6 +547,9 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
|
|||||||
CumulativeTotalResponseMilliseconds: ns[6],
|
CumulativeTotalResponseMilliseconds: ns[6],
|
||||||
CumulativeTotalRequestMilliseconds: ns[7],
|
CumulativeTotalRequestMilliseconds: ns[7],
|
||||||
}
|
}
|
||||||
|
if ns[0] != 0 {
|
||||||
|
opStats.AverageRTTMilliseconds = float64(ns[6]) / float64(ns[0])
|
||||||
|
}
|
||||||
|
|
||||||
if len(ns) > 8 {
|
if len(ns) > 8 {
|
||||||
opStats.Errors = ns[8]
|
opStats.Errors = ns[8]
|
||||||
|
|||||||
88
vendor/github.com/prometheus/procfs/net_conntrackstat.go
generated
vendored
88
vendor/github.com/prometheus/procfs/net_conntrackstat.go
generated
vendored
@@ -18,7 +18,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/prometheus/procfs/internal/util"
|
"github.com/prometheus/procfs/internal/util"
|
||||||
@@ -28,9 +27,13 @@ import (
|
|||||||
// and contains netfilter conntrack statistics at one CPU core.
|
// and contains netfilter conntrack statistics at one CPU core.
|
||||||
type ConntrackStatEntry struct {
|
type ConntrackStatEntry struct {
|
||||||
Entries uint64
|
Entries uint64
|
||||||
|
Searched uint64
|
||||||
Found uint64
|
Found uint64
|
||||||
|
New uint64
|
||||||
Invalid uint64
|
Invalid uint64
|
||||||
Ignore uint64
|
Ignore uint64
|
||||||
|
Delete uint64
|
||||||
|
DeleteList uint64
|
||||||
Insert uint64
|
Insert uint64
|
||||||
InsertFailed uint64
|
InsertFailed uint64
|
||||||
Drop uint64
|
Drop uint64
|
||||||
@@ -81,73 +84,34 @@ func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) {
|
|||||||
|
|
||||||
// Parses a ConntrackStatEntry from given array of fields.
|
// Parses a ConntrackStatEntry from given array of fields.
|
||||||
func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) {
|
func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) {
|
||||||
if len(fields) != 17 {
|
entries, err := util.ParseHexUint64s(fields)
|
||||||
return nil, fmt.Errorf("invalid conntrackstat entry, missing fields")
|
|
||||||
}
|
|
||||||
entry := &ConntrackStatEntry{}
|
|
||||||
|
|
||||||
entries, err := parseConntrackStatField(fields[0])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("invalid conntrackstat entry, couldn't parse fields: %s", err)
|
||||||
}
|
}
|
||||||
entry.Entries = entries
|
numEntries := len(entries)
|
||||||
|
if numEntries < 16 || numEntries > 17 {
|
||||||
found, err := parseConntrackStatField(fields[2])
|
return nil, fmt.Errorf("invalid conntrackstat entry, invalid number of fields: %d", numEntries)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
entry.Found = found
|
|
||||||
|
|
||||||
invalid, err := parseConntrackStatField(fields[4])
|
stats := &ConntrackStatEntry{
|
||||||
if err != nil {
|
Entries: *entries[0],
|
||||||
return nil, err
|
Searched: *entries[1],
|
||||||
|
Found: *entries[2],
|
||||||
|
New: *entries[3],
|
||||||
|
Invalid: *entries[4],
|
||||||
|
Ignore: *entries[5],
|
||||||
|
Delete: *entries[6],
|
||||||
|
DeleteList: *entries[7],
|
||||||
|
Insert: *entries[8],
|
||||||
|
InsertFailed: *entries[9],
|
||||||
|
Drop: *entries[10],
|
||||||
|
EarlyDrop: *entries[11],
|
||||||
}
|
}
|
||||||
entry.Invalid = invalid
|
|
||||||
|
|
||||||
ignore, err := parseConntrackStatField(fields[5])
|
// Ignore missing search_restart on Linux < 2.6.35.
|
||||||
if err != nil {
|
if numEntries == 17 {
|
||||||
return nil, err
|
stats.SearchRestart = *entries[16]
|
||||||
}
|
}
|
||||||
entry.Ignore = ignore
|
|
||||||
|
|
||||||
insert, err := parseConntrackStatField(fields[8])
|
return stats, nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entry.Insert = insert
|
|
||||||
|
|
||||||
insertFailed, err := parseConntrackStatField(fields[9])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entry.InsertFailed = insertFailed
|
|
||||||
|
|
||||||
drop, err := parseConntrackStatField(fields[10])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entry.Drop = drop
|
|
||||||
|
|
||||||
earlyDrop, err := parseConntrackStatField(fields[11])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entry.EarlyDrop = earlyDrop
|
|
||||||
|
|
||||||
searchRestart, err := parseConntrackStatField(fields[16])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
entry.SearchRestart = searchRestart
|
|
||||||
|
|
||||||
return entry, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses a uint64 from given hex in string.
|
|
||||||
func parseConntrackStatField(field string) (uint64, error) {
|
|
||||||
val, err := strconv.ParseUint(field, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("couldn't parse %q field: %w", field, err)
|
|
||||||
}
|
|
||||||
return val, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
vendor/github.com/prometheus/procfs/net_softnet.go
generated
vendored
5
vendor/github.com/prometheus/procfs/net_softnet.go
generated
vendored
@@ -76,6 +76,7 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) {
|
|||||||
s := bufio.NewScanner(r)
|
s := bufio.NewScanner(r)
|
||||||
|
|
||||||
var stats []SoftnetStat
|
var stats []SoftnetStat
|
||||||
|
cpuIndex := 0
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
columns := strings.Fields(s.Text())
|
columns := strings.Fields(s.Text())
|
||||||
width := len(columns)
|
width := len(columns)
|
||||||
@@ -127,9 +128,13 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) {
|
|||||||
|
|
||||||
softnetStat.SoftnetBacklogLen = us[0]
|
softnetStat.SoftnetBacklogLen = us[0]
|
||||||
softnetStat.Index = us[1]
|
softnetStat.Index = us[1]
|
||||||
|
} else {
|
||||||
|
// For older kernels, create the Index based on the scan line number.
|
||||||
|
softnetStat.Index = uint32(cpuIndex)
|
||||||
}
|
}
|
||||||
softnetStat.Width = width
|
softnetStat.Width = width
|
||||||
stats = append(stats, softnetStat)
|
stats = append(stats, softnetStat)
|
||||||
|
cpuIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
return stats, nil
|
return stats, nil
|
||||||
|
|||||||
182
vendor/github.com/prometheus/procfs/net_wireless.go
generated
vendored
Normal file
182
vendor/github.com/prometheus/procfs/net_wireless.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
// Copyright 2023 The Prometheus 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 procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/prometheus/procfs/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wireless models the content of /proc/net/wireless.
|
||||||
|
type Wireless struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Status is the current 4-digit hex value status of the interface.
|
||||||
|
Status uint64
|
||||||
|
|
||||||
|
// QualityLink is the link quality.
|
||||||
|
QualityLink int
|
||||||
|
|
||||||
|
// QualityLevel is the signal gain (dBm).
|
||||||
|
QualityLevel int
|
||||||
|
|
||||||
|
// QualityNoise is the signal noise baseline (dBm).
|
||||||
|
QualityNoise int
|
||||||
|
|
||||||
|
// DiscardedNwid is the number of discarded packets with wrong nwid/essid.
|
||||||
|
DiscardedNwid int
|
||||||
|
|
||||||
|
// DiscardedCrypt is the number of discarded packets with wrong code/decode (WEP).
|
||||||
|
DiscardedCrypt int
|
||||||
|
|
||||||
|
// DiscardedFrag is the number of discarded packets that can't perform MAC reassembly.
|
||||||
|
DiscardedFrag int
|
||||||
|
|
||||||
|
// DiscardedRetry is the number of discarded packets that reached max MAC retries.
|
||||||
|
DiscardedRetry int
|
||||||
|
|
||||||
|
// DiscardedMisc is the number of discarded packets for other reasons.
|
||||||
|
DiscardedMisc int
|
||||||
|
|
||||||
|
// MissedBeacon is the number of missed beacons/superframe.
|
||||||
|
MissedBeacon int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wireless returns kernel wireless statistics.
|
||||||
|
func (fs FS) Wireless() ([]*Wireless, error) {
|
||||||
|
b, err := util.ReadFileNoStat(fs.proc.Path("net/wireless"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := parseWireless(bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse wireless: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseWireless parses the contents of /proc/net/wireless.
|
||||||
|
/*
|
||||||
|
Inter-| sta-| Quality | Discarded packets | Missed | WE
|
||||||
|
face | tus | link level noise | nwid crypt frag retry misc | beacon | 22
|
||||||
|
eth1: 0000 5. -256. -10. 0 1 0 3 0 0
|
||||||
|
eth2: 0000 5. -256. -20. 0 2 0 4 0 0
|
||||||
|
*/
|
||||||
|
func parseWireless(r io.Reader) ([]*Wireless, error) {
|
||||||
|
var (
|
||||||
|
interfaces []*Wireless
|
||||||
|
scanner = bufio.NewScanner(r)
|
||||||
|
)
|
||||||
|
|
||||||
|
for n := 0; scanner.Scan(); n++ {
|
||||||
|
// Skip the 2 header lines.
|
||||||
|
if n < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
line := scanner.Text()
|
||||||
|
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, fmt.Errorf("expected 2 parts after splitting line by ':', got %d for line %q", len(parts), line)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := strings.TrimSpace(parts[0])
|
||||||
|
stats := strings.Fields(parts[1])
|
||||||
|
|
||||||
|
if len(stats) < 10 {
|
||||||
|
return nil, fmt.Errorf("invalid number of fields in line %d, expected at least 10, got %d: %q", n, len(stats), line)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := strconv.ParseUint(stats[0], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid status in line %d: %q", n, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], "."))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Quality:link as integer %q: %w", qlink, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], "."))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Quality:level as integer %q: %w", qlevel, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], "."))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Quality:noise as integer %q: %w", qnoise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dnwid, err := strconv.Atoi(stats[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Discarded:nwid as integer %q: %w", dnwid, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dcrypt, err := strconv.Atoi(stats[5])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Discarded:crypt as integer %q: %w", dcrypt, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dfrag, err := strconv.Atoi(stats[6])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Discarded:frag as integer %q: %w", dfrag, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dretry, err := strconv.Atoi(stats[7])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Discarded:retry as integer %q: %w", dretry, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dmisc, err := strconv.Atoi(stats[8])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Discarded:misc as integer %q: %w", dmisc, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mbeacon, err := strconv.Atoi(stats[9])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse Missed:beacon as integer %q: %w", mbeacon, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &Wireless{
|
||||||
|
Name: name,
|
||||||
|
Status: status,
|
||||||
|
QualityLink: qlink,
|
||||||
|
QualityLevel: qlevel,
|
||||||
|
QualityNoise: qnoise,
|
||||||
|
DiscardedNwid: dnwid,
|
||||||
|
DiscardedCrypt: dcrypt,
|
||||||
|
DiscardedFrag: dfrag,
|
||||||
|
DiscardedRetry: dretry,
|
||||||
|
DiscardedMisc: dmisc,
|
||||||
|
MissedBeacon: mbeacon,
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces = append(interfaces, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to scan /proc/net/wireless: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces, nil
|
||||||
|
}
|
||||||
25
vendor/github.com/prometheus/procfs/netstat.go
generated
vendored
25
vendor/github.com/prometheus/procfs/netstat.go
generated
vendored
@@ -15,7 +15,6 @@ package procfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -38,12 +37,7 @@ func (fs FS) NetStat() ([]NetStat, error) {
|
|||||||
var netStatsTotal []NetStat
|
var netStatsTotal []NetStat
|
||||||
|
|
||||||
for _, filePath := range statFiles {
|
for _, filePath := range statFiles {
|
||||||
file, err := os.Open(filePath)
|
procNetstat, err := parseNetstat(filePath)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
procNetstat, err := parseNetstat(file)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -56,14 +50,17 @@ func (fs FS) NetStat() ([]NetStat, error) {
|
|||||||
|
|
||||||
// parseNetstat parses the metrics from `/proc/net/stat/` file
|
// parseNetstat parses the metrics from `/proc/net/stat/` file
|
||||||
// and returns a NetStat structure.
|
// and returns a NetStat structure.
|
||||||
func parseNetstat(r io.Reader) (NetStat, error) {
|
func parseNetstat(filePath string) (NetStat, error) {
|
||||||
var (
|
netStat := NetStat{
|
||||||
scanner = bufio.NewScanner(r)
|
Stats: make(map[string][]uint64),
|
||||||
netStat = NetStat{
|
}
|
||||||
Stats: make(map[string][]uint64),
|
file, err := os.Open(filePath)
|
||||||
}
|
if err != nil {
|
||||||
)
|
return netStat, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
scanner.Scan()
|
scanner.Scan()
|
||||||
|
|
||||||
// First string is always a header for stats
|
// First string is always a header for stats
|
||||||
|
|||||||
22
vendor/github.com/prometheus/procfs/proc.go
generated
vendored
22
vendor/github.com/prometheus/procfs/proc.go
generated
vendored
@@ -21,7 +21,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/prometheus/procfs/internal/fs"
|
|
||||||
"github.com/prometheus/procfs/internal/util"
|
"github.com/prometheus/procfs/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ type Proc struct {
|
|||||||
// The process ID.
|
// The process ID.
|
||||||
PID int
|
PID int
|
||||||
|
|
||||||
fs fs.FS
|
fs FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procs represents a list of Proc structs.
|
// Procs represents a list of Proc structs.
|
||||||
@@ -92,7 +91,7 @@ func (fs FS) Proc(pid int) (Proc, error) {
|
|||||||
if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
|
if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
return Proc{PID: pid, fs: fs.proc}, nil
|
return Proc{PID: pid, fs: fs}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllProcs returns a list of all currently available processes.
|
// AllProcs returns a list of all currently available processes.
|
||||||
@@ -114,7 +113,7 @@ func (fs FS) AllProcs() (Procs, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p = append(p, Proc{PID: int(pid), fs: fs.proc})
|
p = append(p, Proc{PID: int(pid), fs: fs})
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
@@ -237,6 +236,19 @@ func (p Proc) FileDescriptorTargets() ([]string, error) {
|
|||||||
// FileDescriptorsLen returns the number of currently open file descriptors of
|
// FileDescriptorsLen returns the number of currently open file descriptors of
|
||||||
// a process.
|
// a process.
|
||||||
func (p Proc) FileDescriptorsLen() (int, error) {
|
func (p Proc) FileDescriptorsLen() (int, error) {
|
||||||
|
// Use fast path if available (Linux v6.2): https://github.com/torvalds/linux/commit/f1f1f2569901
|
||||||
|
if p.fs.real {
|
||||||
|
stat, err := os.Stat(p.path("fd"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
size := stat.Size()
|
||||||
|
if size > 0 {
|
||||||
|
return int(size), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fds, err := p.fileDescriptors()
|
fds, err := p.fileDescriptors()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -285,7 +297,7 @@ func (p Proc) fileDescriptors() ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p Proc) path(pa ...string) string {
|
func (p Proc) path(pa ...string) string {
|
||||||
return p.fs.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
|
return p.fs.proc.Path(append([]string{strconv.Itoa(p.PID)}, pa...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileDescriptorsInfo retrieves information about all file descriptors of
|
// FileDescriptorsInfo retrieves information about all file descriptors of
|
||||||
|
|||||||
6
vendor/github.com/prometheus/procfs/proc_stat.go
generated
vendored
6
vendor/github.com/prometheus/procfs/proc_stat.go
generated
vendored
@@ -18,7 +18,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/prometheus/procfs/internal/fs"
|
|
||||||
"github.com/prometheus/procfs/internal/util"
|
"github.com/prometheus/procfs/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,7 +111,7 @@ type ProcStat struct {
|
|||||||
// Aggregated block I/O delays, measured in clock ticks (centiseconds).
|
// Aggregated block I/O delays, measured in clock ticks (centiseconds).
|
||||||
DelayAcctBlkIOTicks uint64
|
DelayAcctBlkIOTicks uint64
|
||||||
|
|
||||||
proc fs.FS
|
proc FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStat returns the current status information of the process.
|
// NewStat returns the current status information of the process.
|
||||||
@@ -210,8 +209,7 @@ func (s ProcStat) ResidentMemory() int {
|
|||||||
|
|
||||||
// StartTime returns the unix timestamp of the process in seconds.
|
// StartTime returns the unix timestamp of the process in seconds.
|
||||||
func (s ProcStat) StartTime() (float64, error) {
|
func (s ProcStat) StartTime() (float64, error) {
|
||||||
fs := FS{proc: s.proc}
|
stat, err := s.proc.Stat()
|
||||||
stat, err := fs.Stat()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
32
vendor/github.com/prometheus/procfs/proc_status.go
generated
vendored
32
vendor/github.com/prometheus/procfs/proc_status.go
generated
vendored
@@ -15,6 +15,7 @@ package procfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -76,6 +77,9 @@ type ProcStatus struct {
|
|||||||
UIDs [4]string
|
UIDs [4]string
|
||||||
// GIDs of the process (Real, effective, saved set, and filesystem GIDs)
|
// GIDs of the process (Real, effective, saved set, and filesystem GIDs)
|
||||||
GIDs [4]string
|
GIDs [4]string
|
||||||
|
|
||||||
|
// CpusAllowedList: List of cpu cores processes are allowed to run on.
|
||||||
|
CpusAllowedList []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStatus returns the current status information of the process.
|
// NewStatus returns the current status information of the process.
|
||||||
@@ -161,10 +165,38 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt
|
|||||||
s.VoluntaryCtxtSwitches = vUint
|
s.VoluntaryCtxtSwitches = vUint
|
||||||
case "nonvoluntary_ctxt_switches":
|
case "nonvoluntary_ctxt_switches":
|
||||||
s.NonVoluntaryCtxtSwitches = vUint
|
s.NonVoluntaryCtxtSwitches = vUint
|
||||||
|
case "Cpus_allowed_list":
|
||||||
|
s.CpusAllowedList = calcCpusAllowedList(vString)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TotalCtxtSwitches returns the total context switch.
|
// TotalCtxtSwitches returns the total context switch.
|
||||||
func (s ProcStatus) TotalCtxtSwitches() uint64 {
|
func (s ProcStatus) TotalCtxtSwitches() uint64 {
|
||||||
return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches
|
return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcCpusAllowedList(cpuString string) []uint64 {
|
||||||
|
s := strings.Split(cpuString, ",")
|
||||||
|
|
||||||
|
var g []uint64
|
||||||
|
|
||||||
|
for _, cpu := range s {
|
||||||
|
// parse cpu ranges, example: 1-3=[1,2,3]
|
||||||
|
if l := strings.Split(strings.TrimSpace(cpu), "-"); len(l) > 1 {
|
||||||
|
startCPU, _ := strconv.ParseUint(l[0], 10, 64)
|
||||||
|
endCPU, _ := strconv.ParseUint(l[1], 10, 64)
|
||||||
|
|
||||||
|
for i := startCPU; i <= endCPU; i++ {
|
||||||
|
g = append(g, i)
|
||||||
|
}
|
||||||
|
} else if len(l) == 1 {
|
||||||
|
cpu, _ := strconv.ParseUint(l[0], 10, 64)
|
||||||
|
g = append(g, cpu)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(g, func(i, j int) bool { return g[i] < g[j] })
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|||||||
9
vendor/github.com/prometheus/procfs/thread.go
generated
vendored
9
vendor/github.com/prometheus/procfs/thread.go
generated
vendored
@@ -54,7 +54,8 @@ func (fs FS) AllThreads(pid int) (Procs, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t = append(t, Proc{PID: int(tid), fs: fsi.FS(taskPath)})
|
|
||||||
|
t = append(t, Proc{PID: int(tid), fs: FS{fsi.FS(taskPath), fs.real}})
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
@@ -66,13 +67,13 @@ func (fs FS) Thread(pid, tid int) (Proc, error) {
|
|||||||
if _, err := os.Stat(taskPath); err != nil {
|
if _, err := os.Stat(taskPath); err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
return Proc{PID: tid, fs: fsi.FS(taskPath)}, nil
|
return Proc{PID: tid, fs: FS{fsi.FS(taskPath), fs.real}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread returns a process for a given TID of Proc.
|
// Thread returns a process for a given TID of Proc.
|
||||||
func (proc Proc) Thread(tid int) (Proc, error) {
|
func (proc Proc) Thread(tid int) (Proc, error) {
|
||||||
tfs := fsi.FS(proc.path("task"))
|
tfs := FS{fsi.FS(proc.path("task")), proc.fs.real}
|
||||||
if _, err := os.Stat(tfs.Path(strconv.Itoa(tid))); err != nil {
|
if _, err := os.Stat(tfs.proc.Path(strconv.Itoa(tid))); err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
return Proc{PID: tid, fs: tfs}, nil
|
return Proc{PID: tid, fs: tfs}, nil
|
||||||
|
|||||||
36
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
36
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Writer at INFO level. See WriterLevel for details.
|
// Writer at INFO level. See WriterLevel for details.
|
||||||
@@ -20,15 +21,18 @@ func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
|||||||
return NewEntry(logger).WriterLevel(level)
|
return NewEntry(logger).WriterLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writer returns an io.Writer that writes to the logger at the info log level
|
||||||
func (entry *Entry) Writer() *io.PipeWriter {
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
return entry.WriterLevel(InfoLevel)
|
return entry.WriterLevel(InfoLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriterLevel returns an io.Writer that writes to the logger at the given log level
|
||||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
var printFunc func(args ...interface{})
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
// Determine which log function to use based on the specified log level
|
||||||
switch level {
|
switch level {
|
||||||
case TraceLevel:
|
case TraceLevel:
|
||||||
printFunc = entry.Trace
|
printFunc = entry.Trace
|
||||||
@@ -48,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
|||||||
printFunc = entry.Print
|
printFunc = entry.Print
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start a new goroutine to scan the input and write it to the logger using the specified print function.
|
||||||
|
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
|
||||||
go entry.writerScanner(reader, printFunc)
|
go entry.writerScanner(reader, printFunc)
|
||||||
|
|
||||||
|
// Set a finalizer function to close the writer when it is garbage collected
|
||||||
runtime.SetFinalizer(writer, writerFinalizer)
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
return writer
|
return writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writerScanner scans the input from the reader and writes it to the logger
|
||||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
scanner := bufio.NewScanner(reader)
|
scanner := bufio.NewScanner(reader)
|
||||||
for scanner.Scan() {
|
|
||||||
printFunc(scanner.Text())
|
// Set the buffer size to the maximum token size to avoid buffer overflows
|
||||||
|
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
|
||||||
|
|
||||||
|
// Define a split function to split the input into chunks of up to 64KB
|
||||||
|
chunkSize := bufio.MaxScanTokenSize // 64KB
|
||||||
|
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
|
||||||
|
if len(data) >= chunkSize {
|
||||||
|
return chunkSize, data[:chunkSize], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufio.ScanLines(data, atEOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the custom split function to split the input
|
||||||
|
scanner.Split(splitFunc)
|
||||||
|
|
||||||
|
// Scan the input and write it to the logger using the specified print function
|
||||||
|
for scanner.Scan() {
|
||||||
|
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was an error while scanning the input, log an error
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
entry.Errorf("Error while reading from Writer: %s", err)
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the reader when we are done
|
||||||
reader.Close()
|
reader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
|
||||||
func writerFinalizer(writer *io.PipeWriter) {
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
writer.Close()
|
writer.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user