Skip to content

Commit

Permalink
add some caching
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanfaerman committed Feb 8, 2024
1 parent fe674a8 commit e48a343
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 8 deletions.
8 changes: 8 additions & 0 deletions internal/models/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type Account struct {
Deleted bool

Distance float64

callsigns []Callsign
}

func init() {
Expand Down Expand Up @@ -219,6 +221,10 @@ func FindAccountByCallsign(ctx context.Context, callsign string) (*Account, erro
}

func (u *Account) Callsigns() ([]Callsign, error) {
if len(u.callsigns) > 0 {
fmt.Println("using preloaded callsigns")
return u.callsigns, nil
}
var callsigns []Callsign
rows, err := global.dao.FindCallsignsForAccount(context.Background(), u.ID)
if err != nil {
Expand All @@ -244,6 +250,8 @@ func (u *Account) Callsigns() ([]Callsign, error) {
}
callsigns = append(callsigns, callsign)
}
u.callsigns = callsigns

return callsigns, nil
}

Expand Down
11 changes: 11 additions & 0 deletions internal/models/account_finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"github.com/ryanfaerman/netctl/internal/models/finders"
)

func (m Account) FindCacheKey() string {
return "accounts"
}

func (m Account) Find(ctx context.Context, queries finders.QuerySet) (any, error) {
var (
raw dao.Account
Expand Down Expand Up @@ -121,6 +125,13 @@ func (m Account) Find(ctx context.Context, queries finders.QuerySet) (any, error
return nil, err
}

switch {
case queries.HasField("callsigns"):
if _, err := (&a).Callsigns(); err != nil {
return nil, err
}
}

found[i] = &a
}

Expand Down
4 changes: 2 additions & 2 deletions internal/models/finders/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ func WithSettings() (Query, error) {
return Query{Type: QueryField, Fields: []string{"settings"}, Values: []any{true}}, nil
}

func WithCallsign() (Query, error) {
return Query{Type: QueryField, Fields: []string{"callsign"}, Values: []any{true}}, nil
func WithCallsigns() (Query, error) {
return Query{Type: QueryField, Fields: []string{"callsigns"}, Values: []any{true}}, nil
}
65 changes: 65 additions & 0 deletions internal/models/finders/finders.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package finders

import (
"context"
"time"

ttlcache "github.com/jellydator/ttlcache/v3"
)

// Finder is an interface for finding things. Anything that wishes to be
Expand All @@ -13,6 +16,11 @@ type Finder interface {
Find(context.Context, QuerySet) (any, error)
}

type FinderCacher interface {
Finder
FindCacheKey() string
}

// Find the all instances of a Finder type, given a set of queries.
func Find[K Finder](ctx context.Context, queries ...QueryFunc) ([]*K, error) {
k := *new(K)
Expand Down Expand Up @@ -50,3 +58,60 @@ func FindOne[K Finder](ctx context.Context, queries ...QueryFunc) (*K, error) {

return nil, ErrNotFound
}

var caches = make(map[string]*ttlcache.Cache[string, any])

func FindCached[K FinderCacher](ctx context.Context, ttl time.Duration, queries ...QueryFunc) ([]*K, error) {
k := *new(K)
cacheKey := k.FindCacheKey()

cache, ok := caches[cacheKey]
if !ok {
cache = ttlcache.New[string, any](
ttlcache.WithTTL[string, any](ttl),
)
go cache.Start()
caches[cacheKey] = cache
}

var qs QuerySet
for _, q := range queries {
v, err := q()
if err != nil {
return nil, err
}
qs = append(qs, v)
}
qsKey := qs.String()
if cache.Has(qsKey) {
return cache.Get(qsKey).Value().([]*K), nil
}

results, err := Find[K](ctx, queries...)
if err != nil {
return nil, nil
}

cache.Set(qsKey, results, ttlcache.DefaultTTL)

return nil, nil
}

func FindOneCached[K FinderCacher](ctx context.Context, ttl time.Duration, queries ...QueryFunc) (*K, error) {
results, err := FindCached[K](ctx, ttl, queries...)
if err != nil {
return nil, err
}
if len(results) >= 1 {
return results[0], nil
}
return nil, ErrNotFound
}

func ClearFinderCache[K FinderCacher]() {
k := *new(K)
cacheKey := k.FindCacheKey()
if cache, ok := caches[cacheKey]; ok {
cache.DeleteAll()
}
}
19 changes: 18 additions & 1 deletion internal/models/finders/query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package finders

import "slices"
import (
"fmt"
"slices"
"strings"
)

type QueryType int

Expand All @@ -16,9 +20,22 @@ type Query struct {
Type QueryType
}

func (q Query) String() string {
return fmt.Sprintf("Query{Fields: %v, Values: %v, Type: %v}", q.Fields, q.Values, q.Type)
}

// QuerySet is a collection of queries
type QuerySet []Query

func (qs QuerySet) String() string {
var b strings.Builder
for _, q := range qs {
b.WriteString(q.String())
b.WriteString(";")
}
return b.String()
}

type QueryFunc func() (Query, error)

// Fields returns a list of all fields in the QuerySet
Expand Down
10 changes: 9 additions & 1 deletion internal/services/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package services

import (
"database/sql"
"fmt"
"sync"
"time"

scs "github.com/alexedwards/scs/v2"
"github.com/r3labs/sse/v2"
Expand Down Expand Up @@ -67,8 +69,14 @@ func Setup(logger *log.Logger, db *sql.DB) error {

err = models.Setup(logger, db)

global.session.Store = sqlite3store.New(global.db)
lifetimeStr := config.Get("session.lifetime", "240h")
lifetime, err := time.ParseDuration(lifetimeStr)
if err != nil {
panic(fmt.Sprintf("unable to parse session.lifetime: %s", err))
}

global.session.Store = sqlite3store.New(global.db)
global.session.Lifetime = lifetime
global.session.Cookie.Name = config.Get("session.name", "_session")
global.session.Cookie.Path = config.Get("session.path", "/")
})
Expand Down
27 changes: 23 additions & 4 deletions internal/services/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@ import (
"github.com/mrz1836/postmark"
"github.com/ryanfaerman/netctl/config"
"github.com/ryanfaerman/netctl/internal/models"

ttlcache "github.com/jellydator/ttlcache/v3"
)

type session struct{}
type session struct {
accountCache *ttlcache.Cache[int64, *models.Account]
}

var Session session
var Session = session{
accountCache: ttlcache.New[int64, *models.Account](
ttlcache.WithTTL[int64, *models.Account](global.session.Lifetime),
),
}

type sessionPayload struct {
Token string
Expand All @@ -25,6 +33,7 @@ type sessionPayload struct {

func init() {
gob.Register(sessionPayload{})
go Session.accountCache.Start()
}

func (session) IsAuthenticated(ctx context.Context) bool {
Expand Down Expand Up @@ -140,11 +149,19 @@ func (session) Destroy(ctx context.Context) error {

var ErrNoAccountInSession = errors.New("no account in session")

func (session) GetAccount(ctx context.Context) *models.Account {
func (s session) GetAccount(ctx context.Context) *models.Account {
id, ok := global.session.Get(ctx, "account_id").(int64)
if !ok {
return models.AccountAnonymous
}
if id < 0 {
return models.AccountAnonymous
}

if s.accountCache.Has(id) {
global.log.Debug("getting account from cache")
return s.accountCache.Get(id).Value()
}

account, err := models.FindAccountByID(ctx, id)
if err != nil {
Expand All @@ -153,9 +170,11 @@ func (session) GetAccount(ctx context.Context) *models.Account {
}
return models.AccountAnonymous
}
s.accountCache.Set(id, account, ttlcache.DefaultTTL)
return account
}

func (session) SetAccount(ctx context.Context, account *models.Account) {
func (s session) SetAccount(ctx context.Context, account *models.Account) {
global.session.Put(ctx, "account_id", account.ID)
s.accountCache.Delete(account.ID)
}

0 comments on commit e48a343

Please sign in to comment.