From f475eae586e62b7ff8195838bee4a3508e6dc5b7 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 15 Jul 2013 00:26:29 -0300 Subject: [PATCH] anidb: Implement GroupByName Also add the invalid key guard to GroupByID. --- groupcache.go | 239 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 168 insertions(+), 71 deletions(-) diff --git a/groupcache.go b/groupcache.go index e67cd59..dbf3037 100644 --- a/groupcache.go +++ b/groupcache.go @@ -3,6 +3,7 @@ package anidb import ( "encoding/gob" "github.com/Kovensky/go-anidb/http" + "github.com/Kovensky/go-anidb/udp" "strconv" "strings" "time" @@ -10,6 +11,8 @@ import ( func init() { gob.RegisterName("*github.com/Kovensky/go-anidb.Group", &Group{}) + gob.RegisterName("github.com/Kovensky/go-anidb.GID", GID(0)) + gob.RegisterName("*github.com/Kovensky/go-anidb.gidCache", &gidCache{}) } func (g *Group) Touch() { @@ -26,6 +29,11 @@ func (g *Group) IsStale() bool { // Unique Group IDentifier type GID int +// make GID cacheable + +func (e GID) Touch() {} +func (e GID) IsStale() bool { return false } + // Retrieves the Group from the cache. func (gid GID) Group() *Group { var g Group @@ -35,6 +43,19 @@ func (gid GID) Group() *Group { return nil } +type gidCache struct { + GID + Time time.Time +} + +func (c *gidCache) Touch() { c.Time = time.Now() } +func (c *gidCache) IsStale() bool { + if c != nil && time.Now().Sub(c.Time) < GroupCacheDuration { + return false + } + return true +} + // Returns a Group from the cache if possible. // // If the Group is stale, then retrieves the Group @@ -49,6 +70,11 @@ func (adb *AniDB) GroupByID(gid GID) <-chan *Group { return ch } + if !cache.CheckValid(keys...) { + intentMap.Notify((*Group)(nil), keys...) + return ch + } + if g := gid.Group(); !g.IsStale() { intentMap.Notify(g, keys...) return ch @@ -60,79 +86,150 @@ func (adb *AniDB) GroupByID(gid GID) <-chan *Group { var g *Group if reply.Error() == nil { - parts := strings.Split(reply.Lines()[1], "|") - ints := make([]int64, len(parts)) - for i := range parts { - ints[i], _ = strconv.ParseInt(parts[i], 10, 32) - } - - irc := "" - if parts[7] != "" { - irc = "irc://" + parts[8] + "/" + parts[7][1:] - } - - pic := "" - if parts[10] != "" { - pic = httpapi.AniDBImageBaseURL + parts[10] - } - - rellist := strings.Split(parts[16], "'") - relations := make(map[GID]GroupRelationType, len(rellist)) - for _, rel := range rellist { - r := strings.Split(rel, ",") - gid, _ := strconv.ParseInt(r[0], 10, 32) - typ, _ := strconv.ParseInt(r[1], 10, 32) - - relations[GID(gid)] = GroupRelationType(typ) - } - - ft := time.Unix(ints[11], 0) - if ints[11] == 0 { - ft = time.Time{} - } - dt := time.Unix(ints[12], 0) - if ints[12] == 0 { - dt = time.Time{} - } - lr := time.Unix(ints[14], 0) - if ints[14] == 0 { - lr = time.Time{} - } - la := time.Unix(ints[15], 0) - if ints[15] == 0 { - la = time.Time{} - } - - g = &Group{ - GID: GID(ints[0]), - - Name: parts[5], - ShortName: parts[6], - - IRC: irc, - URL: parts[9], - Picture: pic, - - Founded: ft, - Disbanded: dt, - // ignore ints[13] - LastRelease: lr, - LastActivity: la, - - Rating: Rating{ - Rating: float32(ints[1]) / 100, - VoteCount: int(ints[2]), - }, - AnimeCount: int(ints[3]), - FileCount: int(ints[4]), - - RelatedGroups: relations, - - Cached: time.Now(), - } + g = parseGroupReply(reply) + } else if reply.Code() == 350 { + cache.MarkInvalid(keys...) + } + if g != nil { + cache.Set(&gidCache{GID: g.GID}, "gid", "by-name", g.Name) + cache.Set(&gidCache{GID: g.GID}, "gid", "by-shortname", g.ShortName) + cache.Set(g, keys...) } - cache.Set(g, keys...) + intentMap.Notify(g, keys...) }() return ch } + +func (adb *AniDB) GroupByName(gname string) <-chan *Group { + keys := []cacheKey{"gid", "by-name", gname} + altKeys := []cacheKey{"gid", "by-shortname", gname} + ch := make(chan *Group, 1) + + ic := make(chan Cacheable, 1) + go func() { + gid := (<-ic).(GID) + if gid > 0 { + ch <- <-adb.GroupByID(gid) + } + close(ch) + }() + if intentMap.Intent(ic, keys...) { + return ch + } + + if !cache.CheckValid(keys...) { + intentMap.Notify(GID(0), keys...) + return ch + } + + var gc gidCache + if cache.Get(&gc, keys...) == nil { + intentMap.Notify(gc.GID, keys...) + return ch + } + + if cache.Get(&gc, altKeys...) == nil { + intentMap.Notify(gc.GID, keys...) + return ch + } + + go func() { + reply := <-adb.udp.SendRecv("GROUP", + paramMap{"gname": gname}) + + var g *Group + if reply.Error() == nil { + g = parseGroupReply(reply) + } else if reply.Code() == 350 { + cache.MarkInvalid(keys...) + } + + gid := GID(0) + if g != nil { + gid = g.GID + + cache.Set(&gidCache{GID: gid}, keys...) + cache.Set(&gidCache{GID: gid}, altKeys...) + cache.Set(g, "gid", gid) + } + intentMap.Notify(gid, keys...) + }() + return ch +} + +func parseGroupReply(reply udpapi.APIReply) *Group { + parts := strings.Split(reply.Lines()[1], "|") + ints := make([]int64, len(parts)) + for i := range parts { + ints[i], _ = strconv.ParseInt(parts[i], 10, 32) + } + + irc := "" + if parts[7] != "" { + irc = "irc://" + parts[8] + "/" + parts[7][1:] + } + + pic := "" + if parts[10] != "" { + pic = httpapi.AniDBImageBaseURL + parts[10] + } + + rellist := strings.Split(parts[16], "'") + relations := make(map[GID]GroupRelationType, len(rellist)) + for _, rel := range rellist { + r := strings.Split(rel, ",") + if len(r) < 2 { + continue + } + gid, _ := strconv.ParseInt(r[0], 10, 32) + typ, _ := strconv.ParseInt(r[1], 10, 32) + + relations[GID(gid)] = GroupRelationType(typ) + } + + ft := time.Unix(ints[11], 0) + if ints[11] == 0 { + ft = time.Time{} + } + dt := time.Unix(ints[12], 0) + if ints[12] == 0 { + dt = time.Time{} + } + lr := time.Unix(ints[14], 0) + if ints[14] == 0 { + lr = time.Time{} + } + la := time.Unix(ints[15], 0) + if ints[15] == 0 { + la = time.Time{} + } + + return &Group{ + GID: GID(ints[0]), + + Name: parts[5], + ShortName: parts[6], + + IRC: irc, + URL: parts[9], + Picture: pic, + + Founded: ft, + Disbanded: dt, + // ignore ints[13] + LastRelease: lr, + LastActivity: la, + + Rating: Rating{ + Rating: float32(ints[1]) / 100, + VoteCount: int(ints[2]), + }, + AnimeCount: int(ints[3]), + FileCount: int(ints[4]), + + RelatedGroups: relations, + + Cached: time.Now(), + } +} -- 2.44.0