mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-29 21:31:48 +02:00
This is a large-scale refactoring across the codebase that replaces the custom `gperr.Error` type with Go's standard `error` interface. The changes include: - Replacing `gperr.Error` return types with `error` in function signatures - Using `errors.New()` and `fmt.Errorf()` instead of `gperr.New()` and `gperr.Errorf()` - Using `%w` format verb for error wrapping instead of `.With()` method - Replacing `gperr.Subject()` calls with `gperr.PrependSubject()` - Converting error logging from `gperr.Log*()` functions to zerolog's `.Err().Msg()` pattern - Update NewLogger to handle multiline error message - Updating `goutils` submodule to latest commit This refactoring aligns with Go idioms and removes the dependency on custom error handling abstractions in favor of standard library patterns.
107 lines
2.7 KiB
Go
107 lines
2.7 KiB
Go
package events
|
|
|
|
import (
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/yusing/godoxy/internal/common"
|
|
gperr "github.com/yusing/goutils/errs"
|
|
"github.com/yusing/goutils/task"
|
|
)
|
|
|
|
type (
|
|
EventQueue struct {
|
|
task *task.Task
|
|
queue []Event
|
|
ticker *time.Ticker
|
|
flushInterval time.Duration
|
|
onFlush OnFlushFunc
|
|
onError OnErrorFunc
|
|
}
|
|
OnFlushFunc = func(events []Event)
|
|
OnErrorFunc = func(err error)
|
|
)
|
|
|
|
const eventQueueCapacity = 10
|
|
|
|
// NewEventQueue returns a new EventQueue with the given
|
|
// queueTask, flushInterval, onFlush and onError.
|
|
//
|
|
// The returned EventQueue will start a goroutine to flush events in the queue
|
|
// when the flushInterval is reached.
|
|
//
|
|
// The onFlush function is called when the flushInterval is reached and the queue is not empty,
|
|
//
|
|
// The onError function is called when an error received from the errCh,
|
|
// or panic occurs in the onFlush function. Panic will cause a E.ErrPanicRecv error.
|
|
//
|
|
// flushTask.Finish must be called after the flush is done,
|
|
// 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(queueTask *task.Task, flushInterval time.Duration, onFlush OnFlushFunc, onError OnErrorFunc) *EventQueue {
|
|
return &EventQueue{
|
|
task: queueTask,
|
|
queue: make([]Event, 0, eventQueueCapacity),
|
|
ticker: time.NewTicker(flushInterval),
|
|
flushInterval: flushInterval,
|
|
onFlush: onFlush,
|
|
onError: onError,
|
|
}
|
|
}
|
|
|
|
func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan error) {
|
|
origOnFlush := e.onFlush
|
|
// recover panic in onFlush when in production mode
|
|
e.onFlush = func(events []Event) {
|
|
defer func() {
|
|
if errV := recover(); errV != nil {
|
|
if err, ok := errV.(error); ok {
|
|
e.onError(gperr.PrependSubject(err, e.task.Name()))
|
|
} else {
|
|
e.onError(gperr.New("recovered panic in onFlush").Withf("%v", errV).Subject(e.task.Name()))
|
|
}
|
|
if common.IsDebug {
|
|
panic(string(debug.Stack()))
|
|
}
|
|
}
|
|
}()
|
|
origOnFlush(events)
|
|
}
|
|
|
|
go func() {
|
|
defer e.ticker.Stop()
|
|
defer e.task.Finish(nil)
|
|
|
|
for {
|
|
select {
|
|
case <-e.task.Context().Done():
|
|
return
|
|
case <-e.ticker.C:
|
|
if len(e.queue) > 0 {
|
|
// 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:
|
|
if !ok {
|
|
return
|
|
}
|
|
e.queue = append(e.queue, event)
|
|
case err, ok := <-errCh:
|
|
if !ok {
|
|
return
|
|
}
|
|
if err != nil {
|
|
e.onError(err)
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|