From a4a57a5556909f6026466ad9691f8cd0944df734 Mon Sep 17 00:00:00 2001 From: eamon Date: Fri, 5 Jun 2020 17:36:30 +0800 Subject: [PATCH] finished deadline pattern --- .vscode/settings.json | 26 +++++----- resiliency/03_deadline/README.md | 2 + resiliency/03_deadline/deadline.go | 32 ++++++------ resiliency/03_deadline/deadline_test.go | 65 +++++++++++++++---------- 4 files changed, 71 insertions(+), 54 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0028a32..80b4314 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,15 @@ { "window.autoDetectColorScheme": false, "editor.tabSize": 2, - "editor.semanticTokenColorCustomizations":{ - "enabled": true, // enable semantic highlighting for all themes - "rules": { - "typeParameter": "#352cea", - "type": { - "foreground": "#00aa00" - }, - "parameter":"#4c70e7", - } + "editor.semanticTokenColorCustomizations": { + "enabled": true, // enable semantic highlighting for all themes + "rules": { + "typeParameter": "#352cea", + "type": { + "foreground": "#00aa00" + }, + "parameter": "#4c70e7", + } }, "extensions.showRecommendationsOnlyOnDemand": true, "files.trimTrailingWhitespace": true, @@ -47,9 +47,9 @@ "editor.codeActionsOnSave": { "source.organizeImports": true }, -"fileheader.Author":"Edward", -"fileheader.tpl": "/*\r\n * @Description: https://github.com/crazybber\r\n * @Author: {author}\r\n * @Date: {createTime}\r\n * @Last Modified by: {lastModifiedBy}\r\n * @Last Modified time: {updateTime}\r\n */\r\n\r\n", -"fileheader.LastModifiedBy": "Edward", -"peacock.color": "#5e7959" + "fileheader.Author": "Edward", + "fileheader.tpl": "/*\r\n * @Description: https://github.com/crazybber\r\n * @Author: {author}\r\n * @Date: {createTime}\r\n * @Last Modified by: {lastModifiedBy}\r\n * @Last Modified time: {updateTime}\r\n */\r\n\r\n", + "fileheader.LastModifiedBy": "Edward", + "peacock.color": "#5e7959" } //https://vscode.readthedocs.io/en/latest/getstarted/settings/ diff --git a/resiliency/03_deadline/README.md b/resiliency/03_deadline/README.md index f91df53..2f138d7 100644 --- a/resiliency/03_deadline/README.md +++ b/resiliency/03_deadline/README.md @@ -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. diff --git a/resiliency/03_deadline/deadline.go b/resiliency/03_deadline/deadline.go index aab0475..e8ba4a4 100644 --- a/resiliency/03_deadline/deadline.go +++ b/resiliency/03_deadline/deadline.go @@ -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 } }() diff --git a/resiliency/03_deadline/deadline_test.go b/resiliency/03_deadline/deadline_test.go index d3934bd..047f082 100644 --- a/resiliency/03_deadline/deadline_test.go +++ b/resiliency/03_deadline/deadline_test.go @@ -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") - } + }