-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.go
More file actions
136 lines (120 loc) · 3.27 KB
/
Copy pathutils.go
File metadata and controls
136 lines (120 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package grace
import (
"context"
"sync"
)
// WaitWithTimeout waits for the WaitGroup to complete or context to timeout
// Returns nil if all goroutines complete successfully
// Returns context.Cause(ctx) if timeout occurs
func WaitWithTimeout(wg *sync.WaitGroup, ctx context.Context) error {
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
return nil
case <-ctx.Done():
return context.Cause(ctx)
}
}
// ServiceConfig holds the configuration for service execution
type ServiceConfig struct {
serviceName string
graceWg *GraceWaitGroup
logger Logger
stopOnFirstError bool // whether to stop all tasks when one task fails
}
// ServiceOption defines the option for service
type ServiceOption func(*ServiceConfig)
// WithServiceLogger sets a custom logger for the service
func WithServiceLogger(logger Logger) ServiceOption {
return func(c *ServiceConfig) {
c.logger = logger
}
}
// WithStopOnFirstError sets whether to stop all tasks when one task fails
func WithStopOnFirstError(stopOnFirstError bool) ServiceOption {
return func(c *ServiceConfig) {
c.stopOnFirstError = stopOnFirstError
}
}
// RunService runs multiple tasks concurrently and manages their lifecycle
// serviceName: service name for logging
// graceWg: WaitGroup for graceful shutdown tracking, can be nil
// tasks: task functions to execute
// opts: optional configurations
//
// The service will:
// - Start all tasks concurrently
// - Wait for context cancellation or task error
// - Wait for all tasks to complete before returning
func RunService(
ctx context.Context,
serviceName string,
graceWg *GraceWaitGroup,
tasks []func(context.Context) error,
opts ...ServiceOption,
) error {
config := &ServiceConfig{
serviceName: serviceName,
graceWg: graceWg,
logger: &defaultLogger{},
stopOnFirstError: true,
}
for _, opt := range opts {
opt(config)
}
config.logger.Info("[%s] starting all background tasks...", serviceName)
errChan := make(chan error, len(tasks))
// Start all tasks
for _, task := range tasks {
taskFunc := task // avoid closure issues
if graceWg != nil {
graceWg.Go(func() {
if err := taskFunc(ctx); err != nil {
select {
case errChan <- err:
default:
}
}
})
} else {
go func() {
if err := taskFunc(ctx); err != nil {
select {
case errChan <- err:
default:
}
}
}()
}
}
config.logger.Info("[%s] all background tasks started", serviceName)
// Wait for error or context cancellation
select {
case err := <-errChan:
// A task encountered an error
config.logger.Error("[%s] task error: %v", serviceName, err)
if config.stopOnFirstError {
config.logger.Info("[%s] waiting for running tasks to complete...", serviceName)
if graceWg != nil {
graceWg.Wait()
}
config.logger.Info("[%s] all tasks completed", serviceName)
return err
}
// Continue running other tasks
<-ctx.Done()
case <-ctx.Done():
// Context was cancelled
config.logger.Info("[%s] received shutdown signal", serviceName)
}
config.logger.Info("[%s] waiting for running tasks to complete...", serviceName)
if graceWg != nil {
graceWg.Wait()
}
config.logger.Info("[%s] all tasks completed", serviceName)
return context.Cause(ctx)
}