diff --git a/README.md b/README.md index f7c4c5c..69914f4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ cachestore -========== +====== + +beta 1 diff --git a/encdec.go b/encdec.go new file mode 100644 index 0000000..14af49e --- /dev/null +++ b/encdec.go @@ -0,0 +1,54 @@ +package cachestore + +import ( + "bytes" + "crypto/md5" + "encoding/gob" + "encoding/json" + "fmt" + "io" +) + +// md5 hash string +func Md5(str string) string { + m := md5.New() + io.WriteString(m, str) + return fmt.Sprintf("%x", m.Sum(nil)) +} +func Encode(data interface{}) ([]byte, error) { + //return JsonEncode(data) + return GobEncode(data) +} + +func Decode(data []byte, to interface{}) error { + //return JsonDecode(data, to) + return GobDecode(data, to) +} + +func GobEncode(data interface{}) ([]byte, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(&data) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func GobDecode(data []byte, to interface{}) error { + buf := bytes.NewBuffer(data) + dec := gob.NewDecoder(buf) + return dec.Decode(to) +} + +func JsonEncode(data interface{}) ([]byte, error) { + val, err := json.Marshal(data) + if err != nil { + return nil, err + } + return val, nil +} + +func JsonDecode(data []byte, to interface{}) error { + return json.Unmarshal(data, to) +} diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..8030b53 --- /dev/null +++ b/example/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "log" + "strings" + "time" + + _ "github.com/go-sql-driver/mysql" + "github.com/go-xorm/cachestore" + //"github.com/go-xorm/core" + "github.com/go-xorm/xorm" +) + +var ( + cacher *xorm.LRUCacher + CacheDir string = "." + cfg []string = []string{"leveldb"} +) + +func main() { + + //cfg := strings.SplitN(cacherName, ":", 2) + + engine, err := xorm.NewEngine("mysql", "root:root@/coscms?charset=utf8") + if err != nil { + log.Fatalf("The database connection failed: %v\n", err) + } + switch strings.ToLower(cfg[0]) { + case "memory": + ccStore := xorm.NewMemoryStore() + //this.CacheStore = ccStore + cacher = xorm.NewLRUCacher(ccStore, 1000) //NewLRUCacher(store core.CacheStore, maxElementSize int) + case "leveldb": + storagePath := CacheDir + "/leveldb/dbcache" + if len(cfg) == 2 { + storagePath = cfg[1] + } + ccStore := cachestore.NewLevelDBStore(storagePath) + //this.CacheStore = ccStore + //ccStore.Debug = this.ShowDebug + cacher = xorm.NewLRUCacher(ccStore, 999999999) + case "memcache": + conn := "127.0.0.1:11211" + if len(cfg) == 2 { + conn = cfg[1] + } + ccStore := cachestore.NewMemCache(conn) + //this.CacheStore = ccStore + //ccStore.Debug = this.ShowDebug + cacher = xorm.NewLRUCacher(ccStore, 999999999) + } + if cacher != nil { + cacher.Expired = 86400 * time.Second + engine.SetDefaultCacher(cacher) + } + + //engine.Where(querystring, ...) +} diff --git a/example/run.bat b/example/run.bat new file mode 100644 index 0000000..8672808 --- /dev/null +++ b/example/run.bat @@ -0,0 +1,2 @@ +go run main.go +pause \ No newline at end of file diff --git a/leveldbstore.go b/leveldbstore.go new file mode 100644 index 0000000..37f7af9 --- /dev/null +++ b/leveldbstore.go @@ -0,0 +1,73 @@ +package cachestore + +import ( + "fmt" + "github.com/syndtr/goleveldb/leveldb" + //"reflect" +) + +// LevelDBStore implements CacheStore provide local machine +type LevelDBStore struct { + store *leveldb.DB + Debug bool + v interface{} +} + +func NewLevelDBStore(dbfile string) *LevelDBStore { + db := &LevelDBStore{} + if h, err := leveldb.OpenFile(dbfile, nil); err != nil { + panic(err) + } else { + db.store = h + } + return db +} + +func (s *LevelDBStore) Put(key string, value interface{}) error { + val, err := Encode(value) + if err != nil { + fmt.Println("[LevelDB]EncodeErr: ", err) + return err + } + err = s.store.Put([]byte(key), val, nil) + if err != nil { + fmt.Println("[LevelDB]PutErr: ", err) + } + if s.Debug { + fmt.Println("[LevelDB]Put: ", key) + } + return err +} + +func (s *LevelDBStore) Get(key string) (interface{}, error) { + data, err := s.store.Get([]byte(key), nil) + if err != nil { + fmt.Println("[LevelDB]GetErr: ", err) + return nil, err + } + + err = Decode(data, &s.v) + if err != nil { + fmt.Println("[LevelDB]DecodeErr: ", err) + } + if s.Debug { + fmt.Println("[LevelDB]Get: ", key, s.v) + } + return s.v, err +} + +func (s *LevelDBStore) Del(key string) error { + err := s.store.Delete([]byte(key), nil) + if err != nil { + fmt.Println("[LevelDB]DelErr: ", err) + return err + } + if s.Debug { + fmt.Println("[LevelDB]Del: ", key) + } + return err +} + +func (s *LevelDBStore) Close() { + s.store.Close() +} diff --git a/memcache.go b/memcache.go new file mode 100644 index 0000000..49c218f --- /dev/null +++ b/memcache.go @@ -0,0 +1,175 @@ +package cachestore + +import ( + "coscms/app/base/lib/cachestore/memcache" + "encoding/json" + "errors" + "fmt" +) + +// Memcache adapter. +type MemcacheCache struct { + c *memcache.Connection + conninfo string + LifeTime uint64 + Debug bool +} + +// create new memcache adapter. +func NewMemCache(conn string) *MemcacheCache { + rc := &MemcacheCache{LifeTime: 86400} + rc.conninfo = conn + rc.c = rc.connectInit() + return rc +} + +// get value from memcache. +func (rc *MemcacheCache) Get(key string) (interface{}, error) { + if rc.c == nil { + rc.c = rc.connectInit() + if rc.c == nil { + return nil, nil + } + } + val, err := rc.c.Get(Md5(key)) + if err != nil || len(val) < 1 { + if err != nil { + fmt.Println("[Memcache]GetErr: ", err) + rc.c.Close() + rc.c = nil + } + return nil, err + } + + var v interface{} + err = Decode(val[0].Value, &v) + if err != nil { + fmt.Println("[Memcache]DecodeErr: ", err) + } + + if rc.Debug { + fmt.Println("[Memcache]Get: ", key) + } + return v, err +} + +// put value to memcache. only support string. +func (rc *MemcacheCache) Put(key string, value interface{}) error { + if rc.c == nil { + rc.c = rc.connectInit() + if rc.c == nil { + return nil + } + } + + val, err := Encode(value) + if err != nil { + fmt.Println("[Memcache]EncodeErr: ", err) + return err + } + + stored, err := rc.c.Set(Md5(key), 0, rc.LifeTime, val) + if err != nil || stored == false { + if err != nil { + fmt.Println("[Memcache]PutErr: ", err) + rc.c.Close() + rc.c = nil + } + return errors.New("stored fail") + } + if rc.Debug { + fmt.Println("[Memcache]Put: ", key) + } + return err +} + +// delete value in memcache. +func (rc *MemcacheCache) Del(key string) error { + if rc.c == nil { + rc.c = rc.connectInit() + if rc.c == nil { + return nil + } + } + _, err := rc.c.Delete(Md5(key)) + if err != nil { + fmt.Println("[Memcache]DelErr: ", err) + rc.c.Close() + rc.c = nil + } + if rc.Debug { + fmt.Println("[Memcache]Del: ", key) + } + return err +} + +// [Not Support] +// increase counter. +func (rc *MemcacheCache) Incr(key string) error { + return errors.New("not support in memcache") +} + +// [Not Support] +// decrease counter. +func (rc *MemcacheCache) Decr(key string) error { + return errors.New("not support in memcache") +} + +// check value exists in memcache. +func (rc *MemcacheCache) IsExist(key string) bool { + if rc.c == nil { + rc.c = rc.connectInit() + } + v, err := rc.c.Get(key) + if err != nil { + rc.c.Close() + rc.c = nil + return false + } + if len(v) == 0 { + return false + } + return true +} + +// clear all cached in memcache. +func (rc *MemcacheCache) ClearAll() error { + if rc.c == nil { + rc.c = rc.connectInit() + if rc.c == nil { + return nil + } + } + err := rc.c.FlushAll() + return err +} + +// start memcache adapter. +// config string is like {"conn":"connection info"}. +// if connecting error, return. +func (rc *MemcacheCache) Connect(config string) error { + var cf map[string]string + json.Unmarshal([]byte(config), &cf) + if _, ok := cf["conn"]; !ok { + return errors.New("config has no conn key") + } + rc.conninfo = cf["conn"] + rc.c = rc.connectInit() + if rc.c == nil { + return errors.New("dial tcp conn error") + } + return nil +} + +// connect to memcache and keep the connection. +func (rc *MemcacheCache) connectInit() *memcache.Connection { + c, err := memcache.Connect(rc.conninfo) + if err != nil { + fmt.Println("[Memcahe]Connect failure:", err) + return nil + } + if rc.Debug { + fmt.Println("[Memcahe]Connect success:", rc.conninfo) + } + return c +} diff --git a/memcache/memcache.go b/memcache/memcache.go new file mode 100644 index 0000000..6a1aaa7 --- /dev/null +++ b/memcache/memcache.go @@ -0,0 +1,280 @@ +// Copyright 2012, Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package memcache + +import ( + "bufio" + "fmt" + "io" + "net" + "strconv" + "strings" +) + +type Connection struct { + conn net.Conn + buffered bufio.ReadWriter +} + +type Result struct { + Key string + Value []byte + Flags uint16 + Cas uint64 +} + +func Connect(address string) (conn *Connection, err error) { + var network string + if strings.Contains(address, "/") { + network = "unix" + } else { + network = "tcp" + } + var nc net.Conn + nc, err = net.Dial(network, address) + if err != nil { + return nil, err + } + return newConnection(nc), nil +} + +func newConnection(nc net.Conn) *Connection { + return &Connection{ + conn: nc, + buffered: *bufio.NewReadWriter( + bufio.NewReader(nc), + bufio.NewWriter(nc), + ), + } +} + +func (mc *Connection) Close() { + mc.conn.Close() + mc.conn = nil +} + +func (mc *Connection) IsClosed() bool { + return mc.conn == nil +} + +func (mc *Connection) Get(keys ...string) (results []Result, err error) { + defer handleError(&err) + results = mc.get("get", keys) + return +} + +func (mc *Connection) Gets(keys ...string) (results []Result, err error) { + defer handleError(&err) + results = mc.get("gets", keys) + return +} + +func (mc *Connection) Set(key string, flags uint16, timeout uint64, value []byte) (stored bool, err error) { + defer handleError(&err) + return mc.store("set", key, flags, timeout, value, 0), nil +} + +func (mc *Connection) Add(key string, flags uint16, timeout uint64, value []byte) (stored bool, err error) { + defer handleError(&err) + return mc.store("add", key, flags, timeout, value, 0), nil +} + +func (mc *Connection) Replace(key string, flags uint16, timeout uint64, value []byte) (stored bool, err error) { + defer handleError(&err) + return mc.store("replace", key, flags, timeout, value, 0), nil +} + +func (mc *Connection) Append(key string, flags uint16, timeout uint64, value []byte) (stored bool, err error) { + defer handleError(&err) + return mc.store("append", key, flags, timeout, value, 0), nil +} + +func (mc *Connection) Prepend(key string, flags uint16, timeout uint64, value []byte) (stored bool, err error) { + defer handleError(&err) + return mc.store("prepend", key, flags, timeout, value, 0), nil +} + +func (mc *Connection) Cas(key string, flags uint16, timeout uint64, value []byte, cas uint64) (stored bool, err error) { + defer handleError(&err) + return mc.store("cas", key, flags, timeout, value, cas), nil +} + +func (mc *Connection) Delete(key string) (deleted bool, err error) { + defer handleError(&err) + // delete [