Database connection drops are handled better now.
This commit is contained in:
parent
9c6af9dd01
commit
d0907134ef
|
@ -28,6 +28,7 @@ var TmplPtrMap = make(map[string]interface{})
|
||||||
var JSTokenBox atomic.Value // TODO: Move this and some of these other globals somewhere else
|
var JSTokenBox atomic.Value // TODO: Move this and some of these other globals somewhere else
|
||||||
var SessionSigningKeyBox atomic.Value // For MFA to avoid hitting the database unneccesarily
|
var SessionSigningKeyBox atomic.Value // For MFA to avoid hitting the database unneccesarily
|
||||||
var OldSessionSigningKeyBox atomic.Value // Just in case we've signed with a key that's about to go stale so we don't annoy the user too much
|
var OldSessionSigningKeyBox atomic.Value // Just in case we've signed with a key that's about to go stale so we don't annoy the user too much
|
||||||
|
var IsDBDown int32 = 0 // 0 = false, 1 = true. this is value which should be manipulated with package atomic for representing whether the database is down so we don't spam the log with lots of redundant errors
|
||||||
|
|
||||||
// ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores
|
// ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores
|
||||||
var ErrNoRows = sql.ErrNoRows
|
var ErrNoRows = sql.ErrNoRows
|
||||||
|
|
|
@ -124,6 +124,13 @@ func InternalErrorJS(err error, w http.ResponseWriter, r *http.Request) RouteErr
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the task system detects if the database is down, some database errors might lip by this
|
||||||
|
func DatabaseError(w http.ResponseWriter, r *http.Request) RouteError {
|
||||||
|
pi := ErrorPage{errorHeader(w, GuestUser, "Internal Server Error"), "A problem has occurred in the system."}
|
||||||
|
handleErrorTemplate(w, r, pi)
|
||||||
|
return HandledRouteError()
|
||||||
|
}
|
||||||
|
|
||||||
var xmlInternalError = []byte(`<?xml version="1.0" encoding="UTF-8"?>
|
var xmlInternalError = []byte(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<error>A problem has occured</error>`)
|
<error>A problem has occured</error>`)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -701,6 +702,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
routes.StaticFile(w, req)
|
routes.StaticFile(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if atomic.LoadInt32(&common.IsDBDown) == 1 {
|
||||||
|
common.DatabaseError(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
if common.Dev.SuperDebug {
|
if common.Dev.SuperDebug {
|
||||||
router.requestLogger.Print("before PreRoute")
|
router.requestLogger.Print("before PreRoute")
|
||||||
}
|
}
|
||||||
|
|
32
main.go
32
main.go
|
@ -16,6 +16,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -346,6 +347,7 @@ 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) {
|
var runTasks = func(tasks []func() error) {
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
if task() != nil {
|
if task() != nil {
|
||||||
|
@ -361,6 +363,24 @@ func main() {
|
||||||
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 func() {
|
||||||
|
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) {
|
var runHook = func(name string) {
|
||||||
err := common.RunTaskHook(name)
|
err := common.RunTaskHook(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -370,10 +390,16 @@ func main() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-halfSecondTicker.C:
|
case <-halfSecondTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
runHook("before_half_second_tick")
|
runHook("before_half_second_tick")
|
||||||
runTasks(common.ScheduledHalfSecondTasks)
|
runTasks(common.ScheduledHalfSecondTasks)
|
||||||
runHook("after_half_second_tick")
|
runHook("after_half_second_tick")
|
||||||
case <-secondTicker.C:
|
case <-secondTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
runHook("before_second_tick")
|
runHook("before_second_tick")
|
||||||
runTasks(common.ScheduledSecondTasks)
|
runTasks(common.ScheduledSecondTasks)
|
||||||
|
|
||||||
|
@ -397,6 +423,9 @@ func main() {
|
||||||
// TODO: Rescan the static files for changes
|
// TODO: Rescan the static files for changes
|
||||||
runHook("after_second_tick")
|
runHook("after_second_tick")
|
||||||
case <-fifteenMinuteTicker.C:
|
case <-fifteenMinuteTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
runHook("before_fifteen_minute_tick")
|
runHook("before_fifteen_minute_tick")
|
||||||
runTasks(common.ScheduledFifteenMinuteTasks)
|
runTasks(common.ScheduledFifteenMinuteTasks)
|
||||||
|
|
||||||
|
@ -404,6 +433,9 @@ func main() {
|
||||||
// TODO: Publish scheduled posts.
|
// TODO: Publish scheduled posts.
|
||||||
runHook("after_fifteen_minute_tick")
|
runHook("after_fifteen_minute_tick")
|
||||||
case <-hourTicker.C:
|
case <-hourTicker.C:
|
||||||
|
if startTick() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
runHook("before_hour_tick")
|
runHook("before_hour_tick")
|
||||||
|
|
||||||
jsToken, err := common.GenerateSafeString(80)
|
jsToken, err := common.GenerateSafeString(80)
|
||||||
|
|
|
@ -225,6 +225,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -481,6 +482,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
routes.StaticFile(w, req)
|
routes.StaticFile(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if atomic.LoadInt32(&common.IsDBDown) == 1 {
|
||||||
|
common.DatabaseError(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
if common.Dev.SuperDebug {
|
if common.Dev.SuperDebug {
|
||||||
router.requestLogger.Print("before PreRoute")
|
router.requestLogger.Print("before PreRoute")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue