Skip to content

Commit 432a3b9

Browse files
committed
fix: add VolumeStats cache to avoid massive statfs calls
1 parent cab0f64 commit 432a3b9

File tree

4 files changed

+64
-27
lines changed

4 files changed

+64
-27
lines changed

cmd/nfsplugin/main.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ import (
2626
)
2727

2828
var (
29-
endpoint = flag.String("endpoint", "unix://tmp/csi.sock", "CSI endpoint")
30-
nodeID = flag.String("nodeid", "", "node id")
31-
mountPermissions = flag.Uint64("mount-permissions", 0, "mounted folder permissions")
32-
driverName = flag.String("drivername", nfs.DefaultDriverName, "name of the driver")
33-
workingMountDir = flag.String("working-mount-dir", "/tmp", "working directory for provisioner to mount nfs shares temporarily")
34-
defaultOnDeletePolicy = flag.String("default-ondelete-policy", "", "default policy for deleting subdirectory when deleting a volume")
29+
endpoint = flag.String("endpoint", "unix://tmp/csi.sock", "CSI endpoint")
30+
nodeID = flag.String("nodeid", "", "node id")
31+
mountPermissions = flag.Uint64("mount-permissions", 0, "mounted folder permissions")
32+
driverName = flag.String("drivername", nfs.DefaultDriverName, "name of the driver")
33+
workingMountDir = flag.String("working-mount-dir", "/tmp", "working directory for provisioner to mount nfs shares temporarily")
34+
defaultOnDeletePolicy = flag.String("default-ondelete-policy", "", "default policy for deleting subdirectory when deleting a volume")
35+
volStatsCacheExpireInMinutes = flag.Int("vol-stats-cache-expire-in-minutes", 10, "The cache expire time in minutes for volume stats cache")
3536
)
3637

3738
func main() {
@@ -48,12 +49,13 @@ func main() {
4849

4950
func handle() {
5051
driverOptions := nfs.DriverOptions{
51-
NodeID: *nodeID,
52-
DriverName: *driverName,
53-
Endpoint: *endpoint,
54-
MountPermissions: *mountPermissions,
55-
WorkingMountDir: *workingMountDir,
56-
DefaultOnDeletePolicy: *defaultOnDeletePolicy,
52+
NodeID: *nodeID,
53+
DriverName: *driverName,
54+
Endpoint: *endpoint,
55+
MountPermissions: *mountPermissions,
56+
WorkingMountDir: *workingMountDir,
57+
DefaultOnDeletePolicy: *defaultOnDeletePolicy,
58+
VolStatsCacheExpireInMinutes: *volStatsCacheExpireInMinutes,
5759
}
5860
d := nfs.NewDriver(&driverOptions)
5961
d.Run(false)

pkg/nfs/nfs.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package nfs
1919
import (
2020
"runtime"
2121
"strings"
22+
"time"
2223

2324
"github.com/container-storage-interface/spec/lib/go/csi"
2425
"k8s.io/klog/v2"
@@ -29,12 +30,13 @@ import (
2930

3031
// DriverOptions defines driver parameters specified in driver deployment
3132
type DriverOptions struct {
32-
NodeID string
33-
DriverName string
34-
Endpoint string
35-
MountPermissions uint64
36-
WorkingMountDir string
37-
DefaultOnDeletePolicy string
33+
NodeID string
34+
DriverName string
35+
Endpoint string
36+
MountPermissions uint64
37+
WorkingMountDir string
38+
DefaultOnDeletePolicy string
39+
VolStatsCacheExpireInMinutes int
3840
}
3941

4042
type Driver struct {
@@ -53,7 +55,8 @@ type Driver struct {
5355
volumeLocks *VolumeLocks
5456

5557
// a timed cache storing volume stats <volumeID, volumeStats>
56-
volStatsCache azcache.Resource
58+
volStatsCache azcache.Resource
59+
volStatsCacheExpireInMinutes int
5760
}
5861

5962
const (
@@ -81,12 +84,13 @@ func NewDriver(options *DriverOptions) *Driver {
8184
klog.V(2).Infof("Driver: %v version: %v", options.DriverName, driverVersion)
8285

8386
n := &Driver{
84-
name: options.DriverName,
85-
version: driverVersion,
86-
nodeID: options.NodeID,
87-
endpoint: options.Endpoint,
88-
mountPermissions: options.MountPermissions,
89-
workingMountDir: options.WorkingMountDir,
87+
name: options.DriverName,
88+
version: driverVersion,
89+
nodeID: options.NodeID,
90+
endpoint: options.Endpoint,
91+
mountPermissions: options.MountPermissions,
92+
workingMountDir: options.WorkingMountDir,
93+
volStatsCacheExpireInMinutes: options.VolStatsCacheExpireInMinutes,
9094
}
9195

9296
n.AddControllerServiceCapabilities([]csi.ControllerServiceCapability_RPC_Type{
@@ -102,6 +106,16 @@ func NewDriver(options *DriverOptions) *Driver {
102106
csi.NodeServiceCapability_RPC_UNKNOWN,
103107
})
104108
n.volumeLocks = NewVolumeLocks()
109+
110+
if options.VolStatsCacheExpireInMinutes <= 0 {
111+
options.VolStatsCacheExpireInMinutes = 10 // default expire in 10 minutes
112+
}
113+
114+
var err error
115+
getter := func(key string) (interface{}, error) { return nil, nil }
116+
if n.volStatsCache, err = azcache.NewTimedCache(time.Duration(options.VolStatsCacheExpireInMinutes)*time.Minute, getter, false); err != nil {
117+
klog.Fatalf("%v", err)
118+
}
105119
return n
106120
}
107121

pkg/nfs/nfs_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
"os"
2121
"path/filepath"
2222
"testing"
23+
"time"
2324

2425
"github.com/container-storage-interface/spec/lib/go/csi"
2526
"github.com/stretchr/testify/assert"
27+
azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
2628
)
2729

2830
const (
@@ -52,6 +54,8 @@ func NewEmptyDriver(emptyField string) *Driver {
5254
}
5355
}
5456
d.volumeLocks = NewVolumeLocks()
57+
getter := func(key string) (interface{}, error) { return nil, nil }
58+
d.volStatsCache, _ = azcache.NewTimedCache(time.Minute, getter, false)
5559
return d
5660
}
5761

pkg/nfs/nodeserver.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"k8s.io/klog/v2"
3131
"k8s.io/kubernetes/pkg/volume"
3232
mount "k8s.io/mount-utils"
33+
34+
azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
3335
)
3436

3537
// NodeServer driver
@@ -195,6 +197,17 @@ func (ns *NodeServer) NodeGetVolumeStats(_ context.Context, req *csi.NodeGetVolu
195197
return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty")
196198
}
197199

200+
// check if the volume stats is cached
201+
cache, err := ns.Driver.volStatsCache.Get(req.VolumeId, azcache.CacheReadTypeDefault)
202+
if err != nil {
203+
return nil, status.Errorf(codes.Internal, err.Error())
204+
}
205+
if cache != nil {
206+
resp := cache.(csi.NodeGetVolumeStatsResponse)
207+
klog.V(6).Infof("NodeGetVolumeStats: volume stats for volume %s path %s is cached", req.VolumeId, req.VolumePath)
208+
return &resp, nil
209+
}
210+
198211
if _, err := os.Lstat(req.VolumePath); err != nil {
199212
if os.IsNotExist(err) {
200213
return nil, status.Errorf(codes.NotFound, "path %s does not exist", req.VolumePath)
@@ -233,7 +246,7 @@ func (ns *NodeServer) NodeGetVolumeStats(_ context.Context, req *csi.NodeGetVolu
233246
return nil, status.Errorf(codes.Internal, "failed to transform disk inodes used(%v)", volumeMetrics.InodesUsed)
234247
}
235248

236-
return &csi.NodeGetVolumeStatsResponse{
249+
resp := csi.NodeGetVolumeStatsResponse{
237250
Usage: []*csi.VolumeUsage{
238251
{
239252
Unit: csi.VolumeUsage_BYTES,
@@ -248,7 +261,11 @@ func (ns *NodeServer) NodeGetVolumeStats(_ context.Context, req *csi.NodeGetVolu
248261
Used: inodesUsed,
249262
},
250263
},
251-
}, nil
264+
}
265+
266+
// cache the volume stats per volume
267+
ns.Driver.volStatsCache.Set(req.VolumeId, &resp)
268+
return &resp, err
252269
}
253270

254271
// NodeUnstageVolume unstage volume

0 commit comments

Comments
 (0)