fix
This commit is contained in:
commit
9edecd8061
8 changed files with 238 additions and 21 deletions
31
config/scheduler.go
Normal file
31
config/scheduler.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2026 Jose Cely <me@jacs.guru>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
)
|
||||
|
||||
// Retrieve the main config for the Simple Scheduler
|
||||
func GetSchedulerConfig() core.SchedulerConfig {
|
||||
//#####################################
|
||||
//# Main configuration for ####
|
||||
//# the Simple Scheduler (sche) ####
|
||||
//#####################################
|
||||
|
||||
return core.SchedulerConfig{
|
||||
// For enabling and disabling the simple scheduler system
|
||||
// set to true to enable it, set to false to disable
|
||||
EnableScheduler: false,
|
||||
|
||||
// How often the scheduler polls the database for new tasks
|
||||
SchedulerInterval: 10 * time.Second,
|
||||
|
||||
// Max tasks to process per tick (0 = unlimited)
|
||||
SchedulerRateLimit: 5,
|
||||
}
|
||||
}
|
||||
90
controllers/schedsample.go
Normal file
90
controllers/schedsample.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// 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 (runs once, sequential) ────────────────
|
||||
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, 1, 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 (maxRuns=1, thread=0)", item1.ID))
|
||||
|
||||
// ── Enqueue a "flaky_task" (runs 3 times, concurrent) ──────────────────
|
||||
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, 3, 1)
|
||||
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 (maxRuns=3, thread=1/concurrent)", item2.ID))
|
||||
|
||||
// ── Enqueue a high-priority "send_email" (repeats indefinitely, sequential) ──
|
||||
urgentPayload, _ := json.Marshal(map[string]interface{}{
|
||||
"to": "urgent@example.com",
|
||||
"subject": "URGENT: Priority message",
|
||||
})
|
||||
item3, err := store.Enqueue(ctx, "send_email", string(urgentPayload), 10, -1, 0)
|
||||
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 id=%d (maxRuns=-1/infinite, thread=0)", item3.ID))
|
||||
|
||||
message := fmt.Sprintf(`{"message": "Enqueued 3 scheduler tasks (send_email, flaky_task)", "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)
|
||||
}
|
||||
22
go.mod
22
go.mod
|
|
@ -10,7 +10,7 @@ replace (
|
|||
go 1.25.0
|
||||
|
||||
require (
|
||||
git.smarteching.com/goffee/core v1.9.8
|
||||
git.smarteching.com/goffee/core v1.9.9
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hibiken/asynq v0.26.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
|
|
@ -26,7 +26,7 @@ require (
|
|||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.2.5 // indirect
|
||||
github.com/go-chi/chi/v5 v5.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.10.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
|
|
@ -34,29 +34,29 @@ require (
|
|||
github.com/harranali/mailing v1.2.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
||||
github.com/jackc/pgx/v5 v5.10.0 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailgun/errors v0.6.0 // indirect
|
||||
github.com/mailgun/mailgun-go/v4 v4.23.0 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.44 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.47 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.19.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.21.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.16.1+incompatible // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.51.0 // indirect
|
||||
golang.org/x/image v0.40.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/image v0.43.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gorm.io/driver/mysql v1.6.0 // indirect
|
||||
|
|
|
|||
32
go.sum
32
go.sum
|
|
@ -4,6 +4,8 @@ git.smarteching.com/goffee/core v1.9.6 h1:GY1EXqbmBEWZAVrl3q22Izb6aXhQzFVQBv2hWh
|
|||
git.smarteching.com/goffee/core v1.9.6/go.mod h1:ifiBgTOR4zCMzdGsabNrEO792EHny2o149NGe3TSlms=
|
||||
git.smarteching.com/goffee/core v1.9.8 h1:FWlUZ9gaPO5J+ti/YXL/Bvs11JYJ1mrmcGHlFtU7C5I=
|
||||
git.smarteching.com/goffee/core v1.9.8/go.mod h1:yTpKuV9/aseMvM/HIpcEVCeSdmZajiXja8gsLxYM9co=
|
||||
git.smarteching.com/goffee/core v1.9.9 h1:UXs2YnPDazBd/lPf3JLUyKFYvq3DpubmDFwFAOaa5z8=
|
||||
git.smarteching.com/goffee/core v1.9.9/go.mod h1:L10xk54fU7+XJVUDCh1fnOXNytHTodfPMf8jLi4siX0=
|
||||
git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q=
|
||||
git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ=
|
||||
git.smarteching.com/zeni/go-charts/v2 v2.6.11 h1:9udzlv3uxGXszpplfkL5IaTUrgkNj++KwhbaN1vVEqI=
|
||||
|
|
@ -31,6 +33,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
|||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM=
|
||||
github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
|
||||
|
|
@ -56,6 +60,8 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw
|
|||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
|
||||
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
|
|
@ -87,6 +93,8 @@ github.com/mailgun/mailgun-go/v4 v4.23.0/go.mod h1:imTtizoFtpfZqPqGP8vltVBB6q9yW
|
|||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
|
||||
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.47 h1:jOBI62gS7nKeZv+as1oGEy0+1qISgXwH/QBlR6KbfIo=
|
||||
github.com/mattn/go-sqlite3 v1.14.47/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -100,6 +108,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.19.0 h1:XPVaaPSnG6RhYf7p+rmSa9zZfeVAnWsH5h3lxthOm/k=
|
||||
github.com/redis/go-redis/v9 v9.19.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E=
|
||||
github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
|
|
@ -128,30 +138,32 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
|
||||
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
|
||||
golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8=
|
||||
golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
|
|||
3
main.go
3
main.go
|
|
@ -65,6 +65,9 @@ func main() {
|
|||
if config.GetQueueConfig().EnableQueue == true {
|
||||
registerQueues()
|
||||
}
|
||||
if config.GetSchedulerConfig().EnableScheduler == true {
|
||||
registerScheduler()
|
||||
}
|
||||
if config.GetGormConfig().EnableGorm == true {
|
||||
RunAutoMigrations()
|
||||
}
|
||||
|
|
|
|||
71
register-scheduler.go
Normal file
71
register-scheduler.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/scheduler"
|
||||
"git.smarteching.com/goffee/cup/config"
|
||||
)
|
||||
|
||||
// ─── Scheduler task handlers ─────────────────────────────────────────────────
|
||||
|
||||
// handleSendEmail processes a send_email scheduler task.
|
||||
func handleSendEmail(ctx context.Context, item *scheduler.QueueItem) error {
|
||||
var payload struct {
|
||||
To string `json:"to"`
|
||||
Subject string `json:"subject"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(item.Payload), &payload); err != nil {
|
||||
return fmt.Errorf("invalid send_email payload: %w", err)
|
||||
}
|
||||
log.Printf("[scheduler] [send_email] → to=%s subject=%q", payload.To, payload.Subject)
|
||||
time.Sleep(2000 * time.Millisecond) // simulate I/O
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFlakyTask demonstrates a task that always fails.
|
||||
func handleFlakyTask(ctx context.Context, item *scheduler.QueueItem) error {
|
||||
log.Printf("[scheduler] [flaky_task] attempting... payload=%s", item.Payload)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return fmt.Errorf("external API returned 503 — simulated failure for demo")
|
||||
}
|
||||
|
||||
// ─── Registration ────────────────────────────────────────────────────────────
|
||||
|
||||
// Register simple scheduler
|
||||
func registerScheduler() {
|
||||
|
||||
var sched = new(core.Schedulermux)
|
||||
sched.SchedulerInit()
|
||||
|
||||
//########################################
|
||||
//# scheduler task registration #####
|
||||
//########################################
|
||||
|
||||
// Register your scheduler task handlers here ...
|
||||
sched.AddWork("send_email", handleSendEmail)
|
||||
sched.AddWork("flaky_task", handleFlakyTask)
|
||||
|
||||
//########################################
|
||||
// Set the store (DB-backed) and start
|
||||
//########################################
|
||||
|
||||
// Create the store using the application's GORM DB
|
||||
st, err := scheduler.NewStore(core.ResolveGorm())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("scheduler: failed to create store: %v", err))
|
||||
}
|
||||
sched.SetStore(st)
|
||||
|
||||
// Start scheduler server in background, DO NOT TOUCH
|
||||
go sched.RunScheduler(config.GetSchedulerConfig())
|
||||
}
|
||||
|
|
@ -31,6 +31,10 @@ func registerRoutes() {
|
|||
// queue sample route
|
||||
controller.Get("/queuesample", controllers.Queuesample)
|
||||
|
||||
// scheduler sample routes
|
||||
controller.Get("/schedsample", controllers.SchedulerSample)
|
||||
controller.Get("/schedsemaphore", controllers.SchedulerSemaphoreToggle)
|
||||
|
||||
// Uncomment the lines below to enable user administration
|
||||
controller.Get("/admin/users", controllers.AdminUsersList)
|
||||
controller.Post("/admin/users", controllers.AdminUsersList)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"errors"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/scheduler"
|
||||
"git.smarteching.com/goffee/cup/models"
|
||||
"git.smarteching.com/goffee/cup/utils"
|
||||
"gorm.io/gorm"
|
||||
|
|
@ -26,6 +27,11 @@ func RunAutoMigrations() {
|
|||
db.AutoMigrate(&models.RolePermission{})
|
||||
db.AutoMigrate(&models.Permission{})
|
||||
|
||||
// Simple scheduler (sche) tables — enabled when scheduler is active
|
||||
db.AutoMigrate(&scheduler.QueueItem{})
|
||||
db.AutoMigrate(&scheduler.ProcessedItem{})
|
||||
db.AutoMigrate(&scheduler.SchedulerMeta{})
|
||||
|
||||
// End your auto migrations
|
||||
|
||||
// Create seed data data, DO NOT TOUCH
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue