Skip to content

Commit 77cc614

Browse files
authored
Adds ONTAP HTTP rate limiting
1 parent 05e0568 commit 77cc614

39 files changed

+566
-482
lines changed

cli/k8s_client/yaml_factory.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ spec:
783783
- "--csi-address=$(ADDRESS)"
784784
- "--retry-interval-start=8s"
785785
- "--retry-interval-max=30s"
786-
- "--worker-threads=5"
786+
- "--worker-threads=100"
787787
{K8S_API_CLIENT_SIDECAR_THROTTLE}
788788
{CSI_SIDECAR_PROVISIONER_RESOURCES}
789789
env:
@@ -803,7 +803,7 @@ spec:
803803
- "--v={SIDECAR_LOG_LEVEL}"
804804
- "--timeout=60s"
805805
- "--retry-interval-start=10s"
806-
- "--worker-threads=10"
806+
- "--worker-threads=100"
807807
- "--csi-address=$(ADDRESS)"
808808
{K8S_API_CLIENT_SIDECAR_THROTTLE}
809809
{CSI_SIDECAR_ATTACHER_RESOURCES}
@@ -823,7 +823,7 @@ spec:
823823
args:
824824
- "--v={SIDECAR_LOG_LEVEL}"
825825
- "--timeout=300s"
826-
- "--workers=10"
826+
- "--workers=100"
827827
- "--csi-address=$(ADDRESS)"
828828
{K8S_API_CLIENT_SIDECAR_THROTTLE}
829829
{CSI_SIDECAR_RESIZER_RESOURCES}
@@ -843,7 +843,7 @@ spec:
843843
args:
844844
- "--v={SIDECAR_LOG_LEVEL}"
845845
- "--timeout=300s"
846-
- "--worker-threads=10"
846+
- "--worker-threads=100"
847847
- "--csi-address=$(ADDRESS)"
848848
{FEATURE_GATES_CSI_SNAPSHOTTER}
849849
{K8S_API_CLIENT_SIDECAR_THROTTLE}

config/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ const (
247247
UnknownVolumeType VolumeType = ""
248248

249249
/* Driver-related constants */
250-
DefaultSolidFireVAG = OrchestratorName
251-
UnknownDriver = "UnknownDriver"
252-
StorageAPITimeoutSeconds = 90
253-
SANResizeDelta = 50000000 // 50mb
250+
DefaultSolidFireVAG = OrchestratorName
251+
UnknownDriver = "UnknownDriver"
252+
StorageAPITimeout = 90 * time.Second
253+
SANResizeDelta = 50000000 // 50mb
254254

255255
// Storage driver names specified in the config file, etc.
256256
OntapNASStorageDriverName = "ontap-nas"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ require (
5757
golang.org/x/crypto v0.40.0 // github.com/golang/crypto
5858
golang.org/x/net v0.42.0 // github.com/golang/net
5959
golang.org/x/oauth2 v0.30.0 // github.com/golang/oauth2
60-
golang.org/x/sync v0.16.0 // indirect
60+
golang.org/x/sync v0.16.0
6161
golang.org/x/sys v0.34.0 // github.com/golang/sys
6262
golang.org/x/text v0.27.0 // github.com/golang/text
6363
golang.org/x/time v0.11.0 // github.com/golang/time

mocks/mock_storage_drivers/mock_ontap/mock_api.go

Lines changed: 12 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mocks/mock_storage_drivers/mock_ontap/mock_ontap_rest_interface.go

Lines changed: 62 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mocks/mock_storage_drivers/mock_ontap/mock_ontap_zapi_interface.go

Lines changed: 12 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

storage_drivers/common.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ import (
88
"encoding/gob"
99
"encoding/json"
1010
"fmt"
11+
"io"
12+
"net/http"
1113
"reflect"
1214
"strconv"
1315
"strings"
16+
"sync"
17+
"time"
18+
19+
"github.com/cenkalti/backoff/v4"
20+
"golang.org/x/sync/semaphore"
1421

1522
trident "github.com/netapp/trident/config"
1623
. "github.com/netapp/trident/logging"
@@ -399,3 +406,94 @@ func RemoveSCSIDeviceByPublishInfo(ctx context.Context, publishInfo *tridentmode
399406
}
400407
}
401408
}
409+
410+
var (
411+
semaphoresLock = sync.Mutex{}
412+
semaphores = make(map[string]*rcSem)
413+
ONTAPRequestLimit = 20
414+
)
415+
416+
const (
417+
initialInterval = 1 * time.Second
418+
multiplier = 1.414 // approx sqrt(2)
419+
maxInterval = 6 * time.Second
420+
randomFactor = 0.1
421+
)
422+
423+
// rcSem is a reference-counted semaphore.
424+
type rcSem struct {
425+
sem *semaphore.Weighted
426+
refs int
427+
}
428+
429+
// NewSemaphore returns a named semaphore, creating it if it does not already exist.
430+
// The semaphore is initialized with the specified maxConcurrent value only the first time it is created.
431+
// Each call to NewSemaphore should be matched with a call to FreeSemaphore to allow proper cleanup.
432+
func NewSemaphore(name string, maxConcurrent int) *semaphore.Weighted {
433+
semaphoresLock.Lock()
434+
defer semaphoresLock.Unlock()
435+
436+
s, exists := semaphores[name]
437+
if !exists {
438+
s = &rcSem{
439+
sem: semaphore.NewWeighted(int64(maxConcurrent)),
440+
refs: 0,
441+
}
442+
semaphores[name] = s
443+
}
444+
s.refs++
445+
return s.sem
446+
}
447+
448+
func FreeSemaphore(name string) {
449+
semaphoresLock.Lock()
450+
defer semaphoresLock.Unlock()
451+
452+
counter, exists := semaphores[name]
453+
if exists {
454+
counter.refs--
455+
if counter.refs <= 0 {
456+
delete(semaphores, name)
457+
}
458+
}
459+
}
460+
461+
type LimitedRetryTransport struct {
462+
base http.RoundTripper
463+
sem *semaphore.Weighted
464+
b *backoff.ExponentialBackOff
465+
}
466+
467+
// NewLimitedRetryTransport wraps a base transport to limit the number of concurrent requests and add retries
468+
// on io.EOF errors.
469+
func NewLimitedRetryTransport(sem *semaphore.Weighted, base http.RoundTripper) *LimitedRetryTransport {
470+
b := backoff.NewExponentialBackOff()
471+
b.InitialInterval = initialInterval
472+
b.Multiplier = multiplier
473+
b.MaxInterval = maxInterval
474+
b.RandomizationFactor = randomFactor
475+
return &LimitedRetryTransport{
476+
base: base,
477+
sem: sem,
478+
b: b,
479+
}
480+
}
481+
482+
func (lrt *LimitedRetryTransport) RoundTrip(req *http.Request) (*http.Response, error) {
483+
var resp *http.Response
484+
f := func() error {
485+
if err := lrt.sem.Acquire(req.Context(), 1); err != nil {
486+
return backoff.Permanent(err)
487+
} else {
488+
defer lrt.sem.Release(1)
489+
}
490+
r, err := lrt.base.RoundTrip(req)
491+
resp = r
492+
if err != nil && !errors.Is(err, io.EOF) {
493+
return backoff.Permanent(err)
494+
}
495+
return err
496+
}
497+
lrt.b.Reset()
498+
return resp, backoff.Retry(f, lrt.b)
499+
}

0 commit comments

Comments
 (0)