finished deadline pattern

This commit is contained in:
eamon 2020-06-05 17:36:30 +08:00
parent 9bb39b3fdf
commit a4a57a5556
4 changed files with 71 additions and 54 deletions

View File

@ -2,4 +2,6 @@
do a thing ,until the deadline time point
as known as timeout pattern
which act like time.after(), but time.after() only do once.

View File

@ -3,10 +3,10 @@
* @Author: Edward
* @Date: 2020-06-05 12:43:39
* @Last Modified by: Edward
* @Last Modified time: 2020-06-05 12:56:40
* @Last Modified time: 2020-06-05 17:34:37
*/
// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go.
// Package deadline implements deadline (also known as "timeout") resiliency pattern for Go.
package deadline
import (
@ -14,38 +14,42 @@ import (
"time"
)
// ErrTimedOut is the error returned from Run when the deadline expires.
// ErrTimedOut is the error returned from Run when the Worker expires.
var ErrTimedOut = errors.New("timed out waiting for function to finish")
// Deadline implements the deadline/timeout resiliency pattern.
type Deadline struct {
// Worker implements the Deadline/timeout resiliency pattern.
// worker do the target job
type Worker struct {
timeout time.Duration
action string
}
// New constructs a new Deadline with the given timeout.
func New(timeout time.Duration, sometile string) *Deadline {
return &Deadline{
// New create a new Worker with the given timeout.and tile
func New(timeout time.Duration, someActionTitle string) *Worker {
return &Worker{
timeout: timeout,
action: someActionTitle,
}
}
// Run runs the given function, passing it a stopper channel. If the deadline passes before
// Run runs the given function, passing it a stopper channel. If the Worker passes before
// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper
// channel so that the work function can attempt to exit gracefully. It does not (and cannot)
// simply kill the running function, so if it doesn't respect the stopper channel then it may
// keep running after the deadline passes. If the function finishes before the deadline, then
// keep running after the Worker passes. If the function finishes before the Worker, then
// the return value of the function is returned from Run.
func (d *Deadline) Run(work func(<-chan struct{}) error) error {
result := make(chan error)
func (d *Worker) Run(work func(stopperSignal chan error) error) error {
stopper := make(chan struct{})
result := make(chan error)
//we can stop the work in advance
stopper := make(chan error, 1)
go func() {
value := work(stopper)
select {
case result <- value:
case <-stopper:
case stopError := <-stopper:
result <- stopError
}
}()

View File

@ -2,64 +2,75 @@ package deadline
import (
"errors"
"fmt"
"testing"
"time"
)
func takes5ms(stopper <-chan struct{}) error {
func workerTakes5ms(stopper chan error) error {
fmt.Println("i'm doing this job in 5ms")
time.Sleep(5 * time.Millisecond)
return nil
}
func takes20ms(stopper <-chan struct{}) error {
func workerTakes20ms(stopper chan error) error {
fmt.Println("i'm doing this job in 20ms,so work will timeout")
time.Sleep(20 * time.Millisecond)
return nil
}
func returnsError(stopper <-chan struct{}) error {
func cancelWork(stopper chan error) error {
fmt.Println("i'm doing this job")
stopper <- errors.New("canceled job") //cancel job
time.Sleep(5 * time.Millisecond)
fmt.Println("job canceled")
return nil
}
func returnsError(stopper chan error) error {
fmt.Println("i'm doing this job but error occurred")
return errors.New("foo")
}
func TestMultiDeadline(t *testing.T) {
dl := New(10*time.Millisecond, "test multi deadline case")
if err := dl.Run(takes5ms); err != nil {
dl := New(15*time.Millisecond, "test multi deadline case")
if err := dl.Run(workerTakes5ms); err != nil {
t.Error(err)
}
if err := dl.Run(takes20ms); err != ErrTimedOut {
err := dl.Run(cancelWork)
t.Log("cancelWork error:", err)
if err.Error() != "canceled job" {
t.Error(err)
}
if err := dl.Run(returnsError); err.Error() != "foo" {
err = dl.Run(workerTakes20ms)
if err != ErrTimedOut {
t.Error(err)
}
}
done := make(chan struct{})
err := dl.Run(func(stopper <-chan struct{}) error {
<-stopper
func TestDeadline(t *testing.T) {
dl := New(1*time.Second, "one time deadline case worker")
done := make(chan error)
err := dl.Run(func(stopper chan error) error {
fmt.Println("i am doing something here")
time.Sleep(time.Second * 2)
close(done)
return nil
})
if err != ErrTimedOut {
t.Error(err)
}
<-done
}
func TestDeadline(t *testing.T) {
dl := New(1*time.Second, "one dead line case")
err := dl.Run(func(stopper <-chan struct{}) error {
time.Sleep(time.Second * 10)
return nil
})
switch err {
case ErrTimedOut:
t.Error("execution took too long, oops")
default:
// some other error
t.Log("done")
}
}