cache middleware backend with ledis
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
4.4 KiB

// Copyright 2014 The Macaron Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package cache
import (
"fmt"
"log"
"strings"
"time"
"github.com/unknwon/com"
"github.com/ledisdb/ledisdb/config"
"github.com/ledisdb/ledisdb/ledis"
"gopkg.in/ini.v1"
"gitea.com/tango/cache"
)
var defaultHSetName = []byte("MacaronCache")
// LedisCacher represents a ledis cache adapter implementation.
type LedisCacher struct {
c *ledis.DB
interval int
}
// Put puts value into cache with key and expire time.
// If expired is 0, it lives forever.
func (c *LedisCacher) Put(key string, val interface{}, expire int64) (err error) {
if expire == 0 {
if err = c.c.Set([]byte(key), []byte(com.ToStr(val))); err != nil {
return err
}
_, err = c.c.HSet([]byte(key), defaultHSetName, []byte("0"))
return err
}
if err = c.c.SetEX([]byte(key), expire, []byte(com.ToStr(val))); err != nil {
return err
}
_, err = c.c.HSet([]byte(key), defaultHSetName, []byte(com.ToStr(time.Now().Add(time.Duration(expire)*time.Second).Unix())))
return err
}
// Get gets cached value by given key.
func (c *LedisCacher) Get(key string) interface{} {
val, err := c.c.Get([]byte(key))
if err != nil || len(val) == 0 {
return nil
}
return string(val)
}
// Delete deletes cached value by given key.
func (c *LedisCacher) Delete(key string) (err error) {
if _, err = c.c.Del([]byte(key)); err != nil {
return err
}
_, err = c.c.HDel(defaultHSetName, []byte(key))
return err
}
// Incr increases cached int-type value by given key as a counter.
func (c *LedisCacher) Incr(key string) error {
if !c.IsExist(key) {
return fmt.Errorf("key '%s' not exist", key)
}
_, err := c.c.Incr([]byte(key))
return err
}
// Decr decreases cached int-type value by given key as a counter.
func (c *LedisCacher) Decr(key string) error {
if !c.IsExist(key) {
return fmt.Errorf("key '%s' not exist", key)
}
_, err := c.c.Decr([]byte(key))
return err
}
// IsExist returns true if cached value exists.
func (c *LedisCacher) IsExist(key string) bool {
count, err := c.c.Exists([]byte(key))
if err == nil && count > 0 {
return true
}
c.c.HDel(defaultHSetName, []byte(key))
return false
}
// Flush deletes all cached data.
func (c *LedisCacher) Flush() error {
// FIXME: there must be something wrong, shouldn't use this one.
_, err := c.c.FlushAll()
return err
// keys, err := c.c.HKeys(defaultHSetName)
// if err != nil {
// return err
// }
// if _, err = c.c.Del(keys...); err != nil {
// return err
// }
// _, err = c.c.Del(defaultHSetName)
// return err
}
func (c *LedisCacher) startGC() {
if c.interval < 1 {
return
}
kvs, err := c.c.HGetAll(defaultHSetName)
if err != nil {
log.Printf("cache/redis: error garbage collecting(get): %v", err)
return
}
now := time.Now().Unix()
for _, v := range kvs {
expire := com.StrTo(v.Value).MustInt64()
if expire == 0 || now < expire {
continue
}
if err = c.Delete(string(v.Field)); err != nil {
log.Printf("cache/redis: error garbage collecting(delete): %v", err)
continue
}
}
time.AfterFunc(time.Duration(c.interval)*time.Second, func() { c.startGC() })
}
// StartAndGC starts GC routine based on config string settings.
// AdapterConfig: data_dir=./app.db,db=0
func (c *LedisCacher) StartAndGC(opts cache.Options) error {
c.interval = opts.Interval
cfg, err := ini.Load([]byte(strings.Replace(opts.AdapterConfig, ",", "\n", -1)))
if err != nil {
return err
}
db := 0
opt := new(config.Config)
for k, v := range cfg.Section("").KeysHash() {
switch k {
case "data_dir":
opt.DataDir = v
case "db":
db = com.StrTo(v).MustInt()
default:
return fmt.Errorf("session/ledis: unsupported option '%s'", k)
}
}
l, err := ledis.Open(opt)
if err != nil {
return fmt.Errorf("session/ledis: error opening db: %v", err)
}
c.c, err = l.Select(db)
if err != nil {
return err
}
go c.startGC()
return nil
}
func init() {
cache.Register("ledis", &LedisCacher{})
}