Repository: matrixorigin/matrixone
Pull request #8846 (2023-04-04 04:13:26)

fileservice: disk cache fixes


## What type of PR is this?

- [ ] API-change
- [x] BUG
- [ ] Improvement
- [ ] Documentation
- [ ] Feature
- [ ] Test and CI
- [ ] Code Refactoring

## Which issue(s) this PR fixes:

issue #8842 

## What this PR does / why we need it:
fileservice: add disk cache to MemoryFS

fileservice: ignore symlink error in DiskCache.Update

fileservice: add more disk cache eviction params


Patch:
diff --git a/etc/launch-tae-CN-tae-DN/cn.toml b/etc/launch-tae-CN-tae-DN/cn.toml
index af4988ef4..1c4a86f98 100644
--- a/etc/launch-tae-CN-tae-DN/cn.toml
+++ b/etc/launch-tae-CN-tae-DN/cn.toml
@@ -24,6 +24,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-CN-tae-DN/dn.toml b/etc/launch-tae-CN-tae-DN/dn.toml
index 50bff344f..7f6a44f18 100644
--- a/etc/launch-tae-CN-tae-DN/dn.toml
+++ b/etc/launch-tae-CN-tae-DN/dn.toml
@@ -24,6 +24,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-CN-tae-DN/log.toml b/etc/launch-tae-CN-tae-DN/log.toml
index 36b916f3e..5b9438830 100644
--- a/etc/launch-tae-CN-tae-DN/log.toml
+++ b/etc/launch-tae-CN-tae-DN/log.toml
@@ -20,6 +20,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-compose/config/cn-0.toml b/etc/launch-tae-compose/config/cn-0.toml
index e11159fa2..cd5ec436a 100644
--- a/etc/launch-tae-compose/config/cn-0.toml
+++ b/etc/launch-tae-compose/config/cn-0.toml
@@ -28,6 +28,8 @@ key-prefix = "server/data"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 backend = "MINIO"
diff --git a/etc/launch-tae-compose/config/cn-1.toml b/etc/launch-tae-compose/config/cn-1.toml
index e942f0f70..01c528dc2 100644
--- a/etc/launch-tae-compose/config/cn-1.toml
+++ b/etc/launch-tae-compose/config/cn-1.toml
@@ -28,6 +28,8 @@ key-prefix = "server/data"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 backend = "MINIO"
diff --git a/etc/launch-tae-compose/config/dn.toml b/etc/launch-tae-compose/config/dn.toml
index 1e4970ab5..9a1aae5be 100644
--- a/etc/launch-tae-compose/config/dn.toml
+++ b/etc/launch-tae-compose/config/dn.toml
@@ -23,6 +23,8 @@ key-prefix = "server/data"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 backend = "MINIO"
diff --git a/etc/launch-tae-compose/config/log.toml b/etc/launch-tae-compose/config/log.toml
index 9e194a00e..589515e8f 100644
--- a/etc/launch-tae-compose/config/log.toml
+++ b/etc/launch-tae-compose/config/log.toml
@@ -24,6 +24,8 @@ key-prefix = "server/data"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 backend = "MINIO"
diff --git a/etc/launch-tae-multi-CN-tae-DN/cn1.toml b/etc/launch-tae-multi-CN-tae-DN/cn1.toml
index 4cd70e4ff..1ff83e162 100644
--- a/etc/launch-tae-multi-CN-tae-DN/cn1.toml
+++ b/etc/launch-tae-multi-CN-tae-DN/cn1.toml
@@ -24,6 +24,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-multi-CN-tae-DN/cn2.toml b/etc/launch-tae-multi-CN-tae-DN/cn2.toml
index 892b32429..570ab1a78 100644
--- a/etc/launch-tae-multi-CN-tae-DN/cn2.toml
+++ b/etc/launch-tae-multi-CN-tae-DN/cn2.toml
@@ -24,6 +24,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-multi-CN-tae-DN/dn.toml b/etc/launch-tae-multi-CN-tae-DN/dn.toml
index 50bff344f..7f6a44f18 100644
--- a/etc/launch-tae-multi-CN-tae-DN/dn.toml
+++ b/etc/launch-tae-multi-CN-tae-DN/dn.toml
@@ -24,6 +24,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/etc/launch-tae-multi-CN-tae-DN/log.toml b/etc/launch-tae-multi-CN-tae-DN/log.toml
index a387d2fb3..888bc038f 100644
--- a/etc/launch-tae-multi-CN-tae-DN/log.toml
+++ b/etc/launch-tae-multi-CN-tae-DN/log.toml
@@ -20,6 +20,8 @@ data-dir = "mo-data/s3"
 memory-capacity = "512MB"
 disk-capacity = "8GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/pkg/fileservice/cache.go b/pkg/fileservice/cache.go
index f5dfd40d7..06b2da4bd 100644
--- a/pkg/fileservice/cache.go
+++ b/pkg/fileservice/cache.go
@@ -17,14 +17,33 @@ package fileservice
 import (
 	"context"
 	"io"
+	"time"
 
 	"github.com/matrixorigin/matrixone/pkg/util/toml"
 )
 
 type CacheConfig struct {
-	MemoryCapacity toml.ByteSize `toml:"memory-capacity"`
-	DiskPath       string        `toml:"disk-path"`
-	DiskCapacity   toml.ByteSize `toml:"disk-capacity"`
+	MemoryCapacity       toml.ByteSize `toml:"memory-capacity"`
+	DiskPath             string        `toml:"disk-path"`
+	DiskCapacity         toml.ByteSize `toml:"disk-capacity"`
+	DiskMinEvictInterval toml.Duration `toml:"disk-min-evict-interval"`
+	DiskEvictTarget      float64       `toml:"disk-evict-target"`
+}
+
+func (c *CacheConfig) SetDefaults() {
+	if c.DiskMinEvictInterval.Duration == 0 {
+		c.DiskMinEvictInterval.Duration = time.Minute * 7
+	}
+	if c.DiskEvictTarget == 0 {
+		c.DiskEvictTarget = 0.8
+	}
+}
+
+const DisableCacheCapacity = 1
+
+var DisabledCacheConfig = CacheConfig{
+	MemoryCapacity: DisableCacheCapacity,
+	DiskCapacity:   DisableCacheCapacity,
 }
 
 // VectorCache caches IOVector
diff --git a/pkg/fileservice/config.go b/pkg/fileservice/config.go
index d23dcddb1..dcae2f5ff 100644
--- a/pkg/fileservice/config.go
+++ b/pkg/fileservice/config.go
@@ -68,8 +68,12 @@ func NewFileService(cfg Config, perfCounterSets []*perfcounter.CounterSet) (File
 	}
 }
 
-func newMemFileService(cfg Config, _ []*perfcounter.CounterSet) (FileService, error) {
-	fs, err := NewMemoryFS(cfg.Name)
+func newMemFileService(cfg Config, perfCounters []*perfcounter.CounterSet) (FileService, error) {
+	fs, err := NewMemoryFS(
+		cfg.Name,
+		cfg.Cache,
+		perfCounters,
+	)
 	if err != nil {
 		return nil, err
 	}
@@ -83,9 +87,7 @@ func newDiskFileService(cfg Config, perfCounters []*perfcounter.CounterSet) (Fil
 	fs, err := NewLocalFS(
 		cfg.Name,
 		cfg.DataDir,
-		int64(cfg.Cache.MemoryCapacity),
-		int64(cfg.Cache.DiskCapacity),
-		cfg.Cache.DiskPath,
+		cfg.Cache,
 		perfCounters,
 	)
 	if err != nil {
@@ -112,9 +114,7 @@ func newMinioFileService(cfg Config, perfCounters []*perfcounter.CounterSet) (Fi
 		cfg.S3.Endpoint,
 		cfg.S3.Bucket,
 		cfg.S3.KeyPrefix,
-		int64(cfg.Cache.MemoryCapacity),
-		int64(cfg.Cache.DiskCapacity),
-		cfg.Cache.DiskPath,
+		cfg.Cache,
 		perfCounters,
 		false,
 	)
@@ -131,9 +131,7 @@ func newS3FileService(cfg Config, perfCounters []*perfcounter.CounterSet) (FileS
 		cfg.S3.Endpoint,
 		cfg.S3.Bucket,
 		cfg.S3.KeyPrefix,
-		int64(cfg.Cache.MemoryCapacity),
-		int64(cfg.Cache.DiskCapacity),
-		cfg.Cache.DiskPath,
+		cfg.Cache,
 		perfCounters,
 		false,
 	)
diff --git a/pkg/fileservice/disk_cache.go b/pkg/fileservice/disk_cache.go
index ef087d242..a5aeb5a22 100644
--- a/pkg/fileservice/disk_cache.go
+++ b/pkg/fileservice/disk_cache.go
@@ -19,6 +19,8 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"io/fs"
+	"math"
 	"math/rand"
 	"os"
 	"path/filepath"
@@ -33,6 +35,8 @@ import (
 
 type DiskCache struct {
 	capacity        int64
+	evictInterval   time.Duration
+	evictTarget     float64
 	path            string
 	fileExists      sync.Map
 	perfCounterSets []*perfcounter.CounterSet
@@ -44,26 +48,42 @@ type DiskCache struct {
 
 	evictState struct {
 		sync.Mutex
-		pending bool
+		timer        *time.Timer
+		newlyWritten int64
 	}
 }
 
 func NewDiskCache(
 	path string,
 	capacity int64,
+	evictInterval time.Duration,
+	evictTarget float64,
 	perfCounterSets []*perfcounter.CounterSet,
 ) (*DiskCache, error) {
+
+	if evictInterval == 0 {
+		panic("zero evict interval")
+	}
+	if evictTarget == 0 {
+		panic("zero evict target")
+	}
+	if evictTarget > 1 {
+		panic("evict target greater than 1")
+	}
+
 	err := os.MkdirAll(path, 0755)
 	if err != nil {
 		return nil, err
 	}
 	ret := &DiskCache{
 		capacity:        capacity,
+		evictInterval:   evictInterval,
+		evictTarget:     evictTarget,
 		path:            path,
 		perfCounterSets: perfCounterSets,
 	}
 	ret.settingContent.m = make(map[string]chan struct{})
-	ret.triggerEvict(context.TODO())
+	ret.triggerEvict(context.TODO(), 0)
 	return ret, nil
 }
 
@@ -265,7 +285,7 @@ func (d *DiskCache) Update(
 				numError++
 				continue // ignore error
 			}
-			d.triggerEvict(ctx)
+			d.triggerEvict(ctx, int64(n))
 			linkTarget = dataPath
 		}
 
@@ -274,7 +294,8 @@ func (d *DiskCache) Update(
 			if os.IsExist(err) {
 				// ok
 			} else {
-				return err
+				numError++
+				continue // ignore error
 			}
 		}
 
@@ -288,21 +309,59 @@ func (d *DiskCache) Flush() {
 	//TODO
 }
 
-func (d *DiskCache) triggerEvict(ctx context.Context) {
+func (d *DiskCache) triggerEvict(ctx context.Context, bytesWritten int64) {
 	d.evictState.Lock()
 	defer d.evictState.Unlock()
-	if d.evictState.pending {
+
+	d.evictState.newlyWritten += bytesWritten
+
+	if d.evictState.timer != nil {
+		// has pending eviction
+
+		newlyWrittenThreshold := int64(math.Ceil(float64(d.capacity) * (1 - d.evictTarget)))
+		if newlyWrittenThreshold > 0 && d.evictState.newlyWritten >= newlyWrittenThreshold {
+			if d.evictState.timer.Stop() {
+				// evict immediately
+				logutil.Info("disk cache: newly written bytes may excceeds eviction target, start immediately",
+					zap.Any("newly-written", d.evictState.newlyWritten),
+					zap.Any("newly-written-threshold", newlyWrittenThreshold),
+				)
+				// timer stopped, start eviction
+				// before the following eviction is end, further calls of triggerEvict will not go this path, since timer.Stop will return false
+				perfcounter.Update(ctx, func(set *perfcounter.CounterSet) {
+					set.FileService.Cache.Disk.EvictImmediately.Add(1)
+				}, d.perfCounterSets...)
+				go func() {
+					defer func() {
+						d.evictState.Lock()
+						d.evictState.timer = nil
+						d.evictState.newlyWritten = 0
+						d.evictState.Unlock()
+					}()
+					d.evict(ctx)
+				}()
+			}
+			return
+		}
+
 		return
 	}
-	d.evictState.pending = true
-	time.AfterFunc(time.Minute*7, func() {
-		defer func() {
-			d.evictState.Lock()
-			d.evictState.pending = false
-			d.evictState.Unlock()
-		}()
-		d.evict(ctx)
-	})
+
+	perfcounter.Update(ctx, func(set *perfcounter.CounterSet) {
+		set.FileService.Cache.Disk.EvictPending.Add(1)
+	}, d.perfCounterSets...)
+	d.evictState.timer = time.AfterFunc(
+		d.evictInterval,
+		func() {
+			defer func() {
+				d.evictState.Lock()
+				d.evictState.timer = nil
+				d.evictState.newlyWritten = 0
+				d.evictState.Unlock()
+			}()
+			d.evict(ctx)
+		},
+	)
 }
 
 func (d *DiskCache) evict(ctx context.Context) {
@@ -338,14 +397,15 @@ func (d *DiskCache) evict(ctx context.Context) {
 			perfcounter.Update(ctx, func(set *perfcounter.CounterSet) {
 				set.FileService.Cache.Disk.Evict.Add(numDeleted)
 			}, d.perfCounterSets...)
-			logutil.Info("disk info eviction",
+			logutil.Info("disk cache: eviction finished",
 				zap.Any("files", numDeleted),
 				zap.Any("bytes", bytesDeleted),
 			)
 		}
 	}()
+	target := int64(float64(d.capacity) * d.evictTarget)
 	for path, size := range paths {
-		if sumSize <= d.capacity {
+		if sumSize <= target {
 			break
 		}
 		if err := os.Remove(path); err != nil {
@@ -454,13 +514,18 @@ func (d *DiskCache) newFileContentWriter(filePath string) (w io.Writer, done fun
 	var doneOnce sync.Once
 	return f, func(ctx context.Context) (err error) {
 		doneOnce.Do(func() {
+			var info fs.FileInfo
+			info, err = f.Stat()
+			if err != nil {
+				return
+			}
 			if err = f.Close(); err != nil {
 				return
 			}
 			if err = os.Rename(tmpPath, contentPath); err != nil {
 				return
 			}
-			d.triggerEvict(ctx)
+			d.triggerEvict(ctx, info.Size())
 		})
 		return
 	}, f.Close, nil
diff --git a/pkg/fileservice/local_fs.go b/pkg/fileservice/local_fs.go
index c7f3e4fe8..f5ef8db29 100644
--- a/pkg/fileservice/local_fs.go
+++ b/pkg/fileservice/local_fs.go
@@ -57,9 +57,7 @@ const (
 func NewLocalFS(
 	name string,
 	rootPath string,
-	memCacheCapacity int64,
-	diskCacheCapacity int64,
-	diskCachePath string,
+	cacheConfig CacheConfig,
 	perfCounterSets []*perfcounter.CounterSet,
 ) (*LocalFS, error) {
 
@@ -115,35 +113,28 @@ func NewLocalFS(
 		perfCounterSets: perfCounterSets,
 	}
 
-	if err := fs.initCaches(
-		memCacheCapacity,
-		diskCacheCapacity,
-		diskCachePath,
-	); err != nil {
+	if err := fs.initCaches(cacheConfig); err != nil {
 		return nil, err
 	}
 
 	return fs, nil
 }
 
-func (l *LocalFS) initCaches(
-	memCacheCapacity int64,
-	diskCacheCapacity int64,
-	diskCachePath string,
-) error {
+func (l *LocalFS) initCaches(config CacheConfig) error {
+	config.SetDefaults()
 
-	if memCacheCapacity > 0 {
+	if config.MemoryCapacity > DisableCacheCapacity { // 1 means disable
 		l.memCache = NewMemCache(
-			WithLRU(memCacheCapacity),
+			WithLRU(int64(config.MemoryCapacity)),
 			WithPerfCounterSets(l.perfCounterSets),
 		)
 		logutil.Info("fileservice: memory cache initialized",
 			zap.Any("fs-name", l.name),
-			zap.Any("capacity", memCacheCapacity),
+			zap.Any("config", config),
 		)
 	}
 
-	//if diskCacheCapacity > 0 && diskCachePath != "" {
+	//if diskCacheCapacity > DisableCacheCapacity && diskCachePath != "" {
 	//	var err error
 	//	l.diskCache, err = NewDiskCache(
 	//		diskCachePath,
diff --git a/pkg/fileservice/memory_fs.go b/pkg/fileservice/memory_fs.go
index d2904bd90..fe6c7e9e7 100644
--- a/pkg/fileservice/memory_fs.go
+++ b/pkg/fileservice/memory_fs.go
@@ -24,6 +24,7 @@ import (
 	"sync"
 
 	"github.com/matrixorigin/matrixone/pkg/common/moerr"
+	"github.com/matrixorigin/matrixone/pkg/perfcounter"
 	"github.com/tidwall/btree"
 )
 
@@ -31,18 +32,46 @@ import (
 type MemoryFS struct {
 	name string
 	sync.RWMutex
-	tree *btree.BTreeG[*_MemFSEntry]
+	tree            *btree.BTreeG[*_MemFSEntry]
+	diskCache       *DiskCache
+	perfCounterSets []*perfcounter.CounterSet
+	asyncUpdate     bool
 }
 
 var _ FileService = new(MemoryFS)
 
-func NewMemoryFS(name string) (*MemoryFS, error) {
-	return &MemoryFS{
+func NewMemoryFS(
+	name string,
+	cacheConfig CacheConfig,
+	perfCounterSets []*perfcounter.CounterSet,
+) (*MemoryFS, error) {
+
+	fs := &MemoryFS{
 		name: name,
 		tree: btree.NewBTreeG(func(a, b *_MemFSEntry) bool {
 			return a.FilePath < b.FilePath
 		}),
-	}, nil
+		perfCounterSets: perfCounterSets,
+	}
+
+	//if diskCacheCapacity > DisableCacheCapacity && diskCachePath != "" {
+	//	var err error
+	//	fs.diskCache, err = NewDiskCache(
+	//		diskCachePath,
+	//		diskCacheCapacity,
+	//		fs.perfCounterSets,
+	//	)
+	//	if err != nil {
+	//		return nil, err
+	//	}
+	//	logutil.Info("fileservice: disk cache initialized",
+	//		zap.Any("fs-name", fs.name),
+	//		zap.Any("capacity", diskCacheCapacity),
+	//		zap.Any("path", diskCachePath),
+	//	)
+	//}
+
+	return fs, nil
 }
 
 func (m *MemoryFS) Name() string {
@@ -160,13 +189,25 @@ func (m *MemoryFS) write(ctx context.Context, vector IOVector) error {
 	return nil
 }
 
-func (m *MemoryFS) Read(ctx context.Context, vector *IOVector) error {
+func (m *MemoryFS) Read(ctx context.Context, vector *IOVector) (err error) {
 	select {
 	case <-ctx.Done():
 		return ctx.Err()
 	default:
 	}
 
+	if m.diskCache != nil {
+		if err := m.diskCache.Read(ctx, vector); err != nil {
+			return err
+		}
+		defer func() {
+			if err != nil {
+				return
+			}
+			err = m.diskCache.Update(ctx, vector, m.asyncUpdate)
+		}()
+	}
+
 	path, err := ParsePathAtService(vector.FilePath, m.name)
 	if err != nil {
 		return err
diff --git a/pkg/fileservice/s3_fs.go b/pkg/fileservice/s3_fs.go
index c4824fecb..c65d34819 100644
--- a/pkg/fileservice/s3_fs.go
+++ b/pkg/fileservice/s3_fs.go
@@ -72,9 +72,7 @@ func NewS3FS(
 	endpoint string,
 	bucket string,
 	keyPrefix string,
-	memCacheCapacity int64,
-	diskCacheCapacity int64,
-	diskCachePath string,
+	cacheConfig CacheConfig,
 	perfCounterSets []*perfcounter.CounterSet,
 	noCache bool,
 ) (*S3FS, error) {
@@ -93,11 +91,7 @@ func NewS3FS(
 	fs.perfCounterSets = perfCounterSets
 
 	if !noCache {
-		if err := fs.initCaches(
-			memCacheCapacity,
-			diskCacheCapacity,
-			diskCachePath,
-		); err != nil {
+		if err := fs.initCaches(cacheConfig); err != nil {
 			return nil, err
 		}
 	}
@@ -113,9 +107,7 @@ func NewS3FSOnMinio(
 	endpoint string,
 	bucket string,
 	keyPrefix string,
-	memCacheCapacity int64,
-	diskCacheCapacity int64,
-	diskCachePath string,
+	cacheConfig CacheConfig,
 	perfCounterSets []*perfcounter.CounterSet,
 	noCache bool,
 ) (*S3FS, error) {
@@ -135,11 +127,7 @@ func NewS3FSOnMinio(
 	fs.perfCounterSets = perfCounterSets
 
 	if !noCache {
-		if err := fs.initCaches(
-			memCacheCapacity,
-			diskCacheCapacity,
-			diskCachePath,
-		); err != nil {
+		if err := fs.initCaches(cacheConfig); err != nil {
 			return nil, err
 		}
 	}
@@ -147,36 +135,35 @@ func NewS3FSOnMinio(
 	return fs, nil
 }
 
-func (s *S3FS) initCaches(
-	memCacheCapacity int64,
-	diskCacheCapacity int64,
-	diskCachePath string,
-) error {
+func (s *S3FS) initCaches(config CacheConfig) error {
+	config.SetDefaults()
 
 	// memory cache
-	if memCacheCapacity == 0 {
-		memCacheCapacity = 512 << 20
+	if config.MemoryCapacity == 0 {
+		config.MemoryCapacity = 512 << 20
 	}
-	if memCacheCapacity > 0 {
+	if config.MemoryCapacity > DisableCacheCapacity {
 		s.memCache = NewMemCache(
-			WithLRU(memCacheCapacity),
+			WithLRU(int64(config.MemoryCapacity)),
 			WithPerfCounterSets(s.perfCounterSets),
 		)
 		logutil.Info("fileservice: memory cache initialized",
 			zap.Any("fs-name", s.name),
-			zap.Any("capacity", memCacheCapacity),
+			zap.Any("capacity", config.MemoryCapacity),
 		)
 	}
 
 	// disk cache
-	if diskCacheCapacity == 0 {
-		diskCacheCapacity = 8 << 30
+	if config.DiskCapacity == 0 {
+		config.DiskCapacity = 8 << 30
 	}
-	if diskCacheCapacity > 0 && diskCachePath != "" {
+	if config.DiskCapacity > DisableCacheCapacity && config.DiskPath != "" {
 		var err error
 		s.diskCache, err = NewDiskCache(
-			diskCachePath,
-			diskCacheCapacity,
+			config.DiskPath,
+			int64(config.DiskCapacity),
+			config.DiskMinEvictInterval.Duration,
+			config.DiskEvictTarget,
 			s.perfCounterSets,
 		)
 		if err != nil {
@@ -184,8 +171,7 @@ func (s *S3FS) initCaches(
 		}
 		logutil.Info("fileservice: disk cache initialized",
 			zap.Any("fs-name", s.name),
-			zap.Any("capacity", diskCacheCapacity),
-			zap.Any("path", diskCachePath),
+			zap.Any("config", config),
 		)
 	}
 
diff --git a/pkg/perfcounter/counter_set.go b/pkg/perfcounter/counter_set.go
index cbebb94ba..d9cb4a3c9 100644
--- a/pkg/perfcounter/counter_set.go
+++ b/pkg/perfcounter/counter_set.go
@@ -45,14 +45,16 @@ type FileServiceCounterSet struct {
 			Hit  stats.Counter
 		}
 		Disk struct {
-			Read           stats.Counter
-			Hit            stats.Counter
-			GetFileContent stats.Counter
-			SetFileContent stats.Counter
-			OpenFile       stats.Counter
-			StatFile       stats.Counter
-			Error          stats.Counter
-			Evict          stats.Counter
+			Read             stats.Counter
+			Hit              stats.Counter
+			GetFileContent   stats.Counter
+			SetFileContent   stats.Counter
+			OpenFile         stats.Counter
+			StatFile         stats.Counter
+			Error            stats.Counter
+			Evict            stats.Counter
+			EvictPending     stats.Counter
+			EvictImmediately stats.Counter
 		}
 	}
 


Test patch:
diff --git a/etc/dn-minimal-s3-test.toml b/etc/dn-minimal-s3-test.toml
index 72c2be3f9..54cc668f0 100644
--- a/etc/dn-minimal-s3-test.toml
+++ b/etc/dn-minimal-s3-test.toml
@@ -34,6 +34,8 @@ key-prefix = "s3 key preifx"
 memory-capacity = "128MB"
 disk-capacity = "1GB"
 disk-path = "mo-data/file-service-cache"
+disk-min-evict-interval = "7m"
+disk-evict-target = 0.8
 
 [[fileservice]]
 name = "ETL"
diff --git a/pkg/cnservice/server_metadata_test.go b/pkg/cnservice/server_metadata_test.go
index 64b438c0f..6a8a478d6 100644
--- a/pkg/cnservice/server_metadata_test.go
+++ b/pkg/cnservice/server_metadata_test.go
@@ -27,7 +27,7 @@ import (
 )
 
 func TestInitMetadata(t *testing.T) {
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 
 	s := &service{logger: logutil.GetPanicLogger(), metadataFS: fs}
@@ -41,7 +41,7 @@ func TestInitMetadata(t *testing.T) {
 }
 
 func TestInitMetadataWithExistData(t *testing.T) {
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 	value := metadata.CNStore{
 		UUID: "cn1",
@@ -63,7 +63,7 @@ func TestInitMetadataWithInvalidUUIDWillPanic(t *testing.T) {
 		assert.Fail(t, "must panic")
 	}()
 
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 	value := metadata.CNStore{
 		UUID: "cn1",
diff --git a/pkg/dnservice/store_metadata_test.go b/pkg/dnservice/store_metadata_test.go
index 109326e21..a1abdc542 100644
--- a/pkg/dnservice/store_metadata_test.go
+++ b/pkg/dnservice/store_metadata_test.go
@@ -27,7 +27,7 @@ import (
 )
 
 func TestInitMetadata(t *testing.T) {
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 
 	s := &store{rt: runtime.DefaultRuntime(), metadataFileService: fs}
@@ -43,7 +43,7 @@ func TestInitMetadata(t *testing.T) {
 }
 
 func TestInitMetadataWithExistData(t *testing.T) {
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 	value := metadata.DNStore{
 		UUID: "dn1",
@@ -82,7 +82,7 @@ func TestInitMetadataWithInvalidUUIDWillPanic(t *testing.T) {
 		assert.Fail(t, "must panic")
 	}()
 
-	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 	value := metadata.DNStore{
 		UUID: "dn1",
diff --git a/pkg/dnservice/store_test.go b/pkg/dnservice/store_test.go
index dcb588ac5..d37dfec1a 100644
--- a/pkg/dnservice/store_test.go
+++ b/pkg/dnservice/store_test.go
@@ -59,11 +59,11 @@ func TestAddReplica(t *testing.T) {
 }
 
 func TestStartWithReplicas(t *testing.T) {
-	localFS, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	localFS, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 
 	factory := func(name string) (*fileservice.FileServices, error) {
-		s3fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName)
+		s3fs, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil)
 		if err != nil {
 			return nil, err
 		}
@@ -145,18 +145,21 @@ func runDNStoreTest(
 	runDNStoreTestWithFileServiceFactory(t, testFn, func(name string) (*fileservice.FileServices, error) {
 		local, err := fileservice.NewMemoryFS(
 			defines.LocalFileServiceName,
+			fileservice.DisabledCacheConfig, nil,
 		)
 		if err != nil {
 			return nil, err
 		}
 		s3, err := fileservice.NewMemoryFS(
 			defines.SharedFileServiceName,
+			fileservice.DisabledCacheConfig, nil,
 		)
 		if err != nil {
 			return nil, err
 		}
 		etl, err := fileservice.NewMemoryFS(
 			defines.ETLFileServiceName,
+			fileservice.DisabledCacheConfig, nil,
 		)
 		if err != nil {
 			return nil, err
@@ -186,7 +189,7 @@ func runDNStoreTestWithFileServiceFactory(
 
 	if fsFactory == nil {
 		fsFactory = func(name string) (*fileservice.FileServices, error) {
-			fs, err := fileservice.NewMemoryFS(name)
+			fs, err := fileservice.NewMemoryFS(name, fileservice.DisabledCacheConfig, nil)
 			if err != nil {
 				return nil, err
 			}
diff --git a/pkg/fileservice/disk_cache_test.go b/pkg/fileservice/disk_cache_test.go
index dce338e80..13dfeab58 100644
--- a/pkg/fileservice/disk_cache_test.go
+++ b/pkg/fileservice/disk_cache_test.go
@@ -21,6 +21,7 @@ import (
 	"io"
 	"sync"
 	"testing"
+	"time"
 
 	"github.com/matrixorigin/matrixone/pkg/perfcounter"
 	"github.com/stretchr/testify/assert"
@@ -31,7 +32,7 @@ func TestDiskCache(t *testing.T) {
 	ctx := context.Background()
 
 	// new
-	cache, err := NewDiskCache(dir, 1024, nil)
+	cache, err := NewDiskCache(dir, 1024, time.Second, 1, nil)
 	assert.Nil(t, err)
 
 	// update
@@ -109,12 +110,12 @@ func TestDiskCache(t *testing.T) {
 	testRead(cache)
 
 	// new cache instance and read
-	cache, err = NewDiskCache(dir, 1024, nil)
+	cache, err = NewDiskCache(dir, 1024, time.Second, 1, nil)
 	assert.Nil(t, err)
 	testRead(cache)
 
 	// new cache instance and update
-	cache, err = NewDiskCache(dir, 1024, nil)
+	cache, err = NewDiskCache(dir, 1024, time.Second, 1, nil)
 	assert.Nil(t, err)
 	testUpdate(cache)
 
@@ -127,7 +128,7 @@ func TestDiskCachePreload(t *testing.T) {
 	ctx = perfcounter.WithCounterSet(ctx, &counterSet)
 
 	// new
-	cache, err := NewDiskCache(dir, 1024, nil)
+	cache, err := NewDiskCache(dir, 1024, time.Second, 1, nil)
 	assert.Nil(t, err)
 
 	// set content
@@ -212,7 +213,7 @@ func TestDiskCacheConcurrentSetFileContent(t *testing.T) {
 	ctx = perfcounter.WithCounterSet(ctx, &counterSet)
 
 	// new
-	cache, err := NewDiskCache(dir, 1024, nil)
+	cache, err := NewDiskCache(dir, 1024, time.Second, 1, nil)
 	assert.Nil(t, err)
 
 	n := 128
@@ -243,7 +244,7 @@ func TestDiskCacheEviction(t *testing.T) {
 	ctx = perfcounter.WithCounterSet(ctx, &counterSet)
 
 	// new
-	cache, err := NewDiskCache(dir, 3, nil)
+	cache, err := NewDiskCache(dir, 3, time.Second, 1, nil)
 	assert.Nil(t, err)
 
 	n := 128
@@ -270,3 +271,34 @@ func TestDiskCacheEviction(t *testing.T) {
 	cache.evict(ctx)
 	assert.True(t, counterSet.FileService.Cache.Disk.Evict.Load() > 0)
 }
+
+func TestImmediatelyEviction(t *testing.T) {
+	dir := t.TempDir()
+	ctx := context.Background()
+	var counterSet perfcounter.CounterSet
+	ctx = perfcounter.WithCounterSet(ctx, &counterSet)
+
+	evictInterval := time.Hour * 1
+	cache, err := NewDiskCache(dir, 3, evictInterval, 0.8, nil)
+	assert.Nil(t, err)
+
+	err = cache.SetFileContent(ctx, "a", func(_ context.Context, vec *IOVector) error {
+		_, err := vec.Entries[0].WriterForRead.Write([]byte("foo"))
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	assert.Nil(t, err)
+
+	err = cache.SetFileContent(ctx, "b", func(_ context.Context, vec *IOVector) error {
+		_, err := vec.Entries[0].WriterForRead.Write([]byte("foo"))
+		if err != nil {
+			return err
+		}
+		return nil
+	})
+	assert.Nil(t, err)
+
+	assert.Equal(t, int64(1), counterSet.FileService.Cache.Disk.EvictImmediately.Load())
+}
diff --git a/pkg/fileservice/example_test.go b/pkg/fileservice/example_test.go
index 9bd2d03fc..b5349f2f3 100644
--- a/pkg/fileservice/example_test.go
+++ b/pkg/fileservice/example_test.go
@@ -29,9 +29,9 @@ func TestCacheWithRCExample(t *testing.T) {
 	fs, err := NewLocalFS(
 		"rc",
 		dir,
-		32<<20,
-		0,
-		"",
+		CacheConfig{
+			MemoryCapacity: 32 << 20,
+		},
 		nil,
 	)
 	assert.Nil(t, err)
@@ -81,9 +81,9 @@ func TestCacheWithReleasableExample(t *testing.T) {
 	fs, err := NewLocalFS(
 		"rc",
 		dir,
-		32<<20,
-		0,
-		"",
+		CacheConfig{
+			MemoryCapacity: 32 << 20,
+		},
 		nil,
 	)
 	assert.Nil(t, err)
diff --git a/pkg/fileservice/file_services_test.go b/pkg/fileservice/file_services_test.go
index c04afc9f0..7ef70059f 100644
--- a/pkg/fileservice/file_services_test.go
+++ b/pkg/fileservice/file_services_test.go
@@ -25,7 +25,7 @@ func TestFileServices(t *testing.T) {
 	t.Run("file service", func(t *testing.T) {
 		testFileService(t, func(name string) FileService {
 			dir := t.TempDir()
-			fs, err := NewLocalFS(name, dir, 0, -1, "", nil)
+			fs, err := NewLocalFS(name, dir, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			fs2, err := NewFileServices(name, fs)
 			assert.Nil(t, err)
@@ -35,9 +35,9 @@ func TestFileServices(t *testing.T) {
 }
 
 func TestFileServicesNameCaseInsensitive(t *testing.T) {
-	fs1, err := NewMemoryFS("foo")
+	fs1, err := NewMemoryFS("foo", DisabledCacheConfig, nil)
 	assert.Nil(t, err)
-	fs2, err := NewMemoryFS("FOO")
+	fs2, err := NewMemoryFS("FOO", DisabledCacheConfig, nil)
 	assert.Nil(t, err)
 	_, err = NewFileServices(fs1.Name(), fs1, fs2)
 	assert.True(t, moerr.IsMoErrCode(err, moerr.ErrDupServiceName))
diff --git a/pkg/fileservice/local_fs_test.go b/pkg/fileservice/local_fs_test.go
index 558ec802f..36c6ee008 100644
--- a/pkg/fileservice/local_fs_test.go
+++ b/pkg/fileservice/local_fs_test.go
@@ -25,7 +25,7 @@ func TestLocalFS(t *testing.T) {
 	t.Run("file service", func(t *testing.T) {
 		testFileService(t, func(name string) FileService {
 			dir := t.TempDir()
-			fs, err := NewLocalFS(name, dir, -1, -1, "", nil)
+			fs, err := NewLocalFS(name, dir, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -34,7 +34,7 @@ func TestLocalFS(t *testing.T) {
 	t.Run("mutable file service", func(t *testing.T) {
 		testMutableFileService(t, func() MutableFileService {
 			dir := t.TempDir()
-			fs, err := NewLocalFS("local", dir, -1, -1, "", nil)
+			fs, err := NewLocalFS("local", dir, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -43,7 +43,7 @@ func TestLocalFS(t *testing.T) {
 	t.Run("replaceable file service", func(t *testing.T) {
 		testReplaceableFileService(t, func() ReplaceableFileService {
 			dir := t.TempDir()
-			fs, err := NewLocalFS("local", dir, -1, -1, "", nil)
+			fs, err := NewLocalFS("local", dir, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -52,7 +52,9 @@ func TestLocalFS(t *testing.T) {
 	t.Run("caching file service", func(t *testing.T) {
 		testCachingFileService(t, func() CachingFileService {
 			dir := t.TempDir()
-			fs, err := NewLocalFS("local", dir, 128*1024, -1, "", nil)
+			fs, err := NewLocalFS("local", dir, CacheConfig{
+				MemoryCapacity: 128 * 1024,
+			}, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -63,7 +65,7 @@ func TestLocalFS(t *testing.T) {
 func BenchmarkLocalFS(b *testing.B) {
 	benchmarkFileService(b, func() FileService {
 		dir := b.TempDir()
-		fs, err := NewLocalFS("local", dir, -1, -1, "", nil)
+		fs, err := NewLocalFS("local", dir, DisabledCacheConfig, nil)
 		assert.Nil(b, err)
 		return fs
 	})
diff --git a/pkg/fileservice/mem_cache_test.go b/pkg/fileservice/mem_cache_test.go
index 5d6592440..24ec38ac7 100644
--- a/pkg/fileservice/mem_cache_test.go
+++ b/pkg/fileservice/mem_cache_test.go
@@ -25,7 +25,7 @@ import (
 func TestMemCacheLeak(t *testing.T) {
 	ctx := context.Background()
 
-	fs, err := NewMemoryFS("test")
+	fs, err := NewMemoryFS("test", DisabledCacheConfig, nil)
 	assert.Nil(t, err)
 	err = fs.Write(ctx, IOVector{
 		FilePath: "foo",
diff --git a/pkg/fileservice/memory_fs_test.go b/pkg/fileservice/memory_fs_test.go
index b83507a93..0a3ca9f55 100644
--- a/pkg/fileservice/memory_fs_test.go
+++ b/pkg/fileservice/memory_fs_test.go
@@ -24,7 +24,7 @@ func TestMemoryFS(t *testing.T) {
 
 	t.Run("file service", func(t *testing.T) {
 		testFileService(t, func(name string) FileService {
-			fs, err := NewMemoryFS(name)
+			fs, err := NewMemoryFS(name, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -32,7 +32,7 @@ func TestMemoryFS(t *testing.T) {
 
 	t.Run("replaceable file service", func(t *testing.T) {
 		testReplaceableFileService(t, func() ReplaceableFileService {
-			fs, err := NewMemoryFS("memory")
+			fs, err := NewMemoryFS("memory", DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return fs
 		})
@@ -42,7 +42,7 @@ func TestMemoryFS(t *testing.T) {
 
 func BenchmarkMemoryFS(b *testing.B) {
 	benchmarkFileService(b, func() FileService {
-		fs, err := NewMemoryFS("memory")
+		fs, err := NewMemoryFS("memory", DisabledCacheConfig, nil)
 		assert.Nil(b, err)
 		return fs
 	})
diff --git a/pkg/fileservice/profile_test.go b/pkg/fileservice/profile_test.go
index aa4986fcd..c67b7713b 100644
--- a/pkg/fileservice/profile_test.go
+++ b/pkg/fileservice/profile_test.go
@@ -26,7 +26,7 @@ func TestProfile(t *testing.T) {
 	defer stop()
 	testFileService(t, func(name string) FileService {
 		dir := t.TempDir()
-		fs, err := NewLocalFS(name, dir, -1, -1, "", nil)
+		fs, err := NewLocalFS(name, dir, DisabledCacheConfig, nil)
 		assert.Nil(t, err)
 		return fs
 	})
diff --git a/pkg/fileservice/s3_fs_test.go b/pkg/fileservice/s3_fs_test.go
index eb727b737..8018a000d 100644
--- a/pkg/fileservice/s3_fs_test.go
+++ b/pkg/fileservice/s3_fs_test.go
@@ -98,9 +98,7 @@ func TestS3FS(t *testing.T) {
 				config.Endpoint,
 				config.Bucket,
 				time.Now().Format("2006-01-02.15:04:05.000000"),
-				-1,
-				-1,
-				"",
+				DisabledCacheConfig,
 				nil,
 				true,
 			)
@@ -118,9 +116,7 @@ func TestS3FS(t *testing.T) {
 			config.Endpoint,
 			config.Bucket,
 			"",
-			-1,
-			-1,
-			"",
+			DisabledCacheConfig,
 			nil,
 			true,
 		)
@@ -144,9 +140,9 @@ func TestS3FS(t *testing.T) {
 				config.Endpoint,
 				config.Bucket,
 				time.Now().Format("2006-01-02.15:04:05.000000"),
-				128*1024,
-				-1,
-				"",
+				CacheConfig{
+					MemoryCapacity: 128 * 1024,
+				},
 				nil,
 				false,
 			)
@@ -163,9 +159,11 @@ func TestS3FS(t *testing.T) {
 				config.Endpoint,
 				config.Bucket,
 				time.Now().Format("2006-01-02.15:04:05.000000"),
-				-1,
-				128*1024,
-				t.TempDir(),
+				CacheConfig{
+					MemoryCapacity: 1,
+					DiskCapacity:   128 * 1024,
+					DiskPath:       t.TempDir(),
+				},
 				nil,
 				false,
 			)
@@ -418,9 +416,9 @@ func TestS3FSMinioServer(t *testing.T) {
 				endpoint,
 				"test",
 				time.Now().Format("2006-01-02.15:04:05.000000"),
-				-1,
-				-1,
-				cacheDir,
+				CacheConfig{
+					DiskPath: cacheDir,
+				},
 				nil,
 				true,
 			)
@@ -455,9 +453,9 @@ func BenchmarkS3FS(b *testing.B) {
 			config.Endpoint,
 			config.Bucket,
 			time.Now().Format("2006-01-02.15:04:05.000000"),
-			-1,
-			-1,
-			cacheDir,
+			CacheConfig{
+				DiskPath: cacheDir,
+			},
 			nil,
 			true,
 		)
diff --git a/pkg/fileservice/sub_path_test.go b/pkg/fileservice/sub_path_test.go
index a1967f226..6e4830686 100644
--- a/pkg/fileservice/sub_path_test.go
+++ b/pkg/fileservice/sub_path_test.go
@@ -23,7 +23,7 @@ import (
 func TestSubPathFS(t *testing.T) {
 	t.Run("file service", func(t *testing.T) {
 		testFileService(t, func(name string) FileService {
-			upstream, err := NewMemoryFS(name)
+			upstream, err := NewMemoryFS(name, DisabledCacheConfig, nil)
 			assert.Nil(t, err)
 			return SubPath(upstream, "foo")
 		})
diff --git a/pkg/logservice/testclient.go b/pkg/logservice/testclient.go
index 0a10a6422..7e6531d00 100644
--- a/pkg/logservice/testclient.go
+++ b/pkg/logservice/testclient.go
@@ -67,15 +67,15 @@ func NewTestService(fs vfs.FS) (*Service, ClientConfig, error) {
 }
 
 func newFS() *fileservice.FileServices {
-	local, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	local, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
-	s3, err := fileservice.NewMemoryFS(defines.SharedFileServiceName)
+	s3, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
-	etl, err := fileservice.NewMemoryFS(defines.ETLFileServiceName)
+	etl, err := fileservice.NewMemoryFS(defines.ETLFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/pkg/tests/service/fileservice.go b/pkg/tests/service/fileservice.go
index ee5838e0a..7a095b6d4 100644
--- a/pkg/tests/service/fileservice.go
+++ b/pkg/tests/service/fileservice.go
@@ -46,13 +46,13 @@ func (c *testCluster) buildFileServices() *fileServices {
 	cnServiceNum := c.opt.initial.cnServiceNum
 
 	factory := func(_ string, name string) fileservice.FileService {
-		fs, err := fileservice.NewMemoryFS(name)
+		fs, err := fileservice.NewMemoryFS(name, fileservice.DisabledCacheConfig, nil)
 		require.NoError(c.t, err)
 		return fs
 	}
 	if c.opt.keepData {
 		factory = func(dir string, name string) fileservice.FileService {
-			fs, err := fileservice.NewLocalFS(name, filepath.Join(dir, name), 0, 0, "", nil)
+			fs, err := fileservice.NewLocalFS(name, filepath.Join(dir, name), fileservice.CacheConfig{}, nil)
 			require.NoError(c.t, err)
 			return fs
 		}
diff --git a/pkg/testutil/util_new.go b/pkg/testutil/util_new.go
index ac6df301d..f216d068a 100644
--- a/pkg/testutil/util_new.go
+++ b/pkg/testutil/util_new.go
@@ -59,15 +59,15 @@ func NewProcessWithMPool(mp *mpool.MPool) *process.Process {
 var NewProc = NewProcess
 
 func NewFS() *fileservice.FileServices {
-	local, err := fileservice.NewMemoryFS(defines.LocalFileServiceName)
+	local, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
-	s3, err := fileservice.NewMemoryFS(defines.SharedFileServiceName)
+	s3, err := fileservice.NewMemoryFS(defines.SharedFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
-	etl, err := fileservice.NewMemoryFS(defines.ETLFileServiceName)
+	etl, err := fileservice.NewMemoryFS(defines.ETLFileServiceName, fileservice.DisabledCacheConfig, nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/pkg/util/export/etl/csv_test.go b/pkg/util/export/etl/csv_test.go
index 27df33aac..499a79b92 100644
--- a/pkg/util/export/etl/csv_test.go
+++ b/pkg/util/export/etl/csv_test.go
@@ -38,7 +38,9 @@ func TestLocalFSWriter(t *testing.T) {
 	t.Logf("whereami: %s, %s", selfDir, basedir)
 
 	require.Equal(t, nil, err)
-	fs, err := fileservice.NewLocalFS("test", path.Join(basedir, "system"), mpool.MB, -1, "", nil)
+	fs, err := fileservice.NewLocalFS("test", path.Join(basedir, "system"), fileservice.CacheConfig{
+		MemoryCapacity: mpool.MB,
+	}, nil)
 	require.Equal(t, nil, err)
 	ctx := context.Background()
 	// csv_test.go:23: whereami: /private/var/folders/lw/05zz3bq12djbnhv1wyzk2jgh0000gn/T/GoLand
diff --git a/pkg/util/file/file_test.go b/pkg/util/file/file_test.go
index e9dda842a..4d4f44127 100644
--- a/pkg/util/file/file_test.go
+++ b/pkg/util/file/file_test.go
@@ -27,7 +27,7 @@ var (
 )
 
 func TestReadAndWriteFile(t *testing.T) {
-	fs, err := fileservice.NewMemoryFS(testPath)
+	fs, err := fileservice.NewMemoryFS(testPath, fileservice.DisabledCacheConfig, nil)
 	assert.NoError(t, err)
 
 	v, err := ReadFile(fs, testFile)