Skip to content
This repository has been archived by the owner on Jun 24, 2019. It is now read-only.

[FEATURE] RunWithLock (wrapper) (#1) #6

Merged
merged 8 commits into from
Nov 13, 2017
Merged

[FEATURE] RunWithLock (wrapper) (#1) #6

merged 8 commits into from
Nov 13, 2017

Conversation

im-kulikov
Copy link
Contributor

  • type name will be used as lock.LockOptions by other packages, and that stutters; consider calling this Options (golint)

  • feature RunWithLock

  • add go 1.9.2

  • coveralls

  • improve caching

* type name will be used as lock.LockOptions by other packages, and that stutters; consider calling this Options (golint)

* feature RunWithLock

* add go 1.9.2

* coveralls

* 🤦 i must read before insert

* fix from http://docs.coveralls.io/go

* we must do it once

* improve caching
@im-kulikov
Copy link
Contributor Author

* fix linter `collides with package name`
* disable coveralls (not work in PR)
.travis.yml Outdated
go:
- 1.6.4
- 1.7.5
- 1.8
- 1.9.2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified to 1.9.x for 1.9 branch(1.8.x etc)

lock.go Outdated

if l.opts.RetriesCount > 0 && retries <= 0 {
break
} else if l.opts.RetriesCount > 0 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else is not needed ;)

lock_test.go Outdated
@@ -197,6 +218,93 @@ var _ = Describe("Lock", func() {
Expect(res).To(Equal(int32(1)))
})

It("should run with locks and prevent fuzzing", func() {
res := int32(0)
wg := new(sync.WaitGroup)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK var wg sync.WaitGroup is more preferred style.

lock_test.go Outdated
if err != nil {
atomic.AddInt32(&res, 100)
return
} else if !ok {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else is not needed or ok, err := lock.Lock() can be moved to if-statement.

lock_test.go Outdated
res++
return nil
}, &Options{RetriesCount: count})
wg.Done()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be deferred?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, this will fail tests

@im-kulikov
Copy link
Contributor Author

@bsm ping 🙂

@im-kulikov
Copy link
Contributor Author

@cristaloleg, @edganiukov, @DmitriyMV thanks for review

Copy link
Member

@dim dim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. Before I release a new version I would like to incorporate a few additional changes. Could you please address the feedback, I will then also add a little PR on top. Thanks again!

lock.go Outdated
return nil, err
} else if !ok {
return nil, ErrCanntGetLock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, that's a breaking change, in which case I will need to release a new major version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I roll back?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, don't worry, just explaining that I need to bump the major version. could you please also mention this in the function comment

lock.go Outdated
}

// NewLock creates a new distributed lock on key
func NewLock(client RedisClient, key string, opts *LockOptions) *Lock {
func NewLock(client RedisClient, key string, opts *Options) *Lock {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the new major version, I would like to rename this to just New

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I can do this

lock.go Outdated

token string
mutex sync.Mutex
}

// RunWithLock run some code with Redis Lock
func RunWithLock(client RedisClient, key string, handler Handler, opts *Options) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you pls change this to: func Run(client RedisClient, key string, opts *Options, handler func() error) error, i.e. rename func name and put the handler at the end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, no problems

lock.go Outdated
// RedisClient is a minimal client interface
type RedisClient interface {
SetNX(key string, value interface{}, expiration time.Duration) *redis.BoolCmd
Eval(script string, keys []string, args ...interface{}) *redis.Cmd
}

// Handler is method wrapper
type Handler func() error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is no need for this, handler can be just inlined

lock.go Outdated
@@ -13,35 +14,52 @@ import (
const luaRefresh = `if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end`
const luaRelease = `if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end`

var ErrCanntGetLock = errors.New("can't get lock")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you pls rename this to ErrCannotGetLock = errors.New("cannot get lock")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm.. no problems

lock.go Outdated
if opts == nil {
opts = new(LockOptions)
opts = new(Options)
}
return &Lock{client: client, key: key, opts: *opts.normalize()}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you are at it, maybe rename the struct from Lock to Locker too?

@im-kulikov
Copy link
Contributor Author

@dim yep.. im@kulikov.im

@dim dim merged commit cf92a2f into bsm:master Nov 13, 2017
@im-kulikov
Copy link
Contributor Author

im-kulikov commented Nov 13, 2017

@dim oops, last commit was not included in the merge, can you remerge?

@im-kulikov
Copy link
Contributor Author

or rebase master with this changes

@dim
Copy link
Member

dim commented Nov 13, 2017

@im-kulikov don't worry, I will fix in my PR. Just one question: how is RetriesCount useful? I am struggling a bit with it as it makes things much more complicated for no obvious reason. You already have WaitRetry which is a delay between retries and a WaitTimeout which is the overall retry timeout. Given these two values, (additionally) counting the attempts seems unnecessary, no?

@im-kulikov
Copy link
Contributor Author

im-kulikov commented Nov 13, 2017

@dim hmm.. not quite. look, if you want to repeat only a certain number of times - it is enough to specify RetriesCount will happen only this number of attempts with a delay in WaitRetry

WaitTimeout suggests that you yourself need to count the number of attempts and specify it in the form of time.Duration

@dim
Copy link
Member

dim commented Nov 13, 2017

I think that's the bit I don't "get", I don't quite see a use case for:

if you want to repeat only a certain number of times

As a user I may want to limit the time I am willing to wait to get a lock, but do I really care how many calls are made to redis in this time? (although you can even kind-of control that via WaitRetry).

Sorry, I really feel this is making the API unnecessarily complicated. At the moment, to enable retries, you only need to set WaitTimeout > 0 but with your patch, you also need to enable RetriesCount which is not really intuitive in my opinion

@dim
Copy link
Member

dim commented Nov 13, 2017

@im-kulikov pls take a look at #7

@im-kulikov
Copy link
Contributor Author

@dim In fact, nothing is changed. Only added option to specify the number of attempts.
That is, if I want to use the WaitTimeout and WaitRetry - I is sufficient to specify a WaitTimeout (and, alternatively, to change WaitRetry).
If I want to make a few attempts - to me it is enough to specify RetriesCount

@dim
Copy link
Member

dim commented Nov 13, 2017

Sorry, but can you please explain the use case for this? As said ...

I really feel this is making the API unnecessarily complicated. At the moment, to enable retries, you only need to set WaitTimeout > 0 but with your patch, you also need to enable RetriesCount which is not really intuitive in my opinion

@im-kulikov
Copy link
Contributor Author

im-kulikov commented Nov 13, 2017

opts = Options{RetriesCount: 3}
RunWithLock(client, key, &opts, func() error {
  //... do something
  return nil
})

will be made 3 attempts ≈ 30 ms (WaitRetry(10ms) * RetriesCount(3) = 30ms)

@dim
Copy link
Member

dim commented Nov 13, 2017

No, I understand that, but if we go down this route, I would consider removing WaitTimeout. I don't think it makes sense to have both. Will update #7

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants