SQLStore: Ensure that sessions are always closed (#55864)
* SQLStore: Ensure that sessions are always closed Delete `NewSession()` in favour of `WithDbSession()` * Add WithDbSessionForceNewSession to the interface * Apply suggestions from code review
This commit is contained in:
@@ -54,36 +54,38 @@ func (dc *databaseCache) internalRunGC() {
|
||||
|
||||
func (dc *databaseCache) Get(ctx context.Context, key string) (interface{}, error) {
|
||||
cacheHit := CacheData{}
|
||||
session := dc.SQLStore.NewSession(ctx)
|
||||
defer session.Close()
|
||||
|
||||
exist, err := session.Where("cache_key= ?", key).Get(&cacheHit)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return nil, ErrCacheItemNotFound
|
||||
}
|
||||
|
||||
if cacheHit.Expires > 0 {
|
||||
existedButExpired := getTime().Unix()-cacheHit.CreatedAt >= cacheHit.Expires
|
||||
if existedButExpired {
|
||||
err = dc.Delete(ctx, key) // ignore this error since we will return `ErrCacheItemNotFound` anyway
|
||||
if err != nil {
|
||||
dc.log.Debug("Deletion of expired key failed: %v", err)
|
||||
}
|
||||
return nil, ErrCacheItemNotFound
|
||||
}
|
||||
}
|
||||
|
||||
item := &cachedItem{}
|
||||
if err = decodeGob(cacheHit.Data, item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := dc.SQLStore.WithDbSession(ctx, func(session *sqlstore.DBSession) error {
|
||||
exist, err := session.Where("cache_key= ?", key).Get(&cacheHit)
|
||||
|
||||
return item.Val, nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exist {
|
||||
return ErrCacheItemNotFound
|
||||
}
|
||||
|
||||
if cacheHit.Expires > 0 {
|
||||
existedButExpired := getTime().Unix()-cacheHit.CreatedAt >= cacheHit.Expires
|
||||
if existedButExpired {
|
||||
err = dc.Delete(ctx, key) // ignore this error since we will return `ErrCacheItemNotFound` anyway
|
||||
if err != nil {
|
||||
dc.log.Debug("Deletion of expired key failed: %v", err)
|
||||
}
|
||||
return ErrCacheItemNotFound
|
||||
}
|
||||
}
|
||||
|
||||
if err = decodeGob(cacheHit.Data, item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return item.Val, err
|
||||
}
|
||||
|
||||
func (dc *databaseCache) Set(ctx context.Context, key string, value interface{}, expire time.Duration) error {
|
||||
@@ -93,34 +95,33 @@ func (dc *databaseCache) Set(ctx context.Context, key string, value interface{},
|
||||
return err
|
||||
}
|
||||
|
||||
session := dc.SQLStore.NewSession(context.Background())
|
||||
defer session.Close()
|
||||
return dc.SQLStore.WithDbSession(ctx, func(session *sqlstore.DBSession) error {
|
||||
var expiresInSeconds int64
|
||||
if expire != 0 {
|
||||
expiresInSeconds = int64(expire) / int64(time.Second)
|
||||
}
|
||||
|
||||
var expiresInSeconds int64
|
||||
if expire != 0 {
|
||||
expiresInSeconds = int64(expire) / int64(time.Second)
|
||||
}
|
||||
|
||||
// attempt to insert the key
|
||||
sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
|
||||
_, err = session.Exec(sql, key, data, getTime().Unix(), expiresInSeconds)
|
||||
if err != nil {
|
||||
// attempt to update if a unique constrain violation or a deadlock (for MySQL) occurs
|
||||
// if the update fails propagate the error
|
||||
// which eventually will result in a key that is not finally set
|
||||
// but since it's a cache does not harm a lot
|
||||
if dc.SQLStore.Dialect.IsUniqueConstraintViolation(err) || dc.SQLStore.Dialect.IsDeadlock(err) {
|
||||
sql := `UPDATE cache_data SET data=?, created_at=?, expires=? WHERE cache_key=?`
|
||||
_, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
|
||||
if err != nil && dc.SQLStore.Dialect.IsDeadlock(err) {
|
||||
// most probably somebody else is upserting the key
|
||||
// so it is safe enough not to propagate this error
|
||||
return nil
|
||||
// attempt to insert the key
|
||||
sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
|
||||
_, err := session.Exec(sql, key, data, getTime().Unix(), expiresInSeconds)
|
||||
if err != nil {
|
||||
// attempt to update if a unique constrain violation or a deadlock (for MySQL) occurs
|
||||
// if the update fails propagate the error
|
||||
// which eventually will result in a key that is not finally set
|
||||
// but since it's a cache does not harm a lot
|
||||
if dc.SQLStore.Dialect.IsUniqueConstraintViolation(err) || dc.SQLStore.Dialect.IsDeadlock(err) {
|
||||
sql := `UPDATE cache_data SET data=?, created_at=?, expires=? WHERE cache_key=?`
|
||||
_, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
|
||||
if err != nil && dc.SQLStore.Dialect.IsDeadlock(err) {
|
||||
// most probably somebody else is upserting the key
|
||||
// so it is safe enough not to propagate this error
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (dc *databaseCache) Delete(ctx context.Context, key string) error {
|
||||
|
||||
Reference in New Issue
Block a user