Skip to content

Commit e641702

Browse files
committed
Add LoginSession cache
1 parent 723bb93 commit e641702

3 files changed

Lines changed: 165 additions & 10 deletions

File tree

models/LoginSessions.go

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package models
22

33
import (
4+
"sync"
5+
46
"github.com/JojiiOfficial/gaw"
57
log "github.com/sirupsen/logrus"
68
"gorm.io/gorm"
@@ -14,30 +16,58 @@ type LoginSession struct {
1416
Token string
1517
Requests int64
1618
MachineID string
19+
20+
mx sync.Mutex `gorm:"-"`
1721
}
1822

1923
// SessionTokenLength length of session token
2024
const SessionTokenLength = 64
2125

2226
//GetUserFromSession return user from session
2327
func GetUserFromSession(db *gorm.DB, token string) (*User, error) {
24-
var session LoginSession
25-
err := db.Model(&LoginSession{}).Where(&LoginSession{
26-
Token: token,
27-
}).Preload("User").Preload("User.Role").Find(&session).Error
28+
var err error
2829

29-
if err != nil {
30-
return nil, err
30+
// Try to get session from cache
31+
session := sessionCache.getSession(token)
32+
if session == nil {
33+
// Load from DB if not cached
34+
session, err = loadSession(token, db)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
// Add token to cache
40+
sessionCache.addSession(session, db)
3141
}
3242

3343
// Increase request counter
34-
session.Requests++
35-
err = db.Save(&session).Error
44+
go func() {
45+
session.mx.Lock()
46+
defer session.mx.Unlock()
47+
48+
session.Requests++
49+
err = db.Model(&LoginSession{}).Save(session).Error
50+
if err != nil {
51+
log.Error(err)
52+
}
53+
}()
54+
55+
return session.User, nil
56+
}
57+
58+
func loadSession(token string, db *gorm.DB) (*LoginSession, error) {
59+
var ss LoginSession
60+
61+
// load session from db
62+
err := db.Model(&LoginSession{}).Where(&LoginSession{
63+
Token: token,
64+
}).Preload("User").Preload("User.Role").Find(&ss).Error
65+
3666
if err != nil {
37-
log.Error(err)
67+
return nil, err
3868
}
3969

40-
return session.User, nil
70+
return &ss, nil
4171
}
4272

4373
// NewSession create new login session

models/SessionCache.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package models
2+
3+
import (
4+
"sync"
5+
"time"
6+
7+
log "github.com/sirupsen/logrus"
8+
"gorm.io/gorm"
9+
)
10+
11+
// Cache sessions
12+
var sessionCache SessionCache
13+
14+
// SessionCache cache sessions in ram
15+
type SessionCache struct {
16+
cache map[string]*sessionCacheEntry
17+
18+
mx sync.Mutex
19+
}
20+
21+
type sessionCacheEntry struct {
22+
db *gorm.DB
23+
session *LoginSession
24+
valid bool
25+
26+
lastCheck int64
27+
requestsSinceLastCheck uint32
28+
29+
mx sync.Mutex
30+
}
31+
32+
// init the Cache
33+
func (sc *SessionCache) init() {
34+
sc.mx.Lock()
35+
defer sc.mx.Unlock()
36+
37+
if sc.cache == nil {
38+
sc.cache = make(map[string]*sessionCacheEntry)
39+
}
40+
}
41+
42+
// Get Session from Cache
43+
func (sc *SessionCache) getSession(token string) *LoginSession {
44+
sc.init()
45+
46+
sc.mx.Lock()
47+
defer sc.mx.Unlock()
48+
49+
// Get cache entry
50+
session, ok := sc.cache[token]
51+
if !ok {
52+
return nil
53+
}
54+
55+
// Update request counter
56+
session.requestsSinceLastCheck++
57+
58+
// Remove session from
59+
// cache if invalid
60+
if !session.valid {
61+
delete(sc.cache, token)
62+
return nil
63+
}
64+
65+
// Update if neccessary
66+
if session.needUpdate() {
67+
if !session.update() {
68+
return nil
69+
}
70+
}
71+
72+
return session.session
73+
}
74+
75+
// Add LoginSession to cache
76+
func (sc *SessionCache) addSession(session *LoginSession, db *gorm.DB) {
77+
sc.init()
78+
79+
sc.mx.Lock()
80+
defer sc.mx.Unlock()
81+
82+
sc.cache[session.Token] = &sessionCacheEntry{
83+
db: db,
84+
valid: true,
85+
session: session,
86+
lastCheck: time.Now().Unix(),
87+
}
88+
}
89+
90+
func (sce *sessionCacheEntry) update() bool {
91+
sce.mx.Lock()
92+
defer sce.mx.Unlock()
93+
94+
// Reload LoginSession frm DB
95+
session, err := loadSession(sce.session.Token, sce.db)
96+
if err != nil {
97+
log.Warn("Session invalid: ", err)
98+
sce.valid = false
99+
return false
100+
}
101+
102+
// Update cache entry
103+
sce.session = session
104+
sce.valid = true
105+
sce.lastCheck = time.Now().Unix()
106+
sce.requestsSinceLastCheck = 0
107+
108+
return true
109+
}
110+
111+
// Allow 60s cache
112+
const (
113+
maxCacheValidTime = int64(10)
114+
maxRequestsUntilCheck = uint32(10)
115+
)
116+
117+
// Return true if session needs update from DB
118+
func (sce *sessionCacheEntry) needUpdate() bool {
119+
sce.mx.Lock()
120+
defer sce.mx.Unlock()
121+
122+
return time.Now().Unix()-maxCacheValidTime > sce.lastCheck || sce.requestsSinceLastCheck > maxRequestsUntilCheck
123+
}

storage/Database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func ConnectToDatabase(config *models.Config) (*gorm.DB, error) {
2121
return nil, err
2222
}
2323

24+
// db = db.Debug()
25+
2426
//Automigration
2527
db.AutoMigrate(
2628
&models.Role{},

0 commit comments

Comments
 (0)