@@ -45,6 +45,10 @@ const (
45
45
// metricsGatheringInterval specifies the interval to retrieve pebble database
46
46
// compaction, io and pause stats to report to the user.
47
47
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
48
52
)
49
53
50
54
// Database is a persistent key-value store based on the pebble storage engine.
@@ -76,14 +80,16 @@ type Database struct {
76
80
77
81
log log.Logger // Contextual logger tracking the database path
78
82
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
87
93
88
94
writeOptions * pebble.WriteOptions
89
95
}
@@ -112,10 +118,13 @@ func (d *Database) onCompactionEnd(info pebble.CompactionInfo) {
112
118
113
119
func (d * Database ) onWriteStallBegin (b pebble.WriteStallBeginInfo ) {
114
120
d .writeDelayStartTime = time .Now ()
121
+ d .writeDelayCount .Add (1 )
122
+ d .writeStalled .Store (true )
115
123
}
116
124
117
125
func (d * Database ) onWriteStallEnd () {
118
126
d .writeDelayTime .Add (int64 (time .Since (d .writeDelayStartTime )))
127
+ d .writeStalled .Store (false )
119
128
}
120
129
121
130
// 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) {
450
459
451
460
// Create storage and warning log tracer for write delay.
452
461
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
458
465
459
466
nWrites [2 ]int64
467
+
468
+ writeDelayTimes [2 ]int64
469
+ writeDelayCounts [2 ]int64
470
+ lastWriteStallReport time.Time
460
471
)
461
472
462
473
// Iterate ad infinitum and collect the stats
@@ -496,6 +507,13 @@ func (d *Database) meter(refresh time.Duration, namespace string) {
496
507
if d .writeDelayMeter != nil {
497
508
d .writeDelayMeter .Mark (writeDelayTimes [i % 2 ] - writeDelayTimes [(i - 1 )% 2 ])
498
509
}
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
+ }
499
517
if d .compTimeMeter != nil {
500
518
d .compTimeMeter .Mark (compTimes [i % 2 ] - compTimes [(i - 1 )% 2 ])
501
519
}
0 commit comments