@@ -45,6 +45,10 @@ const (
4545 // metricsGatheringInterval specifies the interval to retrieve pebble database
4646 // compaction, io and pause stats to report to the user.
4747 metricsGatheringInterval = 3 * time .Second
48+
49+ // degradationWarnInterval specifies how often warning should be printed if the
50+ // leveldb database cannot keep up with requested writes.
51+ degradationWarnInterval = time .Minute
4852)
4953
5054// Database is a persistent key-value store based on the pebble storage engine.
@@ -76,14 +80,16 @@ type Database struct {
7680
7781 log log.Logger // Contextual logger tracking the database path
7882
79- activeComp int // Current number of active compactions
80- compStartTime time.Time // The start time of the earliest currently-active compaction
81- compTime atomic.Int64 // Total time spent in compaction in ns
82- level0Comp atomic.Uint32 // Total number of level-zero compactions
83- nonLevel0Comp atomic.Uint32 // Total number of non level-zero compactions
84- writeDelayStartTime time.Time // The start time of the latest write stall
85- writeDelayCount atomic.Int64 // Total number of write stall counts
86- writeDelayTime atomic.Int64 // Total time spent in write stalls
83+ activeComp int // Current number of active compactions
84+ compStartTime time.Time // The start time of the earliest currently-active compaction
85+ compTime atomic.Int64 // Total time spent in compaction in ns
86+ level0Comp atomic.Uint32 // Total number of level-zero compactions
87+ nonLevel0Comp atomic.Uint32 // Total number of non level-zero compactions
88+
89+ writeStalled atomic.Bool // Flag whether the write is stalled
90+ writeDelayStartTime time.Time // The start time of the latest write stall
91+ writeDelayCount atomic.Int64 // Total number of write stall counts
92+ writeDelayTime atomic.Int64 // Total time spent in write stalls
8793
8894 writeOptions * pebble.WriteOptions
8995}
@@ -112,10 +118,13 @@ func (d *Database) onCompactionEnd(info pebble.CompactionInfo) {
112118
113119func (d * Database ) onWriteStallBegin (b pebble.WriteStallBeginInfo ) {
114120 d .writeDelayStartTime = time .Now ()
121+ d .writeDelayCount .Add (1 )
122+ d .writeStalled .Store (true )
115123}
116124
117125func (d * Database ) onWriteStallEnd () {
118126 d .writeDelayTime .Add (int64 (time .Since (d .writeDelayStartTime )))
127+ d .writeStalled .Store (false )
119128}
120129
121130// panicLogger is just a noop logger to disable Pebble's internal logger.
@@ -450,13 +459,15 @@ func (d *Database) meter(refresh time.Duration, namespace string) {
450459
451460 // Create storage and warning log tracer for write delay.
452461 var (
453- compTimes [2 ]int64
454- writeDelayTimes [2 ]int64
455- writeDelayCounts [2 ]int64
456- compWrites [2 ]int64
457- compReads [2 ]int64
462+ compTimes [2 ]int64
463+ compWrites [2 ]int64
464+ compReads [2 ]int64
458465
459466 nWrites [2 ]int64
467+
468+ writeDelayTimes [2 ]int64
469+ writeDelayCounts [2 ]int64
470+ lastWriteStallReport time.Time
460471 )
461472
462473 // Iterate ad infinitum and collect the stats
@@ -496,6 +507,13 @@ func (d *Database) meter(refresh time.Duration, namespace string) {
496507 if d .writeDelayMeter != nil {
497508 d .writeDelayMeter .Mark (writeDelayTimes [i % 2 ] - writeDelayTimes [(i - 1 )% 2 ])
498509 }
510+ // Print a warning log if writing has been stalled for a while. The log will
511+ // be printed per minute to avoid overwhelming users.
512+ if d .writeStalled .Load () && writeDelayCounts [i % 2 ] == writeDelayCounts [(i - 1 )% 2 ] &&
513+ time .Now ().After (lastWriteStallReport .Add (degradationWarnInterval )) {
514+ d .log .Warn ("Database compacting, degraded performance" )
515+ lastWriteStallReport = time .Now ()
516+ }
499517 if d .compTimeMeter != nil {
500518 d .compTimeMeter .Mark (compTimes [i % 2 ] - compTimes [(i - 1 )% 2 ])
501519 }
0 commit comments