Browse Source

Merge pull request #1 from eapache/deadline

Deadline
Evan Huus 11 năm trước cách đây
mục cha
commit
c62447d36a
4 tập tin đã thay đổi với 137 bổ sung0 xóa
  1. 1 0
      README.md
  2. 26 0
      deadline/README.md
  3. 45 0
      deadline/deadline.go
  4. 65 0
      deadline/deadline_test.go

+ 1 - 0
README.md

@@ -4,3 +4,4 @@ go-resiliency
 Resiliency patterns for golang. Currently implemented are:
 - circuit-breaker pattern (in the `breaker` directory)
 - semaphore pattern (in the `semaphore` directory)
+- deadline/timeout pattern (in the `deadline` directory)

+ 26 - 0
deadline/README.md

@@ -0,0 +1,26 @@
+deadline
+========
+
+[![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency)
+[![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/deadline?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/deadline)
+
+The deadline/timeout resiliency pattern for golang.
+
+Creating a deadline takes one parameter: how long to wait.
+
+```go
+dl := deadline.New(1 * time.Second)
+
+err := dl.Run(func(stopper <-chan struct{}) error {
+	// do something possibly slow
+	// check stopper function and give up if timed out
+	return nil
+})
+
+switch err {
+case deadline.ErrTimedOut:
+	// execution took too long, oops
+default:
+	// some other error
+}
+```

+ 45 - 0
deadline/deadline.go

@@ -0,0 +1,45 @@
+// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go.
+package deadline
+
+import (
+	"errors"
+	"time"
+)
+
+// ErrTimedOut is the error returned from Run when the deadline expires.
+var ErrTimedOut = errors.New("timed out waiting for function to finish")
+
+// Deadline implements the deadline/timeout resiliency pattern.
+type Deadline struct {
+	timeout time.Duration
+}
+
+// New constructs a new Deadline with the given timeout.
+func New(timeout time.Duration) *Deadline {
+	return &Deadline{
+		timeout: timeout,
+	}
+}
+
+// Run runs the given function, passing it a stopper channel. If the deadline 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
+// the return value of the function is returned from Run.
+func (d *Deadline) Run(work func(<-chan struct{}) error) error {
+	result := make(chan error)
+	stopper := make(chan struct{})
+
+	go func() {
+		result <- work(stopper)
+	}()
+
+	select {
+	case ret := <-result:
+		return ret
+	case <-time.After(d.timeout):
+		close(stopper)
+		return ErrTimedOut
+	}
+}

+ 65 - 0
deadline/deadline_test.go

@@ -0,0 +1,65 @@
+package deadline
+
+import (
+	"errors"
+	"testing"
+	"time"
+)
+
+func takesFiveMillis(stopper <-chan struct{}) error {
+	time.Sleep(5 * time.Millisecond)
+	return nil
+}
+
+func takesTwentyMillis(stopper <-chan struct{}) error {
+	time.Sleep(20 * time.Millisecond)
+	return nil
+}
+
+func returnsError(stopper <-chan struct{}) error {
+	return errors.New("foo")
+}
+
+func TestDeadline(t *testing.T) {
+	dl := New(10 * time.Millisecond)
+
+	if err := dl.Run(takesFiveMillis); err != nil {
+		t.Error(err)
+	}
+
+	if err := dl.Run(takesTwentyMillis); err != ErrTimedOut {
+		t.Error(err)
+	}
+
+	if err := dl.Run(returnsError); err.Error() != "foo" {
+		t.Error(err)
+	}
+
+	done := make(chan struct{})
+	err := dl.Run(func(stopper <-chan struct{}) error {
+		<-stopper
+		close(done)
+		return nil
+	})
+	if err != ErrTimedOut {
+		t.Error(err)
+	}
+	<-done
+}
+
+func ExampleDeadline() {
+	dl := New(1 * time.Second)
+
+	err := dl.Run(func(stopper <-chan struct{}) error {
+		// do something possibly slow
+		// check stopper function and give up if timed out
+		return nil
+	})
+
+	switch err {
+	case ErrTimedOut:
+		// execution took too long, oops
+	default:
+		// some other error
+	}
+}