Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add limiter package #8

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open

Conversation

ac55-sanger
Copy link
Contributor

Refactor limiter.go
Add tests for group.go

Add limiter.go
Add tests for limiter.go
Add group.go
Add tests for group.go
exclude a couple of linters for limiter_test.go
Add go-deadlock
Add go-deadlock
@ac55-sanger ac55-sanger requested a review from sb10 November 25, 2020 14:14
Copy link
Collaborator

@sb10 sb10 left a comment

Choose a reason for hiding this comment

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

I'm mostly reviewing my own code here, but that's the point: we are trying to improve the developability (make it easier to read and understand) and stability (restructure to avoid the possibility of concurrency errors) of the old code, not just add tests for it.

@@ -56,6 +56,11 @@ issues:
- path: clog.go
linters:
- gochecknoinits
- path: limiter_test.go
linters:
- nestif
Copy link
Collaborator

Choose a reason for hiding this comment

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

Tests should themselves be clean code, so not too complicated. Extract out the complicated parts in to methods as normal. The only exception we should apply to tests is function length, since we expect lots of (simple) tests. Function length is already excluded on _test.go files.

/*******************************************************************************
* Copyright (c) 2020 Genome Research Ltd.
*
* Author: Sendu Bala <sb10@sanger.ac.uk>.
Copy link
Collaborator

Choose a reason for hiding this comment

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

The idea was to do from-scratch TDD, not copy/paste my old code and add tests afterwards...


g.current--

// notify callers who passed a channel to notifyDecrement(), but do it
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any time you have a separate block of code within a func with a comment to explain what it is, you are almost certainly looking at something that should be extracted out to its own function or struct or package.
Extract this out.

)

func TestGroup(t *testing.T) {
Convey("Test to check the group", t, func() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Name tests such that the test and its nested tests form sentences that describe desired behaviour.
So in this case, it might be "Given a Group", and the first nested Convey might be "you can set the limit".
So it reads "Given a Group you can set the limit".


// increment increases the current count of this group. You must call
// canIncrement() first to make sure you won't go over the limit (and hold a
// lock over the 2 calls to avoid a race condition).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a way to restructure the whole package such that you don't have to hold an external lock across 2 methods, and instead it is able to be thread-safe internally and without room for error?

//
// If an optional wait duration is supplied, will wait for up to the given wait
// period for an increment of every group to be possible.
func (l *Limiter) Increment(groups []string, wait ...time.Duration) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Reimplement to take a context as first arg instead of an optional wait, and to always check for context cancellation.

func (l *Limiter) Increment(groups []string, wait ...time.Duration) bool {
l.mu.Lock()
if l.checkGroups(groups) {
l.incrementGroups(groups)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should somehow be restructured in to a single method call that does internal locking.

l.registerGroupNotifications(groups, ch)
l.mu.Unlock()

continue
Copy link
Collaborator

Choose a reason for hiding this comment

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

continue not necessary

for {
select {
case <-ch:
l.mu.Lock()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This check, increment and then register block is the same as above. Extract in to a method regardless, and then possibly only call it from one place if you also refactor an efficient way to always wait on the context.


if l.Increment(groups) {
atomic.AddUint64(&incs, 1)
time.Sleep(100 * time.Millisecond)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a way to do this and other tests below without apparently arbitrary sleep/wait times?

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

Successfully merging this pull request may close these issues.

2 participants