Compare commits

...

2 Commits

Author SHA1 Message Date
max
ec9c1a8fb5 Initial clear old sessions implementation 2023-04-04 14:37:36 -05:00
max
242029f2e5 Initial task scheduler implementation 2023-04-04 14:37:23 -05:00
4 changed files with 98 additions and 7 deletions

View File

@ -8,7 +8,8 @@ import (
// App contains and supplies available configurations and connections // App contains and supplies available configurations and connections
type App struct { type App struct {
Config config.Configuration // Configuration file Config config.Configuration // Configuration file
Db *sql.DB // Database connection Db *sql.DB // Database connection
Res *embed.FS // Resources from the embedded filesystem Res *embed.FS // Resources from the embedded filesystem
ScheduledTasks Scheduled // Scheduled contains a struct of all scheduled functions
} }

69
app/schedule.go Normal file
View File

@ -0,0 +1,69 @@
package app
import "time"
type Scheduled struct {
EveryReboot []func(app *App)
EverySecond []func(app *App)
EveryMinute []func(app *App)
EveryHour []func(app *App)
EveryDay []func(app *App)
EveryWeek []func(app *App)
EveryMonth []func(app *App)
EveryYear []func(app *App)
}
type Task struct {
Interval time.Duration
Funcs []func(app *App)
}
func RunScheduledTasks(app *App, poolSize int, stop <-chan struct{}) {
// Run every time the server starts
for _, f := range app.ScheduledTasks.EveryReboot {
f(app)
}
tasks := []Task{
{Interval: time.Second, Funcs: app.ScheduledTasks.EverySecond},
{Interval: time.Minute, Funcs: app.ScheduledTasks.EveryMinute},
{Interval: time.Hour, Funcs: app.ScheduledTasks.EveryHour},
{Interval: 24 * time.Hour, Funcs: app.ScheduledTasks.EveryDay},
{Interval: 7 * 24 * time.Hour, Funcs: app.ScheduledTasks.EveryWeek},
{Interval: 30 * 24 * time.Hour, Funcs: app.ScheduledTasks.EveryMonth},
{Interval: 365 * 24 * time.Hour, Funcs: app.ScheduledTasks.EveryYear},
}
// Set up task runners
runners := make([]chan bool, len(tasks))
for i, task := range tasks {
runner := make(chan bool, poolSize)
runners[i] = runner
go func(task Task, runner chan bool) {
ticker := time.NewTicker(task.Interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
for _, f := range task.Funcs {
runner <- true
go func(f func(app *App)) {
defer func() { <-runner }()
f(app)
}(f)
}
case <-stop:
return
}
}
}(task, runner)
}
// Wait for termination
for _, runner := range runners {
for i := 0; i < cap(runner); i++ {
runner <- false
}
close(runner)
}
}

View File

@ -53,6 +53,12 @@ func main() {
} }
} }
// Assign and run scheduled tasks
appLoaded.ScheduledTasks = app.Scheduled{
EveryReboot: []func(app *app.App){models.ScheduledSessionCleanup},
EveryMinute: []func(app *app.App){models.ScheduledSessionCleanup},
}
// Define Routes // Define Routes
routes.GetRoutes(&appLoaded) routes.GetRoutes(&appLoaded)
routes.PostRoutes(&appLoaded) routes.PostRoutes(&appLoaded)
@ -70,6 +76,8 @@ func main() {
// Wait for interrupt signal and shut down the server // Wait for interrupt signal and shut down the server
interrupt := make(chan os.Signal, 1) interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
stop := make(chan struct{})
go app.RunScheduledTasks(&appLoaded, 100, stop)
<-interrupt <-interrupt
log.Println("Interrupt signal received. Shutting down server...") log.Println("Interrupt signal received. Shutting down server...")

View File

@ -21,10 +21,11 @@ const sessionColumns = "\"Id\", " + sessionColumnsNoId
const sessionTable = "public.\"Session\"" const sessionTable = "public.\"Session\""
const ( const (
selectSessionByAuthToken = "SELECT " + sessionColumns + " FROM " + sessionTable + " WHERE \"AuthToken\" = $1" selectSessionByAuthToken = "SELECT " + sessionColumns + " FROM " + sessionTable + " WHERE \"AuthToken\" = $1"
selectAuthTokenIfExists = "SELECT EXISTS(SELECT 1 FROM " + sessionTable + " WHERE \"AuthToken\" = $1)" selectAuthTokenIfExists = "SELECT EXISTS(SELECT 1 FROM " + sessionTable + " WHERE \"AuthToken\" = $1)"
insertSession = "INSERT INTO " + sessionTable + " (" + sessionColumnsNoId + ") VALUES ($1, $2, $3) RETURNING \"Id\"" insertSession = "INSERT INTO " + sessionTable + " (" + sessionColumnsNoId + ") VALUES ($1, $2, $3) RETURNING \"Id\""
deleteSessionByAuthToken = "DELETE FROM " + sessionTable + " WHERE \"AuthToken\" = $1" deleteSessionByAuthToken = "DELETE FROM " + sessionTable + " WHERE \"AuthToken\" = $1"
deleteSessionsOlderThan30Days = "DELETE FROM " + sessionTable + " WHERE \"CreatedAt\" < NOW() - INTERVAL '30 days'"
) )
// CreateSession creates a new session for a user // CreateSession creates a new session for a user
@ -112,3 +113,15 @@ func DeleteSessionByAuthToken(app *app.App, w http.ResponseWriter, authToken str
return nil return nil
} }
// ScheduledSessionCleanup deletes expired sessions from the database
func ScheduledSessionCleanup(app *app.App) {
// Delete sessions older than 30 days
_, err := app.Db.Exec(deleteSessionsOlderThan30Days)
if err != nil {
log.Println("Error deleting expired sessions from database")
log.Println(err)
}
log.Println("Deleted expired sessions from database")
}