cerrado @ fadc75c731368d39edc718246b6dedff40e097e3

feat: Close worker on error

If a worker error the application, as a whole, is terminadated.
  1diff --git a/pkg/worker/http.go b/pkg/worker/http.go
  2index 1d56f86e2587ccf59a047e278e1a7a45f11cb165..973775e14ea0374073c0c59af15a908bdf1254e0 100644
  3--- a/pkg/worker/http.go
  4+++ b/pkg/worker/http.go
  5@@ -16,11 +16,23 @@ 	}
  6 }
  7 
  8 func (self *ServerTask) Start(ctx context.Context) error {
  9+	done := make(chan error)
 10+
 11 	go func() {
 12-		// nolint: errcheck
 13-		self.server.ListenAndServe()
 14+		done <- self.server.ListenAndServe()
 15 	}()
 16 
 17-	<-ctx.Done()
 18-	return self.server.Shutdown(ctx)
 19+	select {
 20+	// if ListenAndServe error for something other than context.Canceled
 21+	//(e.g.: address already in use) it trigger done to return sonner with
 22+	// the return error
 23+	case err := <-done:
 24+		return err
 25+
 26+	// in case of context canceled it will manually trigger the server to
 27+	// shutdown, and return its error, which is most cases, but not limited, is
 28+	// context.Canceled.
 29+	case <-ctx.Done():
 30+		return self.server.Shutdown(ctx)
 31+	}
 32 }
 33diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go
 34index 6b5c21cd67bc557b2b8bd1fa048229dc4b098efb..fc97c97a78aa42a5453eef9b5451eeb7bb648956 100644
 35--- a/pkg/worker/worker.go
 36+++ b/pkg/worker/worker.go
 37@@ -10,31 +10,42 @@ 	"golang.org/x/sync/errgroup"
 38 )
 39 
 40 type (
 41+
 42+	// Task defines a long running core component of the application.
 43 	Task interface {
 44+		// Start defines when the component will be started.
 45+		// The task MUST watch for the context and return when the context is
 46+		// canceled.
 47+		//
 48+		// Start MUST only error on unhandled catastrophic errors, since
 49+		// returning an error will cause the application to halt.
 50+		//
 51+		// Context canceled error is ignored and not reported as error as they
 52+		// will be trigger by OS signals or some other component erring. But in
 53+		// any case the task SHOULD handle and return context cancellation
 54+		// error.
 55 		Start(context.Context) error
 56 	}
 57 
 58-	Work struct {
 59+	// TaskPool manages the life-cycle of a pool of tasks.
 60+	TaskPool struct {
 61+		tasks []*work
 62+	}
 63+
 64+	// work is wrapper around task to add metadata to it.
 65+	work struct {
 66 		Name string
 67 		Task Task
 68 		wait time.Duration
 69 	}
 70-
 71-	TaskPool struct {
 72-		tasks []*Work
 73-	}
 74-)
 75-
 76-const (
 77-	format = "2006.01.02 15:04:05"
 78 )
 79 
 80 func NewTaskPool() *TaskPool {
 81 	return &TaskPool{}
 82 }
 83 
 84-func (w *Work) run(ctx context.Context) error {
 85-	// first time fire from the get go
 86+func (w *work) run(ctx context.Context) error {
 87+	// first time fire the task from the get go
 88 	timer := time.NewTimer(time.Nanosecond)
 89 
 90 	for {
 91@@ -42,7 +53,7 @@ 		select {
 92 		case <-ctx.Done():
 93 			return ctx.Err()
 94 		case <-timer.C:
 95-			if err := w.Task.Start(ctx); err != nil && !errors.Is(err, context.Canceled) {
 96+			if err := w.Task.Start(ctx); err != nil {
 97 				return err
 98 			}
 99 		}
100@@ -51,7 +62,7 @@ 	}
101 }
102 
103 func (self *TaskPool) AddTask(name string, wait time.Duration, task Task) {
104-	self.tasks = append(self.tasks, &Work{
105+	self.tasks = append(self.tasks, &work{
106 		Name: name,
107 		Task: task,
108 		wait: wait,
109@@ -60,27 +71,28 @@ }
110 
111 func (self *TaskPool) Start(ctx context.Context) error {
112 	var g errgroup.Group
113+	ctx, cancel := context.WithCancel(ctx)
114+	defer cancel()
115 
116 	for _, w := range self.tasks {
117-		g.Go(func(w *Work) func() error {
118+		g.Go(func(w *work) func() error {
119 			return func() error {
120-				slog.Info("Process starting", "time", time.Now().Format(format), "name", w.Name)
121+				slog.Info("Process starting", "name", w.Name)
122 				now := time.Now()
123 				if err := w.run(ctx); err != nil && !errors.Is(context.Canceled, err) {
124 					since := time.Since(now)
125 					slog.Error(
126 						"Process erred",
127-						"time", time.Now().Format(format),
128 						"name", w.Name,
129 						"error", err,
130 						"duration", since,
131 					)
132+					cancel()
133 					return err
134 				} else {
135 					since := time.Since(now)
136 					slog.Info(
137 						"Process ended",
138-						"time", time.Now().Format(format),
139 						"name", w.Name,
140 						"duration", since,
141 					)