reload

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2021 License: Apache-2.0 Imports: 5 Imported by: 4

README

reload

GoDoc CI Go Report Card Apache 2 licensed

reload is a universal mechanism to reload components in a Go application. Inspired by oklog/run and its simplicity, reload is a small Go library that has a simple API where Notifiers will trigger the reload process on the Realoders.

The mechanism is based on a reload manager that can have multiple notifiers, these notifiers will be executed and the manager will wait until one of those ends the execution. In that moment it will start the reload process. On the other hand the reload Manager can have multiple reloaders, that will be triggered when the reload process starts. This process will be runing in this manner forever until the reload Manager execution is stopped by an error or endidn the context passed on run.

When adding the reloaders to the manager, they are added with a prirority integer, these reloaders will be grouped in batches of the same priority. When the reload process is started, the manager will execute each reloaders batch sequentially and in priority order (the reloaders on inside the batch will be executed concurrently).

The manager as a security mechanism, is smart enough to ignore a reload trigger if there is already a reloading process being executed in that same moment.

Status

Is in alpha stage, being tested.

Getting started

func main() {
    // Setup reloaders.
    reloadSvc := reload.NewManager()

    reloadSvc.Add(0, reload.ReloaderFunc(func(ctx context.Context, id string) error {
        fmt.Printf("reloader 1: %s\n", id)
        return nil
    }))

    reloadSvc.Add(100, reload.ReloaderFunc(func(ctx context.Context, id string) error {
        fmt.Printf("reloader 2: %s\n", id)
        return nil
    }))


    // Setup notifiers.
    {
        t := time.NewTicker(5 * time.Second)
        defer t.Stop()
        reloadSvc.On(reload.NotifierFunc(func(ctx context.Context) (string, error) {
            select {
            case <-ctx.Done():
                return "", nil // End execution.
            case tickerT := <-t.C:
                return tickerT.String(), nil
            }
        }))
    }

    err := reloadSvc.Run(context.Background())
    if err != nil {
        log.Panic(err.Error())
    }
}

Examples

Check examples.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager handles the reload mechanism. The will be listening to the trigger of any of the notifiers, when this process is triggered it will call to all the reloaders based on the priority groups.

func NewManager

func NewManager() Manager

NewManager returns a new manager.

func (*Manager) Add

func (m *Manager) Add(priority int, r Reloader)

Add a reloader to the manager.

The reloader will be called when any of the notifiers end the execution.

When adding a reloader, the reloader will have a priority. All the reloaders with the same priority will be batched and executed in parallel. When the reloaders batch executing ends, if there is not error, it will execute the next priority batch. This pricess will continue until all priority batches have been executed.

The priority order is ascendant (e.g 0, 42, 100, 250, 999...).

func (*Manager) On

func (m *Manager) On(n Notifier)

On registers a notifier that will execute all reloaders when any of the notifiers returns.

The notifier should stay waiting until the reload needs to take place. The notifier should be able to be called multiple times.

When a notifier ends its execution triggering the reload process the notifier will start again and keep waiting along with the ones already waiting.

This process will be repeated forever until the manager stops.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context) error

Run will start the manager. This starts all the notifiers and wait until any of them returns a result, then it will call the notifiers in priority batches. All the triggered notifiers will start again.

If any of the notifiers returns an error, the execution will end with an error.

If the context is cancelled, the manager Run will end without error. If any of the reloaders reload process ends with an error, run will end its execution and return an error.

type Notifier

type Notifier interface {
	Notify(ctx context.Context) (string, error)
}

Notifier knows how to trigger a reload process.

type NotifierChan

type NotifierChan <-chan string

NotifierChan is a helper to create notifiers from channels.

Note: Closing the channel is not safe, as the channel will be reused and read from it multiple times for each notification.

func (NotifierChan) Notify

func (n NotifierChan) Notify(ctx context.Context) (string, error)

Notify satisifies Notifier interface.

type NotifierFunc

type NotifierFunc func(ctx context.Context) (string, error)

NotifierFunc is a helper to create notifiers from functions.

func (NotifierFunc) Notify

func (n NotifierFunc) Notify(ctx context.Context) (string, error)

Notify satisifies Notifier interface.

type Reloader

type Reloader interface {
	Reload(ctx context.Context, id string) error
}

Reloader knows how to reload a resource.

Example (Basic)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/slok/reload"
)

func main() {
	reloadSvc := reload.NewManager()

	// Add reloader.
	reloadSvc.Add(0, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 1: %s\n", id)
		return nil
	}))

	// Run ticker and add as a reload notifier.
	{
		t := time.NewTicker(100 * time.Millisecond)
		defer t.Stop()

		reloadSvc.On(reload.NotifierFunc(func(ctx context.Context) (string, error) {
			<-t.C
			return "ticker", nil
		}))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 550*time.Millisecond)
	defer cancel()

	_ = reloadSvc.Run(ctx)

}
Output:

Reloader 1: ticker
Reloader 1: ticker
Reloader 1: ticker
Reloader 1: ticker
Reloader 1: ticker
Example (BasicHTTP)
package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/slok/reload"
)

func main() {
	reloadSvc := reload.NewManager()

	// Add reloader.
	reloadSvc.Add(0, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 1: %s\n", id)
		return nil
	}))

	// Run http server and add as a reload notifier.
	serverURL := ""
	{
		reloadC := make(chan string)
		h := http.NewServeMux()
		h.Handle("/-/reload", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
			fmt.Println("Triggering HTTP reload...")
			reloadC <- "http"
		}))
		server := httptest.NewServer(h)
		defer server.Close()
		serverURL = server.URL

		reloadSvc.On(reload.NotifierChan(reloadC))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	// Make a reload HTTP request.
	go func() {
		time.Sleep(100 * time.Millisecond)
		_, _ = http.Get(serverURL + "/-/reload")
	}()

	_ = reloadSvc.Run(ctx)

}
Output:

Triggering HTTP reload...
Reloader 1: http
Example (Multinotifier)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/slok/reload"
)

func main() {
	reloadSvc := reload.NewManager()

	// Add reloaders.
	reloadSvc.Add(0, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 1: %s\n", id)
		return nil
	}))

	// Run ticker 1 and add as a reload notifier.
	{
		t := time.NewTicker(150 * time.Millisecond)
		defer t.Stop()

		reloadSvc.On(reload.NotifierFunc(func(ctx context.Context) (string, error) {
			<-t.C
			return "ticker1", nil
		}))
	}

	// Run ticker 2 and add as a reload notifier.
	{
		t := time.NewTicker(80 * time.Millisecond)
		defer t.Stop()

		reloadSvc.On(reload.NotifierFunc(func(ctx context.Context) (string, error) {
			<-t.C
			return "ticker2", nil
		}))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	_ = reloadSvc.Run(ctx)

}
Output:

Reloader 1: ticker2
Reloader 1: ticker1
Reloader 1: ticker2
Reloader 1: ticker2
Reloader 1: ticker1
Reloader 1: ticker2
Reloader 1: ticker2
Reloader 1: ticker1
Reloader 1: ticker2
Example (Priority)
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/slok/reload"
)

func main() {
	reloadSvc := reload.NewManager()

	// Add reloaders with different reload priorities.
	reloadSvc.Add(10, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 2: %s\n", id)
		return nil
	}))

	reloadSvc.Add(0, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 1: %s\n", id)
		return nil
	}))

	reloadSvc.Add(20, reload.ReloaderFunc(func(ctx context.Context, id string) error {
		fmt.Printf("Reloader 3: %s\n", id)
		return nil
	}))

	// Run ticker and add as a reload notifier.
	{
		t := time.NewTicker(100 * time.Millisecond)
		defer t.Stop()

		reloadSvc.On(reload.NotifierFunc(func(ctx context.Context) (string, error) {
			<-t.C
			return "ticker", nil
		}))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 550*time.Millisecond)
	defer cancel()

	_ = reloadSvc.Run(ctx)

}
Output:

Reloader 1: ticker
Reloader 2: ticker
Reloader 3: ticker
Reloader 1: ticker
Reloader 2: ticker
Reloader 3: ticker
Reloader 1: ticker
Reloader 2: ticker
Reloader 3: ticker
Reloader 1: ticker
Reloader 2: ticker
Reloader 3: ticker
Reloader 1: ticker
Reloader 2: ticker
Reloader 3: ticker

type ReloaderFunc

type ReloaderFunc func(ctx context.Context, id string) error

ReloaderFunc is a helper to create reloaders based on functions.

func (ReloaderFunc) Reload

func (r ReloaderFunc) Reload(ctx context.Context, id string) error

Reload satisifies Reloader interface.

Directories

Path Synopsis
_examples
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL