-
Notifications
You must be signed in to change notification settings - Fork 93
Support request rate limiting #201
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
Conversation
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.
Looking good bar a few minor changes 👍
pkg/oci/client/client.go
Outdated
@@ -40,16 +41,24 @@ type Interface interface { | |||
Networking() NetworkingInterface | |||
} | |||
|
|||
// OperationPollRateLimiter reader and writer. | |||
type OperationPollRateLimiter struct { |
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.
OperationPollRateLimiter
implies to me that the rate limiter is only used for polling operations (e.g. polling work requests). It's used for all API requests, however. Suggest just RateLimiter
.
pkg/oci/client/errors.go
Outdated
if isWrite { | ||
opType = "write" | ||
} | ||
return fmt.Errorf("Rate limited(%s) for operation: %s", opType, opName) |
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.
Should use errors.Errorf
to capture stack and error strings shouldn't start with a capital.
pkg/oci/client/load_balancer.go
Outdated
@@ -48,6 +49,11 @@ type LoadBalancerInterface interface { | |||
} | |||
|
|||
func (c *client) GetLoadBalancer(ctx context.Context, id string) (*loadbalancer.LoadBalancer, error) { | |||
// Read rate limiting |
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.
Probably not needed. Would ditch this and similar comments.
pkg/oci/client/compute.go
Outdated
@@ -48,6 +53,11 @@ func (c *client) GetInstance(ctx context.Context, id string) (*core.Instance, er | |||
} | |||
|
|||
func (c *client) getInstanceByDisplayName(ctx context.Context, compartmentID, displayName string) (*core.Instance, error) { | |||
// Read rate limiting | |||
if !c.rateLimiter.Reader.TryAccept() { |
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.
As getInstanceByDisplayName
(potentially) makes multiple calls to the SDK this should be within the for loop prior to the call to c.compute.ListInstances
.
pkg/oci/ccm.go
Outdated
@@ -73,7 +79,46 @@ func NewCloudProvider(config *Config) (cloudprovider.Interface, error) { | |||
if err != nil { | |||
return nil, err | |||
} | |||
c, err := client.New(cp) | |||
|
|||
operationPollRateLimiterRead := flowcontrol.NewFakeAlwaysRateLimiter() |
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.
Don't think these two lines are required as the rate limiter isn't optional
@@ -273,14 +339,19 @@ func (c *client) AwaitWorkRequest(ctx context.Context, id string) (*loadbalancer | |||
wr = twr | |||
return true, nil | |||
case loadbalancer.WorkRequestLifecycleStateFailed: | |||
return false, errors.Errorf("WorkRequest %q failed: %s", id, twr.Message) | |||
return false, errors.Errorf("WorkRequest %q failed: %s", id, *twr.Message) |
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.
Is WorkRequest.Message
a required field? If not will need to nil check.
pkg/oci/client/networking.go
Outdated
@@ -71,6 +81,11 @@ func (c *client) GetSubnet(ctx context.Context, id string) (*core.Subnet, error) | |||
// GetSubnetFromCacheByIP checks to see if the given IP is contained by any subnet CIDR block in the subnet cache | |||
// If no hits were found then no subnet and no error will be returned (nil, nil) | |||
func (c *client) GetSubnetFromCacheByIP(ip string) (*core.Subnet, error) { | |||
// Read rate limiting | |||
if !c.rateLimiter.Reader.TryAccept() { |
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.
No need to rate limit access to the local cache
pkg/oci/client/networking.go
Outdated
@@ -46,6 +51,11 @@ func (c *client) getVNIC(ctx context.Context, id string) (*core.Vnic, error) { | |||
} | |||
|
|||
func (c *client) GetSubnet(ctx context.Context, id string) (*core.Subnet, error) { | |||
// Read rate limiting | |||
if !c.rateLimiter.Reader.TryAccept() { |
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.
Only need to rate limit if we get a cache miss.
pkg/oci/client/load_balancer.go
Outdated
@@ -61,6 +67,11 @@ func (c *client) GetLoadBalancer(ctx context.Context, id string) (*loadbalancer. | |||
} | |||
|
|||
func (c *client) GetLoadBalancerByName(ctx context.Context, compartmentID, name string) (*loadbalancer.LoadBalancer, error) { | |||
// Read rate limiting | |||
if !c.rateLimiter.Reader.TryAccept() { |
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.
As GetLoadBalancerByName
(potentially) makes multiple calls to the SDK this should be within the for loop prior to the call to c.compute.ListLoadBalancers()
.
pkg/oci/client/load_balancer.go
Outdated
@@ -259,6 +320,11 @@ func (c *client) UpdateListener(ctx context.Context, lbID, name string, details | |||
} | |||
|
|||
func (c *client) AwaitWorkRequest(ctx context.Context, id string) (*loadbalancer.WorkRequest, error) { | |||
// Read rate limiting | |||
if !c.rateLimiter.Reader.TryAccept() { |
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.
No need to rate limit here. The rate limiting is applied in the call to c.GetWorkRequest()
on a per OCI API request basis.
efcfa74
to
47dccc7
Compare
|
||
type mockLoadBalancerClient struct{} | ||
|
||
func TestRateLimiting(t *testing.T) { |
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.
Not sure i see much value in this test as what's being tested is the kubernetes code from the flowcontrol pkg
pkg/oci/ccm.go
Outdated
@@ -218,3 +229,39 @@ func buildConfigurationProvider(config *Config) (common.ConfigurationProvider, e | |||
) | |||
return cp, nil | |||
} | |||
|
|||
// BuildRateLimiter ... |
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.
Could do with a better comment here
47dccc7
to
ec00172
Compare
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.
Some minor changes and query re constants but otherwise LGTM 👍
pkg/oci/ccm.go
Outdated
@@ -43,6 +44,13 @@ import ( | |||
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/util" | |||
) | |||
|
|||
const ( | |||
//RateLimitQPSDefault sets a value for the default queries per second. |
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.
Missing leading space before comment
pkg/oci/ccm.go
Outdated
const ( | ||
//RateLimitQPSDefault sets a value for the default queries per second. | ||
RateLimitQPSDefault = 1.0 | ||
//RateLimitBucketDefault sets a value for the default token bucket burst size. |
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.
Missing leading space before comment
pkg/oci/ccm.go
Outdated
@@ -43,6 +44,13 @@ import ( | |||
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/util" | |||
) | |||
|
|||
const ( | |||
//RateLimitQPSDefault sets a value for the default queries per second. | |||
RateLimitQPSDefault = 1.0 |
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.
What is the rationale behind these constants?
pkg/oci/ccm.go
Outdated
|
||
// buildNewRateLimiter builds and returns a struct containing read and write | ||
// rate limiters. Defaults are used where no (0) value is provided. | ||
func buildNewRateLimiter(config *RateLimiterConfig) client.RateLimiter { |
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.
func newRateLimiter(config *RateLimiterConfig) client.RateLimiter
pkg/oci/ccm_test.go
Outdated
) | ||
|
||
func TestBuildRateLimiterWithConfig(t *testing.T) { | ||
var qpsRead float32 = 6.0 |
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.
qpsRead := float32(6.0)
pkg/oci/client/compute.go
Outdated
@@ -53,6 +57,9 @@ func (c *client) getInstanceByDisplayName(ctx context.Context, compartmentID, di | |||
instances []core.Instance | |||
) | |||
for { | |||
if !c.rateLimiter.Reader.TryAccept() { | |||
return nil, RateLimitError(false, "getInstanceByDisplayName") |
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.
s/getInstanceByDisplayName/ListInstances/
pkg/oci/client/compute.go
Outdated
@@ -81,6 +88,10 @@ func (c *client) getInstanceByDisplayName(ctx context.Context, compartmentID, di | |||
} | |||
|
|||
func (c *client) listVNICAttachments(ctx context.Context, req core.ListVnicAttachmentsRequest) (core.ListVnicAttachmentsResponse, error) { | |||
if !c.rateLimiter.Reader.TryAccept() { | |||
return core.ListVnicAttachmentsResponse{}, RateLimitError(false, "listVNICAttachments") |
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.
s/listVNICAttachments/ListVnicAttachments/
pkg/oci/client/load_balancer.go
Outdated
@@ -63,6 +68,9 @@ func (c *client) GetLoadBalancer(ctx context.Context, id string) (*loadbalancer. | |||
func (c *client) GetLoadBalancerByName(ctx context.Context, compartmentID, name string) (*loadbalancer.LoadBalancer, error) { | |||
var page *string | |||
for { | |||
if !c.rateLimiter.Reader.TryAccept() { | |||
return nil, RateLimitError(false, "GetLoadBalancerByName") |
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.
s/GetLoadBalancerByName/ListLoadBalancers/
pkg/oci/client/load_balancer.go
Outdated
@@ -113,6 +129,10 @@ func (c *client) DeleteLoadBalancer(ctx context.Context, id string) (string, err | |||
} | |||
|
|||
func (c *client) GetCertificateByName(ctx context.Context, lbID, name string) (*loadbalancer.Certificate, error) { | |||
if !c.rateLimiter.Reader.TryAccept() { | |||
return nil, RateLimitError(false, "GetCertificateByName") |
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.
s/GetCertificateByName/ListCertificates/
pkg/oci/client/networking.go
Outdated
@@ -33,6 +33,10 @@ type NetworkingInterface interface { | |||
} | |||
|
|||
func (c *client) getVNIC(ctx context.Context, id string) (*core.Vnic, error) { | |||
if !c.rateLimiter.Reader.TryAccept() { | |||
return nil, RateLimitError(false, "getVNIC") |
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.
s/getVNIC/GetVnic/
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.
One minor nit but otherwise 👍
pkg/oci/client/networking.go
Outdated
func (c *client) getVNIC(ctx context.Context, id string) (*core.Vnic, error) { | ||
func (c *client) GetVNIC(ctx context.Context, id string) (*core.Vnic, error) { | ||
if !c.rateLimiter.Reader.TryAccept() { | ||
return nil, RateLimitError(false, "getVNIC") |
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.
s/getVNIC/GetVNIC/
pkg/oci/ccm.go
Outdated
@@ -43,6 +44,13 @@ import ( | |||
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/util" | |||
) | |||
|
|||
const ( | |||
// RateLimitQPSDefault sets a value for the default queries per second. | |||
RateLimitQPSDefault = 1.0 |
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.
@jhorwit2 Do you have any insight into appropriate values for these constants?
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.
@harveylowndes After talking to @jhorwit2 offline it seems like a value of 20.0
should be ok here.
5562f27
to
1fcffc0
Compare
1fcffc0
to
5165184
Compare
5165184
to
0cad1b7
Compare
@harveylowndes Can you give this a rebase please to fix the conflicts (NOTE: you'll need to pull the branch again as I've made some modifications)? |
779711b
to
237498c
Compare
237498c
to
2d6e127
Compare
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.
Couple of minor nits but otherwise LGTM 👍
pkg/oci/ccm.go
Outdated
// newRateLimiter builds and returns a struct containing read and write | ||
// rate limiters. Defaults are used where no (0) value is provided. | ||
func newRateLimiter(config *RateLimiterConfig) client.RateLimiter { | ||
|
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.
\n
pkg/oci/ccm.go
Outdated
config.RateLimitQPSRead, | ||
config.RateLimitBucketRead) | ||
|
||
glog.V(2).Infof("OCI using write rate limit configuration: QPS=%g, bucket=%d", |
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.
Use zap directly rather than glog
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.
Nearly there!
pkg/oci/ccm.go
Outdated
@@ -269,11 +267,11 @@ func newRateLimiter(config *RateLimiterConfig) client.RateLimiter { | |||
config.RateLimitBucketWrite), | |||
} | |||
|
|||
glog.V(2).Infof("OCI using read rate limit configuration: QPS=%g, bucket=%d", | |||
zap.S().Infof("OCI using read rate limit configuration: QPS=%g, bucket=%d", |
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.
Don't use the global logger; pass in a logger to newRateLimiter
.
dd96ed9
to
ab8fe53
Compare
LGTM 👍 |
@@ -0,0 +1,59 @@ | |||
// Copyright 2017 Oracle and/or its affiliates. All rights reserved. |
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.
2017 => 2018
ab8fe53
to
eb8da02
Compare
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.
Couple of things
pkg/oci/ccm.go
Outdated
@@ -42,6 +43,13 @@ import ( | |||
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/util" | |||
) | |||
|
|||
const ( | |||
// RateLimitQPSDefault sets a value for the default queries per second. |
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.
Remove comment
pkg/oci/ccm.go
Outdated
@@ -42,6 +43,13 @@ import ( | |||
"github.com/oracle/oci-cloud-controller-manager/pkg/oci/util" | |||
) | |||
|
|||
const ( | |||
// RateLimitQPSDefault sets a value for the default queries per second. | |||
RateLimitQPSDefault = 20.0 |
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.
Doesn't need to start with a capital (which reduces req for a comment too) as i don't think it's used outside this pkg?
pkg/oci/ccm.go
Outdated
const ( | ||
// RateLimitQPSDefault sets a value for the default queries per second. | ||
RateLimitQPSDefault = 20.0 | ||
// RateLimitBucketDefault sets a value for the default token bucket burst size. |
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.
Remove comment
pkg/oci/ccm.go
Outdated
// RateLimitQPSDefault sets a value for the default queries per second. | ||
RateLimitQPSDefault = 20.0 | ||
// RateLimitBucketDefault sets a value for the default token bucket burst size. | ||
RateLimitBucketDefault = 5 |
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.
Doesn't need to start with a capital (which reduces req for a comment too) as i don't think it's used outside this pkg?
eb8da02
to
24bc0f4
Compare
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.
Approved for merge.
* Support request rate limiting
Resolves: #108