Add thumbnail filename cache and reuse db connections

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer
2020-05-28 16:26:22 +02:00
parent 0527dd655f
commit 52473a1ca9
9 changed files with 63 additions and 19 deletions

View File

@@ -431,9 +431,9 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
start := time.Now()
typeName := c.Param("type")
uid := c.Param("uid")
start := time.Now()
thumbType, ok := thumb.Types[typeName]
@@ -468,7 +468,7 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
// Set missing flag so that the file doesn't show up in search results anymore.
log.Warnf("album: %s is missing", txt.Quote(f.FileName))
report("album", f.Update("FileMissing", true))
logError("album", f.Update("FileMissing", true))
return
}

View File

@@ -14,7 +14,7 @@ import (
var log = event.Log
func report(prefix string, err error) {
func logError(prefix string, err error) {
if err != nil {
log.Errorf("%s: %s", prefix, err.Error())
}

View File

@@ -44,7 +44,7 @@ func GetDownload(router *gin.RouterGroup, conf *config.Config) {
c.Data(404, "image/svg+xml", brokenIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
report("download", f.Update("FileMissing", true))
logError("download", f.Update("FileMissing", true))
return
}

View File

@@ -167,9 +167,9 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
start := time.Now()
typeName := c.Param("type")
labelUID := c.Param("uid")
start := time.Now()
thumbType, ok := thumb.Types[typeName]
@@ -203,7 +203,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
report("label", f.Update("FileMissing", true))
logError("label", f.Update("FileMissing", true))
return
}

View File

@@ -133,7 +133,7 @@ func GetPhotoDownload(router *gin.RouterGroup, conf *config.Config) {
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
report("photo", f.Update("FileMissing", true))
logError("photo", f.Update("FileMissing", true))
return
}

View File

@@ -1,18 +1,26 @@
package api
import (
"fmt"
"net/http"
"path"
"path/filepath"
"time"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/internal/thumb"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/photoprism/photoprism/pkg/txt"
)
type ThumbCache struct {
FileName string
ShareName string
}
// GET /api/v1/t/:hash/:token/:type
//
// Parameters:
@@ -25,17 +33,41 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
start := time.Now()
fileHash := c.Param("hash")
typeName := c.Param("type")
thumbType, ok := thumb.Types[typeName]
if !ok {
log.Errorf("photo: invalid thumb type %s", txt.Quote(typeName))
log.Errorf("thumbnail: invalid type %s", txt.Quote(typeName))
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
return
}
gc := service.Cache()
cacheKey := fmt.Sprintf("thumbnail:%s:%s", fileHash, typeName)
if cacheData, ok := gc.Get(cacheKey); ok {
log.Debugf("cache hit for %s [%s]", cacheKey, time.Since(start))
cached := cacheData.(ThumbCache)
if !fs.FileExists(cached.FileName) {
log.Errorf("thumbnail: %s not found", fileHash)
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
return
}
if c.Query("download") != "" {
c.FileAttachment(cached.FileName, cached.ShareName)
} else {
c.File(cached.FileName)
}
return
}
f, err := query.FileByHash(fileHash)
if err != nil {
@@ -62,16 +94,16 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
fileName := path.Join(conf.OriginalsPath(), f.FileName)
if !fs.FileExists(fileName) {
log.Errorf("photo: file %s is missing", txt.Quote(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
log.Errorf("thumbnail: file %s is missing", txt.Quote(f.FileName))
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
report("photo", f.Update("FileMissing", true))
logError("thumbnail", f.Update("FileMissing", true))
if f.AllFilesMissing() {
log.Infof("photo: deleting photo, all files missing for %s", txt.Quote(f.FileName))
log.Infof("thumbnail: deleting photo, all files missing for %s", txt.Quote(f.FileName))
report("photo", f.RelatedPhoto().Delete(false))
logError("thumbnail", f.RelatedPhoto().Delete(false))
}
return
@@ -79,7 +111,7 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
// Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157
if thumbType.ExceedsLimit() && c.Query("download") == "" {
log.Debugf("photo: using original, thumbnail size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
log.Debugf("thumbnail: using original, size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
c.File(fileName)
@@ -95,15 +127,19 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
}
if err != nil {
log.Errorf("photo: %s", err)
log.Errorf("thumbnail: %s", err)
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
return
} else if thumbnail == "" {
log.Errorf("photo: thumbnail name for %s is empty - bug?", filepath.Base(fileName))
log.Errorf("thumbnail: thumbnail name for %s is empty - bug?", filepath.Base(fileName))
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
return
}
// Cache thumbnail filename.
gc.Set(cacheKey, ThumbCache{thumbnail, f.ShareFileName()}, time.Hour*24)
log.Debugf("cached %s [%s]", cacheKey, time.Since(start))
if c.Query("download") != "" {
c.FileAttachment(thumbnail, f.ShareFileName())
} else {

View File

@@ -66,7 +66,7 @@ func GetVideo(router *gin.RouterGroup, conf *config.Config) {
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore.
report("video", f.Update("FileMissing", true))
logError("video", f.Update("FileMissing", true))
return
}

View File

@@ -94,7 +94,7 @@ func CreateZip(router *gin.RouterGroup, conf *config.Config) {
log.Infof("zip: added %s as %s", txt.Quote(f.FileName), txt.Quote(fileAlias))
} else {
log.Warnf("zip: file %s is missing", txt.Quote(f.FileName))
report("zip", f.Update("FileMissing", true))
logError("zip", f.Update("FileMissing", true))
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"io/ioutil"
"runtime"
"strings"
"time"
@@ -141,7 +142,14 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
db.LogMode(false)
db.SetLogger(log)
db.DB().SetMaxIdleConns(0)
if runtime.NumCPU() > 4 {
db.DB().SetMaxIdleConns(runtime.NumCPU())
} else {
db.DB().SetMaxIdleConns(4)
}
db.DB().SetConnMaxLifetime(time.Minute)
db.DB().SetMaxOpenConns(c.DatabaseConns())
c.db = db