fix(client): fix putAheadReadBlock Blocking issue

close:#1000412030

Signed-off-by: chihe <chihe@oppo.com>
(cherry picked from commit ccb7de44bb)
This commit is contained in:
chihe 2025-12-09 10:46:46 +08:00
parent a21a0b6b4c
commit b47bc52042
4 changed files with 50 additions and 9 deletions

View File

@ -22,6 +22,7 @@ package main
import ( import (
"bufio" "bufio"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"io" "io"
@ -37,6 +38,8 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
//"runtime/debug"
runtimepprof "runtime/pprof" runtimepprof "runtime/pprof"
"strings" "strings"
"syscall" "syscall"
@ -92,7 +95,8 @@ const (
ControlCommandSetRate = "/rate/set" ControlCommandSetRate = "/rate/set"
ControlCommandGetRate = "/rate/get" ControlCommandGetRate = "/rate/get"
ControlCommandFreeOSMemory = "/debug/freeosmemory" ControlCommandFreeOSMemory = "/freeosmemory"
ControlCommandMemStats = "/memstats"
ControlCommandSuspend = "/suspend" ControlCommandSuspend = "/suspend"
ControlCommandResume = "/resume" ControlCommandResume = "/resume"
ControlCommandStopWarmMeta = "/stopWarmMeta" ControlCommandStopWarmMeta = "/stopWarmMeta"
@ -767,6 +771,7 @@ func mount(opt *proto.MountOptions) (fsConn *fuse.Conn, super *cfs.Super, err er
http.HandleFunc(ControlCommandGetRate, super.GetRate) http.HandleFunc(ControlCommandGetRate, super.GetRate)
http.HandleFunc(log.SetLogLevelPath, log.SetLogLevel) http.HandleFunc(log.SetLogLevelPath, log.SetLogLevel)
http.HandleFunc(ControlCommandFreeOSMemory, freeOSMemory) http.HandleFunc(ControlCommandFreeOSMemory, freeOSMemory)
http.HandleFunc(ControlCommandMemStats, memStats)
http.HandleFunc(log.GetLogPath, log.GetLog) http.HandleFunc(log.GetLogPath, log.GetLog)
http.HandleFunc(ControlCommandSuspend, super.SetSuspend) http.HandleFunc(ControlCommandSuspend, super.SetSuspend)
http.HandleFunc(ControlCommandResume, super.SetResume) http.HandleFunc(ControlCommandResume, super.SetResume)
@ -1136,6 +1141,19 @@ func changeRlimit(val uint64) {
func freeOSMemory(w http.ResponseWriter, r *http.Request) { func freeOSMemory(w http.ResponseWriter, r *http.Request) {
debug.FreeOSMemory() debug.FreeOSMemory()
w.WriteHeader(http.StatusOK)
w.Write([]byte("freeOSMemory is called"))
}
func memStats(w http.ResponseWriter, r *http.Request) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.SetEscapeHTML(false)
_ = enc.Encode(m)
} }
func loadConfFromMaster(opt *proto.MountOptions) (err error) { func loadConfFromMaster(opt *proto.MountOptions) (err error) {

View File

@ -20,6 +20,8 @@ import (
syslog "log" syslog "log"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"os"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"sync" "sync"
@ -235,6 +237,13 @@ func (m *Server) checkConfig(cfg *config.Config) (err error) {
m.bindIp = cfg.GetBool(proto.BindIpKey) m.bindIp = cfg.GetBool(proto.BindIpKey)
m.port = cfg.GetString(proto.ListenPort) m.port = cfg.GetString(proto.ListenPort)
m.logDir = cfg.GetString(LogDir) m.logDir = cfg.GetString(LogDir)
// make logDir absolute to avoid dependency on process working directory
if m.logDir != "" && !filepath.IsAbs(m.logDir) {
if exe, e := os.Executable(); e == nil {
base := filepath.Dir(exe)
m.logDir = filepath.Join(base, m.logDir)
}
}
m.walDir = cfg.GetString(WalDir) m.walDir = cfg.GetString(WalDir)
m.bStoreAddr = cfg.GetString(BStoreAddrKey) m.bStoreAddr = cfg.GetString(BStoreAddrKey)
if m.bStoreAddr == "" { if m.bStoreAddr == "" {

View File

@ -147,9 +147,13 @@ func (arc *AheadReadCache) getAheadReadBlock() (block *AheadReadBlock, err error
} }
func (arc *AheadReadCache) putAheadReadBlock(key string, block *AheadReadBlock) { func (arc *AheadReadCache) putAheadReadBlock(key string, block *AheadReadBlock) {
arc.availableBlockC <- struct{}{} select {
case arc.availableBlockC <- struct{}{}:
atomic.AddInt64(&arc.availableBlockCnt, 1)
default:
// channel is full: duplicate return detected, drop token to avoid blocking
}
putAheadReadBlock(block) putAheadReadBlock(block)
atomic.AddInt64(&arc.availableBlockCnt, 1)
} }
func (arc *AheadReadCache) checkBlockTimeOut() { func (arc *AheadReadCache) checkBlockTimeOut() {
@ -185,11 +189,13 @@ func (arc *AheadReadCache) doCheckBlockTimeOut() {
if atomic.LoadUint32(&bv.readed) == AheadReadedInit { if atomic.LoadUint32(&bv.readed) == AheadReadedInit {
log.LogInfof("doCheckBlockTimeOut delete unreaded block: key(%v) addr(%p)", key, bv) log.LogInfof("doCheckBlockTimeOut delete unreaded block: key(%v) addr(%p)", key, bv)
} }
arc.blockCache.Delete(key.(string)) // block is ready to recycle; ensure return-once semantics
// block is ready to recycle keyStr := key.(string)
bv.key = "" bv.key = ""
bv.lock.Unlock() bv.lock.Unlock()
arc.putAheadReadBlock(key.(string), bv) if actual, loaded := arc.blockCache.LoadAndDelete(keyStr); loaded {
arc.putAheadReadBlock(keyStr, actual.(*AheadReadBlock))
}
} else { } else {
bv.lock.Unlock() bv.lock.Unlock()
} }
@ -747,12 +753,15 @@ func (arw *AheadReadWindow) evictAllBlocks() {
bv.lock.Lock() bv.lock.Lock()
bv.key = "" bv.key = ""
if atomic.LoadUint32(&bv.state) == AheadReadBlockStateInit { if atomic.LoadUint32(&bv.state) == AheadReadBlockStateInit {
arw.cache.blockCache.Delete(key) // ensure only one goroutine returns the token once
arw.cache.putAheadReadBlock(key.(string), bv) bv.lock.Unlock()
if actual, loaded := arw.cache.blockCache.LoadAndDelete(key.(string)); loaded {
arw.cache.putAheadReadBlock(key.(string), actual.(*AheadReadBlock))
}
} else { } else {
atomic.StoreUint32(&bv.state, AheadReadBlockStateClear) atomic.StoreUint32(&bv.state, AheadReadBlockStateClear)
bv.lock.Unlock()
} }
bv.lock.Unlock()
return true return true
}) })
// wait for all blockCache is cleared // wait for all blockCache is cleared

View File

@ -21,6 +21,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"regexp" "regexp"
"runtime/debug" "runtime/debug"
"sort" "sort"
@ -98,6 +99,10 @@ var gSt *Statistic = nil
func NewStatistic(dir, logModule string, logMaxSize int64, timeOutUs [MaxTimeoutLevel]uint32, useMutex bool) (*Statistic, error) { func NewStatistic(dir, logModule string, logMaxSize int64, timeOutUs [MaxTimeoutLevel]uint32, useMutex bool) (*Statistic, error) {
dir = path.Join(dir, logModule) dir = path.Join(dir, logModule)
// normalize to absolute path to avoid cwd-related issues
if absDir, err := filepath.Abs(dir); err == nil {
dir = absDir
}
fi, err := os.Stat(dir) fi, err := os.Stat(dir)
if err != nil { if err != nil {
os.MkdirAll(dir, 0o755) os.MkdirAll(dir, 0o755)