From b342609577c5dcf54ec597eb7a9e47386ff859d5 Mon Sep 17 00:00:00 2001 From: Victor Yves Crispim Date: Fri, 22 Sep 2023 18:04:40 -0300 Subject: [PATCH] feat: use goroutines to start services --- cmd/cartesi-node/full.go | 52 --------------------- cmd/cartesi-node/root.go | 9 +++- cmd/cartesi-node/validator.go | 57 ++++++++++++++++++++++-- internal/pkg/services/graphql-service.go | 39 ++++++++++++++++ internal/pkg/services/service.go | 13 ++++++ 5 files changed, 112 insertions(+), 58 deletions(-) delete mode 100644 cmd/cartesi-node/full.go create mode 100644 internal/pkg/services/graphql-service.go create mode 100644 internal/pkg/services/service.go diff --git a/cmd/cartesi-node/full.go b/cmd/cartesi-node/full.go deleted file mode 100644 index 8a18a41c3..000000000 --- a/cmd/cartesi-node/full.go +++ /dev/null @@ -1,52 +0,0 @@ -// (c) Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: Apache-2.0 (see LICENSE) - -package main - -import ( - "io" - "log" - "os" - "os/exec" - - "github.com/spf13/cobra" -) - -var full = &cobra.Command{ - Use: "full", - Short: "Starts the node in full mode with reader and validator capabilities", - Run: runFullNode, -} - -func runFullNode(cmd *cobra.Command, args []string) { - proc := exec.Command("cartesi-rollups-graphql-server") - - stdoutPipe, err := proc.StdoutPipe() - if err != nil { - log.Fatal("Error creating stdout pipe:", err) - } - - stderrPipe, err := proc.StderrPipe() - if err != nil { - log.Fatal("Error creating stderr pipe:", err) - } - - if err := proc.Start(); err != nil { - log.Fatal("Error starting sub-process:", err) - } - - // Create goroutines to display the sub-process's stdout and stderr. - go func() { - io.Copy(os.Stdout, stdoutPipe) - }() - - go func() { - io.Copy(os.Stderr, stderrPipe) - }() - - if err := proc.Wait(); err != nil { - log.Fatal("Error waiting for sub-process:", err) - } - - log.Println("Sub-process finished") -} diff --git a/cmd/cartesi-node/root.go b/cmd/cartesi-node/root.go index c7ea9d345..8e4ac047f 100644 --- a/cmd/cartesi-node/root.go +++ b/cmd/cartesi-node/root.go @@ -3,16 +3,21 @@ package main -import "github.com/spf13/cobra" +import ( + "time" + + "github.com/spf13/cobra" +) var rootCmd = &cobra.Command{ Use: "cartesi-node [reader|validator|full|no-backend]", Run: func(cmd *cobra.Command, args []string) { cmd.Usage() }, } +const DefaultServiceTimeout = 1 * time.Minute + func init() { rootCmd.AddCommand(reader) rootCmd.AddCommand(validator) - rootCmd.AddCommand(full) rootCmd.AddCommand(noBackend) } diff --git a/cmd/cartesi-node/validator.go b/cmd/cartesi-node/validator.go index 87f7ae18a..fb5dea247 100644 --- a/cmd/cartesi-node/validator.go +++ b/cmd/cartesi-node/validator.go @@ -3,12 +3,61 @@ package main -import "github.com/spf13/cobra" +import ( + "context" + "fmt" + "time" + + s "github.com/cartesi/rollups-node/internal/pkg/services" + "github.com/spf13/cobra" +) var validator = &cobra.Command{ Use: "validator", Short: "Starts the node in validator mode", - Run: func(cmd *cobra.Command, args []string) { - println("TODO") - }, + Run: runValidatorNode, +} + +func runValidatorNode(cmd *cobra.Command, args []string) { + services := []s.Service{ + &s.GraphQLService{}, + } + + // start services + ctx, cancel := context.WithCancel(context.Background()) + exit := make(chan struct{}) + for _, service := range services { + service := service + go func() { + if err := service.Start(ctx); err != nil { + msg := "main: service '%v' exited with error: %v\n" + fmt.Printf(msg, service.String(), err) + } else { + msg := "main: service '%v' exited successfully\n" + fmt.Printf(msg, service.String()) + } + exit <- struct{}{} + }() + } + + // wait for first service to exit + <-exit + + // send stop message to all other services and wait for them to finish + // or timeout + wait := make(chan struct{}) + go func() { + cancel() + for i := 0; i < len(services)-1; i++ { + <-exit + } + wait <- struct{}{} + }() + + select { + case <-wait: + fmt.Println("main: all services exited") + case <-time.After(DefaultServiceTimeout): + fmt.Println("main: exited after timeout") + } } diff --git a/internal/pkg/services/graphql-service.go b/internal/pkg/services/graphql-service.go new file mode 100644 index 000000000..658ee65ec --- /dev/null +++ b/internal/pkg/services/graphql-service.go @@ -0,0 +1,39 @@ +package services + +import ( + "context" + "fmt" + "os" + "os/exec" + "syscall" +) + +const serviceName = "cartesi-rollups-graphql-server" + +type GraphQLService struct{} + +func (g *GraphQLService) Start(ctx context.Context) error { + cmd := exec.Command(serviceName) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + if err := cmd.Start(); err != nil { + return err + } + + go func() { + <-ctx.Done() + fmt.Printf("%v: %v\n", g.String(), ctx.Err()) + cmd.Process.Signal(syscall.SIGTERM) + }() + + err := cmd.Wait() + if err != nil && cmd.ProcessState.ExitCode() != int(syscall.SIGTERM) { + return err + } + return nil +} + +func (g *GraphQLService) String() string { + return serviceName +} diff --git a/internal/pkg/services/service.go b/internal/pkg/services/service.go new file mode 100644 index 000000000..885f4b37d --- /dev/null +++ b/internal/pkg/services/service.go @@ -0,0 +1,13 @@ +package services + +import ( + "context" + "fmt" +) + +type Service interface { + fmt.Stringer + + // Start a service that should run until the context is canceled + Start(ctx context.Context) error +}