All Projects → kamilsk → Breaker

kamilsk / Breaker

Licence: mit
🚧 Flexible mechanism to make execution flow interruptible.

Programming Languages

go
31211 projects - #10 most used programming language

Projects that are alternatives of or similar to Breaker

React Modern Library Boilerplate
Boilerplate for publishing modern React modules with Rollup
Stars: ✭ 285 (+206.45%)
Mutual labels:  library, module
Simple Php Router
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind.
Stars: ✭ 279 (+200%)
Mutual labels:  library, module
Raven
Raven is a Package Manager for Chez Scheme
Stars: ✭ 107 (+15.05%)
Mutual labels:  library, module
Retry
♻️ The most advanced interruptible mechanism to perform actions repetitively until successful.
Stars: ✭ 294 (+216.13%)
Mutual labels:  library, module
Amadeus Node
Node library for the Amadeus Self-Service travel APIs
Stars: ✭ 91 (-2.15%)
Mutual labels:  library
Cog
A Persistent Embedded Graph Database for Python
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Spider
A small dart library to generate Assets dart code from assets folder.
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Redisgraph
A graph database as a Redis module
Stars: ✭ 1,292 (+1289.25%)
Mutual labels:  module
Angular File Uploader
Angular file uploader is an Angular 2/4/5/6/7/8/9/10 + file uploader module with Real-Time Progress Bar, Responsive design, Angular Universal Compatibility, localization and multiple themes which includes Drag and Drop and much more.
Stars: ✭ 92 (-1.08%)
Mutual labels:  module
Completely
Java autocomplete library.
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Badger
Fast key-value DB in Go.
Stars: ✭ 10,127 (+10789.25%)
Mutual labels:  library
Tor Android
Tor binary and library for Android
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Ngx Select Dropdown
Custom Dropdown for Angular 4+ with multiple and single selection options
Stars: ✭ 91 (-2.15%)
Mutual labels:  library
Protobuf Nim
Protobuf implementation in pure Nim that leverages the power of the macro system to not depend on any external tools
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Macros
FVTT Community Macros - 📝 Contribute macros or download them as a module! 📝
Stars: ✭ 91 (-2.15%)
Mutual labels:  module
Xdg
Go implementation of the XDG Base Directory Specification and XDG user directories
Stars: ✭ 88 (-5.38%)
Mutual labels:  library
Rst
PHP library to parse reStructuredText documents
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Floatingactionmenu
I got the original code from douo here - gist.github.com/douo/dfde289778a9b3b6918f
Stars: ✭ 91 (-2.15%)
Mutual labels:  library
Bcast
Broadcasting library for Go. Broadcast message of any type on a set of channels. WIP
Stars: ✭ 90 (-3.23%)
Mutual labels:  library
Python Isc Dhcp Leases
Small python module for reading /var/lib/dhcp/dhcpd.leases from isc-dhcp-server
Stars: ✭ 90 (-3.23%)
Mutual labels:  library

🚧 breaker Awesome Go

Flexible mechanism to make execution flow interruptible.

Build Documentation Quality Template Coverage Mirror

💡 Idea

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

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

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

A full description of the idea is available here.

🏆 Motivation

I have to make modules github.com/kamilsk/retry/v5:

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

and github.com/kamilsk/semaphore/v5:

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

more consistent and reliable.

Additionally, I want to implement a Graceful Shutdown on the same mechanism.

🤼‍♂️ How to

Do HTTP request with retries

interrupter := breaker.Multiplex(
	breaker.BreakBySignal(os.Interrupt, syscall.SIGINT, syscall.SIGTERM),
	breaker.BreakByTimeout(timeout),
)
defer interrupter.Close()

ctx := breaker.ToContext(interrupter)
ctx = context.WithValue(ctx, header, "...")

req, err := http.NewRequestWithContext(ctx, http.MethodGet, server.URL, nil)
if err != nil {
	panic(err)
}

var resp *http.Response
action := func(ctx context.Context) (err error) {
	req = req.Clone(ctx)

	source := ctx.Value(header).(string)
	req.Header.Set(header, source)

	resp, err = http.DefaultClient.Do(req)
	return err
}

if err := retry.Do(ctx, action); err != nil {
	panic(err)
}
Full example
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"syscall"
	"time"

	"github.com/kamilsk/breaker"
	"github.com/kamilsk/retry/v5"
)

func main() {
	const (
		header  = "X-Message"
		timeout = time.Minute
	)

	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		time.Sleep(timeout / 10)
		_, _ = rw.Write([]byte(req.Header.Get(header)))
	}))
	defer server.Close()

	interrupter := breaker.Multiplex(
		breaker.BreakBySignal(os.Interrupt, syscall.SIGINT, syscall.SIGTERM),
		breaker.BreakByTimeout(timeout),
	)
	defer interrupter.Close()

	ctx := breaker.ToContext(interrupter)
	ctx = context.WithValue(ctx, header, "flexible mechanism to make execution flow interruptible")

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, server.URL, nil)
	if err != nil {
		panic(err)
	}

	var resp *http.Response
	action := func(ctx context.Context) (err error) {
		req = req.Clone(ctx)

		source := ctx.Value(header).(string)
		req.Header.Set(header, source)

		resp, err = http.DefaultClient.Do(req)
		return err
	}

	if err := retry.Do(ctx, action); err != nil {
		fmt.Println("error:", err)
		return
	}
	_, _ = io.Copy(os.Stdout, resp.Body)
}

Play it!

Graceful Shutdown HTTP server

interrupter := breaker.Multiplex(
	breaker.BreakBySignal(os.Interrupt, syscall.SIGINT, syscall.SIGTERM),
	breaker.BreakByTimeout(timeout),
)
defer interrupter.Close()

server := http.Server{
	BaseContext: func(net.Listener) context.Context {
		return breaker.ToContext(interrupter)
	},
}
go func() {
	if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
		log.Fatal(err)
	}
}()

<-interrupter.Done()
if errors.Is(interrupter.Err(), breaker.Interrupted) {
	if err := server.Shutdown(context.TODO()); err != nil {
		panic(err)
	}
}
Full example
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"syscall"
	"time"

	"github.com/kamilsk/breaker"
)

func main() {
	const timeout = time.Minute

	interrupter := breaker.Multiplex(
		breaker.BreakBySignal(os.Interrupt, syscall.SIGINT, syscall.SIGTERM),
		breaker.BreakByTimeout(timeout),
	)
	defer interrupter.Close()

	server := http.Server{
		Addr:    ":8080",
		Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}),
		BaseContext: func(net.Listener) context.Context {
			return breaker.ToContext(interrupter)
		},
	}
	go func() {
		if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err)
		}
	}()

	<-interrupter.Done()
	if err := interrupter.Err(); errors.Is(err, breaker.Interrupted) {
		if err := server.Shutdown(context.TODO()); err != nil {
			panic(err)
		}
	}
	fmt.Println("graceful shutdown")
}

Play it!

🧩 Integration

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

$ go get github.com/kamilsk/[email protected]

🤲 Outcomes

Console tool to execute commands for a limited time

The example shows how to execute console commands for ten minutes.

$ date
# Thu Jan  7 21:02:21
$ breakit after 10m -- server run --port=8080
$ breakit ps
# +--------------------------+----------------------------+----------+----------+
# | Process                  | Status                     | Since    | Until    |
# +--------------------------+----------------------------+----------+----------+
# | server run --port=8080   | exit 1; panic: database... | 21:02:36 | -        |
# +--------------------------+----------------------------+----------+----------+
# |                          |                            |    Total |        1 |
# +--------------------------+----------------------------+----------+----------+
$ breakit after 10m -- database run --port=5432
$ breakit after 10m delay 5s -- server run --port=8080
$ breakit ps
# +--------------------------+----------------------------+----------+----------+
# | Process                  | Status                     | Since    | Until    |
# +--------------------------+----------------------------+----------+----------+
# | database run --port=5432 | running                    | 21:04:09 | 21:14:09 |
# | server run --port=8080   | delayed                    | 21:04:30 | 21:14:25 |
# +--------------------------+----------------------------+----------+----------+
# |                          |                            |    Total |        2 |
# +--------------------------+----------------------------+----------+----------+

See more details here.

made with ❤️ for everyone

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].