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

time: create ticker with instant first tick - rebooted #41885

Open
benma opened this issue Oct 9, 2020 · 2 comments
Open

time: create ticker with instant first tick - rebooted #41885

benma opened this issue Oct 9, 2020 · 2 comments

Comments

@benma
Copy link

@benma benma commented Oct 9, 2020

I have the same issue as #17601, but the issue was closed and locked with this reason:

This doesn't seem to rise to the level of new API. You don't have to "copy the section of code underneath the first <-t.C", you just have to put the code in its own function and call that function once at the start and once after each timeout interval. That seems clear enough.

However, "call the function once at the start" does not work when you are not in control of when the function is called.

Consider this clumsy implementation of a rate limiting in rateLimited, vs. the much more concise and elegant one in rateLimitedImmediate:

package main

import (
	"fmt"
	"sync"
	"time"
)

var interval = time.Second

type rateLimited struct {
	rateLimiter <-chan time.Time
	f           func() string
}

func (r *rateLimited) do() string {
	<-r.rateLimiter
	return r.f()
}

type rateLimitedImmediate struct {
	mu          sync.Mutex
	rateLimiter <-chan time.Time
	f           func() string
}

func (r *rateLimitedImmediate) do() string {
	r.mu.Lock()
	defer r.mu.Unlock()
	defer func() { r.rateLimiter = time.After(interval) }()
	<-r.rateLimiter
	return r.f()
}

func main() {
	r := &rateLimited{
		rateLimiter: time.Tick(interval),
		f:           func() string { return "result" },
	}

	fmt.Println(r.do())
	fmt.Println(r.do())
	fmt.Println(r.do())

	r2 := &rateLimitedImmediate{
		rateLimiter: time.After(0), // start immediately
		f:           func() string { return "result" },
	}

	fmt.Println(r2.do())
	fmt.Println(r2.do())
	fmt.Println(r2.do())
}

Just that the nice implementation in rateLimited cannot start immediately.

I am not in control of calling the function myself, the library client does that.

So I second the need for a stdlib solution like proposed in the original issue, and hope the issue can be re-opened (or discussed here).

@seankhliao
Copy link
Contributor

@seankhliao seankhliao commented Oct 9, 2020

in this specific case

package main

import (
	"context"
	"fmt"
	"time"

	"golang.org/x/time/rate"
)

var interval = time.Second

type limited struct {
	l *rate.Limiter
	f func() string
}

func (l *limited) do() string {
	l.l.Wait(context.Background())
	return l.f()
}

func main() {
	r3 := limited{
		l: rate.NewLimiter(rate.Every(interval), 1),
		f: func() string { return "result" },
	}
	fmt.Println(r3.do())
	fmt.Println(r3.do())
	fmt.Println(r3.do())
}
@ALTree ALTree added this to the Unplanned milestone Oct 9, 2020
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 9, 2020

It seems to me that the rate.NewLimiter function is going to need to have some way to choose between waiting for an interval before calling the function, or calling the function immediately. And you can write this without needing any change to the time package.

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

Successfully merging a pull request may close this issue.

None yet
5 participants