anchor package - github.com/kyuff/anchor - Go Packages

View Source

const (
	
	
	OK = 0
	
	Interrupted = 1
	
	SetupFailed = 3
	
	Internal = 4
)

This section is empty.

func Singleton[T any](fn func() (T, error)) func() T

Singleton creates a func that returns a value T created by fn. The value is created only once. If fn returns an error, Singleton panics.

This function is meant to be used as part of application wiring, where a panic seems may seem acceptable.

It is similar to sync.OnceValue, but differs in the way that the creator function fn can return an error.

type Service struct {
	ID       int
	HostName string
}

service := anchor.Singleton(func() (*Service, error) {
	fmt.Println("Creating service")
	return &Service{
		ID:       rand.Int(),
		HostName: "localhost",
	}, nil
})

s := service()

fmt.Printf("host: %s\n", s.HostName)
fmt.Printf("id equal: %v\n", service().ID == s.ID)
Output:

Creating service
host: localhost
id equal: true

Anchor the application Components to a Wire.

The Anchor will run each Component in the order they are added. A Component will follow the lifecycle steps (Setup, Start, Close), so the full application start and close gracefully.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/kyuff/anchor"
)

type ExampleService struct {
	name string
}

func (svc *ExampleService) Setup(ctx context.Context) error {
	fmt.Printf("Setup of %s\n", svc.name)
	return nil
}

func (svc *ExampleService) Start(ctx context.Context) error {
	fmt.Printf("Starting component\n")
	// A component can act in three different ways:
	// 1. Block using the ctx as a signal for when to gracefully shut down
	// 2. Block and implement the Close() method as a signal for graceful shutdown
	// 3. Return a nil when there is no background work that needs to be done.
	<-ctx.Done()
	return nil
}

func (svc *ExampleService) Close() error {
	fmt.Printf("Closing %s\n", svc.name)
	return nil
}

func main() {

	code := anchor.New(newAutoClosingWire(time.Millisecond*100)).
		Add(
			&ExampleService{name: "component-a"},
			&ExampleService{name: "component-b"},
		).
		Run()

	fmt.Printf("Exit code: %d\n", code)

}

func newAutoClosingWire(duration time.Duration) anchor.WireFunc {
	return func(ctx context.Context) (context.Context, context.CancelFunc) {
		ctx, cancel := context.WithCancel(ctx)
		go func() {
			defer cancel()
			time.Sleep(duration)
			fmt.Printf("Closing down the Anchor\n")
		}()
		return ctx, cancel
	}
}
Output:

Setup of component-a
Setup of component-b
Starting component
Starting component
Closing down the Anchor
Closing component-b
Closing component-a
Exit code: 0
func New(wire Wire, opts ...Option) *Anchor

New creates an Anchor that is bound to the Wire. When the Wire is closed, the Anchor will Close all Components.

func (a *Anchor) Add(components ...Component) *Anchor

Add will manage the Component list by the Anchor.

When Run is called, all Components will be started in the order they were given to the Anchor.

func (a *Anchor) Run() int

Run is blocking until the Wire closes or a Component returns an error. When either happens, each Component is closed in in reverse order of which they were added and started.

Component is a central part of an application that needs to have it's lifetime managed.

A Component can optionally Setup and Close methods.. By doing so, Anchor will guarantee to call the methods in order: Setup, Start, Close. This allows applications to prepare and gracefully clean up.

An example of a Component could be a database connection. It can read it's configuration in the Setup phase, connect at Start and disconnect at Close. If the database connection disconnects due to a network outage, it can return and error from Start, which closes down the entire application.

Close creates a component that have an empty Start() and Setup() method, but have Close. It is a convenience to run cleanup code

Make a component by it's setup func. A convenience when the Component is not needed as a reference by other parts of the application, but just needs it's lifecycle handled.

MakeProbe a component by it's setup and probe functions. A convenience when the Component is not needed as a reference by other parts of the application, but just needs it's lifecycle handled. The probe function is called after Start to check if the Component is ready.

Setup creates a component that have an empty Start() and Close() method, but have Setup. It is a convenience to run code before full application start.

Logger is an interface for logging by the Anchor. There is default implementation for slog.Logger.

type Option func(cfg *config)

Option for an Anchor configuration.

See config.go for defaults.

WithAnchorContext runs the Anchor in the given Context. If it is canceled, the Anchor will shutdown.

Default: context.Background()

WithCloseTimeout is the combined time components have to perform a graceful shutdown.

Default: No timeout

func WithDefaultSlog() Option

WithDefaultSlog uses slog.Default() for logging

WithExponentialReadyCheckBackoff doubles the wait time with each retry.

WithFixedReadyCheckBackoff waits a fixed amount of time between retries.

WithLinearReadyCheckBackoff increases the wait time linearly with each retry.

func WithLogger(logger Logger) Option

WithLogger sets the Logger for the application.

Default: No logging is done.

func WithNoopLogger() Option

WithNoopLogger disables logging

WithReadyCallback is called when all Components have been Setup and Started and succeeded any Probe.

This is useful for enabling features that require all Components to be ready before they can be used. If the function returns an error, the Anchor will fail to start and go into a shutdown state.

WithReadyCheckBackoff is called when a Component fails a Probe.

The function should return the amount of time to wait before retrying. If the function returns an error, the Anchor will fail to start and go into a shutdown state.

Default: linear backoff with 100 millisecond increment

WithSetupTimeout fails an Anchor if all Components have not been Setup within the timeout provided.

Default: No timeout

WithSlog uses the give *slog.Logger

WithStartTimeout fails an Anchor if all Components have not been Started within the timeout provided.

Default: No timeout

type TestingM interface {
	Run() int
}

Wire keeps the Anchor running.

When the returned Context is canceled, all Components are Closed and the CancelFunc is called.

func DefaultSignalWire() Wire

DefaultSignalWire returns a Wire that listens for SIGINT and SIGTERM.

SignalWire returns a Wire that listens for the given os signals.

Use DefaultSignalWire to listen for SIGINT and SIGTERM.

func TestingWire(m TestingM) Wire

TestingWire returns a Wire for use in testing. It will Run the tests and and then signal the application to shutdown.

WireFunc is a convenience type for creating a Wire from a function.