ratelimit

package
v0.0.0-...-ba16981 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2025 License: BSD-3-Clause Imports: 3 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FixedWindow

type FixedWindow struct {
	Now func() time.Time
	// contains filtered or unexported fields
}

FixedWindow acts as a counter for a given time period.

Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	now := time.Now()

	rl := ratelimit.NewFixedWindow(5, time.Second)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow()
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, true)
	call(3*time.Millisecond, true)
	call(4*time.Millisecond, true)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, false)
	call(1000*time.Millisecond, true)
	call(1100*time.Millisecond, true)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, true)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, false)

	fmt.Println("fixed window")
	w.Flush()

}
Output:

fixed window
      t| allow|
     0s|  true|
    1ms|  true|
    2ms|  true|
    3ms|  true|
    4ms|  true|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms| false|
     1s|  true|
   1.1s|  true|
   1.2s|  true|
   1.3s|  true|
   1.4s|  true|
   1.5s| false|
 1.999s| false|

func NewFixedWindow

func NewFixedWindow(limit int, period time.Duration) *FixedWindow

func (*FixedWindow) Allow

func (r *FixedWindow) Allow() bool

Allow checks if a request is allowed. Special case of AllowN that consumes only 1 token.

func (*FixedWindow) AllowN

func (r *FixedWindow) AllowN(n int) bool

AllowN checks if a request is allowed. Consumes n token if allowed.

func (*FixedWindow) Remaining

func (r *FixedWindow) Remaining() int

func (*FixedWindow) RetryAt

func (r *FixedWindow) RetryAt() time.Time

type GCRA

type GCRA struct {
	Now func() time.Time
	// contains filtered or unexported fields
}
Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	// We need to truncate the time to the nearest second, as the sliding window
	// is based on the window.
	now := time.Now().Truncate(time.Second)

	rl := ratelimit.NewGCRA(5, time.Second, 1)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow()
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, false)
	call(3*time.Millisecond, false)
	call(4*time.Millisecond, false)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, true)
	call(1000*time.Millisecond, true)
	call(1100*time.Millisecond, false)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, false)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, true)

	fmt.Println("gcra")
	w.Flush()

}
Output:

gcra
      t| allow|
     0s|  true|
    1ms|  true|
    2ms| false|
    3ms| false|
    4ms| false|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms|  true|
     1s|  true|
   1.1s| false|
   1.2s|  true|
   1.3s| false|
   1.4s|  true|
   1.5s| false|
 1.999s|  true|

func NewGCRA

func NewGCRA(limit int, period time.Duration, burst int) *GCRA

func (*GCRA) Allow

func (r *GCRA) Allow() bool

func (*GCRA) AllowN

func (r *GCRA) AllowN(n int) bool

func (*GCRA) RetryAt

func (r *GCRA) RetryAt() time.Time

type MultiFixedWindow

type MultiFixedWindow struct {
	Now func() time.Time
	// contains filtered or unexported fields
}

MultiFixedWindow acts as a counter for a given time period.

Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	now := time.Now()

	rl := ratelimit.NewMultiFixedWindow(5, time.Second)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow("key")
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, true)
	call(3*time.Millisecond, true)
	call(4*time.Millisecond, true)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, false)
	call(1000*time.Millisecond, true)
	call(1100*time.Millisecond, true)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, true)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, false)

	fmt.Println("multi fixed window")
	w.Flush()

}
Output:

multi fixed window
      t| allow|
     0s|  true|
    1ms|  true|
    2ms|  true|
    3ms|  true|
    4ms|  true|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms| false|
     1s|  true|
   1.1s|  true|
   1.2s|  true|
   1.3s|  true|
   1.4s|  true|
   1.5s| false|
 1.999s| false|

func NewMultiFixedWindow

func NewMultiFixedWindow(limit int, period time.Duration) *MultiFixedWindow

func (*MultiFixedWindow) Allow

func (r *MultiFixedWindow) Allow(key string) bool

Allow checks if a request is allowed. Special case of AllowN that consumes only 1 token.

func (*MultiFixedWindow) AllowN

func (r *MultiFixedWindow) AllowN(key string, n int) bool

AllowN checks if a request is allowed. Consumes n token if allowed.

func (*MultiFixedWindow) Clear

func (r *MultiFixedWindow) Clear()

func (*MultiFixedWindow) Remaining

func (r *MultiFixedWindow) Remaining(key string) int

func (*MultiFixedWindow) RetryAt

func (r *MultiFixedWindow) RetryAt(key string) time.Time

func (*MultiFixedWindow) Size

func (r *MultiFixedWindow) Size() int

type MultiGCRA

type MultiGCRA struct {
	Now func() time.Time
	// contains filtered or unexported fields
}
Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	// We need to truncate the time to the nearest second, as the sliding window
	// is based on the window.
	now := time.Now().Truncate(time.Second)

	rl := ratelimit.NewMultiGCRA(5, time.Second, 1)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow("key")
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, false)
	call(3*time.Millisecond, false)
	call(4*time.Millisecond, false)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, true)
	call(1000*time.Millisecond, true)
	call(1100*time.Millisecond, false)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, false)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, true)

	fmt.Println("multi gcra")
	w.Flush()

}
Output:

multi gcra
      t| allow|
     0s|  true|
    1ms|  true|
    2ms| false|
    3ms| false|
    4ms| false|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms|  true|
     1s|  true|
   1.1s| false|
   1.2s|  true|
   1.3s| false|
   1.4s|  true|
   1.5s| false|
 1.999s|  true|

func NewMultiGCRA

func NewMultiGCRA(limit int, period time.Duration, burst int) *MultiGCRA

func (*MultiGCRA) Allow

func (r *MultiGCRA) Allow(key string) bool

func (*MultiGCRA) AllowN

func (r *MultiGCRA) AllowN(key string, n int) bool

func (*MultiGCRA) Clear

func (r *MultiGCRA) Clear()

func (*MultiGCRA) RetryAt

func (r *MultiGCRA) RetryAt(key string) time.Time

func (*MultiGCRA) Size

func (r *MultiGCRA) Size() int

type MultiSlidingWindow

type MultiSlidingWindow struct {
	Now func() time.Time
	// contains filtered or unexported fields
}
Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	now := time.Now()

	rl := ratelimit.NewMultiSlidingWindow(5, time.Second)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow("key")
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, true)
	call(3*time.Millisecond, true)
	call(4*time.Millisecond, true)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, false)
	call(1000*time.Millisecond, false)
	call(1100*time.Millisecond, false)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, false)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, true)

	fmt.Println("multi sliding window")
	w.Flush()

}
Output:

multi sliding window
      t| allow|
     0s|  true|
    1ms|  true|
    2ms|  true|
    3ms|  true|
    4ms|  true|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms| false|
     1s| false|
   1.1s| false|
   1.2s|  true|
   1.3s| false|
   1.4s|  true|
   1.5s| false|
 1.999s|  true|

func NewMultiSlidingWindow

func NewMultiSlidingWindow(limit int, period time.Duration) *MultiSlidingWindow

func (*MultiSlidingWindow) Allow

func (r *MultiSlidingWindow) Allow(key string) bool

func (*MultiSlidingWindow) AllowN

func (r *MultiSlidingWindow) AllowN(key string, n int) bool

func (*MultiSlidingWindow) Clear

func (r *MultiSlidingWindow) Clear()

func (*MultiSlidingWindow) Remaining

func (r *MultiSlidingWindow) Remaining(key string) int

func (*MultiSlidingWindow) Size

func (r *MultiSlidingWindow) Size() int

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

func New

func New(rls ...ratelimiter) *RateLimiter

func (*RateLimiter) Allow

func (r *RateLimiter) Allow() bool

func (*RateLimiter) AllowN

func (r *RateLimiter) AllowN(n int) bool

type SlidingWindow

type SlidingWindow struct {
	Now func() time.Time
	// contains filtered or unexported fields
}
Example
package main

import (
	"fmt"
	"log"
	"os"
	"text/tabwriter"
	"time"

	"github.com/sneakycursor/core/sync/ratelimit"
)

func main() {
	// We need to truncate the time to the nearest second, as the sliding window
	// is based on the window.
	now := time.Now()

	rl := ratelimit.NewSlidingWindow(5, time.Second)
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight|tabwriter.Debug)
	fmt.Fprintf(w, "%s\t%s\t\n", "t", "allow")
	call := func(duration time.Duration, allow bool) {
		rl.Now = func() time.Time {
			return now.Add(duration)
		}

		allowed := rl.Allow()
		if allow != allowed {
			log.Fatalf("unexpected allow at %v", duration)
		}
		fmt.Fprintf(w, "%s\t%t\t\n", duration, allowed)
	}

	call(0, true)
	call(1*time.Millisecond, true)
	call(2*time.Millisecond, true)
	call(3*time.Millisecond, true)
	call(4*time.Millisecond, true)
	call(99*time.Millisecond, false)
	call(100*time.Millisecond, false)
	call(101*time.Millisecond, false)
	call(999*time.Millisecond, false)
	call(1000*time.Millisecond, false)
	call(1100*time.Millisecond, false)
	call(1200*time.Millisecond, true)
	call(1300*time.Millisecond, false)
	call(1400*time.Millisecond, true)
	call(1500*time.Millisecond, false)
	call(1999*time.Millisecond, true)

	fmt.Println("sliding window")
	w.Flush()

}
Output:

sliding window
      t| allow|
     0s|  true|
    1ms|  true|
    2ms|  true|
    3ms|  true|
    4ms|  true|
   99ms| false|
  100ms| false|
  101ms| false|
  999ms| false|
     1s| false|
   1.1s| false|
   1.2s|  true|
   1.3s| false|
   1.4s|  true|
   1.5s| false|
 1.999s|  true|

func NewSlidingWindow

func NewSlidingWindow(limit int, period time.Duration) *SlidingWindow

func (*SlidingWindow) Allow

func (r *SlidingWindow) Allow() bool

func (*SlidingWindow) AllowN

func (r *SlidingWindow) AllowN(n int) bool

func (*SlidingWindow) Remaining

func (r *SlidingWindow) Remaining() int

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL