Upgrade versions of vendored libs
This commit is contained in:
67
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
67
vendor/github.com/cockroachdb/pebble/record/log_writer.go
generated
vendored
@@ -133,7 +133,8 @@ func (q *syncQueue) load() (head, tail, realLength uint32) {
|
||||
return head, tail, realLength
|
||||
}
|
||||
|
||||
func (q *syncQueue) pop(head, tail uint32, err error) error {
|
||||
// REQUIRES: queueSemChan is non-nil.
|
||||
func (q *syncQueue) pop(head, tail uint32, err error, queueSemChan chan struct{}) error {
|
||||
if tail == head {
|
||||
// Queue is empty.
|
||||
return nil
|
||||
@@ -153,6 +154,10 @@ func (q *syncQueue) pop(head, tail uint32, err error) error {
|
||||
// will try to enqueue before we've "freed" space in the queue.
|
||||
atomic.AddUint64(&q.headTail, 1)
|
||||
wg.Done()
|
||||
// Is always non-nil in production.
|
||||
if queueSemChan != nil {
|
||||
<-queueSemChan
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -291,12 +296,20 @@ type LogWriter struct {
|
||||
// used for min-sync-interval. In normal operation this points to
|
||||
// time.AfterFunc.
|
||||
afterFunc func(d time.Duration, f func()) syncTimer
|
||||
|
||||
// See the comment for LogWriterConfig.QueueSemChan.
|
||||
queueSemChan chan struct{}
|
||||
}
|
||||
|
||||
// LogWriterConfig is a struct used for configuring new LogWriters
|
||||
type LogWriterConfig struct {
|
||||
WALMinSyncInterval durationFunc
|
||||
WALFsyncLatency prometheus.Histogram
|
||||
// QueueSemChan is an optional channel to pop from when popping from
|
||||
// LogWriter.flusher.syncQueue. It functions as a semaphore that prevents
|
||||
// the syncQueue from overflowing (which will cause a panic). All production
|
||||
// code ensures this is non-nil.
|
||||
QueueSemChan chan struct{}
|
||||
}
|
||||
|
||||
// CapAllocatedBlocks is the maximum number of blocks allocated by the
|
||||
@@ -319,6 +332,7 @@ func NewLogWriter(w io.Writer, logNum base.FileNum, logWriterConfig LogWriterCon
|
||||
afterFunc: func(d time.Duration, f func()) syncTimer {
|
||||
return time.AfterFunc(d, f)
|
||||
},
|
||||
queueSemChan: logWriterConfig.QueueSemChan,
|
||||
}
|
||||
r.free.cond.L = &r.free.Mutex
|
||||
r.free.blocks = make([]*block, 0, CapAllocatedBlocks)
|
||||
@@ -441,7 +455,7 @@ func (w *LogWriter) flushLoop(context.Context) {
|
||||
// If flusher has an error, we propagate it to waiters. Note in spite of
|
||||
// error we consume the pending list above to free blocks for writers.
|
||||
if f.err != nil {
|
||||
f.syncQ.pop(head, tail, f.err)
|
||||
f.syncQ.pop(head, tail, f.err, w.queueSemChan)
|
||||
// Update the idleStartTime if work could not be done, so that we don't
|
||||
// include the duration we tried to do work as idle. We don't bother
|
||||
// with the rest of the accounting, which means we will undercount.
|
||||
@@ -518,7 +532,7 @@ func (w *LogWriter) flushPending(
|
||||
syncLatency, err = w.syncWithLatency()
|
||||
}
|
||||
f := &w.flusher
|
||||
if popErr := f.syncQ.pop(head, tail, err); popErr != nil {
|
||||
if popErr := f.syncQ.pop(head, tail, err, w.queueSemChan); popErr != nil {
|
||||
return synced, syncLatency, bytesWritten, popErr
|
||||
}
|
||||
}
|
||||
@@ -548,7 +562,7 @@ func (w *LogWriter) flushBlock(b *block) error {
|
||||
|
||||
// queueBlock queues the current block for writing to the underlying writer,
|
||||
// allocates a new block and reserves space for the next header.
|
||||
func (w *LogWriter) queueBlock() {
|
||||
func (w *LogWriter) queueBlock() (waitDuration time.Duration) {
|
||||
// Allocate a new block, blocking until one is available. We do this first
|
||||
// because w.block is protected by w.flusher.Mutex.
|
||||
w.free.Lock()
|
||||
@@ -557,9 +571,11 @@ func (w *LogWriter) queueBlock() {
|
||||
w.free.allocated++
|
||||
w.free.blocks = append(w.free.blocks, &block{})
|
||||
} else {
|
||||
now := time.Now()
|
||||
for len(w.free.blocks) == 0 {
|
||||
w.free.cond.Wait()
|
||||
}
|
||||
waitDuration = time.Since(now)
|
||||
}
|
||||
}
|
||||
nextBlock := w.free.blocks[len(w.free.blocks)-1]
|
||||
@@ -575,6 +591,28 @@ func (w *LogWriter) queueBlock() {
|
||||
f.Unlock()
|
||||
|
||||
w.blockNum++
|
||||
return waitDuration
|
||||
}
|
||||
|
||||
// ReserveAllFreeBlocksForTesting is used to only for testing.
|
||||
func (w *LogWriter) ReserveAllFreeBlocksForTesting() (releaseFunc func()) {
|
||||
w.free.Lock()
|
||||
defer w.free.Unlock()
|
||||
free := w.free.blocks
|
||||
w.free.blocks = nil
|
||||
return func() {
|
||||
w.free.Lock()
|
||||
defer w.free.Unlock()
|
||||
// It is possible that someone has pushed a free block and w.free.blocks
|
||||
// is no longer nil. That is harmless. Also, the waiter loops on the
|
||||
// condition len(w.free.blocks) == 0, so to actually unblock it we need to
|
||||
// give it a free block.
|
||||
if len(free) == 0 {
|
||||
free = append(free, &block{})
|
||||
}
|
||||
w.free.blocks = free
|
||||
w.free.cond.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
// Close flushes and syncs any unwritten data and closes the writer.
|
||||
@@ -626,7 +664,8 @@ func (w *LogWriter) Close() error {
|
||||
// of the record.
|
||||
// External synchronisation provided by commitPipeline.mu.
|
||||
func (w *LogWriter) WriteRecord(p []byte) (int64, error) {
|
||||
return w.SyncRecord(p, nil, nil)
|
||||
logSize, _, err := w.SyncRecord(p, nil, nil)
|
||||
return logSize, err
|
||||
}
|
||||
|
||||
// SyncRecord writes a complete record. If wg!= nil the record will be
|
||||
@@ -634,9 +673,11 @@ func (w *LogWriter) WriteRecord(p []byte) (int64, error) {
|
||||
// the wait group upon completion. Returns the offset just past the end of the
|
||||
// record.
|
||||
// External synchronisation provided by commitPipeline.mu.
|
||||
func (w *LogWriter) SyncRecord(p []byte, wg *sync.WaitGroup, err *error) (int64, error) {
|
||||
func (w *LogWriter) SyncRecord(
|
||||
p []byte, wg *sync.WaitGroup, err *error,
|
||||
) (logSize int64, waitDuration time.Duration, err2 error) {
|
||||
if w.err != nil {
|
||||
return -1, w.err
|
||||
return -1, 0, w.err
|
||||
}
|
||||
|
||||
// The `i == 0` condition ensures we handle empty records. Such records can
|
||||
@@ -644,7 +685,9 @@ func (w *LogWriter) SyncRecord(p []byte, wg *sync.WaitGroup, err *error) (int64,
|
||||
// MANIFEST is currently written using Writer, it is good to support the same
|
||||
// semantics with LogWriter.
|
||||
for i := 0; i == 0 || len(p) > 0; i++ {
|
||||
p = w.emitFragment(i, p)
|
||||
var wd time.Duration
|
||||
p, wd = w.emitFragment(i, p)
|
||||
waitDuration += wd
|
||||
}
|
||||
|
||||
if wg != nil {
|
||||
@@ -663,7 +706,7 @@ func (w *LogWriter) SyncRecord(p []byte, wg *sync.WaitGroup, err *error) (int64,
|
||||
// race with our read. That's ok because the only error we could be seeing is
|
||||
// one to syncing for which the caller can receive notification of by passing
|
||||
// in a non-nil err argument.
|
||||
return offset, nil
|
||||
return offset, waitDuration, nil
|
||||
}
|
||||
|
||||
// Size returns the current size of the file.
|
||||
@@ -684,7 +727,7 @@ func (w *LogWriter) emitEOFTrailer() {
|
||||
atomic.StoreInt32(&b.written, i+int32(recyclableHeaderSize))
|
||||
}
|
||||
|
||||
func (w *LogWriter) emitFragment(n int, p []byte) []byte {
|
||||
func (w *LogWriter) emitFragment(n int, p []byte) (remainingP []byte, waitDuration time.Duration) {
|
||||
b := w.block
|
||||
i := b.written
|
||||
first := n == 0
|
||||
@@ -718,9 +761,9 @@ func (w *LogWriter) emitFragment(n int, p []byte) []byte {
|
||||
for i := b.written; i < blockSize; i++ {
|
||||
b.buf[i] = 0
|
||||
}
|
||||
w.queueBlock()
|
||||
waitDuration = w.queueBlock()
|
||||
}
|
||||
return p[r:]
|
||||
return p[r:], waitDuration
|
||||
}
|
||||
|
||||
// Metrics must be called after Close. The callee will no longer modify the
|
||||
|
||||
14
vendor/github.com/cockroachdb/pebble/record/record.go
generated
vendored
14
vendor/github.com/cockroachdb/pebble/record/record.go
generated
vendored
@@ -19,6 +19,7 @@
|
||||
// Neither Readers or Writers are safe to use concurrently.
|
||||
//
|
||||
// Example code:
|
||||
//
|
||||
// func read(r io.Reader) ([]string, error) {
|
||||
// var ss []string
|
||||
// records := record.NewReader(r)
|
||||
@@ -65,9 +66,9 @@
|
||||
// A record maps to one or more chunks. There are two chunk formats: legacy and
|
||||
// recyclable. The legacy chunk format:
|
||||
//
|
||||
// +----------+-----------+-----------+--- ... ---+
|
||||
// | CRC (4B) | Size (2B) | Type (1B) | Payload |
|
||||
// +----------+-----------+-----------+--- ... ---+
|
||||
// +----------+-----------+-----------+--- ... ---+
|
||||
// | CRC (4B) | Size (2B) | Type (1B) | Payload |
|
||||
// +----------+-----------+-----------+--- ... ---+
|
||||
//
|
||||
// CRC is computed over the type and payload
|
||||
// Size is the length of the payload in bytes
|
||||
@@ -84,9 +85,9 @@
|
||||
// metadata. Additionally, recycling log files is a prequisite for using direct
|
||||
// IO with log writing. The recyclyable format is:
|
||||
//
|
||||
// +----------+-----------+-----------+----------------+--- ... ---+
|
||||
// | CRC (4B) | Size (2B) | Type (1B) | Log number (4B)| Payload |
|
||||
// +----------+-----------+-----------+----------------+--- ... ---+
|
||||
// +----------+-----------+-----------+----------------+--- ... ---+
|
||||
// | CRC (4B) | Size (2B) | Type (1B) | Log number (4B)| Payload |
|
||||
// +----------+-----------+-----------+----------------+--- ... ---+
|
||||
//
|
||||
// Recyclable chunks are distinguished from legacy chunks by the addition of 4
|
||||
// extra "recyclable" chunk types that map directly to the legacy chunk types
|
||||
@@ -247,6 +248,7 @@ func (r *Reader) nextChunk(wantFirst bool) error {
|
||||
r.begin = r.end + headerSize
|
||||
r.end = r.begin + int(length)
|
||||
if r.end > r.n {
|
||||
// The chunk straddles a 32KB boundary (or the end of file).
|
||||
if r.recovering {
|
||||
r.recover()
|
||||
continue
|
||||
|
||||
82
vendor/github.com/cockroachdb/pebble/record/rotation.go
generated
vendored
Normal file
82
vendor/github.com/cockroachdb/pebble/record/rotation.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 record
|
||||
|
||||
// RotationHelper is a type used to inform the decision of rotating a record log
|
||||
// file.
|
||||
//
|
||||
// The assumption is that multiple records can be coalesced into a single record
|
||||
// (called a snapshot). Starting a new file, where the first record is a
|
||||
// snapshot of the current state is referred to as "rotating" the log.
|
||||
//
|
||||
// Normally we rotate files when a certain file size is reached. But in certain
|
||||
// cases (e.g. contents become very large), this can result in too frequent
|
||||
// rotation. This helper contains logic to impose extra conditions on the
|
||||
// rotation.
|
||||
//
|
||||
// The rotation helper uses "size" as a unit-less estimation that is correlated
|
||||
// with the on-disk size of a record or snapshot.
|
||||
type RotationHelper struct {
|
||||
// lastSnapshotSize is the size of the last snapshot.
|
||||
lastSnapshotSize int64
|
||||
// sizeSinceLastSnapshot is the sum of sizes of records applied since the last
|
||||
// snapshot.
|
||||
sizeSinceLastSnapshot int64
|
||||
lastRecordSize int64
|
||||
}
|
||||
|
||||
// AddRecord makes the rotation helper aware of a new record.
|
||||
func (rh *RotationHelper) AddRecord(recordSize int64) {
|
||||
rh.sizeSinceLastSnapshot += recordSize
|
||||
rh.lastRecordSize = recordSize
|
||||
}
|
||||
|
||||
// ShouldRotate returns whether we should start a new log file (with a snapshot).
|
||||
// Does not need to be called if other rotation factors (log file size) are not
|
||||
// satisfied.
|
||||
func (rh *RotationHelper) ShouldRotate(nextSnapshotSize int64) bool {
|
||||
// The primary goal is to ensure that when reopening a log file, the number of
|
||||
// edits that need to be replayed on top of the snapshot is "sane" while
|
||||
// keeping the rotation frequency as low as possible.
|
||||
//
|
||||
// For the purposes of this description, we assume that the log is mainly
|
||||
// storing a collection of "entries", with edits adding or removing entries.
|
||||
// Consider the following cases:
|
||||
//
|
||||
// - The number of live entries is roughly stable: after writing the snapshot
|
||||
// (with S entries), we require that there be enough edits such that the
|
||||
// cumulative number of entries in those edits, E, be greater than S. This
|
||||
// will ensure that at most 50% of data written out is due to rotation.
|
||||
//
|
||||
// - The number of live entries K in the DB is shrinking drastically, say from
|
||||
// S to S/10: After this shrinking, E = 0.9S, and so if we used the previous
|
||||
// snapshot entry count, S, as the threshold that needs to be exceeded, we
|
||||
// will further delay the snapshot writing. Which means on reopen we will
|
||||
// need to replay 0.9S edits to get to a version with 0.1S entries. It would
|
||||
// be better to create a new snapshot when E exceeds the number of entries in
|
||||
// the current version.
|
||||
//
|
||||
// - The number of live entries L in the DB is growing; say the last snapshot
|
||||
// had S entries, and now we have 10S entries, so E = 9S. If we required
|
||||
// that E is at least the current number of entries, we would further delay
|
||||
// writing a new snapshot (which is not desirable).
|
||||
//
|
||||
// The logic below uses the min of the last snapshot size count and the size
|
||||
// count in the current version.
|
||||
return rh.sizeSinceLastSnapshot > rh.lastSnapshotSize || rh.sizeSinceLastSnapshot > nextSnapshotSize
|
||||
}
|
||||
|
||||
// Rotate makes the rotation helper aware that we are rotating to a new snapshot
|
||||
// (to which we will apply the latest edit).
|
||||
func (rh *RotationHelper) Rotate(snapshotSize int64) {
|
||||
rh.lastSnapshotSize = snapshotSize
|
||||
rh.sizeSinceLastSnapshot = rh.lastRecordSize
|
||||
}
|
||||
|
||||
// DebugInfo returns the last snapshot size and size of the edits since the last
|
||||
// snapshot; used for testing and debugging.
|
||||
func (rh *RotationHelper) DebugInfo() (lastSnapshotSize int64, sizeSinceLastSnapshot int64) {
|
||||
return rh.lastSnapshotSize, rh.sizeSinceLastSnapshot
|
||||
}
|
||||
Reference in New Issue
Block a user