Explorar el Código

embed: Move the ReadyNotify() call to a better place

When using the embed functionality, you can't call the Server.Stop()
function until StartEtcd returns, which can block until there is a call
to Server.Stop() in error situations. Since we have a catch-22, the
ReadyNotify() can be called manually by the user if they wish to wait
for the server startup, or in parallel with a timeout if they wish to
cancel it after some time.

Chzz pointed out that this is also more consistent with the
etcdserver.Start() behaviour too.

purpleidea pointed out that this is actually more correct too, because
we can now register the stop interrupt handler before we block on
startup.
James Shubin hace 9 años
padre
commit
9aee3f01cd
Se han modificado 4 ficheros con 14 adiciones y 1 borrados
  1. 8 0
      embed/doc.go
  2. 2 1
      embed/etcd.go
  3. 1 0
      etcdmain/etcd.go
  4. 3 0
      integration/embed_test.go

+ 8 - 0
embed/doc.go

@@ -19,6 +19,7 @@ Launch an embedded etcd server using the configuration defaults:
 
 
 	import (
 	import (
 		"log"
 		"log"
+		"time"
 
 
 		"github.com/coreos/etcd/embed"
 		"github.com/coreos/etcd/embed"
 	)
 	)
@@ -31,6 +32,13 @@ Launch an embedded etcd server using the configuration defaults:
 			log.Fatal(err)
 			log.Fatal(err)
 		}
 		}
 		defer e.Close()
 		defer e.Close()
+		select {
+		case <-e.Server.ReadyNotify():
+			log.Printf("Server is ready!")
+		case <-time.After(60 * time.Second):
+			e.Server.Stop() // trigger a shutdown
+			log.Printf("Server took too long to start!")
+		}
 		log.Fatal(<-e.Err())
 		log.Fatal(<-e.Err())
 	}
 	}
 */
 */

+ 2 - 1
embed/etcd.go

@@ -59,6 +59,8 @@ type Etcd struct {
 }
 }
 
 
 // StartEtcd launches the etcd server and HTTP handlers for client/server communication.
 // StartEtcd launches the etcd server and HTTP handlers for client/server communication.
+// The returned Etcd.Server is not guaranteed to have joined the cluster. Wait
+// on the Etcd.Server.ReadyNotify() channel to know when it completes and is ready for use.
 func StartEtcd(inCfg *Config) (e *Etcd, err error) {
 func StartEtcd(inCfg *Config) (e *Etcd, err error) {
 	if err = inCfg.Validate(); err != nil {
 	if err = inCfg.Validate(); err != nil {
 		return nil, err
 		return nil, err
@@ -130,7 +132,6 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
 	if err = e.serve(); err != nil {
 	if err = e.serve(); err != nil {
 		return
 		return
 	}
 	}
-	<-e.Server.ReadyNotify()
 	return
 	return
 }
 }
 
 

+ 1 - 0
etcdmain/etcd.go

@@ -186,6 +186,7 @@ func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) {
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
 	osutil.RegisterInterruptHandler(e.Server.Stop)
 	osutil.RegisterInterruptHandler(e.Server.Stop)
+	<-e.Server.ReadyNotify() // wait for e.Server to join the cluster
 	return e.Server.StopNotify(), e.Err(), nil
 	return e.Server.StopNotify(), e.Err(), nil
 }
 }
 
 

+ 3 - 0
integration/embed_test.go

@@ -65,6 +65,9 @@ func TestEmbedEtcd(t *testing.T) {
 	for i, tt := range tests {
 	for i, tt := range tests {
 		tests[i].cfg.Dir = dir
 		tests[i].cfg.Dir = dir
 		e, err := embed.StartEtcd(&tests[i].cfg)
 		e, err := embed.StartEtcd(&tests[i].cfg)
+		if e != nil {
+			<-e.Server.ReadyNotify() // wait for e.Server to join the cluster
+		}
 		if tt.werr != "" {
 		if tt.werr != "" {
 			if err == nil || !strings.Contains(err.Error(), tt.werr) {
 			if err == nil || !strings.Contains(err.Error(), tt.werr) {
 				t.Errorf("%d: expected error with %q, got %v", i, tt.werr, err)
 				t.Errorf("%d: expected error with %q, got %v", i, tt.werr, err)