|
@@ -70,7 +70,10 @@ func (dc *databaseCache) Get(key string) (interface{}, error) {
|
|
|
if cacheHit.Expires > 0 {
|
|
if cacheHit.Expires > 0 {
|
|
|
existedButExpired := getTime().Unix()-cacheHit.CreatedAt >= cacheHit.Expires
|
|
existedButExpired := getTime().Unix()-cacheHit.CreatedAt >= cacheHit.Expires
|
|
|
if existedButExpired {
|
|
if existedButExpired {
|
|
|
- _ = dc.Delete(key) //ignore this error since we will return `ErrCacheItemNotFound` anyway
|
|
|
|
|
|
|
+ err = dc.Delete(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
|
|
return nil, ErrCacheItemNotFound
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -84,35 +87,40 @@ func (dc *databaseCache) Get(key string) (interface{}, error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (dc *databaseCache) Set(key string, value interface{}, expire time.Duration) error {
|
|
func (dc *databaseCache) Set(key string, value interface{}, expire time.Duration) error {
|
|
|
- return dc.SQLStore.WithTransactionalDbSession(context.Background(), func(session *sqlstore.DBSession) error {
|
|
|
|
|
- item := &cachedItem{Val: value}
|
|
|
|
|
- data, err := encodeGob(item)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ item := &cachedItem{Val: value}
|
|
|
|
|
+ data, err := encodeGob(item)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var cacheHit CacheData
|
|
|
|
|
- has, err := session.Where("cache_key = ?", key).Get(&cacheHit)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ session := dc.SQLStore.NewSession()
|
|
|
|
|
+ defer session.Close()
|
|
|
|
|
|
|
|
- var expiresInSeconds int64
|
|
|
|
|
- if expire != 0 {
|
|
|
|
|
- expiresInSeconds = int64(expire) / int64(time.Second)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var expiresInSeconds int64
|
|
|
|
|
+ if expire != 0 {
|
|
|
|
|
+ expiresInSeconds = int64(expire) / int64(time.Second)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // insert or update depending on if item already exist
|
|
|
|
|
- if has {
|
|
|
|
|
|
|
+ // 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=?`
|
|
sql := `UPDATE cache_data SET data=?, created_at=?, expires=? WHERE cache_key=?`
|
|
|
_, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
|
|
_, err = session.Exec(sql, data, getTime().Unix(), expiresInSeconds, key)
|
|
|
- } else {
|
|
|
|
|
- sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
|
|
|
|
|
- _, err = session.Exec(sql, key, data, getTime().Unix(), expiresInSeconds)
|
|
|
|
|
|
|
+ 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(key string) error {
|
|
func (dc *databaseCache) Delete(key string) error {
|