diff --git a/cache.go b/cache.go index 126491e..3281418 100644 --- a/cache.go +++ b/cache.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/bradfitz/gomemcache/memcache" "github.com/hoisie/redis" "github.com/miekg/dns" ) @@ -35,10 +36,11 @@ func (e CacheIsFull) Error() string { } type SerializerError struct { + err error } func (e SerializerError) Error() string { - return "Serializer error" + return fmt.Sprintf("Serializer error: got %v", e.err) } type Mesg struct { @@ -50,8 +52,8 @@ type Cache interface { Get(key string) (Msg *dns.Msg, err error) Set(key string, Msg *dns.Msg) error Exists(key string) bool - Remove(key string) - Length() int + Remove(key string) error + Full() bool } type MemoryCache struct { @@ -91,10 +93,11 @@ func (c *MemoryCache) Set(key string, msg *dns.Msg) error { return nil } -func (c *MemoryCache) Remove(key string) { +func (c *MemoryCache) Remove(key string) error { c.mu.Lock() delete(c.Backend, key) c.mu.Unlock() + return nil } func (c *MemoryCache) Exists(key string) bool { @@ -118,6 +121,70 @@ func (c *MemoryCache) Full() bool { return c.Length() >= c.Maxcount } +/* +Memcached backend +*/ + +func NewMemcachedCache(servers []string, expire int32) *MemcachedCache { + c := memcache.New(servers...) + return &MemcachedCache{ + backend: c, + expire: expire, + } +} + +type MemcachedCache struct { + backend *memcache.Client + expire int32 +} + +func (m *MemcachedCache) Set(key string, msg *dns.Msg) error { + var val []byte + var err error + + // handle cases for negacache where it sets nil values + if msg == nil { + val = []byte("nil") + } else { + val, err = msg.Pack() + } + if err != nil { + err = SerializerError{err} + } + return m.backend.Set(&memcache.Item{Key: key, Value: val, Expiration: m.expire}) +} + +func (m *MemcachedCache) Get(key string) (*dns.Msg, error) { + var msg dns.Msg + item, err := m.backend.Get(key) + if err != nil { + err = KeyNotFound{key} + return &msg, err + } + err = msg.Unpack(item.Value) + if err != nil { + err = SerializerError{err} + } + return &msg, err +} + +func (m *MemcachedCache) Exists(key string) bool { + _, err := m.backend.Get(key) + if err != nil { + return true + } + return false +} + +func (m *MemcachedCache) Remove(key string) error { + return m.backend.Delete(key) +} + +func (m *MemcachedCache) Full() bool { + // memcache is never full (LRU) + return false +} + /* TODO: Redis cache Backend */ @@ -149,16 +216,18 @@ func KeyGen(q Question) string { return key } +/* we need to define marsheling to encode and decode + */ type JsonSerializer struct { } func (*JsonSerializer) Dumps(mesg *dns.Msg) (encoded []byte, err error) { - encoded, err = json.Marshal(mesg) + encoded, err = json.Marshal(*mesg) return } -func (*JsonSerializer) Loads(data []byte, mesg **dns.Msg) error { - err := json.Unmarshal(data, mesg) - return err - +func (*JsonSerializer) Loads(data []byte) (*dns.Msg, error) { + var mesg dns.Msg + err := json.Unmarshal(data, &mesg) + return &mesg, err } diff --git a/godns.conf b/godns.conf index 4b4e5c5..5a23165 100644 --- a/godns.conf +++ b/godns.conf @@ -24,6 +24,9 @@ port = 6379 db = 0 password ="" +[memcache] +servers = ["127.0.0.1:11211"] + [log] stdout = true file = "./godns.log" @@ -32,7 +35,7 @@ level = "INFO" #DEBUG | INFO |NOTICE | WARN | ERROR [cache] -# backend option [memory|redis] +# backend option [memory|memcache|redis] # redis backend not implemented yet backend = "memory" expire = 600 # 10 minutes diff --git a/handler.go b/handler.go index a1acc8e..b8a26f4 100644 --- a/handler.go +++ b/handler.go @@ -61,6 +61,13 @@ func NewHandler() *GODNSHandler { Expire: time.Duration(cacheConfig.Expire) * time.Second / 2, Maxcount: cacheConfig.Maxcount, } + case "memcache": + cache = NewMemcachedCache( + settings.Memcache.Servers, + int32(cacheConfig.Expire)) + negCache = NewMemcachedCache( + settings.Memcache.Servers, + int32(cacheConfig.Expire/2)) case "redis": // cache = &MemoryCache{ // Backend: make(map[string]*dns.Msg), diff --git a/settings.go b/settings.go index 0fc5c2b..d1a5dfa 100644 --- a/settings.go +++ b/settings.go @@ -27,6 +27,7 @@ type Settings struct { Server DNSServerSettings `toml:"server"` ResolvConfig ResolvSettings `toml:"resolv"` Redis RedisSettings `toml:"redis"` + Memcache MemcacheSettings `toml:"memcache"` Log LogSettings `toml:"log"` Cache CacheSettings `toml:"cache"` Hosts HostsSettings `toml:"hosts"` @@ -50,6 +51,10 @@ type RedisSettings struct { Password string } +type MemcacheSettings struct { + Servers []string +} + func (s RedisSettings) Addr() string { return s.Host + ":" + strconv.Itoa(s.Port) }