github.com/mailgun/holster/v4@v4.20.0/etcdutil/README.md (about) 1 ## NewElection() 2 Use etcd for leader election if you have several instances of a service running in production 3 and you only want one of the service instances to preform a task. 4 5 `LeaderElection` starts a goroutine which performs an election and maintains a leader 6 while candidates join and leave the election. Calling `Close()` will concede leadership if 7 the service currently has it and will withdraw the candidate from the election. 8 9 ```go 10 11 import ( 12 "github.com/mailgun/holster/v4/etcdutil" 13 ) 14 15 func main() { 16 var wg holster.WaitGroup 17 18 client, err := etcdutil.NewClient(nil) 19 if err != nil { 20 fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err) 21 return 22 } 23 24 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 25 defer cancel() 26 27 // Start a leader election and attempt to become leader, only returns after 28 // determining the current leader. 29 election := etcdutil.NewElection(ctx, client, etcdutil.ElectionConfig{ 30 Election: "my-service", 31 Candidate: "my-candidate", 32 EventObserver: func(e etcdutil.Event) { 33 leaderChan <- e 34 if e.IsDone { 35 close(leaderChan) 36 } 37 }, 38 TTL: 10, 39 }) 40 41 // Handle graceful shutdown 42 signalChan := make(chan os.Signal, 1) 43 signal.Notify(signalChan, os.Interrupt, os.Kill) 44 45 // Do periodic thing 46 tick := time.NewTicker(time.Second * 2) 47 wg.Loop(func() bool { 48 select { 49 case <-tick.C: 50 // Are we currently leader? 51 if election.IsLeader() { 52 err := DoThing() 53 if err != nil { 54 // Have another instance run DoThing() 55 // since we can't for some reason. 56 election.Concede() 57 } 58 } 59 return true 60 case <-signalChan: 61 election.Stop() 62 return false 63 } 64 }) 65 wg.Wait() 66 67 // Or you can pipe events to a channel 68 for leader := range leaderChan { 69 fmt.Printf("Leader: %v\n", leader) 70 } 71 } 72 ``` 73 74 ## NewConfig() 75 Designed to be used in applications that share the same etcd config 76 and wish to reuse the same config throughout the application. 77 78 ```go 79 import ( 80 "os" 81 "fmt" 82 83 "github.com/mailgun/holster/v4/etcdutil" 84 ) 85 86 func main() { 87 // These environment variables provided by the environment, 88 // we set them here to only to illustrate how `NewConfig()` 89 // uses the environment to create a new etcd config 90 os.Setenv("ETCD3_USER", "root") 91 os.Setenv("ETCD3_PASSWORD", "rootpw") 92 os.Setenv("ETCD3_ENDPOINT", "etcd-n01:2379,etcd-n02:2379,etcd-n03:2379") 93 94 // These default to /etc/mailgun/ssl/localhost/etcd-xxx.pem if the files exist 95 os.Setenv("ETCD3_TLS_CERT", "/path/to/etcd-cert.pem") 96 os.Setenv("ETCD3_TLS_KEY", "/path/to/etcd-key.pem") 97 os.Setenv("ETCD3_CA", "/path/to/etcd-ca.pem") 98 99 // Set this to force connecting with TLS, but without cert verification 100 os.Setenv("ETCD3_SKIP_VERIFY", "true") 101 102 // Create a new etc config from available environment variables 103 cfg, err := etcdutil.NewConfig(nil) 104 if err != nil { 105 fmt.Fprintf(os.Stderr, "while creating etcd config: %s\n", err) 106 return 107 } 108 } 109 ``` 110 111 ## NewClient() 112 Just like `NewConfig()` but returns a connected etcd client for use by the 113 rest of the application. 114 115 ```go 116 import ( 117 "os" 118 "fmt" 119 120 "github.com/mailgun/holster/v4/etcdutil" 121 ) 122 123 func main() { 124 // Create a new etc client from available environment variables 125 client, err := etcdutil.NewClient(nil) 126 if err != nil { 127 fmt.Fprintf(os.Stderr, "while creating etcd client: %s\n", err) 128 return 129 } 130 131 // Use client 132 } 133 ```