diff --git a/go.mod b/go.mod index 02b1002..6884ff7 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module main go 1.19 require ( - github.com/cockroachdb/errors v1.9.1 - github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6 + github.com/cockroachdb/errors v1.10.0 + github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839 github.com/go-redis/redis/v8 v8.11.5 github.com/hjson/hjson-go v3.3.0+incompatible - github.com/sirupsen/logrus v1.9.2 + github.com/sirupsen/logrus v1.9.3 ) require ( @@ -15,24 +15,24 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // 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/getsentry/sentry-go v0.21.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // 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/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.15.1 // indirect github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.43.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect ) diff --git a/go.sum b/go.sum index 2f361ba..121a43f 100644 --- a/go.sum +++ b/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/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/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/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/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= -github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= -github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/errors v1.10.0 h1:lfxS8zZz1+OjtV4MtNWgboi/W5tyLEB6VQZBXN+0VUU= +github.com/cockroachdb/errors v1.10.0/go.mod h1:lknhIsEVQ9Ss/qKDBQS/UqFSvPQjOwNq2qyKAxtHRqE= 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/pebble v0.0.0-20230517202617-f6eaf9a696e6 h1:qmPygPODBjUWt7dT3kR118Yn7y0Ol6ULetf78jj/fNQ= -github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6/go.mod h1:TkdVsGYRqtULUppt2RbC+YaKtTHnHoWa2apfFrSKABw= -github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/redact v1.1.4 h1:Y0XrVI2FAyofNyGveodTN//qdpPtFKcKTeCBsK3AHAQ= -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/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839 h1:avSJHOeU0yPVq2rSBuBLlBC7Q6hU1J3IEPAt9fK7bvY= +github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839/go.mod h1:TkdVsGYRqtULUppt2RbC+YaKtTHnHoWa2apfFrSKABw= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/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/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/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-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/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/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.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.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 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/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 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.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/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/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.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -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/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= +github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 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/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/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/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/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 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/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/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_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/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= -github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -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/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 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/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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.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.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-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-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-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 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.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-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-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-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.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/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 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-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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= 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-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-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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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-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-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-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-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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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= 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/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.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-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= diff --git a/vendor/github.com/cockroachdb/pebble/Makefile b/vendor/github.com/cockroachdb/pebble/Makefile index 2377f86..5601e27 100644 --- a/vendor/github.com/cockroachdb/pebble/Makefile +++ b/vendor/github.com/cockroachdb/pebble/Makefile @@ -16,6 +16,8 @@ all: @echo " make stressmeta" @echo " make crossversion-meta" @echo " make mod-update" + @echo " make generate" + @echo " make generate-test-data" @echo " make clean" override testflags := @@ -69,6 +71,16 @@ stress-crossversion: generate: ${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: ${GO} get -u ${GO} mod tidy diff --git a/vendor/github.com/cockroachdb/pebble/batch.go b/vendor/github.com/cockroachdb/pebble/batch.go index ff84fe3..20dd566 100644 --- a/vendor/github.com/cockroachdb/pebble/batch.go +++ b/vendor/github.com/cockroachdb/pebble/batch.go @@ -265,6 +265,13 @@ type Batch struct { // then it will only contain key kinds of IngestSST. 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 // seqnum and waiting for the WAL fsync (if needed). Asynchronous // ApplyNoSyncWait, which implies WriteOptions.Sync is true, uses the commit @@ -413,6 +420,7 @@ func (b *Batch) refreshMemTableSize() { b.countRangeDels = 0 b.countRangeKeys = 0 + b.minimumFormatMajorVersion = 0 for r := b.Reader(); ; { kind, key, value, ok := r.Next() if !ok { @@ -423,12 +431,22 @@ func (b *Batch) refreshMemTableSize() { b.countRangeDels++ case InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete: b.countRangeKeys++ + case InternalKeyKindDeleteSized: + if b.minimumFormatMajorVersion < ExperimentalFormatDeleteSizedAndObsolete { + b.minimumFormatMajorVersion = ExperimentalFormatDeleteSizedAndObsolete + } case InternalKeyKindIngestSST: + if b.minimumFormatMajorVersion < FormatFlushableIngest { + b.minimumFormatMajorVersion = FormatFlushableIngest + } // This key kind doesn't contribute to the memtable size. continue } 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. @@ -598,6 +616,41 @@ func (b *Batch) prepareDeferredKeyRecord(keyLen int, kind InternalKeyKind) { 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. // // 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 } +// 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. // 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() { b.countRangeKeys++ + if b.minimumFormatMajorVersion < FormatRangeKeys { + b.minimumFormatMajorVersion = FormatRangeKeys + } if b.index != nil { b.rangeKeys = nil 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 // number of sstable paths which have been added to the batch. b.memTableSize = origMemTableSize + b.minimumFormatMajorVersion = FormatFlushableIngest } // Empty returns true if the batch is empty, and false otherwise. @@ -1199,6 +1322,7 @@ func (b *Batch) Reset() { b.commitStats = BatchCommitStats{} b.commitErr = nil b.applied.Store(false) + b.minimumFormatMajorVersion = 0 if b.data != nil { if cap(b.data) > batchMaxRetainedSize { // 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 { case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete, - InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete: + InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete, + InternalKeyKindDeleteSized: *r, value, ok = batchDecodeStr(*r) if !ok { return 0, nil, nil, false @@ -1500,7 +1625,8 @@ func (i *batchIter) value() []byte { switch InternalKeyKind(data[offset]) { case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete, - InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete: + InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete, + InternalKeyKindDeleteSized: _, value, ok := batchDecodeStr(data[keyEnd:]) if !ok { return nil @@ -1944,7 +2070,8 @@ func (i *flushableBatchIter) value() base.LazyValue { var ok bool switch kind { case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindRangeDelete, - InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete: + InternalKeyKindRangeKeySet, InternalKeyKindRangeKeyUnset, InternalKeyKindRangeKeyDelete, + InternalKeyKindDeleteSized: keyEnd := i.offsets[i.index].keyEnd _, value, ok = batchDecodeStr(i.data[keyEnd:]) if !ok { diff --git a/vendor/github.com/cockroachdb/pebble/compaction.go b/vendor/github.com/cockroachdb/pebble/compaction.go index fcc840d..51b080b 100644 --- a/vendor/github.com/cockroachdb/pebble/compaction.go +++ b/vendor/github.com/cockroachdb/pebble/compaction.go @@ -1333,10 +1333,10 @@ func (c *compaction) newInputIter( // internal iterator interface). The resulting merged rangedel iterator is // then included with the point levels in a single mergingIter. newRangeDelIter := func( - f manifest.LevelFile, _ *IterOptions, bytesIterated *uint64, + f manifest.LevelFile, _ *IterOptions, l manifest.Level, bytesIterated *uint64, ) (keyspan.FragmentIterator, error) { 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 { // 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 @@ -1415,6 +1415,10 @@ func (c *compaction) newInputIter( iterOpts := IterOptions{logger: c.logger} // TODO(bananabrick): Get rid of the extra manifest.Level parameter and fold it into // 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 { iters = append(iters, newLevelIter(iterOpts, c.cmp, nil /* split */, newIters, level.files.Iter(), l, &c.bytesIterated)) @@ -1455,8 +1459,10 @@ func (c *compaction) newInputIter( // mergingIter. iter := level.files.Iter() 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 { + // 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)) } if rangeDelIter != emptyKeyspanIter { @@ -1474,7 +1480,7 @@ func (c *compaction) newInputIter( } if hasRangeKeys { 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) if iter != nil { // Ensure that the range key iter is not closed until the compaction is @@ -1495,7 +1501,7 @@ func (c *compaction) newInputIter( } 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) } return nil @@ -2066,12 +2072,12 @@ func (d *DB) flush1() (bytesFlushed uint64, err error) { bytesFlushed = c.bytesIterated d.mu.snapshots.cumulativePinnedCount += stats.cumulativePinnedKeys d.mu.snapshots.cumulativePinnedSize += stats.cumulativePinnedSize + d.mu.versions.metrics.Keys.MissizedTombstonesCount += stats.countMissizedDels d.maybeUpdateDeleteCompactionHints(c) d.clearCompactingState(c, err != nil) delete(d.mu.compact.inProgress, c) d.mu.versions.incrementCompactions(c.kind, c.extraLevels) - d.mu.versions.incrementCompactionBytes(-c.bytesWritten) var flushed flushableList if err == nil { @@ -2559,9 +2565,20 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) { info.Duration = d.timeNow().Sub(startTime) if err == nil { d.mu.versions.logLock() - err = d.mu.versions.logAndApply(jobID, ve, c.metrics, false /* forceRotation */, func() []compactionInfo { - return d.getInProgressCompactionInfoLocked(c) - }) + // Confirm if any of this compaction's inputs were deleted while this + // 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 { // TODO(peter): untested. for _, f := range pendingOutputs { @@ -2617,6 +2634,7 @@ func (d *DB) compact1(c *compaction, errChannel chan error) (err error) { type compactStats struct { cumulativePinnedKeys uint64 cumulativePinnedSize uint64 + countMissizedDels uint64 } // runCompactions runs a compaction that produces new on-disk tables from @@ -2850,10 +2868,12 @@ func (d *DB) runCompaction( Path: d.objProvider.Path(objMeta), FileNum: fileNum, }) - writable = &compactionWritable{ - Writable: writable, - versions: d.mu.versions, - written: &c.bytesWritten, + if c.kind != compactionKindFlush { + writable = &compactionWritable{ + Writable: writable, + versions: d.mu.versions, + written: &c.bytesWritten, + } } createdFiles = append(createdFiles, fileNum.DiskFileNum()) cacheOpts := private.SSTableCacheOpts(d.cacheID, fileNum.DiskFileNum()).(sstable.WriterOption) @@ -3232,7 +3252,10 @@ func (d *DB) runCompaction( 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 } 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 { return nil, pendingOutputs, stats, err } @@ -3613,7 +3641,7 @@ func (d *DB) doDeleteObsoleteFiles(jobID int) { if len(filesToDelete) > 0 { d.deleters.Add(1) // 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) } else { d.paceAndDeleteObsoleteFiles(jobID, filesToDelete) @@ -3626,14 +3654,20 @@ func (d *DB) doDeleteObsoleteFiles(jobID int) { func (d *DB) paceAndDeleteObsoleteFiles(jobID int, files []obsoleteFile) { defer d.deleters.Done() pacer := (pacer)(nilPacer) - if d.opts.Experimental.MinDeletionRate > 0 { + if d.opts.TargetByteDeletionRate > 0 { pacer = newDeletionPacer(d.deletionLimiter, d.getDeletionPacerInfo) } for _, of := range files { path := base.MakeFilepath(d.opts.FS, of.dir, of.fileType, of.fileNum) 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.versions.metrics.Table.ObsoleteCount-- d.mu.versions.metrics.Table.ObsoleteSize -= of.fileSize diff --git a/vendor/github.com/cockroachdb/pebble/compaction_iter.go b/vendor/github.com/cockroachdb/pebble/compaction_iter.go index e09e5b0..f3e434b 100644 --- a/vendor/github.com/cockroachdb/pebble/compaction_iter.go +++ b/vendor/github.com/cockroachdb/pebble/compaction_iter.go @@ -6,6 +6,7 @@ package pebble import ( "bytes" + "encoding/binary" "fmt" "io" "sort" @@ -205,6 +206,21 @@ type compactionIter struct { // compaction iterator was only returned because an open snapshot prevents // its elision. This field only applies to point keys, and not to range // 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 // The index of the snapshot for the current key within the snapshots slice. curSnapshotIdx int @@ -240,6 +256,10 @@ type compactionIter struct { // The on-disk format major version. This informs the types of keys that // may be written to disk during a compaction. formatVersion FormatMajorVersion + stats struct { + // count of DELSIZED keys that were missized. + countMissizedDels uint64 + } } func newCompactionIter( @@ -310,10 +330,10 @@ func (i *compactionIter) Next() (*InternalKey, []byte) { // respect to `iterKey` and related state: // // - `!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 // 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. if i.pos == iterPosCurForward { if i.skip { @@ -382,11 +402,18 @@ func (i *compactionIter) Next() (*InternalKey, []byte) { } else if cover == keyspan.CoversInvisibly { // i.iterKey would be deleted by a range deletion if there weren't // 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 } switch i.iterKey.Kind() { - case InternalKeyKindDelete, InternalKeyKindSingleDelete: + case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized: if i.elideTombstone(i.iterKey.UserKey) { if i.curSnapshotIdx == 0 { // If we're at the last snapshot stripe and the tombstone @@ -410,6 +437,12 @@ func (i *compactionIter) Next() (*InternalKey, []byte) { i.skip = true 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: if i.singleDeleteNext() { return &i.key, i.value @@ -686,11 +719,12 @@ func (i *compactionIter) setNext() { i.skip = true return case sameStripeSkippable: - // We're still in the same stripe. If this is a DEL/SINGLEDEL, we - // stop looking and emit a SETWITHDEL. Subsequent keys are - // eligible for skipping. + // We're still in the same stripe. If this is a + // DEL/SINGLEDEL/DELSIZED, we stop looking and emit a SETWITHDEL. + // Subsequent keys are eligible for skipping. if i.iterKey.Kind() == InternalKeyKindDelete || - i.iterKey.Kind() == InternalKeyKindSingleDelete { + i.iterKey.Kind() == InternalKeyKindSingleDelete || + i.iterKey.Kind() == InternalKeyKindDeleteSized { i.key.SetKind(InternalKeyKindSetWithDelete) i.skip = true return @@ -715,7 +749,7 @@ func (i *compactionIter) mergeNext(valueMerger ValueMerger) stripeChangeType { } key := i.iterKey switch key.Kind() { - case InternalKeyKindDelete, InternalKeyKindSingleDelete: + case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized: // 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 // 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 switch key.Kind() { - case InternalKeyKindDelete, InternalKeyKindMerge, InternalKeyKindSetWithDelete: - // We've hit a Delete, Merge or SetWithDelete, transform the - // SingleDelete into a full Delete. + case InternalKeyKindDelete, InternalKeyKindMerge, InternalKeyKindSetWithDelete, InternalKeyKindDeleteSized: + // We've hit a Delete, DeleteSized, Merge, SetWithDelete, transform + // the SingleDelete into a full Delete. i.key.SetKind(InternalKeyKindDelete) i.skip = 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() { i.keyBuf = append(i.keyBuf[:0], i.iterKey.UserKey...) i.key.UserKey = i.keyBuf diff --git a/vendor/github.com/cockroachdb/pebble/compaction_picker.go b/vendor/github.com/cockroachdb/pebble/compaction_picker.go index a7af8cf..f5e7b8e 100644 --- a/vendor/github.com/cockroachdb/pebble/compaction_picker.go +++ b/vendor/github.com/cockroachdb/pebble/compaction_picker.go @@ -674,15 +674,16 @@ type candidateLevelInfo struct { 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 // priorities. -func compensatedSize(f *fileMetadata, pointTombstoneWeight float64) uint64 { - sz := f.Size - // Add in the estimate of disk space that may be reclaimed by compacting - // the file's tombstones. - sz += uint64(float64(f.Stats.PointDeletionsBytesEstimate) * pointTombstoneWeight) - sz += f.Stats.RangeDeletionsBytesEstimate - return sz +func compensatedSize(f *fileMetadata) uint64 { + // Add in the estimate of disk space that may be reclaimed by compacting the + // file's tombstones. + return f.Size + fileCompensation(f) } // 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 // stats have been loaded. type compensatedSizeAnnotator struct { - pointTombstoneWeight float64 } var _ manifest.Annotator = compensatedSizeAnnotator{} @@ -709,7 +709,7 @@ func (a compensatedSizeAnnotator) Accumulate( f *fileMetadata, dst interface{}, ) (v interface{}, cacheOK bool) { vptr := dst.(*uint64) - *vptr = *vptr + compensatedSize(f, a.pointTombstoneWeight) + *vptr = *vptr + compensatedSize(f) 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. Use the compensatedSizeAnnotator if querying the total // compensated size of a level. -func totalCompensatedSize(iter manifest.LevelIterator, pointTombstoneWeight float64) uint64 { +func totalCompensatedSize(iter manifest.LevelIterator) uint64 { var sz uint64 for f := iter.First(); f != nil; f = iter.Next() { - sz += compensatedSize(f, pointTombstoneWeight) + sz += compensatedSize(f) } return sz } @@ -925,9 +925,7 @@ func (p *compactionPickerByScore) initLevelMaxBytes(inProgressCompactions []comp } } -func calculateSizeAdjust( - inProgressCompactions []compactionInfo, pointTombstoneWeight float64, -) [numLevels]int64 { +func calculateSizeAdjust(inProgressCompactions []compactionInfo) [numLevels]int64 { // Compute a size adjustment for each level based on the in-progress // compactions. We subtract the compensated size of start level inputs. // Since compensated file sizes may be compensated because they reclaim @@ -937,10 +935,16 @@ func calculateSizeAdjust( var sizeAdjust [numLevels]int64 for i := range inProgressCompactions { 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 { real := int64(input.files.SizeSum()) - compensated := int64(totalCompensatedSize(input.files.Iter(), pointTombstoneWeight)) + compensated := int64(totalCompensatedSize(input.files.Iter())) if input.level != c.outputLevel { sizeAdjust[input.level] -= compensated @@ -967,10 +971,7 @@ func (p *compactionPickerByScore) calculateScores( } scores[0] = p.calculateL0Score(inProgressCompactions) - sizeAdjust := calculateSizeAdjust( - inProgressCompactions, - p.opts.Experimental.PointTombstoneWeight, - ) + sizeAdjust := calculateSizeAdjust(inProgressCompactions) for level := 1; level < numLevels; level++ { levelSize := int64(levelCompensatedSize(p.vers.Levels[level])) + sizeAdjust[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() { 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. - for outputFile != nil && base.InternalCompare(cmp, outputFile.Largest, f.Smallest) < 0 { + for outputFile != nil && sstableKeyCompare(cmp, outputFile.Largest, f.Smallest) < 0 { outputFile = outputIter.Next() } - compacting := f.IsCompacting() - for outputFile != nil && base.InternalCompare(cmp, outputFile.Smallest, f.Largest) < 0 { + for outputFile != nil && sstableKeyCompare(cmp, outputFile.Smallest, f.Largest) <= 0 && !compacting { overlappingBytes += outputFile.Size 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, // break out and don't advance outputIter because f's successor // 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 } outputFile = outputIter.Next() @@ -1127,9 +1159,9 @@ func (p *compactionPickerByScore) pickFile( continue } - compSz := compensatedSize(f, p.opts.Experimental.PointTombstoneWeight) + compSz := compensatedSize(f) scaledRatio := overlappingBytes * 1024 / compSz - if scaledRatio < smallestRatio && !f.IsCompacting() { + if scaledRatio < smallestRatio { smallestRatio = scaledRatio 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", marker, info.level, info.score, info.origScore, 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]), ) diff --git a/vendor/github.com/cockroachdb/pebble/db.go b/vendor/github.com/cockroachdb/pebble/db.go index a12ee56..67d9b35 100644 --- a/vendor/github.com/cockroachdb/pebble/db.go +++ b/vendor/github.com/cockroachdb/pebble/db.go @@ -96,6 +96,22 @@ type Writer interface { // It is safe to modify the contents of the arguments after Delete returns. 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, // 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 } +// 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. // 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") } + 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 d.split == nil { 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. } @@ -1320,6 +1362,7 @@ func (i *Iterator) constructPointIter( levelsIndex := len(levels) mlevels = mlevels[:numMergingLevels] levels = levels[:numLevelIters] + i.opts.snapshotForHideObsoletePoints = buf.dbi.seqNum addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) { li := &levels[levelsIndex] @@ -1523,6 +1566,16 @@ func (d *DB) Close() error { d.mu.Unlock() d.deleters.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() // 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 { // set to true will return the sstable properties in TableInfo 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`. @@ -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 type SSTableInfo struct { manifest.TableInfo @@ -1883,6 +1949,9 @@ func (d *DB) SSTables(opts ...SSTablesOption) ([][]SSTableInfo, error) { iter := srcLevels[i].Iter() j := 0 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()} if opt.withProperties { p, err := d.tableCache.getTableProperties( @@ -1900,6 +1969,7 @@ func (d *DB) SSTables(opts ...SSTablesOption) ([][]SSTableInfo, error) { destLevels[i] = destTables[:j] destTables = destTables[j:] } + return destLevels, nil } diff --git a/vendor/github.com/cockroachdb/pebble/external_iterator.go b/vendor/github.com/cockroachdb/pebble/external_iterator.go index 3c45c2c..8fbef76 100644 --- a/vendor/github.com/cockroachdb/pebble/external_iterator.go +++ b/vendor/github.com/cockroachdb/pebble/external_iterator.go @@ -209,15 +209,15 @@ func createExternalPointIter(ctx context.Context, it *Iterator) (internalIterato pointIter internalIterator err error ) - pointIter, err = r.NewIterWithBlockPropertyFiltersAndContext( - ctx, - it.opts.LowerBound, - it.opts.UpperBound, - nil, /* BlockPropertiesFilterer */ - false, /* useFilterBlock */ - &it.stats.InternalStats, - sstable.TrivialReaderProvider{Reader: r}, - ) + // We could set hideObsoletePoints=true, since we are reading at + // InternalKeySeqNumMax, but we don't bother since these sstables should + // not have obsolete points (so the performance optimization is + // unnecessary), and we don't want to bother constructing a + // BlockPropertiesFilterer that includes obsoleteKeyBlockPropertyFilter. + pointIter, err = r.NewIterWithBlockPropertyFiltersAndContextEtc( + ctx, it.opts.LowerBound, it.opts.UpperBound, nil, /* BlockPropertiesFilterer */ + false /* hideObsoletePoints */, false, /* useFilterBlock */ + &it.stats.InternalStats, sstable.TrivialReaderProvider{Reader: r}) if err != nil { return nil, err } diff --git a/vendor/github.com/cockroachdb/pebble/flushable.go b/vendor/github.com/cockroachdb/pebble/flushable.go index 0a5b2ae..43bab4e 100644 --- a/vendor/github.com/cockroachdb/pebble/flushable.go +++ b/vendor/github.com/cockroachdb/pebble/flushable.go @@ -184,7 +184,7 @@ func (s *ingestedFlushable) newFlushIter(o *IterOptions, bytesFlushed *uint64) i } func (s *ingestedFlushable) constructRangeDelIter( - file *manifest.FileMetadata, _ *keyspan.SpanIterOptions, + file *manifest.FileMetadata, _ keyspan.SpanIterOptions, ) (keyspan.FragmentIterator, error) { // 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. diff --git a/vendor/github.com/cockroachdb/pebble/format_major_version.go b/vendor/github.com/cockroachdb/pebble/format_major_version.go index 6d96297..0a44abe 100644 --- a/vendor/github.com/cockroachdb/pebble/format_major_version.go +++ b/vendor/github.com/cockroachdb/pebble/format_major_version.go @@ -151,8 +151,29 @@ const ( // compactions for files marked for compaction are complete. 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 FormatMajorVersion = iota - 1 + // TODO(jackson): restore FormatNewest to internalFormatNewest. + FormatNewest FormatMajorVersion = FormatPrePebblev1MarkedCompacted ) // MaxTableFormat returns the maximum sstable.TableFormat that can be used at @@ -170,6 +191,8 @@ func (v FormatMajorVersion) MaxTableFormat() sstable.TableFormat { return sstable.TableFormatPebblev2 case FormatSSTableValueBlocks, FormatFlushableIngest, FormatPrePebblev1MarkedCompacted: return sstable.TableFormatPebblev3 + case ExperimentalFormatDeleteSizedAndObsolete, ExperimentalFormatVirtualSSTables: + return sstable.TableFormatPebblev4 default: panic(fmt.Sprintf("pebble: unsupported format major version: %s", v)) } @@ -186,7 +209,8 @@ func (v FormatMajorVersion) MinTableFormat() sstable.TableFormat { return sstable.TableFormatLevelDB case FormatMinTableFormatPebblev1, FormatPrePebblev1Marked, FormatUnusedPrePebblev1MarkedCompacted, FormatSSTableValueBlocks, - FormatFlushableIngest, FormatPrePebblev1MarkedCompacted: + FormatFlushableIngest, FormatPrePebblev1MarkedCompacted, + ExperimentalFormatDeleteSizedAndObsolete, ExperimentalFormatVirtualSSTables: return sstable.TableFormatPebblev1 default: 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) }, + ExperimentalFormatDeleteSizedAndObsolete: func(d *DB) error { + return d.finalizeFormatVersUpgrade(ExperimentalFormatDeleteSizedAndObsolete) + }, + ExperimentalFormatVirtualSSTables: func(d *DB) error { + return d.finalizeFormatVersUpgrade(ExperimentalFormatVirtualSSTables) + }, } const formatVersionMarkerName = `format-version` @@ -341,7 +371,7 @@ func lookupFormatMajorVersion( if vers == FormatDefault { 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 vers, m, nil @@ -377,8 +407,8 @@ func (d *DB) ratchetFormatMajorVersionLocked(formatVers FormatMajorVersion) erro if d.opts.ReadOnly { return ErrReadOnly } - if formatVers > FormatNewest { - // Guard against accidentally forgetting to update FormatNewest. + if formatVers > internalFormatNewest { + // Guard against accidentally forgetting to update internalFormatNewest. return errors.Errorf("pebble: unknown format version %d", formatVers) } if d.mu.formatVers.vers > formatVers { diff --git a/vendor/github.com/cockroachdb/pebble/get_iter.go b/vendor/github.com/cockroachdb/pebble/get_iter.go index f1fc25c..0ac0484 100644 --- a/vendor/github.com/cockroachdb/pebble/get_iter.go +++ b/vendor/github.com/cockroachdb/pebble/get_iter.go @@ -158,7 +158,7 @@ func (g *getIter) Next() (*InternalKey, base.LazyValue) { if n := len(g.l0); n > 0 { files := g.l0[n-1].Iter() 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, files, manifest.L0Sublevel(n), internalIterOpts{}) g.levelIter.initRangeDel(&g.rangeDelIter) @@ -177,7 +177,7 @@ func (g *getIter) Next() (*InternalKey, base.LazyValue) { 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.version.Levels[g.level].Iter(), manifest.Level(g.level), internalIterOpts{}) g.levelIter.initRangeDel(&g.rangeDelIter) diff --git a/vendor/github.com/cockroachdb/pebble/ingest.go b/vendor/github.com/cockroachdb/pebble/ingest.go index 617c8b2..46905a8 100644 --- a/vendor/github.com/cockroachdb/pebble/ingest.go +++ b/vendor/github.com/cockroachdb/pebble/ingest.go @@ -33,6 +33,32 @@ func sstableKeyCompare(userCmp Compare, a, b InternalKey) int { return 0 } +// KeyRange encodes a key range in user key space. A KeyRange's Start is +// inclusive while its End is exclusive. +type KeyRange struct { + Start, End []byte +} + +// Valid returns true if the KeyRange is defined. +func (k *KeyRange) Valid() bool { + return k.Start != nil && k.End != nil +} + +// Contains returns whether the specified key exists in the KeyRange. +func (k *KeyRange) Contains(cmp base.Compare, key InternalKey) bool { + v := cmp(key.UserKey, k.End) + return (v < 0 || (v == 0 && key.IsExclusiveSentinel())) && cmp(k.Start, key.UserKey) <= 0 +} + +// Overlaps checks if the specified file has an overlap with the KeyRange. +// Note that we aren't checking for full containment of m within k, rather just +// that there's some intersection between m and k's bounds. +func (k *KeyRange) Overlaps(cmp base.Compare, m *fileMetadata) bool { + v := cmp(k.Start, m.Largest.UserKey) + return v <= 0 && !(m.Largest.IsExclusiveSentinel() && v == 0) && + cmp(k.End, m.Smallest.UserKey) > 0 +} + func ingestValidateKey(opts *Options, key *InternalKey) error { if key.Kind() == InternalKeyKindInvalid { return base.CorruptionErrorf("pebble: external sstable has corrupted key: %s", @@ -45,6 +71,54 @@ func ingestValidateKey(opts *Options, key *InternalKey) error { return nil } +// ingestLoad1Shared loads the fileMetadata for one shared sstable. It also +// sets the sequence numbers for a shared sstable. +func ingestLoad1Shared( + opts *Options, sm SharedSSTMeta, fileNum base.DiskFileNum, +) (*fileMetadata, error) { + // Don't load table stats. Doing a round trip to shared storage, one SST + // at a time is not worth it as it slows down ingestion. + meta := &fileMetadata{} + meta.FileNum = fileNum.FileNum() + meta.CreationTime = time.Now().Unix() + meta.Virtual = true + meta.Size = sm.Size + meta.InitProviderBacking(fileNum) + // Set the underlying FileBacking's size to the same size as the virtualized + // view of the sstable. This ensures that we don't over-prioritize this + // sstable for compaction just yet, as we do not have a clear sense of + // what parts of this sstable are referenced by other nodes. + meta.FileBacking.Size = sm.Size + seqNum := base.SeqNumForLevel(int(sm.Level)) + if sm.LargestRangeKey.Valid() && sm.LargestRangeKey.UserKey != nil { + meta.HasRangeKeys = true + meta.SmallestRangeKey = sm.SmallestRangeKey + meta.LargestRangeKey = sm.LargestRangeKey + meta.SmallestRangeKey.SetSeqNum(seqNum) + meta.LargestRangeKey.SetSeqNum(seqNum) + meta.SmallestSeqNum = seqNum + meta.LargestSeqNum = seqNum + // Initialize meta.{Smallest,Largest} and others by calling this. + meta.ExtendRangeKeyBounds(opts.Comparer.Compare, meta.SmallestRangeKey, meta.LargestRangeKey) + } + if sm.LargestPointKey.Valid() && sm.LargestPointKey.UserKey != nil { + meta.HasPointKeys = true + meta.SmallestPointKey = sm.SmallestPointKey + meta.LargestPointKey = sm.LargestPointKey + meta.SmallestPointKey.SetSeqNum(seqNum) + meta.LargestPointKey.SetSeqNum(seqNum) + meta.SmallestSeqNum = seqNum + meta.LargestSeqNum = seqNum + // Initialize meta.{Smallest,Largest} and others by calling this. + meta.ExtendPointKeyBounds(opts.Comparer.Compare, meta.SmallestPointKey, meta.LargestPointKey) + } + + if err := meta.Validate(opts.Comparer.Compare, opts.Comparer.FormatKey); err != nil { + return nil, err + } + return meta, nil +} + func ingestLoad1( opts *Options, fmv FormatMajorVersion, path string, cacheID uint64, fileNum base.DiskFileNum, ) (*fileMetadata, error) { @@ -195,22 +269,55 @@ func ingestLoad1( return meta, nil } +type ingestLoadResult struct { + localMeta, sharedMeta []*fileMetadata + localPaths []string + sharedLevels []uint8 +} + func ingestLoad( - opts *Options, fmv FormatMajorVersion, paths []string, cacheID uint64, pending []base.DiskFileNum, -) ([]*fileMetadata, []string, error) { + opts *Options, + fmv FormatMajorVersion, + paths []string, + shared []SharedSSTMeta, + cacheID uint64, + pending []base.DiskFileNum, +) (ingestLoadResult, error) { meta := make([]*fileMetadata, 0, len(paths)) newPaths := make([]string, 0, len(paths)) for i := range paths { m, err := ingestLoad1(opts, fmv, paths[i], cacheID, pending[i]) if err != nil { - return nil, nil, err + return ingestLoadResult{}, err } if m != nil { meta = append(meta, m) newPaths = append(newPaths, paths[i]) } } - return meta, newPaths, nil + if len(shared) == 0 { + return ingestLoadResult{localMeta: meta, localPaths: newPaths}, nil + } + sharedMeta := make([]*fileMetadata, 0, len(shared)) + levels := make([]uint8, 0, len(shared)) + for i := range shared { + m, err := ingestLoad1Shared(opts, shared[i], pending[len(paths)+i]) + if err != nil { + return ingestLoadResult{}, err + } + if shared[i].Level < sharedLevelsStart { + return ingestLoadResult{}, errors.New("cannot ingest shared file in level below sharedLevelsStart") + } + sharedMeta = append(sharedMeta, m) + levels = append(levels, shared[i].Level) + } + result := ingestLoadResult{ + localMeta: meta, + sharedMeta: sharedMeta, + localPaths: newPaths, + sharedLevels: levels, + } + return result, nil } // Struct for sorting metadatas by smallest user keys, while ensuring the @@ -235,20 +342,48 @@ func (m metaAndPaths) Swap(i, j int) { m.paths[i], m.paths[j] = m.paths[j], m.paths[i] } -func ingestSortAndVerify(cmp Compare, meta []*fileMetadata, paths []string) error { - if len(meta) <= 1 { +func ingestSortAndVerify(cmp Compare, lr ingestLoadResult, exciseSpan KeyRange) error { + // Verify that all the shared files (i.e. files in sharedMeta) + // fit within the exciseSpan. + for i := range lr.sharedMeta { + f := lr.sharedMeta[i] + if !exciseSpan.Contains(cmp, f.Smallest) || !exciseSpan.Contains(cmp, f.Largest) { + return errors.AssertionFailedf("pebble: shared file outside of excise span") + } + } + if len(lr.localMeta) <= 1 || len(lr.localPaths) <= 1 { return nil } sort.Sort(&metaAndPaths{ - meta: meta, - paths: paths, + meta: lr.localMeta, + paths: lr.localPaths, cmp: cmp, }) - for i := 1; i < len(meta); i++ { - if sstableKeyCompare(cmp, meta[i-1].Largest, meta[i].Smallest) >= 0 { - return errors.New("pebble: external sstables have overlapping ranges") + for i := 1; i < len(lr.localPaths); i++ { + if sstableKeyCompare(cmp, lr.localMeta[i-1].Largest, lr.localMeta[i].Smallest) >= 0 { + return errors.AssertionFailedf("pebble: external sstables have overlapping ranges") + } + } + if len(lr.sharedMeta) == 0 { + return nil + } + filesInLevel := make([]*fileMetadata, 0, len(lr.sharedMeta)) + for l := sharedLevelsStart; l < numLevels; l++ { + filesInLevel = filesInLevel[:0] + for i := range lr.sharedMeta { + if lr.sharedLevels[i] == uint8(l) { + filesInLevel = append(filesInLevel, lr.sharedMeta[i]) + } + } + sort.Slice(filesInLevel, func(i, j int) bool { + return cmp(filesInLevel[i].Smallest.UserKey, filesInLevel[j].Smallest.UserKey) < 0 + }) + for i := 1; i < len(filesInLevel); i++ { + if sstableKeyCompare(cmp, filesInLevel[i-1].Largest, filesInLevel[i].Smallest) >= 0 { + return errors.AssertionFailedf("pebble: external shared sstables have overlapping ranges") + } } } return nil @@ -265,17 +400,21 @@ func ingestCleanup(objProvider objstorage.Provider, meta []*fileMetadata) error } // ingestLink creates new objects which are backed by either hardlinks to or -// copies of the ingested files. +// copies of the ingested files. It also attaches shared objects to the provider. func ingestLink( - jobID int, opts *Options, objProvider objstorage.Provider, paths []string, meta []*fileMetadata, + jobID int, + opts *Options, + objProvider objstorage.Provider, + lr ingestLoadResult, + shared []SharedSSTMeta, ) error { - for i := range paths { + for i := range lr.localPaths { objMeta, err := objProvider.LinkOrCopyFromLocal( - context.TODO(), opts.FS, paths[i], fileTypeTable, meta[i].FileBacking.DiskFileNum, + context.TODO(), opts.FS, lr.localPaths[i], fileTypeTable, lr.localMeta[i].FileBacking.DiskFileNum, objstorage.CreateOptions{PreferSharedStorage: true}, ) if err != nil { - if err2 := ingestCleanup(objProvider, meta[:i]); err2 != nil { + if err2 := ingestCleanup(objProvider, lr.localMeta[:i]); err2 != nil { opts.Logger.Infof("ingest cleanup failed: %v", err2) } return err @@ -285,7 +424,33 @@ func ingestLink( JobID: jobID, Reason: "ingesting", Path: objProvider.Path(objMeta), - FileNum: meta[i].FileNum, + FileNum: lr.localMeta[i].FileNum, + }) + } + } + sharedObjs := make([]objstorage.SharedObjectToAttach, 0, len(shared)) + for i := range shared { + backing, err := shared[i].Backing.Get() + if err != nil { + return err + } + sharedObjs = append(sharedObjs, objstorage.SharedObjectToAttach{ + FileNum: lr.sharedMeta[i].FileBacking.DiskFileNum, + FileType: fileTypeTable, + Backing: backing, + }) + } + sharedObjMetas, err := objProvider.AttachSharedObjects(sharedObjs) + if err != nil { + return err + } + for i := range sharedObjMetas { + if opts.EventListener.TableCreated != nil { + opts.EventListener.TableCreated(TableCreateInfo{ + JobID: jobID, + Reason: "ingesting", + Path: objProvider.Path(sharedObjMetas[i]), + FileNum: lr.sharedMeta[i].FileNum, }) } } @@ -310,7 +475,8 @@ func ingestMemtableOverlaps(cmp Compare, mem flushable, meta []*fileMetadata) bo } for _, m := range meta { - if overlapWithIterator(iter, &rangeDelIter, rkeyIter, m, cmp) { + kr := internalKeyRange{smallest: m.Smallest, largest: m.Largest} + if overlapWithIterator(iter, &rangeDelIter, rkeyIter, kr, cmp) { closeIters() return true } @@ -362,21 +528,25 @@ func ingestUpdateSeqNum( return nil } +type internalKeyRange struct { + smallest, largest InternalKey +} + func overlapWithIterator( iter internalIterator, rangeDelIter *keyspan.FragmentIterator, rkeyIter keyspan.FragmentIterator, - meta *fileMetadata, + keyRange internalKeyRange, cmp Compare, ) bool { // Check overlap with point operations. // // When using levelIter, it seeks to the SST whose boundaries - // contain meta.Smallest.UserKey(S). + // contain keyRange.smallest.UserKey(S). // It then tries to find a point in that SST that is >= S. // If there's no such point it means the SST ends in a tombstone in which case // levelIter.SeekGE generates a boundary range del sentinel. - // The comparison of this boundary with meta.Largest(L) below + // The comparison of this boundary with keyRange.largest(L) below // is subtle but maintains correctness. // 1) boundary < L, // since boundary is also > S (initial seek), @@ -388,9 +558,9 @@ func overlapWithIterator( // means boundary < L and hence is similar to 1). // 4) boundary == L and L is sentinel, // we'll always overlap since for any values of i,j ranges [i, k) and [j, k) always overlap. - key, _ := iter.SeekGE(meta.Smallest.UserKey, base.SeekGEFlagsNone) + key, _ := iter.SeekGE(keyRange.smallest.UserKey, base.SeekGEFlagsNone) if key != nil { - c := sstableKeyCompare(cmp, *key, meta.Largest) + c := sstableKeyCompare(cmp, *key, keyRange.largest) if c <= 0 { return true } @@ -402,7 +572,7 @@ func overlapWithIterator( computeOverlapWithSpans := func(rIter keyspan.FragmentIterator) bool { // NB: The spans surfaced by the fragment iterator are non-overlapping. - span := rIter.SeekLT(meta.Smallest.UserKey) + span := rIter.SeekLT(keyRange.smallest.UserKey) if span == nil { span = rIter.Next() } @@ -411,13 +581,13 @@ func overlapWithIterator( continue } key := span.SmallestKey() - c := sstableKeyCompare(cmp, key, meta.Largest) + c := sstableKeyCompare(cmp, key, keyRange.largest) if c > 0 { // The start of the span is after the largest key in the // ingested table. return false } - if cmp(span.End, meta.Smallest.UserKey) > 0 { + if cmp(span.End, keyRange.smallest.UserKey) > 0 { // The end of the span is greater than the smallest in the // table. Note that the span end key is exclusive, thus ">0" // instead of ">=0". @@ -537,7 +707,11 @@ func ingestTargetLevel( v.L0Sublevels.Levels[subLevel].Iter(), manifest.Level(0), manifest.KeyTypeRange, ) - overlap := overlapWithIterator(iter, &rangeDelIter, &levelIter, meta, cmp) + kr := internalKeyRange{ + smallest: meta.Smallest, + largest: meta.Largest, + } + overlap := overlapWithIterator(iter, &rangeDelIter, &levelIter, kr, cmp) err := iter.Close() // Closes range del iter as well. err = firstError(err, levelIter.Close()) if err != nil { @@ -563,7 +737,11 @@ func ingestTargetLevel( v.Levels[level].Iter(), manifest.Level(level), manifest.KeyTypeRange, ) - overlap := overlapWithIterator(levelIter, &rangeDelIter, rkeyLevelIter, meta, cmp) + kr := internalKeyRange{ + smallest: meta.Smallest, + largest: meta.Largest, + } + overlap := overlapWithIterator(levelIter, &rangeDelIter, rkeyLevelIter, kr, cmp) err := levelIter.Close() // Closes range del iter as well. err = firstError(err, rkeyLevelIter.Close()) if err != nil { @@ -612,6 +790,13 @@ func ingestTargetLevel( // the same filesystem as the DB. Sstables can be created for ingestion using // sstable.Writer. On success, Ingest removes the input paths. // +// Two types of sstables are accepted for ingestion(s): one is sstables present +// in the instance's vfs.FS and can be referenced locally. The other is sstables +// present in shared.Storage, referred to as shared or foreign sstables. These +// shared sstables can be linked through objstorageprovider.Provider, and do not +// need to already be present on the local vfs.FS. Foreign sstables must all fit +// in an excise span, and are destined for a level specified in SharedSSTMeta. +// // All sstables *must* be Sync()'d by the caller after all bytes are written // and before its file handle is closed; failure to do so could violate // durability or lead to corrupted on-disk state. This method cannot, in a @@ -623,21 +808,28 @@ func ingestTargetLevel( // Ingestion loads each sstable into the lowest level of the LSM which it // doesn't overlap (see ingestTargetLevel). If an sstable overlaps a memtable, // ingestion forces the memtable to flush, and then waits for the flush to -// occur. +// occur. In some cases, such as with no foreign sstables and no excise span, +// ingestion that gets blocked on a memtable can join the flushable queue and +// finish even before the memtable has been flushed. // // The steps for ingestion are: // // 1. Allocate file numbers for every sstable being ingested. -// 2. Load the metadata for all sstables being ingest. -// 3. Sort the sstables by smallest key, verifying non overlap. -// 4. Hard link (or copy) the sstables into the DB directory. +// 2. Load the metadata for all sstables being ingested. +// 3. Sort the sstables by smallest key, verifying non overlap (for local +// sstables). +// 4. Hard link (or copy) the local sstables into the DB directory. // 5. Allocate a sequence number to use for all of the entries in the -// sstables. This is the step where overlap with memtables is +// local sstables. This is the step where overlap with memtables is // determined. If there is overlap, we remember the most recent memtable // that overlaps. -// 6. Update the sequence number in the ingested sstables. +// 6. Update the sequence number in the ingested local sstables. (Shared +// sstables get fixed sequence numbers that were determined at load time.) // 7. Wait for the most recent memtable that overlaps to flush (if any). // 8. Add the ingested sstables to the version (DB.ingestApply). +// 8.1. If an excise span was specified, figure out what sstables in the +// current version overlap with the excise span, and create new virtual +// sstables out of those sstables that exclude the excised span (DB.excise). // 9. Publish the ingestion sequence number. // // Note that if the mutable memtable overlaps with ingestion, a flush of the @@ -654,7 +846,7 @@ func (d *DB) Ingest(paths []string) error { if d.opts.ReadOnly { return ErrReadOnly } - _, err := d.ingest(paths, ingestTargetLevel) + _, err := d.ingest(paths, ingestTargetLevel, nil /* shared */, KeyRange{}) return err } @@ -683,7 +875,29 @@ func (d *DB) IngestWithStats(paths []string) (IngestOperationStats, error) { if d.opts.ReadOnly { return IngestOperationStats{}, ErrReadOnly } - return d.ingest(paths, ingestTargetLevel) + return d.ingest(paths, ingestTargetLevel, nil /* shared */, KeyRange{}) +} + +// IngestAndExcise does the same as IngestWithStats, and additionally accepts a +// list of shared files to ingest that can be read from a shared.Storage through +// a Provider. All the shared files must live within exciseSpan, and any existing +// keys in exciseSpan are deleted by turning existing sstables into virtual +// sstables (if not virtual already) and shrinking their spans to exclude +// exciseSpan. See the comment at Ingest for a more complete picture of the +// ingestion process. +// +// Panics if this DB instance was not instantiated with a shared.Storage and +// shared sstables are present. +func (d *DB) IngestAndExcise( + paths []string, shared []SharedSSTMeta, exciseSpan KeyRange, +) (IngestOperationStats, error) { + if err := d.closed.Load(); err != nil { + panic(err) + } + if d.opts.ReadOnly { + return IngestOperationStats{}, ErrReadOnly + } + return d.ingest(paths, ingestTargetLevel, shared, exciseSpan) } // Both DB.mu and commitPipeline.mu must be held while this is called. @@ -793,36 +1007,50 @@ func (d *DB) handleIngestAsFlushable(meta []*fileMetadata, seqNum uint64) error return nil } +// See comment at Ingest() for details on how this works. func (d *DB) ingest( - paths []string, targetLevelFunc ingestTargetLevelFunc, + paths []string, + targetLevelFunc ingestTargetLevelFunc, + shared []SharedSSTMeta, + exciseSpan KeyRange, ) (IngestOperationStats, error) { + if len(shared) > 0 && d.opts.Experimental.SharedStorage == nil { + panic("cannot ingest shared sstables with nil SharedStorage") + } + if (exciseSpan.Valid() || len(shared) > 0) && d.opts.FormatMajorVersion < ExperimentalFormatVirtualSSTables { + return IngestOperationStats{}, errors.New("pebble: format major version too old for excise or shared sstable ingestion") + } // Allocate file numbers for all of the files being ingested and mark them as // pending in order to prevent them from being deleted. Note that this causes // the file number ordering to be out of alignment with sequence number // ordering. The sorting of L0 tables by sequence number avoids relying on // that (busted) invariant. d.mu.Lock() - pendingOutputs := make([]base.DiskFileNum, len(paths)) + pendingOutputs := make([]base.DiskFileNum, len(paths)+len(shared)) for i := range paths { pendingOutputs[i] = d.mu.versions.getNextFileNum().DiskFileNum() } + for i := range shared { + pendingOutputs[len(paths)+i] = d.mu.versions.getNextFileNum().DiskFileNum() + } jobID := d.mu.nextJobID d.mu.nextJobID++ d.mu.Unlock() // Load the metadata for all of the files being ingested. This step detects // and elides empty sstables. - meta, paths, err := ingestLoad(d.opts, d.FormatMajorVersion(), paths, d.cacheID, pendingOutputs) + loadResult, err := ingestLoad(d.opts, d.FormatMajorVersion(), paths, shared, d.cacheID, pendingOutputs) if err != nil { return IngestOperationStats{}, err } - if len(meta) == 0 { + + if len(loadResult.localMeta) == 0 && len(loadResult.sharedMeta) == 0 { // All of the sstables to be ingested were empty. Nothing to do. return IngestOperationStats{}, nil } // Verify the sstables do not overlap. - if err := ingestSortAndVerify(d.cmp, meta, paths); err != nil { + if err := ingestSortAndVerify(d.cmp, loadResult, exciseSpan); err != nil { return IngestOperationStats{}, err } @@ -831,7 +1059,7 @@ func (d *DB) ingest( // (e.g. because the files reside on a different filesystem), ingestLink will // fall back to copying, and if that fails we undo our work and return an // error. - if err := ingestLink(jobID, d.opts, d.objProvider, paths, meta); err != nil { + if err := ingestLink(jobID, d.opts, d.objProvider, loadResult, shared); err != nil { return IngestOperationStats{}, err } // Make the new tables durable. We need to do this at some point before we @@ -844,11 +1072,12 @@ func (d *DB) ingest( // metaFlushableOverlaps is a slice parallel to meta indicating which of the // ingested sstables overlap some table in the flushable queue. It's used to // approximate ingest-into-L0 stats when using flushable ingests. - metaFlushableOverlaps := make([]bool, len(meta)) + metaFlushableOverlaps := make([]bool, len(loadResult.localMeta)+len(loadResult.sharedMeta)) var mem *flushableEntry var mut *memTable // asFlushable indicates whether the sstable was ingested as a flushable. var asFlushable bool + var overlapWithExciseSpan bool prepare := func(seqNum uint64) { // Note that d.commit.mu is held by commitPipeline when calling prepare. @@ -865,12 +1094,17 @@ func (d *DB) ingest( iter := m.newIter(nil) rangeDelIter := m.newRangeDelIter(nil) rkeyIter := m.newRangeKeyIter(nil) - for i := range meta { + + checkForOverlap := func(i int, meta *fileMetadata) { if metaFlushableOverlaps[i] { // This table already overlapped a more recent flushable. - continue + return } - if overlapWithIterator(iter, &rangeDelIter, rkeyIter, meta[i], d.cmp) { + kr := internalKeyRange{ + smallest: meta.Smallest, + largest: meta.Largest, + } + if overlapWithIterator(iter, &rangeDelIter, rkeyIter, kr, d.cmp) { // If this is the first table to overlap a flushable, save // the flushable. This ingest must be ingested or flushed // after it. @@ -880,6 +1114,24 @@ func (d *DB) ingest( metaFlushableOverlaps[i] = true } } + for i := range loadResult.localMeta { + checkForOverlap(i, loadResult.localMeta[i]) + } + for i := range loadResult.sharedMeta { + checkForOverlap(len(loadResult.localMeta)+i, loadResult.sharedMeta[i]) + } + if exciseSpan.Valid() { + kr := internalKeyRange{ + smallest: base.MakeInternalKey(exciseSpan.Start, InternalKeySeqNumMax, InternalKeyKindMax), + largest: base.MakeExclusiveSentinelKey(InternalKeyKindRangeDelete, exciseSpan.End), + } + if overlapWithIterator(iter, &rangeDelIter, rkeyIter, kr, d.cmp) { + if mem == nil { + mem = m + } + overlapWithExciseSpan = true + } + } err := iter.Close() if rangeDelIter != nil { err = firstError(err, rangeDelIter.Close()) @@ -910,9 +1162,16 @@ func (d *DB) ingest( // The ingestion overlaps with some entry in the flushable queue. if d.mu.formatVers.vers < FormatFlushableIngest || d.opts.Experimental.DisableIngestAsFlushable() || + len(shared) > 0 || overlapWithExciseSpan || (len(d.mu.mem.queue) > d.opts.MemTableStopWritesThreshold-1) { // We're not able to ingest as a flushable, // so we must synchronously flush. + // + // TODO(bilal): Currently, if any of the files being ingested are shared or + // there's overlap between the memtable and an excise span, we cannot use + // flushable ingests and need to wait synchronously. Either remove this + // caveat by fleshing out flushable ingest logic to also account for these + // cases, or remove this TODO. if mem.flushable == d.mu.mem.mutable { err = d.makeRoomForWrite(nil) } @@ -932,7 +1191,7 @@ func (d *DB) ingest( // Since there aren't too many memtables already queued up, we can // slide the ingested sstables on top of the existing memtables. asFlushable = true - err = d.handleIngestAsFlushable(meta, seqNum) + err = d.handleIngestAsFlushable(loadResult.localMeta, seqNum) } var ve *versionEdit @@ -940,20 +1199,31 @@ func (d *DB) ingest( if err != nil || asFlushable { // An error occurred during prepare. if mut != nil { - mut.writerUnref() + if mut.writerUnref() { + d.mu.Lock() + d.maybeScheduleFlush() + d.mu.Unlock() + } } return } - // Update the sequence number for all of the sstables in the + // Update the sequence number for all local sstables in the // metadata. Writing the metadata to the manifest when the // version edit is applied is the mechanism that persists the // sequence number. The sstables themselves are left unmodified. + // + // For shared sstables, we do not need to update sequence numbers. These + // sequence numbers are already set in ingestLoad. if err = ingestUpdateSeqNum( - d.cmp, d.opts.Comparer.FormatKey, seqNum, meta, + d.cmp, d.opts.Comparer.FormatKey, seqNum, loadResult.localMeta, ); err != nil { if mut != nil { - mut.writerUnref() + if mut.writerUnref() { + d.mu.Lock() + d.maybeScheduleFlush() + d.mu.Unlock() + } } return } @@ -966,28 +1236,34 @@ func (d *DB) ingest( // Assign the sstables to the correct level in the LSM and apply the // version edit. - ve, err = d.ingestApply(jobID, meta, targetLevelFunc, mut) + ve, err = d.ingestApply(jobID, loadResult, targetLevelFunc, mut, exciseSpan) } - d.commit.AllocateSeqNum(len(meta), prepare, apply) + d.commit.AllocateSeqNum(len(loadResult.localPaths), prepare, apply) if err != nil { - if err2 := ingestCleanup(d.objProvider, meta); err2 != nil { + if err2 := ingestCleanup(d.objProvider, loadResult.localMeta); err2 != nil { d.opts.Logger.Infof("ingest cleanup failed: %v", err2) } } else { // Since we either created a hard link to the ingesting files, or copied // them over, it is safe to remove the originals paths. - for _, path := range paths { + for _, path := range loadResult.localPaths { if err2 := d.opts.FS.Remove(path); err2 != nil { d.opts.Logger.Infof("ingest failed to remove original file: %s", err2) } } } + // NB: Shared-sstable-only ingestions do not assign a sequence number to + // any sstables. + globalSeqNum := uint64(0) + if len(loadResult.localMeta) > 0 { + globalSeqNum = loadResult.localMeta[0].SmallestSeqNum + } info := TableIngestInfo{ JobID: jobID, - GlobalSeqNum: meta[0].SmallestSeqNum, + GlobalSeqNum: globalSeqNum, Err: err, flushable: asFlushable, } @@ -1005,16 +1281,17 @@ func (d *DB) ingest( if e.Level == 0 { stats.ApproxIngestedIntoL0Bytes += e.Meta.Size } - if metaFlushableOverlaps[i] { + if i < len(metaFlushableOverlaps) && metaFlushableOverlaps[i] { stats.MemtableOverlappingFiles++ } } } else if asFlushable { + // NB: If asFlushable == true, there are no shared sstables. info.Tables = make([]struct { TableInfo Level int - }, len(meta)) - for i, f := range meta { + }, len(loadResult.localMeta)) + for i, f := range loadResult.localMeta { info.Tables[i].Level = -1 info.Tables[i].TableInfo = f.TableInfo() stats.Bytes += f.Size @@ -1037,6 +1314,266 @@ func (d *DB) ingest( return stats, err } +// excise updates ve to include a replacement of the file m with new virtual +// sstables that exclude exciseSpan, returning a slice of newly-created files if +// any. If the entirety of m is deleted by exciseSpan, no new sstables are added +// and m is deleted. Note that ve is updated in-place. +// +// The manifest lock must be held when calling this method. +func (d *DB) excise( + exciseSpan KeyRange, m *fileMetadata, ve *versionEdit, level int, +) ([]manifest.NewFileEntry, error) { + numCreatedFiles := 0 + // Check if there's actually an overlap between m and exciseSpan. + if !exciseSpan.Overlaps(d.cmp, m) { + return nil, nil + } + ve.DeletedFiles[deletedFileEntry{ + Level: level, + FileNum: m.FileNum, + }] = m + // Fast path: m sits entirely within the exciseSpan, so just delete it. + if exciseSpan.Contains(d.cmp, m.Smallest) && exciseSpan.Contains(d.cmp, m.Largest) { + return nil, nil + } + var iter internalIterator + var rangeDelIter keyspan.FragmentIterator + var rangeKeyIter keyspan.FragmentIterator + backingTableCreated := false + // Create a file to the left of the excise span, if necessary. + // The bounds of this file will be [m.Smallest, lastKeyBefore(exciseSpan.Start)]. + // + // We create bounds that are tight on user keys, and we make the effort to find + // the last key in the original sstable that's smaller than exciseSpan.Start + // even though it requires some sstable reads. We could choose to create + // virtual sstables on loose userKey bounds, in which case we could just set + // leftFile.Largest to an exclusive sentinel at exciseSpan.Start. The biggest + // issue with that approach would be that it'd lead to lots of small virtual + // sstables in the LSM that have no guarantee on containing even a single user + // key within the file bounds. This has the potential to increase both read and + // write-amp as we will be opening up these sstables only to find no relevant + // keys in the read path, and compacting sstables on top of them instead of + // directly into the space occupied by them. We choose to incur the cost of + // calculating tight bounds at this time instead of creating more work in the + // future. + // + // TODO(bilal): Some of this work can happen without grabbing the manifest + // lock; we could grab one currentVersion, release the lock, calculate excised + // files, then grab the lock again and recalculate for just the files that + // have changed since our previous calculation. Do this optimiaztino as part of + // https://github.com/cockroachdb/pebble/issues/2112 . + if d.cmp(m.Smallest.UserKey, exciseSpan.Start) < 0 { + leftFile := &fileMetadata{ + Virtual: true, + FileBacking: m.FileBacking, + FileNum: d.mu.versions.getNextFileNum(), + } + leftFile.Smallest = m.Smallest + leftFile.SmallestRangeKey = m.SmallestRangeKey + leftFile.SmallestPointKey = m.SmallestPointKey + leftFile.HasPointKeys = m.HasPointKeys + leftFile.HasRangeKeys = m.HasRangeKeys + if m.HasPointKeys && exciseSpan.Contains(d.cmp, m.SmallestPointKey) { + // This file will not contain any point keys, but will contain range keys. + leftFile.HasPointKeys = false + leftFile.Smallest = m.SmallestRangeKey + } else if m.HasRangeKeys && exciseSpan.Contains(d.cmp, m.SmallestRangeKey) { + leftFile.HasRangeKeys = false + leftFile.Smallest = m.SmallestPointKey + } + if leftFile.HasPointKeys { + var err error + iter, rangeDelIter, err = d.newIters(context.TODO(), m, &IterOptions{level: manifest.Level(level)}, internalIterOpts{}) + if err != nil { + return nil, err + } + var key *InternalKey + if iter != nil { + defer iter.Close() + key, _ = iter.SeekLT(exciseSpan.Start, base.SeekLTFlagsNone) + } else { + iter = emptyIter + } + // Store the min of (exciseSpan.Start, rdel.End) in lastRangeDel. This + // needs to be a copy if the key is owned by the range del iter. + var lastRangeDel []byte + if rangeDelIter != nil { + defer rangeDelIter.Close() + rdel := rangeDelIter.SeekLT(exciseSpan.Start) + if rdel != nil { + lastRangeDel = append(lastRangeDel[:0], rdel.End...) + if d.cmp(lastRangeDel, exciseSpan.Start) > 0 { + lastRangeDel = exciseSpan.Start + } + } + } else { + rangeDelIter = emptyKeyspanIter + } + leftFile.HasPointKeys = key != nil || lastRangeDel != nil + if key != nil && (lastRangeDel == nil || d.cmp(lastRangeDel, key.UserKey) <= 0) { + leftFile.LargestPointKey = key.Clone() + } else if lastRangeDel != nil { + // key == nil || lastRangeDel > key.UserKey. + leftFile.LargestPointKey = base.MakeExclusiveSentinelKey(InternalKeyKindRangeDelete, lastRangeDel) + } + leftFile.Largest = leftFile.LargestPointKey + } + if leftFile.HasRangeKeys { + var err error + rangeKeyIter, err = d.tableNewRangeKeyIter(m, keyspan.SpanIterOptions{}) + if err != nil { + return nil, err + } + // Store the min of (exciseSpan.Start, rkey.End) in lastRangeKey. This + // needs to be a copy if the key is owned by the range key iter. + var lastRangeKey []byte + var lastRangeKeyKind InternalKeyKind + defer rangeKeyIter.Close() + rkey := rangeKeyIter.SeekLT(exciseSpan.Start) + if rkey != nil { + lastRangeKey = append(lastRangeKey[:0], rkey.End...) + if d.cmp(lastRangeKey, exciseSpan.Start) > 0 { + lastRangeKey = exciseSpan.Start + } + lastRangeKeyKind = rkey.Keys[0].Kind() + } + leftFile.HasRangeKeys = lastRangeKey != nil + if leftFile.HasRangeKeys { + leftFile.LargestRangeKey = base.MakeExclusiveSentinelKey(lastRangeKeyKind, lastRangeKey) + if !leftFile.HasPointKeys || base.InternalCompare(d.cmp, leftFile.LargestPointKey, leftFile.LargestRangeKey) < 0 { + leftFile.Largest = leftFile.LargestRangeKey + } + } + } + if leftFile.HasRangeKeys || leftFile.HasPointKeys { + var err error + leftFile.Size, err = d.tableCache.estimateSize(m, leftFile.Smallest.UserKey, leftFile.Largest.UserKey) + if err != nil { + return nil, err + } + if err := leftFile.Validate(d.cmp, d.opts.Comparer.FormatKey); err != nil { + return nil, err + } + ve.NewFiles = append(ve.NewFiles, newFileEntry{Level: level, Meta: leftFile}) + ve.CreatedBackingTables = append(ve.CreatedBackingTables, leftFile.FileBacking) + backingTableCreated = true + numCreatedFiles++ + } + } + // Create a file to the right, if necessary. + if exciseSpan.Contains(d.cmp, m.Largest) { + // No key exists to the right of the excise span in this file. + return ve.NewFiles[len(ve.NewFiles)-numCreatedFiles:], nil + } + // Create a new file, rightFile, between [firstKeyAfter(exciseSpan.End), m.Largest]. + // + // See comment before the definition of leftFile for the motivation behind + // calculating tight user-key bounds. + rightFile := &fileMetadata{ + Virtual: true, + FileBacking: m.FileBacking, + FileNum: d.mu.versions.getNextFileNum(), + } + rightFile.Largest = m.Largest + rightFile.LargestRangeKey = m.LargestRangeKey + rightFile.LargestPointKey = m.LargestPointKey + rightFile.HasPointKeys = m.HasPointKeys + rightFile.HasRangeKeys = m.HasRangeKeys + if m.HasPointKeys && exciseSpan.Contains(d.cmp, m.LargestPointKey) { + // This file will not contain any point keys, but will contain range keys. + rightFile.HasPointKeys = false + rightFile.Largest = m.LargestRangeKey + } else if m.HasRangeKeys && exciseSpan.Contains(d.cmp, m.LargestRangeKey) { + rightFile.HasRangeKeys = false + rightFile.Largest = m.LargestPointKey + } + if rightFile.HasPointKeys { + var err error + if iter == nil && rangeDelIter == nil { + iter, rangeDelIter, err = d.newIters(context.TODO(), m, &IterOptions{level: manifest.Level(level)}, internalIterOpts{}) + if err != nil { + return nil, err + } + if iter != nil { + defer iter.Close() + } else { + iter = emptyIter + } + if rangeDelIter != nil { + defer rangeDelIter.Close() + } else { + rangeDelIter = emptyKeyspanIter + } + } + // Store the max of (exciseSpan.End, rdel.Start) in firstRangeDel. This + // needs to be a copy if the key is owned by the range del iter. + key, _ := iter.SeekGE(exciseSpan.End, base.SeekGEFlagsNone) + var firstRangeDel []byte + rdel := rangeDelIter.SeekGE(exciseSpan.End) + if rdel != nil { + firstRangeDel = append(firstRangeDel[:0], rdel.Start...) + if d.cmp(firstRangeDel, exciseSpan.End) < 0 { + firstRangeDel = exciseSpan.End + } + } + rightFile.HasPointKeys = key != nil || firstRangeDel != nil + if key != nil && (firstRangeDel == nil || base.InternalCompare(d.cmp, *key, rdel.SmallestKey()) < 0) { + rightFile.SmallestPointKey = key.Clone() + } else if firstRangeDel != nil { + // key == nil || firstRangeDel <= key.UserKey. + rightFile.SmallestPointKey = rdel.SmallestKey() + rightFile.SmallestPointKey.UserKey = firstRangeDel + } + rightFile.Smallest = rightFile.SmallestPointKey + } + if rightFile.HasRangeKeys { + if rangeKeyIter == nil { + var err error + rangeKeyIter, err = d.tableNewRangeKeyIter(m, keyspan.SpanIterOptions{}) + if err != nil { + return nil, err + } + defer rangeKeyIter.Close() + } + // Store the max of (exciseSpan.End, rkey.Start) in firstRangeKey. This + // needs to be a copy if the key is owned by the range key iter. + var firstRangeKey []byte + rkey := rangeKeyIter.SeekGE(exciseSpan.End) + if rkey != nil { + firstRangeKey = append(firstRangeKey[:0], rkey.Start...) + if d.cmp(firstRangeKey, exciseSpan.End) < 0 { + firstRangeKey = exciseSpan.End + } + } + rightFile.HasRangeKeys = firstRangeKey != nil + if rightFile.HasRangeKeys { + rightFile.SmallestRangeKey = rkey.SmallestKey() + rightFile.SmallestRangeKey.UserKey = firstRangeKey + if !rightFile.HasPointKeys || base.InternalCompare(d.cmp, rightFile.SmallestPointKey, rightFile.SmallestRangeKey) > 0 { + rightFile.Smallest = rightFile.SmallestRangeKey + } + } + } + if rightFile.HasRangeKeys || rightFile.HasPointKeys { + var err error + rightFile.Size, err = d.tableCache.estimateSize(m, rightFile.Smallest.UserKey, rightFile.Largest.UserKey) + if err != nil { + return nil, err + } + ve.NewFiles = append(ve.NewFiles, newFileEntry{Level: level, Meta: rightFile}) + if !backingTableCreated { + ve.CreatedBackingTables = append(ve.CreatedBackingTables, rightFile.FileBacking) + backingTableCreated = true + } + numCreatedFiles++ + } + + if err := rightFile.Validate(d.cmp, d.opts.Comparer.FormatKey); err != nil { + return nil, err + } + return ve.NewFiles[len(ve.NewFiles)-numCreatedFiles:], nil +} + type ingestTargetLevelFunc func( newIters tableNewIters, newRangeKeyIter keyspan.TableNewSpanIter, @@ -1049,13 +1586,20 @@ type ingestTargetLevelFunc func( ) (int, error) func (d *DB) ingestApply( - jobID int, meta []*fileMetadata, findTargetLevel ingestTargetLevelFunc, mut *memTable, + jobID int, + lr ingestLoadResult, + findTargetLevel ingestTargetLevelFunc, + mut *memTable, + exciseSpan KeyRange, ) (*versionEdit, error) { d.mu.Lock() defer d.mu.Unlock() ve := &versionEdit{ - NewFiles: make([]newFileEntry, len(meta)), + NewFiles: make([]newFileEntry, len(lr.localMeta)+len(lr.sharedMeta)), + } + if exciseSpan.Valid() { + ve.DeletedFiles = map[manifest.DeletedFileEntry]*manifest.FileMetadata{} } metrics := make(map[int]*LevelMetrics) @@ -1067,25 +1611,56 @@ func (d *DB) ingestApply( // returns must unlock the manifest. d.mu.versions.logLock() + scheduleFlush := false if mut != nil { // Unref the mutable memtable to allows its flush to proceed. Now that we've // acquired the manifest lock, we can be certain that if the mutable // memtable has received more recent conflicting writes, the flush won't // beat us to applying to the manifest resulting in sequence number // inversion. - mut.writerUnref() + scheduleFlush = mut.writerUnref() } current := d.mu.versions.currentVersion() baseLevel := d.mu.versions.picker.getBaseLevel() iterOps := IterOptions{logger: d.opts.Logger} - for i := range meta { + for i := 0; i < len(lr.localMeta)+len(lr.sharedMeta); i++ { // Determine the lowest level in the LSM for which the sstable doesn't // overlap any existing files in the level. - m := meta[i] + var m *fileMetadata + sharedIdx := -1 + sharedLevel := -1 + if i < len(lr.localMeta) { + m = lr.localMeta[i] + } else { + sharedIdx = i - len(lr.localMeta) + m = lr.sharedMeta[sharedIdx] + sharedLevel = int(lr.sharedLevels[sharedIdx]) + } f := &ve.NewFiles[i] var err error - f.Level, err = findTargetLevel(d.newIters, d.tableNewRangeKeyIter, iterOps, d.cmp, current, baseLevel, d.mu.compact.inProgress, m) + if sharedIdx >= 0 { + f.Level = sharedLevel + if f.Level < sharedLevelsStart { + panic("cannot slot a shared file higher than the highest shared level") + } + ve.CreatedBackingTables = append(ve.CreatedBackingTables, m.FileBacking) + } else { + if exciseSpan.Valid() && exciseSpan.Contains(d.cmp, m.Smallest) && exciseSpan.Contains(d.cmp, m.Largest) { + // This file fits perfectly within the excise span. We can slot it at + // L6, or sharedLevelsStart - 1 if we have shared files. + if len(lr.sharedMeta) > 0 { + f.Level = sharedLevelsStart - 1 + if baseLevel > f.Level { + f.Level = 0 + } + } else { + f.Level = 6 + } + } else { + f.Level, err = findTargetLevel(d.newIters, d.tableNewRangeKeyIter, iterOps, d.cmp, current, baseLevel, d.mu.compact.inProgress, m) + } + } if err != nil { d.mu.versions.logUnlock() return nil, err @@ -1101,16 +1676,67 @@ func (d *DB) ingestApply( levelMetrics.BytesIngested += m.Size levelMetrics.TablesIngested++ } + if exciseSpan.Valid() { + // Iterate through all levels and find files that intersect with exciseSpan. + // + // TODO(bilal): We could drop the DB mutex here as we don't need it for + // excises; we only need to hold the version lock which we already are + // holding. However releasing the DB mutex could mess with the + // ingestTargetLevel calculation that happened above, as it assumed that it + // had a complete view of in-progress compactions that wouldn't change + // until logAndApply is called. If we were to drop the mutex now, we could + // schedule another in-progress compaction that would go into the chosen target + // level and lead to file overlap within level (which would panic in + // logAndApply). We should drop the db mutex here, do the excise, then + // re-grab the DB mutex and rerun just the in-progress compaction check to + // see if any new compactions are conflicting with our chosen target levels + // for files, and if they are, we should signal those compactions to error + // out. + for level := range current.Levels { + iter := current.Levels[level].Iter() + for m := iter.SeekGE(d.cmp, exciseSpan.Start); m != nil && d.cmp(m.Smallest.UserKey, exciseSpan.End) < 0; m = iter.Next() { + excised, err := d.excise(exciseSpan, m, ve, level) + if err != nil { + return nil, err + } + + if _, ok := ve.DeletedFiles[deletedFileEntry{ + Level: level, + FileNum: m.FileNum, + }]; !ok { + // We did not excise this file. + continue + } + levelMetrics := metrics[level] + if levelMetrics == nil { + levelMetrics = &LevelMetrics{} + metrics[level] = levelMetrics + } + levelMetrics.NumFiles-- + levelMetrics.Size -= int64(m.Size) + for i := range excised { + levelMetrics.NumFiles++ + levelMetrics.Size += int64(excised[i].Meta.Size) + } + } + } + } if err := d.mu.versions.logAndApply(jobID, ve, metrics, false /* forceRotation */, func() []compactionInfo { return d.getInProgressCompactionInfoLocked(nil) }); err != nil { return nil, err } + + d.mu.versions.metrics.Ingest.Count++ + d.updateReadStateLocked(d.opts.DebugCheck) d.updateTableStatsLocked(ve.NewFiles) // The ingestion may have pushed a level over the threshold for compaction, // so check to see if one is necessary and schedule it. d.maybeScheduleCompaction() + if scheduleFlush { + d.maybeScheduleFlush() + } d.maybeValidateSSTablesLocked(ve.NewFiles) return ve, nil } diff --git a/vendor/github.com/cockroachdb/pebble/internal.go b/vendor/github.com/cockroachdb/pebble/internal.go index b505207..61a4284 100644 --- a/vendor/github.com/cockroachdb/pebble/internal.go +++ b/vendor/github.com/cockroachdb/pebble/internal.go @@ -23,6 +23,7 @@ const ( InternalKeyKindRangeKeyUnset = base.InternalKeyKindRangeKeyUnset InternalKeyKindRangeKeyDelete = base.InternalKeyKindRangeKeyDelete InternalKeyKindIngestSST = base.InternalKeyKindIngestSST + InternalKeyKindDeleteSized = base.InternalKeyKindDeleteSized InternalKeyKindInvalid = base.InternalKeyKindInvalid InternalKeySeqNumBatch = base.InternalKeySeqNumBatch InternalKeySeqNumMax = base.InternalKeySeqNumMax diff --git a/vendor/github.com/cockroachdb/pebble/internal/base/internal.go b/vendor/github.com/cockroachdb/pebble/internal/base/internal.go index 5be485d..082307a 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/base/internal.go +++ b/vendor/github.com/cockroachdb/pebble/internal/base/internal.go @@ -24,6 +24,14 @@ const ( //InternalKeyKindColumnFamilyDeletion InternalKeyKind = 4 //InternalKeyKindColumnFamilyValue InternalKeyKind = 5 //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 //InternalKeyKindColumnFamilySingleDelete InternalKeyKind = 8 //InternalKeyKindBeginPrepareXID InternalKeyKind = 9 @@ -66,8 +74,15 @@ const ( // batch, or in an sstable. InternalKeyKindIngestSST InternalKeyKind = 22 - // This maximum value isn't part of the file format. It's unlikely, - // but future extensions may increase this value. + // InternalKeyKindDeleteSized keys behave identically to + // 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}, // 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 // searching for any kind of internal key formed by a certain user key and // 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 // zero sequence number. - InternalKeyZeroSeqnumMaxTrailer = uint64(InternalKeyKindInvalid) + InternalKeyZeroSeqnumMaxTrailer = uint64(255) // A marker for an invalid key. - InternalKeyKindInvalid InternalKeyKind = 255 + InternalKeyKindInvalid InternalKeyKind = InternalKeyKindSSTableInternalObsoleteMask // InternalKeySeqNumBatch is a bit that is set on batch sequence numbers // which prevents those entries from being excluded from iteration. @@ -105,6 +125,9 @@ const ( InternalKeyBoundaryRangeKey = (InternalKeySeqNumMax << 8) | uint64(InternalKeyKindRangeKeySet) ) +// Assert InternalKeyKindSSTableInternalObsoleteBit > InternalKeyKindMax +const _ = uint(InternalKeyKindSSTableInternalObsoleteBit - InternalKeyKindMax - 1) + var internalKeyKindNames = []string{ InternalKeyKindDelete: "DEL", InternalKeyKindSet: "SET", @@ -118,6 +141,7 @@ var internalKeyKindNames = []string{ InternalKeyKindRangeKeyUnset: "RANGEKEYUNSET", InternalKeyKindRangeKeyDelete: "RANGEKEYDEL", InternalKeyKindIngestSST: "INGESTSST", + InternalKeyKindDeleteSized: "DELSIZED", InternalKeyKindInvalid: "INVALID", } @@ -204,6 +228,7 @@ var kindsMap = map[string]InternalKeyKind{ "RANGEKEYUNSET": InternalKeyKindRangeKeyUnset, "RANGEKEYDEL": InternalKeyKindRangeKeyDelete, "INGESTSST": InternalKeyKindIngestSST, + "DELSIZED": InternalKeyKindDeleteSized, } // ParseInternalKey parses the string representation of an internal key. The diff --git a/vendor/github.com/cockroachdb/pebble/internal/base/metrics.go b/vendor/github.com/cockroachdb/pebble/internal/base/metrics.go index 4a7789a..520edc3 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/base/metrics.go +++ b/vendor/github.com/cockroachdb/pebble/internal/base/metrics.go @@ -53,6 +53,15 @@ func (tm *ThroughputMetric) Rate() int64 { 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 // accumulating samples of that gauge. type GaugeSampleMetric struct { diff --git a/vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go b/vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go index f563c1f..cd95724 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go +++ b/vendor/github.com/cockroachdb/pebble/internal/base/seqnums.go @@ -4,6 +4,8 @@ package base +import "fmt" + // This file defines sequence numbers that are reserved for foreign keys i.e. // 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 @@ -14,41 +16,41 @@ const ( // guarantee there are no keys underneath an internal key. SeqNumZero = uint64(0) - // SeqNumL6Point is the sequence number reserved for foreign point keys in L6. - // This sequence number must be lower than SeqNumL6RangeKey for range key - // masking to work correctly. - SeqNumL6Point = uint64(1) + // SeqNumL6 is the sequence number reserved for foreign keys in L6. This seqnum + // will be used to expose any range key sets as well as point keys in L6. Range + // deletes do not need to be exposed in L6. + SeqNumL6 = uint64(1) - // SeqNumL6RangeKey is the sequence number reserved for foreign range keys in - // L6. Only RangeKeySets are expected at this level. - SeqNumL6RangeKey = uint64(2) + // SeqNumL5 is the sequence number reserved for foreign keys in L5. This seqnum + // needs to be greater than SeqNumL6, so that range deletes in L5 can be + // 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 - // 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. + // Sequence numbers 3-9 are reserved for future use. // SeqNumStart is the first sequence number assigned to a key written by // ourselves. 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)) + } +} diff --git a/vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go b/vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go index 4798561..1b08126 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go +++ b/vendor/github.com/cockroachdb/pebble/internal/keyspan/iter.go @@ -62,7 +62,7 @@ type FragmentIterator interface { // TableNewSpanIter creates a new iterator for range key spans for the given // 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 // per-sstable span iterators. @@ -70,6 +70,9 @@ type SpanIterOptions struct { // RangeKeyFilters can be used to avoid scanning tables and blocks in tables // when iterating over range keys. 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. diff --git a/vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go b/vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go index 047c6a2..6dd7ac6 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go +++ b/vendor/github.com/cockroachdb/pebble/internal/keyspan/level_iter.go @@ -98,7 +98,7 @@ func (l *LevelIter) Init( ) { l.err = nil l.level = level - l.tableOpts.RangeKeyFilters = opts.RangeKeyFilters + l.tableOpts = opts l.cmp = cmp l.iterFile = nil l.newIter = newIter @@ -179,7 +179,7 @@ func (l *LevelIter) loadFile(file *manifest.FileMetadata, dir int) loadFileRetur return noFileLoaded } if indicator != fileAlreadyLoaded { - l.iter, l.err = l.newIter(file, &l.tableOpts) + l.iter, l.err = l.newIter(file, l.tableOpts) indicator = newFileLoaded } if l.err != nil { diff --git a/vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go b/vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go index 2476024..c73ba59 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go +++ b/vendor/github.com/cockroachdb/pebble/internal/keyspan/merging_iter.go @@ -25,49 +25,6 @@ import ( // MergingIter implementation, but will require a bit of plumbing to thread the // 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 // spans that yields sets of spans fragmented at unique user key boundaries. // diff --git a/vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go b/vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go index 92694d5..257b373 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go +++ b/vendor/github.com/cockroachdb/pebble/internal/keyspan/span.go @@ -368,6 +368,7 @@ func (s Span) String() string { // Pretty returns a formatter for the span. func (s Span) Pretty(f base.FormatKey) fmt.Formatter { + // TODO(jackson): Take a base.FormatValue to format Key.Value too. return prettySpan{s, f} } diff --git a/vendor/github.com/cockroachdb/pebble/internal/keyspan/transformer.go b/vendor/github.com/cockroachdb/pebble/internal/keyspan/transformer.go new file mode 100644 index 0000000..e0152cf --- /dev/null +++ b/vendor/github.com/cockroachdb/pebble/internal/keyspan/transformer.go @@ -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 +} diff --git a/vendor/github.com/cockroachdb/pebble/internal/manifest/version.go b/vendor/github.com/cockroachdb/pebble/internal/manifest/version.go index ca16ba1..a9da238 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/manifest/version.go +++ b/vendor/github.com/cockroachdb/pebble/internal/manifest/version.go @@ -227,6 +227,12 @@ type FileMetadata struct { // we'd have to write virtual sstable stats to the version edit. 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 L0Index 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 // 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 // compaction. When it's true, IsCompacting must also return true. If // 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 // is created to verify that the fields of the virtual sstable are sound. func (m *FileMetadata) ValidateVirtual(createdFrom *FileMetadata) { diff --git a/vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go b/vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go index 33f70d6..d94247e 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go +++ b/vendor/github.com/cockroachdb/pebble/internal/manifest/version_edit.go @@ -51,7 +51,9 @@ const ( tagMaxColumnFamily = 203 // Pebble tags. - tagNewFile5 = 104 // Range keys. + tagNewFile5 = 104 // Range keys. + tagCreatedBackingTable = 105 + tagRemovedBackingTable = 106 // The custom tags sub-format used by tagNewFile4 and above. customTagTerminate = 1 @@ -59,6 +61,7 @@ const ( customTagCreationTime = 6 customTagPathID = 65 customTagNonSafeIgnoreMask = 1 << 6 + customTagVirtual = 66 ) // DeletedFileEntry holds the state for a file deletion from a level. The file @@ -73,6 +76,9 @@ type DeletedFileEntry struct { type NewFileEntry struct { Level int 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 @@ -143,7 +149,9 @@ type VersionEdit struct { // 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 { br, ok := r.(byteReader) if !ok { @@ -196,6 +204,28 @@ func (v *VersionEdit) Decode(r io.Reader) error { } // 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: level, err := d.readLevel() if err != nil { @@ -301,6 +331,10 @@ func (v *VersionEdit) Decode(r io.Reader) error { } var markedForCompaction bool var creationTime uint64 + virtualState := struct { + virtual bool + backingFileNum uint64 + }{} if tag == tagNewFile4 || tag == tagNewFile5 { for { customTag, err := d.readUvarint() @@ -309,7 +343,16 @@ func (v *VersionEdit) Decode(r io.Reader) error { } if customTag == customTagTerminate { 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() if err != nil { return err @@ -345,6 +388,7 @@ func (v *VersionEdit) Decode(r io.Reader) error { SmallestSeqNum: smallestSeqNum, LargestSeqNum: largestSeqNum, MarkedForCompaction: markedForCompaction, + Virtual: virtualState.virtual, } if tag != tagNewFile5 { // no range keys present m.SmallestPointKey = base.DecodeInternalKey(smallestPointKey) @@ -376,11 +420,18 @@ func (v *VersionEdit) Decode(r io.Reader) error { } } m.boundsSet = true - m.InitPhysicalBacking() - v.NewFiles = append(v.NewFiles, NewFileEntry{ + if !virtualState.virtual { + m.InitPhysicalBacking() + } + + nfe := NewFileEntry{ Level: level, Meta: m, - }) + } + if virtualState.virtual { + nfe.BackingFileNum = base.FileNum(virtualState.backingFileNum).DiskFileNum() + } + v.NewFiles = append(v.NewFiles, nfe) case tagPrevLogNumber: n, err := d.readUvarint() @@ -442,8 +493,6 @@ func (v *VersionEdit) String() string { } // Encode encodes an edit to the specified writer. -// -// TODO(bananabrick): Support encoding of virtual sstable state. func (v *VersionEdit) Encode(w io.Writer) error { e := versionEditEncoder{new(bytes.Buffer)} @@ -463,6 +512,15 @@ func (v *VersionEdit) Encode(w io.Writer) error { e.writeUvarint(tagNextFileNumber) 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, // even though its value is zero. We detect this by encoding LastSeqNum when // ComparerName is set. @@ -476,7 +534,7 @@ func (v *VersionEdit) Encode(w io.Writer) error { e.writeUvarint(uint64(x.FileNum)) } 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 switch { case x.Meta.HasRangeKeys: @@ -529,6 +587,10 @@ func (v *VersionEdit) Encode(w io.Writer) error { e.writeUvarint(customTagNeedsCompaction) e.writeBytes([]byte{1}) } + if x.Meta.Virtual { + e.writeUvarint(customTagVirtual) + e.writeUvarint(uint64(x.Meta.FileBacking.DiskFileNum.FileNum())) + } e.writeUvarint(customTagTerminate) } } @@ -536,6 +598,7 @@ func (v *VersionEdit) Encode(w io.Writer) error { return err } +// versionEditDecoder should be used to decode version edits. type versionEditDecoder struct { byteReader } @@ -634,7 +697,9 @@ type BulkVersionEdit struct { Added [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 // 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 { // A new file should not have been deleted in this or a preceding // 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) } } + 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 { 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 // edit it is safe to just append without any de-duplication. b.RemovedFileBacking = append(b.RemovedFileBacking, ve.RemovedBackingTables...) diff --git a/vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go b/vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go index a25b64d..c1208ca 100644 --- a/vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go +++ b/vendor/github.com/cockroachdb/pebble/internal/rangekey/coalesce.go @@ -6,6 +6,7 @@ package rangekey import ( "bytes" + "fmt" "math" "sort" @@ -367,3 +368,78 @@ func coalesce( } 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 +} diff --git a/vendor/github.com/cockroachdb/pebble/iterator.go b/vendor/github.com/cockroachdb/pebble/iterator.go index 3348274..9c1ff3e 100644 --- a/vendor/github.com/cockroachdb/pebble/iterator.go +++ b/vendor/github.com/cockroachdb/pebble/iterator.go @@ -568,7 +568,10 @@ func (i *Iterator) findNextEntry(limit []byte) { i.iterValidityState = IterValid 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() continue @@ -631,7 +634,10 @@ func (i *Iterator) nextPointCurrentUserKey() bool { i.err = base.CorruptionErrorf("pebble: unexpected range key set mid-user key") 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 case InternalKeyKindSet, InternalKeyKindSetWithDelete: @@ -929,7 +935,7 @@ func (i *Iterator) findPrevEntry(limit []byte) { // return the key even if the MERGE point key is deleted. rangeKeyBoundary = true - case InternalKeyKindDelete, InternalKeyKindSingleDelete: + case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized: i.value = LazyValue{} i.iterValidityState = IterExhausted valueMerger = nil @@ -1092,9 +1098,13 @@ func (i *Iterator) mergeNext(key InternalKey, valueMerger ValueMerger) { return } switch key.Kind() { - case InternalKeyKindDelete, InternalKeyKindSingleDelete: + case InternalKeyKindDelete, InternalKeyKindSingleDelete, InternalKeyKindDeleteSized: // We've hit a deletion tombstone. Return everything up to this // point. + // + // NB: treating InternalKeyKindSingleDelete as equivalent to DEL is not + // only simpler, but is also necessary for correctness due to + // InternalKeyKindSSTableInternalObsoleteBit. return case InternalKeyKindSet, InternalKeyKindSetWithDelete: diff --git a/vendor/github.com/cockroachdb/pebble/level_checker.go b/vendor/github.com/cockroachdb/pebble/level_checker.go index 4bbd9a4..4cec440 100644 --- a/vendor/github.com/cockroachdb/pebble/level_checker.go +++ b/vendor/github.com/cockroachdb/pebble/level_checker.go @@ -165,7 +165,7 @@ func (m *simpleMergingIter) step() bool { if m.valueMerger != nil { // Ongoing series of MERGE records. switch item.key.Kind() { - case InternalKeyKindSingleDelete, InternalKeyKindDelete: + case InternalKeyKindSingleDelete, InternalKeyKindDelete, InternalKeyKindDeleteSized: var closer io.Closer _, closer, m.err = m.valueMerger.Finish(true /* includesBase */) if m.err == nil && closer != nil { @@ -379,7 +379,7 @@ func checkRangeTombstones(c *checkConfig) error { atomicUnit, _ := expandToAtomicUnit(c.cmp, lf.Slice(), true /* disableIsCompacting */) lower, upper := manifest.KeyRange(c.cmp, atomicUnit.Iter()) iterToClose, iter, err := c.newIters( - context.Background(), lf.FileMetadata, nil, internalIterOpts{}) + context.Background(), lf.FileMetadata, &IterOptions{level: manifest.Level(lsmLevel)}, internalIterOpts{}) if err != nil { return err } diff --git a/vendor/github.com/cockroachdb/pebble/level_iter.go b/vendor/github.com/cockroachdb/pebble/level_iter.go index feb809d..68e2e33 100644 --- a/vendor/github.com/cockroachdb/pebble/level_iter.go +++ b/vendor/github.com/cockroachdb/pebble/level_iter.go @@ -36,9 +36,9 @@ type tableNewIters func( // tableNewRangeDelIter takes a tableNewIters and returns a TableNewSpanIter // for the rangedel iterator returned by tableNewIters. 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( - ctx, file, &IterOptions{RangeKeyFilters: iterOptions.RangeKeyFilters}, internalIterOpts{}) + ctx, file, &IterOptions{RangeKeyFilters: iterOptions.RangeKeyFilters, level: iterOptions.Level}, internalIterOpts{}) if iter != nil { _ = iter.Close() } @@ -200,6 +200,11 @@ type levelIter struct { // cache when constructing new table iterators. 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 // which construct "impossible" situations (e.g. seeking to a key before the // lower bound). @@ -267,8 +272,12 @@ func (l *levelIter) init( l.upper = opts.UpperBound l.tableOpts.TableFilter = opts.TableFilter l.tableOpts.PointKeyFilters = opts.PointKeyFilters + if len(opts.PointKeyFilters) == 0 { + l.tableOpts.PointKeyFilters = l.filtersBuf[:0:1] + } l.tableOpts.UseL6Filters = opts.UseL6Filters l.tableOpts.level = l.level + l.tableOpts.snapshotForHideObsoletePoints = opts.snapshotForHideObsoletePoints l.cmp = cmp l.split = split l.iterFile = nil diff --git a/vendor/github.com/cockroachdb/pebble/metrics.go b/vendor/github.com/cockroachdb/pebble/metrics.go index cd90d03..7749583 100644 --- a/vendor/github.com/cockroachdb/pebble/metrics.go +++ b/vendor/github.com/cockroachdb/pebble/metrics.go @@ -184,6 +184,11 @@ type Metrics struct { Duration time.Duration } + Ingest struct { + // The total number of ingestions + Count uint64 + } + Flush struct { // The total number of flushes. Count int64 @@ -225,6 +230,9 @@ type Metrics struct { // The approximate count of internal tombstones (DEL, SINGLEDEL and // RANGEDEL key kinds) within the database. TombstoneCount uint64 + // A cumulative total number of missized DELSIZED keys encountered by + // compactions since the database was opened. + MissizedTombstonesCount uint64 } Snapshots struct { @@ -504,6 +512,9 @@ func (m *Metrics) SafeFormat(w redact.SafePrinter, _ rune) { notApplicable, notApplicable, 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 { diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go b/vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go index 5632be7..72c0a85 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/noop_readahead.go @@ -27,8 +27,8 @@ func (h *NoopReadHandle) ReadAt(ctx context.Context, p []byte, off int64) error // Close is part of the ReadHandle interface. func (*NoopReadHandle) Close() error { return nil } -// MaxReadahead is part of the ReadHandle interface. -func (*NoopReadHandle) MaxReadahead() {} +// SetupForCompaction is part of the ReadHandle interface. +func (*NoopReadHandle) SetupForCompaction() {} // RecordCacheHit is part of the ReadHandle interface. func (*NoopReadHandle) RecordCacheHit(_ context.Context, offset, size int64) {} diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go index 5cc22a1..ec96e81 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorage.go @@ -16,6 +16,9 @@ import ( type Readable interface { // 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 // same Readable. ReadAt(ctx context.Context, p []byte, off int64) error @@ -39,14 +42,18 @@ type Readable interface { type ReadHandle interface { // 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. ReadAt(ctx context.Context, p []byte, off int64) error Close() error - // MaxReadahead configures the implementation to expect large sequential - // reads. Used to skip any initial read-ahead ramp-up. - MaxReadahead() + // SetupForCompaction informs the implementation that the read handle will + // be used to read data blocks for a compaction. The implementation can expect + // sequential reads, and can decide to not retain data in any caches. + SetupForCompaction() // RecordCacheHit informs the implementation that we were able to retrieve a // 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. 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(meta *ObjectMetadata) (SharedObjectBackingHandle, error) diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing.go index 4956935..2672636 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing.go @@ -16,9 +16,9 @@ const ( // RecordCacheHitOp happens when a read is satisfied from the block cache. See // objstorage.ReadHandle.RecordCacheHit(). RecordCacheHitOp - // MaxReadaheadOp is a "meta operation" that configures a read handle for - // large sequential reads. See objstorage.ReadHandle.MaxReadahead(). - MaxReadaheadOp + // SetupForCompactionOp is a "meta operation" that configures a read handle + // for large sequential reads. See objstorage.ReadHandle.SetupForCompaction(). + SetupForCompactionOp ) // Reason indicates the higher-level context of the operation. diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing_on.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing_on.go index 8030538..0680b34 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing_on.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing/obj_io_tracing_on.go @@ -209,14 +209,14 @@ func (rh *readHandle) Close() error { return rh.rh.Close() } -// MaxReadahead is part of the objstorage.ReadHandle interface. -func (rh *readHandle) MaxReadahead() { +// SetupForCompaction is part of the objstorage.ReadHandle interface. +func (rh *readHandle) SetupForCompaction() { rh.g.add(context.Background(), Event{ - Op: MaxReadaheadOp, + Op: SetupForCompactionOp, FileNum: rh.fileNum, HandleID: rh.handleID, }) - rh.rh.MaxReadahead() + rh.rh.SetupForCompaction() } // RecordCacheHit is part of the objstorage.ReadHandle interface. diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go index 9e3d34f..f4c349b 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/provider.go @@ -93,6 +93,21 @@ type Settings struct { // (experimental). Shared struct { 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). } } diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go index ec04efb..e07017c 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/readahead.go @@ -11,15 +11,15 @@ const ( minFileReadsForReadahead = 2 // TODO(bilal): Have the initial size value be a factor of the block size, // as opposed to a hardcoded value. - initialReadaheadSize = 64 << 10 /* 64KB */ - maxReadaheadSize = 256 << 10 /* 256KB */ + initialReadaheadSize = 64 << 10 /* 64KB */ ) // readaheadState contains state variables related to readahead. Updated on // file reads. type readaheadState struct { // Number of sequential reads. - numReads int64 + numReads int64 + maxReadaheadSize int64 // Size issued to the next call to Prefetch. Starts at or above // initialReadaheadSize and grows exponentially until maxReadaheadSize. size int64 @@ -32,22 +32,23 @@ type readaheadState struct { limit int64 } -func makeReadaheadState() readaheadState { +func makeReadaheadState(maxReadaheadSize int64) readaheadState { return readaheadState{ - size: initialReadaheadSize, + size: initialReadaheadSize, + maxReadaheadSize: maxReadaheadSize, } } func (rs *readaheadState) recordCacheHit(offset, blockLength int64) { currentReadEnd := offset + blockLength 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 // not been a cache hit. rs.limit = currentReadEnd 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 // any scenario. Reset all variables. rs.numReads = 1 @@ -61,7 +62,7 @@ func (rs *readaheadState) recordCacheHit(offset, blockLength int64) { // readahead. 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 // down the line. 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: // [rs.limit, rs.limit + rs.size] // 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 // time the readahead size would increase exponentially to make it // 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 // the last readahead range. In the diagrams below, ++++ is the last // 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. // - // rs.limit rs.limit + maxReadaheadSize + // rs.limit rs.limit + rs.maxReadaheadSize // ++++++++++|===========================| // // |-------------| // offset currentReadEnd // // 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]: // - // 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 // Increase rs.size for the next read. rs.size *= 2 - if rs.size > maxReadaheadSize { - rs.size = maxReadaheadSize + if rs.size > rs.maxReadaheadSize { + rs.size = rs.maxReadaheadSize } 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 // rs.limit - rs.prevSize would not underflow. // We read too far away from rs.limit to benefit from readahead in // any scenario. Reset all variables. // 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: // - // (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 // moment. // - // (rs.limit - rs.prevSize) (rs.limit + maxReadaheadSize) + // (rs.limit - rs.prevSize) (rs.limit + rs.maxReadaheadSize) // |+++++++++++++|===============| // (rs.limit) // @@ -170,11 +171,11 @@ func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 { rs.numReads++ 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 // 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 // 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) // |+++++++++++++|=============| // // |-------| diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go index b6fd71d..2fb4ed5 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared.go @@ -6,6 +6,7 @@ package objstorageprovider import ( "context" + "runtime" "sync" "sync/atomic" @@ -13,6 +14,7 @@ import ( "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/internal/invariants" "github.com/cockroachdb/pebble/objstorage" + "github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache" "github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedobjcat" "github.com/cockroachdb/pebble/objstorage/shared" ) @@ -21,6 +23,7 @@ import ( // All fields remain unset if shared storage is not configured. type sharedSubsystem struct { catalog *sharedobjcat.Catalog + cache *sharedcache.Cache // checkRefsOnOpen controls whether we check the ref marker file when opening // 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") } + 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 { o := objstorage.ObjectMetadata{ DiskFileNum: meta.FileNum, @@ -95,6 +116,14 @@ func (p *provider) SetCreatorID(creatorID objstorage.CreatorID) error { 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 { if p.sharedStorage() == nil { return errors.Errorf("shared object support not configured") @@ -211,7 +240,7 @@ func (p *provider) sharedOpenForReading( } 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) { diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go index ee203bf..e0a3106 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/shared_readable.go @@ -6,28 +6,55 @@ package objstorageprovider import ( "context" + "io" + "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/objstorage" + "github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache" "github.com/cockroachdb/pebble/objstorage/shared" ) +const sharedMaxReadaheadSize = 1024 * 1024 /* 1MB */ + // sharedReadable is a very simple implementation of Readable on top of the // ReadCloser returned by shared.Storage.CreateObject. type sharedReadable struct { objReader shared.ObjectReader size int64 + fileNum base.DiskFileNum + provider *provider } 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{ objReader: objReader, 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 { + 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) } @@ -42,24 +69,94 @@ func (r *sharedReadable) Size() int64 { func (r *sharedReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle { // TODO(radu): use a pool. - return &sharedReadHandle{readable: r} + rh := &sharedReadHandle{readable: r} + rh.readahead.state = makeReadaheadState(sharedMaxReadaheadSize) + return rh } type sharedReadHandle struct { - readable *sharedReadable + readable *sharedReadable + readahead struct { + state readaheadState + data []byte + offset int64 + } + forCompaction bool } 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 { - 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 { r.readable = nil + r.readahead.data = 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) + } +} diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go new file mode 100644 index 0000000..1bba1a6 --- /dev/null +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache/shared_cache.go @@ -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)) + } + } +} diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go index 25cea57..9362464 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/objstorageprovider/vfs_readable.go @@ -15,6 +15,8 @@ import ( "github.com/cockroachdb/pebble/vfs" ) +const fileMaxReadaheadSize = 256 * 1024 /* 256KB */ + // fileReadable implements objstorage.Readable on top of a vfs.File. // // 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 { rh := readHandlePool.Get().(*vfsReadHandle) rh.r = r - rh.rs = makeReadaheadState() + rh.rs = makeReadaheadState(fileMaxReadaheadSize) return rh } @@ -126,10 +128,10 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error n, err = rh.sequentialFile.ReadAt(p, offset) } else { 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 // OS-level readahead. - rh.MaxReadahead() + rh.switchToOSReadahead() } else { _ = rh.r.file.Prefetch(offset, readaheadSize) } @@ -142,8 +144,12 @@ func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error return err } -// MaxReadahead is part of the objstorage.ReadHandle interface. -func (rh *vfsReadHandle) MaxReadahead() { +// SetupForCompaction is part of the objstorage.ReadHandle interface. +func (rh *vfsReadHandle) SetupForCompaction() { + rh.switchToOSReadahead() +} + +func (rh *vfsReadHandle) switchToOSReadahead() { if rh.sequentialFile != nil { return } diff --git a/vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go b/vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go index c910c70..4665176 100644 --- a/vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go +++ b/vendor/github.com/cockroachdb/pebble/objstorage/shared/storage.go @@ -66,8 +66,11 @@ type Storage interface { type ObjectReader interface { // ReadAt reads len(p) bytes into p starting at offset off. // - // Clients of ReadAt can execute parallel ReadAt calls on the - // same ObjectReader. + // Does not return partial results; if offset + len(p) is past the end of the + // 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 Close() error diff --git a/vendor/github.com/cockroachdb/pebble/open.go b/vendor/github.com/cockroachdb/pebble/open.go index ac45175..12947e0 100644 --- a/vendor/github.com/cockroachdb/pebble/open.go +++ b/vendor/github.com/cockroachdb/pebble/open.go @@ -210,8 +210,8 @@ func Open(dirname string, opts *Options) (db *DB, _ error) { write: d.commitWrite, }) d.deletionLimiter = rate.NewLimiter( - rate.Limit(d.opts.Experimental.MinDeletionRate), - d.opts.Experimental.MinDeletionRate) + rate.Limit(d.opts.TargetByteDeletionRate), + d.opts.TargetByteDeletionRate) d.mu.nextJobID = 1 d.mu.mem.nextSize = opts.MemTableSize 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.tableValidation.cond.L = &d.mu.Mutex - if !d.opts.ReadOnly && !d.opts.private.disableTableStats { + if !d.opts.ReadOnly { d.maybeCollectTableStatsLocked() } d.calculateDiskAvailableBytes() @@ -804,13 +804,12 @@ func (d *DB) replayWAL( paths[i] = base.MakeFilepath(d.opts.FS, d.dirname, fileTypeTable, n) } - var meta []*manifest.FileMetadata - meta, _, err = ingestLoad( - d.opts, d.mu.formatVers.vers, paths, d.cacheID, fileNums, - ) + var lr ingestLoadResult + lr, err = ingestLoad(d.opts, d.mu.formatVers.vers, paths, nil, d.cacheID, fileNums) if err != nil { return nil, 0, err } + meta := lr.localMeta if uint32(len(meta)) != b.Count() { panic("pebble: couldn't load all files in WAL entry.") diff --git a/vendor/github.com/cockroachdb/pebble/options.go b/vendor/github.com/cockroachdb/pebble/options.go index 2bdc9d5..7674e42 100644 --- a/vendor/github.com/cockroachdb/pebble/options.go +++ b/vendor/github.com/cockroachdb/pebble/options.go @@ -17,6 +17,7 @@ import ( "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/internal/cache" "github.com/cockroachdb/pebble/internal/humanize" + "github.com/cockroachdb/pebble/internal/keyspan" "github.com/cockroachdb/pebble/internal/manifest" "github.com/cockroachdb/pebble/objstorage/shared" "github.com/cockroachdb/pebble/sstable" @@ -118,10 +119,13 @@ type IterOptions struct { // function can be used by multiple iterators, if the iterator is cloned. TableFilter func(userProps map[string]string) bool // 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 - // increasing order of the BlockPropertyFilter.ShortID. This slice represents - // an intersection across all filters, i.e., all filters must indicate that the - // block is relevant. + // when iterating over point keys. This slice represents an intersection + // across all filters, i.e., all filters must indicate that the 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 // RangeKeyFilters can be usefd to avoid scanning tables and blocks in tables // when iterating over range keys. The same requirements that apply to @@ -180,6 +184,10 @@ type IterOptions struct { level manifest.Level // disableLazyCombinedIteration is an internal testing option. 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 // construction and Iterator.SetOptions. @@ -222,6 +230,17 @@ func (o *IterOptions) getLogger() 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 // scanInternalIterator. type scanInternalOptions struct { @@ -509,17 +528,6 @@ type Options struct { // concurrency slots as determined by the two options is chosen. 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 // compactions by adjusting `AllowedSeeks` in manifest.FileMetadata: // @@ -606,12 +614,6 @@ type Options struct { // for CPUWorkPermissionGranter for more details. CPUWorkPermissionGranter CPUWorkPermissionGranter - // PointTombstoneWeight is a float in the range [0, +inf) used to weight the - // point tombstone heuristics during compaction picking. - // - // The default value is 1, which results in no scaling of point tombstones. - PointTombstoneWeight float64 - // EnableValueBlocks is used to decide whether to enable writing // TableFormatPebblev3 sstables. This setting is only respected by a // specific subset of format major versions: FormatSSTableValueBlocks, @@ -626,18 +628,24 @@ type Options struct { ShortAttributeExtractor ShortAttributeExtractor // RequiredInPlaceValueBound specifies an optional span of user key - // prefixes for which the values must be stored with the key. This is - // useful for statically known exclusions to value separation. In - // CockroachDB, this will be used for the lock table key space that has - // non-empty suffixes, but those locks don't represent actual MVCC - // versions (the suffix ordering is arbitrary). We will also need to add - // support for dynamically configured exclusions (we want the default to - // be to allow Pebble to decide whether to separate the value or not, - // hence this is structured as exclusions), for example, for users of - // CockroachDB to dynamically exclude certain tables. + // prefixes that are not-MVCC, but have a suffix. For these the values + // must be stored with the key, since the concept of "older versions" is + // not defined. It is also useful for statically known exclusions to value + // separation. In CockroachDB, this will be used for the lock table key + // space that has non-empty suffixes, but those locks don't represent + // actual MVCC versions (the suffix ordering is arbitrary). We will also + // need to add support for dynamically configured exclusions (we want the + // default to be to allow Pebble to decide whether to separate the value + // 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 // 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 // DisableIngestAsFlushable disables lazy ingestion of sstables through @@ -858,6 +866,19 @@ type Options struct { // changing options dynamically? 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 // for facilitating upgrade paths of unconfigurable functionality. private struct { @@ -1043,10 +1064,6 @@ func (o *Options) EnsureDefaults() *Options { if o.Experimental.CPUWorkPermissionGranter == nil { o.Experimental.CPUWorkPermissionGranter = defaultCPUWorkGranter{} } - if o.Experimental.PointTombstoneWeight == 0 { - o.Experimental.PointTombstoneWeight = 1 - } - if o.Experimental.MultiLevelCompactionHueristic == nil { 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, " mem_table_size=%d\n", o.MemTableSize) fmt.Fprintf(&buf, " mem_table_stop_writes_threshold=%d\n", o.MemTableStopWritesThreshold) - fmt.Fprintf(&buf, " min_deletion_rate=%d\n", o.Experimental.MinDeletionRate) + fmt.Fprintf(&buf, " min_deletion_rate=%d\n", o.TargetByteDeletionRate) fmt.Fprintf(&buf, " merger=%s\n", o.Merger.Name) - fmt.Fprintf(&buf, " point_tombstone_weight=%f\n", o.Experimental.PointTombstoneWeight) fmt.Fprintf(&buf, " read_compaction_rate=%d\n", o.Experimental.ReadCompactionRate) fmt.Fprintf(&buf, " read_sampling_multiplier=%d\n", o.Experimental.ReadSamplingMultiplier) fmt.Fprintf(&buf, " strict_wal_tail=%t\n", o.private.strictWALTail) @@ -1374,7 +1390,7 @@ func (o *Options) Parse(s string, hooks *ParseHooks) error { // version is valid right here. var v uint64 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) } 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 // may be meaningful again eventually. case "min_deletion_rate": - o.Experimental.MinDeletionRate, err = strconv.Atoi(value) + o.TargetByteDeletionRate, err = strconv.Atoi(value) case "min_flush_rate": // Do nothing; option existed in older versions of pebble, and // may be meaningful again eventually. case "point_tombstone_weight": - o.Experimental.PointTombstoneWeight, err = strconv.ParseFloat(value, 64) + // Do nothing; deprecated. case "strict_wal_tail": o.private.strictWALTail, err = strconv.ParseBool(value) case "merger": @@ -1594,9 +1610,9 @@ func (o *Options) Validate() error { fmt.Fprintf(&buf, "MemTableStopWritesThreshold (%d) must be >= 2\n", o.MemTableStopWritesThreshold) } - if o.FormatMajorVersion > FormatNewest { + if o.FormatMajorVersion > internalFormatNewest { fmt.Fprintf(&buf, "FormatMajorVersion (%d) must be <= %d\n", - o.FormatMajorVersion, FormatNewest) + o.FormatMajorVersion, internalFormatNewest) } if o.TableCache != nil && o.Cache != o.TableCache.cache { 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.Filters = o.Filters if o.Merger != nil { + readerOpts.Merge = o.Merger.Merge readerOpts.MergerName = o.Merger.Name } readerOpts.LoggerAndTracer = o.LoggerAndTracer @@ -1640,6 +1657,9 @@ func (o *Options) MakeWriterOptions(level int, format sstable.TableFormat) sstab if format >= sstable.TableFormatPebblev3 { writerOpts.ShortAttributeExtractor = o.Experimental.ShortAttributeExtractor writerOpts.RequiredInPlaceValueBound = o.Experimental.RequiredInPlaceValueBound + if format >= sstable.TableFormatPebblev4 && level == numLevels-1 { + writerOpts.WritingToLowestLevel = true + } } levelOpts := o.Level(level) writerOpts.BlockRestartInterval = levelOpts.BlockRestartInterval diff --git a/vendor/github.com/cockroachdb/pebble/pacer.go b/vendor/github.com/cockroachdb/pebble/pacer.go index 8d3d17f..5cbba2b 100644 --- a/vendor/github.com/cockroachdb/pebble/pacer.go +++ b/vendor/github.com/cockroachdb/pebble/pacer.go @@ -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 -// opts.Experimental.MinDeletionRate. +// opts.TargetByteDeletionRate. func (p *deletionPacer) maybeThrottle(bytesToDelete uint64) error { return p.limit(bytesToDelete, p.getInfo()) } diff --git a/vendor/github.com/cockroachdb/pebble/range_keys.go b/vendor/github.com/cockroachdb/pebble/range_keys.go index 30549d5..6cd3e58 100644 --- a/vendor/github.com/cockroachdb/pebble/range_keys.go +++ b/vendor/github.com/cockroachdb/pebble/range_keys.go @@ -63,8 +63,7 @@ func (i *Iterator) constructRangeKeyIter() { // around Key Trailer order. iter := current.RangeKeyLevels[0].Iter() for f := iter.Last(); f != nil; f = iter.Prev() { - spanIterOpts := &keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters} - spanIter, err := i.newIterRangeKey(f, spanIterOpts) + spanIter, err := i.newIterRangeKey(f, i.opts.SpanIterOptions(manifest.Level(0))) if err != nil { i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err}) continue @@ -78,7 +77,7 @@ func (i *Iterator) constructRangeKeyIter() { continue } 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(), manifest.Level(level), manifest.KeyTypeRange) i.rangeKey.iterConfig.AddLevel(li) diff --git a/vendor/github.com/cockroachdb/pebble/scan_internal.go b/vendor/github.com/cockroachdb/pebble/scan_internal.go index 4984b0f..f7d5aeb 100644 --- a/vendor/github.com/cockroachdb/pebble/scan_internal.go +++ b/vendor/github.com/cockroachdb/pebble/scan_internal.go @@ -7,12 +7,15 @@ package pebble import ( "context" "fmt" + "sync" "github.com/cockroachdb/errors" "github.com/cockroachdb/pebble/internal/base" + "github.com/cockroachdb/pebble/internal/invariants" "github.com/cockroachdb/pebble/internal/keyspan" "github.com/cockroachdb/pebble/internal/manifest" "github.com/cockroachdb/pebble/objstorage" + "github.com/cockroachdb/pebble/sstable" ) const ( @@ -92,8 +95,41 @@ type pcIterPos int const ( pcIterPosCur pcIterPos = iota 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 // 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 @@ -112,32 +148,52 @@ type pointCollapsingIterator struct { merge base.Merge err error seqNum uint64 - // The current position of `iter`. Could be backed by savedKey (i.e. iterKey == - // &savedKey) or could be owned by `iter`. findNextEntry and similar methods - // are expected to save the current value of this to savedKey if they're - // iterating away from the current key but still need to retain it. See - // comments in findNextEntry on how this field is used. + // The current position of `iter`. Always owned by the underlying iter. + iterKey *InternalKey + // The last saved key. findNextEntry and similar methods are expected to save + // the current value of iterKey to savedKey if they're iterating away from the + // 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: // - if pos == pcIterPosNext, iterKey is pointing to the next user key owned // by `iter` while savedKey is holding a copy to our current key. - // - If pos == pcIterPosCur, iterKey is pointing to either a savedKey-backed - // copy of the current key, or an `iter`-owned current key in which case - // the value of savedKey is undefined. - iterKey *InternalKey - savedKey InternalKey + // - If pos == pcIterPosCur, iterKey is pointing to an `iter`-owned current + // key, and savedKey is either undefined or pointing to a version of the + // current key owned by this iterator (i.e. backed by savedKeyBuf). + // - if pos == pcIterPosPrev, iterKey is pointing to the key before + // 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 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. valueMerger ValueMerger 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. func (p *pointCollapsingIterator) SeekPrefixGE( prefix, key []byte, flags base.SeekGEFlags, ) (*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. @@ -145,7 +201,7 @@ func (p *pointCollapsingIterator) SeekGE( key []byte, flags base.SeekGEFlags, ) (*base.InternalKey, base.LazyValue) { p.resetKey() - p.iterKey, p.value = p.iter.SeekGE(key, flags) + p.iterKey, p.iterValue = p.iter.SeekGE(key, flags) p.pos = pcIterPosCur if p.iterKey == nil { return nil, base.LazyValue{} @@ -157,11 +213,17 @@ func (p *pointCollapsingIterator) SeekGE( func (p *pointCollapsingIterator) SeekLT( key []byte, flags base.SeekLTFlags, ) (*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() { - p.savedKey.UserKey = p.savedKey.UserKey[:0] + p.savedKey.UserKey = p.savedKeyBuf[:0] p.savedKey.Trailer = 0 p.valueMerger = nil p.valueBuf = p.valueBuf[:0] @@ -169,59 +231,73 @@ func (p *pointCollapsingIterator) resetKey() { p.pos = pcIterPosCur } -// 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) { - 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 +func (p *pointCollapsingIterator) verifySeqNum(key *base.InternalKey) *base.InternalKey { + if !invariants.Enabled { + return key } + 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 - // as a proxy to determine if we're at the first internal key for a user key. +// finishAndReturnMerge finishes off the valueMerger and returns the saved 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() + // Saves a comparison in the fast path + firstIteration := true 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. - 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.savedKey.Kind() != InternalKeyKindMerge { panic(fmt.Sprintf("expected key %s to have MERGE kind", p.iterKey)) } p.pos = pcIterPosNext - return finishAndReturnMerge() + return p.finishAndReturnMerge() } p.saveKey() continue } + firstIteration = false if s := p.iter.Span(); s != nil && s.CoversAt(p.seqNum, p.iterKey.SeqNum()) { // All future keys for this user key must be deleted. if p.valueMerger != nil { - return finishAndReturnMerge() + return p.finishAndReturnMerge() } else if p.savedKey.Kind() == InternalKeyKindSingleDelete { panic("cannot process singledel key in point collapsing iterator") } // Fast forward to the next user key. 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) { - p.iterKey, p.value = p.iter.Next() + p.iterKey, p.iterValue = p.iter.Next() } continue } switch p.savedKey.Kind() { - case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete: - p.saveKey() + case InternalKeyKindSet, InternalKeyKindDelete, InternalKeyKindSetWithDelete, InternalKeyKindDeleteSized: // 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 // 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 // or comparisons. p.pos = pcIterPosCur - return p.iterKey, p.value + return p.verifySeqNum(p.iterKey), p.iterValue case InternalKeyKindSingleDelete: // Panic, as this iterator is not expected to observe single deletes. panic("cannot process singledel key in point collapsing iterator") case InternalKeyKindMerge: if p.valueMerger == nil { // 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 { p.err = err return nil, base.LazyValue{} @@ -263,13 +339,13 @@ func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyV return nil, base.LazyValue{} } p.saveKey() - p.iterKey, p.value = p.iter.Next() + p.iterKey, p.iterValue = p.iter.Next() continue } switch p.iterKey.Kind() { case InternalKeyKindSet, InternalKeyKindMerge, InternalKeyKindSetWithDelete: // Merge into key. - value, callerOwned, err := p.value.Value(p.valueBuf[:0]) + value, callerOwned, err := p.iterValue.Value(p.valueBuf[:0]) if err != nil { p.err = err return nil, base.LazyValue{} @@ -287,31 +363,144 @@ func (p *pointCollapsingIterator) findNextEntry() (*base.InternalKey, base.LazyV if p.iterKey.Kind() != InternalKeyKindMerge { p.savedKey.SetKind(p.iterKey.Kind()) p.pos = pcIterPosCur - return finishAndReturnMerge() + return p.finishAndReturnMerge() } - p.iterKey, p.value = p.iter.Next() + p.iterKey, p.iterValue = p.iter.Next() 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. // We should pass them as-is, but also account for any points ahead of // them. p.pos = pcIterPosCur - return p.iterKey, p.value + return p.verifySeqNum(p.iterKey), p.iterValue default: panic(fmt.Sprintf("unexpected kind: %d", p.iterKey.Kind())) } } if p.valueMerger != nil { p.pos = pcIterPosNext - return finishAndReturnMerge() + return p.finishAndReturnMerge() } p.resetKey() 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. func (p *pointCollapsingIterator) First() (*base.InternalKey, base.LazyValue) { p.resetKey() - p.iterKey, p.value = p.iter.First() + p.iterKey, p.iterValue = p.iter.First() p.pos = pcIterPosCur if p.iterKey == nil { return nil, base.LazyValue{} @@ -321,41 +510,67 @@ func (p *pointCollapsingIterator) First() (*base.InternalKey, base.LazyValue) { // Last implements the InternalIterator interface. 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() { if p.iterKey == nil { - p.savedKey = InternalKey{UserKey: p.savedKey.UserKey[:0]} - return - } else if p.iterKey == &p.savedKey { - // Do nothing. + p.savedKey = InternalKey{UserKey: p.savedKeyBuf[:0]} return } - p.savedKey.CopyFrom(*p.iterKey) - p.iterKey = &p.savedKey + p.savedKeyBuf = append(p.savedKeyBuf[:0], p.iterKey.UserKey...) + p.savedKey = InternalKey{UserKey: p.savedKeyBuf, Trailer: p.iterKey.Trailer} } // Next implements the InternalIterator interface. func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) { 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: 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 // 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. - p.iterKey, p.value = p.iter.Next() + p.iterKey, p.iterValue = p.iter.Next() break } - // Fast forward to the next user key. p.iterKey stayed at p.savedKey for - // this loop. + // 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 p.iterKey != nil && key != nil && p.iterKey.SeqNum() >= key.SeqNum() && - p.comparer.Equal(p.iterKey.UserKey, key.UserKey) { + for key != nil && p.savedKey.SeqNum() >= key.SeqNum() && + p.comparer.Equal(p.savedKey.UserKey, key.UserKey) { key, val = p.iter.Next() } if key == nil { @@ -363,7 +578,7 @@ func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) { p.resetKey() return nil, base.LazyValue{} } - p.iterKey, p.value = key, val + p.iterKey, p.iterValue = key, val case pcIterPosNext: p.pos = pcIterPosCur } @@ -376,12 +591,56 @@ func (p *pointCollapsingIterator) Next() (*base.InternalKey, base.LazyValue) { // NextPrefix implements the InternalIterator interface. 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") } // Prev implements the InternalIterator interface. 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. @@ -477,10 +736,6 @@ func (d *DB) truncateSharedFile( // We will need to truncate file bounds in at least one direction. Open all // 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{ LowerBound: lower, UpperBound: upper, @@ -497,7 +752,7 @@ func (d *DB) truncateSharedFile( ) defer rangeDelIter.Close() } - rangeKeyIter, err := d.tableNewRangeKeyIter(file, nil /* spanIterOptions */) + rangeKeyIter, err := d.tableNewRangeKeyIter(file, keyspan.SpanIterOptions{Level: manifest.Level(level)}) if err != nil { return nil, false, err } @@ -584,6 +839,10 @@ func (d *DB) truncateSharedFile( if len(sst.Smallest.UserKey) == 0 { 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 } @@ -616,7 +875,7 @@ func scanInternalImpl( for f := files.SeekGE(cmp, lower); f != nil && cmp(f.Smallest.UserKey, upper) < 0; f = files.Next() { var objMeta objstorage.ObjectMetadata var err error - objMeta, err = provider.Lookup(fileTypeTable, f.FileNum.DiskFileNum()) + objMeta, err = provider.Lookup(fileTypeTable, f.FileBacking.DiskFileNum) if err != nil { return err } @@ -718,6 +977,7 @@ func (i *scanInternalIterator) constructPointIter(memtables flushableList, buf * mlevels = mlevels[:numMergingLevels] levels = levels[:numLevelIters] rangeDelLevels = rangeDelLevels[:numLevelIters] + i.opts.IterOptions.snapshotForHideObsoletePoints = i.seqNum addLevelIterForFiles := func(files manifest.LevelIterator, level manifest.Level) { li := &levels[levelsIndex] rli := &rangeDelLevels[levelsIndex] @@ -804,8 +1064,7 @@ func (i *scanInternalIterator) constructRangeKeyIter() { // around Key Trailer order. iter := current.RangeKeyLevels[0].Iter() for f := iter.Last(); f != nil; f = iter.Prev() { - spanIterOpts := &keyspan.SpanIterOptions{RangeKeyFilters: i.opts.RangeKeyFilters} - spanIter, err := i.newIterRangeKey(f, spanIterOpts) + spanIter, err := i.newIterRangeKey(f, i.opts.SpanIterOptions(manifest.Level(0))) if err != nil { i.rangeKey.iterConfig.AddLevel(&errorKeyspanIter{err: err}) continue @@ -822,7 +1081,7 @@ func (i *scanInternalIterator) constructRangeKeyIter() { continue } 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(), manifest.Level(level), manifest.KeyTypeRange) i.rangeKey.iterConfig.AddLevel(li) diff --git a/vendor/github.com/cockroachdb/pebble/sstable/block.go b/vendor/github.com/cockroachdb/pebble/sstable/block.go index 27cf77f..53fe799 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/block.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/block.go @@ -47,7 +47,10 @@ type blockWriter struct { // ensuring that any of the keys with the same prefix can be used to // assemble the full key when the prefix does change. 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 // storeWithOptionalValuePrefix. curValue []byte @@ -79,6 +82,20 @@ const setHasSameKeyPrefixRestartMask uint32 = 1 << 31 const restartMaskLittleEndianHighByteWithoutSetHasSamePrefix byte = 0b0111_1111 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. func (w *blockWriter) storeWithOptionalValuePrefix( keySize int, @@ -191,12 +208,21 @@ func (w *blockWriter) storeWithOptionalValuePrefix( func (w *blockWriter) add(key InternalKey, value []byte) { 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. +// +// 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( key InternalKey, + isObsolete bool, value []byte, maxSharedKeyLen int, addValuePrefix bool, @@ -210,6 +236,9 @@ func (w *blockWriter) addWithOptionalValuePrefix( w.curKey = make([]byte, 0, size*2) } w.curKey = w.curKey[:size] + if isObsolete { + key.Trailer = key.Trailer | trailerObsoleteBit + } key.Encode(w.curKey) w.storeWithOptionalValuePrefix( @@ -374,13 +403,14 @@ type blockIter struct { cached []blockEntry cachedBuf []byte 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. - firstKey InternalKey + firstUserKey []byte lazyValueHandling struct { vbr *valueBlockReader hasValuePrefix bool } + hideObsoletePoints bool } // blockIter implements the base.InternalIterator interface. @@ -388,14 +418,16 @@ var _ base.InternalIterator = (*blockIter)(nil) func newBlockIter(cmp Compare, block block) (*blockIter, error) { i := &blockIter{} - return i, i.init(cmp, block, 0) + return i, i.init(cmp, block, 0, false) } func (i *blockIter) String() string { 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:])) if numRestarts == 0 { 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.fullKey = i.fullKey[:0] i.val = nil + i.hideObsoletePoints = hideObsoletePoints i.clearCache() if i.restarts > 0 { if err := i.readFirstKey(); err != nil { @@ -415,15 +448,21 @@ func (i *blockIter) init(cmp Compare, block block, globalSeqNum uint64) error { } } else { // Block is empty. - i.firstKey = InternalKey{} + i.firstUserKey = 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 = block - return i.init(cmp, block.Get(), globalSeqNum) + return i.init(cmp, block.Get(), globalSeqNum, hideObsoletePoints) } func (i *blockIter) invalidate() { @@ -590,24 +629,27 @@ func (i *blockIter) readFirstKey() error { // Manually inlining base.DecodeInternalKey provides a 5-10% speedup on // BlockIter benchmarks. if n := len(firstKey) - 8; n >= 0 { - i.firstKey.Trailer = binary.LittleEndian.Uint64(firstKey[n:]) - i.firstKey.UserKey = firstKey[:n:n] - if i.globalSeqNum != 0 { - i.firstKey.SetSeqNum(i.globalSeqNum) - } + i.firstUserKey = firstKey[:n:n] } else { - i.firstKey.Trailer = uint64(InternalKeyKindInvalid) - i.firstKey.UserKey = nil + i.firstUserKey = nil return base.CorruptionErrorf("pebble/table: invalid firstKey in block") } 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 // BlockIter benchmarks. 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] if i.globalSeqNum != 0 { i.ikey.SetSeqNum(i.globalSeqNum) @@ -616,6 +658,7 @@ func (i *blockIter) decodeInternalKey(key []byte) { i.ikey.Trailer = uint64(InternalKeyKindInvalid) i.ikey.UserKey = nil } + return hiddenPoint } func (i *blockIter) clearCache() { @@ -640,13 +683,15 @@ func (i *blockIter) cacheEntry() { i.cachedBuf = append(i.cachedBuf, i.key...) } +func (i *blockIter) getFirstUserKey() []byte { + return i.firstUserKey +} + // SeekGE implements internalIterator.SeekGE, as documented in the pebble // package. func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, base.LazyValue) { i.clearCache() - ikey := base.MakeSearchKey(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. i.offset = 0 @@ -700,23 +745,23 @@ func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, ba ptr = unsafe.Pointer(uintptr(ptr) + 5) } - // Manually inlining base.DecodeInternalKey provides a 5-10% speedup on - // BlockIter benchmarks. + // Manually inlining part of base.DecodeInternalKey provides a 5-10% + // speedup on BlockIter benchmarks. s := getBytes(ptr, int(v1)) - var k InternalKey + var k []byte if n := len(s) - 8; n >= 0 { - k.Trailer = binary.LittleEndian.Uint64(s[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) + k = s[:n:n] } + // 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 } else { + // k >= search key, so prune everything after index (since index + // satisfies the property we are looking for). upper = h // preserves f(j) == true } } @@ -724,21 +769,25 @@ func (i *blockIter) SeekGE(key []byte, flags base.SeekGEFlags) (*InternalKey, ba // => answer is index. } - // Since keys are strictly increasing, if index > 0 then the restart point at - // index-1 will be the largest whose key is <= the key sought. If index == - // 0, then all keys in this block are larger than the key sought, and offset - // remains at zero. + // index is the first restart point with key >= search key. Define the keys + // between a restart point and the next restart point as belonging to that + // restart point. + // + // 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 { i.offset = decodeRestart(i.data[i.restarts+4*(index-1):]) } i.readEntry() - i.decodeInternalKey(i.key) + hiddenPoint := i.decodeInternalKey(i.key) // Iterate from that restart point to somewhere >= the key sought. if !i.valid() { 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 if !i.lazyValueHandling.hasValuePrefix || 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 } 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. 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) { i.clearCache() - ikey := base.MakeSearchKey(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. i.offset = 0 @@ -828,23 +875,23 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba ptr = unsafe.Pointer(uintptr(ptr) + 5) } - // Manually inlining base.DecodeInternalKey provides a 5-10% speedup on - // BlockIter benchmarks. + // Manually inlining part of base.DecodeInternalKey provides a 5-10% + // speedup on BlockIter benchmarks. s := getBytes(ptr, int(v1)) - var k InternalKey + var k []byte if n := len(s) - 8; n >= 0 { - k.Trailer = binary.LittleEndian.Uint64(s[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) + k = s[:n:n] } + // 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 } else { + // k >= search key, so prune everything after index (since index + // satisfies the property we are looking for). upper = h // preserves f(j) == true } } @@ -852,8 +899,15 @@ func (i *blockIter) SeekLT(key []byte, flags base.SeekLTFlags) (*InternalKey, ba // => answer is index. } - // Since keys are strictly increasing, if index > 0 then the restart point at - // index-1 will be the largest whose key is < the key sought. + // index is the first restart point with key >= search key. Define the keys + // 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 if index > 0 { 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 { i.offset = i.nextOffset 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 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 // the key we are backing up to is the last one this loop cached. - i.Prev() - // i.Prev() has already initialized i.lazyValue. - return &i.ikey, i.lazyValue + return i.Prev() } if i.nextOffset >= targetOffset { - // We've reached the end of the current restart block. Return the current - // key. When the restart interval is 1, the first iteration of the for - // 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 - // blockIter. + // We've reached the end of the current restart block. Return the + // current key if not hidden, else call Prev(). + // + // When the restart interval is 1, the first iteration of the for 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 + // blockIter. That is, we never cache anything and therefore never + // return a key backed by cachedBuf. + if hiddenPoint { + return i.Prev() + } break } @@ -923,7 +994,10 @@ func (i *blockIter) First() (*InternalKey, base.LazyValue) { } i.clearCache() i.readEntry() - i.decodeInternalKey(i.key) + hiddenPoint := i.decodeInternalKey(i.key) + if hiddenPoint { + return i.Next() + } if !i.lazyValueHandling.hasValuePrefix || base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet { i.lazyValue = base.MakeInPlaceValue(i.val) @@ -958,7 +1032,10 @@ func (i *blockIter) Last() (*InternalKey, base.LazyValue) { i.readEntry() } - i.decodeInternalKey(i.key) + hiddenPoint := i.decodeInternalKey(i.key) + if hiddenPoint { + return i.Prev() + } if !i.lazyValueHandling.hasValuePrefix || base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet { i.lazyValue = base.MakeInPlaceValue(i.val) @@ -988,6 +1065,7 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) { i.clearCache() } +start: i.offset = i.nextOffset if !i.valid() { return nil, base.LazyValue{} @@ -995,11 +1073,17 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) { i.readEntry() // Manually inlined version of i.decodeInternalKey(i.key). 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] if i.globalSeqNum != 0 { i.ikey.SetSeqNum(i.globalSeqNum) } + if hiddenPoint { + goto start + } } else { i.ikey.Trailer = uint64(InternalKeyKindInvalid) i.ikey.UserKey = nil @@ -1015,8 +1099,8 @@ func (i *blockIter) Next() (*InternalKey, base.LazyValue) { return &i.ikey, i.lazyValue } -// nextPrefix is used for implementing NPrefix. -func (i *blockIter) nextPrefix(succKey []byte) (*InternalKey, base.LazyValue) { +// NextPrefix implements (base.InternalIterator).NextPrefix. +func (i *blockIter) NextPrefix(succKey []byte) (*InternalKey, base.LazyValue) { if i.lazyValueHandling.hasValuePrefix { 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 // byte in the trailer that is encoded in the slice [unshared-8:unshared]. keyKind := InternalKeyKind((*[manual.MaxArrayLen]byte)(ptr)[unshared-8]) + keyKind = keyKind & base.InternalKeyKindSSTableInternalObsoleteMask prefixChanged := false if keyKind == InternalKeyKindSet { if invariants.Enabled && value == 0 { @@ -1256,8 +1341,12 @@ func (i *blockIter) nextPrefixV3(succKey []byte) (*InternalKey, base.LazyValue) i.key = i.fullKey } // Manually inlined version of i.decodeInternalKey(i.key). + hiddenPoint := false 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] if i.globalSeqNum != 0 { 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 { // Prefix has changed. + if hiddenPoint { + return i.Next() + } if invariants.Enabled && !i.lazyValueHandling.hasValuePrefix { 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) } -// 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 // package. 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 e := &i.cached[n] i.offset = e.offset @@ -1318,7 +1398,13 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) { // Manually inlined version of i.decodeInternalKey(i.key). i.key = i.cachedBuf[e.keyStart:e.keyEnd] 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] if i.globalSeqNum != 0 { i.ikey.SetSeqNum(i.globalSeqNum) @@ -1360,6 +1446,8 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) { // index ≤ h < upper offset := decodeRestart(i.data[i.restarts+4*h:]) 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 } else { upper = h // preserves f(j) == true @@ -1369,20 +1457,35 @@ func (i *blockIter) Prev() (*InternalKey, base.LazyValue) { // => 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 if index > 0 { 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() + // 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 { i.cacheEntry() i.offset = i.nextOffset i.readEntry() } - i.decodeInternalKey(i.key) + hiddenPoint := i.decodeInternalKey(i.key) + if hiddenPoint { + // Use the cache. + goto start + } if !i.lazyValueHandling.hasValuePrefix || base.TrailerKind(i.ikey.Trailer) != InternalKeyKindSet { i.lazyValue = base.MakeInPlaceValue(i.val) @@ -1455,6 +1558,10 @@ type fragmentBlockIter struct { err error dir int8 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 { @@ -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 // 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 @@ -1532,6 +1657,9 @@ func (i *fragmentBlockIter) gatherForward(k *InternalKey, lazyValue base.LazyVal k, lazyValue = i.blockIter.Next() 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. 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. keyspan.SortKeysByTrailer(&i.span.Keys) + if i.elideSameSeqnum && len(i.span.Keys) > 0 { + i.elideKeysOfSameSeqNum() + } return &i.span } diff --git a/vendor/github.com/cockroachdb/pebble/sstable/block_property.go b/vendor/github.com/cockroachdb/pebble/sstable/block_property.go index 1e986fb..9bec529 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/block_property.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/block_property.go @@ -117,7 +117,7 @@ type BlockPropertyCollector interface { } // 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 // all keys have the same key suffix is updated to have a new suffix. // diff --git a/vendor/github.com/cockroachdb/pebble/sstable/format.go b/vendor/github.com/cockroachdb/pebble/sstable/format.go index 9190983..a1d9be0 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/format.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/format.go @@ -24,11 +24,161 @@ const ( TableFormatPebblev1 // Block properties. TableFormatPebblev2 // Range keys. TableFormatPebblev3 // Value blocks. - TableFormatPebblev4 // Reserved. + TableFormatPebblev4 // DELSIZED tombstones. 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 // corresponding internal TableFormat. func ParseTableFormat(magic []byte, version uint32) (TableFormat, error) { diff --git a/vendor/github.com/cockroachdb/pebble/sstable/internal.go b/vendor/github.com/cockroachdb/pebble/sstable/internal.go index 9bab181..0fe7c99 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/internal.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/internal.go @@ -23,6 +23,8 @@ const ( InternalKeyKindLogData = base.InternalKeyKindLogData InternalKeyKindSingleDelete = base.InternalKeyKindSingleDelete InternalKeyKindRangeDelete = base.InternalKeyKindRangeDelete + InternalKeyKindSetWithDelete = base.InternalKeyKindSetWithDelete + InternalKeyKindDeleteSized = base.InternalKeyKindDeleteSized InternalKeyKindMax = base.InternalKeyKindMax InternalKeyKindInvalid = base.InternalKeyKindInvalid InternalKeySeqNumBatch = base.InternalKeySeqNumBatch diff --git a/vendor/github.com/cockroachdb/pebble/sstable/options.go b/vendor/github.com/cockroachdb/pebble/sstable/options.go index 66dbdd4..c5e1f79 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/options.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/options.go @@ -21,6 +21,17 @@ const ( 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 { switch c { case DefaultCompression: @@ -100,6 +111,9 @@ type ReaderOptions struct { // The default cache size is a zero-size 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 // than' relationship. The same comparison algorithm must be used for reads // 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. 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 // debugging tools which may be used on multiple databases configured with // different filter policies. It is not necessary to populate this filters @@ -126,12 +143,18 @@ func (o ReaderOptions) ensureDefaults() ReaderOptions { if o.Comparer == nil { o.Comparer = base.DefaultComparer } + if o.Merge == nil { + o.Merge = base.DefaultMerger.Merge + } if o.MergerName == "" { o.MergerName = base.DefaultMerger.Name } if o.LoggerAndTracer == nil { o.LoggerAndTracer = base.NoopLoggerAndTracer{} } + if o.DeniedUserProperties == nil { + o.DeniedUserProperties = ignoredInternalProperties + } return o } @@ -210,6 +233,17 @@ type WriterOptions struct { // by a wider range of tools and libraries. 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 // functions. A new TablePropertyCollector is created for each sstable built // and lives for the lifetime of the table. diff --git a/vendor/github.com/cockroachdb/pebble/sstable/properties.go b/vendor/github.com/cockroachdb/pebble/sstable/properties.go index ca417ce..004d14a 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/properties.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/properties.go @@ -23,14 +23,6 @@ var propTagMap = make(map[string]reflect.StructField) var propBoolTrue = []byte{'1'} 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) func init() { @@ -56,51 +48,35 @@ func init() { // automatically populated during sstable creation and load from the properties // meta block when an sstable is opened. 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. ComparerName string `prop:"rocksdb.comparator"` // The compression algorithm used to compress blocks. CompressionName string `prop:"rocksdb.compression"` // The compression options used to compress blocks. 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. DataSize uint64 `prop:"rocksdb.data.size"` // 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 // sstable if this property has been set. 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 // policy is used. FilterPolicyName string `prop:"rocksdb.filter.policy"` // The size of filter block. 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 table was created externally and ingested whole. 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. IndexPartitions uint64 `prop:"rocksdb.index.partitions"` // The size of index block. IndexSize uint64 `prop:"rocksdb.index.size"` // The index type. TODO(peter): add a more detailed description. IndexType uint32 `prop:"rocksdb.block.based.table.index.type"` - // Whether delta encoding is used to encode the index values. - IndexValueIsDeltaEncoded uint64 `prop:"rocksdb.index.value.is.delta.encoded"` + // For formats >= TableFormatPebblev4, this is set to true if the obsolete + // 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. MergerName string `prop:"rocksdb.merge.operator"` // 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 // range deletions. 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. NumEntries uint64 `prop:"rocksdb.num.entries"` // The number of merge operands in the table. @@ -124,8 +103,6 @@ type Properties struct { NumValueBlocks uint64 `prop:"pebble.num.value-blocks"` // The number of values stored in value blocks. Only serialized if > 0. 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 // extractor is used. 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 // to RawKeySize. 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. RawRangeKeyKeySize uint64 `prop:"pebble.raw.range-key.key.size"` // Total raw rangekey value size. @@ -206,12 +187,7 @@ func (p *Properties) String() string { case reflect.Uint32: fmt.Fprintf(&buf, "%d\n", f.Uint()) case reflect.Uint64: - u := f.Uint() - if ft.Offset == columnFamilyIDField.Offset && u == math.MaxInt32 { - fmt.Fprintf(&buf, "-\n") - } else { - fmt.Fprintf(&buf, "%d\n", f.Uint()) - } + fmt.Fprintf(&buf, "%d\n", f.Uint()) case reflect.String: fmt.Fprintf(&buf, "%s\n", f.String()) default: @@ -229,7 +205,9 @@ func (p *Properties) String() 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) if err != nil { return err @@ -237,8 +215,7 @@ func (p *Properties) load(b block, blockOffset uint64) error { p.Loaded = make(map[uintptr]struct{}) v := reflect.ValueOf(p).Elem() for valid := i.First(); valid; valid = i.Next() { - tag := intern.Bytes(i.Key().UserKey) - if f, ok := propTagMap[tag]; ok { + if f, ok := propTagMap[string(i.Key().UserKey)]; ok { p.Loaded[f.Offset] = struct{}{} field := v.FieldByIndex(f.Index) 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()))) case reflect.Uint64: var n uint64 - if tag == propGlobalSeqnumName { + if string(i.Key().UserKey) == propGlobalSeqnumName { n = binary.LittleEndian.Uint64(i.Value()) } else { n, _ = binary.Uvarint(i.Value()) @@ -264,7 +241,10 @@ func (p *Properties) load(b block, blockOffset uint64) error { if p.UserProperties == nil { 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 } @@ -306,10 +286,6 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) { 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 != "" { p.saveString(m, unsafe.Offsetof(p.ComparerName), p.ComparerName) } @@ -319,44 +295,45 @@ func (p *Properties) save(tblFormat TableFormat, w *rawBlockWriter) { if 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) if p.ExternalFormatVersion != 0 { p.saveUint32(m, unsafe.Offsetof(p.ExternalFormatVersion), p.ExternalFormatVersion) 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 != "" { p.saveString(m, unsafe.Offsetof(p.FilterPolicyName), p.FilterPolicyName) } 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 { p.saveUvarint(m, unsafe.Offsetof(p.IndexPartitions), p.IndexPartitions) p.saveUvarint(m, unsafe.Offsetof(p.TopLevelIndexSize), p.TopLevelIndexSize) } p.saveUvarint(m, unsafe.Offsetof(p.IndexSize), p.IndexSize) 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 != "" { p.saveString(m, unsafe.Offsetof(p.MergerName), p.MergerName) } p.saveUvarint(m, unsafe.Offsetof(p.NumDataBlocks), p.NumDataBlocks) p.saveUvarint(m, unsafe.Offsetof(p.NumEntries), p.NumEntries) 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.NumRangeDeletions), p.NumRangeDeletions) - // NB: We only write out the RawTombstoneKeySize for Pebble formats. This - // isn't strictly necessary because unrecognized properties are interpreted - // as user-defined properties, however writing them prevents byte-for-byte + // NB: We only write out some properties for Pebble formats. This isn't + // strictly necessary because unrecognized properties are interpreted as + // user-defined properties, however writing them prevents byte-for-byte // equivalence with RocksDB files that some of our testing requires. if p.RawPointTombstoneKeySize > 0 && tblFormat >= TableFormatPebblev1 { 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 { p.saveUvarint(m, unsafe.Offsetof(p.NumRangeKeyDels), p.NumRangeKeyDels) 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 { p.saveUvarint(m, unsafe.Offsetof(p.NumValuesInValueBlocks), p.NumValuesInValueBlocks) } - p.saveUvarint(m, unsafe.Offsetof(p.OldestKeyTime), p.OldestKeyTime) if 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) + 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)) for key := range m { keys = append(keys, key) diff --git a/vendor/github.com/cockroachdb/pebble/sstable/reader.go b/vendor/github.com/cockroachdb/pebble/sstable/reader.go index 776e87e..716b668 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/reader.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/reader.go @@ -323,6 +323,8 @@ type singleLevelIterator struct { // is high). useFilter bool lastBloomFilterMatched bool + + hideObsoletePoints bool } // singleLevelIterator implements the base.InternalIterator interface. @@ -404,7 +406,7 @@ func (i *singleLevelIterator) init( v *virtualState, lower, upper []byte, filterer *BlockPropertiesFilterer, - useFilter bool, + useFilter, hideObsoletePoints bool, stats *base.InternalIteratorStats, rp ReaderProvider, ) error { @@ -428,7 +430,8 @@ func (i *singleLevelIterator) init( i.reader = r i.cmp = r.Compare 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 { // blockIter.Close releases indexH and always returns a nil error _ = i.index.Close() @@ -464,9 +467,9 @@ func (i *singleLevelIterator) init( // setupForCompaction sets up the singleLevelIterator for use with compactionIter. // Currently, it skips readahead ramp-up. It should be called after init is called. func (i *singleLevelIterator) setupForCompaction() { - i.dataRH.MaxReadahead() + i.dataRH.SetupForCompaction() if i.vbRH != nil { - i.vbRH.MaxReadahead() + i.vbRH.SetupForCompaction() } } @@ -566,7 +569,7 @@ func (i *singleLevelIterator) loadBlock(dir int8) loadBlockResult { i.err = err 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 { // The block is partially loaded, and we don't want it to appear valid. i.data.invalidate() @@ -653,12 +656,13 @@ func (i *singleLevelIterator) resolveMaybeExcluded(dir int8) intersectsResult { } 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") } i.blockLower = i.lower 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 // to check the lower-bound again for this block. i.blockLower = nil @@ -1066,7 +1070,7 @@ func (i *singleLevelIterator) SeekLT( var dontSeekWithinBlock bool 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 // respecting the upper bound (guaranteed by Iterator). We know that // 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 { 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 { cmp := i.cmp(key.UserKey, i.blockUpper) if (!i.endKeyInclusive && cmp >= 0) || cmp > 0 { @@ -1763,8 +1767,7 @@ func (i *twoLevelIterator) loadIndex(dir int8) loadBlockResult { i.err = err return loadBlockFailed } - if i.err = i.index.initHandle( - i.cmp, indexBlock, i.reader.Properties.GlobalSeqNum); i.err == nil { + if i.err = i.index.initHandle(i.cmp, indexBlock, i.reader.Properties.GlobalSeqNum, false); i.err == nil { return loadBlockOK } return loadBlockFailed @@ -1842,7 +1845,7 @@ func (i *twoLevelIterator) init( v *virtualState, lower, upper []byte, filterer *BlockPropertiesFilterer, - useFilter bool, + useFilter, hideObsoletePoints bool, stats *base.InternalIteratorStats, rp ReaderProvider, ) error { @@ -1867,7 +1870,8 @@ func (i *twoLevelIterator) init( i.reader = r i.cmp = r.Compare 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 { // blockIter.Close releases topLevelIndexH and always returns a nil error _ = i.topLevelIndex.Close() @@ -2907,21 +2911,20 @@ func (v *VirtualReader) NewCompactionIter( return v.reader.newCompactionIter(bytesIterated, rp, &v.vState) } -// NewIterWithBlockPropertyFiltersAndContext wraps +// NewIterWithBlockPropertyFiltersAndContextEtc wraps // Reader.NewIterWithBlockPropertyFiltersAndContext. We assume that the passed // in [lower, upper) bounds will have at least some overlap with the virtual // sstable bounds. No overlap is not currently supported in the iterator. -func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContext( +func (v *VirtualReader) NewIterWithBlockPropertyFiltersAndContextEtc( ctx context.Context, lower, upper []byte, filterer *BlockPropertiesFilterer, - useFilterBlock bool, + hideObsoletePoints, useFilterBlock bool, stats *base.InternalIteratorStats, rp ReaderProvider, ) (Iterator, error) { return v.reader.newIterWithBlockPropertyFiltersAndContext( - ctx, - lower, upper, filterer, useFilterBlock, stats, rp, &v.vState, + ctx, lower, upper, filterer, hideObsoletePoints, useFilterBlock, stats, rp, &v.vState, ) } @@ -3060,31 +3063,51 @@ func (r *Reader) NewIterWithBlockPropertyFilters( ) (Iterator, error) { return r.newIterWithBlockPropertyFiltersAndContext( 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 // 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, lower, upper []byte, filterer *BlockPropertiesFilterer, - useFilterBlock bool, + hideObsoletePoints, useFilterBlock bool, stats *base.InternalIteratorStats, rp ReaderProvider, ) (Iterator, error) { return r.newIterWithBlockPropertyFiltersAndContext( - ctx, - lower, upper, filterer, useFilterBlock, stats, rp, nil, + ctx, lower, upper, filterer, hideObsoletePoints, 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( ctx context.Context, lower, upper []byte, filterer *BlockPropertiesFilterer, + hideObsoletePoints bool, useFilterBlock bool, stats *base.InternalIteratorStats, rp ReaderProvider, @@ -3095,7 +3118,7 @@ func (r *Reader) newIterWithBlockPropertyFiltersAndContext( // until the final iterator closes. if r.Properties.IndexType == twoLevelIndex { 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 { return nil, err } @@ -3103,7 +3126,7 @@ func (r *Reader) newIterWithBlockPropertyFiltersAndContext( } 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 { return nil, err } @@ -3135,7 +3158,8 @@ func (r *Reader) newCompactionIter( err := i.init( context.Background(), r, v, nil /* lower */, nil /* upper */, nil, - false /* useFilter */, nil /* stats */, rp, + false /* useFilter */, false, /* hideObsoletePoints */ + nil /* stats */, rp, ) if err != nil { return nil, err @@ -3149,7 +3173,8 @@ func (r *Reader) newCompactionIter( i := singleLevelIterPool.Get().(*singleLevelIterator) err := i.init( 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 { return nil, err @@ -3175,8 +3200,8 @@ func (r *Reader) NewRawRangeDelIter() (keyspan.FragmentIterator, error) { if err != nil { return nil, err } - i := &fragmentBlockIter{} - if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum); err != nil { + i := &fragmentBlockIter{elideSameSeqnum: true} + if err := i.blockIter.initHandle(r.Compare, h, r.Properties.GlobalSeqNum, false); err != nil { return nil, err } return i, nil @@ -3197,7 +3222,7 @@ func (r *Reader) NewRawRangeKeyIter() (keyspan.FragmentIterator, error) { return nil, err } 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 i, nil @@ -3357,7 +3382,7 @@ func (r *Reader) transformRangeDelV1(b []byte) ([]byte, error) { // tombstones. We need properly fragmented and sorted range tombstones in // order to serve from them directly. 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 } var tombstones []keyspan.Span @@ -3444,7 +3469,7 @@ func (r *Reader) readMetaindex(metaindexBH BlockHandle) error { return err } r.propertiesBH = bh - err := r.Properties.load(b.Get(), bh.Offset) + err := r.Properties.load(b.Get(), bh.Offset, r.opts.DeniedUserProperties) b.Release() if err != nil { return err @@ -3549,7 +3574,8 @@ func (r *Reader) Layout() (*Layout, error) { if err != nil { 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 } 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 { return nil, r.Close() } + return r, nil } diff --git a/vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go b/vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go index b1423fd..9672ded 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/suffix_rewriter.go @@ -43,6 +43,9 @@ func RewriteKeySuffixes( // modification as they are not affected by the suffix, while block and table // 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 // implement SuffixReplaceableTableCollector/SuffixReplaceableBlockCollector. // @@ -50,6 +53,26 @@ func RewriteKeySuffixes( // 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 // 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( sst []byte, rOpts ReaderOptions, @@ -184,7 +207,7 @@ func rewriteBlocks( if err != nil { 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 } @@ -433,8 +456,13 @@ func (c copyFilterWriter) policyName() string { return c.origPolicyName } // RewriteKeySuffixesViaWriter is similar to RewriteKeySuffixes but uses just a // 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 -// more work to rederive filters, props, etc, however re-doing that work makes -// it less restrictive -- props no longer need to +// more work to rederive filters, props, etc. +// +// 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( r *Reader, out objstorage.Writable, o WriterOptions, from, to []byte, ) (*WriterMetadata, error) { @@ -442,6 +470,7 @@ func RewriteKeySuffixesViaWriter( return nil, errors.New("a valid splitter is required to rewrite suffixes") } + o.IsStrictObsolete = false w := NewWriter(out, o) defer func() { if w != nil { @@ -472,7 +501,7 @@ func RewriteKeySuffixesViaWriter( if err != nil { return nil, err } - if w.addPoint(scratch, val); err != nil { + if w.addPoint(scratch, val, false); err != nil { return nil, err } k, v = i.Next() diff --git a/vendor/github.com/cockroachdb/pebble/sstable/table.go b/vendor/github.com/cockroachdb/pebble/sstable/table.go index 5267123..040efaf 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/table.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/table.go @@ -160,8 +160,27 @@ The metaindex block also contains block handles as values, with keys being the names of the meta blocks. 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 ( diff --git a/vendor/github.com/cockroachdb/pebble/sstable/writer.go b/vendor/github.com/cockroachdb/pebble/sstable/writer.go index d818688..66a207e 100644 --- a/vendor/github.com/cockroachdb/pebble/sstable/writer.go +++ b/vendor/github.com/cockroachdb/pebble/sstable/writer.go @@ -132,6 +132,8 @@ type Writer struct { separator Separator successor Successor tableFormat TableFormat + isStrictObsolete bool + writingToLowestLevel bool cache *cache.Cache restartInterval int checksumType ChecksumType @@ -166,6 +168,7 @@ type Writer struct { props Properties propCollectors []TablePropertyCollector blockPropCollectors []BlockPropertyCollector + obsoleteCollector obsoleteKeyBlockPropertyCollector blockPropsEncoder blockPropertiesEncoder // 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 @@ -215,6 +218,8 @@ type pointKeyInfo struct { // prefixLen uses w.split, if not nil. Only computed when w.valueBlockWriter // is not nil. prefixLen int + // True iff the point was marked obsolete. + isObsolete bool } type coordinationState struct { @@ -673,7 +678,12 @@ func (w *Writer) Set(key, value []byte) error { if w.err != nil { 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 @@ -685,7 +695,12 @@ func (w *Writer) Delete(key []byte) error { if w.err != nil { 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) @@ -711,7 +726,13 @@ func (w *Writer) Merge(key, value []byte) error { if w.err != nil { 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, @@ -721,6 +742,24 @@ func (w *Writer) Merge(key, value []byte) error { // point entries. Additionally, range deletion tombstones must be fragmented // (i.e. by keyspan.Fragmenter). 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 { 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") return w.err } - return w.addPoint(key, value) + return w.addPoint(key, value, forceObsolete) } func (w *Writer) makeAddPointDecisionV2(key InternalKey) error { @@ -745,12 +784,7 @@ func (w *Writer) makeAddPointDecisionV2(key InternalKey) error { return nil } if !w.disableKeyOrderChecks { - prevPointUserKey := w.dataBlockBuf.dataBlock.curKey - n := len(prevPointUserKey) - base.InternalTrailerLen - if n < 0 { - panic("corrupt key in blockWriter buffer") - } - prevPointUserKey = prevPointUserKey[:n:n] + prevPointUserKey := w.dataBlockBuf.dataBlock.getCurUserKey() cmpUser := w.compare(prevPointUserKey, key.UserKey) if cmpUser > 0 || (cmpUser == 0 && prevTrailer <= key.Trailer) { return errors.Errorf( @@ -762,9 +796,17 @@ func (w *Writer) makeAddPointDecisionV2(key InternalKey) error { 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( key InternalKey, valueLen int, -) (setHasSamePrefix bool, writeToValueBlock bool, err error) { +) (setHasSamePrefix bool, writeToValueBlock bool, isObsolete bool, err error) { prevPointKeyInfo := w.lastPointKeyInfo w.lastPointKeyInfo.userKeyLen = len(key.UserKey) w.lastPointKeyInfo.prefixLen = w.lastPointKeyInfo.userKeyLen @@ -772,18 +814,16 @@ func (w *Writer) makeAddPointDecisionV3( w.lastPointKeyInfo.prefixLen = w.split(key.UserKey) } w.lastPointKeyInfo.trailer = key.Trailer - if w.dataBlockBuf.dataBlock.nEntries == 0 { - return false, false, nil + w.lastPointKeyInfo.isObsolete = false + if !w.meta.HasPointKeys { + return false, false, false, nil } - prevPointUserKey := w.dataBlockBuf.dataBlock.curKey - n := len(prevPointUserKey) - base.InternalTrailerLen - if n < 0 { - panic("corrupt key in blockWriter buffer") - } - prevPointUserKey = prevPointUserKey[:n:n] + keyKind := base.TrailerKind(key.Trailer) + prevPointUserKey := w.getLastPointUserKey() prevPointKey := InternalKey{UserKey: prevPointUserKey, Trailer: prevPointKeyInfo.trailer} - considerWriteToValueBlock := base.TrailerKind(prevPointKeyInfo.trailer) == InternalKeyKindSet && - base.TrailerKind(key.Trailer) == InternalKeyKindSet + prevKeyKind := base.TrailerKind(prevPointKeyInfo.trailer) + considerWriteToValueBlock := prevKeyKind == InternalKeyKindSet && + keyKind == InternalKeyKindSet if considerWriteToValueBlock && !w.requiredInPlaceValueBound.IsEmpty() { keyPrefix := key.UserKey[:w.lastPointKeyInfo.prefixLen] cmpUpper := w.compare( @@ -812,14 +852,59 @@ func (w *Writer) makeAddPointDecisionV3( } else { 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 && (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", prevPointKey.Pretty(w.formatKey), key.Pretty(w.formatKey)) } 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 // 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 { 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 setHasSameKeyPrefix, writeToValueBlock, addPrefixToValueStoredWithKey bool + var isObsolete bool maxSharedKeyLen := len(key.UserKey) if w.valueBlockWriter != nil { // maxSharedKeyLen is limited to the prefix of the preceding key. If the // preceding key was in a different block, then the blockWriter will // ignore this maxSharedKeyLen. 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 } else { err = w.makeAddPointDecisionV2(key) @@ -852,6 +942,8 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error { if err != nil { return err } + isObsolete = w.tableFormat >= TableFormatPebblev4 && (isObsolete || forceObsolete) + w.lastPointKeyInfo.isObsolete = isObsolete var valueStoredWithKey []byte var prefix valuePrefix var valueStoredWithKeyLen int @@ -907,16 +999,19 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error { return err } } + if w.tableFormat >= TableFormatPebblev4 { + w.obsoleteCollector.AddPoint(isObsolete) + } w.maybeAddToFilter(key.UserKey) w.dataBlockBuf.dataBlock.addWithOptionalValuePrefix( - key, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix, + key, isObsolete, valueStoredWithKey, maxSharedKeyLen, addPrefixToValueStoredWithKey, prefix, setHasSameKeyPrefix) w.meta.updateSeqNum(key.SeqNum()) 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 // 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 @@ -932,7 +1027,22 @@ func (w *Writer) addPoint(key InternalKey, value []byte) error { switch key.Kind() { case InternalKeyKindDelete, InternalKeyKindSingleDelete: 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: 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 { // Check that tombstones are being added in fragmented order. If the two // 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); { case c > 0: 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 { 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) if !ok { // 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 // byte slice which supports "sep" will eventually be copied when "sep" is // added to the index block. - prevKey := base.DecodeInternalKey(w.dataBlockBuf.dataBlock.curKey) + prevKey := w.dataBlockBuf.dataBlock.getCurKey() sep := w.indexEntrySep(prevKey, key, w.dataBlockBuf) // We determine that we should flush an index block from the Writer client // 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, } w.indexSepAlloc, part.sep = cloneKeyWithBuf( - base.DecodeInternalKey(indexBuf.block.curKey), w.indexSepAlloc, + indexBuf.block.getCurKey(), w.indexSepAlloc, ) bk := indexBuf.finish() 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 || w.props.ValueBlocksSize > 0) && w.tableFormat < TableFormatPebblev3 { return errors.Newf( "table format version %s is less than the minimum required version %s for value blocks", 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 } @@ -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 // addPoint function after the flush occurs. 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 @@ -1795,7 +1913,7 @@ func (w *Writer) Close() (err error) { if err != nil { 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 { return err } @@ -1874,7 +1992,7 @@ func (w *Writer) Close() (err error) { var rangeKeyBH BlockHandle if w.props.NumRangeKeys() > 0 { - key := base.DecodeInternalKey(w.rangeKeyBlock.curKey) + key := w.rangeKeyBlock.getCurKey() kind := key.Kind() endKey, _, ok := rangekey.DecodeEndKey(kind, w.rangeKeyBlock.curValue) if !ok { @@ -2063,7 +2181,7 @@ func (o PreviousPointKeyOpt) UnsafeKey() base.InternalKey { if o.w.dataBlockBuf.dataBlock.nEntries >= 1 { // o.w.dataBlockBuf.dataBlock.curKey is guaranteed to point to the last point key // which was added to the Writer. - return base.DecodeInternalKey(o.w.dataBlockBuf.dataBlock.curKey) + return o.w.dataBlockBuf.dataBlock.getCurKey() } return base.InternalKey{} } @@ -2092,6 +2210,8 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write separator: o.Comparer.Separator, successor: o.Comparer.Successor, tableFormat: o.TableFormat, + isStrictObsolete: o.IsStrictObsolete, + writingToLowestLevel: o.WritingToLowestLevel, cache: o.Cache, restartInterval: o.BlockRestartInterval, 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.CompressionName = o.Compression.String() w.props.MergerName = o.MergerName w.props.PropertyCollectorNames = "[]" 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 buf.WriteString("[") if len(o.TablePropertyCollectors) > 0 { @@ -2178,16 +2298,22 @@ func NewWriter(writable objstorage.Writable, o WriterOptions, extraOpts ...Write 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 { - // 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 // this slice. - w.blockPropCollectors = make([]BlockPropertyCollector, len(o.BlockPropertyCollectors)) for i := range o.BlockPropertyCollectors { w.blockPropCollectors[i] = o.BlockPropertyCollectors[i]() 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()) } } + 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("]") w.props.PropertyCollectorNames = buf.String() } @@ -2229,3 +2362,85 @@ func init() { } 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 +} diff --git a/vendor/github.com/cockroachdb/pebble/table_cache.go b/vendor/github.com/cockroachdb/pebble/table_cache.go index 9a3c5b5..b0d62c6 100644 --- a/vendor/github.com/cockroachdb/pebble/table_cache.go +++ b/vendor/github.com/cockroachdb/pebble/table_cache.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "io" + "math" "runtime/debug" "runtime/pprof" "sync" @@ -21,6 +22,7 @@ import ( "github.com/cockroachdb/pebble/internal/keyspan" "github.com/cockroachdb/pebble/internal/manifest" "github.com/cockroachdb/pebble/internal/private" + "github.com/cockroachdb/pebble/internal/rangekey" "github.com/cockroachdb/pebble/objstorage" "github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing" "github.com/cockroachdb/pebble/sstable" @@ -138,7 +140,7 @@ func (c *tableCacheContainer) newIters( } func (c *tableCacheContainer) newRangeKeyIter( - file *manifest.FileMetadata, opts *keyspan.SpanIterOptions, + file *manifest.FileMetadata, opts keyspan.SpanIterOptions, ) (keyspan.FragmentIterator, error) { 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 } +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 { s := c.tableCache.getShard(meta.FileBacking.DiskFileNum) v := s.findNode(meta.FileMetadata, &c.dbOpts) @@ -373,6 +401,26 @@ func (c *tableCacheShard) newIters( 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 var filterer *sstable.BlockPropertiesFilterer var err error @@ -387,14 +435,7 @@ func (c *tableCacheShard) newIters( type iterCreator interface { NewRawRangeDelIter() (keyspan.FragmentIterator, error) - NewIterWithBlockPropertyFiltersAndContext( - ctx context.Context, - lower, upper []byte, - filterer *sstable.BlockPropertiesFilterer, - useFilterBlock bool, - stats *base.InternalIteratorStats, - rp sstable.ReaderProvider, - ) (sstable.Iterator, error) + NewIterWithBlockPropertyFiltersAndContextEtc(ctx context.Context, lower, upper []byte, filterer *sstable.BlockPropertiesFilterer, hideObsoletePoints, useFilterBlock bool, stats *base.InternalIteratorStats, rp sstable.ReaderProvider) (sstable.Iterator, error) NewCompactionIter( bytesIterated *uint64, rp sstable.ReaderProvider, @@ -410,11 +451,38 @@ func (c *tableCacheShard) newIters( 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 // does it need to read from it after creation. 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 { c.unrefValue(v) return nil, nil, err @@ -454,11 +522,9 @@ func (c *tableCacheShard) newIters( if internalOpts.bytesIterated != nil { iter, err = ic.NewCompactionIter(internalOpts.bytesIterated, rp) } else { - iter, err = ic.NewIterWithBlockPropertyFiltersAndContext( - ctx, - opts.GetLowerBound(), opts.GetUpperBound(), - filterer, useFilter, internalOpts.stats, rp, - ) + iter, err = ic.NewIterWithBlockPropertyFiltersAndContextEtc( + ctx, opts.GetLowerBound(), opts.GetUpperBound(), filterer, hideObsoletePoints, useFilter, + internalOpts.stats, rp) } if err != nil { if rangeDelIter != nil { @@ -470,6 +536,35 @@ func (c *tableCacheShard) newIters( // NB: v.closeHook takes responsibility for calling unrefValue(v) here. Take // care to avoid introducing an allocation here by adding a closure. 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) dbOpts.iterCount.Add(1) @@ -482,7 +577,7 @@ func (c *tableCacheShard) newIters( } func (c *tableCacheShard) newRangeKeyIter( - file *manifest.FileMetadata, opts *keyspan.SpanIterOptions, dbOpts *tableCacheOpts, + file *manifest.FileMetadata, opts keyspan.SpanIterOptions, dbOpts *tableCacheOpts, ) (keyspan.FragmentIterator, error) { // Calling findNode gives us the responsibility of decrementing v's // 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 // done here, rather than deferring to the block-property collector in order // to maintain parity with point keys and the treatment of RANGEDELs. - if opts != nil && v.reader.Properties.NumRangeKeyDels == 0 { + if v.reader.Properties.NumRangeKeyDels == 0 { ok, _, err = c.checkAndIntersectFilters(v, nil, opts.RangeKeyFilters, nil) } if err != nil { @@ -539,6 +634,29 @@ func (c *tableCacheShard) newRangeKeyIter( 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 } @@ -986,9 +1104,16 @@ type loadInfo struct { func (v *tableCacheValue) load(loadInfo loadInfo, c *tableCacheShard, dbOpts *tableCacheOpts) { // Try opening the file first. 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}, ) + 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 { cacheOpts := private.SSTableCacheOpts(dbOpts.cacheID, loadInfo.backingFileNum).(sstable.ReaderOption) v.reader, v.err = sstable.NewReader(f, dbOpts.opts, cacheOpts, dbOpts.filterMetrics) diff --git a/vendor/github.com/cockroachdb/pebble/table_stats.go b/vendor/github.com/cockroachdb/pebble/table_stats.go index dadcabf..b96651b 100644 --- a/vendor/github.com/cockroachdb/pebble/table_stats.go +++ b/vendor/github.com/cockroachdb/pebble/table_stats.go @@ -126,7 +126,7 @@ func (d *DB) collectTableStats() bool { maybeCompact := false for _, c := range collected { c.fileMetadata.Stats = c.TableStats - maybeCompact = maybeCompact || c.TableStats.RangeDeletionsBytesEstimate > 0 + maybeCompact = maybeCompact || fileCompensation(c.fileMetadata) > 0 c.fileMetadata.StatsMarkValid() } d.mu.tableStats.cond.Broadcast() @@ -186,6 +186,11 @@ func (d *DB) loadNewFileStats( continue } + if nf.Meta.Virtual { + // cannot load virtual table stats + continue + } + stats, newHints, err := d.loadTableStats( rs.current, nf.Level, nf.Meta.PhysicalMeta(), @@ -217,6 +222,12 @@ func (d *DB) scanReadStateTableStats( for l, levelMetadata := range rs.current.Levels { iter := levelMetadata.Iter() 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 // active stats collection job updates f.Stats for active files, // and we ensure only one goroutine runs it at a time through @@ -225,6 +236,11 @@ func (d *DB) scanReadStateTableStats( if f.StatsValid() { 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 // 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 // a sstable property and call averageValueSizeBeneath for each of // 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 { return err } stats.PointDeletionsBytesEstimate = - pointDeletionsBytesEstimate(meta.Size, &r.Properties, avgValSize) + pointDeletionsBytesEstimate(meta.Size, &r.Properties, avgValLogicalSize, compressionRatio) return nil } @@ -420,51 +436,60 @@ func (d *DB) loadTableRangeDelStats( return compactionHints, err } -func (d *DB) averagePhysicalValueSizeBeneath( +func (d *DB) estimateSizesBeneath( 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, // summing their value sizes and entry counts. + file := meta.FileMetadata 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++ { overlaps := v.Overlaps(l, d.cmp, meta.Smallest.UserKey, meta.Largest.UserKey, meta.Largest.IsExclusiveSentinel()) iter := overlaps.Iter() - for file := iter.First(); file != nil; file = iter.Next() { + for file = iter.First(); file != nil; file = iter.Next() { var err error if file.Virtual { - err = d.tableCache.withVirtualReader( - 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 - }) + err = d.tableCache.withVirtualReader(file.VirtualMeta(), addVirtualTableStats) } else { - err = d.tableCache.withReader( - 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 - }) + err = d.tableCache.withReader(file.PhysicalMeta(), addPhysicalTableStats) } - if err != nil { - return 0, err + return 0, 0, err } } } if entryCount == 0 { - return 0, nil + return 0, 0, nil } - // RawKeySize and RawValueSize are uncompressed totals. Scale the value sum - // according to the data size to account for compression, index blocks and - // metadata overhead. Eg: + // RawKeySize and RawValueSize are uncompressed totals. We'll need to scale + // the value sum according to the data size to account for compression, + // index blocks and metadata overhead. Eg: // // Compression rate × Average uncompressed value size // @@ -473,10 +498,14 @@ func (d *DB) averagePhysicalValueSizeBeneath( // FileSize RawValueSize // ----------------------- × ------------ // 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) - compressionRatio := float64(fileSum) / uncompressedSum - avgCompressedValueSize := (float64(valSum) / float64(entryCount)) * compressionRatio - return uint64(avgCompressedValueSize), nil + compressionRatio = float64(fileSum) / uncompressedSum + avgValueLogicalSize = (float64(valSum) / float64(entryCount)) + return avgValueLogicalSize, compressionRatio, nil } func (d *DB) estimateReclaimedSizeBeneath( @@ -593,12 +622,12 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b return false } - // If a table is more than 10% point deletions, don't calculate the - // PointDeletionsBytesEstimate statistic using our limited knowledge. The - // table stats collector can populate the stats and calculate an average - // of value size of all the tables beneath the table in the LSM, which - // will be more accurate. - if props.NumDeletions > props.NumEntries/10 { + // If a table is more than 10% point deletions without user-provided size + // estimates, don't calculate the PointDeletionsBytesEstimate statistic + // using our limited knowledge. The table stats collector can populate the + // stats and calculate an average of value size of all the tables beneath + // the table in the LSM, which will be more accurate. + if unsizedDels := (props.NumDeletions - props.NumSizedDeletions); unsizedDels > props.NumEntries/10 { 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 // deletions in the file is low, the error introduced by this crude // estimate is expected to be small. - avgValSize := estimateValuePhysicalSize(meta.Size, props) - pointEstimate = pointDeletionsBytesEstimate(meta.Size, props, avgValSize) + avgValSize, compressionRatio := estimatePhysicalSizes(meta.Size, props) + pointEstimate = pointDeletionsBytesEstimate(meta.Size, props, avgValSize, compressionRatio) } meta.Stats.NumEntries = props.NumEntries @@ -623,7 +652,7 @@ func maybeSetStatsFromProperties(meta physicalMeta, props *sstable.Properties) b } func pointDeletionsBytesEstimate( - fileSize uint64, props *sstable.Properties, avgValPhysicalSize uint64, + fileSize uint64, props *sstable.Properties, avgValLogicalSize, compressionRatio float64, ) (estimate uint64) { if props.NumEntries == 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 // 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 // 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 - // estimate which tombstones cover keys and which do not. + // cover a key. + // + // 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 - // itself and 1x for the key that's expected to exist lower in the LSM. - var keysLogicalSize uint64 + // Below, we calculate the tombstone contributions and the shadowed keys' + // contributions separately. + var tombstonesLogicalSize float64 + var shadowedLogicalSize float64 + + // 1. Calculate the contribution of the tombstone keys themselves. if props.RawPointTombstoneKeySize > 0 { - // This table has a RawPointTombstoneKeySize property, so we know the - // exact size of the logical, uncompressed bytes of the point tombstone - // keys. - keysLogicalSize = 2 * props.RawPointTombstoneKeySize + tombstonesLogicalSize += float64(props.RawPointTombstoneKeySize) } else { // This sstable predates the existence of the RawPointTombstoneKeySize // property. We can use the average key size within the file itself and // 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. // // Physical FileSize // ----------- = ----------------------- // Logical RawKeySize+RawValueSize // - estimate += (keysLogicalSize * fileSize) / (props.RawKeySize + props.RawValueSize) - - // 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 + return uint64((tombstonesLogicalSize + shadowedLogicalSize) * compressionRatio) } -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 // the data size to account for compression, index blocks and metadata // overhead. Eg: @@ -689,9 +754,9 @@ func estimateValuePhysicalSize(fileSize uint64, props *sstable.Properties) (avgV // RawKeySize+RawValueSize NumEntries // uncompressedSum := props.RawKeySize + props.RawValueSize - compressionRatio := float64(fileSize) / float64(uncompressedSum) - avgCompressedValueSize := (float64(props.RawValueSize) / float64(props.NumEntries)) * compressionRatio - return uint64(avgCompressedValueSize) + compressionRatio = float64(fileSize) / float64(uncompressedSum) + avgValLogicalSize = (float64(props.RawValueSize) / float64(props.NumEntries)) + return avgValLogicalSize, compressionRatio } // newCombinedDeletionKeyspanIter returns a keyspan.FragmentIterator that diff --git a/vendor/github.com/cockroachdb/pebble/version_set.go b/vendor/github.com/cockroachdb/pebble/version_set.go index 41246ab..573f461 100644 --- a/vendor/github.com/cockroachdb/pebble/version_set.go +++ b/vendor/github.com/cockroachdb/pebble/version_set.go @@ -293,8 +293,8 @@ func (vs *versionSet) load( } vs.markFileNumUsed(vs.minUnflushedLogNum) - // Populate the fileBackingMap since we have finished version - // edit accumulation. + // Populate the fileBackingMap and the FileBacking for virtual sstables since + // we have finished version edit accumulation. for _, s := range bve.AddedFileBacking { vs.fileBackingMap[s.DiskFileNum] = s } @@ -490,6 +490,9 @@ func (vs *versionSet) logAndApply( defer vs.mu.Lock() 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( ve, currentVersion, vs.cmp, vs.opts.Comparer.FormatKey, vs.opts.FlushSplitBytes, vs.opts.Experimental.ReadCompactionRate, @@ -516,7 +519,7 @@ func (vs *versionSet) logAndApply( 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 // fraught. Instead we rely on the standard recovery mechanism run when a // database is open. In particular, that mechanism generates a new MANIFEST @@ -569,6 +572,14 @@ func (vs *versionSet) logAndApply( for fileNum, size := range zombies { 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. vs.append(newVersion) @@ -688,7 +699,6 @@ func (vs *versionSet) createManifest( Level: level, Meta: meta, }) - // TODO(bananabrick): Test snapshot changes. if _, ok := dedup[meta.FileBacking.DiskFileNum]; meta.Virtual && !ok { dedup[meta.FileBacking.DiskFileNum] = struct{}{} snapshot.CreatedBackingTables = append( diff --git a/vendor/github.com/cockroachdb/pebble/vfs/disk_full.go b/vendor/github.com/cockroachdb/pebble/vfs/disk_full.go index a5ef3e4..16f6a5d 100644 --- a/vendor/github.com/cockroachdb/pebble/vfs/disk_full.go +++ b/vendor/github.com/cockroachdb/pebble/vfs/disk_full.go @@ -228,6 +228,17 @@ func (fs *enospcFS) Open(name string, opts ...OpenOption) (File, error) { 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) { f, err := fs.inner.OpenDir(name) if f != nil { @@ -375,6 +386,20 @@ func (f *enospcFile) Write(p []byte) (n int, err error) { 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 { return f.inner.Prefetch(offset, length) } diff --git a/vendor/github.com/cockroachdb/pebble/vfs/disk_health.go b/vendor/github.com/cockroachdb/pebble/vfs/disk_health.go index 1c0b9c5..47e6787 100644 --- a/vendor/github.com/cockroachdb/pebble/vfs/disk_health.go +++ b/vendor/github.com/cockroachdb/pebble/vfs/disk_health.go @@ -219,6 +219,14 @@ func (d *diskHealthCheckingFile) Write(p []byte) (n int, err error) { 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. func (d *diskHealthCheckingFile) Close() error { d.stopTicker() @@ -680,6 +688,11 @@ func (d *diskHealthCheckingFS) Open(name string, opts ...OpenOption) (File, erro 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. func (d *diskHealthCheckingFS) OpenDir(name string) (File, error) { f, err := d.fs.OpenDir(name) diff --git a/vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go b/vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go index 3ba5584..820c081 100644 --- a/vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go +++ b/vendor/github.com/cockroachdb/pebble/vfs/logging_fs.go @@ -46,6 +46,15 @@ func (fs *loggingFS) Open(name string, opts ...OpenOption) (File, error) { 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 { fs.logFn("link: %s -> %s", 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) } +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 { f.logFn("prefetch(%d, %d): %s", offset, length, f.name) return f.File.Prefetch(offset, length) diff --git a/vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go b/vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go index 2b3a6d2..259b2bb 100644 --- a/vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go +++ b/vendor/github.com/cockroachdb/pebble/vfs/mem_fs.go @@ -208,6 +208,7 @@ func (y *MemFS) Create(fullname string) (File, error) { ret = &memFile{ n: n, fs: y, + read: 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 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { if final { @@ -275,9 +276,10 @@ func (y *MemFS) open(fullname string) (File, error) { } if n := dir.children[frag]; n != nil { ret = &memFile{ - n: n, - fs: y, - read: true, + n: n, + fs: y, + read: true, + write: openForWrite, } } } @@ -299,12 +301,22 @@ func (y *MemFS) open(fullname string) (File, error) { // Open implements FS.Open. 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. func (y *MemFS) OpenDir(fullname string) (File, error) { - return y.open(fullname) + return y.open(fullname, false /* openForWrite */) } // Remove implements FS.Remove. @@ -702,6 +714,29 @@ func (f *memFile) Write(p []byte) (int, error) { 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) Preallocate(offset, length int64) error { return nil } diff --git a/vendor/github.com/cockroachdb/pebble/vfs/vfs.go b/vendor/github.com/cockroachdb/pebble/vfs/vfs.go index 7eafbf6..3cc0b43 100644 --- a/vendor/github.com/cockroachdb/pebble/vfs/vfs.go +++ b/vendor/github.com/cockroachdb/pebble/vfs/vfs.go @@ -29,6 +29,8 @@ type File interface { // method *is* allowed to modify the slice passed in, whether temporarily // or permanently. Callers of Write() need to take this into account. io.Writer + // WriteAt() is only supported for files that were opened with FS.OpenReadWrite. + io.WriterAt // Preallocate optionally preallocates storage for `length` at `offset` // 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(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(name string) (File, error) @@ -232,6 +238,18 @@ func (defaultFS) Open(name string, opts ...OpenOption) (File, error) { 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 { return errors.WithStack(os.Remove(name)) } diff --git a/vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go b/vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go index 74fd9b8..387e254 100644 --- a/vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go +++ b/vendor/github.com/cockroachdb/redact/internal/buffer/buffer.go @@ -15,6 +15,7 @@ package buffer import ( + "bytes" origFmt "fmt" "unicode/utf8" "unsafe" @@ -197,11 +198,16 @@ func (b *Buffer) endRedactable() { if len(b.buf) == 0 { return } - p, ok := b.tryGrowByReslice(len(m.EndS)) - if !ok { - p = b.grow(len(m.EndS)) + if bytes.HasSuffix(b.buf, m.StartBytes) { + // Special case: remove a trailing open marker and call it a day. + 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 } @@ -214,18 +220,23 @@ func (b *Buffer) startWrite() { // endRedactable adds the closing redaction marker. func (b *Buffer) startRedactable() { - p, ok := b.tryGrowByReslice(len(m.StartS)) - if !ok { - p = b.grow(len(m.StartS)) + if bytes.HasSuffix(b.buf, m.EndBytes) { + // Special case: remove a trailing closing marker and call it a day. + 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 } // escapeToEnd escapes occurrences of redaction markers in // b.buf[b.validUntil:] and advances b.validUntil until the end. func (b *Buffer) escapeToEnd(breakNewLines bool) { - b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, breakNewLines) + b.buf = escape.InternalEscapeBytes(b.buf, b.validUntil, breakNewLines, false /* trim */) b.validUntil = len(b.buf) } @@ -313,6 +324,13 @@ func (b *Buffer) Grow(n int) { 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 // with ErrTooLarge. func makeSlice(n int) []byte { diff --git a/vendor/github.com/cockroachdb/redact/internal/escape/escape.go b/vendor/github.com/cockroachdb/redact/internal/escape/escape.go index cb4ba8a..1fc6706 100644 --- a/vendor/github.com/cockroachdb/redact/internal/escape/escape.go +++ b/vendor/github.com/cockroachdb/redact/internal/escape/escape.go @@ -28,6 +28,9 @@ import ( // If breakNewLines is set, a closing redaction marker // is placed before sequences of one or more newline characters, // and an open redaction marker is placed afterwards. +// +// If strip is set, final newlines and spaces are trimmed from the +// output. func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res []byte) { // Note: we use len(...RedactableS) and not len(...RedactableBytes) // because the ...S variant is a compile-time constant so this @@ -69,7 +72,12 @@ func InternalEscapeBytes(b []byte, startLoc int, breakNewLines, strip bool) (res copied = true } 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 // them all in a single call to doWrite, for performance. lastNewLine := i diff --git a/vendor/github.com/cockroachdb/redact/internal/markers/constants.go b/vendor/github.com/cockroachdb/redact/internal/markers/constants.go index 51f488f..f062f7e 100644 --- a/vendor/github.com/cockroachdb/redact/internal/markers/constants.go +++ b/vendor/github.com/cockroachdb/redact/internal/markers/constants.go @@ -20,8 +20,10 @@ import "regexp" const ( Start = '‹' StartS = string(Start) + StartLen = len(StartS) End = '›' EndS = string(End) + EndLen = len(EndS) EscapeMark = '?' EscapeMarkS = string(EscapeMark) RedactedS = StartS + "×" + EndS diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index efab55e..f710a34 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -16,6 +16,10 @@ This package provides various compression algorithms. # 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) * 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 diff --git a/vendor/github.com/klauspost/compress/SECURITY.md b/vendor/github.com/klauspost/compress/SECURITY.md new file mode 100644 index 0000000..23a4338 --- /dev/null +++ b/vendor/github.com/klauspost/compress/SECURITY.md @@ -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. diff --git a/vendor/github.com/klauspost/compress/huff0/bitwriter.go b/vendor/github.com/klauspost/compress/huff0/bitwriter.go index aed2347..b4d7164 100644 --- a/vendor/github.com/klauspost/compress/huff0/bitwriter.go +++ b/vendor/github.com/klauspost/compress/huff0/bitwriter.go @@ -13,14 +13,6 @@ type bitWriter struct { 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. // 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) { diff --git a/vendor/github.com/klauspost/compress/huff0/decompress.go b/vendor/github.com/klauspost/compress/huff0/decompress.go index 3c0b398..54bd08b 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress.go @@ -253,7 +253,7 @@ func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) { switch d.actualTableLog { case 8: - const shift = 8 - 8 + const shift = 0 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] diff --git a/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go b/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go index 05db94d..2aa6a95 100644 --- a/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go +++ b/vendor/github.com/klauspost/compress/internal/snapref/encode_other.go @@ -87,18 +87,6 @@ func emitCopy(dst []byte, offset, length int) int { 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 { return (u * 0x1e35a7bd) >> shift } diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index 65b38ab..bdd49c8 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -304,7 +304,7 @@ import "github.com/klauspost/compress/zstd" // Create a reader that caches decompressors. // 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, // so it will be allocated by the decoder. diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index 5f272d8..9f17ce6 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -592,7 +592,7 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) { } seq.fse.setRLE(symb) if debugDecoder { - printf("RLE set to %+v, code: %v", symb, v) + printf("RLE set to 0x%x, code: %v", symb, v) } case compModeFSE: println("Reading table for", tableIndex(i)) diff --git a/vendor/github.com/klauspost/compress/zstd/decoder_options.go b/vendor/github.com/klauspost/compress/zstd/decoder_options.go index 07a90dd..774c5f0 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder_options.go @@ -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. func WithDecoderDictRaw(id uint32, content []byte) DOption { return func(o *decoderOptions) error { diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 315b1a8..cbc626e 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -133,8 +133,7 @@ encodeLoop: if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { // Consider history as well. 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) // We might be able to match backwards. @@ -645,8 +644,7 @@ encodeLoop: if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { // Consider history as well. 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) diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index 50f7053..faaf819 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -129,7 +129,7 @@ func WithEncoderPadding(n int) EOption { } // No need to waste our time. if n == 1 { - o.pad = 0 + n = 0 } if n > 1<<30 { return fmt.Errorf("padding must less than 1GB (1<<30 bytes) ") diff --git a/vendor/github.com/klauspost/compress/zstd/framedec.go b/vendor/github.com/klauspost/compress/zstd/framedec.go index cc0aa22..53e160f 100644 --- a/vendor/github.com/klauspost/compress/zstd/framedec.go +++ b/vendor/github.com/klauspost/compress/zstd/framedec.go @@ -73,20 +73,20 @@ func (d *frameDec) reset(br byteBuffer) error { switch err { case io.EOF, io.ErrUnexpectedEOF: return io.EOF - default: - return err case nil: signature[0] = b[0] + default: + return err } // Read the rest, don't allow io.ErrUnexpectedEOF b, err = br.readSmall(3) switch err { case io.EOF: return io.EOF - default: - return err case nil: copy(signature[1:], b) + default: + return err } if string(signature[1:4]) != skippableFrameMagic || signature[0]&0xf0 != 0x50 { diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go new file mode 100644 index 0000000..f41932b --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go @@ -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 diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s new file mode 100644 index 0000000..9a7655c --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s @@ -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 diff --git a/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go new file mode 100644 index 0000000..57b9c31 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/matchlen_generic.go @@ -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 + +} diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go index 8939667..4be7cc7 100644 --- a/vendor/github.com/klauspost/compress/zstd/zstd.go +++ b/vendor/github.com/klauspost/compress/zstd/zstd.go @@ -9,7 +9,6 @@ import ( "errors" "log" "math" - "math/bits" ) // 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 { return binary.LittleEndian.Uint32(b[:len(b):len(b)][i:]) } diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index e358db6..b111d25 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := 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. # windows isn't included here because of the path separator being different. 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)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) +SANITIZED_DOCKER_IMAGE_TAG := $(subst +,-,$(DOCKER_IMAGE_TAG)) + ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows)) # Only supported on amd64 @@ -205,7 +207,7 @@ common-tarball: promu .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(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) \ --build-arg ARCH="$*" \ --build-arg OS="linux" \ @@ -214,19 +216,19 @@ $(BUILD_DOCKER_ARCHS): common-docker-%: .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(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))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(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-$*:$(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-$*:latest" + 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 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 push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(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):$(SANITIZED_DOCKER_IMAGE_TAG)" .PHONY: promu promu: $(PROMU) diff --git a/vendor/github.com/prometheus/procfs/fs.go b/vendor/github.com/prometheus/procfs/fs.go index 0102ab0..60c551e 100644 --- a/vendor/github.com/prometheus/procfs/fs.go +++ b/vendor/github.com/prometheus/procfs/fs.go @@ -21,6 +21,7 @@ import ( // kernel data structures. type FS struct { proc fs.FS + real bool } // DefaultMountPoint is the common mount point of the proc filesystem. @@ -39,5 +40,11 @@ func NewFS(mountPoint string) (FS, error) { if err != nil { return FS{}, err } - return FS{fs}, nil + + real, err := isRealProc(mountPoint) + if err != nil { + return FS{}, err + } + + return FS{fs, real}, nil } diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go new file mode 100644 index 0000000..8005769 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go @@ -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 +} diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_type.go b/vendor/github.com/prometheus/procfs/fs_statfs_type.go new file mode 100644 index 0000000..6233217 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/fs_statfs_type.go @@ -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 +} diff --git a/vendor/github.com/prometheus/procfs/internal/util/parse.go b/vendor/github.com/prometheus/procfs/internal/util/parse.go index b030951..14272dc 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/parse.go +++ b/vendor/github.com/prometheus/procfs/internal/util/parse.go @@ -64,6 +64,21 @@ func ParsePInt64s(ss []string) ([]*int64, error) { 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. func ReadUintFromFile(path string) (uint64, error) { data, err := os.ReadFile(path) diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go index 0c482c1..7f68890 100644 --- a/vendor/github.com/prometheus/procfs/mountstats.go +++ b/vendor/github.com/prometheus/procfs/mountstats.go @@ -186,6 +186,8 @@ type NFSOperationStats struct { CumulativeTotalResponseMilliseconds uint64 // Duration from when a request was enqueued to when it was completely handled. 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. Errors uint64 } @@ -534,7 +536,6 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) { ns = append(ns, n) } - opStats := NFSOperationStats{ Operation: strings.TrimSuffix(ss[0], ":"), Requests: ns[0], @@ -546,6 +547,9 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) { CumulativeTotalResponseMilliseconds: ns[6], CumulativeTotalRequestMilliseconds: ns[7], } + if ns[0] != 0 { + opStats.AverageRTTMilliseconds = float64(ns[6]) / float64(ns[0]) + } if len(ns) > 8 { opStats.Errors = ns[8] diff --git a/vendor/github.com/prometheus/procfs/net_conntrackstat.go b/vendor/github.com/prometheus/procfs/net_conntrackstat.go index 8300dac..64a0e94 100644 --- a/vendor/github.com/prometheus/procfs/net_conntrackstat.go +++ b/vendor/github.com/prometheus/procfs/net_conntrackstat.go @@ -18,7 +18,6 @@ import ( "bytes" "fmt" "io" - "strconv" "strings" "github.com/prometheus/procfs/internal/util" @@ -28,9 +27,13 @@ import ( // and contains netfilter conntrack statistics at one CPU core. type ConntrackStatEntry struct { Entries uint64 + Searched uint64 Found uint64 + New uint64 Invalid uint64 Ignore uint64 + Delete uint64 + DeleteList uint64 Insert uint64 InsertFailed uint64 Drop uint64 @@ -81,73 +84,34 @@ func parseConntrackStat(r io.Reader) ([]ConntrackStatEntry, error) { // Parses a ConntrackStatEntry from given array of fields. func parseConntrackStatEntry(fields []string) (*ConntrackStatEntry, error) { - if len(fields) != 17 { - return nil, fmt.Errorf("invalid conntrackstat entry, missing fields") - } - entry := &ConntrackStatEntry{} - - entries, err := parseConntrackStatField(fields[0]) + entries, err := util.ParseHexUint64s(fields) if err != nil { - return nil, err + return nil, fmt.Errorf("invalid conntrackstat entry, couldn't parse fields: %s", err) } - entry.Entries = entries - - found, err := parseConntrackStatField(fields[2]) - if err != nil { - return nil, err + numEntries := len(entries) + if numEntries < 16 || numEntries > 17 { + return nil, fmt.Errorf("invalid conntrackstat entry, invalid number of fields: %d", numEntries) } - entry.Found = found - invalid, err := parseConntrackStatField(fields[4]) - if err != nil { - return nil, err + stats := &ConntrackStatEntry{ + Entries: *entries[0], + 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]) - if err != nil { - return nil, err + // Ignore missing search_restart on Linux < 2.6.35. + if numEntries == 17 { + stats.SearchRestart = *entries[16] } - entry.Ignore = ignore - insert, err := parseConntrackStatField(fields[8]) - 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 + return stats, nil } diff --git a/vendor/github.com/prometheus/procfs/net_softnet.go b/vendor/github.com/prometheus/procfs/net_softnet.go index 06b7b8f..540cea5 100644 --- a/vendor/github.com/prometheus/procfs/net_softnet.go +++ b/vendor/github.com/prometheus/procfs/net_softnet.go @@ -76,6 +76,7 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) { s := bufio.NewScanner(r) var stats []SoftnetStat + cpuIndex := 0 for s.Scan() { columns := strings.Fields(s.Text()) width := len(columns) @@ -127,9 +128,13 @@ func parseSoftnet(r io.Reader) ([]SoftnetStat, error) { softnetStat.SoftnetBacklogLen = us[0] softnetStat.Index = us[1] + } else { + // For older kernels, create the Index based on the scan line number. + softnetStat.Index = uint32(cpuIndex) } softnetStat.Width = width stats = append(stats, softnetStat) + cpuIndex++ } return stats, nil diff --git a/vendor/github.com/prometheus/procfs/net_wireless.go b/vendor/github.com/prometheus/procfs/net_wireless.go new file mode 100644 index 0000000..c80fb15 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/net_wireless.go @@ -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 +} diff --git a/vendor/github.com/prometheus/procfs/netstat.go b/vendor/github.com/prometheus/procfs/netstat.go index 5cc40ae..742dff4 100644 --- a/vendor/github.com/prometheus/procfs/netstat.go +++ b/vendor/github.com/prometheus/procfs/netstat.go @@ -15,7 +15,6 @@ package procfs import ( "bufio" - "io" "os" "path/filepath" "strconv" @@ -38,12 +37,7 @@ func (fs FS) NetStat() ([]NetStat, error) { var netStatsTotal []NetStat for _, filePath := range statFiles { - file, err := os.Open(filePath) - if err != nil { - return nil, err - } - - procNetstat, err := parseNetstat(file) + procNetstat, err := parseNetstat(filePath) if err != nil { return nil, err } @@ -56,14 +50,17 @@ func (fs FS) NetStat() ([]NetStat, error) { // parseNetstat parses the metrics from `/proc/net/stat/` file // and returns a NetStat structure. -func parseNetstat(r io.Reader) (NetStat, error) { - var ( - scanner = bufio.NewScanner(r) - netStat = NetStat{ - Stats: make(map[string][]uint64), - } - ) +func parseNetstat(filePath string) (NetStat, error) { + 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() // First string is always a header for stats diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go index c30223a..48f39da 100644 --- a/vendor/github.com/prometheus/procfs/proc.go +++ b/vendor/github.com/prometheus/procfs/proc.go @@ -21,7 +21,6 @@ import ( "strconv" "strings" - "github.com/prometheus/procfs/internal/fs" "github.com/prometheus/procfs/internal/util" ) @@ -30,7 +29,7 @@ type Proc struct { // The process ID. PID int - fs fs.FS + fs FS } // 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 { 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. @@ -114,7 +113,7 @@ func (fs FS) AllProcs() (Procs, error) { if err != nil { continue } - p = append(p, Proc{PID: int(pid), fs: fs.proc}) + p = append(p, Proc{PID: int(pid), fs: fs}) } return p, nil @@ -237,6 +236,19 @@ func (p Proc) FileDescriptorTargets() ([]string, error) { // FileDescriptorsLen returns the number of currently open file descriptors of // a process. 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() if err != nil { return 0, err @@ -285,7 +297,7 @@ func (p Proc) fileDescriptors() ([]string, error) { } 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 diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go index b278eb2..14b249f 100644 --- a/vendor/github.com/prometheus/procfs/proc_stat.go +++ b/vendor/github.com/prometheus/procfs/proc_stat.go @@ -18,7 +18,6 @@ import ( "fmt" "os" - "github.com/prometheus/procfs/internal/fs" "github.com/prometheus/procfs/internal/util" ) @@ -112,7 +111,7 @@ type ProcStat struct { // Aggregated block I/O delays, measured in clock ticks (centiseconds). DelayAcctBlkIOTicks uint64 - proc fs.FS + proc FS } // 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. func (s ProcStat) StartTime() (float64, error) { - fs := FS{proc: s.proc} - stat, err := fs.Stat() + stat, err := s.proc.Stat() if err != nil { return 0, err } diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go index 3d8c064..c055d07 100644 --- a/vendor/github.com/prometheus/procfs/proc_status.go +++ b/vendor/github.com/prometheus/procfs/proc_status.go @@ -15,6 +15,7 @@ package procfs import ( "bytes" + "sort" "strconv" "strings" @@ -76,6 +77,9 @@ type ProcStatus struct { UIDs [4]string // GIDs of the process (Real, effective, saved set, and filesystem GIDs) 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. @@ -161,10 +165,38 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt s.VoluntaryCtxtSwitches = vUint case "nonvoluntary_ctxt_switches": s.NonVoluntaryCtxtSwitches = vUint + case "Cpus_allowed_list": + s.CpusAllowedList = calcCpusAllowedList(vString) } + } // TotalCtxtSwitches returns the total context switch. func (s ProcStatus) TotalCtxtSwitches() uint64 { 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 +} diff --git a/vendor/github.com/prometheus/procfs/thread.go b/vendor/github.com/prometheus/procfs/thread.go index f08bfc7..490c147 100644 --- a/vendor/github.com/prometheus/procfs/thread.go +++ b/vendor/github.com/prometheus/procfs/thread.go @@ -54,7 +54,8 @@ func (fs FS) AllThreads(pid int) (Procs, error) { if err != nil { 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 @@ -66,13 +67,13 @@ func (fs FS) Thread(pid, tid int) (Proc, error) { if _, err := os.Stat(taskPath); err != nil { 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. func (proc Proc) Thread(tid int) (Proc, error) { - tfs := fsi.FS(proc.path("task")) - if _, err := os.Stat(tfs.Path(strconv.Itoa(tid))); err != nil { + tfs := FS{fsi.FS(proc.path("task")), proc.fs.real} + if _, err := os.Stat(tfs.proc.Path(strconv.Itoa(tid))); err != nil { return Proc{}, err } return Proc{PID: tid, fs: tfs}, nil diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go index 72e8e3a..074fd4b 100644 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -4,6 +4,7 @@ import ( "bufio" "io" "runtime" + "strings" ) // 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) } +// Writer returns an io.Writer that writes to the logger at the info log level func (entry *Entry) Writer() *io.PipeWriter { 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 { reader, writer := io.Pipe() var printFunc func(args ...interface{}) + // Determine which log function to use based on the specified log level switch level { case TraceLevel: printFunc = entry.Trace @@ -48,23 +52,51 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { 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) + + // Set a finalizer function to close the writer when it is garbage collected runtime.SetFinalizer(writer, writerFinalizer) 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{})) { 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 { entry.Errorf("Error while reading from Writer: %s", err) } + + // Close the reader when we are done reader.Close() } +// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected func writerFinalizer(writer *io.PipeWriter) { writer.Close() } diff --git a/vendor/golang.org/x/sys/unix/mkall.sh b/vendor/golang.org/x/sys/unix/mkall.sh index 8e3947c..e6f31d3 100644 --- a/vendor/golang.org/x/sys/unix/mkall.sh +++ b/vendor/golang.org/x/sys/unix/mkall.sh @@ -50,7 +50,7 @@ if [[ "$GOOS" = "linux" ]]; then # Use the Docker-based build system # Files generated through docker (use $cmd so you can Ctl-C the build or run) $cmd docker build --tag generate:$GOOS $GOOS - $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && /bin/pwd):/build generate:$GOOS + $cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS exit fi diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index be0423e..3156462 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -741,7 +741,8 @@ main(void) e = errors[i].num; if(i > 0 && errors[i-1].num == e) continue; - strcpy(buf, strerror(e)); + strncpy(buf, strerror(e), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; // lowercase first letter: Bad -> bad, but STREAM -> STREAM. if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) buf[0] += a - A; @@ -760,7 +761,8 @@ main(void) e = signals[i].num; if(i > 0 && signals[i-1].num == e) continue; - strcpy(buf, strsignal(e)); + strncpy(buf, strsignal(e), sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; // lowercase first letter: Bad -> bad, but STREAM -> STREAM. if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z) buf[0] += a - A; diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index fbaeb5f..6de486b 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -1699,12 +1699,23 @@ func PtracePokeUser(pid int, addr uintptr, data []byte) (count int, err error) { return ptracePoke(PTRACE_POKEUSR, PTRACE_PEEKUSR, pid, addr, data) } +// elfNT_PRSTATUS is a copy of the debug/elf.NT_PRSTATUS constant so +// x/sys/unix doesn't need to depend on debug/elf and thus +// compress/zlib, debug/dwarf, and other packages. +const elfNT_PRSTATUS = 1 + func PtraceGetRegs(pid int, regsout *PtraceRegs) (err error) { - return ptracePtr(PTRACE_GETREGS, pid, 0, unsafe.Pointer(regsout)) + var iov Iovec + iov.Base = (*byte)(unsafe.Pointer(regsout)) + iov.SetLen(int(unsafe.Sizeof(*regsout))) + return ptracePtr(PTRACE_GETREGSET, pid, uintptr(elfNT_PRSTATUS), unsafe.Pointer(&iov)) } func PtraceSetRegs(pid int, regs *PtraceRegs) (err error) { - return ptracePtr(PTRACE_SETREGS, pid, 0, unsafe.Pointer(regs)) + var iov Iovec + iov.Base = (*byte)(unsafe.Pointer(regs)) + iov.SetLen(int(unsafe.Sizeof(*regs))) + return ptracePtr(PTRACE_SETREGSET, pid, uintptr(elfNT_PRSTATUS), unsafe.Pointer(&iov)) } func PtraceSetOptions(pid int, options int) (err error) { @@ -2420,6 +2431,21 @@ func PthreadSigmask(how int, set, oldset *Sigset_t) error { return rtSigprocmask(how, set, oldset, _C__NSIG/8) } +//sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) +//sysnb getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) + +func Getresuid() (ruid, euid, suid int) { + var r, e, s _C_int + getresuid(&r, &e, &s) + return int(r), int(e), int(s) +} + +func Getresgid() (rgid, egid, sgid int) { + var r, e, s _C_int + getresgid(&r, &e, &s) + return int(r), int(e), int(s) +} + /* * Unimplemented */ diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index f9c7a96..c5f166a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -151,6 +151,21 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { return } +//sysnb getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) +//sysnb getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) + +func Getresuid() (ruid, euid, suid int) { + var r, e, s _C_int + getresuid(&r, &e, &s) + return int(r), int(e), int(s) +} + +func Getresgid() (rgid, egid, sgid int) { + var r, e, s _C_int + getresgid(&r, &e, &s) + return int(r), int(e), int(s) +} + //sys ioctl(fd int, req uint, arg uintptr) (err error) //sys ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_IOCTL @@ -338,8 +353,6 @@ func Uname(uname *Utsname) error { // getgid // getitimer // getlogin -// getresgid -// getresuid // getthrid // ktrace // lfs_bmapv diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index f619252..4898420 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -329,6 +329,54 @@ const ( SCM_WIFI_STATUS = 0x25 SFD_CLOEXEC = 0x400000 SFD_NONBLOCK = 0x4000 + SF_FP = 0x38 + SF_I0 = 0x20 + SF_I1 = 0x24 + SF_I2 = 0x28 + SF_I3 = 0x2c + SF_I4 = 0x30 + SF_I5 = 0x34 + SF_L0 = 0x0 + SF_L1 = 0x4 + SF_L2 = 0x8 + SF_L3 = 0xc + SF_L4 = 0x10 + SF_L5 = 0x14 + SF_L6 = 0x18 + SF_L7 = 0x1c + SF_PC = 0x3c + SF_RETP = 0x40 + SF_V9_FP = 0x70 + SF_V9_I0 = 0x40 + SF_V9_I1 = 0x48 + SF_V9_I2 = 0x50 + SF_V9_I3 = 0x58 + SF_V9_I4 = 0x60 + SF_V9_I5 = 0x68 + SF_V9_L0 = 0x0 + SF_V9_L1 = 0x8 + SF_V9_L2 = 0x10 + SF_V9_L3 = 0x18 + SF_V9_L4 = 0x20 + SF_V9_L5 = 0x28 + SF_V9_L6 = 0x30 + SF_V9_L7 = 0x38 + SF_V9_PC = 0x78 + SF_V9_RETP = 0x80 + SF_V9_XARG0 = 0x88 + SF_V9_XARG1 = 0x90 + SF_V9_XARG2 = 0x98 + SF_V9_XARG3 = 0xa0 + SF_V9_XARG4 = 0xa8 + SF_V9_XARG5 = 0xb0 + SF_V9_XXARG = 0xb8 + SF_XARG0 = 0x44 + SF_XARG1 = 0x48 + SF_XARG2 = 0x4c + SF_XARG3 = 0x50 + SF_XARG4 = 0x54 + SF_XARG5 = 0x58 + SF_XXARG = 0x5c SIOCATMARK = 0x8905 SIOCGPGRP = 0x8904 SIOCGSTAMPNS_NEW = 0x40108907 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index da63d9d..722c29a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -2172,3 +2172,17 @@ func rtSigprocmask(how int, set *Sigset_t, oldset *Sigset_t, sigsetsize uintptr) } return } + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + RawSyscallNoError(SYS_GETRESUID, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + RawSyscallNoError(SYS_GETRESGID, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 6699a78..9ab9abf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 04f0de3..3dcacd3 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $4 DATA ·libc_getcwd_trampoline_addr(SB)/4, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresuid_trampoline_addr(SB)/4, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresgid_trampoline_addr(SB)/4, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index 1e775fe..915761e 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -519,15 +519,29 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func ioctl(fd int, req uint, arg uintptr) (err error) { - _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) - if e1 != 0 { - err = errnoErr(e1) - } +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) return } -func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { err = errnoErr(e1) @@ -541,6 +555,16 @@ var libc_ioctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) { + _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { var _p0 unsafe.Pointer if len(mib) > 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 27b6f4d..2763620 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index 7f64278..8e87fdf 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index b797045..c922314 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $4 DATA ·libc_getcwd_trampoline_addr(SB)/4, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresuid_trampoline_addr(SB)/4, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $4 +DATA ·libc_getresgid_trampoline_addr(SB)/4, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 756ef7b..12a7a21 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index a871266..a6bc32c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index 7bc2e24..b19e8aa 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index 05d4bff..b4e7bce 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index 739be62..fb99594 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index 74a25f8..ca3f766 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -189,6 +189,18 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getresuid(SB) + RET +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_getresgid(SB) + RET +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ioctl(SB) RET diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index 7d95a19..32cbbbc 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -519,6 +519,28 @@ var libc_getcwd_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getresuid(ruid *_C_int, euid *_C_int, suid *_C_int) { + syscall_rawSyscall(libc_getresuid_trampoline_addr, uintptr(unsafe.Pointer(ruid)), uintptr(unsafe.Pointer(euid)), uintptr(unsafe.Pointer(suid))) + return +} + +var libc_getresuid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresuid getresuid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getresgid(rgid *_C_int, egid *_C_int, sgid *_C_int) { + syscall_rawSyscall(libc_getresgid_trampoline_addr, uintptr(unsafe.Pointer(rgid)), uintptr(unsafe.Pointer(egid)), uintptr(unsafe.Pointer(sgid))) + return +} + +var libc_getresgid_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_getresgid getresgid "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := syscall_syscall(libc_ioctl_trampoline_addr, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 990be24..477a7d5 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -158,6 +158,16 @@ TEXT libc_getcwd_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_getcwd_trampoline_addr(SB), RODATA, $8 DATA ·libc_getcwd_trampoline_addr(SB)/8, $libc_getcwd_trampoline<>(SB) +TEXT libc_getresuid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresuid(SB) +GLOBL ·libc_getresuid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresuid_trampoline_addr(SB)/8, $libc_getresuid_trampoline<>(SB) + +TEXT libc_getresgid_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_getresgid(SB) +GLOBL ·libc_getresgid_trampoline_addr(SB), RODATA, $8 +DATA ·libc_getresgid_trampoline_addr(SB)/8, $libc_getresgid_trampoline<>(SB) + TEXT libc_ioctl_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ioctl(SB) GLOBL ·libc_ioctl_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index ca84727..00c3b8c 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -2555,6 +2555,11 @@ const ( BPF_REG_8 = 0x8 BPF_REG_9 = 0x9 BPF_REG_10 = 0xa + BPF_CGROUP_ITER_ORDER_UNSPEC = 0x0 + BPF_CGROUP_ITER_SELF_ONLY = 0x1 + BPF_CGROUP_ITER_DESCENDANTS_PRE = 0x2 + BPF_CGROUP_ITER_DESCENDANTS_POST = 0x3 + BPF_CGROUP_ITER_ANCESTORS_UP = 0x4 BPF_MAP_CREATE = 0x0 BPF_MAP_LOOKUP_ELEM = 0x1 BPF_MAP_UPDATE_ELEM = 0x2 @@ -2566,6 +2571,7 @@ const ( BPF_PROG_ATTACH = 0x8 BPF_PROG_DETACH = 0x9 BPF_PROG_TEST_RUN = 0xa + BPF_PROG_RUN = 0xa BPF_PROG_GET_NEXT_ID = 0xb BPF_MAP_GET_NEXT_ID = 0xc BPF_PROG_GET_FD_BY_ID = 0xd @@ -2610,6 +2616,7 @@ const ( BPF_MAP_TYPE_CPUMAP = 0x10 BPF_MAP_TYPE_XSKMAP = 0x11 BPF_MAP_TYPE_SOCKHASH = 0x12 + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED = 0x13 BPF_MAP_TYPE_CGROUP_STORAGE = 0x13 BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 0x14 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 0x15 @@ -2620,6 +2627,10 @@ const ( BPF_MAP_TYPE_STRUCT_OPS = 0x1a BPF_MAP_TYPE_RINGBUF = 0x1b BPF_MAP_TYPE_INODE_STORAGE = 0x1c + BPF_MAP_TYPE_TASK_STORAGE = 0x1d + BPF_MAP_TYPE_BLOOM_FILTER = 0x1e + BPF_MAP_TYPE_USER_RINGBUF = 0x1f + BPF_MAP_TYPE_CGRP_STORAGE = 0x20 BPF_PROG_TYPE_UNSPEC = 0x0 BPF_PROG_TYPE_SOCKET_FILTER = 0x1 BPF_PROG_TYPE_KPROBE = 0x2 @@ -2651,6 +2662,7 @@ const ( BPF_PROG_TYPE_EXT = 0x1c BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e + BPF_PROG_TYPE_SYSCALL = 0x1f BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2689,6 +2701,12 @@ const ( BPF_XDP_CPUMAP = 0x23 BPF_SK_LOOKUP = 0x24 BPF_XDP = 0x25 + BPF_SK_SKB_VERDICT = 0x26 + BPF_SK_REUSEPORT_SELECT = 0x27 + BPF_SK_REUSEPORT_SELECT_OR_MIGRATE = 0x28 + BPF_PERF_EVENT = 0x29 + BPF_TRACE_KPROBE_MULTI = 0x2a + BPF_LSM_CGROUP = 0x2b BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2696,6 +2714,9 @@ const ( BPF_LINK_TYPE_ITER = 0x4 BPF_LINK_TYPE_NETNS = 0x5 BPF_LINK_TYPE_XDP = 0x6 + BPF_LINK_TYPE_PERF_EVENT = 0x7 + BPF_LINK_TYPE_KPROBE_MULTI = 0x8 + BPF_LINK_TYPE_STRUCT_OPS = 0x9 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2733,6 +2754,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff BPF_F_CTXLEN_MASK = 0xfffff00000000 @@ -2747,6 +2769,7 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 0x8 BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 + BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2771,10 +2794,16 @@ const ( BPF_LWT_ENCAP_SEG6 = 0x0 BPF_LWT_ENCAP_SEG6_INLINE = 0x1 BPF_LWT_ENCAP_IP = 0x2 + BPF_F_BPRM_SECUREEXEC = 0x1 + BPF_F_BROADCAST = 0x8 + BPF_F_EXCLUDE_INGRESS = 0x10 + BPF_SKB_TSTAMP_UNSPEC = 0x0 + BPF_SKB_TSTAMP_DELIVERY_MONO = 0x1 BPF_OK = 0x0 BPF_DROP = 0x2 BPF_REDIRECT = 0x7 BPF_LWT_REROUTE = 0x80 + BPF_FLOW_DISSECTOR_CONTINUE = 0x81 BPF_SOCK_OPS_RTO_CB_FLAG = 0x1 BPF_SOCK_OPS_RETRANS_CB_FLAG = 0x2 BPF_SOCK_OPS_STATE_CB_FLAG = 0x4 @@ -2838,6 +2867,10 @@ const ( BPF_FIB_LKUP_RET_UNSUPP_LWT = 0x6 BPF_FIB_LKUP_RET_NO_NEIGH = 0x7 BPF_FIB_LKUP_RET_FRAG_NEEDED = 0x8 + BPF_MTU_CHK_SEGS = 0x1 + BPF_MTU_CHK_RET_SUCCESS = 0x0 + BPF_MTU_CHK_RET_FRAG_NEEDED = 0x1 + BPF_MTU_CHK_RET_SEGS_TOOBIG = 0x2 BPF_FD_TYPE_RAW_TRACEPOINT = 0x0 BPF_FD_TYPE_TRACEPOINT = 0x1 BPF_FD_TYPE_KPROBE = 0x2 @@ -2847,6 +2880,19 @@ const ( BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 0x1 BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 0x2 BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 0x4 + BPF_CORE_FIELD_BYTE_OFFSET = 0x0 + BPF_CORE_FIELD_BYTE_SIZE = 0x1 + BPF_CORE_FIELD_EXISTS = 0x2 + BPF_CORE_FIELD_SIGNED = 0x3 + BPF_CORE_FIELD_LSHIFT_U64 = 0x4 + BPF_CORE_FIELD_RSHIFT_U64 = 0x5 + BPF_CORE_TYPE_ID_LOCAL = 0x6 + BPF_CORE_TYPE_ID_TARGET = 0x7 + BPF_CORE_TYPE_EXISTS = 0x8 + BPF_CORE_TYPE_SIZE = 0x9 + BPF_CORE_ENUMVAL_EXISTS = 0xa + BPF_CORE_ENUMVAL_VALUE = 0xb + BPF_CORE_TYPE_MATCHES = 0xc ) const ( diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index 3723b2c..9645900 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -405,7 +405,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) = version.VerQueryValueW // Process Status API (PSAPI) -//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses +//sys enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses //sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules //sys EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *uint32, filterFlag uint32) (err error) = psapi.EnumProcessModulesEx //sys GetModuleInformation(process Handle, module Handle, modinfo *ModuleInfo, cb uint32) (err error) = psapi.GetModuleInformation @@ -1354,6 +1354,17 @@ func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return syscall.EWINDOWS } +func EnumProcesses(processIds []uint32, bytesReturned *uint32) error { + // EnumProcesses syscall expects the size parameter to be in bytes, but the code generated with mksyscall uses + // the length of the processIds slice instead. Hence, this wrapper function is added to fix the discrepancy. + var p *uint32 + if len(processIds) > 0 { + p = &processIds[0] + } + size := uint32(len(processIds) * 4) + return enumProcesses(p, size, bytesReturned) +} + func Getpid() (pid int) { return int(GetCurrentProcessId()) } func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index a81ea2c..566dd3e 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -3516,12 +3516,8 @@ func EnumProcessModulesEx(process Handle, module *Handle, cb uint32, cbNeeded *u return } -func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) { - var _p0 *uint32 - if len(processIds) > 0 { - _p0 = &processIds[0] - } - r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(_p0)), uintptr(len(processIds)), uintptr(unsafe.Pointer(bytesReturned))) +func enumProcesses(processIds *uint32, nSize uint32, bytesReturned *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(processIds)), uintptr(nSize), uintptr(unsafe.Pointer(bytesReturned))) if r1 == 0 { err = errnoErr(e1) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 3764869..5054dc8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,8 +7,8 @@ github.com/beorn7/perks/quantile # github.com/cespare/xxhash/v2 v2.2.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/cockroachdb/errors v1.9.1 -## explicit; go 1.13 +# github.com/cockroachdb/errors v1.10.0 +## explicit; go 1.17 github.com/cockroachdb/errors github.com/cockroachdb/errors/assert github.com/cockroachdb/errors/barriers @@ -30,7 +30,7 @@ github.com/cockroachdb/errors/withstack # github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b ## explicit; go 1.16 github.com/cockroachdb/logtags -# github.com/cockroachdb/pebble v0.0.0-20230517202617-f6eaf9a696e6 +# github.com/cockroachdb/pebble v0.0.0-20230613192303-a8a7ebf58839 ## explicit; go 1.19 github.com/cockroachdb/pebble github.com/cockroachdb/pebble/internal/arenaskl @@ -55,13 +55,14 @@ github.com/cockroachdb/pebble/internal/testkeys github.com/cockroachdb/pebble/objstorage github.com/cockroachdb/pebble/objstorage/objstorageprovider github.com/cockroachdb/pebble/objstorage/objstorageprovider/objiotracing +github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedobjcat github.com/cockroachdb/pebble/objstorage/shared github.com/cockroachdb/pebble/record github.com/cockroachdb/pebble/sstable github.com/cockroachdb/pebble/vfs github.com/cockroachdb/pebble/vfs/atomicfs -# github.com/cockroachdb/redact v1.1.4 +# github.com/cockroachdb/redact v1.1.5 ## explicit; go 1.14 github.com/cockroachdb/redact github.com/cockroachdb/redact/builder @@ -109,7 +110,7 @@ github.com/golang/snappy # github.com/hjson/hjson-go v3.3.0+incompatible ## explicit github.com/hjson/hjson-go -# github.com/klauspost/compress v1.16.5 +# github.com/klauspost/compress v1.16.6 ## explicit; go 1.18 github.com/klauspost/compress github.com/klauspost/compress/fse @@ -137,32 +138,32 @@ github.com/prometheus/client_golang/prometheus/internal # github.com/prometheus/client_model v0.4.0 ## explicit; go 1.18 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.43.0 +# github.com/prometheus/common v0.44.0 ## explicit; go 1.18 github.com/prometheus/common/expfmt github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg github.com/prometheus/common/model -# github.com/prometheus/procfs v0.9.0 -## explicit; go 1.18 +# github.com/prometheus/procfs v0.10.1 +## explicit; go 1.19 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util # github.com/rogpeppe/go-internal v1.10.0 ## explicit; go 1.19 github.com/rogpeppe/go-internal/fmtsort -# github.com/sirupsen/logrus v1.9.2 +# github.com/sirupsen/logrus v1.9.3 ## explicit; go 1.13 github.com/sirupsen/logrus -# golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc +# golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 ## explicit; go 1.20 golang.org/x/exp/rand -# golang.org/x/sys v0.8.0 +# golang.org/x/sys v0.9.0 ## explicit; go 1.17 golang.org/x/sys/execabs golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/text v0.9.0 +# golang.org/x/text v0.10.0 ## explicit; go 1.17 golang.org/x/text/cases golang.org/x/text/internal