Skip to content
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

Add rate limiting #411

Merged
merged 7 commits into from
Sep 15, 2023
Merged

Add rate limiting #411

merged 7 commits into from
Sep 15, 2023

Conversation

Choraden
Copy link
Contributor

@Choraden Choraden commented Sep 11, 2023

This adds global read and write rate limit.

@what-the-diff
Copy link

what-the-diff bot commented Sep 11, 2023

PR Summary

  • Inclusion of Read and Write Limit Controls
    An update has been done to our HTTPProxyConfig settings. This now includes ReadLimit and WriteLimit controls that dictate the amount of data read or written per second via the proxy. This is an overarching control that applies to all proxy activities.

  • New Dependency Introduction
    There's been an update to our module information file, go.mod. This now includes reference to another module: golang.org/x/time v0.0.0-20191024005414-555d28b269f0.

  • Inclusion of a Rate Limit Package
    A new package named ratelimit has been added. This includes several files which achieve the following:

    • conn.go: Incorporates rate limiting for data read or written via the network connection.
    • listener.go: Incorporates rate limiting on connections accepted.
    • ratelimit.go: This enables creation of a new rate limiter, based on the bandwidth requirements.
      Bear in mind, these changes have also culminated in a range of other updates across different packages and files, though the most crucial updates are covered above.

http_proxy.go Outdated
@@ -86,6 +87,8 @@ type HTTPProxyConfig struct {
CloseAfterReply bool
DenyDomains []*regexp.Regexp
DenyDomainsExclude []*regexp.Regexp
ReadLimit uint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be beneficial to use a special type that supports kb mb etc, see in rclone for inspiration.

bind/flag.go Outdated
@@ -127,6 +127,12 @@ func HTTPProxyConfig(fs *pflag.FlagSet, cfg *forwarder.HTTPProxyConfig, lcfg *lo
"Use this flag multiple times to specify multiple deny domain excludes. "+
"This flag takes precedence over deny-domain. "+
"Can be specified only if --deny-domain is also specified. ")

fs.UintVar(&cfg.ReadLimit, "read-limit", cfg.ReadLimit, "<bandwidth>"+
"Global read rate limit in bytes per second.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here more sophisticated input would be nice.

@mmatczuk
Copy link
Contributor

Looks good, some commits could be squashed.

@Choraden Choraden force-pushed the hg/rate_limit branch 3 times, most recently from d1478a6 to 9b88eb0 Compare September 13, 2023 10:33
@Choraden Choraden marked this pull request as ready for review September 13, 2023 10:54
@Choraden Choraden force-pushed the hg/rate_limit branch 2 times, most recently from 88ace92 to 2f340ee Compare September 13, 2023 14:37
@Choraden Choraden requested a review from mmatczuk September 13, 2023 14:46
@@ -0,0 +1,247 @@
package resource
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about putting it directly in forwarder. This is needed for config and CLI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

const defaultMaxBurstSize = 4 * 1024 * 1024 // Must be bigger than the biggest request.

func newRateLimiter(bandwidth resource.SizeSuffix) *rate.Limiter {
// The rate limiter is configured to allow a burst of 1/64th of the bandwidth or defaultMaxBurstSize.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave the original discussion and a pointer to rclone issue.
The point is that it does not allow high bandwidth if defaultMaxBurstSize is used.

@Choraden
Copy link
Contributor Author

@mmatczuk comments addressed

@@ -94,3 +97,50 @@ func TestFlagDenyDomain(t *testing.T) {
newClient(t, "https://www.google.com").GET("/").ExpectStatus(http.StatusForbidden)
newClient(t, httpbin).GET("/status/200").ExpectStatus(http.StatusOK)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those tests do not make any sense to me.

  • There should be one setup with rx and tx
  • Test must use more than one connection
  • TestFlagWriteLimit is in fact read limit
  • TestFlagReadLimit is doing sth very strange

We need an httpbin endpoint that copies request body to io.Discard

@Choraden Choraden force-pushed the hg/rate_limit branch 3 times, most recently from b3bf3a1 to 4780cea Compare September 14, 2023 11:57
@@ -24,6 +24,7 @@ func Handler() http.Handler {
m.HandleFunc("/delay/", delayHandler)
m.HandleFunc("/status/", statusHandler)
m.HandleFunc("/stream-bytes/", streamBytesHandler)
m.HandleFunc("/stream-discard/", streamDiscardHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stream comes from Dynamic data Generates random and dynamic data

It's more like sink just read all body, it feels like any function should do it.
You could be more creative and have count-bytes that one would return body size as a header etc.

@@ -359,6 +360,45 @@ func SetupFlagDenyDomain(l *setupList) {
)
}

func SetupFlagRateLimit(l *setupList) {
l.Add(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a single setup?

}()
}
wg.Wait()
if elapsed := time.Since(ts); elapsed.Round(time.Second) != expectedTime {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rounding is not great, let's say it can be 5% off and compute.

@Choraden Choraden force-pushed the hg/rate_limit branch 5 times, most recently from 41311a3 to 86fd15d Compare September 14, 2023 13:12
connections = 2
size = 5 * 1024 * 1024 // 5MiB
expectedTime = 6 * time.Second
acceptableRange = expectedTime * 5 / 100 // 5%
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about epsilon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

SizeSuffix code and tests were copied from rclone project https://github.com/rclone/rclone.

golangci: skip sizesuffix files

chore: go mod tidy
The package exports custom Listener with global rate limiters for every accepted connection.

The burst setup is inspired by the one in rclone project [1].
More on that in this thread [2].

[1] https://github.com/rclone/rclone/blob/cda09704a8078fe5cecd1107503de5ab8b401ad4/fs/accounting/token_bucket.go#L59-L77
[2] rclone/rclone#5507

golangci: disable err check in Limiter.WaitN
It reads the request body and sends back the number of bytes read in a header.
@@ -94,3 +98,79 @@ func TestFlagDenyDomain(t *testing.T) {
newClient(t, "https://www.google.com").GET("/").ExpectStatus(http.StatusForbidden)
newClient(t, httpbin).GET("/status/200").ExpectStatus(http.StatusOK)
}

func TestFlagReadLimit(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking since the test init sequence is almost identical that maybe we should have a helper function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted it, looks better indeed.

e2e/tests/flag_test.go Outdated Show resolved Hide resolved
@Choraden Choraden force-pushed the hg/rate_limit branch 3 times, most recently from 0871cfa to 5a1b0bd Compare September 14, 2023 14:16
@mmatczuk mmatczuk merged commit 595f74c into main Sep 15, 2023
@mmatczuk mmatczuk deleted the hg/rate_limit branch September 15, 2023 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants