basic task scheduler

This commit is contained in:
Ruidy 2024-06-30 12:49:26 +02:00
parent ee3674e12f
commit b25d7be29d
No known key found for this signature in database
GPG key ID: E00F51288CB857CC
4 changed files with 154 additions and 0 deletions

View file

@ -9,6 +9,8 @@ run: build
@docker run -p ${PORT}:${PORT} -e DATABASE_URL="host=docker.for.mac.host.internal user=${DB_USER} database=${DB_NAME}" -e PORT=${PORT} ${NAME}
dev: templ
@air cmd/main.go
cron:
@go run cmd/cron/main.go
templ:
@templ generate --watch --proxy=http://localhost:${PORT} &
format:

48
cmd/cron/main.go Normal file
View file

@ -0,0 +1,48 @@
package main
import (
"log"
"os"
"os/signal"
"syscall"
"github.com/rjNemo/rentease/internal/cron"
)
func main() {
scheduler := cron.New()
scheduler.AddJob(cron.Job{
Name: "Monthly Booking Report",
Schedule: "minute",
//Schedule: "monthly",
Action: cron.JobMonthlyBookingReport,
})
go scheduler.Start()
go func() {
for {
select {
case err := <-scheduler.ErrChan:
if err != nil {
log.Println("Error:", err)
}
case msg := <-scheduler.SuccessChan:
log.Print(msg)
}
}
}()
// Capture termination signals for graceful shutdown
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// Wait for termination signal
<-sigChan
log.Println("Received termination signal, shutting down...")
// Stop the task manager and close channels
scheduler.Stop()
log.Println("All tasks stopped, exiting.")
}

88
internal/cron/cron.go Normal file
View file

@ -0,0 +1,88 @@
package cron
import (
"fmt"
"log"
"time"
)
// Cron handles jobs scheduling and execution
type Cron struct {
jobs []Job
ErrChan chan error
DoneChan chan struct{}
SuccessChan chan string
}
// Job is a type that holds the details for each job.
type Job struct {
Name string
Schedule string
Action JobFunc
}
type JobFunc func() error
func New() *Cron {
return &Cron{
jobs: make([]Job, 0),
ErrChan: make(chan error),
SuccessChan: make(chan string),
DoneChan: make(chan struct{}),
}
}
func (c *Cron) AddJob(job Job) {
c.jobs = append(c.jobs, job)
}
func (c *Cron) Start() {
for _, j := range c.jobs {
go c.scheduleJob(j)
}
}
func (c *Cron) Stop() {
close(c.DoneChan)
close(c.SuccessChan)
close(c.ErrChan)
}
// scheduleJob adds a task to the Cron schedule based on its schedule
func (c *Cron) scheduleJob(j Job) {
for {
select {
case <-c.DoneChan:
log.Printf("stopping job %s", j.Name)
return
default:
now := time.Now()
var next time.Time
switch j.Schedule {
case "minute":
next = now.Add(10 * time.Second)
case "daily":
next = now.AddDate(0, 0, 1).Truncate(24 * time.Hour)
case "weekly":
next = now.AddDate(0, 0, 7).Truncate(24 * time.Hour)
case "monthly":
nextMonth := now.AddDate(0, 1, 0)
next = time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location())
default:
log.Printf("Unknown schedule %q for job %q", j.Schedule, j.Name)
return
}
sleepDuration := time.Until(next)
log.Printf("Job %q will run in %s", j.Name, sleepDuration.String())
time.Sleep(sleepDuration)
if err := j.Action(); err != nil {
c.ErrChan <- fmt.Errorf("job %s failed: %w", j.Name, err)
} else {
c.SuccessChan <- fmt.Sprintf("job %s completed successfully", j.Name)
}
}
}
}

View file

@ -0,0 +1,16 @@
package cron
import (
"errors"
"log"
"time"
)
func JobMonthlyBookingReport() error {
now := time.Now()
log.Println("Start Monthly Booking Report job at:", now)
//err := booking.NewService().BuildReport("monthly", int(now.Month()), now.Year())
err := errors.New("test")
log.Printf("Executed Monthly Booking Report job at %v with errors: %s:", time.Now().Format(time.DateTime), err)
return err
}