Skip to content

Commit

Permalink
feat - add candiutils redis locker with options
Browse files Browse the repository at this point in the history
  • Loading branch information
9iksans committed Jan 16, 2025
1 parent 1788f7c commit 181a923
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 45 deletions.
64 changes: 29 additions & 35 deletions candiutils/locker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"time"

"github.com/golangid/candi/options"
"github.com/gomodule/redigo/redis"
)

Expand All @@ -15,39 +16,37 @@ type (
// RedisLocker lock using redis
RedisLocker struct {
pool *redis.Pool
lockeroptions LockerOptions
lockeroptions options.LockerOptions
}

// NoopLocker empty locker
NoopLocker struct{}

// Options for RedisLocker
LockerOptions struct {
Prefix string
TTL time.Duration
}

// Option function type for setting options
LockerOption func(*LockerOptions)
)

// WithPrefix sets the prefix for keys
func WithPrefixLocker(prefix string) LockerOption {
return func(o *LockerOptions) {
func WithPrefixLocker(prefix string) options.LockerOption {
return func(o *options.LockerOptions) {
o.Prefix = prefix
}
}

// WithTTL sets the default TTL for keys
func WithTTLLocker(ttl time.Duration) LockerOption {
return func(o *LockerOptions) {
func WithTTLLocker(ttl time.Duration) options.LockerOption {
return func(o *options.LockerOptions) {
o.TTL = ttl
}
}

// WithLimit sets the limit for keys
func WithLimitLocker(limit int) options.LockerOption {
return func(o *options.LockerOptions) {
o.Limit = limit
}
}

// NewRedisLocker constructor
func NewRedisLocker(pool *redis.Pool, opts ...LockerOption) *RedisLocker {
lockeroptions := LockerOptions{
func NewRedisLocker(pool *redis.Pool, opts ...options.LockerOption) *RedisLocker {
lockeroptions := options.LockerOptions{
Prefix: "LOCKFOR",
TTL: 0,
}
Expand Down Expand Up @@ -104,31 +103,26 @@ func (r *RedisLocker) IsLockedTTL(key string, TTL time.Duration) bool {
return incr > 1
}

// IsLockedTTLWithLimit checks if the key has been incremented more than the specified limit
// within the given TTL. If the key is being created for the first time, it sets the TTL.
// Example usage: check if a key has been incremented more than 10 times within 1 minute.
func (r *RedisLocker) IsLockedTTLWithLimit(key string, limit int, TTL time.Duration) bool {
func (r *RedisLocker) IsLockedWithOpts(key string, opts ...options.LockerOption) bool {
conn := r.pool.Get()
defer conn.Close()

lockOpt := r.lockeroptions
for _, opt := range opts {
opt(&lockOpt)
}

lockKey := fmt.Sprintf("%s:%s", r.lockeroptions.Prefix, key)
incr, err := redis.Int64(conn.Do("INCR", lockKey))
if err != nil {
return false
}

var expireTime time.Duration
if TTL > 0 {
expireTime = TTL
} else {
expireTime = r.lockeroptions.TTL
}

if expireTime > 0 && incr == 1 {
conn.Do("EXPIRE", lockKey, int(expireTime.Seconds()))
withLimit := lockOpt.Limit > 1
if lockOpt.TTL > 0 && !(withLimit && incr == 1) {
conn.Do("EXPIRE", lockKey, int(lockOpt.TTL.Seconds()))
}

return incr > int64(limit)
return incr > int64(lockOpt.Limit)
}

func (r *RedisLocker) HasBeenLocked(key string) bool {
Expand Down Expand Up @@ -242,6 +236,9 @@ func (NoopLocker) IsLocked(string) bool { return false }
// IsLockedTTL method
func (NoopLocker) IsLockedTTL(string, time.Duration) bool { return false }

// IsLockedWithOpts method
func (NoopLocker) IsLockedWithOpts(string, ...options.LockerOption) bool { return false }

// HasBeenLocked method
func (NoopLocker) HasBeenLocked(string) bool { return false }

Expand All @@ -254,13 +251,10 @@ func (NoopLocker) Reset(string) {}
// Lock method
func (NoopLocker) Lock(string, time.Duration) (func(), error) { return func() {}, nil }

func (NoopLocker) Disconnect(context.Context) error { return nil }

// GetPrefix method
func (NoopLocker) GetPrefixLocker() string { return "" }

// GetTTLLocker method
func (NoopLocker) GetTTLLocker() time.Duration { return 0 }

// IsLockedTTLWithLimit method
func (NoopLocker) IsLockedTTLWithLimit(string, int, time.Duration) bool { return false }
func (NoopLocker) Disconnect(context.Context) error { return nil }
8 changes: 6 additions & 2 deletions codebase/interfaces/locker.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package interfaces

import "time"
import (
"time"

"github.com/golangid/candi/options"
)

type (
// Locker abstraction, lock concurrent process
Locker interface {
IsLocked(key string) bool
IsLockedTTL(key string, ttl time.Duration) bool
IsLockedWithOpts(key string, opts ...options.LockerOption) bool
HasBeenLocked(key string) bool
Unlock(key string)
Reset(key string)
Lock(key string, timeout time.Duration) (unlockFunc func(), err error)
GetPrefixLocker() string
GetTTLLocker() time.Duration
IsLockedTTLWithLimit(key string, limit int, TTL time.Duration) bool
Closer
}
)
2 changes: 1 addition & 1 deletion mocks/candiutils/HTTPRequest.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions mocks/candiutils/WorkerPool.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mocks/candiutils/cronparser/Schedule.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions options/locker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package options

import "time"

type (
// Options for RedisLocker
LockerOptions struct {
Prefix string
TTL time.Duration
Limit int
}

// Option function type for setting options
LockerOption func(*LockerOptions)
)

0 comments on commit 181a923

Please sign in to comment.