DEV Community

邱敬幃 Pardn Chiu
邱敬幃 Pardn Chiu

Posted on

Cron Job (Golang)

cover

Minimal scheduler for Golang supporting standard cron expressions, predefined descriptors, and custom intervals for easy scheduled task implementation in Go.

Originally designed for threat score decay calculation scheduling in pardnchiu/go-ip-sentry.

Three key features

  • Flexible Scheduling Support: Complete support for standard cron expressions, predefined descriptors (@hourly, @daily, @weekly, etc.), and custom interval (@every) syntax
  • Concurrent Safe Execution: Thread-safe task execution and management with panic recovery mechanism and dynamic task addition/removal functionality
  • High-Performance Architecture: Min-heap based task scheduling algorithm with optimized memory usage, ensuring optimal performance in high-volume task scenarios

Flow

Click to show

flowchart TD
  A[Initialize] --> C[Setup Log System]
  C --> D[Initialize Task Heap]
  D --> H{Already Running?}
  H -->|Yes| I[No Action]
  H -->|No| J[Start Execution]

  J --> K[Calculate Initial Task Times]
  K --> L[Initialize Min-Heap]
  L --> M[Start Main Loop]

  M --> N{Check Heap Status}
  N -->|Empty| O[Wait for Events]
  N -->|Has Tasks| P[Set Timer to Next Task]

  O --> Q[Listen for Events]
  P --> Q

  Q --> R{Event Type}
  R -->|Timer Expired| S[Execute Due Tasks]
  R -->|Add Task| T[Add to Heap]
  R -->|Remove Task| U[Remove from Heap]
  R -->|Stop Signal| V[Cleanup and Exit]

  S --> W[Pop Task from Heap]
  W --> X[Check if Enabled]
  X -->|Disabled| Y[Skip Task]
  X -->|Enabled| BB[Execute Task Function]
  BB --> DD[Calculate Next Execution]
  BB -->|Panic| CC[Recover]
  CC --> DD[Calculate Next Execution]
  DD --> EE[Re-add to Heap if Recurring]

  T --> FF[Parse Schedule]
  FF --> GG[Create Task Object]
  GG --> HH[Add to Heap]

  U --> II[Find Task by ID]
  II --> JJ[Mark as Disabled]
  JJ --> KK[Remove from Heap]

  Y --> M
  EE --> M
  HH --> M
  KK --> M

  V --> LL[Wait for Running Tasks]
  LL --> MM[Close Channels]
  MM --> NN[Scheduler Stopped]
Enter fullscreen mode Exit fullscreen mode

Dependencies

How to use

Installation

go get github.com/pardnchiu/go-cron
Enter fullscreen mode Exit fullscreen mode

Initialization

package main

import (
  "fmt"
  "log"
  "time"

  cronJob "github.com/pardnchiu/go-cron"
)

func main() {
  // Create configuration
  config := cronJob.Config{
  Log: &cronJob.Log{
    Stdout: true,
  },
  Location: time.Local,
  }

  // Initialize cron scheduler
  scheduler, err := cronJob.New(config)
  if err != nil {
  log.Fatal(err)
  }

  // Add tasks with different schedules

  // Standard cron expression - every 5 minutes
  id1, err := scheduler.Add("*/5 * * * *", func() {
  fmt.Println("Task runs every 5 minutes")
  })

  // Predefined descriptor - hourly
  id2, err := scheduler.Add("@hourly", func() {
  fmt.Println("Hourly task executed")
  })

  // Custom interval - every 30 seconds
  id3, err := scheduler.Add("@every 30s", func() {
  fmt.Println("Task runs every 30 seconds")
  })

  if err != nil {
  log.Printf("Failed to add task: %v", err)
  }

  // Run for some time
  time.Sleep(10 * time.Minute)

  // Remove specific task
  scheduler.Remove(id1)

  // Stop scheduler and wait for completion
  ctx := scheduler.Stop()
  <-ctx.Done()

  fmt.Println("Scheduler stopped gracefully")
}
Enter fullscreen mode Exit fullscreen mode

Configuration

type Config struct {
  Log      *Log           // Log 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")
}
Enter fullscreen mode Exit fullscreen mode

Supported Schedule Formats

Standard Cron Expressions

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)
Enter fullscreen mode Exit fullscreen mode

Predefined Descriptors

// January 1st at midnight
scheduler.Add("@yearly", task)    // or "@annually"

// First day of month at midnight
scheduler.Add("@monthly", task)

// Sunday at midnight
scheduler.Add("@weekly", task)

// Daily at midnight
scheduler.Add("@daily", task)     // or "@midnight"

// Every hour at minute 0
scheduler.Add("@hourly", task)
Enter fullscreen mode Exit fullscreen mode

Custom Intervals

// 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)
Enter fullscreen mode Exit fullscreen mode

Core Functions

Scheduler Management

  • New - Create new scheduler instance
  scheduler, err := cronJob.New(config)
Enter fullscreen mode Exit fullscreen mode
  • Initializes logging system with configurable output and rotation
  • Sets up task heap and communication channels
  • Automatically starts scheduling loop

    • Stop - Gracefully stop scheduler
  ctx := scheduler.Stop()
  <-ctx.Done() // Wait for all tasks to complete
Enter fullscreen mode Exit fullscreen mode
  • Sends stop signal to main loop
  • Returns context that completes when all running tasks finish
  • Ensures clean shutdown without interrupting tasks

Task Management

  • Add - Schedule new task
  taskID, err := scheduler.Add("0 */2 * * *", func() {
  // Task logic
  })
Enter fullscreen mode Exit fullscreen mode
  • Parses schedule expression or descriptor
  • Generates unique task ID for management
  • Thread-safe addition during runtime

    • Remove - Unschedule task
  scheduler.Remove(taskID)
Enter fullscreen mode Exit fullscreen mode
  • Removes task from scheduling queue
  • Safe to call regardless of scheduler state

Execution Flow

  1. Create scheduler instance using New
  2. Add tasks to schedule using Add
  3. Scheduler automatically calculates next execution times
  4. Tasks are triggered when execution time is reached
  5. Remove unwanted tasks using Remove
  6. Gracefully shutdown scheduler using Stop

License

This project is licensed under the MIT License.


©️ 2025 邱敬幃 Pardn Chiu

Top comments (0)