Browse Source

Add closer

Dmitriy Gnatenko 4 tháng trước cách đây
mục cha
commit
24356e784b
3 tập tin đã thay đổi với 145 bổ sung0 xóa
  1. 80 0
      closer/closer.go
  2. 45 0
      closer/closer_config.go
  3. 20 0
      closer/readme.md

+ 80 - 0
closer/closer.go

@@ -0,0 +1,80 @@
+package closer
+
+import (
+	"context"
+	"os/signal"
+	"sync"
+	"syscall"
+	"time"
+)
+
+var closer = &Closer{}
+
+type Closer struct {
+	initOnce  sync.Once
+	waitOnce  sync.Once
+	mu        sync.Mutex
+	functions []func(ctx context.Context) error
+	timeout   *time.Duration
+	logger    Logger
+}
+
+func Init(c Config) error {
+	closer.initOnce.Do(func() {
+		closer.timeout = c.timeout
+		closer.logger = c.logger
+	})
+
+	return nil
+}
+
+func Add(f func(ctx context.Context) error) {
+	closer.mu.Lock()
+	defer closer.mu.Unlock()
+
+	closer.functions = append(closer.functions, f)
+}
+
+func Wait(ctx context.Context) {
+	closer.waitOnce.Do(func() {
+		ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
+		defer stop()
+
+		<-ctx.Done()
+
+		// start graceful shutdown
+		closeCtx := context.Background()
+		var cancel context.CancelFunc
+		if closer.timeout != nil {
+			closeCtx, cancel = context.WithTimeout(closeCtx, *closer.timeout)
+			defer cancel()
+		}
+
+		complete := make(chan struct{}, 1)
+
+		go func() {
+			for _, f := range closer.functions {
+				if err := f(closeCtx); err != nil {
+					if closer.logger != nil {
+						closer.logger.Errorf(closeCtx, "closer: %s", err.Error())
+					}
+				}
+			}
+
+			complete <- struct{}{}
+		}()
+
+		select {
+		case <-complete:
+			if closer.logger != nil {
+				closer.logger.Info(closeCtx, "closer: all processes successfully completed")
+			}
+
+			break
+		case <-closeCtx.Done():
+			if closer.logger != nil {
+				closer.logger.Info(closeCtx, "closer: completed by timeout")
+			}
+		}
+	})
+}

+ 45 - 0
closer/closer_config.go

@@ -0,0 +1,45 @@
+package closer
+
+import (
+	"context"
+	"time"
+)
+
+type Config struct {
+	timeout *time.Duration
+	logger  Logger
+}
+
+type ConfigOption func(*Config)
+
+type ConfigOptions []ConfigOption
+
+type Logger interface {
+	Info(ctx context.Context, msg string)
+	Errorf(ctx context.Context, format string, args ...any)
+}
+
+func (s *ConfigOptions) Add(option ConfigOption) {
+	*s = append(*s, option)
+}
+
+func NewConfig(opts ...ConfigOption) Config {
+	c := &Config{}
+	for _, opt := range opts {
+		opt(c)
+	}
+
+	return *c
+}
+
+func WithTimeout(timeout time.Duration) ConfigOption {
+	return func(s *Config) {
+		s.timeout = &timeout
+	}
+}
+
+func WithLogger(logger Logger) ConfigOption {
+	return func(s *Config) {
+		s.logger = logger
+	}
+}

+ 20 - 0
closer/readme.md

@@ -0,0 +1,20 @@
+
+## Usage example
+
+```
+
+func main() {
+	ctx, cancel := context.WithCancel(context.Background())
+	
+	closer.Init(
+	    closer.NewConfig(
+            closer.WithTimeout(time.Second * 10),
+        )
+    )
+
+    // run server 
+    
+	cancel()
+	closer.Wait(ctx)
+}
+```