Move the tickloop logic into it's own file.
This commit is contained in:
parent
c25b289076
commit
f9a579ae4d
110
main.go
110
main.go
|
@ -18,7 +18,6 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -352,14 +351,6 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Initialising the task system")
|
log.Print("Initialising the task system")
|
||||||
// TODO: Name the tasks so we can figure out which one it was when something goes wrong? Or maybe toss it up WithStack down there?
|
|
||||||
var runTasks = func(tasks []func() error) {
|
|
||||||
for _, task := range tasks {
|
|
||||||
if task() != nil {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests
|
// Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests
|
||||||
// TODO: Could we expand this to attachments and other things too?
|
// TODO: Could we expand this to attachments and other things too?
|
||||||
|
@ -413,109 +404,14 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// TODO: Write tests for these
|
// TODO: Write tests for these
|
||||||
// Run this goroutine once every half second
|
// Run this goroutine once every half second
|
||||||
halfSecondTicker := time.NewTicker(time.Second / 2)
|
halfSecondTicker := time.NewTicker(time.Second / 2)
|
||||||
secondTicker := time.NewTicker(time.Second)
|
secondTicker := time.NewTicker(time.Second)
|
||||||
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
|
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
|
||||||
hourTicker := time.NewTicker(time.Hour)
|
hourTicker := time.NewTicker(time.Hour)
|
||||||
go func() {
|
go tickLoop(thumbChan, halfSecondTicker, secondTicker, fifteenMinuteTicker, hourTicker)
|
||||||
var startTick = func() (abort bool) {
|
|
||||||
var isDBDown = atomic.LoadInt32(&common.IsDBDown)
|
|
||||||
err := db.Ping()
|
|
||||||
if err != nil {
|
|
||||||
// TODO: There's a bit of a race here, but it doesn't matter if this error appears multiple times in the logs as it's capped at three times, we just want to cut it down 99% of the time
|
|
||||||
if isDBDown == 0 {
|
|
||||||
common.LogWarning(err)
|
|
||||||
common.LogWarning(errors.New("The database is down"))
|
|
||||||
}
|
|
||||||
atomic.StoreInt32(&common.IsDBDown, 1)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if isDBDown == 1 {
|
|
||||||
log.Print("The database is back")
|
|
||||||
}
|
|
||||||
atomic.StoreInt32(&common.IsDBDown, 0)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var runHook = func(name string) {
|
|
||||||
err := common.RunTaskHook(name)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(err, "Failed at task '"+name+"'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-halfSecondTicker.C:
|
|
||||||
if startTick() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
runHook("before_half_second_tick")
|
|
||||||
runTasks(common.ScheduledHalfSecondTasks)
|
|
||||||
runHook("after_half_second_tick")
|
|
||||||
case <-secondTicker.C:
|
|
||||||
if startTick() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
runHook("before_second_tick")
|
|
||||||
go func() { thumbChan <- true }()
|
|
||||||
runTasks(common.ScheduledSecondTasks)
|
|
||||||
|
|
||||||
// TODO: Stop hard-coding this
|
|
||||||
err := common.HandleExpiredScheduledGroups()
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle delayed moderation tasks
|
|
||||||
|
|
||||||
// Sync with the database, if there are any changes
|
|
||||||
err = common.HandleServerSync()
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Manage the TopicStore, UserStore, and ForumStore
|
|
||||||
// TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
|
|
||||||
// TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
|
|
||||||
// TODO: Rescan the static files for changes
|
|
||||||
runHook("after_second_tick")
|
|
||||||
case <-fifteenMinuteTicker.C:
|
|
||||||
if startTick() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
runHook("before_fifteen_minute_tick")
|
|
||||||
runTasks(common.ScheduledFifteenMinuteTasks)
|
|
||||||
|
|
||||||
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
|
||||||
// TODO: Publish scheduled posts.
|
|
||||||
runHook("after_fifteen_minute_tick")
|
|
||||||
case <-hourTicker.C:
|
|
||||||
if startTick() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
runHook("before_hour_tick")
|
|
||||||
|
|
||||||
jsToken, err := common.GenerateSafeString(80)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
common.JSTokenBox.Store(jsToken)
|
|
||||||
|
|
||||||
common.OldSessionSigningKeyBox.Store(common.SessionSigningKeyBox.Load().(string)) // TODO: We probably don't need this type conversion
|
|
||||||
sessionSigningKey, err := common.GenerateSafeString(80)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(err)
|
|
||||||
}
|
|
||||||
common.SessionSigningKeyBox.Store(sessionSigningKey)
|
|
||||||
|
|
||||||
runTasks(common.ScheduledHourTasks)
|
|
||||||
runHook("after_hour_tick")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Handle the daily clean-up.
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Print("Initialising the router")
|
log.Print("Initialising the router")
|
||||||
router, err = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
router, err = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||||
|
@ -549,7 +445,7 @@ func main() {
|
||||||
|
|
||||||
// TODO: Add a graceful shutdown function
|
// TODO: Add a graceful shutdown function
|
||||||
func stoppedServer(msg ...interface{}) {
|
func stoppedServer(msg ...interface{}) {
|
||||||
log.Print("stopped server")
|
//log.Print("stopped server")
|
||||||
stopServerChan <- msg
|
stopServerChan <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"./common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Name the tasks so we can figure out which one it was when something goes wrong? Or maybe toss it up WithStack down there?
|
||||||
|
func runTasks(tasks []func() error) {
|
||||||
|
for _, task := range tasks {
|
||||||
|
err := task()
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startTick() (abort bool) {
|
||||||
|
var isDBDown = atomic.LoadInt32(&common.IsDBDown)
|
||||||
|
err := db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
// TODO: There's a bit of a race here, but it doesn't matter if this error appears multiple times in the logs as it's capped at three times, we just want to cut it down 99% of the time
|
||||||
|
if isDBDown == 0 {
|
||||||
|
common.LogWarning(err)
|
||||||
|
common.LogWarning(errors.New("The database is down"))
|
||||||
|
}
|
||||||
|
atomic.StoreInt32(&common.IsDBDown, 1)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isDBDown == 1 {
|
||||||
|
log.Print("The database is back")
|
||||||
|
}
|
||||||
|
atomic.StoreInt32(&common.IsDBDown, 0)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHook(name string) {
|
||||||
|
err := common.RunTaskHook(name)
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err, "Failed at task '"+name+"'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tickLoop(thumbChan chan bool, halfSecondTicker *time.Ticker, secondTicker *time.Ticker, fifteenMinuteTicker *time.Ticker, hourTicker *time.Ticker) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-halfSecondTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runHook("before_half_second_tick")
|
||||||
|
runTasks(common.ScheduledHalfSecondTasks)
|
||||||
|
runHook("after_half_second_tick")
|
||||||
|
case <-secondTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runHook("before_second_tick")
|
||||||
|
go func() { thumbChan <- true }()
|
||||||
|
runTasks(common.ScheduledSecondTasks)
|
||||||
|
|
||||||
|
// TODO: Stop hard-coding this
|
||||||
|
err := common.HandleExpiredScheduledGroups()
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle delayed moderation tasks
|
||||||
|
|
||||||
|
// Sync with the database, if there are any changes
|
||||||
|
err = common.HandleServerSync()
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Manage the TopicStore, UserStore, and ForumStore
|
||||||
|
// TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
|
||||||
|
// TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
|
||||||
|
// TODO: Rescan the static files for changes
|
||||||
|
runHook("after_second_tick")
|
||||||
|
case <-fifteenMinuteTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runHook("before_fifteen_minute_tick")
|
||||||
|
runTasks(common.ScheduledFifteenMinuteTasks)
|
||||||
|
|
||||||
|
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
||||||
|
// TODO: Publish scheduled posts.
|
||||||
|
runHook("after_fifteen_minute_tick")
|
||||||
|
case <-hourTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
runHook("before_hour_tick")
|
||||||
|
|
||||||
|
jsToken, err := common.GenerateSafeString(80)
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
common.JSTokenBox.Store(jsToken)
|
||||||
|
|
||||||
|
common.OldSessionSigningKeyBox.Store(common.SessionSigningKeyBox.Load().(string)) // TODO: We probably don't need this type conversion
|
||||||
|
sessionSigningKey, err := common.GenerateSafeString(80)
|
||||||
|
if err != nil {
|
||||||
|
common.LogError(err)
|
||||||
|
}
|
||||||
|
common.SessionSigningKeyBox.Store(sessionSigningKey)
|
||||||
|
|
||||||
|
runTasks(common.ScheduledHourTasks)
|
||||||
|
runHook("after_hour_tick")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle the daily clean-up.
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue