Merge pull request #284 from writeas/high-load-error-page
Show 503 page on blogs under high load
This commit is contained in:
commit
dbd7eff7ea
|
@ -22,3 +22,7 @@ func (db *datastore) isDuplicateKeyErr(err error) bool {
|
||||||
func (db *datastore) isIgnorableError(err error) bool {
|
func (db *datastore) isIgnorableError(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) isHighLoadError(err error) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -40,3 +40,13 @@ func (db *datastore) isIgnorableError(err error) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) isHighLoadError(err error) bool {
|
||||||
|
if db.driverName == driverMySQL {
|
||||||
|
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
|
||||||
|
return mysqlErr.Number == mySQLErrMaxUserConns || mysqlErr.Number == mySQLErrTooManyConns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// +build sqlite,!wflib
|
// +build sqlite,!wflib
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright © 2019 A Bunch Tell LLC.
|
* Copyright © 2019-2020 A Bunch Tell LLC.
|
||||||
*
|
*
|
||||||
* This file is part of WriteFreely.
|
* This file is part of WriteFreely.
|
||||||
*
|
*
|
||||||
|
@ -60,3 +60,13 @@ func (db *datastore) isIgnorableError(err error) bool {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *datastore) isHighLoadError(err error) bool {
|
||||||
|
if db.driverName == driverMySQL {
|
||||||
|
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
|
||||||
|
return mysqlErr.Number == mySQLErrMaxUserConns || mysqlErr.Number == mySQLErrTooManyConns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ import (
|
||||||
const (
|
const (
|
||||||
mySQLErrDuplicateKey = 1062
|
mySQLErrDuplicateKey = 1062
|
||||||
mySQLErrCollationMix = 1267
|
mySQLErrCollationMix = 1267
|
||||||
|
mySQLErrTooManyConns = 1040
|
||||||
|
mySQLErrMaxUserConns = 1203
|
||||||
|
|
||||||
driverMySQL = "mysql"
|
driverMySQL = "mysql"
|
||||||
driverSQLite = "sqlite3"
|
driverSQLite = "sqlite3"
|
||||||
|
@ -795,6 +797,8 @@ func (db *datastore) GetCollectionBy(condition string, value interface{}) (*Coll
|
||||||
switch {
|
switch {
|
||||||
case err == sql.ErrNoRows:
|
case err == sql.ErrNoRows:
|
||||||
return nil, impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
return nil, impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
||||||
|
case db.isHighLoadError(err):
|
||||||
|
return nil, ErrUnavailable
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Error("Failed selecting from collections: %v", err)
|
log.Error("Failed selecting from collections: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -37,6 +37,8 @@ var (
|
||||||
ErrInternalGeneral = impart.HTTPError{http.StatusInternalServerError, "The humans messed something up. They've been notified."}
|
ErrInternalGeneral = impart.HTTPError{http.StatusInternalServerError, "The humans messed something up. They've been notified."}
|
||||||
ErrInternalCookieSession = impart.HTTPError{http.StatusInternalServerError, "Could not get cookie session."}
|
ErrInternalCookieSession = impart.HTTPError{http.StatusInternalServerError, "Could not get cookie session."}
|
||||||
|
|
||||||
|
ErrUnavailable = impart.HTTPError{http.StatusServiceUnavailable, "Service temporarily unavailable due to high load."}
|
||||||
|
|
||||||
ErrCollectionNotFound = impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
ErrCollectionNotFound = impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
|
||||||
ErrCollectionGone = impart.HTTPError{http.StatusGone, "This blog was unpublished."}
|
ErrCollectionGone = impart.HTTPError{http.StatusGone, "This blog was unpublished."}
|
||||||
ErrCollectionPageNotFound = impart.HTTPError{http.StatusNotFound, "Collection page doesn't exist."}
|
ErrCollectionPageNotFound = impart.HTTPError{http.StatusNotFound, "Collection page doesn't exist."}
|
||||||
|
|
|
@ -83,6 +83,7 @@ type ErrorPages struct {
|
||||||
NotFound *template.Template
|
NotFound *template.Template
|
||||||
Gone *template.Template
|
Gone *template.Template
|
||||||
InternalServerError *template.Template
|
InternalServerError *template.Template
|
||||||
|
UnavailableError *template.Template
|
||||||
Blank *template.Template
|
Blank *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +95,7 @@ func NewHandler(apper Apper) *Handler {
|
||||||
NotFound: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>404</title></head><body><p>Not found.</p></body></html>{{end}}")),
|
NotFound: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>404</title></head><body><p>Not found.</p></body></html>{{end}}")),
|
||||||
Gone: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>410</title></head><body><p>Gone.</p></body></html>{{end}}")),
|
Gone: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>410</title></head><body><p>Gone.</p></body></html>{{end}}")),
|
||||||
InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>500</title></head><body><p>Internal server error.</p></body></html>{{end}}")),
|
InternalServerError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>500</title></head><body><p>Internal server error.</p></body></html>{{end}}")),
|
||||||
|
UnavailableError: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>503</title></head><body><p>Service is temporarily unavailable.</p></body></html>{{end}}")),
|
||||||
Blank: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>{{.Title}}</title></head><body><p>{{.Content}}</p></body></html>{{end}}")),
|
Blank: template.Must(template.New("").Parse("{{define \"base\"}}<html><head><title>{{.Title}}</title></head><body><p>{{.Content}}</p></body></html>{{end}}")),
|
||||||
},
|
},
|
||||||
sessionStore: apper.App().SessionStore(),
|
sessionStore: apper.App().SessionStore(),
|
||||||
|
@ -111,6 +113,7 @@ func NewWFHandler(apper Apper) *Handler {
|
||||||
NotFound: pages["404-general.tmpl"],
|
NotFound: pages["404-general.tmpl"],
|
||||||
Gone: pages["410.tmpl"],
|
Gone: pages["410.tmpl"],
|
||||||
InternalServerError: pages["500.tmpl"],
|
InternalServerError: pages["500.tmpl"],
|
||||||
|
UnavailableError: pages["503.tmpl"],
|
||||||
Blank: pages["blank.tmpl"],
|
Blank: pages["blank.tmpl"],
|
||||||
})
|
})
|
||||||
return h
|
return h
|
||||||
|
@ -763,6 +766,10 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er
|
||||||
log.Info("handleHTTPErorr internal error render")
|
log.Info("handleHTTPErorr internal error render")
|
||||||
h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
|
h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
|
||||||
return
|
return
|
||||||
|
} else if err.Status == http.StatusServiceUnavailable {
|
||||||
|
w.WriteHeader(err.Status)
|
||||||
|
h.errors.UnavailableError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
|
||||||
|
return
|
||||||
} else if err.Status == http.StatusAccepted {
|
} else if err.Status == http.StatusAccepted {
|
||||||
impart.WriteSuccess(w, "", err.Status)
|
impart.WriteSuccess(w, "", err.Status)
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{define "head"}}<title>Temporarily Unavailable — {{.SiteMetaName}}</title>{{end}}
|
||||||
|
{{define "content"}}
|
||||||
|
<div class="error-page">
|
||||||
|
<p class="msg">The words aren't coming to me. 🗅</p>
|
||||||
|
<p>We couldn't serve this page due to high server load. This should only be temporary.</p>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
Loading…
Reference in New Issue