Skip to content

da440dil/go-counter

Repository files navigation

go-counter

Build Status Coverage Status Go Reference Go Report Card

Distributed rate limiting using Redis.

Example usage:

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/da440dil/go-counter"
	"github.com/go-redis/redis/v8"
)

func main() {
	client := redis.NewClient(&redis.Options{})
	defer client.Close()

	ctx := context.Background()
	key := "key"
	err := client.Del(ctx, key).Err()
	requireNoError(err)

	// Create limiter with 2 limits.
	limiter := counter.NewLimiter(
		client,
		// First limit: no more than 3 limiter calls within 1 second.
		counter.WithLimit(time.Second, 3),
		// Second limit: no more than 5 limiter calls within 2 seconds.
		counter.WithLimit(time.Second*2, 5),
	)

	limit := func() {
		r, err := limiter.Limit(ctx, key)
		requireNoError(err)
		fmt.Printf(
			"Result: { ok: %v, counter: %v, remainder: %v, ttl: %v }\n",
			r.OK(), r.Counter(), r.Remainder(), r.TTL(),
		)
	}
	limitN := func(n int) {
		var wg sync.WaitGroup
		wg.Add(n)
		for i := 0; i < n; i++ {
			go func() {
				defer wg.Done()
				limit()
			}()
		}
		wg.Wait()
	}

	limitN(4)
	time.Sleep(time.Second) // wait for the next window to start
	limitN(2)
	// Output:
	// Result: { ok: true, counter: 1, remainder: 2, ttl: 1s }
	// Result: { ok: true, counter: 3, remainder: 0, ttl: 998ms }
	// Result: { ok: true, counter: 2, remainder: 1, ttl: 998ms }
	// Result: { ok: false, counter: 3, remainder: 0, ttl: 998ms }
	// Result: { ok: true, counter: 5, remainder: 0, ttl: 993ms }
	// Result: { ok: false, counter: 5, remainder: 0, ttl: 993ms }
}

func requireNoError(err error) {
	if err != nil {
		panic(err)
	}
}