Files
EdgeNode/internal/caches/open_file_cache.go

214 lines
4.3 KiB
Go
Raw Normal View History

2024-05-17 18:30:33 +08:00
// Copyright 2022 GoEdge goedge.cdn@gmail.com. All rights reserved.
2022-01-12 21:09:00 +08:00
package caches
import (
2023-10-11 21:51:05 +08:00
"fmt"
2024-05-11 09:23:54 +08:00
"github.com/TeaOSLab/EdgeNode/internal/utils/goman"
2022-01-12 21:09:00 +08:00
"github.com/TeaOSLab/EdgeNode/internal/utils/linkedlist"
2024-03-28 17:17:34 +08:00
memutils "github.com/TeaOSLab/EdgeNode/internal/utils/mem"
2022-01-12 21:09:00 +08:00
"github.com/fsnotify/fsnotify"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"path/filepath"
2022-03-31 13:30:52 +08:00
"runtime"
2022-01-12 21:09:00 +08:00
"sync"
"time"
)
2023-10-11 21:51:05 +08:00
const (
maxOpenFileSize = 256 << 20
)
2022-01-12 21:09:00 +08:00
type OpenFileCache struct {
poolMap map[string]*OpenFilePool // file path => Pool
2023-10-03 21:38:45 +08:00
poolList *linkedlist.List[*OpenFilePool]
2022-01-12 21:09:00 +08:00
watcher *fsnotify.Watcher
2022-11-08 21:37:20 +08:00
locker sync.RWMutex
2022-01-12 21:09:00 +08:00
2023-10-11 21:51:05 +08:00
maxCount int
capacitySize int64
count int
usedSize int64
2022-01-12 21:09:00 +08:00
}
2023-10-11 21:51:05 +08:00
func NewOpenFileCache(maxCount int) (*OpenFileCache, error) {
if maxCount <= 0 {
maxCount = 16384
2022-01-12 21:09:00 +08:00
}
2024-05-16 20:40:41 +08:00
if maxCount > 65535 {
maxCount = 65535
}
2022-01-12 21:09:00 +08:00
var cache = &OpenFileCache{
2023-10-11 21:51:05 +08:00
maxCount: maxCount,
poolMap: map[string]*OpenFilePool{},
poolList: linkedlist.NewList[*OpenFilePool](),
2024-03-28 17:17:34 +08:00
capacitySize: (int64(memutils.SystemMemoryGB()) << 30) / 16,
2022-01-12 21:09:00 +08:00
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
cache.watcher = watcher
goman.New(func() {
for event := range watcher.Events {
2022-03-31 13:30:52 +08:00
if runtime.GOOS == "linux" || event.Op&fsnotify.Chmod != fsnotify.Chmod {
2022-01-12 21:09:00 +08:00
cache.Close(event.Name)
}
}
})
return cache, nil
}
func (this *OpenFileCache) Get(filename string) *OpenFile {
2024-03-28 08:52:53 +08:00
filename = filepath.Clean(filename)
2022-11-08 21:37:20 +08:00
this.locker.RLock()
2022-01-12 21:09:00 +08:00
pool, ok := this.poolMap[filename]
2022-11-08 21:37:20 +08:00
this.locker.RUnlock()
2022-01-12 21:09:00 +08:00
if ok {
2023-10-11 21:51:05 +08:00
file, consumed, consumedSize := pool.Get()
2022-01-12 21:09:00 +08:00
if consumed {
2022-11-08 21:37:20 +08:00
this.locker.Lock()
2022-01-12 21:09:00 +08:00
this.count--
2023-10-11 21:51:05 +08:00
this.usedSize -= consumedSize
2022-11-25 14:52:04 +08:00
// pool如果为空也不需要从列表中删除避免put时需要重新创建
2022-11-08 21:37:20 +08:00
this.locker.Unlock()
2022-01-12 21:09:00 +08:00
}
2023-10-11 21:51:05 +08:00
2022-01-12 21:09:00 +08:00
return file
}
return nil
}
func (this *OpenFileCache) Put(filename string, file *OpenFile) {
2024-03-28 08:52:53 +08:00
filename = filepath.Clean(filename)
2023-10-11 21:51:05 +08:00
if file.size > maxOpenFileSize {
return
}
2022-01-12 21:09:00 +08:00
this.locker.Lock()
defer this.locker.Unlock()
2023-10-11 21:51:05 +08:00
// 如果超过当前容量,则关闭最早的
2023-10-17 09:59:04 +08:00
if this.count >= this.maxCount || this.usedSize+file.size >= this.capacitySize {
2023-10-11 21:51:05 +08:00
this.consumeHead()
return
}
2022-01-12 21:09:00 +08:00
pool, ok := this.poolMap[filename]
var success bool
if ok {
success = pool.Put(file)
} else {
_ = this.watcher.Add(filename)
pool = NewOpenFilePool(filename)
pool.version = file.version
2022-01-12 21:09:00 +08:00
this.poolMap[filename] = pool
success = pool.Put(file)
}
this.poolList.Push(pool.linkItem)
// 检查长度
if success {
this.count++
2023-10-11 21:51:05 +08:00
this.usedSize += file.size
2022-01-12 21:09:00 +08:00
}
}
func (this *OpenFileCache) Close(filename string) {
2024-03-28 08:52:53 +08:00
filename = filepath.Clean(filename)
2022-01-12 21:09:00 +08:00
this.locker.Lock()
pool, ok := this.poolMap[filename]
if ok {
2022-12-05 11:16:04 +08:00
// 设置关闭状态
pool.SetClosing()
2022-01-12 21:09:00 +08:00
delete(this.poolMap, filename)
this.poolList.Remove(pool.linkItem)
_ = this.watcher.Remove(filename)
this.count -= pool.Len()
2023-10-11 21:51:05 +08:00
this.usedSize -= pool.usedSize
2022-01-12 21:09:00 +08:00
}
this.locker.Unlock()
// 在locker之外提升性能
if ok {
pool.Close()
}
}
func (this *OpenFileCache) CloseAll() {
this.locker.Lock()
for _, pool := range this.poolMap {
pool.Close()
}
this.poolMap = map[string]*OpenFilePool{}
this.poolList.Reset()
_ = this.watcher.Close()
2022-11-25 14:52:04 +08:00
this.count = 0
2023-10-11 21:51:05 +08:00
this.usedSize = 0
2022-01-12 21:09:00 +08:00
this.locker.Unlock()
}
2023-10-11 21:51:05 +08:00
func (this *OpenFileCache) SetCapacity(capacityBytes int64) {
this.capacitySize = capacityBytes
}
2022-01-12 21:09:00 +08:00
func (this *OpenFileCache) Debug() {
var ticker = time.NewTicker(5 * time.Second)
goman.New(func() {
for range ticker.C {
2023-10-11 21:51:05 +08:00
logs.Println("==== " + types.String(this.count) + ", " + fmt.Sprintf("%.4fMB", float64(this.usedSize)/(1<<20)) + " ====")
2023-10-03 21:38:45 +08:00
this.poolList.Range(func(item *linkedlist.Item[*OpenFilePool]) (goNext bool) {
logs.Println(filepath.Base(item.Value.Filename()), item.Value.Len())
2022-01-12 21:09:00 +08:00
return true
})
}
})
}
2023-10-11 21:51:05 +08:00
func (this *OpenFileCache) consumeHead() {
var delta = 1
if this.count > 100 {
delta = 2
}
for i := 0; i < delta; i++ {
var head = this.poolList.Head()
if head == nil {
break
}
var headPool = head.Value
headFile, consumed, consumedSize := headPool.Get()
if consumed {
this.count--
this.usedSize -= consumedSize
if headFile != nil {
_ = headFile.Close()
}
}
if headPool.Len() == 0 {
delete(this.poolMap, headPool.filename)
this.poolList.Remove(head)
_ = this.watcher.Remove(headPool.filename)
}
}
}