mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-23 09:18:51 +02:00
simplify task package implementation
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user