0

Upgrade versions of vendored libs

This commit is contained in:
eleksir
2024-08-03 21:26:21 +03:00
parent 9bac4fb257
commit b6f8cf9155
812 changed files with 56387 additions and 31548 deletions

View File

@@ -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)
}

View File

@@ -13,7 +13,6 @@ import (
"sync/atomic"
"time"
"github.com/cockroachdb/pebble/internal/invariants"
"github.com/cockroachdb/redact"
)
@@ -135,7 +134,7 @@ type diskHealthCheckingFile struct {
//
// NB: this packing scheme is not persisted, and is therefore safe to adjust
// across process boundaries.
lastWritePacked uint64
lastWritePacked atomic.Uint64
createTime time.Time
}
@@ -174,7 +173,7 @@ func (d *diskHealthCheckingFile) startTicker() {
return
case <-ticker.C:
packed := atomic.LoadUint64(&d.lastWritePacked)
packed := d.lastWritePacked.Load()
if packed == 0 {
continue
}
@@ -219,6 +218,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()
@@ -279,20 +286,12 @@ func (d *diskHealthCheckingFile) timeDiskOp(opType OpType, writeSizeInBytes int6
delta := time.Since(d.createTime)
packed := pack(delta, writeSizeInBytes, opType)
if invariants.Enabled {
if !atomic.CompareAndSwapUint64(&d.lastWritePacked, 0, packed) {
panic("concurrent write operations detected on file")
}
} else {
atomic.StoreUint64(&d.lastWritePacked, packed)
if d.lastWritePacked.Swap(packed) != 0 {
panic("concurrent write operations detected on file")
}
defer func() {
if invariants.Enabled {
if !atomic.CompareAndSwapUint64(&d.lastWritePacked, packed, 0) {
panic("concurrent write operations detected on file")
}
} else {
atomic.StoreUint64(&d.lastWritePacked, 0)
if d.lastWritePacked.Swap(0) != packed {
panic("concurrent write operations detected on file")
}
}()
op()
@@ -375,9 +374,17 @@ func (i DiskSlowInfo) String() string {
// SafeFormat implements redact.SafeFormatter.
func (i DiskSlowInfo) SafeFormat(w redact.SafePrinter, _ rune) {
w.Printf("disk slowness detected: %s on file %s (%d bytes) has been ongoing for %0.1fs",
redact.Safe(i.OpType.String()), redact.Safe(filepath.Base(i.Path)),
redact.Safe(i.WriteSize), redact.Safe(i.Duration.Seconds()))
switch i.OpType {
// Operations for which i.WriteSize is meaningful.
case OpTypeWrite, OpTypeSyncTo, OpTypePreallocate:
w.Printf("disk slowness detected: %s on file %s (%d bytes) has been ongoing for %0.1fs",
redact.Safe(i.OpType.String()), redact.Safe(filepath.Base(i.Path)),
redact.Safe(i.WriteSize), redact.Safe(i.Duration.Seconds()))
default:
w.Printf("disk slowness detected: %s on file %s has been ongoing for %0.1fs",
redact.Safe(i.OpType.String()), redact.Safe(filepath.Base(i.Path)),
redact.Safe(i.Duration.Seconds()))
}
}
// diskHealthCheckingFS adds disk-health checking facilities to a VFS.
@@ -443,7 +450,7 @@ type diskHealthCheckingFS struct {
type slot struct {
name string
opType OpType
startNanos int64
startNanos atomic.Int64
}
// diskHealthCheckingFS implements FS.
@@ -510,12 +517,12 @@ func (d *diskHealthCheckingFS) timeFilesystemOp(name string, opType OpType, op f
startNanos := time.Now().UnixNano()
for i := 0; i < len(d.mu.inflight); i++ {
if atomic.LoadInt64(&d.mu.inflight[i].startNanos) == 0 {
if d.mu.inflight[i].startNanos.Load() == 0 {
// This slot is not in use. Claim it.
s = d.mu.inflight[i]
s.name = name
s.opType = opType
atomic.StoreInt64(&s.startNanos, startNanos)
s.startNanos.Store(startNanos)
break
}
}
@@ -526,10 +533,10 @@ func (d *diskHealthCheckingFS) timeFilesystemOp(name string, opType OpType, op f
// incur an allocation.
if s == nil {
s = &slot{
name: name,
opType: opType,
startNanos: startNanos,
name: name,
opType: opType,
}
s.startNanos.Store(startNanos)
d.mu.inflight = append(d.mu.inflight, s)
}
}()
@@ -537,7 +544,7 @@ func (d *diskHealthCheckingFS) timeFilesystemOp(name string, opType OpType, op f
op()
// Signal completion by zeroing the start time.
atomic.StoreInt64(&s.startNanos, 0)
s.startNanos.Store(0)
}
// startTickerLocked starts a new goroutine with a ticker to monitor disk
@@ -548,7 +555,12 @@ func (d *diskHealthCheckingFS) startTickerLocked() {
go func() {
ticker := time.NewTicker(d.tickInterval)
defer ticker.Stop()
var exceededSlots []slot
type exceededSlot struct {
name string
opType OpType
startNanos int64
}
var exceededSlots []exceededSlot
for {
select {
@@ -559,14 +571,15 @@ func (d *diskHealthCheckingFS) startTickerLocked() {
d.mu.Lock()
now := time.Now()
for i := range d.mu.inflight {
nanos := atomic.LoadInt64(&d.mu.inflight[i].startNanos)
nanos := d.mu.inflight[i].startNanos.Load()
if nanos != 0 && time.Unix(0, nanos).Add(d.diskSlowThreshold).Before(now) {
// diskSlowThreshold was exceeded. Copy this inflightOp into
// exceededSlots and call d.onSlowDisk after dropping the mutex.
var inflightOp slot
inflightOp.name = d.mu.inflight[i].name
inflightOp.opType = d.mu.inflight[i].opType
inflightOp.startNanos = nanos
inflightOp := exceededSlot{
name: d.mu.inflight[i].name,
opType: d.mu.inflight[i].opType,
startNanos: nanos,
}
exceededSlots = append(exceededSlots, inflightOp)
}
}
@@ -680,6 +693,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.OpenReadWrite(name, opts...)
}
// OpenDir implements the FS interface.
func (d *diskHealthCheckingFS) OpenDir(name string) (File, error) {
f, err := d.fs.OpenDir(name)

View File

@@ -0,0 +1,26 @@
// 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.
//go:build openbsd
// +build openbsd
package vfs
import "golang.org/x/sys/unix"
func (defaultFS) GetDiskUsage(path string) (DiskUsage, error) {
stat := unix.Statfs_t{}
if err := unix.Statfs(path, &stat); err != nil {
return DiskUsage{}, err
}
freeBytes := uint64(stat.F_bsize) * uint64(stat.F_bfree)
availBytes := uint64(stat.F_bsize) * uint64(stat.F_bavail)
totalBytes := uint64(stat.F_bsize) * uint64(stat.F_blocks)
return DiskUsage{
AvailBytes: availBytes,
TotalBytes: totalBytes,
UsedBytes: totalBytes - freeBytes,
}, nil
}

View File

@@ -2,8 +2,8 @@
// of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//go:build darwin || openbsd || dragonfly || freebsd
// +build darwin openbsd dragonfly freebsd
//go:build darwin || dragonfly || freebsd
// +build darwin dragonfly freebsd
package vfs

View File

@@ -5,6 +5,7 @@
package vfs
import (
"fmt"
"io"
"os"
)
@@ -38,7 +39,15 @@ func (fs *loggingFS) Create(name string) (File, error) {
}
func (fs *loggingFS) Open(name string, opts ...OpenOption) (File, error) {
fs.logFn("open: %s", name)
var optsStr string
if len(opts) > 0 {
optsStr = " (options:"
for i := range opts {
optsStr += fmt.Sprintf(" %T", opts[i])
}
optsStr += ")"
}
fs.logFn("open: %s%s", name, optsStr)
f, err := fs.FS.Open(name, opts...)
if err != nil {
return nil, err
@@ -46,6 +55,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 +155,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)

View File

@@ -14,6 +14,7 @@ import (
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/cockroachdb/errors"
@@ -63,7 +64,8 @@ func NewStrictMem() *MemFS {
// NewMemFile returns a memory-backed File implementation. The memory-backed
// file takes ownership of data.
func NewMemFile(data []byte) File {
n := &memNode{refs: 1}
n := &memNode{}
n.refs.Store(1)
n.mu.data = data
n.mu.modTime = time.Now()
return &memFile{
@@ -77,6 +79,11 @@ type MemFS struct {
mu sync.Mutex
root *memNode
// lockFiles holds a map of open file locks. Presence in this map indicates
// a file lock is currently held. Keys are strings holding the path of the
// locked file. The stored value is untyped and unused; only presence of
// the key within the map is significant.
lockedFiles sync.Map
strict bool
ignoreSyncs bool
// Windows has peculiar semantics with respect to hard links and deleting
@@ -208,6 +215,7 @@ func (y *MemFS) Create(fullname string) (File, error) {
ret = &memFile{
n: n,
fs: y,
read: true,
write: true,
}
}
@@ -216,7 +224,7 @@ func (y *MemFS) Create(fullname string) (File, error) {
if err != nil {
return nil, err
}
atomic.AddInt32(&ret.n.refs, 1)
ret.n.refs.Add(1)
return ret, nil
}
@@ -262,7 +270,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 +283,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,
}
}
}
@@ -293,18 +302,28 @@ func (y *MemFS) open(fullname string) (File, error) {
Err: oserror.ErrNotExist,
}
}
atomic.AddInt32(&ret.n.refs, 1)
ret.n.refs.Add(1)
return ret, nil
}
// 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.
@@ -323,7 +342,7 @@ func (y *MemFS) Remove(fullname string) error {
// Windows semantics. This ensures that we don't regress in the
// ordering of operations and try to remove a file while it is
// still open.
if n := atomic.LoadInt32(&child.refs); n > 0 {
if n := child.refs.Load(); n > 0 {
return oserror.ErrInvalid
}
}
@@ -444,9 +463,31 @@ func (y *MemFS) MkdirAll(dirname string, perm os.FileMode) error {
// Lock implements FS.Lock.
func (y *MemFS) Lock(fullname string) (io.Closer, error) {
// FS.Lock excludes other processes, but other processes cannot see this
// process' memory. We translate Lock into Create so that have the normal
// detection of non-existent directory paths.
return y.Create(fullname)
// process' memory. However some uses (eg, Cockroach tests) may open and
// close the same MemFS-backed database multiple times. We want mutual
// exclusion in this case too. See cockroachdb/cockroach#110645.
_, loaded := y.lockedFiles.Swap(fullname, nil /* the value itself is insignificant */)
if loaded {
// This file lock has already been acquired. On unix, this results in
// either EACCES or EAGAIN so we mimic.
return nil, syscall.EAGAIN
}
// Otherwise, we successfully acquired the lock. Locks are visible in the
// parent directory listing, and they also must be created under an existent
// directory. Create the path so that we have the normal detection of
// non-existent directory paths, and make the lock visible when listing
// directory entries.
f, err := y.Create(fullname)
if err != nil {
// "Release" the lock since we failed.
y.lockedFiles.Delete(fullname)
return nil, err
}
return &memFileLock{
y: y,
f: f,
fullname: fullname,
}, nil
}
// List implements FS.List.
@@ -513,7 +554,7 @@ func (*MemFS) GetDiskUsage(string) (DiskUsage, error) {
type memNode struct {
name string
isDir bool
refs int32
refs atomic.Int32
// Mutable state.
// - For a file: data, syncedDate, modTime: A file is only being mutated by a single goroutine,
@@ -629,7 +670,7 @@ type memFile struct {
var _ File = (*memFile)(nil)
func (f *memFile) Close() error {
if n := atomic.AddInt32(&f.n.refs, -1); n < 0 {
if n := f.n.refs.Add(-1); n < 0 {
panic(fmt.Sprintf("pebble: close of unopened file: %d", n))
}
f.n = nil
@@ -702,6 +743,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 }
@@ -751,3 +815,18 @@ func (f *memFile) Fd() uintptr {
func (f *memFile) Flush() error {
return nil
}
type memFileLock struct {
y *MemFS
f File
fullname string
}
func (l *memFileLock) Close() error {
if l.y == nil {
return nil
}
l.y.lockedFiles.Delete(l.fullname)
l.y = nil
return l.f.Close()
}

View File

@@ -26,16 +26,14 @@ type syncingFile struct {
noSyncOnClose bool
bytesPerSync int64
preallocateSize int64
atomic struct {
// The offset at which dirty data has been written.
offset int64
// The offset at which data has been synced. Note that if SyncFileRange is
// being used, the periodic syncing of data during writing will only ever
// sync up to offset-1MB. This is done to avoid rewriting the tail of the
// file multiple times, but has the side effect of ensuring that Close will
// sync the file's metadata.
syncOffset int64
}
// The offset at which dirty data has been written.
offset atomic.Int64
// The offset at which data has been synced. Note that if SyncFileRange is
// being used, the periodic syncing of data during writing will only ever
// sync up to offset-1MB. This is done to avoid rewriting the tail of the
// file multiple times, but has the side effect of ensuring that Close will
// sync the file's metadata.
syncOffset atomic.Int64
preallocatedBlocks int64
}
@@ -54,13 +52,13 @@ func NewSyncingFile(f File, opts SyncingFileOptions) File {
}
// Ensure a file that is opened and then closed will be synced, even if no
// data has been written to it.
s.atomic.syncOffset = -1
s.syncOffset.Store(-1)
return s
}
// NB: syncingFile.Write is unsafe for concurrent use!
func (f *syncingFile) Write(p []byte) (n int, err error) {
_ = f.preallocate(atomic.LoadInt64(&f.atomic.offset))
_ = f.preallocate(f.offset.Load())
n, err = f.File.Write(p)
if err != nil {
@@ -68,7 +66,7 @@ func (f *syncingFile) Write(p []byte) (n int, err error) {
}
// The offset is updated atomically so that it can be accessed safely from
// Sync.
atomic.AddInt64(&f.atomic.offset, int64(n))
f.offset.Add(int64(n))
if err := f.maybeSync(); err != nil {
return 0, err
}
@@ -93,11 +91,11 @@ func (f *syncingFile) preallocate(offset int64) error {
func (f *syncingFile) ratchetSyncOffset(offset int64) {
for {
syncOffset := atomic.LoadInt64(&f.atomic.syncOffset)
syncOffset := f.syncOffset.Load()
if syncOffset >= offset {
return
}
if atomic.CompareAndSwapInt64(&f.atomic.syncOffset, syncOffset, offset) {
if f.syncOffset.CompareAndSwap(syncOffset, offset) {
return
}
}
@@ -109,7 +107,7 @@ func (f *syncingFile) Sync() error {
// offset, we still need to call the underlying file's sync for persistence
// guarantees which are not provided by SyncTo (or by sync_file_range on
// Linux).
f.ratchetSyncOffset(atomic.LoadInt64(&f.atomic.offset))
f.ratchetSyncOffset(f.offset.Load())
return f.SyncData()
}
@@ -127,7 +125,7 @@ func (f *syncingFile) maybeSync() error {
// Xfs does neighbor page flushing outside of the specified ranges. We
// need to make sure sync range is far from the write offset.
const syncRangeBuffer = 1 << 20 // 1 MB
offset := atomic.LoadInt64(&f.atomic.offset)
offset := f.offset.Load()
if offset <= syncRangeBuffer {
return nil
}
@@ -135,7 +133,7 @@ func (f *syncingFile) maybeSync() error {
const syncRangeAlignment = 4 << 10 // 4 KB
syncToOffset := offset - syncRangeBuffer
syncToOffset -= syncToOffset % syncRangeAlignment
syncOffset := atomic.LoadInt64(&f.atomic.syncOffset)
syncOffset := f.syncOffset.Load()
if syncToOffset < 0 || (syncToOffset-syncOffset) < f.bytesPerSync {
return nil
}
@@ -167,7 +165,7 @@ func (f *syncingFile) Close() error {
// caller has not called Sync since the last write, syncOffset is guaranteed
// to be less than atomic.offset. This ensures we fall into the below
// conditional and perform a full sync to durably persist the file.
if off := atomic.LoadInt64(&f.atomic.offset); off > atomic.LoadInt64(&f.atomic.syncOffset) {
if off := f.offset.Load(); off > f.syncOffset.Load() {
// There's still remaining dirty data.
if f.noSyncOnClose {

View File

@@ -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))
}