fix(mount): prevent negative activity-score shifts (#12631)

fix(mount): bound activity decay shifts across time regressions
This commit is contained in:
Ping Qiu authored and GitHub committed 2026-11-14 10:46:57 -08:00
1 parent 0a38738076
commit 65ecb6aa12
2 files changed
+43 -3

No files matched your search

+4 -3
View File
@@ -17,7 +17,8 @@ func NewActivityScore() *ActivityScore {
func (as *ActivityScore) MarkRead() {
now := time.Now().UnixNano()
last := atomic.SwapInt64(&as.lastActiveTsNs, now)
deltaTime := (now - last) >> 30 // about number of seconds
// Concurrent timestamp swaps or a clock adjustment can make last newer than now.
deltaTime := max(0, min(63, (now-last)>>30)) // about number of seconds
for {
old := atomic.LoadInt64(&as.decayedActivenessScore)
score := old>>deltaTime + 256
@@ -33,7 +34,7 @@ func (as *ActivityScore) MarkRead() {
func (as *ActivityScore) MarkWrite() {
now := time.Now().UnixNano()
last := atomic.SwapInt64(&as.lastActiveTsNs, now)
deltaTime := (now - last) >> 30 // about number of seconds
deltaTime := max(0, min(63, (now-last)>>30)) // about number of seconds
for {
old := atomic.LoadInt64(&as.decayedActivenessScore)
score := old>>deltaTime + 1024
@@ -47,6 +48,6 @@ func (as *ActivityScore) MarkWrite() {
}
func (as *ActivityScore) ActivityScore() int64 {
deltaTime := (time.Now().UnixNano() - atomic.LoadInt64(&as.lastActiveTsNs)) >> 30
deltaTime := max(0, min(63, (time.Now().UnixNano()-atomic.LoadInt64(&as.lastActiveTsNs))>>30))
return atomic.LoadInt64(&as.decayedActivenessScore) >> deltaTime
}
@@ -0,0 +1,39 @@
package page_writer
import (
"sync/atomic"
"testing"
"time"
)
func TestActivityScoreHandlesFutureTimestamp(t *testing.T) {
for _, test := range []struct {
name string
call func(*ActivityScore) int64
want int64
}{
{"MarkRead", func(score *ActivityScore) int64 {
score.MarkRead()
return score.ActivityScore()
}, 4352},
{"MarkWrite", func(score *ActivityScore) int64 {
score.MarkWrite()
return score.ActivityScore()
}, 5120},
{"ActivityScore", (*ActivityScore).ActivityScore, 4096},
} {
t.Run(test.name, func(t *testing.T) {
score := NewActivityScore()
atomic.StoreInt64(&score.lastActiveTsNs, time.Now().Add(time.Hour).UnixNano())
atomic.StoreInt64(&score.decayedActivenessScore, 4096)
defer func() {
if caught := recover(); caught != nil {
t.Fatalf("future last-active timestamp must not panic: %v", caught)
}
}()
if got := test.call(score); got != test.want {
t.Fatalf("score=%d, want %d without backwards-time decay", got, test.want)
}
})
}
}