-
Notifications
You must be signed in to change notification settings - Fork 604
Add maxInflightMountCalls and volumeAttachLimit args #1706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,8 +41,12 @@ func main() { | |
| volMetricsFsRateLimit = flag.Int("vol-metrics-fs-rate-limit", 5, "Volume metrics routines rate limiter per file system") | ||
| deleteAccessPointRootDir = flag.Bool("delete-access-point-root-dir", false, | ||
| "Opt in to delete access point root directory by DeleteVolume. By default, DeleteVolume will delete the access point behind Persistent Volume and deleting access point will not delete the access point root directory or its contents.") | ||
| adaptiveRetryMode = flag.Bool("adaptive-retry-mode", true, "Opt out to use standard sdk retry configuration. By default, adaptive retry mode will be used to more heavily client side rate limit EFS API requests.") | ||
| tags = flag.String("tags", "", "Space separated key:value pairs which will be added as tags for EFS resources. For example, 'environment:prod region:us-east-1'") | ||
| adaptiveRetryMode = flag.Bool("adaptive-retry-mode", true, "Opt out to use standard sdk retry configuration. By default, adaptive retry mode will be used to more heavily client side rate limit EFS API requests.") | ||
| tags = flag.String("tags", "", "Space separated key:value pairs which will be added as tags for EFS resources. For example, 'environment:prod region:us-east-1'") | ||
| maxInflightMountCallsOptIn = flag.Bool("max-inflight-mount-calls-opt-in", false, "Opt in to use max inflight mount calls limit.") | ||
| maxInflightMountCalls = flag.Int64("max-inflight-mount-calls", driver.UnsetMaxInflightMountCounts, "New NodePublishVolume operation will be blocked if maximum number of inflight calls is reached. If maxInflightMountCallsOptIn is true, it has to be set to a positive value.") | ||
|
DavidXU12345 marked this conversation as resolved.
|
||
| volumeAttachLimitOptIn = flag.Bool("volume-attach-limit-opt-in", false, "Opt in to use volume attach limit.") | ||
| volumeAttachLimit = flag.Int64("volume-attach-limit", driver.UnsetVolumeAttachLimit, "Maximum number of volumes that can be attached to a node. If volumeAttachLimitOptIn is true, it has to be set to a positive value.") | ||
| ) | ||
| klog.InitFlags(nil) | ||
| flag.Parse() | ||
|
|
@@ -61,7 +65,7 @@ func main() { | |
| if err != nil { | ||
| klog.Fatalln(err) | ||
| } | ||
| drv := driver.NewDriver(*endpoint, etcAmazonEfs, *efsUtilsStaticFilesPath, *tags, *volMetricsOptIn, *volMetricsRefreshPeriod, *volMetricsFsRateLimit, *deleteAccessPointRootDir, *adaptiveRetryMode) | ||
| drv := driver.NewDriver(*endpoint, etcAmazonEfs, *efsUtilsStaticFilesPath, *tags, *volMetricsOptIn, *volMetricsRefreshPeriod, *volMetricsFsRateLimit, *deleteAccessPointRootDir, *adaptiveRetryMode, *maxInflightMountCallsOptIn, *maxInflightMountCalls, *volumeAttachLimitOptIn, *volumeAttachLimit) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably add all options into its own type (similar as EBS CSI driver) so that we can pass option around rather than passing each value into I may consider to do the refactoring in next PR since the size of this PR is already very large. |
||
| if err := drv.Run(); err != nil { | ||
| klog.Fatalln(err) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package driver | ||
|
|
||
| import ( | ||
| "sync" | ||
|
|
||
| "k8s.io/klog/v2" | ||
| ) | ||
|
|
||
| type InFlightMountTracker struct { | ||
| mux sync.Mutex | ||
| count int64 | ||
| maxCount int64 | ||
| } | ||
|
|
||
| func NewInFlightMountTracker(maxCount int64) *InFlightMountTracker { | ||
| if maxCount <= 0 { | ||
| klog.V(4).InfoS("InFlightMountTracker is disabled") | ||
| return nil | ||
| } | ||
| return &InFlightMountTracker{ | ||
| maxCount: maxCount, | ||
| } | ||
| } | ||
|
|
||
| func (checker *InFlightMountTracker) increment() bool { | ||
| checker.mux.Lock() | ||
| defer checker.mux.Unlock() | ||
|
|
||
| if checker.count >= checker.maxCount { | ||
| return false | ||
| } | ||
|
|
||
| checker.count++ | ||
| return true | ||
| } | ||
|
|
||
| func (checker *InFlightMountTracker) decrement() bool { | ||
| checker.mux.Lock() | ||
| defer checker.mux.Unlock() | ||
| if checker.count == 0 { | ||
| klog.Error("InFlightMountTracker: trying to decrement count when it is already 0") | ||
| return false | ||
| } | ||
|
|
||
| checker.count-- | ||
| return true | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| package driver | ||
|
|
||
| import ( | ||
| "sync" | ||
| "testing" | ||
| ) | ||
|
|
||
| func assertEqual[T comparable](t *testing.T, actual, expected T, description string) { | ||
| if expected != actual { | ||
| t.Errorf("%s: expected %v != actual %v", description, expected, actual) | ||
| } | ||
| } | ||
|
|
||
| func TestNewInFlightMountTracker(t *testing.T) { | ||
| checker := NewInFlightMountTracker(5) | ||
| assertEqual(t, checker.maxCount, 5, "Max inflight count") | ||
| assertEqual(t, checker.count, 0, "Inflight count") | ||
|
|
||
| checker = NewInFlightMountTracker(UnsetMaxInflightMountCounts) | ||
| assertEqual(t, checker, nil, "Nil checker for negative max inflight mount counts") | ||
|
|
||
| checker = NewInFlightMountTracker(0) | ||
| assertEqual(t, checker, nil, "Nil checker for zero max inflight mount counts") | ||
| } | ||
|
|
||
| func TestIncrement(t *testing.T) { | ||
| maxFlightCount := int64(2) | ||
| checker := NewInFlightMountTracker(maxFlightCount) | ||
|
|
||
| if !checker.increment() { | ||
| t.Errorf("First increment should succeed with max inflight count=%d", maxFlightCount) | ||
| } | ||
| assertEqual(t, checker.count, 1, "Inflight count after first increment") | ||
|
|
||
| if !checker.increment() { | ||
| t.Errorf("Second increment should succeed with max inflight count=%d", maxFlightCount) | ||
| } | ||
| assertEqual(t, checker.count, 2, "Inflight count after second increment") | ||
|
|
||
| if checker.increment() { | ||
| t.Errorf("Third increment should fail with max inflight count=%d", maxFlightCount) | ||
| } | ||
| assertEqual(t, checker.count, 2, "Inflight count after third increment") | ||
| } | ||
|
|
||
| func TestDecrement(t *testing.T) { | ||
| maxFlightCount := int64(2) | ||
| checker := NewInFlightMountTracker(maxFlightCount) | ||
| checker.increment() | ||
| checker.increment() | ||
|
|
||
| checker.decrement() | ||
| assertEqual(t, checker.count, 1, "Inflight count after first decrement") | ||
|
|
||
| checker.decrement() | ||
| assertEqual(t, checker.count, 0, "Inflight count after second decrement") | ||
|
|
||
| // Should not decrement further when the count is already zero | ||
| checker.decrement() | ||
| assertEqual(t, checker.count, 0, "Inflight count after decrement when count is already zero") | ||
| } | ||
|
|
||
| func TestConcurrency(t *testing.T) { | ||
| // Run multiple iterations to increase chance of catching race conditions | ||
| for i := 0; i < 100; i++ { | ||
| maxFlightCount := int64(500) | ||
| checker := NewInFlightMountTracker(maxFlightCount) | ||
| var wg sync.WaitGroup | ||
| var mu sync.Mutex | ||
|
|
||
| numGoRoutinesForIncrement := 400 | ||
| for range numGoRoutinesForIncrement { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| checker.increment() | ||
| }() | ||
| } | ||
|
|
||
| numGoRoutinesForDecrement := 350 | ||
| actualDecrements := 0 | ||
| for range numGoRoutinesForDecrement { | ||
| wg.Add(1) | ||
| go func() { | ||
| defer wg.Done() | ||
| if checker.decrement() { | ||
| mu.Lock() | ||
| actualDecrements++ | ||
| mu.Unlock() | ||
| } | ||
| }() | ||
| } | ||
|
|
||
| wg.Wait() | ||
| assertEqual(t, checker.count, int64(numGoRoutinesForIncrement-actualDecrements), "inflight count") | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we could simplify this by just creating one flag for each? If no is maxInflightCalls or volumeLimit is provided we just use current behavior (not limited at all)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to keep the
OptInflag primarily for future-proofing. If we later implement default calculations formaxInflightCallsorvolumeLimit, users could setOptIn=truewithout specifying values for parameters likemaxInflightMountCalls.While we could remove the
OptInfeature gate later if we decide not to provide default calculations, adding it back would be more disruptive. From a user perspective, withoutOptIn=true, a configuration withmaxInflightMountCalls=10would be ignored, breaking backward compatibility.