2020-09-28 00:09:46 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2016-11-04 01:16:01 +03:00
package cache
import (
"fmt"
2022-04-01 19:34:57 +03:00
"strconv"
2016-11-04 01:16:01 +03:00
"time"
2021-02-11 00:28:32 +03:00
"code.gitea.io/gitea/modules/graceful"
2020-09-28 00:09:46 +03:00
"code.gitea.io/gitea/modules/nosql"
2016-11-04 01:16:01 +03:00
2024-04-13 11:38:44 +03:00
"gitea.com/go-chi/cache" //nolint:depguard
2023-04-14 01:41:04 +03:00
"github.com/redis/go-redis/v9"
2016-11-04 01:16:01 +03:00
)
// RedisCacher represents a redis cache adapter implementation.
type RedisCacher struct {
2020-09-28 00:09:46 +03:00
c redis . UniversalClient
2016-11-04 01:16:01 +03:00
prefix string
hsetName string
occupyMode bool
}
2022-04-01 19:34:57 +03:00
// toStr convert string/int/int64 interface to string. it's only used by the RedisCacher.Put internally
2023-07-04 21:36:08 +03:00
func toStr ( v any ) string {
2022-04-01 19:34:57 +03:00
if v == nil {
return ""
}
switch v := v . ( type ) {
case string :
return v
case [ ] byte :
return string ( v )
case int :
return strconv . FormatInt ( int64 ( v ) , 10 )
case int64 :
return strconv . FormatInt ( v , 10 )
default :
return fmt . Sprint ( v ) // as what the old com.ToStr does in most cases
}
}
// Put puts value (string type) into cache with key and expire time.
2016-11-04 01:16:01 +03:00
// If expired is 0, it lives forever.
2023-07-04 21:36:08 +03:00
func ( c * RedisCacher ) Put ( key string , val any , expire int64 ) error {
2022-04-01 19:34:57 +03:00
// this function is not well-designed, it only puts string values into cache
2016-11-04 01:16:01 +03:00
key = c . prefix + key
if expire == 0 {
2022-04-01 19:34:57 +03:00
if err := c . c . Set ( graceful . GetManager ( ) . HammerContext ( ) , key , toStr ( val ) , 0 ) . Err ( ) ; err != nil {
2016-11-04 01:16:01 +03:00
return err
}
} else {
2022-04-01 19:34:57 +03:00
dur := time . Duration ( expire ) * time . Second
if err := c . c . Set ( graceful . GetManager ( ) . HammerContext ( ) , key , toStr ( val ) , dur ) . Err ( ) ; err != nil {
2016-11-04 01:16:01 +03:00
return err
}
}
if c . occupyMode {
return nil
}
2021-02-11 00:28:32 +03:00
return c . c . HSet ( graceful . GetManager ( ) . HammerContext ( ) , c . hsetName , key , "0" ) . Err ( )
2016-11-04 01:16:01 +03:00
}
// Get gets cached value by given key.
2023-07-04 21:36:08 +03:00
func ( c * RedisCacher ) Get ( key string ) any {
2021-02-11 00:28:32 +03:00
val , err := c . c . Get ( graceful . GetManager ( ) . HammerContext ( ) , c . prefix + key ) . Result ( )
2016-11-04 01:16:01 +03:00
if err != nil {
return nil
}
return val
}
// Delete deletes cached value by given key.
func ( c * RedisCacher ) Delete ( key string ) error {
key = c . prefix + key
2021-02-11 00:28:32 +03:00
if err := c . c . Del ( graceful . GetManager ( ) . HammerContext ( ) , key ) . Err ( ) ; err != nil {
2016-11-04 01:16:01 +03:00
return err
}
if c . occupyMode {
return nil
}
2021-02-11 00:28:32 +03:00
return c . c . HDel ( graceful . GetManager ( ) . HammerContext ( ) , c . hsetName , key ) . Err ( )
2016-11-04 01:16:01 +03:00
}
// Incr increases cached int-type value by given key as a counter.
func ( c * RedisCacher ) Incr ( key string ) error {
if ! c . IsExist ( key ) {
return fmt . Errorf ( "key '%s' not exist" , key )
}
2021-02-11 00:28:32 +03:00
return c . c . Incr ( graceful . GetManager ( ) . HammerContext ( ) , c . prefix + key ) . Err ( )
2016-11-04 01:16:01 +03:00
}
// Decr decreases cached int-type value by given key as a counter.
func ( c * RedisCacher ) Decr ( key string ) error {
if ! c . IsExist ( key ) {
return fmt . Errorf ( "key '%s' not exist" , key )
}
2021-02-11 00:28:32 +03:00
return c . c . Decr ( graceful . GetManager ( ) . HammerContext ( ) , c . prefix + key ) . Err ( )
2016-11-04 01:16:01 +03:00
}
// IsExist returns true if cached value exists.
func ( c * RedisCacher ) IsExist ( key string ) bool {
2021-02-11 00:28:32 +03:00
if c . c . Exists ( graceful . GetManager ( ) . HammerContext ( ) , c . prefix + key ) . Val ( ) == 1 {
2016-11-04 01:16:01 +03:00
return true
}
if ! c . occupyMode {
2021-02-11 00:28:32 +03:00
c . c . HDel ( graceful . GetManager ( ) . HammerContext ( ) , c . hsetName , c . prefix + key )
2016-11-04 01:16:01 +03:00
}
return false
}
// Flush deletes all cached data.
func ( c * RedisCacher ) Flush ( ) error {
if c . occupyMode {
2021-02-11 00:28:32 +03:00
return c . c . FlushDB ( graceful . GetManager ( ) . HammerContext ( ) ) . Err ( )
2016-11-04 01:16:01 +03:00
}
2021-02-11 00:28:32 +03:00
keys , err := c . c . HKeys ( graceful . GetManager ( ) . HammerContext ( ) , c . hsetName ) . Result ( )
2016-11-04 01:16:01 +03:00
if err != nil {
return err
}
2021-02-11 00:28:32 +03:00
if err = c . c . Del ( graceful . GetManager ( ) . HammerContext ( ) , keys ... ) . Err ( ) ; err != nil {
2016-11-04 01:16:01 +03:00
return err
}
2021-02-11 00:28:32 +03:00
return c . c . Del ( graceful . GetManager ( ) . HammerContext ( ) , c . hsetName ) . Err ( )
2016-11-04 01:16:01 +03:00
}
// StartAndGC starts GC routine based on config string settings.
// AdapterConfig: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,hset_name=MacaronCache,prefix=cache:
func ( c * RedisCacher ) StartAndGC ( opts cache . Options ) error {
c . hsetName = "MacaronCache"
c . occupyMode = opts . OccupyMode
2020-09-28 00:09:46 +03:00
uri := nosql . ToRedisURI ( opts . AdapterConfig )
2016-11-04 01:16:01 +03:00
2020-09-28 00:09:46 +03:00
c . c = nosql . GetManager ( ) . GetRedisClient ( uri . String ( ) )
for k , v := range uri . Query ( ) {
2016-11-04 01:16:01 +03:00
switch k {
case "hset_name" :
2020-09-28 00:09:46 +03:00
c . hsetName = v [ 0 ]
2016-11-04 01:16:01 +03:00
case "prefix" :
2020-09-28 00:09:46 +03:00
c . prefix = v [ 0 ]
2016-11-04 01:16:01 +03:00
}
}
2021-02-11 00:28:32 +03:00
return c . c . Ping ( graceful . GetManager ( ) . HammerContext ( ) ) . Err ( )
2016-11-04 01:16:01 +03:00
}
2022-05-15 21:43:27 +03:00
// Ping tests if the cache is alive.
func ( c * RedisCacher ) Ping ( ) error {
return c . c . Ping ( graceful . GetManager ( ) . HammerContext ( ) ) . Err ( )
}
2016-11-04 01:16:01 +03:00
func init ( ) {
cache . Register ( "redis" , & RedisCacher { } )
}