etcdutil

package
v2.3.4+incompatible Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2019 License: Apache-2.0 Imports: 15 Imported by: 4

README

NewElection()

Use etcd for leader election if you have several instances of a service running in production and you only want one of the service instances to preform a task.

LeaderElection starts a goroutine which performs an election and maintains a leader while services join and leave the election. Calling Stop() will Concede() leadership if the service currently has it.


import (
    "github.com/mailgun/holster"
    "github.com/mailgun/holster/etcdutil"
)

func main() {
    var wg holster.WaitGroup

    hostname, err := os.Hostname()
    if err != nil {
        fmt.Fprintf(os.Stderr, "while obtaining hostname: %s\n", err)
        return
    }

    client, err := etcdutil.NewClient(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err)
        return
    }

    // Preform an election called 'my-service' with hostname as the candidate name
	e := etcdutil.NewElection(client, etcdutil.ElectionConfig{
		Election:                "my-service",
		Candidate:               hostname,
		LeaderChannelSize:       10,
		ResumeLeaderOnReconnect: true,
		TTL: 10,
	})

    // Start the election, will block until a leader is elected
	if err = e.Start(); err != nil {
		fmt.Printf("during election start: %s\n", err)
		os.Exit(1)
	}

    // Handle graceful shutdown
    signalChan := make(chan os.Signal, 1)
    signal.Notify(signalChan, os.Interrupt, os.Kill)

    // Do periodic thing
    tick := time.NewTicker(time.Second * 2)
    wg.Loop(func() bool {
        select {
        case <-tick.C:
            // Are we currently leader?
            if election.IsLeader() {
                err := DoThing()
                if err != nil {
                    // Have another instance DoThing(), we can't for some reason
                    election.Concede()
                }
            }
            return true
        case <-signalChan:
            election.Stop()
            return false
        }
    })
    wg.Wait()
    
    // Or you can listen on a channel for leadership updates
    for leader := range e.LeaderChan() {
    	fmt.Printf("Leader: %t\n", leader)
    }
}

NewConfig()

Designed to be used in applications that share the same etcd config and wish to reuse the same config throughout the application.

import (
    "os"
    "fmt"

    "github.com/mailgun/holster/etcdutil"
)

func main() {
    // These environment variables provided by the environment,
    // we set them here to only to illustrate how `NewConfig()`
    // uses the environment to create a new etcd config
    os.Setenv("ETCD3_USER", "root")
    os.Setenv("ETCD3_PASSWORD", "rootpw")
    os.Setenv("ETCD3_ENDPOINT", "etcd-n01:2379,etcd-n02:2379,etcd-n03:2379")

    // These default to /etc/mailgun/ssl/localhost/etcd-xxx.pem if the files exist
    os.Setenv("ETCD3_TLS_CERT", "/path/to/etcd-cert.pem")
    os.Setenv("ETCD3_TLS_KEY", "/path/to/etcd-key.pem")
    os.Setenv("ETCD3_CA", "/path/to/etcd-ca.pem")
    
    // Set this to force connecting with TLS, but without cert verification
    os.Setenv("ETCD3_SKIP_VERIFY", "true")

    // Create a new etc config from available environment variables
    cfg, err := etcdutil.NewConfig(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd config: %s\n", err)
        return
    }
}

NewClient()

Just like NewConfig() but returns a connected etcd client for use by the rest of the application.

import (
    "os"
    "fmt"

    "github.com/mailgun/holster/etcdutil"
)

func main() {
    // Create a new etc client from available environment variables
    client, err := etcdutil.NewClient(nil)
    if err != nil {
        fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err)
        return
    }

    // Use client
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewClient

func NewClient(cfg *etcd.Config) (*etcd.Client, error)

NewClient creates a new etcd.Client with the specified config where blanks are filled from environment variables by NewConfig.

If the provided config is nil and no environment variables are set, it will return a client connecting without TLS via localhost:2379.

func NewConfig

func NewConfig(cfg *etcd.Config) (*etcd.Config, error)

NewConfig creates a new etcd.Config using environment variables. If an existing config is passed, it will fill in missing configuration using environment variables or defaults if they exists on the local system.

If no environment variables are set, it will return a config set to connect without TLS via localhost:2379.

Types

type Election

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

func (*Election) Concede

func (e *Election) Concede() bool

Release leadership and return true if we own it, else do nothing and return false

func (*Election) IsLeader

func (e *Election) IsLeader() bool

func (*Election) LeaderChan

func (e *Election) LeaderChan() chan bool

func (*Election) Start

func (e *Election) Start() error

func (*Election) Stop

func (e *Election) Stop()

type ElectionConfig

type ElectionConfig struct {
	// The name of the election (IE: scout, blackbird, etc...)
	Election string
	// The name of this instance (IE: worker-n01, worker-n02, etc...)
	Candidate string
	// Seconds to wait before giving up the election if leader disconnected
	TTL int
	// Report not leader when etcd connection is interrupted
	LoseLeaderOnDisconnect bool
	// If we were leader before connection or service interruption attempt
	// to resume leadership without initiating a new election
	ResumeLeaderOnReconnect bool
	// How long to wait until attempting to establish a new session between failures
	ReconnectBackOff time.Duration
	// The size of the leader channel buffer as returned by LeaderChan(). Set this to
	// something other than zero to avoid losing leadership changes.
	LeaderChannelSize int
}

type LeaderElectionMock

type LeaderElectionMock struct{}

func (*LeaderElectionMock) Concede

func (s *LeaderElectionMock) Concede() bool

func (*LeaderElectionMock) IsLeader

func (s *LeaderElectionMock) IsLeader() bool

func (*LeaderElectionMock) LeaderChan

func (s *LeaderElectionMock) LeaderChan() chan bool

func (*LeaderElectionMock) Start

func (s *LeaderElectionMock) Start() error

func (*LeaderElectionMock) Stop

func (s *LeaderElectionMock) Stop()

type LeaderElector

type LeaderElector interface {
	IsLeader() bool
	LeaderChan() chan bool
	Concede() bool
	Start() error
	Stop()
}

func NewElection

func NewElection(client *etcd.Client, conf ElectionConfig) LeaderElector

Use leader election if you have several instances of a service running in production and you only want one of the service instances to preform a periodic task.

 client, _ := etcdutil.NewClient(nil)

 election := etcdutil.NewElection(client, etcdutil.ElectionConfig{
     Election: "election-name",
     Candidate: "",
     TTL: 5,
 })

 // Start the leader election and attempt to become leader
 if err := election.Start(); err != nil {
     panic(err)
 }

	// Returns true if we are leader (thread safe)
	if election.IsLeader() {
		// Do periodic thing
	}

 select {
 case isLeader := <-election.LeaderChan():
 	fmt.Printf("Leader: %t\n", isLeader)
 }

NOTE: If this instance is elected leader and connection is interrupted to etcd, this library will continue to report it is leader until connection to etcd is resumed and a new leader is elected. If you wish to lose leadership on disconnect set `LoseLeaderOnDisconnect = true`

Jump to

Keyboard shortcuts

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