Skip to content

Commit

Permalink
use gateway explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
da440dil committed Aug 4, 2019
1 parent ca93531 commit 462e916
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 51 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![GoDoc](https://godoc.org/github.com/da440dil/go-counter?status.svg)](https://godoc.org/github.com/da440dil/go-counter)
[![Go Report Card](https://goreportcard.com/badge/github.com/da440dil/go-counter)](https://goreportcard.com/report/github.com/da440dil/go-counter)

Distributed rate limiting using [Redis](https://redis.io/).
Distributed rate limiting with pluggable storage to store a counters state.

## Example

Expand All @@ -18,14 +18,15 @@ import (
"time"

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

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

c, err := counter.NewCounter(client, 2, time.Millisecond*100)
c, err := counter.NewCounter(gw.NewGateway(client), 2, time.Millisecond*100)
if err != nil {
panic(err)
}
Expand Down
19 changes: 5 additions & 14 deletions counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ package counter
import (
"errors"
"time"

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

// Gateway to storage to store a counter value.
type Gateway interface {
// Incr sets key value and TTL of key if key not exists.
// Increments key value if key exists.
// Returns key value after increment, TTL of a key in milliseconds.
// Returns key value after increment.
// Returns TTL of a key in milliseconds.
Incr(key string, ttl int) (int, int, error)
}

Expand Down Expand Up @@ -48,11 +46,12 @@ type Counter struct {
prefix string
}

// NewCounterWithGateway creates new Counter using custom Gateway.
// NewCounter creates new Counter.
// Gateway is gateway to storage to store a counter value.
// Limit is maximum key value, must be greater than 0.
// TTL is TTL of a key, must be greater than or equal to 1 millisecond.
// Options are functional options.
func NewCounterWithGateway(gateway Gateway, limit int, ttl time.Duration, options ...Option) (*Counter, error) {
func NewCounter(gateway Gateway, limit int, ttl time.Duration, options ...Option) (*Counter, error) {
if limit < 1 {
return nil, ErrInvalidLimit
}
Expand All @@ -73,14 +72,6 @@ func NewCounterWithGateway(gateway Gateway, limit int, ttl time.Duration, option
return c, nil
}

// NewCounter creates new Counter using Redis Gateway.
// Limit is maximum key value, must be greater than 0.
// TTL is TTL of a key, must be greater than or equal to 1 millisecond.
// Options are functional options.
func NewCounter(client *redis.Client, limit int, ttl time.Duration, options ...Option) (*Counter, error) {
return NewCounterWithGateway(gw.NewGateway(client), limit, ttl, options...)
}

// Count increments key value.
// Returns limit remainder.
// Returns TTLError if limit exceeded.
Expand Down
42 changes: 9 additions & 33 deletions counter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"time"
"unsafe"

"github.com/go-redis/redis"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
Expand All @@ -31,45 +30,22 @@ var p = make([]byte, MaxKeySize+1)
var invalidKey = *(*string)(unsafe.Pointer(&p))

func TestNewCounter(t *testing.T) {
client := redis.NewClient(&redis.Options{Addr: Addr, DB: DB})
defer client.Close()

t.Run("ErrInvalidLimit", func(t *testing.T) {
_, err := NewCounter(client, 0, time.Microsecond)
assert.Error(t, err)
assert.Equal(t, ErrInvalidLimit, err)
})

t.Run("ErrInvalidTTL", func(t *testing.T) {
_, err := NewCounter(client, Limit, time.Microsecond)
assert.Error(t, err)
assert.Equal(t, ErrInvalidTTL, err)
})

t.Run("success", func(t *testing.T) {
c, err := NewCounter(client, Limit, TTL)
assert.NoError(t, err)
assert.IsType(t, &Counter{}, c)
})
}

func TestNewCounterWithGateway(t *testing.T) {
gw := &gwMock{}

t.Run("ErrInvalidLimit", func(t *testing.T) {
_, err := NewCounterWithGateway(gw, 0, time.Microsecond)
_, err := NewCounter(gw, 0, time.Microsecond)
assert.Error(t, err)
assert.Equal(t, ErrInvalidLimit, err)
})

t.Run("ErrInvalidTTL", func(t *testing.T) {
_, err := NewCounterWithGateway(gw, Limit, time.Microsecond)
_, err := NewCounter(gw, Limit, time.Microsecond)
assert.Error(t, err)
assert.Equal(t, ErrInvalidTTL, err)
})

t.Run("success", func(t *testing.T) {
c, err := NewCounterWithGateway(gw, Limit, TTL)
c, err := NewCounter(gw, Limit, TTL)
assert.NoError(t, err)
assert.IsType(t, &Counter{}, c)
})
Expand All @@ -79,13 +55,13 @@ func TestOptions(t *testing.T) {
gw := &gwMock{}

t.Run("ErrInvaldKey", func(t *testing.T) {
_, err := NewCounterWithGateway(gw, Limit, TTL, WithPrefix(invalidKey))
_, err := NewCounter(gw, Limit, TTL, WithPrefix(invalidKey))
assert.Error(t, err)
assert.Equal(t, ErrInvalidKey, err)
})

t.Run("success", func(t *testing.T) {
c, err := NewCounterWithGateway(gw, Limit, TTL, WithPrefix(""))
c, err := NewCounter(gw, Limit, TTL, WithPrefix(""))
assert.NoError(t, err)
assert.IsType(t, &Counter{}, c)
})
Expand All @@ -97,7 +73,7 @@ func TestCounter(t *testing.T) {
t.Run("ErrInvaldKey", func(t *testing.T) {
gw := &gwMock{}

c, err := NewCounterWithGateway(gw, Limit, TTL)
c, err := NewCounter(gw, Limit, TTL)
assert.NoError(t, err)

v, err := c.Count(invalidKey)
Expand All @@ -111,7 +87,7 @@ func TestCounter(t *testing.T) {
gw := &gwMock{}
gw.On("Incr", Key, ttl).Return(-1, 42, e)

c, err := NewCounterWithGateway(gw, Limit, TTL)
c, err := NewCounter(gw, Limit, TTL)
assert.NoError(t, err)

v, err := c.Count(Key)
Expand All @@ -126,7 +102,7 @@ func TestCounter(t *testing.T) {
gw := &gwMock{}
gw.On("Incr", Key, ttl).Return(Limit+1, et, nil)

c, err := NewCounterWithGateway(gw, Limit, TTL)
c, err := NewCounter(gw, Limit, TTL)
assert.NoError(t, err)

v, err := c.Count(Key)
Expand All @@ -140,7 +116,7 @@ func TestCounter(t *testing.T) {
gw := &gwMock{}
gw.On("Incr", Key, ttl).Return(Limit, 42, nil)

c, err := NewCounterWithGateway(gw, Limit, TTL)
c, err := NewCounter(gw, Limit, TTL)
assert.NoError(t, err)

v, err := c.Count(Key)
Expand Down
3 changes: 2 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import (
"time"

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

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

c, err := counter.NewCounter(client, 2, time.Millisecond*100)
c, err := counter.NewCounter(gw.NewGateway(client), 2, time.Millisecond*100)
if err != nil {
panic(err)
}
Expand Down
3 changes: 2 additions & 1 deletion redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ func NewGateway(client *redis.Client) *Gateway {

// Incr sets key value and TTL of key if key not exists.
// Increments key value if key exists.
// Returns key value after increment, TTL of a key in milliseconds.
// Returns key value after increment.
// Returns TTL of a key in milliseconds.
func (gw *Gateway) Incr(key string, ttl int) (int, int, error) {
res, err := incr.Run(gw.client, []string{key}, ttl).Result()
if err != nil {
Expand Down

0 comments on commit 462e916

Please sign in to comment.