Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #2039 (2025-04-03 14:57:04)

[release-1.17]Automated cherry pick of #2037: update cache logic to calculate chunk size based on total
Cherry pick of #2037 on release-1.17.

#2037: update cache logic to calculate chunk size based on total

For details on the cherry pick process, see the [cherry pick requests](https://git.k8s.io/community/contributors/devel/sig-release/cherry-picks.md) page.

```release-note
Updates the logic to calculate chunk size for data cache
```


Patch:
diff --git a/pkg/gce-pd-csi-driver/cache.go b/pkg/gce-pd-csi-driver/cache.go
index 1982a934..e1b1cb1f 100644
--- a/pkg/gce-pd-csi-driver/cache.go
+++ b/pkg/gce-pd-csi-driver/cache.go
@@ -175,10 +175,18 @@ func setupCaching(devicePath string, req *csi.NodeStageVolumeRequest, nodeId str
 		klog.V(4).Infof("Assuming valid data cache size and mode, resizing cache is not supported")
 	} else {
 		cacheSize := req.GetPublishContext()[common.ContextDataCacheSize]
-		chunkSize, err := fetchChunkSizeKiB(cacheSize)
+		maxChunkSizeStr := strconv.FormatInt(int64(maxChunkSize/KiB), 10)
+		var chunkSize string
+		cachePvSize, err := fetchPvSizeGiB()
 		if err != nil {
-			klog.Errorf("Errored to fetch cache size, verify the data-cache-size is valid: got %v, error: %q", cacheSize, err)
-			return mainDevicePath, err
+			klog.Errorf("Errored while fetching PV size, got %v, falling back to default chunkSize of %v", err, maxChunkSize)
+			chunkSize = maxChunkSizeStr
+		} else {
+			chunkSize, err = fetchChunkSizeKiB(cachePvSize)
+			if err != nil {
+				klog.Errorf("Errored to fetch cache size, verify the data-cache-size is valid: got %v, error: %q", chunkSize, err)
+				chunkSize = maxChunkSizeStr
+			}
 		}
 		// Check if LV exists
 		info, err = common.RunCommand("" /* pipedCmd */, nil /* pipedCmdArg */, "lvs", args...)
@@ -650,7 +658,7 @@ func watchDiskDetaches(watcher *fsnotify.Watcher, nodeName string, errorCh chan
 				klog.Errorf("Error updating volume group's metadata: %v", err)
 			}
 			reduceVolumeGroup(getVolumeGroupName(nodeName), true)
-			klog.V(2).Infof("disk attach/detach event %#v\n", event)
+			klog.V(6).Infof("disk attach/detach event %#v\n", event)
 		}
 	}
 }
@@ -682,3 +690,48 @@ func addRaidedLSSDToVg(vgName, lssdPath string) error {
 	}
 	return nil
 }
+
+func fetchPvSizeGiB() (string, error) {
+	args := []string{
+		"--select",
+		"-o",
+		"--noheadings",
+		"pv_size",
+		"--units=b",
+	}
+	// RAIDed device is always registered with its /dev/md127 equivalent in VG so cannot check it directly based on the RAIDed LSSD path which could be /dev/md/csi-driver-data-cache
+	info, err := common.RunCommand("grep" /* pipedCmd */, []string{"/dev/md"} /* pipedCmdArg */, "pvs", args...)
+	if err != nil {
+		return "", fmt.Errorf("errored while fetching PV size %v: %s", err, info)
+	}
+	infoString := strings.TrimSpace(string(info))
+	infoSlice := strings.Fields(infoString)
+	pvSize, err := fetchNumberGiB(infoSlice)
+	if err != nil {
+		return "", fmt.Errorf("Error fetching PV size for cache %v", err)
+	}
+	return pvSize, nil
+
+}
+
+func fetchNumberGiB(infoSlice []string) (string, error) {
+	re, err := regexp.Compile("^[0-9]+B$")
+	if err != nil {
+		return "", fmt.Errorf("Failed to compile regex match %v", err)
+	}
+	var pvSize string
+	for _, i := range infoSlice {
+		if re.MatchString(i) {
+			pvSize, err = strings.TrimSuffix(i, "B"), nil
+			if err != nil {
+				return "", fmt.Errorf("Failed to extract PV size %v", err)
+			}
+			break
+		}
+	}
+	pvSizeInt, err := strconv.ParseFloat(pvSize, 64)
+	if err != nil {
+		return "", fmt.Errorf("Error while fetching PV size for cache %v", err)
+	}
+	return strconv.FormatInt(int64(math.Ceil(pvSizeInt/GiB)), 10) + "GiB", nil
+}


Test patch:
diff --git a/pkg/gce-pd-csi-driver/cache_test.go b/pkg/gce-pd-csi-driver/cache_test.go
index 4574dde4..340c924a 100644
--- a/pkg/gce-pd-csi-driver/cache_test.go
+++ b/pkg/gce-pd-csi-driver/cache_test.go
@@ -55,3 +55,63 @@ func TestFetchChunkSizeKiB(t *testing.T) {
 	}
 
 }
+
+func TestFetchNumberGiB(t *testing.T) {
+	testCases := []struct {
+		name        string
+		stringInput []string
+		expOutput   string // Outputs value in GiB
+		expErr      bool
+	}{
+		{
+			name:        "valid input 1",
+			stringInput: []string{"5000000000B"},
+			expOutput:   "5GiB", //range defined in fetchChunkSizeKiB
+		},
+		{
+			name:        "valid input 2",
+			stringInput: []string{"375000000000B"}, // 1 LSSD attached
+			expOutput:   "350GiB",                  //range defined in fetchChunkSizeKiB
+		},
+		{
+			name:        "valid input 3",
+			stringInput: []string{"9000000000000B"}, // 24 LSSD attached
+			expOutput:   "8382GiB",                  //range defined in fetchChunkSizeKiB
+		},
+		{
+			name:        "valid input 4",
+			stringInput: []string{"Some text before ", "9000000000000B", "Some text after"}, // 24 LSSD attached
+			expOutput:   "8382GiB",                                                          //range defined in fetchChunkSizeKiB
+		},
+		{
+			name:        "invalid input 1",
+			stringInput: []string{"9000000000000"},
+			expErr:      true,
+		},
+		{
+			name:        "invalid input 2",
+			stringInput: []string{"A9000000000000B"},
+			expErr:      true,
+		},
+		{
+			name:        "valid input 5",
+			stringInput: []string{"900000B"}, // <1GiB gets rounded off to 0GiB
+			expOutput:   "1GiB",
+		},
+	}
+
+	for _, tc := range testCases {
+		v, err := fetchNumberGiB(tc.stringInput)
+		if err != nil {
+			if !tc.expErr {
+				t.Errorf("Errored %s", err)
+			}
+			continue
+		}
+		if v != tc.expOutput {
+			t.Errorf("Got %s want %s", v, tc.expOutput)
+		}
+
+	}
+
+}