simplify task package implementation

This commit is contained in:
yusing
2025-01-01 06:07:32 +08:00
parent e7aaa95ec5
commit 1ab34ed46f
35 changed files with 547 additions and 600 deletions

View File

@@ -16,8 +16,10 @@ var (
func NewConfigFileWatcher(filename string) Watcher {
configDirWatcherMu.Lock()
defer configDirWatcherMu.Unlock()
if configDirWatcher == nil {
configDirWatcher = NewDirectoryWatcher(task.GlobalTask("config watcher"), common.ConfigBasePath)
t := task.RootTask("config_dir_watcher", false)
configDirWatcher = NewDirectoryWatcher(t, common.ConfigBasePath)
}
return configDirWatcher.Add(filename)
}

View File

@@ -37,7 +37,7 @@ type DirWatcher struct {
//
// Note that the returned DirWatcher is not ready to use until the goroutine
// started by NewDirectoryWatcher has finished.
func NewDirectoryWatcher(callerSubtask *task.Task, dirPath string) *DirWatcher {
func NewDirectoryWatcher(parent task.Parent, dirPath string) *DirWatcher {
//! subdirectories are not watched
w, err := fsnotify.NewWatcher()
if err != nil {
@@ -56,7 +56,7 @@ func NewDirectoryWatcher(callerSubtask *task.Task, dirPath string) *DirWatcher {
fwMap: F.NewMapOf[string, *fileWatcher](),
eventCh: make(chan Event),
errCh: make(chan E.Error),
task: callerSubtask,
task: parent.Subtask("dir_watcher(" + dirPath + ")"),
}
go helper.start()
return helper
@@ -80,17 +80,19 @@ func (h *DirWatcher) Add(relPath string) Watcher {
eventCh: make(chan Event),
errCh: make(chan E.Error),
}
h.task.OnFinished("close file watcher for "+relPath, func() {
close(s.eventCh)
close(s.errCh)
})
h.fwMap.Store(relPath, s)
return s
}
func (h *DirWatcher) cleanup() {
h.w.Close()
close(h.eventCh)
close(h.errCh)
h.task.Finish(nil)
}
func (h *DirWatcher) start() {
defer close(h.eventCh)
defer h.w.Close()
defer h.cleanup()
for {
select {

View File

@@ -1,6 +1,7 @@
package events
import (
"runtime/debug"
"time"
"github.com/yusing/go-proxy/internal/common"
@@ -17,7 +18,7 @@ type (
onFlush OnFlushFunc
onError OnErrorFunc
}
OnFlushFunc = func(flushTask *task.Task, events []Event)
OnFlushFunc = func(events []Event)
OnErrorFunc = func(err E.Error)
)
@@ -38,9 +39,9 @@ const eventQueueCapacity = 10
// but the onFlush function can return earlier (e.g. run in another goroutine).
//
// If task is canceled before the flushInterval is reached, the events in queue will be discarded.
func NewEventQueue(parent *task.Task, flushInterval time.Duration, onFlush OnFlushFunc, onError OnErrorFunc) *EventQueue {
func NewEventQueue(queueTask *task.Task, flushInterval time.Duration, onFlush OnFlushFunc, onError OnErrorFunc) *EventQueue {
return &EventQueue{
task: parent.Subtask("event queue"),
task: queueTask,
queue: make([]Event, 0, eventQueueCapacity),
ticker: time.NewTicker(flushInterval),
flushInterval: flushInterval,
@@ -50,19 +51,20 @@ func NewEventQueue(parent *task.Task, flushInterval time.Duration, onFlush OnFlu
}
func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan E.Error) {
if common.IsProduction {
origOnFlush := e.onFlush
// recover panic in onFlush when in production mode
e.onFlush = func(flushTask *task.Task, events []Event) {
defer func() {
if err := recover(); err != nil {
e.onError(E.New("recovered panic in onFlush").
Withf("%v", err).
Subject(e.task.Parent().String()))
origOnFlush := e.onFlush
// recover panic in onFlush when in production mode
e.onFlush = func(events []Event) {
defer func() {
if err := recover(); err != nil {
e.onError(E.New("recovered panic in onFlush").
Withf("%v", err).
Subject(e.task.Name()))
if common.IsDebug {
panic(string(debug.Stack()))
}
}()
origOnFlush(flushTask, events)
}
}
}()
origOnFlush(events)
}
go func() {
@@ -75,19 +77,24 @@ func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan E.Error) {
return
case <-e.ticker.C:
if len(e.queue) > 0 {
flushTask := e.task.Subtask("flush events")
queue := e.queue
e.queue = make([]Event, 0, eventQueueCapacity)
go e.onFlush(flushTask, queue)
flushTask.Wait()
// clone -> clear -> flush
queue := make([]Event, len(e.queue))
copy(queue, e.queue)
e.queue = e.queue[:0]
e.onFlush(queue)
}
e.ticker.Reset(e.flushInterval)
case event, ok := <-eventCh:
e.queue = append(e.queue, event)
if !ok {
return
}
case err := <-errCh:
e.queue = append(e.queue, event)
case err, ok := <-errCh:
if !ok {
return
}
if err != nil {
e.onError(err)
}
@@ -95,10 +102,3 @@ func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan E.Error) {
}
}()
}
// Wait waits for all events to be flushed and the task to finish.
//
// It is safe to call this method multiple times.
func (e *EventQueue) Wait() {
e.task.Wait()
}

View File

@@ -13,7 +13,7 @@ import (
"github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/notif"
"github.com/yusing/go-proxy/internal/task"
U "github.com/yusing/go-proxy/internal/utils"
"github.com/yusing/go-proxy/internal/utils/atomic"
"github.com/yusing/go-proxy/internal/utils/strutils"
"github.com/yusing/go-proxy/internal/watcher/health"
)
@@ -23,9 +23,9 @@ type (
monitor struct {
service string
config *health.HealthCheckConfig
url U.AtomicValue[types.URL]
url atomic.Value[types.URL]
status U.AtomicValue[health.Status]
status atomic.Value[health.Status]
lastResult *health.HealthCheckResult
lastSeen time.Time
@@ -59,10 +59,7 @@ func (mon *monitor) ContextWithTimeout(cause string) (ctx context.Context, cance
}
// Start implements task.TaskStarter.
func (mon *monitor) Start(routeSubtask *task.Task) E.Error {
mon.service = routeSubtask.Parent().Name()
mon.task = routeSubtask
func (mon *monitor) Start(parent task.Parent) E.Error {
if mon.config.Interval <= 0 {
return E.From(ErrNegativeInterval)
}
@@ -71,6 +68,9 @@ func (mon *monitor) Start(routeSubtask *task.Task) E.Error {
mon.metric = metrics.GetServiceMetrics().HealthStatus.With(metrics.HealthMetricLabels(mon.service))
}
mon.service = parent.Name()
mon.task = parent.Subtask("health_monitor")
go func() {
logger := logging.With().Str("name", mon.service).Logger()
@@ -78,10 +78,10 @@ func (mon *monitor) Start(routeSubtask *task.Task) E.Error {
if mon.status.Load() != health.StatusError {
mon.status.Store(health.StatusUnknown)
}
mon.task.Finish(nil)
if mon.metric != nil {
mon.metric.Reset()
}
mon.task.Finish(nil)
}()
if err := mon.checkUpdateHealth(); err != nil {
@@ -108,6 +108,11 @@ func (mon *monitor) Start(routeSubtask *task.Task) E.Error {
return nil
}
// Task implements task.TaskStarter.
func (mon *monitor) Task() *task.Task {
return mon.task
}
// Finish implements task.TaskFinisher.
func (mon *monitor) Finish(reason any) {
mon.task.Finish(reason)