scheduler

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 13, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StatusSuccess = "success"
	StatusFailure = "failure"
)

Variables

View Source
var (
	// ErrInvalidScheduleType indicates an unknown or unsupported schedule type
	ErrInvalidScheduleType = errors.New("scheduler: invalid schedule type")

	// ErrInvalidInterval indicates an invalid interval for fixed-rate schedules
	ErrInvalidInterval = errors.New("scheduler: invalid interval")

	// ErrInvalidTimeRange indicates a time value (hour, minute, day) is out of valid range
	ErrInvalidTimeRange = errors.New("scheduler: invalid time range")
)

Sentinel errors for testability and error type checking

Functions

func CIDRMiddleware

func CIDRMiddleware(allowlist, trustedProxies []string) echo.MiddlewareFunc

CIDRMiddleware creates middleware that restricts access based on CIDR allowlist. Per clarification #2: Empty allowlist = localhost-only access (127.0.0.1, ::1). Non-empty allowlist = restrict to matching IP ranges only.

trustedProxies: CIDR ranges of reverse proxies that can be trusted to provide X-Forwarded-For/X-Real-IP headers. If empty, proxy headers are IGNORED to prevent spoofing.

func ParseTime

func ParseTime(timeStr string) time.Time

ParseTime parses a time string in "HH:MM" format (24-hour) and returns a time.Time. This is a convenience function intended for use in application initialization code when registering scheduled jobs.

The function panics if the input format is invalid, following the "fail fast" principle from the GoBricks constitution - configuration errors should be caught at startup, not runtime.

Example:

scheduler.DailyAt("cleanup-job", &CleanupJob{}, scheduler.ParseTime("03:00"))  // 3:00 AM
scheduler.WeeklyAt("report-job", &ReportJob{}, time.Monday, scheduler.ParseTime("09:30"))  // Monday at 9:30 AM

Format: "HH:MM" where HH is 00-23 and MM is 00-59

Panics on invalid input (wrong format, invalid hour/minute values).

Types

type EmptyRequest

type EmptyRequest struct{}

Empty request type for handlers with no request body

type Job

type Job interface {
	// Execute performs the job's work using the provided context.
	// Return error to mark execution as failed (will be logged and recorded in metrics per FR-020).
	// Panic will be recovered, logged with stack trace, and marked as failed per FR-021.
	// Jobs SHOULD respect context cancellation (check ctx.Done()) for graceful shutdown per FR-024.
	Execute(ctx JobContext) error
}

Job represents a unit of work to be executed on a schedule. Implementations must be thread-safe as different job instances may run concurrently (though the scheduler prevents overlapping executions of the SAME job per FR-026).

Example:

type CleanupJob struct{}

func (j *CleanupJob) Execute(ctx JobContext) error {
    ctx.Logger().Info().Str("jobID", ctx.JobID()).Msg("Starting cleanup")
    rows, err := ctx.DB().Query(ctx, "DELETE FROM temp_data WHERE created_at < NOW() - INTERVAL '24 hours'")
    if err != nil {
        return fmt.Errorf("cleanup failed: %w", err)
    }
    defer rows.Close()
    return nil
}

type JobContext

type JobContext interface {
	context.Context // Embed stdlib context for cancellation, deadlines, values, and trace context

	// JobID returns the unique identifier for this job (as registered via JobRegistrar)
	JobID() string

	// TriggerType returns how this job was triggered: "scheduled" or "manual"
	// "scheduled" = automatic execution based on schedule
	// "manual" = triggered via POST /_sys/job/:jobId
	TriggerType() string

	// Logger returns the framework logger with job-specific fields pre-populated
	// (jobID, trigger type). Use this for all job logging per FR-020.
	Logger() logger.Logger

	// DB returns the database interface (may be nil if not configured in ModuleDeps)
	DB() types.Interface

	// Messaging returns the messaging client (may be nil if not configured in ModuleDeps)
	Messaging() messaging.Client

	// Config returns the application configuration
	Config() *config.Config
}

JobContext provides access to framework dependencies and execution metadata during job execution. Embeds context.Context for cancellation, deadlines, and trace context.

JobContext mirrors the HTTP handler context pattern per Constitution VII (UX Consistency). OpenTelemetry trace context is automatically propagated to DB, Messaging calls per FR-019.

type JobIDParam

type JobIDParam struct {
	JobID string `param:"jobId" validate:"required"`
}

JobIDParam captures the jobId path parameter

type JobListResponse

type JobListResponse struct {
	Data []*JobMetadata         `json:"data"`
	Meta map[string]interface{} `json:"meta"`
}

JobListResponse represents the response for GET /_sys/job using standard GoBricks envelope

type JobMetadata

type JobMetadata struct {
	// JobID is the unique identifier provided during registration
	JobID string `json:"jobId"`

	// ScheduleType identifies the scheduling pattern (fixed-rate, daily, weekly, hourly, monthly)
	ScheduleType string `json:"scheduleType"`

	// CronExpression is a cron-style representation of the schedule (e.g., "0 3 * * *" for daily at 3 AM)
	// Generated from ScheduleConfiguration per data-model.md
	CronExpression string `json:"cronExpression"`

	// HumanReadable is a user-friendly description (e.g., "Every Monday at 2:00 AM", "Every 30 minutes")
	HumanReadable string `json:"humanReadable"`

	// NextExecutionTime is when the job will execute next (nil if not yet scheduled)
	NextExecutionTime *time.Time `json:"nextExecutionTime,omitempty"`

	// LastExecutionTime is when the job last executed (nil if never run)
	LastExecutionTime *time.Time `json:"lastExecutionTime,omitempty"`

	// LastExecutionStatus is the status of the last execution: "success" or "failure" (nil if never run)
	// Note: skipped triggers do not update last execution status.
	LastExecutionStatus string `json:"lastExecutionStatus,omitempty"`

	// TotalExecutions is the total number of times the job has been triggered (scheduled + manual)
	TotalExecutions int64 `json:"totalExecutions"`

	// SuccessCount is the number of executions that completed without error
	SuccessCount int64 `json:"successCount"`

	// FailureCount is the number of executions that returned error or panicked
	FailureCount int64 `json:"failureCount"`

	// SkippedCount is the number of triggers skipped due to already-running instance (overlapping prevention per FR-026)
	SkippedCount int64 `json:"skippedCount"`
	// contains filtered or unexported fields
}

JobMetadata contains information about a registered job for system API responses. Thread-safe access is managed by jobEntry mutex.

Exposed via GET /_sys/job endpoint per FR-009, FR-010.

type JobTriggerData

type JobTriggerData struct {
	JobID   string `json:"jobId"`
	Trigger string `json:"trigger"`
	Message string `json:"message"`
}

JobTriggerData contains the trigger response data

type JobTriggerResponse

type JobTriggerResponse struct {
	Data JobTriggerData         `json:"data"`
	Meta map[string]interface{} `json:"meta"`
}

JobTriggerResponse represents the response for POST /_sys/job/:jobId using standard GoBricks envelope

type ScheduleConfiguration

type ScheduleConfiguration struct {
	Type ScheduleType

	// FixedRate fields
	Interval time.Duration // Used when Type == ScheduleTypeFixedRate

	// Time-based fields (daily, weekly, monthly, hourly)
	Hour   int // 0-23 (used for daily, weekly, monthly)
	Minute int // 0-59 (used for all time-based schedules)

	// WeeklyAt field
	DayOfWeek time.Weekday // Sunday-Saturday

	// MonthlyAt field
	DayOfMonth int // 1-31

	// Timezone (nil = system local time per ASSUME-001)
	// For MVP, this is always nil (local time).
	// Future enhancement: allow explicit timezone specification.
	Timezone *time.Location
}

ScheduleConfiguration holds the configuration details for a scheduled job

func (*ScheduleConfiguration) ToCronExpression

func (c *ScheduleConfiguration) ToCronExpression() string

ToCronExpression converts the schedule configuration to a cron-style expression. Note: Fixed-rate schedules don't have a true cron equivalent, so we use "@every" notation.

func (*ScheduleConfiguration) ToHumanReadable

func (c *ScheduleConfiguration) ToHumanReadable() string

ToHumanReadable converts the schedule configuration to a user-friendly description.

func (*ScheduleConfiguration) Validate

func (c *ScheduleConfiguration) Validate(_ time.Time) error

Validate checks the ScheduleConfiguration for validity according to its Type

type ScheduleType

type ScheduleType string

ScheduleType identifies the scheduling pattern

const (
	// ScheduleTypeFixedRate represents jobs that execute every N duration
	ScheduleTypeFixedRate ScheduleType = "fixed-rate"

	// ScheduleTypeDaily represents jobs that execute once per day at a specific time
	ScheduleTypeDaily ScheduleType = "daily"

	// ScheduleTypeWeekly represents jobs that execute once per week on a specific day and time
	ScheduleTypeWeekly ScheduleType = "weekly"

	// ScheduleTypeHourly represents jobs that execute once per hour at a specific minute
	ScheduleTypeHourly ScheduleType = "hourly"

	// ScheduleTypeMonthly represents jobs that execute once per month on a specific day and time
	ScheduleTypeMonthly ScheduleType = "monthly"
)

type SchedulerModule

type SchedulerModule struct {
	// contains filtered or unexported fields
}

SchedulerModule implements the GoBricks Module interface for job scheduling. It provides lazy initialization per FR-016: scheduler created only when first job is registered.

Example usage:

func (m *MyModule) Init(deps *app.ModuleDeps) error {
    return deps.Scheduler.DailyAt("cleanup-job", &CleanupJob{}, mustParseTime("03:00"))
}

func NewSchedulerModule

func NewSchedulerModule() *SchedulerModule

NewSchedulerModule creates a new SchedulerModule instance. Per FR-016: The scheduler itself is lazy-initialized on first job registration.

func (*SchedulerModule) DailyAt

func (m *SchedulerModule) DailyAt(jobID string, job any, localTime time.Time) error

DailyAt implements JobRegistrar per FR-004

func (*SchedulerModule) DeclareMessaging

func (m *SchedulerModule) DeclareMessaging(_ *messaging.Declarations)

DeclareMessaging implements app.Module Scheduler does not declare any messaging exchanges/queues

func (*SchedulerModule) FixedRate

func (m *SchedulerModule) FixedRate(jobID string, job any, interval time.Duration) error

FixedRate implements JobRegistrar per FR-003

func (*SchedulerModule) HourlyAt

func (m *SchedulerModule) HourlyAt(jobID string, job any, minute int) error

HourlyAt implements JobRegistrar per FR-006

func (*SchedulerModule) Init

func (m *SchedulerModule) Init(deps *app.ModuleDeps) error

Init implements app.Module Stores dependencies and makes the module available as a JobRegistrar via deps.

func (*SchedulerModule) MonthlyAt

func (m *SchedulerModule) MonthlyAt(jobID string, job any, dayOfMonth int, localTime time.Time) error

MonthlyAt implements JobRegistrar per FR-007

func (*SchedulerModule) Name

func (m *SchedulerModule) Name() string

Name implements app.Module

func (*SchedulerModule) RegisterRoutes

func (m *SchedulerModule) RegisterRoutes(hr *server.HandlerRegistry, r server.RouteRegistrar)

RegisterRoutes implements app.Module Registers system API routes for job listing and manual triggering

func (*SchedulerModule) Shutdown

func (m *SchedulerModule) Shutdown() error

Shutdown implements app.Module Gracefully shuts down the scheduler per FR-013, FR-014, FR-015.

func (*SchedulerModule) WeeklyAt

func (m *SchedulerModule) WeeklyAt(jobID string, job any, dayOfWeek time.Weekday, localTime time.Time) error

WeeklyAt implements JobRegistrar per FR-005

type ValidationError

type ValidationError struct {
	Field   string // The field that failed validation (e.g., "hour", "minute", "interval")
	Message string // Description of what's wrong (e.g., "must be 0-23")
	Action  string // Optional: Actionable guidance (e.g., "Choose a value between 0 and 23")
	Err     error  // Optional: Wrapped sentinel error for errors.Is checking
}

ValidationError represents a validation error during job registration or schedule validation. Per Constitution VII: Error messages follow format "scheduler: <field> <message>. <action>"

func NewInvalidValueError

func NewInvalidValueError(field string, value any, expected string) *ValidationError

NewInvalidValueError creates a ValidationError for invalid values

func NewRangeError

func NewRangeError(field string, minVal, maxVal, actual any) *ValidationError

NewRangeError creates a ValidationError for values outside valid ranges

func NewValidationError

func NewValidationError(field, message, action string) *ValidationError

NewValidationError creates a new ValidationError with the specified field, message, and action

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap returns the wrapped error for errors.Is/As support

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL