first commit
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
Some checks failed
Backend Tests / Static Checks (push) Has been cancelled
Backend Tests / Tests (other) (push) Has been cancelled
Backend Tests / Tests (plugin) (push) Has been cancelled
Backend Tests / Tests (server) (push) Has been cancelled
Backend Tests / Tests (store) (push) Has been cancelled
Build Canary Image / build-frontend (push) Has been cancelled
Build Canary Image / build-push (linux/amd64) (push) Has been cancelled
Build Canary Image / build-push (linux/arm64) (push) Has been cancelled
Build Canary Image / merge (push) Has been cancelled
Frontend Tests / Lint (push) Has been cancelled
Frontend Tests / Build (push) Has been cancelled
Proto Linter / Lint Protos (push) Has been cancelled
This commit is contained in:
120
plugin/scheduler/middleware.go
Normal file
120
plugin/scheduler/middleware.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Middleware wraps a JobHandler to add cross-cutting behavior.
|
||||
type Middleware func(JobHandler) JobHandler
|
||||
|
||||
// Chain combines multiple middleware into a single middleware.
|
||||
// Middleware are applied in the order they're provided (left to right).
|
||||
func Chain(middlewares ...Middleware) Middleware {
|
||||
return func(handler JobHandler) JobHandler {
|
||||
// Apply middleware in reverse order so first middleware wraps outermost
|
||||
for i := len(middlewares) - 1; i >= 0; i-- {
|
||||
handler = middlewares[i](handler)
|
||||
}
|
||||
return handler
|
||||
}
|
||||
}
|
||||
|
||||
// Recovery recovers from panics in job handlers and converts them to errors.
|
||||
func Recovery(onPanic func(jobName string, recovered interface{})) Middleware {
|
||||
return func(next JobHandler) JobHandler {
|
||||
return func(ctx context.Context) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
jobName := getJobName(ctx)
|
||||
if onPanic != nil {
|
||||
onPanic(jobName, r)
|
||||
}
|
||||
err = errors.Errorf("job %q panicked: %v", jobName, r)
|
||||
}
|
||||
}()
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Logger is a minimal logging interface.
|
||||
type Logger interface {
|
||||
Info(msg string, args ...interface{})
|
||||
Error(msg string, args ...interface{})
|
||||
}
|
||||
|
||||
// Logging adds execution logging to jobs.
|
||||
func Logging(logger Logger) Middleware {
|
||||
return func(next JobHandler) JobHandler {
|
||||
return func(ctx context.Context) error {
|
||||
jobName := getJobName(ctx)
|
||||
start := time.Now()
|
||||
|
||||
logger.Info("Job started", "job", jobName)
|
||||
|
||||
err := next(ctx)
|
||||
duration := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Job failed", "job", jobName, "duration", duration, "error", err)
|
||||
} else {
|
||||
logger.Info("Job completed", "job", jobName, "duration", duration)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Timeout wraps a job handler with a timeout.
|
||||
func Timeout(duration time.Duration) Middleware {
|
||||
return func(next JobHandler) JobHandler {
|
||||
return func(ctx context.Context) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, duration)
|
||||
defer cancel()
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- next(ctx)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return errors.Errorf("job %q timed out after %v", getJobName(ctx), duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Context keys for job metadata.
|
||||
type contextKey int
|
||||
|
||||
const (
|
||||
jobNameKey contextKey = iota
|
||||
)
|
||||
|
||||
// withJobName adds the job name to the context.
|
||||
func withJobName(ctx context.Context, name string) context.Context {
|
||||
return context.WithValue(ctx, jobNameKey, name)
|
||||
}
|
||||
|
||||
// getJobName retrieves the job name from the context.
|
||||
func getJobName(ctx context.Context) string {
|
||||
if name, ok := ctx.Value(jobNameKey).(string); ok {
|
||||
return name
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// GetJobName retrieves the job name from the context (public API).
|
||||
// Returns empty string if not found.
|
||||
//
|
||||
//nolint:revive // GetJobName is the public API, getJobName is internal
|
||||
func GetJobName(ctx context.Context) string {
|
||||
return getJobName(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user