Skip to content
🚧 Flexible mechanism to make execution flow interruptible.
Go Makefile
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github
internal
.gitattributes
.gitignore
.travis.yml
LICENSE
Makefile
README.md
breaker.go
breaker_test.go
context.go
context_test.go
example_test.go
go.mod
helper_test.go
interface.go
multiplexer.go
multiplexer_test.go

README.md

🚧 breaker

Flexible mechanism to make execution flow interruptible.

Build Quality Documentation Coverage Awesome

💡 Idea

The breaker carries a cancellation signal to interrupt an action execution.

interrupter := breaker.Multiplex(
	breaker.BreakByContext(context.WithTimeout(req.Context(), time.Minute)),
	breaker.BreakByDeadline(NewYear),
	breaker.BreakBySignal(os.Interrupt),
)
defer interrupter.Close()

<-interrupter.Done() // wait context cancellation, timeout or interrupt signal

Full description of the idea is available here.

🏆 Motivation

I have to make github.com/kamilsk/retry package:

if err := retry.Retry(breaker.BreakByTimeout(time.Minute), action); err != nil {
	log.Fatal(err)
}

and github.com/kamilsk/semaphore package:

if err := semaphore.Acquire(breaker.BreakByTimeout(time.Minute), 5); err != nil {
	log.Fatal(err)
}

more consistent and reliable.

🤼‍♂️ How to

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/kamilsk/breaker"
)

var NewYear = time.Time{}.AddDate(time.Now().Year(), 0, 0)

func Handle(rw http.ResponseWriter, req *http.Request) {
	ctx, cancel := context.WithCancel(req.Context())
	defer cancel()

	deadline, _ := time.ParseDuration(req.Header.Get("X-Timeout"))
	interrupter := breaker.Multiplex(
		breaker.BreakByContext(context.WithTimeout(ctx, deadline)),
		breaker.BreakByDeadline(NewYear),
		breaker.BreakBySignal(os.Interrupt),
	)
	defer interrupter.Close()

	buf, work := bytes.NewBuffer(nil), Work(ctx, struct{}{})
	for {
		select {
		case b, ok := <-work:
			if !ok {
				rw.WriteHeader(http.StatusOK)
				io.Copy(rw, buf)
				return
			}
			buf.WriteByte(b)
		case <-interrupter.Done():
			rw.WriteHeader(http.StatusPartialContent)
			rw.Header().Set("Content-Range", fmt.Sprintf("bytes=0-%d", buf.Len()))
			io.Copy(rw, buf)
			return
		}
	}
}

func Work(ctx context.Context, _ struct{}) <-chan byte {
	outcome := make(chan byte, 1)

	go func() { ... }()

	return outcome
}

🧩 Integration

The library uses SemVer for versioning, and it is not BC-safe through major releases. You can use go modules or dep to manage its version.

$ go get -u github.com/kamilsk/breaker

$ dep ensure -add github.com/kamilsk/breaker

made with ❤️ for everyone

You can’t perform that action at this time.