• Stars
    star
    152
  • Rank 244,685 (Top 5 %)
  • Language
    Go
  • License
    MIT License
  • Created over 4 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Orchestra is a library to manage long running go processes.

Orchestra

Orchestra is a library to manage long running go processes.

At the heart of the library is an interface called Player

// Player is a long running background worker
type Player interface {
    Play(context.Context) error
}

All a type needs to do to satisfy the interface is to have a Play method that will gracefully shutdown when the context is done.

It can also return an error if it encounters a problem when playing.

Next, there's the Conductor type (which itself is a Player)

// Conductor is a group of workers. It is also a Player itself **evil laugh**
type Conductor struct {
    Timeout time.Duration
    Players map[string]Player
}

With the conductor, you add Players to it, and when you call the Play method on the conductor, it will start the Players under it and gracefully shut them all down when the main context is done.

The timeout is there incase there is a Player that refused to stop

Helper functions

PlayUntilSignal(p Player, sig ...os.Signal)

This will start a player with a context, and close the context once it receives any of the signals provided.

Example:

package main 

import (
    "os"
    "syscall"
    
    "github.com/stephenafamo/orchestra"
)

func main() {
    player := ... // something that satisfies the player interface
    err := orchestra.PlayUntilSignal(player, os.Interrupt, syscall.SIGTERM)
    if err != nil {
        panic(err)
    }
}

PlayerFunc(func(context.Context) error)

PlayerFunc is a quick way to convert a standalone function into a type that satisfies the Player interface.

package main

import (
    "context"
    "os"
    "syscall"

    "github.com/stephenafamo/orchestra"
)

func main() {
    player := orchestra.PlayerFunc(myFunction)
    err := orchestra.PlayUntilSignal(player, os.Interrupt, syscall.SIGTERM)
    if err != nil {
        panic(err)
    }
}

func myFunction(ctx context.Context) error {
    // A continuously running process
    // Exits when ctx is done
    <-ctx.Done()
    return nil
}

ServerPlayer{*http.Server}

ServerPlayer is a type that embeds the *http.Server and extends it to satisfy the Player interface.

Since a very common long running process is the *http.Server, this makes it easy to create a player from one without having to re-write the boilerplate each time.

With the help of multiple helper functions, we can create a gracefully shutting down server that closes on SIGINT and SIGTERM by:

package main 

import (
    "net/http"
    "os"
    "syscall"
    
    "github.com/stephenafamo/orchestra"
)

func main() {
    s := orchestra.ServerPlayer{&http.Server{}}
    err := orchestra.PlayUntilSignal(s, os.Interrupt, syscall.SIGTERM)
    if err != nil {
        panic(err)
    }
}

Using the Conductor

The Conductor type makes it easy to coordinate multiple long running processes. Because each one is blocking, it is often clumsy to start and stop all of them nicely.

Well, the Conductor is here to make the pain go away.

package main

import (
    "context"
    "net/http"
    "os"
    "syscall"
    "time"

    "github.com/stephenafamo/orchestra"
)

func main() {
    // A player from a function
    a := orchestra.PlayerFunc(myFunction)
    // A player from a server
    b := orchestra.ServerPlayer{&http.Server{}}

    // A conductor to control them all
    conductor := &orchestra.Conductor{
        Timeout: 5 * time.Second,
        Players: map[string]orchestra.Player{
            // the names are used to identify the players
            // both in logs and the returned errors
            "function": a,
            "server":   b,
        },
    }

    // Use the conductor as a Player
    err := orchestra.PlayUntilSignal(conductor, os.Interrupt, syscall.SIGTERM)
    if err != nil {
        panic(err)
    }
}

func myFunction(ctx context.Context) error {
    // A continuously running process
    // Exits when ctx is done
    <-ctx.Done()
    return nil
}

Note: The Conductor makes sure that if by some mistake you add the conductor as a player to itself (or another conductor under it), it will not start the players multiple times.

If the conductor has to exit because of the timeout and not because all the Players exited successfully, it will return an error of type TimeoutErr.

You can ignore this type of error by checking for it like this:

// Use the conductor as a Player
err := orchestra.PlayUntilSignal(conductor, os.Interrupt, syscall.SIGTERM)
if err != nil && !errors.As(err, &orchestra.TimeoutErr{}) {
    panic(err)
}

Or you can specially handle it like this:

// Use the conductor as a Player
err := orchestra.PlayUntilSignal(conductor, os.Interrupt, syscall.SIGTERM)
if err != nil {
    timeoutErr := orchestra.TimeoutErr{}
    if errors.As(err, &timeoutErr) {
        fmt.Println(timeoutErr) // Handle the timeout error
    } else {
        panic(err) // handle other errors
    }
}

Customization

The logger can be modified by assiging a logger to orchestra.Logger

Contributing

Looking forward to pull requests.

More Repositories

1

bob

SQL query builder and ORM/Factory generator for Go with support for PostgreSQL, MySQL and SQLite
Go
717
star
2

goldmark-pdf

A PDF renderer for the goldmark markdown parser.
Go
117
star
3

nginx-proxy-load-balancer

Complete proxy with much simpler configuration
Go
106
star
4

scan

Scan provides the ability to to scan sql rows directly to any defined structure.
Go
69
star
5

laravel-2fa-starter

A laravel application with 2fa added to the registration and login flows
PHP
57
star
6

kronika

Kronika adds some extra utility around the standard time package. It does not have any other external dependencies.
Go
36
star
7

adonisjs-docker

A docker image for adonis.js applications
Shell
33
star
8

javascript-autocomplete-demo

JavaScript
27
star
9

isbot

Detect bots/crawlers/spiders using the user agent string.
Go
18
star
10

golangci-server

Go
15
star
11

audio-slideshow

Make audio based slideshows
JavaScript
13
star
12

go-site-boilerplate

a boilerplate for building webapps with golang
Go
13
star
13

woocommerce-global-cart

Global cart for woocommerce on WordPress multisite installs
PHP
13
star
14

boilingfactory

BoilingFactory is a CLI tool that generates factories for models generated by sqlboiler. https://github.com/volatiletech/sqlboiler.
Smarty
12
star
15

ci-bot

Go
12
star
16

boilingseed

BoilingSeed is a CLI tool that helps generate database seeding helpers with sqlboiler. https://github.com/volatiletech/sqlboiler.
Smarty
9
star
17

fakedb

fakedb registers a fake database driver named test for... testing.
Go
7
star
18

eventbus

Go
6
star
19

janus

A modular framework for Go web applications
Go
6
star
20

demo-site-hng-challenge

Source code for a nice UI concept
HTML
5
star
21

knowledgebase

knowldegebase is a tool to quickly start a knowledge base server or add one to a go web app.
Go
5
star
22

go-sql-builder-benchmarks

Benchmarking Golang SQL query builders
Go
5
star
23

sqlparser

Go Package to parse SQL statements for MySQL and SQLite
ANTLR
4
star
24

crdbstore

Gorilla's Session store implementation with cockroachDB
Go
3
star
25

typesql

SQL Query Builder for Go
Go
3
star
26

expense-tracker

Go
2
star
27

manticore-docker

A docker image for manticore search
2
star
28

audio-slideshow-wp-plugin

PHP
1
star
29

spa-nginx

Dockerfile
1
star
30

web-components

TypeScript
1
star
31

authboss-oauth1

Oauth1 module for Authboss (github.com/volatiletech/authboss)
Go
1
star
32

packr-migrate

A driver to read migration files for golang-migrate from packr boxes
Go
1
star
33

signup-login-ui-concept

Source code for a nice UI concept
JavaScript
1
star
34

jsctags-docker

docker image for jsctags. Tag js files without Node or NPM
Shell
1
star
35

at-ussd-airtime

PHP
1
star