Browse Source

Merge pull request #284 from writeas/high-load-error-page

Show 503 page on blogs under high load
pull/288/head
Matt Baer 4 years ago
committed by GitHub
parent
commit
dbd7eff7ea
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 1 deletions
  1. +4
    -0
      database-lib.go
  2. +10
    -0
      database-no-sqlite.go
  3. +11
    -1
      database-sqlite.go
  4. +4
    -0
      database.go
  5. +2
    -0
      errors.go
  6. +7
    -0
      handle.go
  7. +7
    -0
      pages/503.tmpl

+ 4
- 0
database-lib.go View File

@@ -22,3 +22,7 @@ func (db *datastore) isDuplicateKeyErr(err error) bool {
func (db *datastore) isIgnorableError(err error) bool {
return false
}

func (db *datastore) isHighLoadError(err error) bool {
return false
}

+ 10
- 0
database-no-sqlite.go View File

@@ -40,3 +40,13 @@ func (db *datastore) isIgnorableError(err error) bool {

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
}

+ 11
- 1
database-sqlite.go View File

@@ -1,7 +1,7 @@
// +build sqlite,!wflib

/*
* Copyright © 2019 A Bunch Tell LLC.
* Copyright © 2019-2020 A Bunch Tell LLC.
*
* This file is part of WriteFreely.
*
@@ -60,3 +60,13 @@ func (db *datastore) isIgnorableError(err error) bool {

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
}

+ 4
- 0
database.go View File

@@ -39,6 +39,8 @@ import (
const (
mySQLErrDuplicateKey = 1062
mySQLErrCollationMix = 1267
mySQLErrTooManyConns = 1040
mySQLErrMaxUserConns = 1203

driverMySQL = "mysql"
driverSQLite = "sqlite3"
@@ -795,6 +797,8 @@ func (db *datastore) GetCollectionBy(condition string, value interface{}) (*Coll
switch {
case err == sql.ErrNoRows:
return nil, impart.HTTPError{http.StatusNotFound, "Collection doesn't exist."}
case db.isHighLoadError(err):
return nil, ErrUnavailable
case err != nil:
log.Error("Failed selecting from collections: %v", err)
return nil, err


+ 2
- 0
errors.go View File

@@ -37,6 +37,8 @@ var (
ErrInternalGeneral = impart.HTTPError{http.StatusInternalServerError, "The humans messed something up. They've been notified."}
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."}
ErrCollectionGone = impart.HTTPError{http.StatusGone, "This blog was unpublished."}
ErrCollectionPageNotFound = impart.HTTPError{http.StatusNotFound, "Collection page doesn't exist."}


+ 7
- 0
handle.go View File

@@ -83,6 +83,7 @@ type ErrorPages struct {
NotFound *template.Template
Gone *template.Template
InternalServerError *template.Template
UnavailableError *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}}")),
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}}")),
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}}")),
},
sessionStore: apper.App().SessionStore(),
@@ -111,6 +113,7 @@ func NewWFHandler(apper Apper) *Handler {
NotFound: pages["404-general.tmpl"],
Gone: pages["410.tmpl"],
InternalServerError: pages["500.tmpl"],
UnavailableError: pages["503.tmpl"],
Blank: pages["blank.tmpl"],
})
return h
@@ -763,6 +766,10 @@ func (h *Handler) handleHTTPError(w http.ResponseWriter, r *http.Request, err er
log.Info("handleHTTPErorr internal error render")
h.errors.InternalServerError.ExecuteTemplate(w, "base", pageForReq(h.app.App(), r))
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 {
impart.WriteSuccess(w, "", err.Status)
return


+ 7
- 0
pages/503.tmpl View File

@@ -0,0 +1,7 @@
{{define "head"}}<title>Temporarily Unavailable &mdash; {{.SiteMetaName}}</title>{{end}}
{{define "content"}}
<div class="error-page">
<p class="msg">The words aren't coming to me. &#x1F5C5;</p>
<p>We couldn't serve this page due to high server load. This should only be temporary.</p>
</div>
{{end}}

Loading…
Cancel
Save