Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/master' into protos
Browse files Browse the repository at this point in the history
  • Loading branch information
miparnisari committed Feb 19, 2024
2 parents 4d08381 + 452c5b5 commit e27b9f5
Show file tree
Hide file tree
Showing 24 changed files with 252 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:

env:
GOLANGCI_LINT_VERSION: v1.54.2
GOLANGCI_LINT_VERSION: v1.56.2

jobs:
lint:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version-file: './go.mod'
cache-dependency-path: './go.sum'
Expand All @@ -40,7 +40,7 @@ jobs:
comment-on-alert: true

- name: Save benchmark JSON to cache
uses: actions/cache/save@v3
uses: actions/cache/save@v4
with:
path: ./cache/benchmark-data.json
# Save with commit hash to avoid "cache already exists"
Expand Down
19 changes: 14 additions & 5 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
pull_request:
branches: [ master ]

env:
GOLANGCI_LINT_VERSION: v1.56.2

jobs:
test:
name: test
Expand All @@ -25,12 +28,12 @@ jobs:
run: ./contrib/check-version.sh

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}

- name: Cache deps
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
Expand All @@ -40,19 +43,25 @@ jobs:
- name: Install deps
run: go mod download

- name: golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: ${{ env.GOLANGCI_LINT_VERSION }}
skip-cache: true

- name: Test
run: go test -v -race -p=1 -count=1
go-bench:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0 # to be able to retrieve the last commit in master branch

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version-file: './go.mod'
cache-dependency-path: './go.sum'
Expand All @@ -71,7 +80,7 @@ jobs:
- name: Get benchmark JSON from Master branch
id: cache
uses: actions/cache/restore@v3
uses: actions/cache/restore@v4
with:
path: ./cache/benchmark-data.json
key: ${{ steps.get-master-branch-sha.outputs.sha }}-${{ runner.os }}-go-benchmark
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/on-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
password: ${{ github.token }}

- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
tags: |
ghcr.io/mailgun/gubernator:${{ github.event.release.tag_name }}
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
.vscode/
__pycache__
*.pyc
gubernator.egg-info/
.DS_Store
*.iml
googleapis/
coverage.out
coverage.html
/gubernator
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
VERSION=$(shell cat version)
LDFLAGS="-X main.Version=$(VERSION)"
GOLANGCI_LINT = $(GOPATH)/bin/golangci-lint
GOLANGCI_LINT_VERSION = 1.54.2
GOLANGCI_LINT_VERSION = 1.56.2

.PHONY: help
help:
Expand Down
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ rate_limits:
limit: 100
# The duration of the rate limit in milliseconds
duration: 1000
# The algorithm used to calculate the rate limit
# The algorithm used to calculate the rate limit
# 0 = Token Bucket
# 1 = Leaky Bucket
algorithm: 0
Expand Down Expand Up @@ -166,7 +166,7 @@ Given the following `Duration` values
* 3 = Weeks
* 4 = Months
* 5 = Years

Examples when using `Behavior = DURATION_IS_GREGORIAN`
* If `Duration = 2` (Days) then the rate limit will reset to `Current = 0` at the end of the current day the rate limit was created.
* If `Duration = 0` (Minutes) then the rate limit will reset to `Current = 0` at the end of the minute the rate limit was created.
Expand All @@ -178,6 +178,31 @@ This will reset the rate limit as if created new on first use.

When using Reset Remaining, the `Hits` field should be 0.

## Drain Over Limit Behavior
Users may add behavior `Behavior_DRAIN_OVER_LIMIT` to the rate check request.
A `GetRateLimits` call drains the remaining counter on first over limit event.
Then, successive `GetRateLimits` calls will return zero remaining counter and
not any residual value. This behavior works best with token bucket algorithm
because the `Remaining` counter will stay zero after an over limit until reset
time, whereas leaky bucket algorithm will immediately update `Remaining` to a
non-zero value.

This facilitates scenarios that require an over limit event to stay over limit
until the rate limit resets. This approach is necessary if a process must make
two rate checks, before and after a process, and the `Hit` amount is not known
until after the process.

- Before process: Call `GetRateLimits` with `Hits=0` to check the value of
`Remaining` counter. If `Remaining` is zero, it's known
that the rate limit is depleted and the process can be aborted.
- After process: Call `GetRateLimits` with a user specified `Hits` value. If
the call returns over limit, the process cannot be aborted because it had
already completed. Using `DRAIN_OVER_LIMIT` behavior, the `Remaining` count
will be drained to zero.

Once an over limit occurs in the "After" step, successive processes will detect
the over limit state in the "Before" step.

## Gubernator as a library
If you are using golang, you can use Gubernator as a library. This is useful if
you wish to implement a rate limit service with your own company specific model
Expand Down Expand Up @@ -346,4 +371,4 @@ Gubernator publishes Prometheus metrics for realtime monitoring. See
[prometheus.md](docs/prometheus.md) for details.

## OpenTelemetry Tracing (OTEL)
Gubernator supports OpenTelemetry. See [tracing.md](docs/tracing.md) for details.
Gubernator supports OpenTelemetry. See [tracing.md](docs/tracing.md) for details.
10 changes: 10 additions & 0 deletions algorithms.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ func tokenBucket(ctx context.Context, s Store, c Cache, r *RateLimitReq) (resp *
trace.SpanFromContext(ctx).AddEvent("Over the limit")
metricOverLimitCounter.Add(1)
rl.Status = Status_OVER_LIMIT
if HasBehavior(r.Behavior, Behavior_DRAIN_OVER_LIMIT) {
// DRAIN_OVER_LIMIT behavior drains the remaining counter.
t.Remaining = 0
rl.Remaining = 0
}
return rl, nil
}

Expand Down Expand Up @@ -394,6 +399,11 @@ func leakyBucket(ctx context.Context, s Store, c Cache, r *RateLimitReq) (resp *
if r.Hits > int64(b.Remaining) {
metricOverLimitCounter.Add(1)
rl.Status = Status_OVER_LIMIT
if HasBehavior(r.Behavior, Behavior_DRAIN_OVER_LIMIT) {
// DRAIN_OVER_LIMIT behavior drains the remaining counter.
b.Remaining = 0
rl.Remaining = 0
}
return rl, nil
}

Expand Down
Empty file modified buf.gen.yaml
100644 → 100755
Empty file.
6 changes: 5 additions & 1 deletion cmd/gubernator-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ func main() {
cmdLine := strings.Join(os.Args[1:], " ")
logrus.WithContext(ctx).Info("Command line: " + cmdLine)

conf, err := guber.SetupDaemonConfig(log, configFile)
configFileReader, err := os.Open(configFile)
if err != nil {
return fmt.Errorf("while opening config file: %s", err)
}
conf, err := guber.SetupDaemonConfig(log, configFileReader)
if err != nil {
return err
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/gubernator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ func main() {
}

// Read our config from the environment or optional environment config file
conf, err := gubernator.SetupDaemonConfig(logrus.StandardLogger(), configFile)
configFileReader, err := os.Open(configFile)
if err != nil {
log.WithError(err).Fatal("while opening config file")
}
conf, err := gubernator.SetupDaemonConfig(logrus.StandardLogger(), configFileReader)
checkErr(err, "while getting config")

ctx, cancel := context.WithTimeout(ctx, clock.Second*10)
Expand Down
23 changes: 9 additions & 14 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type BehaviorConfig struct {
GlobalTimeout time.Duration
// The max number of global updates we can batch into a single peer request
GlobalBatchLimit int
// ForceGlobal forces global mode on all rate limit checks.
// ForceGlobal forces global behavior on all rate limit checks.
ForceGlobal bool

// Number of concurrent requests that will be made to peers. Defaults to 100
Expand Down Expand Up @@ -263,17 +263,17 @@ func (d *DaemonConfig) ServerTLS() *tls.Config {
return nil
}

// SetupDaemonConfig returns a DaemonConfig object as configured by reading the provided config file
// and environment.
func SetupDaemonConfig(logger *logrus.Logger, configFile string) (DaemonConfig, error) {
// SetupDaemonConfig returns a DaemonConfig object that is the result of merging the lines
// in the provided configFile and the environment variables. See `example.conf` for all available config options and their descriptions.
func SetupDaemonConfig(logger *logrus.Logger, configFile io.Reader) (DaemonConfig, error) {
log := logrus.NewEntry(logger)
var conf DaemonConfig
var logLevel string
var logFormat string
var advAddr, advPort string
var err error

if configFile != "" {
if configFile != nil {
log.Infof("Loading env config: %s", configFile)
if err := fromEnvFile(log, configFile); err != nil {
return conf, err
Expand Down Expand Up @@ -628,15 +628,10 @@ func getEnvSlice(name string) []string {
return strings.Split(v, ",")
}

// Take values from a file in the format `GUBER_CONF_ITEM=my-value` and put them into the environment
// lines that begin with `#` are ignored
func fromEnvFile(log logrus.FieldLogger, configFile string) error {
fd, err := os.Open(configFile)
if err != nil {
return fmt.Errorf("while opening config file: %s", err)
}

contents, err := io.ReadAll(fd)
// Take values from a file in the format `GUBER_CONF_ITEM=my-value` and sets them as environment variables.
// Lines that begin with `#` are ignored
func fromEnvFile(log logrus.FieldLogger, configFile io.Reader) error {
contents, err := io.ReadAll(configFile)
if err != nil {
return fmt.Errorf("while reading config file '%s': %s", configFile, err)
}
Expand Down
40 changes: 40 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gubernator

import (
"fmt"
"os"
"strings"
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)

func TestParsesGrpcAddress(t *testing.T) {
os.Clearenv()
s := `
# a comment
GUBER_GRPC_ADDRESS=10.10.10.10:9000`
daemonConfig, err := SetupDaemonConfig(logrus.StandardLogger(), strings.NewReader(s))
require.NoError(t, err)
require.Equal(t, "10.10.10.10:9000", daemonConfig.GRPCListenAddress)
require.NotEmpty(t, daemonConfig.InstanceID)
}

func TestDefaultGrpcAddress(t *testing.T) {
os.Clearenv()
s := `
# a comment`
daemonConfig, err := SetupDaemonConfig(logrus.StandardLogger(), strings.NewReader(s))
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("%s:81", LocalHost()), daemonConfig.GRPCListenAddress)
require.NotEmpty(t, daemonConfig.InstanceID)
}

func TestDefaultInstanceId(t *testing.T) {
os.Clearenv()
s := ``
daemonConfig, err := SetupDaemonConfig(logrus.StandardLogger(), strings.NewReader(s))
require.NoError(t, err)
require.NotEmpty(t, daemonConfig.InstanceID)
}
4 changes: 2 additions & 2 deletions contrib/charts/gubernator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ description: A Helm Chart for gubernator

type: application

version: 2.2.1
version: 2.3.2

appVersion: 2.2.1
appVersion: 2.3.2
Loading

0 comments on commit e27b9f5

Please sign in to comment.