leader

package
v0.9.2 Latest Latest
Warning

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

Go to latest
Published: Dec 31, 2024 License: MIT Imports: 4 Imported by: 0

README

leader

Good-enough leader election pattern implementation using MySQL as the coordinator.

Builds on the ideas found in this gist.

import (
	"context"
	"database/sql"
	"errors"
	"log"
	"sync"
	"time"

	"github.com/tomcz/gotools/leader"
)

func main() {
	var db *sql.DB // initialisation omitted

	node := leader.NewMysqlLeader(db, "app_leader")

	ctx, cancelElections := context.WithCancel(context.Background())

	var wg sync.WaitGroup
	wg.Add(1)

	go func() {
		err := node.Acquire(ctx)
		if errors.Is(err, context.Canceled) {
			log.Println("elections canceled")
		} else {
			log.Println("elections failed:", err)
		}
		wg.Done()
	}()

	go func() {
		for i := 0; i < 100; i++ {
			if i > 0 {
				time.Sleep(time.Second)
			}
			isLeader, err := node.IsLeader(ctx)
			if err != nil {
				log.Println("leader check failed:", err)
				continue
			}
			if isLeader {
				log.Println("I am the Leader :)")
				continue
			}
			log.Println("I am NOT the Leader :(")
		}
		cancelElections()
	}()

	wg.Wait()
}

Documentation

Overview

Package leader provides a good-enough leader pattern implementation using MySQL as the coordinator.

Ref: https://aws.amazon.com/builders-library/leader-election-in-distributed-systems/

Ref: https://gist.github.com/ljjjustin/f2213ac9b9b8c31df746f8b56095ea32

Index

Constants

View Source
const CreateMysqlLeaderSQL = `` /* 230-byte string literal not displayed */

CreateMysqlLeaderSQL is the create statement used by CreateMysqlLeaderTable. It's published so that it can be used in database migrations without needing to call the CreateMysqlLeaderTable function.

Variables

This section is empty.

Functions

func AbortOnError added in v0.5.0

func AbortOnError(err error) error

AbortOnError is the default WithOnError strategy.

func ContinueOnError added in v0.1.0

func ContinueOnError(_ error) error

ContinueOnError is an example WithOnError strategy that ignores the error and allows the leadership election to proceed.

func CreateMysqlLeaderTable

func CreateMysqlLeaderTable(db *sql.DB) error

CreateMysqlLeaderTable sets up the leadership election table and its constraints. It is not part of the MysqlLeader object since in practice it's a bad idea to run services with permissions to create or modify database schemas.

Types

type AlwaysLeader added in v0.2.0

type AlwaysLeader struct{}

AlwaysLeader is an implementation that always considers itself the leader.

func (AlwaysLeader) Acquire added in v0.2.0

func (a AlwaysLeader) Acquire(ctx context.Context) error

Acquire blocks until the context is cancelled.

func (AlwaysLeader) IsLeader added in v0.2.0

func (a AlwaysLeader) IsLeader(context.Context) (bool, error)

IsLeader always returns true.

type Leader

type Leader interface {
	// IsLeader returns whether this node is the leader, or an error if it was
	// unable to determine if it is the leader for any reason.
	IsLeader(ctx context.Context) (bool, error)
	// Acquire leadership blocking call. It should exit when the context is
	// cancelled, or when the implementation determines that the leadership
	// election process should terminate.
	Acquire(ctx context.Context) error
}

Leader election, in distributed computing, is the process of designating a single process as the organizer of some task distributed among several computers (nodes). Before the task has begun, all network nodes are either unaware which node will serve as the "leader" (or coordinator) of the task, or unable to communicate with the current coordinator. After a leader election algorithm has been run, however, each node throughout the network recognizes a particular, unique node as the task leader.

func NewMysqlLeader

func NewMysqlLeader(db *sql.DB, leaderName string, opts ...MysqlOpt) Leader

NewMysqlLeader provides an implementation of the Leader interface using MySQL as the point of coordination between nodes. It is not a perfect leadership election implementation but should be good enough providing that tasks that require leadership election do not run for longer than either the tick or age intervals.

type MysqlOpt

type MysqlOpt func(leader *mysqlLeader)

MysqlOpt allows configuration of leader defaults.

func WithAge

func WithAge(age time.Duration) MysqlOpt

WithAge allows the default lifespan of an election to be specified. The default is 60 seconds.

func WithNodeName

func WithNodeName(name string) MysqlOpt

WithNodeName allows the node name to be specified. The default value is a random UUID.

func WithOnError added in v0.2.0

func WithOnError(onError func(error) error) MysqlOpt

WithOnError allows the default strategy of terminating leadership elections on errors during the Leader.Acquire blocking call to be replaced with something more nuanced. If the onError strategy returns a non-nil error value, the blocking call will exit with the returned error. If the strategy returns a nil value, leadership election will continue on the next clock tick.

func WithTick

func WithTick(tick time.Duration) MysqlOpt

WithTick allows the default election frequency to be specified. The default is 15 seconds.

Jump to

Keyboard shortcuts

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