Go-cron: A minimal Golang scheduler supporting standard cron expressions

4 months ago 5

Note

This content is translated by LLM. Original text can be found here

Ultra-lightweight Golang scheduler supporting standard cron expressions, custom descriptors, and custom intervals. A minimalist scheduler for Go that makes scheduling effortless.
Originally designed for the scheduling functionality used in threat score decay calculations for pardnchiu/go-ip-sentry.

license version readme

Built with Go's standard library heap, focusing on core functionality with minimal memory usage and zero learning cost - if you can write cron expressions, you can use this

Supports standard cron expressions, custom descriptors (@hourly, @daily, @weekly, etc.), and custom interval (@every) syntax

Min-heap based task scheduling algorithm with concurrent task execution and management, panic recovery mechanism, and dynamic task addition/removal, ensuring optimal performance in high-volume task scenarios

Click to view
flowchart TD A[Initialize] --> B[Setup Logger] B --> C[Initialize Task Heap] C --> D{Already Running?} D -->|Yes| D1[No Action] D -->|No| D2[Start Execution] D2 --> E[Calculate Initial Task Times] E --> F[Initialize Min Heap] F --> G[Start Main Loop] G --> H{Check Heap Status} G -->|No Tasks<br>Wait for Events| Q[Listen for Events] G -->|Has Tasks<br>Set Timer to Next Task| Q Q --> R{Event Type} R -->|Timer Expires| R1[Execute Due Tasks] R -->|Add Task| R2[Add to Heap] R -->|Remove Task| R3[Remove from Heap] R -->|Stop Signal| R4[Cleanup and Exit] R1 --> S[Pop Task from Heap] S --> T{Check if Enabled} T -->|Disabled| T0[Skip Task] T0 --> G T -->|Enabled| T1{Execute Task Function} T1 --> T11[Calculate Next Execution Time] T1 -->|Panic| T10[Recover] T10 --> T11[Calculate Next Execution Time] T11 --> U[Re-add to Heap if Recurring] R2 --> V[Parse Schedule] V --> W[Create Task Object] W --> X[Add to Heap] R3 --> Y[Find Task by ID] Y --> Z[Mark as Disabled] Z --> AA[Remove from Heap] U --> G X --> G AA --> G R4 --> BB[Wait for Running Tasks to Complete] BB --> CC[Close Channels] CC --> DD[Scheduler Stopped]
Loading
go get github.com/pardnchiu/go-cron
package main import ( "fmt" "log" "time" cron "github.com/pardnchiu/go-cron" ) func main() { // Initialize (optional configuration) scheduler, err := cron.New(cron.Config{ Log: &cron.Log{Stdout: true}, Location: time.Local, }) if err != nil { log.Fatal(err) } // Start scheduler scheduler.Start() // Add tasks id1, _ := scheduler.Add("@daily", func() { fmt.Println("Daily execution") }, "Backup task") id2, _ := scheduler.Add("@every 5m", func() { fmt.Println("Execute every 5 minutes") }) // View task list tasks := scheduler.List() fmt.Printf("Currently have %d tasks\n", len(tasks)) // Remove specific task scheduler.Remove(id1) // Remove all tasks scheduler.RemoveAll() // Graceful shutdown ctx := scheduler.Stop() <-ctx.Done() }
type Config struct { Log *Log // Logger configuration Location *time.Location // Timezone setting (default: time.Local) } type Log struct { Path string // Log file path (default: ./logs/cron.log) Stdout bool // Output to stdout (default: false) MaxSize int64 // Maximum log file size in bytes (default: 16MB) MaxBackup int // Number of backup files to retain (default: 5) Type string // Output format: "json" for slog standard, "text" for tree format (default: "text") }

5-field format: minute hour day month weekday

// Every minute scheduler.Add("* * * * *", task) // Daily at midnight scheduler.Add("0 0 * * *", task) // Weekdays at 9 AM scheduler.Add("0 9 * * 1-5", task) // Every 15 minutes scheduler.Add("*/15 * * * *", task) // First day of month at 6 AM scheduler.Add("0 6 1 * *", task)
// January 1st at midnight scheduler.Add("@yearly", task) // First day of month at midnight scheduler.Add("@monthly", task) // Every Sunday at midnight scheduler.Add("@weekly", task) // Daily at midnight scheduler.Add("@daily", task) // Every hour on the hour scheduler.Add("@hourly", task) // Every 30 seconds scheduler.Add("@every 30s", task) // Every 5 minutes scheduler.Add("@every 5m", task) // Every 2 hours scheduler.Add("@every 2h", task) // Every 12 hours scheduler.Add("@every 12h", task)
  • New - Create new scheduler instance

    scheduler, err := cron.New(config)
    • Sets up task heap and communication channels
  • Start - Start scheduler instance

    • Starts scheduling loop
  • Stop - Gracefully stop scheduler

    ctx := scheduler.Stop() <-ctx.Done() // Wait for all tasks to complete
    • Sends stop signal to main loop
    • Returns context that completes when all running tasks finish
    • Ensures graceful shutdown without interrupting tasks
  • Add - Add scheduled task

    taskID, err := scheduler.Add("0 */2 * * *", func() { // Task logic })
    • Parses schedule syntax
    • Generates unique task ID for management
  • Remove - Cancel task schedule

    • Removes task from scheduling queue
    • Safe to call regardless of scheduler state
  • RemoveAll - Remove all tasks

    • Immediately removes all scheduled tasks
    • Does not affect currently running tasks
  • List - Get task list

    tasks := scheduler.List()
  • Task dependency management similar to php-async
    • Pre-dependencies: Task B executes after Task A completes
    • Post-dependencies: Task B executes before Task A starts
    • Multiple dependencies: Task C waits for both Tasks A and B to complete before executing

This project is licensed under the MIT License.


©️ 2025 邱敬幃 Pardn Chiu

Read Entire Article