mirror of https://github.com/jlelse/GoBlog
Implement basic db-based cache
This commit is contained in:
parent
6c4a9476b4
commit
7becbd6aad
|
@ -0,0 +1,53 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CacheMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestUrl, _ := url.ParseRequestURI(r.RequestURI)
|
||||||
|
if appConfig.cache.enable &&
|
||||||
|
// Check bypass query
|
||||||
|
!(requestUrl != nil && requestUrl.Query().Get("cache") == "0") {
|
||||||
|
mime, t, cache := getCache(SlashTrimmedPath(r), r.Context())
|
||||||
|
if cache == nil {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
expiresTime := time.Unix(t+appConfig.cache.expiration, 0).Format(time.RFC1123)
|
||||||
|
w.Header().Set("Expires", expiresTime)
|
||||||
|
w.Header().Set("Content-Type", mime)
|
||||||
|
_, _ = w.Write(cache)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCache(path string, context context.Context) (string, int64, []byte) {
|
||||||
|
var mime string
|
||||||
|
var t int64
|
||||||
|
var cache []byte
|
||||||
|
allowedTime := time.Now().Unix() - appConfig.cache.expiration
|
||||||
|
row := appDb.QueryRowContext(context, "select COALESCE(mime, ''), COALESCE(time, 0), value from cache where path=? and time>=?", path, allowedTime)
|
||||||
|
_ = row.Scan(&mime, &t, &cache)
|
||||||
|
return mime, t, cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveCache(path string, mime string, value []byte) {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
startWritingToDb()
|
||||||
|
tx, err := appDb.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = tx.Exec("delete from cache where time<?;", now-appConfig.cache.expiration)
|
||||||
|
_, _ = tx.Exec("insert or replace into cache (path, time, mime, value) values (?, ?, ?, ?);", path, now, mime, value)
|
||||||
|
_ = tx.Commit()
|
||||||
|
finishWritingToDb()
|
||||||
|
}
|
16
config.go
16
config.go
|
@ -8,6 +8,7 @@ import (
|
||||||
type config struct {
|
type config struct {
|
||||||
server *configServer
|
server *configServer
|
||||||
db *configDb
|
db *configDb
|
||||||
|
cache *configCache
|
||||||
}
|
}
|
||||||
|
|
||||||
type configServer struct {
|
type configServer struct {
|
||||||
|
@ -19,9 +20,15 @@ type configDb struct {
|
||||||
file string
|
file string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type configCache struct {
|
||||||
|
enable bool
|
||||||
|
expiration int64
|
||||||
|
}
|
||||||
|
|
||||||
var appConfig = &config{
|
var appConfig = &config{
|
||||||
server: &configServer{},
|
server: &configServer{},
|
||||||
db: &configDb{},
|
db: &configDb{},
|
||||||
|
cache: &configCache{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() error {
|
func initConfig() error {
|
||||||
|
@ -45,6 +52,15 @@ func initConfig() error {
|
||||||
viper.SetDefault(databaseFile, "data/db.sqlite")
|
viper.SetDefault(databaseFile, "data/db.sqlite")
|
||||||
appConfig.db.file = viper.GetString(databaseFile)
|
appConfig.db.file = viper.GetString(databaseFile)
|
||||||
logConfig(databaseFile, appConfig.db.file)
|
logConfig(databaseFile, appConfig.db.file)
|
||||||
|
// Caching
|
||||||
|
cacheEnable := "cache.enable"
|
||||||
|
viper.SetDefault(cacheEnable, true)
|
||||||
|
appConfig.cache.enable = viper.GetBool(cacheEnable)
|
||||||
|
logConfig(cacheEnable, appConfig.cache.enable)
|
||||||
|
cacheExpiration := "cache.expiration"
|
||||||
|
viper.SetDefault(cacheExpiration, 600)
|
||||||
|
appConfig.cache.expiration = viper.GetInt64(cacheExpiration)
|
||||||
|
logConfig(cacheExpiration, appConfig.cache.expiration)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,13 @@ func migrateDb() error {
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&migrator.Migration{
|
||||||
|
Name: "00004",
|
||||||
|
Func: func(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec("create table cache (path text not null primary key, time integer, mime text, value blob);")
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
4
http.go
4
http.go
|
@ -63,7 +63,7 @@ func buildHandler() (http.Handler, error) {
|
||||||
} else {
|
} else {
|
||||||
for _, path := range allPostPaths {
|
for _, path := range allPostPaths {
|
||||||
if path != "" {
|
if path != "" {
|
||||||
r.Get(path, servePost)
|
r.With(CacheMiddleware).Get(path, servePost)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,4 +109,4 @@ func SlashTrimmedPath(r *http.Request) string {
|
||||||
path = strings.TrimSuffix(path, "/")
|
path = strings.TrimSuffix(path, "/")
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
9
posts.go
9
posts.go
|
@ -18,7 +18,8 @@ type post struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func servePost(w http.ResponseWriter, r *http.Request) {
|
func servePost(w http.ResponseWriter, r *http.Request) {
|
||||||
post, err := getPost(SlashTrimmedPath(r), r.Context())
|
path := SlashTrimmedPath(r)
|
||||||
|
post, err := getPost(path, r.Context())
|
||||||
if err == postNotFound {
|
if err == postNotFound {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
|
@ -31,7 +32,11 @@ func servePost(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html")
|
mime := "text/html"
|
||||||
|
if appConfig.cache.enable {
|
||||||
|
saveCache(path, mime, htmlContent)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", mime)
|
||||||
_, _ = w.Write(htmlContent)
|
_, _ = w.Write(htmlContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue