forked from goffee/cup
Wires the scheduler into the cup application following the exact conventions of the Asynq queue integration. A new testmac/config/scheduler.go file provides GetSchedulerConfig() with enable flag, polling interval, and rate limit. A new testmac/register-scheduler.go file registers task handlers (send_email and flaky_task as samples) on the Schedulermux, creates the DB-backed store via core.ResolveGorm(), and launches the scheduler in a goroutine — only when EnableScheduler: true. The scheduler tables are migrated alongside other models in run-auto-migrations.go via db.AutoMigrate(&scheduler.QueueItem{}, &scheduler.ProcessedItem{}).
This commit is contained in:
parent
f4476d3e89
commit
3d051e9617
6 changed files with 203 additions and 0 deletions
89
controllers/schedsample.go
Normal file
89
controllers/schedsample.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
// SchedulerSample demonstrates enqueuing tasks into the simple scheduler
|
||||
// from a controller.
|
||||
func SchedulerSample(c *core.Context) *core.Response {
|
||||
|
||||
store := core.ResolveSchedulerStore()
|
||||
if store == nil {
|
||||
return c.Response.SetStatusCode(500).Json(`{"message": "scheduler store not available"}`)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// ── Enqueue a "send_email" task ─────────────────────────────────────────
|
||||
emailPayload, _ := json.Marshal(map[string]interface{}{
|
||||
"to": "user@example.com",
|
||||
"subject": "Hello from the scheduler!",
|
||||
})
|
||||
item1, err := store.Enqueue(ctx, "send_email", string(emailPayload), 0)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(fmt.Sprintf("failed to enqueue send_email: %v", err))
|
||||
return c.Response.SetStatusCode(500).Json(`{"message": "failed to enqueue send_email"}`)
|
||||
}
|
||||
c.GetLogger().Info(fmt.Sprintf("[scheduler] enqueued send_email task id=%d", item1.ID))
|
||||
|
||||
// ── Enqueue a "flaky_task" (demonstrates error recording) ───────────────
|
||||
flakyPayload, _ := json.Marshal(map[string]interface{}{
|
||||
"description": "This task will fail and the error will be recorded",
|
||||
})
|
||||
item2, err := store.Enqueue(ctx, "flaky_task", string(flakyPayload), 0)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(fmt.Sprintf("failed to enqueue flaky_task: %v", err))
|
||||
return c.Response.SetStatusCode(500).Json(`{"message": "failed to enqueue flaky_task"}`)
|
||||
}
|
||||
c.GetLogger().Info(fmt.Sprintf("[scheduler] enqueued flaky_task id=%d", item2.ID))
|
||||
|
||||
// ── Enqueue a high-priority "send_email" task ───────────────────────────
|
||||
urgentPayload, _ := json.Marshal(map[string]interface{}{
|
||||
"to": "urgent@example.com",
|
||||
"subject": "URGENT: Priority message",
|
||||
})
|
||||
item3, err := store.Enqueue(ctx, "send_email", string(urgentPayload), 10)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(fmt.Sprintf("failed to enqueue urgent send_email: %v", err))
|
||||
return c.Response.SetStatusCode(500).Json(`{"message": "failed to enqueue urgent send_email"}`)
|
||||
}
|
||||
c.GetLogger().Info(fmt.Sprintf("[scheduler] enqueued high-priority send_email task id=%d", item3.ID))
|
||||
|
||||
message := fmt.Sprintf(`{"message": "Enqueued 3 scheduler tasks (send_email x2, flaky_task x1)", "tasks": [%d, %d, %d]}`,
|
||||
item1.ID, item2.ID, item3.ID)
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
|
||||
// SchedulerSemaphoreToggle flips the scheduler semaphore state.
|
||||
// If it was green it becomes red (blocking execution), and vice versa.
|
||||
// Useful for testing the semaphore kill switch without restarting the app.
|
||||
func SchedulerSemaphoreToggle(c *core.Context) *core.Response {
|
||||
wasGreen := core.SchedulerSemaphoreIsGreen()
|
||||
|
||||
if wasGreen {
|
||||
core.SchedulerSemaphoreSetRed()
|
||||
} else {
|
||||
core.SchedulerSemaphoreSetGreen()
|
||||
}
|
||||
|
||||
newState := "GREEN"
|
||||
if !core.SchedulerSemaphoreIsGreen() {
|
||||
newState = "RED"
|
||||
}
|
||||
|
||||
c.GetLogger().Info(fmt.Sprintf("[scheduler] semaphore toggled: was %s → now %s",
|
||||
map[bool]string{true: "GREEN", false: "RED"}[wasGreen], newState))
|
||||
|
||||
message := fmt.Sprintf(`{"message": "Scheduler semaphore flipped", "was": "%s", "now": "%s"}`,
|
||||
map[bool]string{true: "GREEN", false: "RED"}[wasGreen], newState)
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue